Changeset 41376
- Timestamp:
- 09/13/2017 06:07:48 AM (7 years ago)
- Location:
- trunk
- Files:
-
- 12 added
- 21 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Gruntfile.js
r41375 r41376 361 361 'wp-includes/js/tinymce/plugins/wp*/plugin.js', 362 362 // Third party scripts 363 '!wp-includes/js/codemirror/*.js', 363 364 '!wp-admin/js/farbtastic.js', 364 365 '!wp-includes/js/backbone*.js', -
trunk/src/wp-admin/css/common.css
r41356 r41376 2201 2201 } 2202 2202 2203 #template div {2203 #template > div { 2204 2204 margin-right: 190px; 2205 2205 } … … 3016 3016 font-family: Consolas, Monaco, monospace; 3017 3017 font-size: 13px; 3018 width: 97%;3019 3018 background: #f9f9f9; 3020 3019 -moz-tab-size: 4; … … 3023 3022 } 3024 3023 3024 #template textarea, 3025 #template .CodeMirror { 3026 width: 97%; 3027 height: calc( 100vh - 220px ); 3028 } 3029 3025 3030 /* rtl:ignore */ 3026 3031 #template textarea, … … 3033 3038 } 3034 3039 3040 #file-editor-linting-error { 3041 margin-top: 1em; 3042 margin-bottom: 1em; 3043 } 3044 #file-editor-linting-error > .notice { 3045 margin: 0; 3046 display: inline-block; 3047 } 3048 #file-editor-linting-error > .notice > p { 3049 width: auto; 3050 } 3051 #template .submit { 3052 margin-top: 1em; 3053 padding: 0; 3054 } 3055 3056 #template .submit input[type=submit][disabled] { 3057 cursor: not-allowed; 3058 } 3035 3059 #templateside { 3036 3060 float: right; … … 3586 3610 } 3587 3611 3588 #template div {3612 #template > div { 3589 3613 float: none; 3590 3614 margin: 0; … … 3592 3616 } 3593 3617 3618 #template .CodeMirror, 3594 3619 #template textarea { 3595 3620 width: 100%; -
trunk/src/wp-admin/css/customize-controls.css
r41374 r41376 551 551 margin-bottom: 0; 552 552 } 553 .customize-section-description ul { 554 margin-left: 1em; 555 } 556 .customize-section-description ul > li { 557 list-style: disc; 558 } 559 .section-description-buttons { 560 text-align: right; 561 } 562 563 .section-description-buttons button.button-link { 564 color: #0073aa; 565 text-decoration: underline; 566 } 553 567 554 568 .customize-control { … … 1153 1167 width: calc( 100% + 24px ); 1154 1168 margin-bottom: -12px; 1169 } 1170 1171 .customize-section-description-container + #customize-control-custom_css:last-child .CodeMirror { 1172 height: calc( 100vh - 185px ); 1173 } 1174 .CodeMirror-lint-tooltip, 1175 .CodeMirror-hints { 1176 z-index: 500000 !important; 1177 } 1178 1179 .customize-section-description-container + #customize-control-custom_css:last-child .customize-control-notifications-container { 1180 margin-left: 12px; 1181 margin-right: 12px; 1155 1182 } 1156 1183 -
trunk/src/wp-admin/css/widgets.css
r41352 r41376 646 646 } 647 647 648 .custom-html-widget-fields > p > .CodeMirror { 649 border: 1px solid #e5e5e5; 650 } 651 .custom-html-widget-fields code { 652 padding-top: 1px; 653 padding-bottom: 1px; 654 } 655 ul.CodeMirror-hints { 656 z-index: 101; /* Due to z-index 100 set on .widget.open */ 657 } 658 .widget-control-actions .custom-html-widget-save-button.button.validation-blocked { 659 cursor: not-allowed; 660 } 661 648 662 /* =Media Queries 649 663 -------------------------------------------------------------- */ -
trunk/src/wp-admin/includes/user.php
r40940 r41376 92 92 93 93 if ( $update ) { 94 $user->rich_editing = isset( $_POST['rich_editing'] ) && 'false' == $_POST['rich_editing'] ? 'false' : 'true'; 94 $user->rich_editing = isset( $_POST['rich_editing'] ) && 'false' === $_POST['rich_editing'] ? 'false' : 'true'; 95 $user->syntax_highlighting = isset( $_POST['syntax_highlighting'] ) && 'false' === $_POST['syntax_highlighting'] ? 'false' : 'true'; 95 96 $user->admin_color = isset( $_POST['admin_color'] ) ? sanitize_text_field( $_POST['admin_color'] ) : 'fresh'; 96 97 $user->show_admin_bar_front = isset( $_POST['admin_bar_front'] ) ? 'true' : 'false'; -
trunk/src/wp-admin/js/customize-controls.js
r41374 r41376 5632 5632 }); 5633 5633 5634 // Allow tabs to be entered in Custom CSS textarea. 5635 api.control( 'custom_css', function setupCustomCssControl( control ) { 5636 control.deferred.embedded.done( function allowTabs() { 5637 var $textarea = control.container.find( 'textarea' ), textarea = $textarea[0]; 5638 5639 $textarea.on( 'blur', function onBlur() { 5640 $textarea.data( 'next-tab-blurs', false ); 5641 } ); 5642 5643 $textarea.on( 'keydown', function onKeydown( event ) { 5644 var selectionStart, selectionEnd, value, tabKeyCode = 9, escKeyCode = 27; 5645 5646 if ( escKeyCode === event.keyCode ) { 5647 if ( ! $textarea.data( 'next-tab-blurs' ) ) { 5648 $textarea.data( 'next-tab-blurs', true ); 5649 event.stopPropagation(); // Prevent collapsing the section. 5634 // Add code editor for Custom CSS. 5635 (function() { 5636 var ready, sectionReady = $.Deferred(), controlReady = $.Deferred(); 5637 5638 api.section( 'custom_css', function( section ) { 5639 section.deferred.embedded.done( function() { 5640 if ( section.expanded() ) { 5641 sectionReady.resolve( section ); 5642 } else { 5643 section.expanded.bind( function( isExpanded ) { 5644 if ( isExpanded ) { 5645 sectionReady.resolve( section ); 5646 } 5647 } ); 5648 } 5649 }); 5650 }); 5651 api.control( 'custom_css', function( control ) { 5652 control.deferred.embedded.done( function() { 5653 controlReady.resolve( control ); 5654 }); 5655 }); 5656 5657 ready = $.when( sectionReady, controlReady ); 5658 5659 // Set up the section desription behaviors. 5660 ready.done( function setupSectionDescription( section, control ) { 5661 5662 // Close the section description when clicking the close button. 5663 section.container.find( '.section-description-buttons .section-description-close' ).on( 'click', function() { 5664 section.container.find( '.section-meta .customize-section-description:first' ) 5665 .removeClass( 'open' ) 5666 .slideUp() 5667 .attr( 'aria-expanded', 'false' ); 5668 }); 5669 5670 // Reveal help text if setting is empty. 5671 if ( ! control.setting.get() ) { 5672 section.container.find( '.section-meta .customize-section-description:first' ) 5673 .addClass( 'open' ) 5674 .show() 5675 .attr( 'aria-expanded', 'true' ); 5676 } 5677 }); 5678 5679 // Set up the code editor itself. 5680 if ( api.settings.customCss && api.settings.customCss.codeEditor ) { 5681 5682 // Set up the syntax highlighting editor. 5683 ready.done( function setupSyntaxHighlightingEditor( section, control ) { 5684 var $textarea = control.container.find( 'textarea' ), settings, suspendEditorUpdate = false; 5685 5686 // Make sure editor gets focused when control is focused. 5687 control.focus = (function( originalFocus ) { // eslint-disable-line max-nested-callbacks 5688 return function( params ) { // eslint-disable-line max-nested-callbacks 5689 var extendedParams = _.extend( {}, params ), originalCompleteCallback; 5690 originalCompleteCallback = extendedParams.completeCallback; 5691 extendedParams.completeCallback = function() { 5692 if ( originalCompleteCallback ) { 5693 originalCompleteCallback(); 5694 } 5695 if ( control.editor ) { 5696 control.editor.codemirror.focus(); 5697 } 5698 }; 5699 originalFocus.call( this, extendedParams ); 5700 }; 5701 })( control.focus ); 5702 5703 settings = _.extend( {}, api.settings.customCss.codeEditor, { 5704 5705 /** 5706 * Handle tabbing to the field after the editor. 5707 * 5708 * @returns {void} 5709 */ 5710 onTabNext: function onTabNext() { 5711 var controls, controlIndex; 5712 controls = section.controls(); 5713 controlIndex = controls.indexOf( control ); 5714 if ( controls.length === controlIndex + 1 ) { 5715 $( '#customize-footer-actions .collapse-sidebar' ).focus(); 5716 } else { 5717 controls[ controlIndex + 1 ].container.find( ':focusable:first' ).focus(); 5718 } 5719 }, 5720 5721 /** 5722 * Handle tabbing to the field before the editor. 5723 * 5724 * @returns {void} 5725 */ 5726 onTabPrevious: function onTabPrevious() { 5727 var controls, controlIndex; 5728 controls = section.controls(); 5729 controlIndex = controls.indexOf( control ); 5730 if ( 0 === controlIndex ) { 5731 section.contentContainer.find( '.customize-section-title .customize-help-toggle, .customize-section-title .customize-section-description.open .section-description-close' ).last().focus(); 5732 } else { 5733 controls[ controlIndex - 1 ].contentContainer.find( ':focusable:first' ).focus(); 5734 } 5735 }, 5736 5737 /** 5738 * Update error notice. 5739 * 5740 * @param {Array} errorAnnotations - Error annotations. 5741 * @returns {void} 5742 */ 5743 onUpdateErrorNotice: function onUpdateErrorNotice( errorAnnotations ) { 5744 var message; 5745 control.setting.notifications.remove( 'csslint_error' ); 5746 5747 if ( 0 !== errorAnnotations.length ) { 5748 if ( 1 === errorAnnotations.length ) { 5749 message = api.l10n.customCssError.singular.replace( '%d', '1' ); 5750 } else { 5751 message = api.l10n.customCssError.plural.replace( '%d', String( errorAnnotations.length ) ); 5752 } 5753 control.setting.notifications.add( 'csslint_error', new api.Notification( 'csslint_error', { 5754 message: message, 5755 type: 'error' 5756 } ) ); 5757 } 5650 5758 } 5651 return; 5652 } 5653 5654 // Short-circuit if tab key is not being pressed or if a modifier key *is* being pressed. 5655 if ( tabKeyCode !== event.keyCode || event.ctrlKey || event.altKey || event.shiftKey ) { 5656 return; 5657 } 5658 5659 // Prevent capturing Tab characters if Esc was pressed. 5660 if ( $textarea.data( 'next-tab-blurs' ) ) { 5661 return; 5662 } 5663 5664 selectionStart = textarea.selectionStart; 5665 selectionEnd = textarea.selectionEnd; 5666 value = textarea.value; 5667 5668 if ( selectionStart >= 0 ) { 5669 textarea.value = value.substring( 0, selectionStart ).concat( '\t', value.substring( selectionEnd ) ); 5670 $textarea.selectionStart = textarea.selectionEnd = selectionStart + 1; 5671 } 5672 5673 event.stopPropagation(); 5674 event.preventDefault(); 5675 } ); 5676 } ); 5677 } ); 5759 }); 5760 5761 control.editor = wp.codeEditor.initialize( $textarea, settings ); 5762 5763 // Refresh when receiving focus. 5764 control.editor.codemirror.on( 'focus', function( codemirror ) { 5765 codemirror.refresh(); 5766 }); 5767 5768 /* 5769 * When the CodeMirror instance changes, mirror to the textarea, 5770 * where we have our "true" change event handler bound. 5771 */ 5772 control.editor.codemirror.on( 'change', function( codemirror ) { 5773 suspendEditorUpdate = true; 5774 $textarea.val( codemirror.getValue() ).trigger( 'change' ); 5775 suspendEditorUpdate = false; 5776 }); 5777 5778 // Update CodeMirror when the setting is changed by another plugin. 5779 control.setting.bind( function( value ) { 5780 if ( ! suspendEditorUpdate ) { 5781 control.editor.codemirror.setValue( value ); 5782 } 5783 }); 5784 5785 // Prevent collapsing section when hitting Esc to tab out of editor. 5786 control.editor.codemirror.on( 'keydown', function onKeydown( codemirror, event ) { 5787 var escKeyCode = 27; 5788 if ( escKeyCode === event.keyCode ) { 5789 event.stopPropagation(); 5790 } 5791 }); 5792 }); 5793 } else { 5794 5795 // Allow tabs to be entered in Custom CSS textarea. 5796 ready.done( function allowTabs( section, control ) { 5797 5798 var $textarea = control.container.find( 'textarea' ), textarea = $textarea[0]; 5799 5800 $textarea.on( 'blur', function onBlur() { 5801 $textarea.data( 'next-tab-blurs', false ); 5802 } ); 5803 5804 $textarea.on( 'keydown', function onKeydown( event ) { 5805 var selectionStart, selectionEnd, value, tabKeyCode = 9, escKeyCode = 27; 5806 5807 if ( escKeyCode === event.keyCode ) { 5808 if ( ! $textarea.data( 'next-tab-blurs' ) ) { 5809 $textarea.data( 'next-tab-blurs', true ); 5810 event.stopPropagation(); // Prevent collapsing the section. 5811 } 5812 return; 5813 } 5814 5815 // Short-circuit if tab key is not being pressed or if a modifier key *is* being pressed. 5816 if ( tabKeyCode !== event.keyCode || event.ctrlKey || event.altKey || event.shiftKey ) { 5817 return; 5818 } 5819 5820 // Prevent capturing Tab characters if Esc was pressed. 5821 if ( $textarea.data( 'next-tab-blurs' ) ) { 5822 return; 5823 } 5824 5825 selectionStart = textarea.selectionStart; 5826 selectionEnd = textarea.selectionEnd; 5827 value = textarea.value; 5828 5829 if ( selectionStart >= 0 ) { 5830 textarea.value = value.substring( 0, selectionStart ).concat( '\t', value.substring( selectionEnd ) ); 5831 $textarea.selectionStart = textarea.selectionEnd = selectionStart + 1; 5832 } 5833 5834 event.stopPropagation(); 5835 event.preventDefault(); 5836 }); 5837 }); 5838 } 5839 })(); 5678 5840 5679 5841 // Toggle visibility of Header Video notice when active state change. -
trunk/src/wp-admin/plugin-editor.php
r38745 r41376 116 116 117 117 // List of allowable extensions 118 $editable_extensions = array('php', 'txt', 'text', 'js', 'css', 'html', 'htm', 'xml', 'inc', 'include'); 118 $editable_extensions = array( 119 'bash', 120 'conf', 121 'css', 122 'diff', 123 'htm', 124 'html', 125 'http', 126 'inc', 127 'include', 128 'js', 129 'json', 130 'jsx', 131 'less', 132 'md', 133 'patch', 134 'php', 135 'php3', 136 'php4', 137 'php5', 138 'php7', 139 'phps', 140 'phtml', 141 'sass', 142 'scss', 143 'sh', 144 'sql', 145 'svg', 146 'text', 147 'txt', 148 'xml', 149 'yaml', 150 'yml', 151 ); 119 152 120 153 /** … … 157 190 '<p>' . __('<a href="https://wordpress.org/support/">Support Forums</a>') . '</p>' 158 191 ); 192 193 $settings = wp_enqueue_code_editor( array( 'file' => $real_file ) ); 194 if ( ! empty( $settings ) ) { 195 wp_enqueue_script( 'wp-theme-plugin-editor' ); 196 wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function() { wp.themePluginEditor.init( %s ); } )', wp_json_encode( $settings ) ) ); 197 } 159 198 160 199 require_once(ABSPATH . 'wp-admin/admin-header.php'); -
trunk/src/wp-admin/theme-editor.php
r38722 r41376 64 64 $allowed_files = $style_files = array(); 65 65 $has_templates = false; 66 $default_types = array( 'php', 'css' ); 66 $default_types = array( 67 'bash', 68 'conf', 69 'css', 70 'diff', 71 'htm', 72 'html', 73 'http', 74 'inc', 75 'include', 76 'js', 77 'json', 78 'jsx', 79 'less', 80 'md', 81 'patch', 82 'php', 83 'php3', 84 'php4', 85 'php5', 86 'php7', 87 'phps', 88 'phtml', 89 'sass', 90 'scss', 91 'sh', 92 'sql', 93 'svg', 94 'text', 95 'txt', 96 'xml', 97 'yaml', 98 'yml', 99 ); 67 100 68 101 /** … … 126 159 127 160 default: 161 162 $settings = wp_enqueue_code_editor( compact( 'file' ) ); 163 if ( ! empty( $settings ) ) { 164 wp_enqueue_script( 'wp-theme-plugin-editor' ); 165 wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function() { wp.themePluginEditor.init( %s ); } )', wp_json_encode( $settings ) ) ); 166 } 128 167 129 168 require_once( ABSPATH . 'wp-admin/admin-header.php' ); -
trunk/src/wp-admin/user-edit.php
r41163 r41376 246 246 <th scope="row"><?php _e( 'Visual Editor' ); ?></th> 247 247 <td><label for="rich_editing"><input name="rich_editing" type="checkbox" id="rich_editing" value="false" <?php if ( ! empty( $profileuser->rich_editing ) ) checked( 'false', $profileuser->rich_editing ); ?> /> <?php _e( 'Disable the visual editor when writing' ); ?></label></td> 248 </tr> 249 <?php endif; ?> 250 <?php 251 $show_syntax_highlighting_preference = ( 252 // For Custom HTML widget and Additional CSS in Customizer. 253 user_can( $profileuser, 'edit_theme_options' ) 254 || 255 // Edit plugins. 256 user_can( $profileuser, 'edit_plugins' ) 257 || 258 // Edit themes. 259 user_can( $profileuser, 'edit_themes' ) 260 ); 261 ?> 262 <?php if ( $show_syntax_highlighting_preference ) : ?> 263 <tr class="user-syntax-highlighting-wrap"> 264 <th scope="row"><?php _e( 'Syntax Highlighting' ); ?></th> 265 <td> 266 <label for="syntax_highlighting"><input name="syntax_highlighting" type="checkbox" id="syntax_highlighting" value="false" <?php if ( ! empty( $profileuser->syntax_highlighting ) ) checked( 'false', $profileuser->syntax_highlighting ); ?> /> <?php _e( 'Disable syntax highlighting when editing code' ); ?></label> 267 </td> 248 268 </tr> 249 269 <?php endif; ?> -
trunk/src/wp-includes/author-template.php
r40952 r41376 129 129 * - plugins_per_page 130 130 * - rich_editing 131 * - syntax_highlighting 131 132 * - user_activation_key 132 133 * - user_description -
trunk/src/wp-includes/class-wp-customize-manager.php
r41374 r41376 212 212 */ 213 213 private $_changeset_data; 214 215 /** 216 * Code Editor Settings for Custom CSS. 217 * 218 * This variable contains the settings returned by `wp_enqueue_code_editor()` which are then later output 219 * to the client in `WP_Customize_Manager::customize_pane_settings()`. A value of false means that the 220 * Custom CSS section or control was removed, or that the Syntax Highlighting user pref was turned off. 221 * 222 * @see wp_enqueue_code_editor() 223 * @see WP_Customize_Manager::enqueue_control_scripts() 224 * @see WP_Customize_Manager::customize_pane_settings() 225 * @since 4.9.0 226 * @var array|false 227 */ 228 private $_custom_css_code_editor_settings = false; 214 229 215 230 /** … … 3323 3338 $control->enqueue(); 3324 3339 } 3340 3341 if ( $this->get_section( 'custom_css' ) && $this->get_control( 'custom_css' ) ) { 3342 $this->_custom_css_code_editor_settings = wp_enqueue_code_editor( array( 3343 'type' => 'text/css', 3344 ) ); 3345 } 3325 3346 } 3326 3347 … … 3579 3600 'stylesheet' => $this->get_stylesheet(), 3580 3601 'active' => $this->is_theme_active(), 3602 ), 3603 'customCss' => array( 3604 'codeEditor' => $this->_custom_css_code_editor_settings, 3581 3605 ), 3582 3606 'url' => array( … … 4178 4202 4179 4203 /* Custom CSS */ 4204 $section_description = '<p>'; 4205 $section_description .= __( 'Add your own CSS code here to customize the appearance and layout of your site.', 'better-code-editing' ); 4206 $section_description .= sprintf( 4207 ' <a href="%1$s" class="external-link" target="_blank">%2$s<span class="screen-reader-text">%3$s</span></a>', 4208 esc_url( __( 'https://codex.wordpress.org/CSS', 'default' ) ), 4209 __( 'Learn more about CSS', 'default' ), 4210 /* translators: accessibility text */ 4211 __( '(opens in a new window)', 'default' ) 4212 ); 4213 $section_description .= '</p>'; 4214 4215 $section_description .= '<p>' . __( 'When using a keyboard to navigate:', 'better-code-editing' ) . '</p>'; 4216 $section_description .= '<ul>'; 4217 $section_description .= '<li>' . __( 'In the CSS edit field, Tab enters a tab character.', 'better-code-editing' ) . '</li>'; 4218 $section_description .= '<li>' . __( 'To move keyboard focus, press Esc then Tab for the next element, or Esc then Shift+Tab for the previous element.', 'better-code-editing' ) . '</li>'; 4219 $section_description .= '</ul>'; 4220 4221 if ( 'false' !== wp_get_current_user()->syntax_highlighting ) { 4222 $section_description .= '<p>'; 4223 $section_description .= sprintf( 4224 /* translators: placeholder is link to user profile */ 4225 __( 'The edit field automatically highlights code syntax. You can disable this in your %s to work in plain text mode.', 'better-code-editing' ), 4226 sprintf( 4227 ' <a href="%1$s" class="external-link" target="_blank">%2$s<span class="screen-reader-text">%3$s</span></a>', 4228 esc_url( get_edit_profile_url() . '#syntax_highlighting' ), 4229 __( 'user profile', 'better-code-editing' ), 4230 /* translators: accessibility text */ 4231 __( '(opens in a new window)', 'default' ) 4232 ) 4233 ); 4234 $section_description .= '</p>'; 4235 } 4236 4237 $section_description .= '<p class="section-description-buttons">'; 4238 $section_description .= '<button type="button" class="button-link section-description-close">' . __( 'Close', 'default' ) . '</button>'; 4239 $section_description .= '</p>'; 4240 4180 4241 $this->add_section( 'custom_css', array( 4181 4242 'title' => __( 'Additional CSS' ), 4182 4243 'priority' => 200, 4183 4244 'description_hidden' => true, 4184 'description' => sprintf( '%s<br /><a href="%s" class="external-link" target="_blank">%s<span class="screen-reader-text">%s</span></a>', 4185 __( 'CSS allows you to customize the appearance and layout of your site with code. Separate CSS is saved for each of your themes. In the editing area the Tab key enters a tab character. To move below this area by pressing Tab, press the Esc key followed by the Tab key.' ), 4186 esc_url( __( 'https://codex.wordpress.org/CSS' ) ), 4187 __( 'Learn more about CSS' ), 4188 /* translators: accessibility text */ 4189 __( '(opens in a new window)' ) 4190 ), 4245 'description' => $section_description, 4191 4246 ) ); 4192 4247 4193 4248 $custom_css_setting = new WP_Customize_Custom_CSS_Setting( $this, sprintf( 'custom_css[%s]', get_stylesheet() ), array( 4194 4249 'capability' => 'edit_css', 4195 'default' => sprintf( "/*\n%s\n*/", __( "You can add your own CSS here.\n\nClick the help icon above to learn more." ) ),4250 'default' => '', 4196 4251 ) ); 4197 4252 $this->add_setting( $custom_css_setting ); -
trunk/src/wp-includes/class-wp-user.php
r41366 r41376 33 33 * @property string $deleted 34 34 * @property string $locale 35 * @property string $rich_editing 36 * @property string $syntax_highlighting 35 37 */ 36 38 class WP_User { -
trunk/src/wp-includes/customize/class-wp-customize-custom-css-setting.php
r41162 r41376 149 149 * Notifications are rendered when the customizer state is saved. 150 150 * 151 * @todo There are cases where valid CSS can be incorrectly marked as invalid when strings or comments include balancing characters. To fix, CSS tokenization needs to be used.152 *153 151 * @since 4.7.0 152 * @since 4.9.0 Checking for balanced characters has been moved client-side via linting in code editor. 154 153 * 155 154 * @param string $css The input string. … … 161 160 if ( preg_match( '#</?\w+#', $css ) ) { 162 161 $validity->add( 'illegal_markup', __( 'Markup is not allowed in CSS.' ) ); 163 }164 165 $imbalanced = false;166 167 // Make sure that there is a closing brace for each opening brace.168 if ( ! $this->validate_balanced_characters( '{', '}', $css ) ) {169 $validity->add( 'imbalanced_curly_brackets', sprintf(170 /* translators: 1: {}, 2: }, 3: { */171 __( 'Your curly brackets %1$s are imbalanced. Make sure there is a closing %2$s for every opening %3$s.' ),172 '<code>{}</code>',173 '<code>}</code>',174 '<code>{</code>'175 ) );176 $imbalanced = true;177 }178 179 // Ensure brackets are balanced.180 if ( ! $this->validate_balanced_characters( '[', ']', $css ) ) {181 $validity->add( 'imbalanced_braces', sprintf(182 /* translators: 1: [], 2: ], 3: [ */183 __( 'Your brackets %1$s are imbalanced. Make sure there is a closing %2$s for every opening %3$s.' ),184 '<code>[]</code>',185 '<code>]</code>',186 '<code>[</code>'187 ) );188 $imbalanced = true;189 }190 191 // Ensure parentheses are balanced.192 if ( ! $this->validate_balanced_characters( '(', ')', $css ) ) {193 $validity->add( 'imbalanced_parentheses', sprintf(194 /* translators: 1: (), 2: ), 3: ( */195 __( 'Your parentheses %1$s are imbalanced. Make sure there is a closing %2$s for every opening %3$s.' ),196 '<code>()</code>',197 '<code>)</code>',198 '<code>(</code>'199 ) );200 $imbalanced = true;201 }202 203 // Ensure double quotes are equal.204 if ( ! $this->validate_equal_characters( '"', $css ) ) {205 $validity->add( 'unequal_double_quotes', sprintf(206 /* translators: 1: " (double quote) */207 __( 'Your double quotes %1$s are uneven. Make sure there is a closing %1$s for every opening %1$s.' ),208 '<code>"</code>'209 ) );210 $imbalanced = true;211 }212 213 /*214 * Make sure any code comments are closed properly.215 *216 * The first check could miss stray an unpaired comment closing figure, so if217 * The number appears to be balanced, then check for equal numbers218 * of opening/closing comment figures.219 *220 * Although it may initially appear redundant, we use the first method221 * to give more specific feedback to the user.222 */223 $unclosed_comment_count = $this->validate_count_unclosed_comments( $css );224 if ( 0 < $unclosed_comment_count ) {225 $validity->add( 'unclosed_comment', sprintf(226 /* translators: 1: number of unclosed comments, 2: */ */227 _n(228 'There is %1$s unclosed code comment. Close each comment with %2$s.',229 'There are %1$s unclosed code comments. Close each comment with %2$s.',230 $unclosed_comment_count231 ),232 $unclosed_comment_count,233 '<code>*/</code>'234 ) );235 $imbalanced = true;236 } elseif ( ! $this->validate_balanced_characters( '/*', '*/', $css ) ) {237 $validity->add( 'imbalanced_comments', sprintf(238 /* translators: 1: */, 2: /* */239 __( 'There is an extra %1$s, indicating an end to a comment. Be sure that there is an opening %2$s for every closing %1$s.' ),240 '<code>*/</code>',241 '<code>/*</code>'242 ) );243 $imbalanced = true;244 }245 if ( $imbalanced && $this->is_possible_content_error( $css ) ) {246 $validity->add( 'possible_false_positive', sprintf(247 /* translators: %s: content: ""; */248 __( 'Imbalanced/unclosed character errors can be caused by %s declarations. You may need to remove this or add it to a custom CSS file.' ),249 '<code>content: "";</code>'250 ) );251 162 } 252 163 … … 286 197 return $post_id; 287 198 } 288 289 /**290 * Ensure there are a balanced number of paired characters.291 *292 * This is used to check that the number of opening and closing293 * characters is equal.294 *295 * For instance, there should be an equal number of braces ("{", "}")296 * in the CSS.297 *298 * @since 4.7.0299 *300 * @param string $opening_char The opening character.301 * @param string $closing_char The closing character.302 * @param string $css The CSS input string.303 *304 * @return bool305 */306 private function validate_balanced_characters( $opening_char, $closing_char, $css ) {307 return substr_count( $css, $opening_char ) === substr_count( $css, $closing_char );308 }309 310 /**311 * Ensure there are an even number of paired characters.312 *313 * This is used to check that the number of a specific314 * character is even.315 *316 * For instance, there should be an even number of double quotes317 * in the CSS.318 *319 * @since 4.7.0320 *321 * @param string $char A character.322 * @param string $css The CSS input string.323 * @return bool Equality.324 */325 private function validate_equal_characters( $char, $css ) {326 $char_count = substr_count( $css, $char );327 return ( 0 === $char_count % 2 );328 }329 330 /**331 * Count unclosed CSS Comments.332 *333 * Used during validation.334 *335 * @see self::validate()336 *337 * @since 4.7.0338 *339 * @param string $css The CSS input string.340 * @return int Count.341 */342 private function validate_count_unclosed_comments( $css ) {343 $count = 0;344 $comments = explode( '/*', $css );345 346 if ( ! is_array( $comments ) || ( 1 >= count( $comments ) ) ) {347 return $count;348 }349 350 unset( $comments[0] ); // The first item is before the first comment.351 foreach ( $comments as $comment ) {352 if ( false === strpos( $comment, '*/' ) ) {353 $count++;354 }355 }356 return $count;357 }358 359 /**360 * Find "content:" within a string.361 *362 * Imbalanced/Unclosed validation errors may be caused363 * when a character is used in a "content:" declaration.364 *365 * This function is used to detect if this is a possible366 * cause of the validation error, so that if it is,367 * a notification may be added to the Validation Errors.368 *369 * Example:370 * .element::before {371 * content: "(\"";372 * }373 * .element::after {374 * content: "\")";375 * }376 *377 * Using ! empty() because strpos() may return non-boolean values378 * that evaluate to false. This would be problematic when379 * using a strict "false === strpos()" comparison.380 *381 * @since 4.7.0382 *383 * @param string $css The CSS input string.384 * @return bool385 */386 private function is_possible_content_error( $css ) {387 $found = preg_match( '/\bcontent\s*:/', $css );388 if ( ! empty( $found ) ) {389 return true;390 }391 return false;392 }393 199 } -
trunk/src/wp-includes/general-template.php
r41371 r41376 3117 3117 3118 3118 /** 3119 * Enqueue assets needed by the code editor for the given settings. 3120 * 3121 * @since 4.9.0 3122 * 3123 * @see wp_enqueue_editor() 3124 * @see _WP_Editors::parse_settings() 3125 * @param array $args { 3126 * Args. 3127 * 3128 * @type string $type The MIME type of the file to be edited. 3129 * @type string $file Filename to be edited. Extension is used to sniff the type. Can be supplied as alternative to `$type` param. 3130 * @type array $settings Settings to merge on top of defaults which derive from `$type` or `$file` args. 3131 * @type WP_Theme $theme Theme being edited when on theme editor. 3132 * @type string $plugin Plugin being edited when on plugin editor. 3133 * } 3134 * @returns array|false Settings for the enqueued code editor, or false if the editor was not enqueued . 3135 */ 3136 function wp_enqueue_code_editor( $args ) { 3137 if ( is_user_logged_in() && 'false' === wp_get_current_user()->syntax_highlighting ) { 3138 return false; 3139 } 3140 3141 $settings = array( 3142 'codemirror' => array( 3143 'indentUnit' => 4, 3144 'indentWithTabs' => true, 3145 'inputStyle' => 'contenteditable', 3146 'lineNumbers' => true, 3147 'lineWrapping' => true, 3148 'styleActiveLine' => true, 3149 'continueComments' => true, 3150 'extraKeys' => array( 3151 'Ctrl-Space' => 'autocomplete', 3152 'Ctrl-/' => 'toggleComment', 3153 'Cmd-/' => 'toggleComment', 3154 'Alt-F' => 'findPersistent', 3155 ), 3156 'direction' => 'ltr', // Code is shown in LTR even in RTL languages. 3157 ), 3158 'csslint' => array( 3159 'errors' => true, // Parsing errors. 3160 'box-model' => true, 3161 'display-property-grouping' => true, 3162 'duplicate-properties' => true, 3163 'known-properties' => true, 3164 'outline-none' => true, 3165 ), 3166 'jshint' => array( 3167 // The following are copied from <https://github.com/WordPress/wordpress-develop/blob/4.8.1/.jshintrc>. 3168 'boss' => true, 3169 'curly' => true, 3170 'eqeqeq' => true, 3171 'eqnull' => true, 3172 'es3' => true, 3173 'expr' => true, 3174 'immed' => true, 3175 'noarg' => true, 3176 'nonbsp' => true, 3177 'onevar' => true, 3178 'quotmark' => 'single', 3179 'trailing' => true, 3180 'undef' => true, 3181 'unused' => true, 3182 3183 'browser' => true, 3184 3185 'globals' => array( 3186 '_' => false, 3187 'Backbone' => false, 3188 'jQuery' => false, 3189 'JSON' => false, 3190 'wp' => false, 3191 ), 3192 ), 3193 'htmlhint' => array( 3194 'tagname-lowercase' => true, 3195 'attr-lowercase' => true, 3196 'attr-value-double-quotes' => true, 3197 'doctype-first' => false, 3198 'tag-pair' => true, 3199 'spec-char-escape' => true, 3200 'id-unique' => true, 3201 'src-not-empty' => true, 3202 'attr-no-duplication' => true, 3203 'alt-require' => true, 3204 'space-tab-mixed-disabled' => 'tab', 3205 'attr-unsafe-chars' => true, 3206 ), 3207 ); 3208 3209 $type = ''; 3210 if ( isset( $args['type'] ) ) { 3211 $type = $args['type']; 3212 3213 // Remap MIME types to ones that CodeMirror modes will recognize. 3214 if ( 'application/x-patch' === $type || 'text/x-patch' === $type ) { 3215 $type = 'text/x-diff'; 3216 } 3217 } elseif ( isset( $args['file'] ) && false !== strpos( basename( $args['file'] ), '.' ) ) { 3218 $extension = strtolower( pathinfo( $args['file'], PATHINFO_EXTENSION ) ); 3219 foreach ( wp_get_mime_types() as $exts => $mime ) { 3220 if ( preg_match( '!^(' . $exts . ')$!i', $extension ) ) { 3221 $type = $mime; 3222 break; 3223 } 3224 } 3225 3226 // Supply any types that are not matched by wp_get_mime_types(). 3227 if ( empty( $type ) ) { 3228 switch ( $extension ) { 3229 case 'conf': 3230 $type = 'text/nginx'; 3231 break; 3232 case 'css': 3233 $type = 'text/css'; 3234 break; 3235 case 'diff': 3236 case 'patch': 3237 $type = 'text/x-diff'; 3238 break; 3239 case 'html': 3240 case 'htm': 3241 $type = 'text/html'; 3242 break; 3243 case 'http': 3244 $type = 'message/http'; 3245 break; 3246 case 'js': 3247 $type = 'text/javascript'; 3248 break; 3249 case 'json': 3250 $type = 'application/json'; 3251 break; 3252 case 'jsx': 3253 $type = 'text/jsx'; 3254 break; 3255 case 'less': 3256 $type = 'text/x-less'; 3257 break; 3258 case 'md': 3259 $type = 'text/x-gfm'; 3260 break; 3261 case 'php': 3262 case 'phtml': 3263 case 'php3': 3264 case 'php4': 3265 case 'php5': 3266 case 'php7': 3267 case 'phps': 3268 $type = 'application/x-httpd-php'; 3269 break; 3270 case 'scss': 3271 $type = 'text/x-scss'; 3272 break; 3273 case 'sass': 3274 $type = 'text/x-sass'; 3275 break; 3276 case 'sh': 3277 case 'bash': 3278 $type = 'text/x-sh'; 3279 break; 3280 case 'sql': 3281 $type = 'text/x-sql'; 3282 break; 3283 case 'svg': 3284 $type = 'application/svg+xml'; 3285 break; 3286 case 'xml': 3287 $type = 'text/xml'; 3288 break; 3289 case 'yml': 3290 case 'yaml': 3291 $type = 'text/x-yaml'; 3292 break; 3293 case 'txt': 3294 default: 3295 $type = 'text/plain'; 3296 break; 3297 } 3298 } 3299 } 3300 3301 if ( 'text/css' === $type ) { 3302 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3303 'mode' => 'css', 3304 'lint' => true, 3305 'autoCloseBrackets' => true, 3306 'matchBrackets' => true, 3307 ) ); 3308 } elseif ( 'text/x-scss' === $type || 'text/x-less' === $type || 'text/x-sass' === $type ) { 3309 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3310 'mode' => $type, 3311 'autoCloseBrackets' => true, 3312 'matchBrackets' => true, 3313 ) ); 3314 } elseif ( 'text/x-diff' === $type ) { 3315 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3316 'mode' => 'diff', 3317 ) ); 3318 } elseif ( 'text/html' === $type ) { 3319 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3320 'mode' => 'htmlmixed', 3321 'lint' => true, 3322 'autoCloseBrackets' => true, 3323 'autoCloseTags' => true, 3324 'matchTags' => array( 3325 'bothTags' => true, 3326 ), 3327 ) ); 3328 3329 if ( ! current_user_can( 'unfiltered_html' ) ) { 3330 $settings['htmlhint']['kses'] = wp_kses_allowed_html( 'post' ); 3331 } 3332 } elseif ( 'text/x-gfm' === $type ) { 3333 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3334 'mode' => 'gfm', 3335 'highlightFormatting' => true, 3336 ) ); 3337 } elseif ( 'application/javascript' === $type || 'text/javascript' === $type ) { 3338 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3339 'mode' => 'javascript', 3340 'lint' => true, 3341 'autoCloseBrackets' => true, 3342 'matchBrackets' => true, 3343 ) ); 3344 } elseif ( false !== strpos( $type, 'json' ) ) { 3345 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3346 'mode' => array( 3347 'name' => 'javascript', 3348 ), 3349 'lint' => true, 3350 'autoCloseBrackets' => true, 3351 'matchBrackets' => true, 3352 ) ); 3353 if ( 'application/ld+json' === $type ) { 3354 $settings['codemirror']['mode']['jsonld'] = true; 3355 } else { 3356 $settings['codemirror']['mode']['json'] = true; 3357 } 3358 } elseif ( false !== strpos( $type, 'jsx' ) ) { 3359 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3360 'mode' => 'jsx', 3361 'autoCloseBrackets' => true, 3362 'matchBrackets' => true, 3363 ) ); 3364 } elseif ( 'text/x-markdown' === $type ) { 3365 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3366 'mode' => 'markdown', 3367 'highlightFormatting' => true, 3368 ) ); 3369 } elseif ( 'text/nginx' === $type ) { 3370 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3371 'mode' => 'nginx', 3372 ) ); 3373 } elseif ( 'application/x-httpd-php' === $type ) { 3374 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3375 'mode' => 'php', 3376 'autoCloseBrackets' => true, 3377 'autoCloseTags' => true, 3378 'matchBrackets' => true, 3379 'matchTags' => array( 3380 'bothTags' => true, 3381 ), 3382 ) ); 3383 } elseif ( 'text/x-sql' === $type || 'text/x-mysql' === $type ) { 3384 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3385 'mode' => 'sql', 3386 'autoCloseBrackets' => true, 3387 'matchBrackets' => true, 3388 ) ); 3389 } elseif ( false !== strpos( $type, 'xml' ) ) { 3390 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3391 'mode' => 'xml', 3392 'autoCloseBrackets' => true, 3393 'autoCloseTags' => true, 3394 'matchTags' => array( 3395 'bothTags' => true, 3396 ), 3397 ) ); 3398 } elseif ( 'text/x-yaml' === $type ) { 3399 $settings['codemirror'] = array_merge( $settings['codemirror'], array( 3400 'mode' => 'yaml', 3401 ) ); 3402 } else { 3403 $settings['codemirror']['mode'] = $type; 3404 } 3405 3406 if ( ! empty( $settings['codemirror']['lint'] ) ) { 3407 $settings['codemirror']['gutters'][] = 'CodeMirror-lint-markers'; 3408 } 3409 3410 // Let settings supplied via args override any defaults. 3411 if ( isset( $args['settings'] ) ) { 3412 foreach ( $args['settings'] as $key => $value ) { 3413 $settings[ $key ] = array_merge( 3414 $settings[ $key ], 3415 $value 3416 ); 3417 } 3418 } 3419 3420 /** 3421 * Filters settings that are passed into the code editor. 3422 * 3423 * Returning a falsey value will disable the syntax-highlighting code editor. 3424 * 3425 * @since 4.9.0 3426 * 3427 * @param array $settings The array of settings passed to the code editor. A falsey value disables the editor. 3428 * @param array $args { 3429 * Args passed when calling `wp_enqueue_code_editor()`. 3430 * 3431 * @type string $type The MIME type of the file to be edited. 3432 * @type string $file Filename being edited. 3433 * @type array $settings Settings to merge on top of defaults which derive from `$type` or `$file` args. 3434 * @type WP_Theme $theme Theme being edited when on theme editor. 3435 * @type string $plugin Plugin being edited when on plugin editor. 3436 * } 3437 */ 3438 $settings = apply_filters( 'wp_code_editor_settings', $settings, $args ); 3439 3440 if ( empty( $settings ) || empty( $settings['codemirror'] ) ) { 3441 return false; 3442 } 3443 3444 wp_enqueue_script( 'code-editor' ); 3445 wp_enqueue_style( 'code-editor' ); 3446 3447 wp_enqueue_script( 'codemirror' ); 3448 wp_enqueue_style( 'codemirror' ); 3449 3450 if ( isset( $settings['codemirror']['mode'] ) ) { 3451 $mode = $settings['codemirror']['mode']; 3452 if ( is_string( $mode ) ) { 3453 $mode = array( 3454 'name' => $mode, 3455 ); 3456 } 3457 3458 if ( ! empty( $settings['codemirror']['lint'] ) ) { 3459 switch ( $mode['name'] ) { 3460 case 'css': 3461 case 'text/css': 3462 case 'text/x-scss': 3463 case 'text/x-less': 3464 wp_enqueue_script( 'csslint' ); 3465 break; 3466 case 'htmlmixed': 3467 case 'text/html': 3468 case 'php': 3469 case 'application/x-httpd-php': 3470 case 'text/x-php': 3471 wp_enqueue_script( 'htmlhint' ); 3472 wp_enqueue_script( 'csslint' ); 3473 wp_enqueue_script( 'jshint' ); 3474 if ( ! current_user_can( 'unfiltered_html' ) ) { 3475 wp_enqueue_script( 'htmlhint-kses' ); 3476 } 3477 break; 3478 case 'javascript': 3479 case 'application/ecmascript': 3480 case 'application/json': 3481 case 'application/javascript': 3482 case 'application/ld+json': 3483 case 'text/typescript': 3484 case 'application/typescript': 3485 wp_enqueue_script( 'jshint' ); 3486 wp_enqueue_script( 'jsonlint' ); 3487 break; 3488 } 3489 } 3490 } 3491 3492 wp_add_inline_script( 'code-editor', sprintf( 'jQuery.extend( wp.codeEditor.defaultSettings, %s );', wp_json_encode( $settings ) ) ); 3493 3494 /** 3495 * Fires when scripts and styles are enqueued for the code editor. 3496 * 3497 * @since 4.9.0 3498 * 3499 * @param array $settings Settings for the enqueued code editor. 3500 */ 3501 do_action( 'wp_enqueue_code_editor', $settings ); 3502 3503 return $settings; 3504 } 3505 3506 /** 3119 3507 * Retrieves the contents of the search WordPress query variable. 3120 3508 * -
trunk/src/wp-includes/script-loader.php
r41375 r41376 467 467 ); 468 468 469 $scripts->add( 'codemirror', '/wp-includes/js/codemirror/codemirror.min.js', array(), '5.29.1-alpha-ee20357' ); 470 $scripts->add( 'csslint', '/wp-includes/js/codemirror/csslint.js', array(), '1.0.5' ); 471 $scripts->add( 'jshint', '/wp-includes/js/codemirror/jshint.js', array(), '2.9.5' ); 472 $scripts->add( 'jsonlint', '/wp-includes/js/codemirror/jsonlint.js', array(), '1.6.2' ); 473 $scripts->add( 'htmlhint', '/wp-includes/js/codemirror/htmlhint.js', array(), '0.9.14-xwp' ); 474 $scripts->add( 'htmlhint-kses', '/wp-includes/js/codemirror/htmlhint-kses.js', array( 'htmlhint' ) ); 475 $scripts->add( 'code-editor', "/wp-admin/js/code-editor$suffix.js", array( 'jquery', 'codemirror' ) ); 476 $scripts->add( 'wp-theme-plugin-editor', "/wp-admin/js/theme-plugin-editor$suffix.js", array( 'code-editor', 'jquery', 'jquery-ui-core', 'wp-a11y', 'underscore' ) ); 477 did_action( 'init' ) && $scripts->add_inline_script( 'wp-theme-plugin-editor', sprintf( 'wp.themePluginEditor.l10n = %s;', wp_json_encode( wp_array_slice_assoc( 478 /* translators: placeholder is error count */ 479 _n_noop( 'There is %d error which must be fixed before you can save.', 'There are %d errors which must be fixed before you can save.' ), 480 array( 'singular', 'plural' ) 481 ) ) ) ); 482 469 483 $scripts->add( 'wp-playlist', "/wp-includes/js/mediaelement/wp-playlist$suffix.js", array( 'wp-util', 'backbone', 'mediaelement' ), false, 1 ); 470 484 … … 552 566 // Used for overriding the file types allowed in plupload. 553 567 'allowedFiles' => __( 'Allowed Files' ), 568 'customCssError' => wp_array_slice_assoc( 569 /* translators: placeholder is error count */ 570 _n_noop( 'There is %d error which must be fixed before you can save.', 'There are %d errors which must be fixed before you can save.' ), 571 array( 'singular', 'plural' ) 572 ), 554 573 ) ); 555 574 $scripts->add( 'customize-selective-refresh', "/wp-includes/js/customize-selective-refresh$suffix.js", array( 'jquery', 'wp-util', 'customize-preview' ), false, 1 ); … … 689 708 $scripts->add( 'media-video-widget', "/wp-admin/js/widgets/media-video-widget$suffix.js", array( 'media-widgets', 'media-audiovideo', 'wp-api-request' ) ); 690 709 $scripts->add( 'text-widgets', "/wp-admin/js/widgets/text-widgets$suffix.js", array( 'jquery', 'backbone', 'editor', 'wp-util', 'wp-a11y' ) ); 691 $scripts->add _inline_script( 'text-widgets', 'wp.textWidgets.init();', 'after');710 $scripts->add( 'custom-html-widgets', "/wp-admin/js/widgets/custom-html-widgets$suffix.js", array( 'code-editor', 'jquery', 'backbone', 'wp-util', 'jquery-ui-core', 'wp-a11y' ) ); 692 711 693 712 $scripts->add( 'theme', "/wp-admin/js/theme$suffix.js", array( 'wp-backbone', 'wp-a11y' ), false, 1 ); … … 930 949 $styles->add( 'site-icon', "/wp-admin/css/site-icon$suffix.css" ); 931 950 $styles->add( 'l10n', "/wp-admin/css/l10n$suffix.css" ); 951 $styles->add( 'code-editor', "/wp-admin/css/code-editor$suffix.css", array( 'codemirror' ) ); 932 952 933 953 $styles->add( 'wp-admin', false, array( 'dashicons', 'common', 'forms', 'admin-menu', 'dashboard', 'list-tables', 'edit', 'revisions', 'media', 'themes', 'about', 'nav-menus', 'widgets', 'site-icon', 'l10n' ) ); … … 964 984 $styles->add( 'wp-mediaelement', "/wp-includes/js/mediaelement/wp-mediaelement$suffix.css", array( 'mediaelement' ) ); 965 985 $styles->add( 'thickbox', '/wp-includes/js/thickbox/thickbox.css', array( 'dashicons' ) ); 986 $styles->add( 'codemirror', '/wp-includes/js/codemirror/codemirror.min.css', array(), '5.29.1-alpha-ee20357' ); 966 987 967 988 // Deprecated CSS -
trunk/src/wp-includes/user.php
r41289 r41376 1377 1377 * 1378 1378 * Most of the `$userdata` array fields have filters associated with the values. Exceptions are 1379 * 'ID', 'rich_editing', ' comment_shortcuts', 'admin_color', 'use_ssl',1379 * 'ID', 'rich_editing', 'syntax_highlighting', 'comment_shortcuts', 'admin_color', 'use_ssl', 1380 1380 * 'user_registered', and 'role'. The filters have the prefix 'pre_user_' followed by the field 1381 1381 * name. An example using 'description' would have the filter called, 'pre_user_description' that … … 1411 1411 * @type string|bool $rich_editing Whether to enable the rich-editor for the user. 1412 1412 * False if not empty. 1413 * @type string|bool $syntax_highlighting Whether to enable the rich code editor for the user. 1414 * False if not empty. 1413 1415 * @type string|bool $comment_shortcuts Whether to enable comment moderation keyboard 1414 1416 * shortcuts for the user. Default false. … … 1623 1625 1624 1626 $meta['rich_editing'] = empty( $userdata['rich_editing'] ) ? 'true' : $userdata['rich_editing']; 1627 1628 $meta['syntax_highlighting'] = empty( $userdata['syntax_highlighting'] ) ? 'true' : $userdata['syntax_highlighting']; 1625 1629 1626 1630 $meta['comment_shortcuts'] = empty( $userdata['comment_shortcuts'] ) || 'false' === $userdata['comment_shortcuts'] ? 'false' : 'true'; … … 1710 1714 * @type string $description The user's description. 1711 1715 * @type bool $rich_editing Whether to enable the rich-editor for the user. False if not empty. 1716 * @type bool $syntax_highlighting Whether to enable the rich code editor for the user. False if not empty. 1712 1717 * @type bool $comment_shortcuts Whether to enable keyboard shortcuts for the user. Default false. 1713 1718 * @type string $admin_color The color scheme for a user's admin screen. Default 'fresh'. … … 2036 2041 */ 2037 2042 function _get_additional_user_keys( $user ) { 2038 $keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', ' comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front', 'locale' );2043 $keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'syntax_highlighting', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front', 'locale' ); 2039 2044 return array_merge( $keys, array_keys( wp_get_user_contact_methods( $user ) ) ); 2040 2045 } -
trunk/src/wp-includes/widgets/class-wp-widget-custom-html.php
r41132 r41376 18 18 19 19 /** 20 * Whether or not the widget has been registered yet. 21 * 22 * @since 4.9.0 23 * @var bool 24 */ 25 protected $registered = false; 26 27 /** 20 28 * Default instance. 21 29 * … … 44 52 ); 45 53 parent::__construct( 'custom_html', __( 'Custom HTML' ), $widget_ops, $control_ops ); 54 } 55 56 /** 57 * Add hooks for enqueueing assets when registering all widget instances of this widget class. 58 * 59 * @since 4.9.0 60 * 61 * @param integer $number Optional. The unique order number of this widget instance 62 * compared to other instances of the same class. Default -1. 63 */ 64 public function _register_one( $number = -1 ) { 65 parent::_register_one( $number ); 66 if ( $this->registered ) { 67 return; 68 } 69 $this->registered = true; 70 71 wp_add_inline_script( 'custom-html-widgets', sprintf( 'wp.customHtmlWidgets.idBases.push( %s );', wp_json_encode( $this->id_base ) ) ); 72 73 // Note that the widgets component in the customizer will also do the 'admin_print_scripts-widgets.php' action in WP_Customize_Widgets::print_scripts(). 74 add_action( 'admin_print_scripts-widgets.php', array( $this, 'enqueue_admin_scripts' ) ); 75 76 // Note that the widgets component in the customizer will also do the 'admin_footer-widgets.php' action in WP_Customize_Widgets::print_footer_scripts(). 77 add_action( 'admin_footer-widgets.php', array( 'WP_Widget_Custom_HTML', 'render_control_template_scripts' ) ); 78 79 // Note this action is used to ensure the help text is added to the end. 80 add_action( 'admin_head-widgets.php', array( 'WP_Widget_Custom_HTML', 'add_help_text' ) ); 46 81 } 47 82 … … 119 154 120 155 /** 156 * Loads the required scripts and styles for the widget control. 157 * 158 * @since 4.9.0 159 */ 160 public function enqueue_admin_scripts() { 161 $settings = wp_enqueue_code_editor( array( 162 'type' => 'text/html', 163 ) ); 164 165 wp_enqueue_script( 'custom-html-widgets' ); 166 if ( empty( $settings ) ) { 167 $settings = array( 168 'disabled' => true, 169 ); 170 } 171 wp_add_inline_script( 'custom-html-widgets', sprintf( 'wp.customHtmlWidgets.init( %s );', wp_json_encode( $settings ) ), 'after' ); 172 173 $l10n = array( 174 'errorNotice' => wp_array_slice_assoc( 175 /* translators: placeholder is error count */ 176 _n_noop( 'There is %d error which must be fixed before you can save.', 'There are %d errors which must be fixed before you can save.' ), 177 array( 'singular', 'plural' ) 178 ), 179 ); 180 wp_add_inline_script( 'custom-html-widgets', sprintf( 'jQuery.extend( wp.customHtmlWidgets.l10n, %s );', wp_json_encode( $l10n ) ), 'after' ); 181 } 182 183 /** 121 184 * Outputs the Custom HTML widget settings form. 122 185 * 123 186 * @since 4.8.1 124 * 187 * @since 4.9.0 The form contains only hidden sync inputs. For the control UI, see `WP_Widget_Custom_HTML::render_control_template_scripts()`. 188 * 189 * @see WP_Widget_Custom_HTML::render_control_template_scripts() 125 190 * @param array $instance Current instance. 126 191 * @returns void … … 129 194 $instance = wp_parse_args( (array) $instance, $this->default_instance ); 130 195 ?> 131 <p> 132 <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label> 133 <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>"/> 134 </p> 135 136 <p> 137 <label for="<?php echo $this->get_field_id( 'content' ); ?>"><?php _e( 'Content:' ); ?></label> 138 <textarea class="widefat code" rows="16" cols="20" id="<?php echo $this->get_field_id( 'content' ); ?>" name="<?php echo $this->get_field_name( 'content' ); ?>"><?php echo esc_textarea( $instance['content'] ); ?></textarea> 139 </p> 140 141 <?php if ( ! current_user_can( 'unfiltered_html' ) ) : ?> 142 <?php 143 $probably_unsafe_html = array( 'script', 'iframe', 'form', 'input', 'style' ); 144 $allowed_html = wp_kses_allowed_html( 'post' ); 145 $disallowed_html = array_diff( $probably_unsafe_html, array_keys( $allowed_html ) ); 146 ?> 147 <?php if ( ! empty( $disallowed_html ) ) : ?> 148 <p> 149 <?php _e( 'Some HTML tags are not permitted, including:' ); ?> 150 <code><?php echo join( '</code>, <code>', $disallowed_html ); ?></code> 151 </p> 196 <input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" class="title sync-input" type="hidden" value="<?php echo esc_attr( $instance['title'] ); ?>"/> 197 <textarea id="<?php echo $this->get_field_id( 'content' ); ?>" name="<?php echo $this->get_field_name( 'content' ); ?>" class="content sync-input" hidden><?php echo esc_textarea( $instance['content'] ); ?></textarea> 198 <?php 199 } 200 201 /** 202 * Render form template scripts. 203 * 204 * @since 4.9.0 205 */ 206 public static function render_control_template_scripts() { 207 ?> 208 <script type="text/html" id="tmpl-widget-custom-html-control-fields"> 209 <# var elementIdPrefix = 'el' + String( Math.random() ).replace( /\D/g, '' ) + '_' #> 210 <p> 211 <label for="{{ elementIdPrefix }}title"><?php esc_html_e( 'Title:', 'default' ); ?></label> 212 <input id="{{ elementIdPrefix }}title" type="text" class="widefat title"> 213 </p> 214 215 <p> 216 <label for="{{ elementIdPrefix }}content" class="screen-reader-text"><?php esc_html_e( 'Content:', 'default' ); ?></label> 217 <textarea id="{{ elementIdPrefix }}content" class="widefat code content" rows="16" cols="20"></textarea> 218 </p> 219 220 <?php if ( ! current_user_can( 'unfiltered_html' ) ) : ?> 221 <?php 222 $probably_unsafe_html = array( 'script', 'iframe', 'form', 'input', 'style' ); 223 $allowed_html = wp_kses_allowed_html( 'post' ); 224 $disallowed_html = array_diff( $probably_unsafe_html, array_keys( $allowed_html ) ); 225 ?> 226 <?php if ( ! empty( $disallowed_html ) ) : ?> 227 <# if ( data.codeEditorDisabled ) { #> 228 <p> 229 <?php _e( 'Some HTML tags are not permitted, including:', 'default' ); ?> 230 <code><?php echo join( '</code>, <code>', $disallowed_html ); ?></code> 231 </p> 232 <# } #> 233 <?php endif; ?> 152 234 <?php endif; ?> 153 <?php endif; ?> 235 236 <div class="code-editor-error-container"></div> 237 </script> 154 238 <?php 155 239 } 240 241 /** 242 * Add help text to widgets admin screen. 243 * 244 * @since 4.9.0 245 */ 246 public static function add_help_text() { 247 $screen = get_current_screen(); 248 249 $content = '<p>'; 250 $content .= __( 'Use the Custom HTML widget to add arbitrary HTML code to your widget areas.' ); 251 $content .= '</p>'; 252 253 $content .= '<p>' . __( 'When using a keyboard to navigate:' ) . '</p>'; 254 $content .= '<ul>'; 255 $content .= '<li>' . __( 'In the HTML edit field, Tab enters a tab character.' ) . '</li>'; 256 $content .= '<li>' . __( 'To move keyboard focus, press Esc then Tab for the next element, or Esc then Shift+Tab for the previous element.' ) . '</li>'; 257 $content .= '</ul>'; 258 259 $content .= '<p>'; 260 $content .= sprintf( 261 /* translators: placeholder is link to user profile */ 262 __( 'The edit field automatically highlights code syntax. You can disable this in your %s to work in plan text mode.' ), 263 sprintf( 264 ' <a href="%1$s" class="external-link" target="_blank">%2$s<span class="screen-reader-text">%3$s</span></a>', 265 esc_url( get_edit_profile_url() . '#syntax_highlighting' ), 266 __( 'user profile' ), 267 /* translators: accessibility text */ 268 __( '(opens in a new window)', 'default' ) 269 ) 270 ); 271 $content .= '</p>'; 272 273 $screen->add_help_tab( array( 274 'id' => 'custom_html_widget', 275 'title' => __( 'Custom HTML Widget' ), 276 'content' => $content, 277 ) ); 278 } 156 279 } -
trunk/src/wp-includes/widgets/class-wp-widget-text.php
r41361 r41376 354 354 wp_enqueue_editor(); 355 355 wp_enqueue_script( 'text-widgets' ); 356 wp_add_inline_script( 'text-widgets', 'wp.textWidgets.init();', 'after' ); 356 357 } 357 358 -
trunk/tests/phpunit/tests/customize/custom-css-setting.php
r39919 r41376 356 356 $this->assertTrue( $result ); 357 357 358 // Check for Unclosed Comment.359 $unclosed_comment = $basic_css . ' /* This is a comment.';358 // Check for markup. 359 $unclosed_comment = $basic_css . '</style>'; 360 360 $result = $this->setting->validate( $unclosed_comment ); 361 $this->assertTrue( array_key_exists( 'unclosed_comment', $result->errors ) ); 362 363 // Check for Unopened Comment. 364 $unclosed_comment = $basic_css . ' This is a comment.*/'; 365 $result = $this->setting->validate( $unclosed_comment ); 366 $this->assertTrue( array_key_exists( 'imbalanced_comments', $result->errors ) ); 367 368 // Check for Unclosed Curly Brackets. 369 $unclosed_curly_bracket = $basic_css . ' a.link { text-decoration: none;'; 370 $result = $this->setting->validate( $unclosed_curly_bracket ); 371 $this->assertTrue( array_key_exists( 'imbalanced_curly_brackets', $result->errors ) ); 372 373 // Check for Unopened Curly Brackets. 374 $unopened_curly_bracket = $basic_css . ' a.link text-decoration: none; }'; 375 $result = $this->setting->validate( $unopened_curly_bracket ); 376 $this->assertTrue( array_key_exists( 'imbalanced_curly_brackets', $result->errors ) ); 377 378 // Check for Unclosed Braces. 379 $unclosed_brace = $basic_css . ' input[type="text" { color: #f00; } '; 380 $result = $this->setting->validate( $unclosed_brace ); 381 $this->assertTrue( array_key_exists( 'imbalanced_braces', $result->errors ) ); 382 383 // Check for Unopened Braces. 384 $unopened_brace = $basic_css . ' inputtype="text"] { color: #f00; } '; 385 $result = $this->setting->validate( $unopened_brace ); 386 $this->assertTrue( array_key_exists( 'imbalanced_braces', $result->errors ) ); 387 388 // Check for Imbalanced Double Quotes. 389 $imbalanced_double_quotes = $basic_css . ' div.background-image { background-image: url( "image.jpg ); } '; 390 $result = $this->setting->validate( $imbalanced_double_quotes ); 391 $this->assertTrue( array_key_exists( 'unequal_double_quotes', $result->errors ) ); 392 393 // Check for Unclosed Parentheses. 394 $unclosed_parentheses = $basic_css . ' div.background-image { background-image: url( "image.jpg" ; } '; 395 $result = $this->setting->validate( $unclosed_parentheses ); 396 $this->assertTrue( array_key_exists( 'imbalanced_parentheses', $result->errors ) ); 397 398 // Check for Unopened Parentheses. 399 $unopened_parentheses = $basic_css . ' div.background-image { background-image: url "image.jpg" ); } '; 400 $result = $this->setting->validate( $unopened_parentheses ); 401 $this->assertTrue( array_key_exists( 'imbalanced_parentheses', $result->errors ) ); 402 403 // A basic Content declaration with no other errors should not throw an error. 404 $content_declaration = $basic_css . ' a:before { content: ""; display: block; }'; 405 $result = $this->setting->validate( $content_declaration ); 406 $this->assertTrue( $result ); 407 408 // An error, along with a Content declaration will throw two errors. 409 // In this case, we're using an extra opening brace. 410 $content_declaration = $basic_css . ' a:before { content: "["; display: block; }'; 411 $result = $this->setting->validate( $content_declaration ); 412 $this->assertTrue( array_key_exists( 'imbalanced_braces', $result->errors ) ); 413 $this->assertTrue( array_key_exists( 'possible_false_positive', $result->errors ) ); 414 415 $css = 'body { background: #f00; } h1.site-title { font-size: 36px; } a:hover { text-decoration: none; } input[type="text"] { padding: 1em; } /* This is a comment */'; 416 $this->assertTrue( $this->setting->validate( $css ) ); 417 418 $validity = $this->setting->validate( $css . ' /* This is another comment.' ); 419 $this->assertInstanceOf( 'WP_Error', $validity ); 420 $this->assertContains( 'unclosed code comment', join( ' ', $validity->get_error_messages() ) ); 421 422 $css = '/* This is comment one. */ /* This is comment two. */'; 423 $this->assertTrue( $this->setting->validate( $css ) ); 424 425 $basic_css = 'body { background: #f00; } h1.site-title { font-size: 36px; } a:hover { text-decoration: none; } input[type="text"] { padding: 1em; }'; 426 $this->assertTrue( $this->setting->validate( $basic_css ) ); 427 428 $css = $basic_css . ' .link:before { content: "*"; display: block; }'; 429 $this->assertTrue( $this->setting->validate( $css ) ); 430 431 $css .= ' ( trailing'; 432 $validity = $this->setting->validate( $css ); 433 $this->assertWPError( $validity ); 434 $this->assertNotEmpty( $result->get_error_message( 'possible_false_positive' ) ); 361 $this->assertTrue( array_key_exists( 'illegal_markup', $result->errors ) ); 435 362 } 436 363 } -
trunk/tests/phpunit/tests/customize/manager.php
r41372 r41376 2352 2352 $this->assertNotEmpty( $data ); 2353 2353 2354 $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'c hangeset', 'timeouts' ), array_keys( $data ) );2354 $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'customCss', 'changeset', 'timeouts' ), array_keys( $data ) ); 2355 2355 $this->assertEquals( $autofocus, $data['autofocus'] ); 2356 2356 $this->assertArrayHasKey( 'save', $data['nonce'] ); -
trunk/tests/phpunit/tests/user.php
r41255 r41376 359 359 $user_data = array( 360 360 'ID' => self::$author_id, 'use_ssl' => 1, 'show_admin_bar_front' => 1, 361 'rich_editing' => 1, ' first_name' => 'first', 'last_name' => 'last',361 'rich_editing' => 1, 'syntax_highlighting' => 1, 'first_name' => 'first', 'last_name' => 'last', 362 362 'nickname' => 'nick', 'comment_shortcuts' => 'true', 'admin_color' => 'classic', 363 363 'description' => 'describe'
Note: See TracChangeset
for help on using the changeset viewer.