WordPress.org

Make WordPress Core

Ticket #38342: 38342-6.diff

File 38342-6.diff, 26.7 KB (added by aduth, 4 years ago)
  • src/wp-admin/css/dashboard.css

    diff --git src/wp-admin/css/dashboard.css src/wp-admin/css/dashboard.css
    index 72b0230..3ef5d82 100644
    form.initial-form.quickpress-open input#title { 
    518518        resize: none;
    519519}
    520520
     521#quick-press.is-saving .spinner {
     522        visibility: inherit;
     523}
     524
    521525/* Dashboard Quick Draft - Drafts list */
    522526
    523527.js #dashboard_quick_press .drafts {
    form.initial-form.quickpress-open input#title { 
    541545        margin: 0 12px;
    542546}
    543547
     548#dashboard_quick_press .drafts ul.is-placeholder li {
     549        padding: 3px 0;
     550        color: transparent;
     551}
     552
     553@keyframes loading-fade {
     554        0% { opacity: .5; }
     555        50% { opacity: 1; }
     556        100% { opacity: .5; }
     557}
     558
     559#dashboard_quick_press .drafts ul.is-placeholder li:before,
     560#dashboard_quick_press .drafts ul.is-placeholder li:after {
     561        content: '';
     562        display: block;
     563        height: 13px;
     564        background: #eee;
     565        animation: loading-fade 1.6s ease-in-out infinite;
     566}
     567
     568#dashboard_quick_press .drafts ul.is-placeholder li:before {
     569        margin-bottom: 5px;
     570        width: 40%;
     571}
     572
     573#dashboard_quick_press .drafts ul.is-placeholder li:after {
     574        width: 80%;
     575}
     576
    544577#dashboard_quick_press .drafts li {
    545578        margin-bottom: 1em;
    546579}
     580
     581#dashboard_quick_press .drafts li.is-new {
     582        background-color: #fffbe5;
     583}
     584
    547585#dashboard_quick_press .drafts li time {
    548586        color: #72777c;
    549587}
  • src/wp-admin/includes/dashboard.php

    diff --git src/wp-admin/includes/dashboard.php src/wp-admin/includes/dashboard.php
    index aab5976..4a23fc4 100644
    function wp_dashboard_quick_press( $error_msg = false ) { 
    491491        $post_ID = (int) $post->ID;
    492492?>
    493493
    494         <form name="post" action="<?php echo esc_url( admin_url( 'post.php' ) ); ?>" method="post" id="quick-press" class="initial-form hide-if-no-js">
    495 
    496                 <?php if ( $error_msg ) : ?>
    497                 <div class="error"><?php echo $error_msg; ?></div>
    498                 <?php endif; ?>
     494        <form name="post" method="post" id="quick-press" class="initial-form hide-if-no-js">
    499495
    500496                <div class="input-text-wrap" id="title-wrap">
    501                         <label class="screen-reader-text prompt" for="title" id="title-prompt-text">
     497                        <label class="prompt" for="title" id="title-prompt-text">
    502498
    503499                                <?php
    504500                                /** This filter is documented in wp-admin/edit-form-advanced.php */
    505501                                echo apply_filters( 'enter_title_here', __( 'Title' ), $post );
    506502                                ?>
    507503                        </label>
    508                         <input type="text" name="post_title" id="title" autocomplete="off" />
     504                        <input type="text" name="title" id="title" autocomplete="off" />
    509505                </div>
    510506
    511507                <div class="textarea-wrap" id="description-wrap">
    512                         <label class="screen-reader-text prompt" for="content" id="content-prompt-text"><?php _e( 'What&#8217;s on your mind?' ); ?></label>
     508                        <label class="prompt" for="content" id="content-prompt-text"><?php _e( 'What&#8217;s on your mind?' ); ?></label>
    513509                        <textarea name="content" id="content" class="mceEditor" rows="3" cols="15" autocomplete="off"></textarea>
    514510                </div>
    515 
    516511                <p class="submit">
    517                         <input type="hidden" name="action" id="quickpost-action" value="post-quickdraft-save" />
    518                         <input type="hidden" name="post_ID" value="<?php echo $post_ID; ?>" />
    519                         <input type="hidden" name="post_type" value="post" />
    520                         <?php wp_nonce_field( 'add-post' ); ?>
     512                        <div class="error inline" style="display: none;"><p></p></div>
     513                        <div class="spinner no-float"></div>
    521514                        <?php submit_button( __( 'Save Draft' ), 'primary', 'save', false, array( 'id' => 'save-post' ) ); ?>
    522515                        <br class="clear" />
    523516                </p>
    524517
    525518        </form>
    526519        <?php
    527         wp_dashboard_recent_drafts();
     520        echo wp_dashboard_get_recent_drafts_js_template();
    528521}
    529522
    530523/**
    function wp_dashboard_recent_drafts( $drafts = false ) { 
    581574        }
    582575        echo "</ul>\n</div>";
    583576}
     577/**
     578 * Get the HTML template for the Quick Draft recent posts.
     579 *
     580 * @since 4.8.0
     581 *
     582 * @return string The template HTML.
     583 */
     584function wp_dashboard_get_recent_drafts_js_template() {
     585        $template_html  = '<div id="quick-press-drafts" class="drafts">';
     586        $template_html .= '<p class="view-all" style="display: none;"><a href="' . esc_url( admin_url( 'edit.php?post_status=draft' ) ) . '" aria-label="' . __( 'View all drafts' ) . '">' . _x( 'View all', 'drafts' ) . "</a></p>\n";
     587        $template_html .= '<h2 class="hide-if-no-js">' . __( 'Drafts' ) . "</h2>\n";
     588        $template_html .= '<script id="tmpl-item-quick-press-draft" type="text/template">';
     589        $template_html .= '<div class="draft-title"><a href="{{{ data.edit_link }}}" aria-label="' . esc_attr( __( 'Edit Post' ) ) . '">{{ data.title }}</a>';
     590        $template_html .= '<time datetime="{{ data.date.raw }}">{{ data.date.rendered }}</time></div>';
     591        $template_html .= '{{{ data.content }}}';
     592        $template_html .= '</script>';
     593        $template_html .= '<ul class="drafts-list is-placeholder">';
     594
     595        // Add a placeholder for each draft.
     596        $query_args = array(
     597                'post_type'      => 'post',
     598                'post_status'    => 'draft',
     599                'author'         => get_current_user_id(),
     600                'posts_per_page' => 4,
     601                'fields'         => 'ids',
     602                'no_found_rows'  => true,
     603        );
     604
     605        /** This filter is documented in wp-admin/includes/dashboard.php */
     606        $query_args = apply_filters( 'dashboard_recent_drafts_query_args', $query_args );
     607
     608        $count_query = new WP_Query( $query_args );
     609        if ( $count_query->have_posts()  ) {
     610                for ( $i = 0; $i < ($count_query->post_count ? $count_query->post_count : 4 ); $i++ ) {
     611                        $template_html .= '<li><span class="screen-reader-text">' . esc_html( __( 'Loading…' ) ) . '</span></li>';
     612                }
     613        }
     614        $template_html .= '</ul></div>';
     615
     616        return $template_html;
     617}
    584618
    585619/**
    586620 * Outputs a row for the Recent Comments widget.
  • src/wp-admin/js/dashboard.js

    diff --git src/wp-admin/js/dashboard.js src/wp-admin/js/dashboard.js
    index fa100dd..9a11c4f 100644
     
    1 /* global pagenow, ajaxurl, postboxes, wpActiveEditor:true */
    2 var ajaxWidgets, ajaxPopulateWidgets, quickPressLoad;
     1/* global _, wp, quickPress, pagenow, ajaxurl, postboxes, wpActiveEditor:true */
     2var ajaxWidgets, ajaxPopulateWidgets;
    33
    44jQuery(document).ready( function($) {
    55        var welcomePanel = $( '#welcome-panel' ),
    jQuery(document).ready( function($) { 
    6161
    6262        postboxes.add_postbox_toggles(pagenow, { pbshow: ajaxPopulateWidgets } );
    6363
    64         /* QuickPress */
    65         quickPressLoad = function() {
    66                 var act = $('#quickpost-action'), t;
    67 
    68                 $( '#quick-press .submit input[type="submit"], #quick-press .submit input[type="reset"]' ).prop( 'disabled' , false );
    69 
    70                 t = $('#quick-press').submit( function( e ) {
    71                         e.preventDefault();
    72                         $('#dashboard_quick_press #publishing-action .spinner').show();
    73                         $('#quick-press .submit input[type="submit"], #quick-press .submit input[type="reset"]').prop('disabled', true);
    74 
    75                         $.post( t.attr( 'action' ), t.serializeArray(), function( data ) {
    76                                 // Replace the form, and prepend the published post.
    77                                 $('#dashboard_quick_press .inside').html( data );
    78                                 $('#quick-press').removeClass('initial-form');
    79                                 quickPressLoad();
    80                                 highlightLatestPost();
    81                                 $('#title').focus();
    82                         });
    83 
    84                         function highlightLatestPost () {
    85                                 var latestPost = $('.drafts ul li').first();
    86                                 latestPost.css('background', '#fffbe5');
    87                                 setTimeout(function () {
    88                                         latestPost.css('background', 'none');
    89                                 }, 1000);
    90                         }
    91                 } );
    92 
    93                 $('#publish').click( function() { act.val( 'post-quickpress-publish' ); } );
    94 
    95                 $('#title, #tags-input, #content').each( function() {
    96                         var input = $(this), prompt = $('#' + this.id + '-prompt-text');
    97 
    98                         if ( '' === this.value ) {
    99                                 prompt.removeClass('screen-reader-text');
    100                         }
    101 
    102                         prompt.click( function() {
    103                                 $(this).addClass('screen-reader-text');
    104                                 input.focus();
    105                         });
    106 
    107                         input.blur( function() {
    108                                 if ( '' === this.value ) {
    109                                         prompt.removeClass('screen-reader-text');
    110                                 }
    111                         });
    112 
    113                         input.focus( function() {
    114                                 prompt.addClass('screen-reader-text');
    115                         });
    116                 });
    117 
    118                 $('#quick-press').on( 'click focusin', function() {
    119                         wpActiveEditor = 'content';
    120                 });
    121 
    122                 autoResizeTextarea();
    123         };
    124         quickPressLoad();
    125 
    12664        $( '.meta-box-sortables' ).sortable( 'option', 'containment', '#wpwrap' );
    12765
    12866        function autoResizeTextarea() {
    jQuery(document).ready( function($) { 
    186124                });
    187125        }
    188126
     127        autoResizeTextarea();
     128
     129});
     130
     131wp.api.loadPromise.done( function( endpoint ) {
     132        var $ = jQuery,
     133                QuickPress = {},
     134                draftsCollection;
     135
     136        /**
     137         * Models
     138         */
     139
     140        QuickPress.Models = {};
     141
     142        QuickPress.Models.Draft = wp.api.models.Post.extend( {
     143                url: endpoint.get( 'apiRoot' ) + 'wp-admin/v1/quick-drafts',
     144
     145                validate: function( attributes ) {
     146                        if ( ! attributes.title && ! attributes.content ) {
     147                                return 'no-content';
     148                        }
     149                }
     150        } );
     151
     152        /**
     153         * Collections
     154         */
     155
     156        QuickPress.Collections = {};
     157
     158        QuickPress.Collections.Drafts = wp.api.collections.Posts.extend( {
     159                model: QuickPress.Models.Draft,
     160
     161                url: QuickPress.Models.Draft.prototype.url,
     162
     163                comparator: function( a, b ) {
     164                        // Sort by date descending, date is an ISO8601 string and can be
     165                        // compared lexicographically
     166                        return a.get( 'date' ).raw < b.get( 'date' ).raw;
     167                }
     168        } );
     169
     170        /**
     171         * Collections
     172         */
     173
     174        QuickPress.Views = {};
     175
     176        QuickPress.Views.Form = wp.Backbone.View.extend( {
     177                events: {
     178                        'click :input': 'hidePromptAndFocus',
     179                        'focus :input': 'hidePrompt',
     180                        'blur :input': 'showPrompt',
     181                        reset: 'showAllPrompts',
     182                        click: 'setActiveEditor',
     183                        focusin: 'setActiveEditor',
     184                        submit: 'submit'
     185                },
     186
     187                initialize: function() {
     188                        this.showAllPrompts();
     189
     190                        this.listenTo( this.model, 'invalid', this.render );
     191                        this.listenTo( this.model, 'error', this.showSyncError );
     192                },
     193
     194                togglePrompt: function( element, visible ) {
     195                        var $input = $( element ),
     196                                hasContent = $input.val().length > 0;
     197
     198                        $( element ).siblings( '.prompt' ).toggleClass( 'screen-reader-text', ! visible || hasContent );
     199                },
     200
     201                showAllPrompts: function() {
     202                        this.$el.find( ':input' ).each( _.bind( function( i, input ) {
     203                                // Prompt toggling must be deferred because the reset event is
     204                                // fired before the input values have been cleared
     205                                _.defer( _.bind( this.togglePrompt, this, input, true ) );
     206                        }, this ) );
     207                },
     208
     209                showPrompt: function( event ) {
     210                        this.togglePrompt( event.target, true );
     211                },
     212
     213                hidePrompt: function( event ) {
     214                        this.togglePrompt( event.target, false );
     215                },
     216
     217                hidePromptAndFocus: function( event ) {
     218                        this.togglePrompt( event.target, false );
     219                        $( ':input', event.target ).focus();
     220                },
     221
     222                setActiveEditor: function() {
     223                        wpActiveEditor = 'content';
     224                },
     225
     226                showSyncError: function() {
     227                        this.syncError = true;
     228                        this.render();
     229                },
     230
     231                submit: function( event ) {
     232                        var values, isValid;
     233
     234                        delete this.syncError;
     235                        event.preventDefault();
     236
     237                        // jQuery's serializeArray returns an array of field tuples, which
     238                        // we need to transform into an object before sending to API
     239                        values = _.reduce( this.$el.serializeArray(), function( memo, field ) {
     240                                memo[ field.name ] = field.value;
     241                                return memo;
     242                        }, {} );
     243
     244                        // Ensure that by setting these fields on model that it is valid
     245                        // before proceeding with save
     246                        this.model.set( values );
     247                        isValid = this.model.isValid();
     248
     249                        // Render again after validating to ensure error notice is accurate
     250                        this.render();
     251
     252                        // Abort save attempt if invalid
     253                        if ( ! isValid ) {
     254                                return;
     255                        }
     256
     257                        // Show a spinner during the callback.
     258                        this.$el.addClass( 'is-saving' );
     259
     260                        this.model.save()
     261                                .always( _.bind( function() {
     262                                        this.$el.removeClass( 'is-saving' );
     263                                }, this ) )
     264                                .success( _.bind( function() {
     265                                        this.collection.add( this.model );
     266                                        this.model = new QuickPress.Models.Draft();
     267                                        this.el.reset();
     268                                }, this ) );
     269
     270                        // Clear any previous error messages
     271                        this.render();
     272                },
     273
     274                render: function() {
     275                        var $error = this.$el.find( '.error' ),
     276                                errorText;
     277
     278                        if ( this.model.validationError ) {
     279                                // Error via submission validation
     280                                errorText = quickPress.l10n[ this.model.validationError ];
     281                        } else if ( this.syncError ) {
     282                                // Error via API save failure
     283                                errorText = quickPress.l10n.error;
     284                        }
     285
     286                        // Error notice is only visible if error text determined
     287                        $error.toggle( !! errorText );
     288                        if ( errorText ) {
     289                                $error.html( $( '<p />', { text: errorText } ) );
     290                        }
     291                }
     292        } );
     293
     294        QuickPress.Views.DraftList = wp.Backbone.View.extend( {
     295                initialize: function() {
     296                        this.listenTo( this.collection, 'sync', this.onDraftsLoaded );
     297                },
     298
     299                onDraftsLoaded: function() {
     300                        this.listenTo( this.collection, 'add', this.renderNew );
     301                        this.render();
     302                },
     303
     304                renderNew: function() {
     305                        // Display highlight effect to first (added) item for one second
     306                        var $newEl = this.render().$el.find( 'li:first' ).addClass( 'is-new' );
     307                        setTimeout( function() {
     308                                $newEl.removeClass( 'is-new' );
     309                        }, 1000 );
     310                },
     311
     312                render: function() {
     313                        // Hide drafts list if no drafts exist
     314                        this.$el.toggle( this.collection.length > 0 );
     315
     316                        // "View All" link is visible if 5 or more drafts, since we only
     317                        // show a maximum of 5 drafts in the list
     318                        this.$el.find( '.view-all' ).toggle( this.collection.length > 0 );
     319
     320                        // If after drafts load, this could be the first render, so remove
     321                        // placeholder effect and render the first four drafts
     322                        this.$el.find( '.drafts-list' )
     323                                .removeClass( 'is-placeholder' )
     324                                .html( _.map( _.take( this.collection.models, 4 ), function( draft ) {
     325                                        return new QuickPress.Views.DraftListItem( {
     326                                                model: draft
     327                                        } ).render().el;
     328                                } ) );
     329
     330                        return this;
     331                }
     332        } );
     333
     334        QuickPress.Views.DraftListItem = wp.Backbone.View.extend( {
     335                tagName: 'li',
     336
     337                template: wp.template( 'item-quick-press-draft' ),
     338
     339                render: function() {
     340                        this.$el.html( this.template( this.model.attributes ) );
     341
     342                        return this;
     343                }
     344        } );
     345
     346        /**
     347         * Initialize
     348         */
     349
     350        // Fetch drafts
     351        draftsCollection = new QuickPress.Collections.Drafts();
     352        draftsCollection.fetch();
     353
     354        // Drafts list is initialized but not rendered until drafts load
     355        new QuickPress.Views.DraftList( {
     356                el: '#quick-press-drafts',
     357                collection: draftsCollection
     358        } );
     359
     360        new QuickPress.Views.Form( {
     361                el: '#quick-press',
     362                model: new QuickPress.Models.Draft(),
     363                collection: draftsCollection
     364        } ).render();
    189365} );
  • src/wp-includes/rest-api.php

    diff --git src/wp-includes/rest-api.php src/wp-includes/rest-api.php
    index 1a7da17..e41412f 100644
    function create_initial_rest_routes() { 
    229229        // Settings.
    230230        $controller = new WP_REST_Settings_Controller;
    231231        $controller->register_routes();
     232
     233        // Quick Drafts.
     234        $controller = new WP_REST_Quick_Drafts_Controller;
     235        $controller->register_routes();
    232236}
    233237
    234238/**
  • new file src/wp-includes/rest-api/endpoints/class-wp-rest-quick-drafts-controller.php

    diff --git src/wp-includes/rest-api/endpoints/class-wp-rest-quick-drafts-controller.php src/wp-includes/rest-api/endpoints/class-wp-rest-quick-drafts-controller.php
    new file mode 100644
    index 0000000..0084590
    - +  
     1<?php
     2/**
     3 * REST API: WP_REST_Quick_Drafts_Controller class
     4 *
     5 * @package WordPress
     6 * @subpackage REST_API
     7 * @since 4.8.0
     8 */
     9
     10/**
     11 * Core class to access dashboard Quick Drafts via the REST API.
     12 *
     13 * @since 4.8.0
     14 *
     15 * @see WP_REST_Controller
     16 */
     17class WP_REST_Quick_Drafts_Controller extends WP_REST_Posts_Controller {
     18
     19        /**
     20         * Post type.
     21         *
     22         * @since 4.8.0
     23         * @access protected
     24         * @var string
     25         */
     26        protected $post_type = 'post';
     27
     28        /**
     29         * Constructor.
     30         *
     31         * @since 4.8.0
     32         * @access public
     33         */
     34        public function __construct() {
     35                $this->namespace = 'wp-admin/v1';
     36                $this->rest_base = 'quick-drafts';
     37                $this->meta = new WP_REST_Post_Meta_Fields( $this->post_type );
     38        }
     39
     40        /**
     41         * Registers the routes for the objects of the controller.
     42         *
     43         * @since 4.8.0
     44         * @access public
     45         *
     46         * @see register_rest_route()
     47         */
     48        public function register_routes() {
     49                register_rest_route( $this->namespace, '/' . $this->rest_base, array(
     50                        array(
     51                                'methods'             => WP_REST_Server::READABLE,
     52                                'callback'            => array( $this, 'get_items' ),
     53                                'permission_callback' => array( $this, 'get_items_permissions_check' )
     54                        ),
     55                        array(
     56                                'methods'             => WP_REST_Server::CREATABLE,
     57                                'callback'            => array( $this, 'create_item' ),
     58                                'permission_callback' => array( $this, 'create_item_permissions_check' ),
     59                                'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
     60                        ),
     61                        'schema' => array( $this, 'get_public_item_schema' ),
     62                ) );
     63        }
     64
     65        /**
     66         * Checks if a given request has access to read posts.
     67         *
     68         * @since 4.8.0
     69         * @access public
     70         *
     71         * @param  WP_REST_Request $request Full details about the request.
     72         * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
     73         */
     74        public function get_items_permissions_check( $request ) {
     75                if ( ! current_user_can( 'edit_posts' ) ) {
     76                        return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit these posts.' ), array( 'status' => rest_authorization_required_code() ) );
     77                }
     78
     79                return true;
     80        }
     81
     82        /**
     83         * Retrieves a collection of posts.
     84         *
     85         * @since 4.8.0
     86         * @access public
     87         *
     88         * @param WP_REST_Request $request Full details about the request.
     89         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     90         */
     91        public function get_items( $request ) {
     92                $query_args = array(
     93                        'post_type'      => 'post',
     94                        'post_status'    => 'draft',
     95                        'author'         => get_current_user_id(),
     96                        'posts_per_page' => 4,
     97                        'orderby'        => 'modified',
     98                        'order'          => 'DESC'
     99                );
     100
     101                /**
     102                 * Filters the post query arguments for the 'Recent Drafts' dashboard widget.
     103                 *
     104                 * @since 4.4.0
     105                 *
     106                 * @param array $query_args The query arguments for the 'Recent Drafts' dashboard widget.
     107                 */
     108                $query_args = apply_filters( 'dashboard_recent_drafts_query_args', $query_args );
     109
     110                $posts_query  = new WP_Query();
     111                $query_result = $posts_query->query( $query_args );
     112
     113                $posts = array();
     114
     115                foreach ( $query_result as $post ) {
     116                        if ( ! $this->check_read_permission( $post ) ) {
     117                                continue;
     118                        }
     119
     120                        $data    = $this->prepare_item_for_response( $post, $request );
     121                        $posts[] = $this->prepare_response_for_collection( $data );
     122                }
     123
     124                $response = rest_ensure_response( $posts );
     125
     126                return $response;
     127        }
     128
     129        /**
     130         * Checks if a given request has access to create a post.
     131         *
     132         * @since 4.8.0
     133         * @access public
     134         *
     135         * @param WP_REST_Request $request Full details about the request.
     136         * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
     137         */
     138        public function create_item_permissions_check( $request ) {
     139                if ( ! current_user_can( 'edit_posts' ) ) {
     140                        return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to create new posts.' ), array( 'status' => rest_authorization_required_code() ) );
     141                }
     142
     143                return true;
     144        }
     145
     146        /**
     147         * Creates a single post.
     148         *
     149         * @since 4.8.0
     150         * @access public
     151         *
     152         * @param WP_REST_Request $request Full details about the request.
     153         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     154         */
     155        public function create_item( $request ) {
     156                $post = $this->prepare_item_for_database( $request );
     157
     158                if ( is_wp_error( $post ) ) {
     159                        return $post;
     160                }
     161
     162                $post->post_type = $this->post_type;
     163                $post_id         = wp_insert_post( $post, true );
     164
     165                if ( is_wp_error( $post_id ) ) {
     166                        if ( 'db_insert_error' === $post_id->get_error_code() ) {
     167                                $post_id->add_data( array( 'status' => 500 ) );
     168                        } else {
     169                                $post_id->add_data( array( 'status' => 400 ) );
     170                        }
     171
     172                        return $post_id;
     173                }
     174
     175                $schema = $this->get_item_schema();
     176
     177                $post = $this->get_post( $post_id );
     178
     179                $fields_update = $this->update_additional_fields_for_object( $post, $request );
     180
     181                if ( is_wp_error( $fields_update ) ) {
     182                        return $fields_update;
     183                }
     184
     185                /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
     186                do_action( "rest_insert_{$this->post_type}", $post, $request, true );
     187
     188                $request->set_param( 'context', 'edit' );
     189
     190                $response = $this->prepare_item_for_response( $post, $request );
     191                $response = rest_ensure_response( $response );
     192
     193                $response->set_status( 201 );
     194                $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $post_id ) ) );
     195
     196                return $response;
     197        }
     198
     199        /**
     200         * Prepares a single post for create.
     201         *
     202         * @since 4.8.0
     203         * @access protected
     204         *
     205         * @param WP_REST_Request $request Request object.
     206         * @return stdClass|WP_Error Post object or WP_Error.
     207         */
     208        protected function prepare_item_for_database( $request ) {
     209                $prepared_post = new stdClass;
     210
     211                $schema = $this->get_item_schema();
     212
     213                // Post title.
     214                if ( ! empty( $schema['properties']['title'] ) && is_string( $request['title'] ) ) {
     215                        $prepared_post->post_title = wp_filter_post_kses( $request['title'] );
     216                }
     217
     218                // Post content.
     219                if ( ! empty( $schema['properties']['content'] ) && is_string( $request['content'] ) ) {
     220                        $prepared_post->post_content = wp_filter_post_kses( $request['content'] );
     221                }
     222
     223                /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
     224                return apply_filters( "rest_pre_insert_{$this->post_type}", $prepared_post, $request );
     225
     226        }
     227
     228        /**
     229         * Checks if a post can be read.
     230         *
     231         * Correctly handles posts with the inherit status.
     232         *
     233         * @since 4.8.0
     234         * @access public
     235         *
     236         * @param object $post Post object.
     237         * @return bool Whether the post can be read.
     238         */
     239        public function check_read_permission( $post ) {
     240                return current_user_can( 'edit_posts' );
     241        }
     242
     243        /**
     244         * Checks if a post can be created.
     245         *
     246         * @since 4.8.0
     247         * @access protected
     248         *
     249         * @param object $post Post object.
     250         * @return bool Whether the post can be created.
     251         */
     252        protected function check_create_permission( $post ) {
     253                return current_user_can( 'edit_posts' );
     254        }
     255
     256        /**
     257         * Prepares a single post output for response.
     258         *
     259         * @since 4.8.0
     260         * @access public
     261         *
     262         * @param WP_Post         $post    Post object.
     263         * @param WP_REST_Request $request Request object.
     264         * @return WP_REST_Response Response object.
     265         */
     266        public function prepare_item_for_response( $post, $request ) {
     267                $GLOBALS['post'] = $post;
     268
     269                setup_postdata( $post );
     270
     271                $schema = $this->get_item_schema();
     272
     273                $data = array();
     274
     275                if ( ! empty( $schema['properties']['date'] ) ) {
     276                        $data['date'] = array(
     277                                'raw'      => $this->prepare_date_response( $post->post_date_gmt, $post->post_date ),
     278                                'rendered' => get_the_time( __( 'F j, Y' ), $post->ID ),
     279                        );
     280                }
     281
     282                if ( ! empty( $schema['properties']['title'] ) ) {
     283                        $data['title'] = get_the_title( $post->ID );
     284                }
     285
     286                if ( ! empty( $schema['properties']['content'] ) ) {
     287                        $data['content'] = wp_trim_words( $post->post_content, 10 );
     288                }
     289
     290                if ( ! empty( $schema['properties']['edit_link'] ) ) {
     291                        $data['edit_link'] = get_edit_post_link( $post->ID );
     292                }
     293
     294                $data    = $this->add_additional_fields_to_object( $data, $request );
     295
     296                // Wrap the data in a response object.
     297                $response = rest_ensure_response( $data );
     298
     299                $response->add_links( $this->prepare_links( $post ) );
     300
     301                /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
     302                return apply_filters( "rest_prepare_{$this->post_type}", $response, $post, $request );
     303        }
     304
     305        /**
     306         * Retrieves the post's schema, conforming to JSON Schema.
     307         *
     308         * @since 4.8.0
     309         * @access public
     310         *
     311         * @return array Item schema data.
     312         */
     313        public function get_item_schema() {
     314                $schema = array(
     315                        '$schema'    => 'http://json-schema.org/draft-04/schema#',
     316                        'title'      => $this->post_type,
     317                        'type'       => 'object',
     318                        'properties' => array(
     319                                'title'           => array(
     320                                        'description' => __( 'The title for the object.' ),
     321                                        'type'        => 'string',
     322                                        'arg_options' => array(
     323                                                'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
     324                                        ),
     325                                ),
     326                                'content'         => array(
     327                                        'description' => __( 'The truncated content for the object.' ),
     328                                        'type'        => 'string',
     329                                        'arg_options' => array(
     330                                                'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
     331                                        ),
     332                                ),
     333                                'date'            => array(
     334                                        'description' => __( 'The date the object was published.' ),
     335                                        'type'        => 'object',
     336                                        'readonly'    => true,
     337                                        'properties'  => array(
     338                                                'raw'      => array(
     339                                                        'description' => __( 'The ISO8601 compliant date the object was published.' ),
     340                                                        'type'        => 'string',
     341                                                        'format'      => 'date-time',
     342                                                        'readonly'    => true,
     343                                                ),
     344                                                'rendered' => array(
     345                                                        'description' => __( "The date the object was published, in the site's timezone." ),
     346                                                        'type'        => 'string',
     347                                                        'readonly'    => true,
     348                                                ),
     349                                        ),
     350                                ),
     351                                'edit_link'       => array(
     352                                        'description' => __( 'URL to edit the post.' ),
     353                                        'type'        => 'string',
     354                                        'readonly'    => true,
     355                                ),
     356                        ),
     357                );
     358
     359                return $this->add_additional_fields_schema( $schema );
     360        }
     361}
  • src/wp-includes/script-loader.php

    diff --git src/wp-includes/script-loader.php src/wp-includes/script-loader.php
    index 143bfa1..531bb0f 100644
    function wp_default_scripts( &$scripts ) { 
    716716                        'current' => __( 'Current Color' ),
    717717                ) );
    718718
    719                 $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox' ), false, 1 );
     719                $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox', 'wp-api', 'wp-backbone' ), false, 2 );
     720                did_action( 'init' ) && $scripts->localize( 'dashboard', 'quickPress', array(
     721                        'l10n' => array(
     722                                'no-content' => __( 'Post content cannot be empty.' ),
     723                                'error'      => __( 'An error has occurred. Please reload the page and try again.' )
     724                        )
     725                ) );
    720726
    721727                $scripts->add( 'list-revisions', "/wp-includes/js/wp-list-revisions$suffix.js" );
    722728
  • src/wp-settings.php

    diff --git src/wp-settings.php src/wp-settings.php
    index 3347cc9..054001d 100644
    require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-terms-controller.p 
    227227require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-users-controller.php' );
    228228require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-comments-controller.php' );
    229229require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-settings-controller.php' );
     230require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-quick-drafts-controller.php' );
    230231require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-meta-fields.php' );
    231232require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.php' );
    232233require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php' );