Ticket #38342: 38342.9.diff
File 38342.9.diff, 19.0 KB (added by , 8 years ago) |
---|
-
src/wp-admin/css/dashboard.css
518 518 resize: none; 519 519 } 520 520 521 #quick-press.is-saving .spinner { 522 visibility: inherit; 523 } 524 521 525 /* Dashboard Quick Draft - Drafts list */ 522 526 523 527 .js #dashboard_quick_press .drafts { … … 541 545 margin: 0 12px; 542 546 } 543 547 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 544 577 #dashboard_quick_press .drafts li { 545 578 margin-bottom: 1em; 546 579 } 580 581 #dashboard_quick_press .drafts li.is-new { 582 background-color: #fffbe5; 583 } 584 547 585 #dashboard_quick_press .drafts li time { 548 586 color: #72777c; 549 587 } -
src/wp-admin/includes/dashboard.php
50 50 if ( is_blog_admin() && current_user_can( get_post_type_object( 'post' )->cap->create_posts ) ) { 51 51 $quick_draft_title = sprintf( '<span class="hide-if-no-js">%1$s</span> <span class="hide-if-js">%2$s</span>', __( 'Quick Draft' ), __( 'Drafts' ) ); 52 52 wp_add_dashboard_widget( 'dashboard_quick_press', $quick_draft_title, 'wp_dashboard_quick_press' ); 53 add_action( 'admin_footer', 'wp_dashboard_print_recent_drafts_template' ); 53 54 } 54 55 55 56 // WordPress News … … 494 495 $post_ID = (int) $post->ID; 495 496 ?> 496 497 497 <form name="post" action="<?php echo esc_url( admin_url( 'post.php' ) ); ?>"method="post" id="quick-press" class="initial-form hide-if-no-js">498 <form name="post" method="post" id="quick-press" class="initial-form hide-if-no-js"> 498 499 499 <?php if ( $error_msg ) : ?>500 <div class="error"><?php echo $error_msg; ?></div>501 <?php endif; ?>502 503 500 <div class="input-text-wrap" id="title-wrap"> 504 <label class=" screen-reader-textprompt" for="title" id="title-prompt-text">501 <label class="prompt" for="title" id="title-prompt-text"> 505 502 506 503 <?php 507 504 /** This filter is documented in wp-admin/edit-form-advanced.php */ … … 508 505 echo apply_filters( 'enter_title_here', __( 'Title' ), $post ); 509 506 ?> 510 507 </label> 511 <input type="text" name=" post_title" id="title" autocomplete="off" />508 <input type="text" name="title" id="title" autocomplete="off" /> 512 509 </div> 513 510 514 511 <div class="textarea-wrap" id="description-wrap"> 515 <label class=" screen-reader-textprompt" for="content" id="content-prompt-text"><?php _e( 'What’s on your mind?' ); ?></label>512 <label class="prompt" for="content" id="content-prompt-text"><?php _e( 'What’s on your mind?' ); ?></label> 516 513 <textarea name="content" id="content" class="mceEditor" rows="3" cols="15" autocomplete="off"></textarea> 517 514 </div> 518 519 515 <p class="submit"> 520 <input type="hidden" name="action" id="quickpost-action" value="post-quickdraft-save" /> 521 <input type="hidden" name="post_ID" value="<?php echo $post_ID; ?>" /> 522 <input type="hidden" name="post_type" value="post" /> 523 <?php wp_nonce_field( 'add-post' ); ?> 516 <div class="error inline" style="display: none;"><p></p></div> 517 <div class="spinner no-float"></div> 524 518 <?php submit_button( __( 'Save Draft' ), 'primary', 'save', false, array( 'id' => 'save-post' ) ); ?> 525 519 <br class="clear" /> 526 520 </p> 527 521 528 522 </form> 523 <div id="quick-press-drafts" class="drafts"> 524 <p class="view-all" style="display: none;"> 525 <a href="<?php echo esc_url( admin_url( 'edit.php?post_status=draft' ) ) ?>" aria-label="<?php esc_attr_e( 'View all drafts' ) ?>"><?php _ex( 'View all', 'drafts' ) ?></a> 526 </p> 527 <h2 class="hide-if-no-js"><?php esc_html_e( 'Drafts' ) ?></h2> 528 <ul class="drafts-list is-placeholder"> 529 <li><span class="screen-reader-text"><?php esc_html_e( 'Loading…' ) ?></span></li> 530 </ul> 531 </div> 529 532 <?php 530 wp_dashboard_recent_drafts();531 533 } 532 534 533 535 /** … … 586 588 } 587 589 588 590 /** 591 * Get the HTML template for the Quick Draft recent posts. 592 * 593 * @since 4.7.0 594 * 595 * @return string The template HTML. 596 */ 597 function wp_dashboard_print_recent_drafts_template() { 598 ?> 599 <script id="tmpl-item-quick-press-draft" type="text/template"> 600 <div class="draft-title"> 601 <a href="post.php?post={{ data.id }}&action=edit" aria-label="<?php esc_attr_e( 'Edit Post' ) ?>">{{ data.title }}</a> 602 <time datetime="{{ data.date }}">{{ data.formattedDate }}</time> 603 </div> 604 {{{ data.formattedContent }}} 605 </script> 606 <?php 607 } 608 609 /** 589 610 * Outputs a row for the Recent Comments widget. 590 611 * 591 612 * @access private -
src/wp-admin/js/dashboard.js
1 /* global pagenow, ajaxurl, postboxes, wpActiveEditor:true */2 var ajaxWidgets, ajaxPopulateWidgets , quickPressLoad;1 /* global _, wp, quickPress, pagenow, ajaxurl, postboxes, wpActiveEditor:true */ 2 var ajaxWidgets, ajaxPopulateWidgets; 3 3 4 4 jQuery(document).ready( function($) { 5 5 var welcomePanel = $( '#welcome-panel' ), … … 61 61 62 62 postboxes.add_postbox_toggles(pagenow, { pbshow: ajaxPopulateWidgets } ); 63 63 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 126 64 $( '.meta-box-sortables' ).sortable( 'option', 'containment', '#wpwrap' ); 127 65 128 66 function autoResizeTextarea() { … … 186 124 }); 187 125 } 188 126 127 autoResizeTextarea(); 128 129 }); 130 131 wp.api.loadPromise.done( function() { 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 initialize: function( attributes ) { 144 if ( attributes ) { 145 this.set( this.normalizeAttributes( attributes ) ); 146 } 147 }, 148 149 parse: function( response ) { 150 return this.normalizeAttributes( response ); 151 }, 152 153 normalizeAttributes: function( attributes ) { 154 var date; 155 156 if ( ! attributes ) { 157 return attributes; 158 } 159 160 // Post entities from the REST API include the content and title in 161 // nested objects, but our new form model will assign as a string, 162 // so we normalize to simplify display logic 163 164 if ( 'object' === typeof attributes.content ) { 165 attributes.content = attributes.content.rendered; 166 } 167 168 if ( 'object' === typeof attributes.title ) { 169 attributes.title = attributes.title.rendered; 170 } 171 172 attributes.formattedContent = wp.formatting.trimWords( attributes.content, 10 ); 173 174 // We can format dates using newer browser i18n features, but also 175 // provide a fallback to the not-as-nice Date#toLocaleDateString 176 date = new Date( wp.api.utils.parseISO8601( attributes.date + quickPress.timezoneOffset ) ); 177 if ( 'undefined' !== typeof Intl && Intl.DateTimeFormat ) { 178 attributes.formattedDate = new Intl.DateTimeFormat( undefined, { 179 hour: 'numeric', 180 minute: 'numeric', 181 month: 'long', 182 day: 'numeric', 183 year: 'numeric' 184 } ).format( date ); 185 } else { 186 attributes.formattedDate = date.toLocaleDateString(); 187 } 188 189 return attributes; 190 }, 191 192 validate: function( attributes ) { 193 if ( ! attributes.title && ! attributes.content ) { 194 return 'no-content'; 195 } 196 } 197 } ); 198 199 /** 200 * Collections 201 */ 202 203 QuickPress.Collections = {}; 204 205 QuickPress.Collections.Drafts = wp.api.collections.Posts.extend( { 206 model: QuickPress.Models.Draft, 207 208 comparator: function( a, b ) { 209 // Sort by date descending, date is an ISO8601 string and can be 210 // compared lexicographically 211 return a.get( 'date' ) < b.get( 'date' ); 212 } 213 } ); 214 215 /** 216 * Collections 217 */ 218 219 QuickPress.Views = {}; 220 221 QuickPress.Views.Form = wp.Backbone.View.extend( { 222 events: { 223 'click :input': 'hidePromptAndFocus', 224 'focus :input': 'hidePrompt', 225 'blur :input': 'showPrompt', 226 reset: 'showAllPrompts', 227 click: 'setActiveEditor', 228 focusin: 'setActiveEditor', 229 submit: 'submit' 230 }, 231 232 initialize: function() { 233 this.showAllPrompts(); 234 235 this.listenTo( this.model, 'invalid', this.render ); 236 this.listenTo( this.model, 'error', this.showSyncError ); 237 }, 238 239 togglePrompt: function( element, visible ) { 240 var $input = $( element ), 241 hasContent = $input.val().length > 0; 242 243 $( element ).siblings( '.prompt' ).toggleClass( 'screen-reader-text', ! visible || hasContent ); 244 }, 245 246 showAllPrompts: function() { 247 this.$el.find( ':input' ).each( _.bind( function( i, input ) { 248 // Prompt toggling must be deferred because the reset event is 249 // fired before the input values have been cleared 250 _.defer( _.bind( this.togglePrompt, this, input, true ) ); 251 }, this ) ); 252 }, 253 254 showPrompt: function( event ) { 255 this.togglePrompt( event.target, true ); 256 }, 257 258 hidePrompt: function( event ) { 259 this.togglePrompt( event.target, false ); 260 }, 261 262 hidePromptAndFocus: function( event ) { 263 this.togglePrompt( event.target, false ); 264 $( ':input', event.target ).focus(); 265 }, 266 267 setActiveEditor: function() { 268 wpActiveEditor = 'content'; 269 }, 270 271 showSyncError: function() { 272 this.syncError = true; 273 this.render(); 274 }, 275 276 submit: function( event ) { 277 var values; 278 279 delete this.syncError; 280 event.preventDefault(); 281 282 // jQuery's serializeArray returns an array of field tuples, which 283 // we need to transform into an object before sending to API 284 values = _.reduce( this.$el.serializeArray(), function( memo, field ) { 285 memo[ field.name ] = field.value; 286 return memo; 287 }, {} ); 288 289 // Ensure that by setting these fields on model that it is valid 290 // before proceeding with save 291 this.model.set( values ); 292 if ( ! this.model.isValid() ) { 293 return; 294 } 295 296 // Show a spinner during the callback. 297 this.$el.addClass( 'is-saving' ); 298 299 this.model.save() 300 .always( _.bind( function() { 301 this.$el.removeClass( 'is-saving' ); 302 }, this ) ) 303 .success( _.bind( function() { 304 this.collection.add( this.model ); 305 this.model = new QuickPress.Models.Draft(); 306 this.el.reset(); 307 }, this ) ); 308 }, 309 310 render: function() { 311 var $error = this.$el.find( '.error' ), 312 errorText; 313 314 if ( this.model.validationError ) { 315 // Error via submission validation 316 errorText = quickPress.l10n[ this.model.validationError ]; 317 } else if ( this.syncError ) { 318 // Error via API save failure 319 errorText = quickPress.l10n.error; 320 } 321 322 // Error notice is only visible if error text determined 323 $error.toggle( !! errorText ); 324 if ( errorText ) { 325 $error.html( $( '<p />', { text: errorText } ) ); 326 } 327 } 328 } ); 329 330 QuickPress.Views.DraftList = wp.Backbone.View.extend( { 331 332 initialize: function() { 333 this.listenTo( this.collection, 'sync', this.onDraftsLoaded ); 334 }, 335 336 onDraftsLoaded: function() { 337 this.listenTo( this.collection, 'add', this.renderNew ); 338 this.render(); 339 }, 340 341 renderNew: function() { 342 // Display highlight effect to first (added) item for one second 343 var $newEl = this.render().$el.find( 'li:first' ).addClass( 'is-new' ); 344 setTimeout( function() { 345 $newEl.removeClass( 'is-new' ); 346 }, 1000 ); 347 }, 348 349 render: function() { 350 // Hide drafts list if no drafts exist 351 this.$el.toggle( this.collection.length > 0 ); 352 353 // "View All" link is visible if 5 or more drafts, since we only 354 // show a maximum of 5 drafts in the list 355 this.$el.find( '.view-all' ).toggle( this.collection.length > 0 ); 356 357 // If after drafts load, this could be the first render, so remove 358 // placeholder effect and render the first four drafts 359 this.$el.find( '.drafts-list' ) 360 .removeClass( 'is-placeholder' ) 361 .html( _.map( this.collection.models, function( draft ) { 362 return new QuickPress.Views.DraftListItem( { 363 model: draft 364 } ).render().el; 365 } ) ); 366 367 return this; 368 } 369 } ); 370 371 QuickPress.Views.DraftListItem = wp.Backbone.View.extend( { 372 tagName: 'li', 373 374 template: wp.template( 'item-quick-press-draft' ), 375 376 render: function() { 377 this.$el.html( this.template( this.model.attributes ) ); 378 379 return this; 380 } 381 } ); 382 383 /** 384 * Initialize 385 */ 386 387 // Fetch drafts 388 draftsCollection = new QuickPress.Collections.Drafts(); 389 draftsCollection.fetch( { 390 data: { 391 status: 'draft', 392 author: quickPress.currentUserId, 393 per_page: 4, 394 'quick-draft-post-list': true 395 } 396 } ); 397 398 // Drafts list is initialized but not rendered until drafts load 399 new QuickPress.Views.DraftList( { 400 el: '#quick-press-drafts', 401 collection: draftsCollection 402 } ); 403 404 new QuickPress.Views.Form( { 405 el: '#quick-press', 406 model: new QuickPress.Models.Draft(), 407 collection: draftsCollection 408 } ).render(); 189 409 } ); -
src/wp-includes/js/wp-util.js
121 121 } 122 122 }; 123 123 124 // wp.formatting 125 // ------ 126 // 127 // Tools for formatting strings 128 wp.formatting = { 129 settings: settings.formatting || {}, 130 131 /** 132 * Trims text to a certain number of words. 133 * 134 * @see wp_trim_words 135 * 136 * @param {string} text Text to trim. 137 * @param {?number} numWords Number of words (default: 55). 138 * @param {?string} more What to append if text needs to be trimmed (default: '…'). 139 * @return {string} Trimmed text. 140 */ 141 trimWords: function( text, numWords, more ) { 142 var words, separator; 143 144 if ( 'undefined' === typeof numWords ) { 145 numWords = 55; 146 } 147 148 if ( 'undefined' === typeof more ) { 149 more = wp.formatting.settings.trimWordsMore; 150 } 151 152 text = text.replace( /[\n\r\t ]+/g, ' ' ).replace( /^ | $/g, '' ); 153 154 if ( wp.formatting.settings.trimWordsByCharacter ) { 155 separator = ''; 156 } else { 157 separator = ' '; 158 } 159 160 words = text.split( separator ); 161 162 if ( words.length <= numWords ) { 163 return words.join( separator ); 164 } 165 166 return words.slice( 0, numWords ).join( separator ) + more; 167 } 168 }; 169 124 170 }(jQuery)); -
src/wp-includes/rest-api.php
164 164 add_filter( 'rest_post_dispatch', 'rest_send_allow_header', 10, 3 ); 165 165 166 166 add_filter( 'rest_pre_dispatch', 'rest_handle_options_request', 10, 3 ); 167 168 // Legacy filter for Quick Draft recent posts. 169 add_filter( 'rest_post_query', 'rest_filter_quick_draft_query', 10, 2 ); 167 170 } 168 171 169 172 /** 173 * Filter query args used by he Quick Draft recent posts list. 174 * 175 * @param array $args Key value array of query var to query value. 176 * @param WP_REST_Request $request The request used. 177 */ 178 function rest_filter_quick_draft_query( $query_args, $request ) { 179 180 // Only modify Quick Draft queries. 181 $params = $request->get_query_params(); 182 183 if ( ! isset( $params['quick-draft-post-list'] ) ) { 184 return $query_args; 185 } 186 187 /** This filter is documented in wp-admin/includes/dashboard.php */ 188 $query_args = apply_filters( 'dashboard_recent_drafts_query_args', $query_args ); 189 190 return $query_args; 191 192 } 193 194 /** 170 195 * Registers default REST API routes. 171 196 * 172 197 * @since 4.7.0 -
src/wp-includes/script-loader.php
336 336 'ajax' => array( 337 337 'url' => admin_url( 'admin-ajax.php', 'relative' ), 338 338 ), 339 'formatting' => array( 340 'trimWordsMore' => __( '…' ), 341 /* 342 * translators: If your word count is based on single characters (e.g. East Asian characters), 343 * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'. 344 * Do not translate into your own language. 345 */ 346 'trimWordsByCharacter' => strpos( _x( 'words', 'Word count type. Do not translate!' ), 'characters' ) === 0 && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ), 347 ), 339 348 ) ); 340 349 341 350 $scripts->add( 'wp-backbone', "/wp-includes/js/wp-backbone$suffix.js", array('backbone', 'wp-util'), false, 1 ); … … 721 730 'current' => __( 'Current Color' ), 722 731 ) ); 723 732 724 $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox' ), false, 1 ); 733 $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox', 'wp-api', 'wp-backbone' ), false, 1 ); 734 did_action( 'init' ) && $scripts->localize( 'dashboard', 'quickPress', array( 735 'currentUserId' => get_current_user_id(), 736 'l10n' => array( 737 'no-content' => __( 'Post content cannot be empty.' ), 738 'error' => __( 'An error has occurred. Please reload the page and try again.' ), 739 ), 740 'timezoneOffset' => ( get_option( 'gmt_offset' ) >= 0 ? '+' : '-' ) . date( 'H:i', abs( get_option( 'gmt_offset' ) * 3600 ) ), 741 ) ); 725 742 726 743 $scripts->add( 'list-revisions', "/wp-includes/js/wp-list-revisions$suffix.js" ); 727 744