Ticket #38342: 38342.16.diff
File 38342.16.diff, 21.7 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 @-webkit-keyframes loading-fade { 554 555 0% { 556 opacity: 0.5; 557 } 558 559 50% { 560 opacity: 1; 561 } 562 563 100% { 564 opacity: 0.5; 565 } 566 } 567 568 @keyframes loading-fade { 569 570 0% { 571 opacity: 0.5; 572 } 573 574 50% { 575 opacity: 1; 576 } 577 578 100% { 579 opacity: 0.5; 580 } 581 } 582 583 #dashboard_quick_press .drafts ul.is-placeholder li:before, 584 #dashboard_quick_press .drafts ul.is-placeholder li:after { 585 content: ""; 586 display: block; 587 height: 13px; 588 background: #eee; 589 -webkit-animation: loading-fade 1.6s ease-in-out infinite; 590 animation: loading-fade 1.6s ease-in-out infinite; 591 } 592 593 #dashboard_quick_press .drafts ul.is-placeholder li:before { 594 margin-bottom: 5px; 595 width: 40%; 596 } 597 598 #dashboard_quick_press .drafts ul.is-placeholder li:after { 599 width: 80%; 600 } 601 544 602 #dashboard_quick_press .drafts li { 545 603 margin-bottom: 1em; 546 604 } 605 606 #dashboard_quick_press .drafts li.is-new { 607 background-color: #fffbe5; 608 } 609 547 610 #dashboard_quick_press .drafts li time { 548 611 color: #72777c; 549 612 } -
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 499 <?php if ( $error_msg ) : ?> 500 <div class="error"><?php echo $error_msg; ?></div> 501 <?php endif; ?> 502 498 <form name="post" method="post" id="quick-press" class="initial-form hide-if-no-js"> 499 <div class="notice notice-info notice-alt inline" style="display: none;"><p></p></div> 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="spinner no-float"></div> 524 517 <?php submit_button( __( 'Save Draft' ), 'primary', 'save', false, array( 'id' => 'save-post' ) ); ?> 525 518 <br class="clear" /> 526 519 </p> 527 520 528 521 </form> 522 <div id="quick-press-drafts" class="drafts"> 523 <p class="view-all" style="display: none;"> 524 <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> 525 </p> 526 <h2 class="hide-if-no-js"><?php _e( 'Drafts' ) ?></h2> 527 <ul class="drafts-list is-placeholder"> 528 <li><span class="screen-reader-text"><?php _e( 'Loading…' ) ?></span></li> 529 </ul> 530 </div> 529 531 <?php 530 wp_dashboard_recent_drafts();531 532 } 532 533 533 534 /** 534 535 * Show recent drafts of the user on the dashboard. 535 536 * 536 537 * @since 2.7.0 538 * @deprecated 4.7 537 539 * 538 540 * @param array $drafts 539 541 */ … … 548 550 'order' => 'DESC' 549 551 ); 550 552 551 /** 552 * Filters the post query arguments for the 'Recent Drafts' dashboard widget. 553 * 554 * @since 4.4.0 555 * 556 * @param array $query_args The query arguments for the 'Recent Drafts' dashboard widget. 557 */ 553 /** This filter is documented in wp-includes/rest-api.php */ 558 554 $query_args = apply_filters( 'dashboard_recent_drafts_query_args', $query_args ); 559 555 560 556 $drafts = get_posts( $query_args ); … … 586 582 } 587 583 588 584 /** 585 * Get the HTML template for the Quick Draft recent posts. 586 * 587 * @since 4.7.0 588 * 589 * @return string The template HTML. 590 */ 591 function wp_dashboard_print_recent_drafts_template() { 592 ?> 593 <script id="tmpl-item-quick-press-draft" type="text/template"> 594 <div class="draft-title"> 595 <a href="post.php?post={{ data.id }}&action=edit" aria-label="<?php esc_attr_e( 'Edit Post' ) ?>">{{ data.formattedTitle }}</a> 596 <time datetime="{{ data.date }}">{{ data.formattedDate }}</time> 597 </div> 598 {{{ data.formattedContent }}} 599 </script> 600 <?php 601 } 602 603 /** 589 604 * Outputs a row for the Recent Comments widget. 590 605 * 591 606 * @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, QuickPress = {}; 3 3 4 4 jQuery(document).ready( function($) { 5 5 var welcomePanel = $( '#welcome-panel' ), … … 59 59 }; 60 60 ajaxPopulateWidgets(); 61 61 62 postboxes.add_postbox_toggles(pagenow, { pbshow: ajaxPopulateWidgets } ); 62 postboxes.add_postbox_toggles(pagenow, { pbshow: function( id ) { 63 ajaxPopulateWidgets(); 63 64 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(); 65 if ( 'dashboard_quick_press' === id ) { 66 QuickPress.init(); 67 } 68 } } ); 125 69 126 70 $( '.meta-box-sortables' ).sortable( 'option', 'containment', '#wpwrap' ); 127 71 … … 186 130 }); 187 131 } 188 132 133 autoResizeTextarea(); 134 135 if ( jQuery( '#dashboard_quick_press' ).is( ':visible' ) ) { 136 QuickPress.init(); 137 } 138 }); 139 140 /** 141 * Collections 142 */ 143 144 QuickPress.Views = {}; 145 146 QuickPress.Views.Form = wp.Backbone.View.extend( { 147 events: { 148 'click :input': 'hidePromptAndFocus', 149 'focus :input': 'hidePrompt', 150 'blur :input': 'showPrompt', 151 reset: 'showAllPrompts', 152 click: 'setActiveEditor', 153 focusin: 'setActiveEditor', 154 submit: 'submit' 155 }, 156 157 initialize: function() { 158 this.showAllPrompts(); 159 160 this.model.on( 'invalid', _.bind( this.render, this ) ); 161 this.model.on( 'error', _.bind( this.showSyncError, this ) ); 162 }, 163 164 togglePrompt: function( element, visible ) { 165 var $input = jQuery( element ), 166 hasContent = $input.val().length > 0; 167 168 jQuery( element ).siblings( '.prompt' ).toggleClass( 'screen-reader-text', ! visible || hasContent ); 169 }, 170 171 showAllPrompts: function() { 172 this.$el.find( ':input' ).each( _.bind( function( i, input ) { 173 // Prompt toggling must be deferred because the reset event is 174 // fired before the input values have been cleared 175 _.defer( _.bind( this.togglePrompt, this, input, true ) ); 176 }, this ) ); 177 }, 178 179 showPrompt: function( event ) { 180 this.togglePrompt( event.target, true ); 181 }, 182 183 hidePrompt: function( event ) { 184 this.togglePrompt( event.target, false ); 185 }, 186 187 hidePromptAndFocus: function( event ) { 188 this.togglePrompt( event.target, false ); 189 jQuery( ':input', event.target ).focus(); 190 }, 191 192 setActiveEditor: function() { 193 wpActiveEditor = 'content'; 194 }, 195 196 showSyncError: function( model, error ) { 197 try { 198 this.syncError = JSON.parse( error.responseText ).message; 199 } catch( e ) { 200 this.syncError = quickPress.l10n.error; 201 } 202 203 // Alert screen readers that an error occurred. 204 wp.a11y.speak( quickPress.l10n.error ); 205 206 this.render(); 207 }, 208 209 submit: function( event ) { 210 var values; 211 212 delete this.syncError; 213 event.preventDefault(); 214 215 // jQuery's serializeArray returns an array of field tuples, which 216 // we need to transform into an object before sending to API 217 values = _.reduce( this.$el.serializeArray(), function( memo, field ) { 218 memo[ field.name ] = field.value; 219 return memo; 220 }, {} ); 221 222 // Ensure that by setting these fields on model that it is valid 223 // before proceeding with save 224 this.model.set( values ); 225 if ( ! this.model.isValid() ) { 226 return; 227 } 228 229 // Show a spinner during the callback. 230 this.$el.addClass( 'is-saving' ); 231 232 this.model.save() 233 .always( _.bind( function() { 234 this.$el.removeClass( 'is-saving' ); 235 }, this ) ) 236 .success( _.bind( function() { 237 this.collection.add( this.model, { at: 0 } ); 238 this.model = new wp.api.models.Post(); 239 this.el.reset(); 240 }, this ) ); 241 }, 242 243 render: function() { 244 var $error = this.$el.find( '.notice-alt' ), 245 errorText; 246 247 if ( this.syncError ) { 248 // Error via API save failure 249 errorText = this.syncError; 250 } 251 252 // Error notice is only visible if error text determined 253 $error.toggle( !! errorText ); 254 if ( errorText ) { 255 $error.html( jQuery( '<p />', { text: errorText } ) ); 256 } 257 } 258 } ); 259 260 QuickPress.Views.DraftList = wp.Backbone.View.extend( { 261 262 initialize: function() { 263 this.listenTo( this.collection, 'sync', this.onDraftsLoaded ); 264 }, 265 266 onDraftsLoaded: function() { 267 this.listenTo( this.collection, 'add', this.renderNew ); 268 this.render(); 269 }, 270 271 renderNew: function() { 272 273 // Display highlight effect to first (added) item for one second 274 var $newEl = this.render().$el.find( 'li:first' ).addClass( 'is-new' ); 275 setTimeout( function() { 276 $newEl.removeClass( 'is-new' ); 277 }, 1000 ); 278 279 // Alert screen readers that a new draft has been added. 280 wp.a11y.speak( quickPress.l10n.newDraftCreated ); 281 }, 282 283 render: function() { 284 285 // Hide drafts list if no drafts exist 286 this.$el.toggle( this.collection.length > 0 ); 287 288 // Display a 'View All' link if there are more drafts available. 289 this.$el.find( '.view-all' ).toggle( this.collection.hasMore() ); 290 291 // Remove the placeholder class and render the models. 292 this.$el.find( '.drafts-list' ) 293 .removeClass( 'is-placeholder' ) 294 .html( _.map( this.collection.models, function( draft ) { 295 return new QuickPress.Views.DraftListItem( { 296 model: draft 297 } ).render().el; 298 } ) ); 299 300 return this; 301 } 189 302 } ); 303 304 QuickPress.Views.DraftListItem = wp.Backbone.View.extend( { 305 tagName: 'li', 306 307 template: wp.template( 'item-quick-press-draft' ), 308 309 render: function() { 310 var attributes = _.clone( this.model.attributes ); 311 attributes.formattedContent = wp.formatting.trimWords( attributes.content.rendered, 10 ); 312 attributes.formattedTitle = attributes.title.rendered.length > 0 ? attributes.title.rendered : quickPress.l10n.noTitle; 313 // We can format dates using newer browser i18n features, but also 314 // provide a fallback to the not-as-nice Date#toLocaleDateString 315 var date = new Date( wp.api.utils.parseISO8601( attributes.date + quickPress.timezoneOffset ) ); 316 if ( 'undefined' !== typeof Intl && Intl.DateTimeFormat ) { 317 attributes.formattedDate = new Intl.DateTimeFormat( undefined, { 318 month: 'long', 319 day: 'numeric', 320 year: 'numeric' 321 } ).format( date ); 322 } else { 323 attributes.formattedDate = date.toLocaleDateString(); 324 } 325 326 this.$el.html( this.template( attributes ) ); 327 328 return this; 329 } 330 } ); 331 332 QuickPress.init = function() { 333 wp.api.loadPromise.done( function() { 334 // Fetch drafts 335 var draftsCollection = new wp.api.collections.Posts(); 336 draftsCollection.fetch( { 337 data: { 338 status: 'draft', 339 author: quickPress.currentUserId, 340 per_page: 4, 341 order_by: 'date', 342 'quick-draft-post-list': true 343 } 344 } ); 345 346 // Drafts list is initialized but not rendered until drafts load 347 new QuickPress.Views.DraftList( { 348 el: '#quick-press-drafts', 349 collection: draftsCollection 350 } ); 351 352 new QuickPress.Views.Form( { 353 el: '#quick-press', 354 model: new wp.api.models.Post(), 355 collection: draftsCollection 356 } ).render(); 357 }); 358 }; -
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. Optional, default is 55. 138 * @param {string} more What to append if text needs to be trimmed. Optional, default is '…'. 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 ); 170 } 171 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 /** 188 * Filters the post query arguments for the 'Recent Drafts' dashboard widget. 189 * 190 * @since 4.4.0 191 * 192 * @param array $query_args The query arguments for the 'Recent Drafts' dashboard widget. 193 */ 194 $query_args = apply_filters( 'dashboard_recent_drafts_query_args', $query_args ); 195 196 return $query_args; 197 167 198 } 168 199 169 200 /** -
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', 'wp-a11y', 'wp-util' ), false, 1 ); 734 did_action( 'init' ) && $scripts->localize( 'dashboard', 'quickPress', array( 735 'currentUserId' => get_current_user_id(), 736 'l10n' => array( 737 'error' => __( 'An error has occurred. Please reload the page and try again.' ), 738 'newDraftCreated' => __( 'Success. A new draft was created.' ), 739 'noTitle' => __( '(no title)' ), 740 ), 741 'timezoneOffset' => ( get_option( 'gmt_offset' ) >= 0 ? '+' : '-' ) . date( 'H:i', abs( get_option( 'gmt_offset' ) * 3600 ) ), 742 ) ); 725 743 726 744 $scripts->add( 'list-revisions', "/wp-includes/js/wp-list-revisions$suffix.js" ); 727 745 -
tests/qunit/index.html
61 61 <script src="wp-admin/js/customize-base.js"></script> 62 62 <script src="wp-admin/js/customize-header.js"></script> 63 63 <script src="wp-includes/js/shortcode.js"></script> 64 <script src="wp-includes/js/wp-util.js"></script> 64 65 <script src="wp-admin/js/customize-controls.js"></script> 65 66 <script src="wp-admin/js/customize-controls-utils.js"></script> 66 67 <script src="wp-admin/js/customize-nav-menus.js"></script> -
tests/qunit/wp-includes/js/wp-util.js
1 ( function( QUnit ) { 2 wp.formatting.settings.trimWordsMore = '…'; 3 QUnit.module( 'wp-util' ); 4 var longText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce varius lacinia vehicula. Etiam sapien risus, ultricies ac posuere eu, convallis sit amet augue. Pellentesque urna massa, lacinia vel iaculis eget, bibendum in mauris. Aenean eleifend pulvinar ligula, a convallis eros gravida non. Suspendisse potenti. Pellentesque et odio tortor. In vulputate pellentesque libero, sed dapibus velit mollis viverra. Pellentesque id urna euismod dolor cursus sagittis.'; 5 6 QUnit.test( 'wp.formatting.trimWords', function( assert ) { 7 _.each( [ 8 { 9 'trimmed': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce varius lacinia vehicula. Etiam sapien risus, ultricies ac posuere eu, convallis sit amet augue. Pellentesque urna massa, lacinia vel iaculis eget, bibendum in mauris. Aenean eleifend pulvinar ligula, a convallis eros gravida non. Suspendisse potenti. Pellentesque et odio tortor. In vulputate pellentesque libero, sed dapibus velit…', 10 'text': longText, 11 'description': 'Trims to 55 by default.' 12 }, 13 { 14 'trimmed': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce varius…', 15 'text': longText, 16 'length': 10, 17 'description': 'Trims to 10.' 18 }, 19 { 20 'trimmed': 'Lorem ipsum dolor sit amet,[...] Read on!', 21 'text': longText, 22 'description': 'Trims to 5 and uses custom more.', 23 'length': 5, 24 'more': '[...] Read on!' 25 }, 26 { 27 'trimmed': 'This is some short text.', 28 'text': 'This is some short text.', 29 'description': 'Doesn\'t strip short text.', 30 } 31 32 ], function( test ) { 33 assert.equal( 34 wp.formatting.trimWords( test.text, test.length, test.more ), 35 test.trimmed, 36 test.description 37 ); 38 } ); 39 } ); 40 } )( window.QUnit );