Changeset 41721
- Timestamp:
- 10/04/2017 12:19:16 AM (7 years ago)
- Location:
- trunk/src
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/admin-ajax.php
r41584 r41721 65 65 'generate-password', 'save-wporg-username', 'delete-plugin', 'search-plugins', 66 66 'search-install-plugins', 'activate-plugin', 'update-theme', 'delete-theme', 'install-theme', 67 'get-post-thumbnail-html', 'get-community-events', 67 'get-post-thumbnail-html', 'get-community-events', 'edit-theme-plugin-file', 68 68 ); 69 69 -
trunk/src/wp-admin/css/common.css
r41711 r41721 2218 2218 margin-right: 190px; 2219 2219 } 2220 #template . active-plugin-edit-warning{2220 #template .notice { 2221 2221 margin-top: 1em; 2222 margin-right: 30%; 2223 margin-right: calc( 184px + 3% ); 2224 } 2225 #template .active-plugin-edit-warning p { 2222 margin-right: 3%; 2223 } 2224 #template .notice p { 2226 2225 width: auto; 2226 } 2227 #template .submit .spinner { 2228 float: none; 2227 2229 } 2228 2230 … … 3033 3035 #template .CodeMirror { 3034 3036 width: 97%; 3035 height: calc( 100vh - 220px ); 3036 } 3037 3038 #template label { 3037 height: calc( 100vh - 280px ); 3038 } 3039 #templateside { 3040 margin-top: 31px; 3041 overflow: scroll; 3042 } 3043 3044 #theme-plugin-editor-label { 3039 3045 display: inline-block; 3040 3046 margin-bottom: 1em; … … 3046 3052 #docs-list { 3047 3053 direction: ltr; 3054 } 3055 3056 .fileedit-sub #theme, 3057 .fileedit-sub #plugin { 3058 max-width: 40%; 3059 } 3060 .fileedit-sub .alignright { 3061 text-align: right; 3048 3062 } 3049 3063 … … 3625 3639 3626 3640 #template > div, 3627 #template .active-plugin-edit-warning{3641 #template .notice { 3628 3642 float: none; 3629 3643 margin: 1em 0; -
trunk/src/wp-admin/includes/ajax-actions.php
r41611 r41721 3967 3967 wp_send_json_success( $status ); 3968 3968 } 3969 3970 /** 3971 * Ajax handler for editing a theme or plugin file. 3972 * 3973 * @since 4.9.0 3974 * @see wp_edit_theme_plugin_file() 3975 */ 3976 function wp_ajax_edit_theme_plugin_file() { 3977 $r = wp_edit_theme_plugin_file( wp_unslash( $_POST ) ); // Validation of args is done in wp_edit_theme_plugin_file(). 3978 if ( is_wp_error( $r ) ) { 3979 wp_send_json_error( array_merge( 3980 array( 3981 'code' => $r->get_error_code(), 3982 'message' => $r->get_error_message(), 3983 ), 3984 (array) $r->get_error_data() 3985 ) ); 3986 } else { 3987 wp_send_json_success( array( 3988 'message' => __( 'File edited successfully.' ), 3989 ) ); 3990 } 3991 } -
trunk/src/wp-admin/includes/file.php
r41457 r41721 71 71 * 72 72 * @global array $wp_file_descriptions Theme file descriptions. 73 * @global array $allowed_files List of allowed files. 73 * @global array $allowed_files List of allowed files. 74 74 * @param string $file Filesystem path or filename 75 75 * @return string Description of file from $wp_file_descriptions or basename of $file if description doesn't exist. … … 152 152 return $files; 153 153 } 154 155 /** 156 * Get list of file extensions that are editable in plugins. 157 * 158 * @since 4.9.0 159 * 160 * @param string $plugin Plugin. 161 * @return array File extensions. 162 */ 163 function wp_get_plugin_file_editable_extensions( $plugin ) { 164 165 $editable_extensions = array( 166 'bash', 167 'conf', 168 'css', 169 'diff', 170 'htm', 171 'html', 172 'http', 173 'inc', 174 'include', 175 'js', 176 'json', 177 'jsx', 178 'less', 179 'md', 180 'patch', 181 'php', 182 'php3', 183 'php4', 184 'php5', 185 'php7', 186 'phps', 187 'phtml', 188 'sass', 189 'scss', 190 'sh', 191 'sql', 192 'svg', 193 'text', 194 'txt', 195 'xml', 196 'yaml', 197 'yml', 198 ); 199 200 /** 201 * Filters file type extensions editable in the plugin editor. 202 * 203 * @since 2.8.0 204 * @since 4.9.0 Adds $plugin param. 205 * 206 * @param string $plugin Plugin file. 207 * @param array $editable_extensions An array of editable plugin file extensions. 208 */ 209 $editable_extensions = (array) apply_filters( 'editable_extensions', $editable_extensions, $plugin ); 210 211 return $editable_extensions; 212 } 213 214 /** 215 * Get list of file extensions that are editable for a given theme. 216 * 217 * @param WP_Theme $theme Theme. 218 * @return array File extensions. 219 */ 220 function wp_get_theme_file_editable_extensions( $theme ) { 221 222 $default_types = array( 223 'bash', 224 'conf', 225 'css', 226 'diff', 227 'htm', 228 'html', 229 'http', 230 'inc', 231 'include', 232 'js', 233 'json', 234 'jsx', 235 'less', 236 'md', 237 'patch', 238 'php', 239 'php3', 240 'php4', 241 'php5', 242 'php7', 243 'phps', 244 'phtml', 245 'sass', 246 'scss', 247 'sh', 248 'sql', 249 'svg', 250 'text', 251 'txt', 252 'xml', 253 'yaml', 254 'yml', 255 ); 256 257 /** 258 * Filters the list of file types allowed for editing in the Theme editor. 259 * 260 * @since 4.4.0 261 * 262 * @param array $default_types List of file types. Default types include 'php' and 'css'. 263 * @param WP_Theme $theme The current Theme object. 264 */ 265 $file_types = apply_filters( 'wp_theme_editor_filetypes', $default_types, $theme ); 266 267 // Ensure that default types are still there. 268 return array_unique( array_merge( $file_types, $default_types ) ); 269 } 270 271 /** 272 * Print file editor templates (for plugins and themes). 273 * 274 * @since 4.9.0 275 */ 276 function wp_print_file_editor_templates() { 277 ?> 278 <script type="text/html" id="tmpl-wp-file-editor-notice"> 279 <div class="notice inline notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }} {{ data.classes || '' }}"> 280 <# if ( 'php_error' === data.code ) { #> 281 <p> 282 <?php 283 printf( 284 /* translators: %$1s is line number and %1$s is file path. */ 285 __( 'Your PHP code changes were rolled back due to an error on line %1$s of file %2$s. Please fix and try saving again.' ), 286 '{{ data.line }}', 287 '{{ data.file }}' 288 ); 289 ?> 290 </p> 291 <pre>{{ data.message }}</pre> 292 <# } else if ( 'file_not_writable' === data.code ) { #> 293 <p><?php _e( 'You need to make this file writable before you can save your changes. See <a href="https://codex.wordpress.org/Changing_File_Permissions">the Codex</a> for more information.' ); ?></p> 294 <# } else { #> 295 <p>{{ data.message || data.code }}</p> 296 297 <# if ( 'lint_errors' === data.code ) { #> 298 <p> 299 <# var elementId = 'el-' + String( Math.random() ); #> 300 <input id="{{ elementId }}" type="checkbox"> 301 <label for="{{ elementId }}"><?php _e( 'Update anyway, even though it might break your site?' ); ?></label> 302 </p> 303 <# } #> 304 <# } #> 305 <# if ( data.dismissible ) { #> 306 <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e( 'Dismiss' ); ?></span></button> 307 <# } #> 308 </div> 309 </script> 310 <?php 311 } 312 313 /** 314 * Attempt to edit a file for a theme or plugin. 315 * 316 * When editing a PHP file, loopback requests will be made to the admin and the homepage 317 * to attempt to see if there is a fatal error introduced. If so, the PHP change will be 318 * reverted. 319 * 320 * @since 4.9.0 321 * 322 * @param array $args { 323 * Args. Note that all of the arg values are already unslashed. They are, however, 324 * coming straight from $_POST and are not validated or sanitized in any way. 325 * 326 * @type string $file Relative path to file. 327 * @type string $plugin Plugin being edited. 328 * @type string $theme Theme being edited. 329 * @type string $newcontent New content for the file. 330 * @type string $nonce Nonce. 331 * } 332 * @return true|WP_Error True on success or `WP_Error` on failure. 333 */ 334 function wp_edit_theme_plugin_file( $args ) { 335 if ( empty( $args['file'] ) ) { 336 return new WP_Error( 'missing_file' ); 337 } 338 $file = $args['file']; 339 if ( 0 !== validate_file( $file ) ) { 340 return new WP_Error( 'bad_file' ); 341 } 342 343 if ( ! isset( $args['newcontent'] ) ) { 344 return new WP_Error( 'missing_content' ); 345 } 346 $content = $args['newcontent']; 347 348 if ( ! isset( $args['nonce'] ) ) { 349 return new WP_Error( 'missing_nonce' ); 350 } 351 352 $plugin = null; 353 $theme = null; 354 $real_file = null; 355 if ( ! empty( $args['plugin'] ) ) { 356 $plugin = $args['plugin']; 357 358 if ( ! current_user_can( 'edit_plugins' ) ) { 359 return new WP_Error( 'unauthorized', __( 'Sorry, you are not allowed to edit plugins for this site.' ) ); 360 } 361 362 if ( ! wp_verify_nonce( $args['nonce'], 'edit-plugin_' . $file ) ) { 363 return new WP_Error( 'nonce_failure' ); 364 } 365 366 if ( ! array_key_exists( $plugin, get_plugins() ) ) { 367 return new WP_Error( 'invalid_plugin' ); 368 } 369 370 if ( 0 !== validate_file( $file, get_plugin_files( $plugin ) ) ) { 371 return new WP_Error( 'bad_plugin_file_path', __( 'Sorry, that file cannot be edited.' ) ); 372 } 373 374 $editable_extensions = wp_get_plugin_file_editable_extensions( $plugin ); 375 376 $real_file = WP_PLUGIN_DIR . '/' . $file; 377 378 $is_active = in_array( 379 $plugin, 380 (array) get_option( 'active_plugins', array() ), 381 true 382 ); 383 384 } elseif ( ! empty( $args['theme'] ) ) { 385 $stylesheet = $args['theme']; 386 if ( 0 !== validate_file( $stylesheet ) ) { 387 return new WP_Error( 'bad_theme_path' ); 388 } 389 390 if ( ! current_user_can( 'edit_themes' ) ) { 391 return new WP_Error( 'unauthorized', __( 'Sorry, you are not allowed to edit templates for this site.' ) ); 392 } 393 394 $theme = wp_get_theme( $stylesheet ); 395 if ( ! $theme->exists() ) { 396 return new WP_Error( 'non_existent_theme', __( 'The requested theme does not exist.' ) ); 397 } 398 399 $real_file = $theme->get_stylesheet_directory() . '/' . $file; 400 if ( ! wp_verify_nonce( $args['nonce'], 'edit-theme_' . $real_file . $stylesheet ) ) { 401 return new WP_Error( 'nonce_failure' ); 402 } 403 404 if ( $theme->errors() && 'theme_no_stylesheet' === $theme->errors()->get_error_code() ) { 405 return new WP_Error( 406 'theme_no_stylesheet', 407 __( 'The requested theme does not exist.' ) . ' ' . $theme->errors()->get_error_message() 408 ); 409 } 410 411 $editable_extensions = wp_get_theme_file_editable_extensions( $theme ); 412 413 $allowed_files = array(); 414 foreach ( $editable_extensions as $type ) { 415 switch ( $type ) { 416 case 'php': 417 $allowed_files = array_merge( $allowed_files, $theme->get_files( 'php', 1 ) ); 418 break; 419 case 'css': 420 $style_files = $theme->get_files( 'css' ); 421 $allowed_files['style.css'] = $style_files['style.css']; 422 $allowed_files = array_merge( $allowed_files, $style_files ); 423 break; 424 default: 425 $allowed_files = array_merge( $allowed_files, $theme->get_files( $type ) ); 426 break; 427 } 428 } 429 430 if ( 0 !== validate_file( $real_file, $allowed_files ) ) { 431 return new WP_Error( 'disallowed_theme_file', __( 'Sorry, that file cannot be edited.' ) ); 432 } 433 434 $is_active = ( get_stylesheet() === $stylesheet || get_template() === $stylesheet ); 435 } else { 436 return new WP_Error( 'missing_theme_or_plugin' ); 437 } 438 439 // Ensure file is real. 440 if ( ! is_file( $real_file ) ) { 441 return new WP_Error( 'file_does_not_exist', __( 'No such file exists! Double check the name and try again.' ) ); 442 } 443 444 // Ensure file extension is allowed. 445 $extension = null; 446 if ( preg_match( '/\.([^.]+)$/', $real_file, $matches ) ) { 447 $extension = strtolower( $matches[1] ); 448 if ( ! in_array( $extension, $editable_extensions, true ) ) { 449 return new WP_Error( 'illegal_file_type', __( 'Files of this type are not editable.' ) ); 450 } 451 } 452 453 $previous_content = file_get_contents( $real_file ); 454 455 if ( ! is_writeable( $real_file ) ) { 456 return new WP_Error( 'file_not_writable' ); 457 } 458 459 $f = fopen( $real_file, 'w+' ); 460 if ( false === $f ) { 461 return new WP_Error( 'file_not_writable' ); 462 } 463 464 $written = fwrite( $f, $content ); 465 fclose( $f ); 466 if ( false === $written ) { 467 return new WP_Error( 'unable_to_write', __( 'Unable to write to file.' ) ); 468 } 469 if ( 'php' === $extension && function_exists( 'opcache_invalidate' ) ) { 470 opcache_invalidate( $real_file, true ); 471 } 472 473 if ( $is_active && 'php' === $extension ) { 474 475 $scrape_key = md5( rand() ); 476 $transient = 'scrape_key_' . $scrape_key; 477 $scrape_nonce = strval( rand() ); 478 set_transient( $transient, $scrape_nonce, 60 ); // It shouldn't take more than 60 seconds to make the two loopback requests. 479 480 $cookies = wp_unslash( $_COOKIE ); 481 $scrape_params = array( 482 'wp_scrape_key' => $scrape_key, 483 'wp_scrape_nonce' => $scrape_nonce, 484 ); 485 $headers = array( 486 'Cache-Control' => 'no-cache', 487 ); 488 489 $needle = "###### begin_scraped_error:$scrape_key ######"; 490 491 // Attempt loopback request to editor to see if user just whitescreened themselves. 492 if ( $plugin ) { 493 $url = add_query_arg( compact( 'plugin', 'file' ), admin_url( 'plugin-editor.php' ) ); 494 } elseif ( isset( $stylesheet ) ) { 495 $url = add_query_arg( 496 array( 497 'theme' => $stylesheet, 498 'file' => $file, 499 ), 500 admin_url( 'theme-editor.php' ) 501 ); 502 } else { 503 $url = admin_url(); 504 } 505 $url = add_query_arg( $scrape_params, $url ); 506 $r = wp_remote_get( $url, compact( 'cookies', 'headers' ) ); 507 $body = wp_remote_retrieve_body( $r ); 508 $error_position = strpos( $body, $needle ); 509 510 // Try making request to homepage as well to see if visitors have been whitescreened. 511 if ( false === $error_position ) { 512 $url = home_url( '/' ); 513 $url = add_query_arg( $scrape_params, $url ); 514 $r = wp_remote_get( $url, compact( 'cookies', 'headers' ) ); 515 $body = wp_remote_retrieve_body( $r ); 516 $error_position = strpos( $body, $needle ); 517 } 518 519 delete_transient( $transient ); 520 521 if ( false !== $error_position ) { 522 file_put_contents( $real_file, $previous_content ); 523 if ( function_exists( 'opcache_invalidate' ) ) { 524 opcache_invalidate( $real_file, true ); 525 } 526 527 $error_output = trim( substr( $body, $error_position + strlen( $needle ) ) ); 528 $error = json_decode( $error_output, true ); 529 if ( ! isset( $error['message'] ) ) { 530 $message = $error_output; 531 } else { 532 $message = $error['message']; 533 unset( $error['message'] ); 534 } 535 return new WP_Error( 'php_error', $message, $error ); 536 } 537 } 538 539 if ( $theme instanceof WP_Theme ) { 540 $theme->cache_delete(); 541 } 542 543 return true; 544 } 545 154 546 155 547 /** -
trunk/src/wp-admin/js/theme-plugin-editor.js
r41586 r41721 13 13 singular: '', 14 14 plural: '' 15 } 15 }, 16 saveAlert: '' 16 17 }, 17 instance: null 18 codeEditor: {}, 19 instance: null, 20 noticeElements: {}, 21 dirty: false, 22 lintErrors: [] 18 23 }; 19 24 … … 21 26 * Initialize component. 22 27 * 23 * @param {object} settings Settings. 28 * @since 4.9.0 29 * 30 * @param {jQuery} form - Form element. 31 * @param {object} settings - Settings. 32 * @param {object|boolean} settings.codeEditor - Code editor settings (or `false` if syntax highlighting is disabled). 24 33 * @returns {void} 25 34 */ 26 component.init = function( settings ) { 27 var codeEditorSettings, noticeContainer, errorNotice = [], editor; 28 29 codeEditorSettings = $.extend( {}, settings ); 35 component.init = function init( form, settings ) { 36 37 component.form = form; 38 if ( settings ) { 39 $.extend( component, settings ); 40 } 41 42 component.noticeTemplate = wp.template( 'wp-file-editor-notice' ); 43 component.noticesContainer = component.form.find( '.editor-notices' ); 44 component.submitButton = component.form.find( ':input[name=submit]' ); 45 component.spinner = component.form.find( '.submit .spinner' ); 46 component.form.on( 'submit', component.submit ); 47 component.textarea = component.form.find( '#newcontent' ); 48 component.textarea.on( 'change', component.onChange ); 49 50 if ( false !== component.codeEditor ) { 51 /* 52 * Defer adding notices until after DOM ready as workaround for WP Admin injecting 53 * its own managed dismiss buttons and also to prevent the editor from showing a notice 54 * when the file had linting errors to begin with. 55 */ 56 _.defer( function() { 57 component.initCodeEditor(); 58 } ); 59 } 60 61 $( window ).on( 'beforeunload', function() { 62 if ( component.dirty ) { 63 return component.l10n.saveAlert; 64 } 65 return undefined; 66 } ); 67 }; 68 69 /** 70 * Callback for when a change happens. 71 * 72 * @since 4.9.0 73 * @returns {void} 74 */ 75 component.onChange = function() { 76 component.dirty = true; 77 component.removeNotice( 'file_saved' ); 78 }; 79 80 /** 81 * Submit file via Ajax. 82 * 83 * @since 4.9.0 84 * @param {jQuery.Event} event - Event. 85 * @returns {void} 86 */ 87 component.submit = function( event ) { 88 var data = {}, request; 89 event.preventDefault(); // Prevent form submission in favor of Ajax below. 90 $.each( component.form.serializeArray(), function() { 91 data[ this.name ] = this.value; 92 } ); 93 94 // Use value from codemirror if present. 95 if ( component.instance ) { 96 data.newcontent = component.instance.codemirror.getValue(); 97 } 98 99 if ( component.isSaving ) { 100 return; 101 } 102 103 // Scroll ot the line that has the error. 104 if ( component.lintErrors.length ) { 105 component.instance.codemirror.setCursor( component.lintErrors[0].from.line ); 106 return; 107 } 108 109 component.isSaving = true; 110 component.textarea.prop( 'readonly', true ); 111 if ( component.instance ) { 112 component.instance.codemirror.setOption( 'readOnly', true ); 113 } 114 115 component.spinner.addClass( 'is-active' ); 116 request = wp.ajax.post( 'edit-theme-plugin-file', data ); 117 118 // Remove previous save notice before saving. 119 if ( component.lastSaveNoticeCode ) { 120 component.removeNotice( component.lastSaveNoticeCode ); 121 } 122 123 request.done( function ( response ) { 124 component.lastSaveNoticeCode = 'file_saved'; 125 component.addNotice({ 126 code: component.lastSaveNoticeCode, 127 type: 'success', 128 message: response.message, 129 dismissible: true 130 }); 131 component.dirty = false; 132 } ); 133 134 request.fail( function ( response ) { 135 var notice = $.extend( 136 { 137 code: 'save_error' 138 }, 139 response, 140 { 141 type: 'error', 142 dismissible: true 143 } 144 ); 145 component.lastSaveNoticeCode = notice.code; 146 component.addNotice( notice ); 147 } ); 148 149 request.always( function() { 150 component.spinner.removeClass( 'is-active' ); 151 component.isSaving = false; 152 153 component.textarea.prop( 'readonly', false ); 154 if ( component.instance ) { 155 component.instance.codemirror.setOption( 'readOnly', false ); 156 } 157 } ); 158 }; 159 160 /** 161 * Add notice. 162 * 163 * @since 4.9.0 164 * 165 * @param {object} notice - Notice. 166 * @param {string} notice.code - Code. 167 * @param {string} notice.type - Type. 168 * @param {string} notice.message - Message. 169 * @param {boolean} [notice.dismissible=false] - Dismissible. 170 * @param {Function} [notice.onDismiss] - Callback for when a user dismisses the notice. 171 * @returns {jQuery} Notice element. 172 */ 173 component.addNotice = function( notice ) { 174 var noticeElement; 175 176 if ( ! notice.code ) { 177 throw new Error( 'Missing code.' ); 178 } 179 180 // Only let one notice of a given type be displayed at a time. 181 component.removeNotice( notice.code ); 182 183 noticeElement = $( component.noticeTemplate( notice ) ); 184 noticeElement.hide(); 185 186 noticeElement.find( '.notice-dismiss' ).on( 'click', function() { 187 component.removeNotice( notice.code ); 188 if ( notice.onDismiss ) { 189 notice.onDismiss( notice ); 190 } 191 } ); 192 193 wp.a11y.speak( notice.message ); 194 195 component.noticesContainer.append( noticeElement ); 196 noticeElement.slideDown( 'fast' ); 197 component.noticeElements[ notice.code ] = noticeElement; 198 return noticeElement; 199 }; 200 201 /** 202 * Remove notice. 203 * 204 * @since 4.9.0 205 * 206 * @param {string} code - Notice code. 207 * @returns {boolean} Whether a notice was removed. 208 */ 209 component.removeNotice = function( code ) { 210 if ( component.noticeElements[ code ] ) { 211 component.noticeElements[ code ].slideUp( 'fast', function() { 212 $( this ).remove(); 213 } ); 214 delete component.noticeElements[ code ]; 215 return true; 216 } 217 return false; 218 }; 219 220 /** 221 * Initialize code editor. 222 * 223 * @since 4.9.0 224 * @returns {void} 225 */ 226 component.initCodeEditor = function initCodeEditor() { 227 var codeEditorSettings, editor; 228 229 codeEditorSettings = $.extend( {}, component.codeEditor ); 30 230 31 231 /** 32 232 * Handle tabbing to the field before the editor. 233 * 234 * @since 4.9.0 33 235 * 34 236 * @returns {void} … … 41 243 * Handle tabbing to the field after the editor. 42 244 * 245 * @since 4.9.0 246 * 43 247 * @returns {void} 44 248 */ … … 47 251 }; 48 252 49 // Create the error notice container. 50 noticeContainer = $( '<div id="file-editor-linting-error"></div>' ); 51 errorNotice = $( '<div class="inline notice notice-error"></div>' ); 52 noticeContainer.append( errorNotice ); 53 noticeContainer.hide(); 54 $( 'p.submit' ).before( noticeContainer ); 253 /** 254 * Handle change to the linting errors. 255 * 256 * @since 4.9.0 257 * 258 * @param {Array} errors - List of linting errors. 259 * @returns {void} 260 */ 261 codeEditorSettings.onChangeLintingErrors = function( errors ) { 262 component.lintErrors = errors; 263 264 // Only disable the button in onUpdateErrorNotice when there are errors so users can still feel they can click the button. 265 if ( 0 === errors.length ) { 266 component.submitButton.toggleClass( 'disabled', false ); 267 } 268 }; 55 269 56 270 /** 57 271 * Update error notice. 272 * 273 * @since 4.9.0 58 274 * 59 275 * @param {Array} errorAnnotations - Error annotations. … … 61 277 */ 62 278 codeEditorSettings.onUpdateErrorNotice = function onUpdateErrorNotice( errorAnnotations ) { 63 var message ;64 65 $( '#submit' ).prop( 'disabled', 0 !== errorAnnotations.length);279 var message, noticeElement; 280 281 component.submitButton.toggleClass( 'disabled', errorAnnotations.length > 0 ); 66 282 67 283 if ( 0 !== errorAnnotations.length ) { 68 errorNotice.empty();69 284 if ( 1 === errorAnnotations.length ) { 70 message = component.l10n. singular.replace( '%d', '1' );285 message = component.l10n.lintError.singular.replace( '%d', '1' ); 71 286 } else { 72 message = component.l10n. plural.replace( '%d', String( errorAnnotations.length ) );287 message = component.l10n.lintError.plural.replace( '%d', String( errorAnnotations.length ) ); 73 288 } 74 errorNotice.append( $( '<p></p>', { 75 text: message 76 } ) ); 77 noticeContainer.slideDown( 'fast' ); 78 wp.a11y.speak( message ); 289 noticeElement = component.addNotice({ 290 code: 'lint_errors', 291 type: 'error', 292 message: message, 293 dismissible: false 294 }); 295 noticeElement.find( 'input[type=checkbox]' ).on( 'click', function() { 296 codeEditorSettings.onChangeLintingErrors( [] ); 297 component.removeNotice( 'lint_errors' ); 298 } ); 79 299 } else { 80 noticeContainer.slideUp( 'fast' );300 component.removeNotice( 'lint_errors' ); 81 301 } 82 302 }; 83 303 84 304 editor = wp.codeEditor.initialize( $( '#newcontent' ), codeEditorSettings ); 305 editor.codemirror.on( 'change', component.onChange ); 85 306 86 307 // Improve the editor accessibility. -
trunk/src/wp-admin/plugin-editor.php
r41595 r41721 69 69 $plugin_files = get_plugin_files($plugin); 70 70 71 if ( empty( $file) )71 if ( empty( $file ) ) { 72 72 $file = $plugin_files[0]; 73 } 73 74 74 75 $file = validate_file_to_edit($file, $plugin_files); 75 76 $real_file = WP_PLUGIN_DIR . '/' . $file; 76 $scrollto = isset($_REQUEST['scrollto']) ? (int) $_REQUEST['scrollto'] : 0; 77 78 if ( isset( $_REQUEST['action'] ) && 'update' === $_REQUEST['action'] ) { 79 80 check_admin_referer('edit-plugin_' . $file); 81 82 $newcontent = wp_unslash( $_POST['newcontent'] ); 83 if ( is_writeable($real_file) ) { 84 $f = fopen($real_file, 'w+'); 85 fwrite($f, $newcontent); 86 fclose($f); 87 88 if ( preg_match( '/\.php$/', $real_file ) && function_exists( 'opcache_invalidate' ) ) { 89 opcache_invalidate( $real_file, true ); 90 } 91 92 $network_wide = is_plugin_active_for_network( $file ); 93 94 // Deactivate so we can test it. 95 if ( is_plugin_active( $plugin ) || isset( $_POST['phperror'] ) ) { 96 if ( is_plugin_active( $plugin ) ) { 97 deactivate_plugins( $plugin, true ); 98 } 99 100 if ( ! is_network_admin() ) { 101 update_option( 'recently_activated', array( $file => time() ) + (array) get_option( 'recently_activated' ) ); 102 } else { 103 update_site_option( 'recently_activated', array( $file => time() ) + (array) get_site_option( 'recently_activated' ) ); 104 } 105 106 wp_redirect( add_query_arg( '_wpnonce', wp_create_nonce( 'edit-plugin-test_' . $file ), "plugin-editor.php?file=$file&plugin=$plugin&liveupdate=1&scrollto=$scrollto&networkwide=" . $network_wide ) ); 107 exit; 108 } 109 wp_redirect( self_admin_url( "plugin-editor.php?file=$file&plugin=$plugin&a=te&scrollto=$scrollto" ) ); 110 } else { 111 wp_redirect( self_admin_url( "plugin-editor.php?file=$file&plugin=$plugin&scrollto=$scrollto" ) ); 112 } 113 exit; 114 115 } else { 116 117 if ( isset($_GET['liveupdate']) ) { 118 check_admin_referer('edit-plugin-test_' . $file); 119 120 $error = validate_plugin( $plugin ); 121 122 if ( is_wp_error( $error ) ) { 123 wp_die( $error ); 124 } 125 126 if ( ( ! empty( $_GET['networkwide'] ) && ! is_plugin_active_for_network( $file ) ) || ! is_plugin_active( $file ) ) { 127 activate_plugin( $plugin, "plugin-editor.php?file=" . urlencode( $file ) . "&phperror=1", ! empty( $_GET['networkwide'] ) ); 128 } // we'll override this later if the plugin can be included without fatal error 129 130 wp_redirect( self_admin_url( 'plugin-editor.php?file=' . urlencode( $file ) . '&plugin=' . urlencode( $plugin ) . "&a=te&scrollto=$scrollto" ) ); 77 78 // Handle fallback editing of file when JavaScript is not available. 79 $edit_error = null; 80 $posted_content = null; 81 if ( 'POST' === $_SERVER['REQUEST_METHOD'] ) { 82 $r = wp_edit_theme_plugin_file( wp_unslash( $_POST ) ); 83 if ( is_wp_error( $r ) ) { 84 $edit_error = $r; 85 if ( check_ajax_referer( 'edit-plugin_' . $file, 'nonce', false ) && isset( $_POST['newcontent'] ) ) { 86 $posted_content = wp_unslash( $_POST['newcontent'] ); 87 } 88 } else { 89 wp_redirect( add_query_arg( 90 array( 91 'a' => 1, // This means "success" for some reason. 92 'plugin' => $plugin, 93 'file' => $file, 94 ), 95 admin_url( 'plugin-editor.php' ) 96 ) ); 131 97 exit; 132 98 } 99 } 133 100 134 101 // List of allowable extensions 135 $editable_extensions = array( 136 'bash', 137 'conf', 138 'css', 139 'diff', 140 'htm', 141 'html', 142 'http', 143 'inc', 144 'include', 145 'js', 146 'json', 147 'jsx', 148 'less', 149 'md', 150 'patch', 151 'php', 152 'php3', 153 'php4', 154 'php5', 155 'php7', 156 'phps', 157 'phtml', 158 'sass', 159 'scss', 160 'sh', 161 'sql', 162 'svg', 163 'text', 164 'txt', 165 'xml', 166 'yaml', 167 'yml', 168 ); 169 170 /** 171 * Filters file type extensions editable in the plugin editor. 172 * 173 * @since 2.8.0 174 * 175 * @param array $editable_extensions An array of editable plugin file extensions. 176 */ 177 $editable_extensions = (array) apply_filters( 'editable_extensions', $editable_extensions ); 102 $editable_extensions = wp_get_plugin_file_editable_extensions( $plugin ); 178 103 179 104 if ( ! is_file($real_file) ) { … … 213 138 ); 214 139 215 $settings = wp_enqueue_code_editor( array( 'file' => $real_file ) );216 if ( ! empty( $settings ) ) {217 wp_enqueue_script( 'wp-theme-plugin-editor');218 wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function() { wp.themePluginEditor.init( %s ); } )', wp_json_encode( $settings ) ));219 }140 $settings = array( 141 'codeEditor' => wp_enqueue_code_editor( array( 'file' => $real_file ) ), 142 ); 143 wp_enqueue_script( 'wp-theme-plugin-editor' ); 144 wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function( $ ) { wp.themePluginEditor.init( $( "#template" ), %s ); } )', wp_json_encode( $settings ) ) ); 220 145 221 146 require_once(ABSPATH . 'wp-admin/admin-header.php'); … … 223 148 update_recently_edited(WP_PLUGIN_DIR . '/' . $file); 224 149 225 $content = file_get_contents( $real_file ); 150 if ( ! empty( $posted_content ) ) { 151 $content = $posted_content; 152 } else { 153 $content = file_get_contents( $real_file ); 154 } 226 155 227 156 if ( '.php' == substr( $real_file, strrpos( $real_file, '.' ) ) ) { … … 240 169 $content = esc_textarea( $content ); 241 170 ?> 242 <?php if (isset($_GET['a'])) : ?>243 <div id="message" class="updated notice is-dismissible"><p><?php _e('File edited successfully.') ?></p></div>244 <?php elseif (isset($_GET['phperror'])) : ?>245 <div id="message" class="notice notice-error"><p><?php _e( 'This plugin has been deactivated because your changes resulted in a <strong>fatal error</strong>.' ); ?></p>246 <?php247 if ( wp_verify_nonce( $_GET['_error_nonce'], 'plugin-activation-error_' . $plugin ) ) {248 $iframe_url = add_query_arg( array(249 'action' => 'error_scrape',250 'plugin' => urlencode( $plugin ),251 '_wpnonce' => urlencode( $_GET['_error_nonce'] ),252 ), admin_url( 'plugins.php' ) );253 ?>254 <iframe style="border:0" width="100%" height="70px" src="<?php echo esc_url( $iframe_url ); ?>"></iframe>255 <?php } ?>256 </div>257 <?php endif; ?>258 171 <div class="wrap"> 259 172 <h1><?php echo esc_html( $title ); ?></h1> 173 174 <?php if ( isset( $_GET['a'] ) ) : ?> 175 <div id="message" class="updated notice is-dismissible"> 176 <p><?php _e( 'File edited successfully.' ); ?></p> 177 </div> 178 <?php elseif ( is_wp_error( $edit_error ) ) : ?> 179 <div id="message" class="notice notice-error"> 180 <p><?php _e( 'There was an error while trying to update the file. You may need to fix something and try updating again.' ); ?></p> 181 <pre><?php echo esc_html( $edit_error->get_error_message() ? $edit_error->get_error_message() : $edit_error->get_error_code() ); ?></pre> 182 </div> 183 <?php endif; ?> 260 184 261 185 <div class="fileedit-sub"> … … 284 208 </div> 285 209 <div class="alignright"> 286 <form action="plugin-editor.php" method=" post">210 <form action="plugin-editor.php" method="get"> 287 211 <strong><label for="plugin"><?php _e('Select plugin to edit:'); ?> </label></strong> 288 212 <select name="plugin" id="plugin"> … … 309 233 <h2><?php _e( 'Plugin Files' ); ?></h2> 310 234 235 <?php 236 $plugin_editable_files = array(); 237 foreach ( $plugin_files as $plugin_file ) { 238 if ( preg_match('/\.([^.]+)$/', $plugin_file, $matches ) && in_array( $matches[1], $editable_extensions ) ) { 239 $plugin_editable_files[] = $plugin_file; 240 } 241 } 242 ?> 311 243 <ul> 312 <?php 313 foreach ( $plugin_files as $plugin_file ) : 314 // Get the extension of the file 315 if ( preg_match('/\.([^.]+)$/', $plugin_file, $matches) ) { 316 $ext = strtolower($matches[1]); 317 // If extension is not in the acceptable list, skip it 318 if ( !in_array( $ext, $editable_extensions ) ) 319 continue; 320 } else { 321 // No extension found 322 continue; 323 } 324 ?> 325 <li class="<?php echo esc_attr( $file === $plugin_file ? 'notice notice-info' : '' ); ?>"><a href="plugin-editor.php?file=<?php echo urlencode( $plugin_file ); ?>&plugin=<?php echo urlencode( $plugin ); ?>"><?php echo esc_html( $plugin_file ); ?></a></li> 326 <?php endforeach; ?> 244 <?php foreach ( $plugin_editable_files as $plugin_file ) : ?> 245 <li class="<?php echo esc_attr( $file === $plugin_file ? 'notice notice-info' : '' ); ?>"> 246 <a href="plugin-editor.php?file=<?php echo urlencode( $plugin_file ); ?>&plugin=<?php echo urlencode( $plugin ); ?>"><?php echo esc_html( preg_replace( '#^.+?/#', '', $plugin_file ) ); ?></a> 247 </li> 248 <?php endforeach; ?> 327 249 </ul> 328 250 </div> 329 251 <form name="template" id="template" action="plugin-editor.php" method="post"> 330 <?php wp_nonce_field( 'edit-plugin_' . $file)?>252 <?php wp_nonce_field( 'edit-plugin_' . $file, 'nonce' ); ?> 331 253 <div> 332 254 <label for="newcontent" id="theme-plugin-editor-label"><?php _e( 'Selected file content:' ); ?></label> … … 335 257 <input type="hidden" name="file" value="<?php echo esc_attr( $file ); ?>" /> 336 258 <input type="hidden" name="plugin" value="<?php echo esc_attr( $plugin ); ?>" /> 337 <input type="hidden" name="scrollto" id="scrollto" value="<?php echo esc_attr( $scrollto ); ?>" />338 259 </div> 339 260 <?php if ( !empty( $docs_select ) ) : ?> … … 341 262 <?php endif; ?> 342 263 <?php if ( is_writeable($real_file) ) : ?> 343 <?php if ( in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) ) { ?> 344 <div class="notice notice-warning inline active-plugin-edit-warning"> 345 <p><?php _e('<strong>Warning:</strong> Making changes to active plugins is not recommended. If your changes cause a fatal error, the plugin will be automatically deactivated.'); ?></p> 264 <div class="editor-notices"> 265 <?php if ( in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) ) { ?> 266 <div class="notice notice-warning inline active-plugin-edit-warning"> 267 <p><?php _e('<strong>Warning:</strong> Making changes to active plugins is not recommended.'); ?></p> 346 268 </div> 347 <?php } ?> 269 <?php } ?> 270 </div> 348 271 <p class="submit"> 349 <?php 350 if ( isset($_GET['phperror']) ) { 351 echo "<input type='hidden' name='phperror' value='1' />"; 352 submit_button( __( 'Update File and Attempt to Reactivate' ), 'primary', 'submit', false ); 353 } else { 354 submit_button( __( 'Update File' ), 'primary', 'submit', false ); 355 } 356 ?> 272 <?php submit_button( __( 'Update File' ), 'primary', 'submit', false ); ?> 273 <span class="spinner"></span> 357 274 </p> 358 275 <?php else : ?> 359 276 <p><em><?php _e('You need to make this file writable before you can save your changes. See <a href="https://codex.wordpress.org/Changing_File_Permissions">the Codex</a> for more information.'); ?></em></p> 360 277 <?php endif; ?> 278 <?php wp_print_file_editor_templates(); ?> 361 279 </form> 362 280 <br class="clear" /> 363 281 </div> 364 <script type="text/javascript">365 jQuery(document).ready(function($){366 $('#template').submit(function(){ $('#scrollto').val( $('#newcontent').scrollTop() ); });367 $('#newcontent').scrollTop( $('#scrollto').val() );368 });369 </script>370 282 <?php 371 }372 283 373 284 include(ABSPATH . "wp-admin/admin-footer.php"); -
trunk/src/wp-admin/theme-editor.php
r41640 r41721 70 70 $allowed_files = $style_files = array(); 71 71 $has_templates = false; 72 $default_types = array( 73 'bash', 74 'conf', 75 'css', 76 'diff', 77 'htm', 78 'html', 79 'http', 80 'inc', 81 'include', 82 'js', 83 'json', 84 'jsx', 85 'less', 86 'md', 87 'patch', 88 'php', 89 'php3', 90 'php4', 91 'php5', 92 'php7', 93 'phps', 94 'phtml', 95 'sass', 96 'scss', 97 'sh', 98 'sql', 99 'svg', 100 'text', 101 'txt', 102 'xml', 103 'yaml', 104 'yml', 105 ); 106 107 /** 108 * Filters the list of file types allowed for editing in the Theme editor. 109 * 110 * @since 4.4.0 111 * 112 * @param array $default_types List of file types. Default types include 'php' and 'css'. 113 * @param WP_Theme $theme The current Theme object. 114 */ 115 $file_types = apply_filters( 'wp_theme_editor_filetypes', $default_types, $theme ); 116 117 // Ensure that default types are still there. 118 $file_types = array_unique( array_merge( $file_types, $default_types ) ); 72 73 $file_types = wp_get_theme_file_editable_extensions( $theme ); 119 74 120 75 foreach ( $file_types as $type ) { … … 144 99 145 100 validate_file_to_edit( $file, $allowed_files ); 146 $scrollto = isset( $_REQUEST['scrollto'] ) ? (int) $_REQUEST['scrollto'] : 0; 147 148 switch( $action ) { 149 case 'update': 150 check_admin_referer( 'edit-theme_' . $file . $stylesheet ); 151 $newcontent = wp_unslash( $_POST['newcontent'] ); 152 $location = 'theme-editor.php?file=' . urlencode( $relative_file ) . '&theme=' . urlencode( $stylesheet ) . '&scrollto=' . $scrollto; 153 if ( is_writeable( $file ) ) { 154 // is_writable() not always reliable, check return value. see comments @ https://secure.php.net/is_writable 155 $f = fopen( $file, 'w+' ); 156 if ( $f !== false ) { 157 fwrite( $f, $newcontent ); 158 fclose( $f ); 159 $location .= '&updated=true'; 160 $theme->cache_delete(); 161 } 101 102 // Handle fallback editing of file when JavaScript is not available. 103 $edit_error = null; 104 $posted_content = null; 105 if ( 'POST' === $_SERVER['REQUEST_METHOD'] ) { 106 $r = wp_edit_theme_plugin_file( wp_unslash( $_POST ) ); 107 if ( is_wp_error( $r ) ) { 108 $edit_error = $r; 109 if ( check_ajax_referer( 'edit-theme_' . $file . $stylesheet, 'nonce', false ) && isset( $_POST['newcontent'] ) ) { 110 $posted_content = wp_unslash( $_POST['newcontent'] ); 111 } 112 } else { 113 wp_redirect( add_query_arg( 114 array( 115 'a' => 1, // This means "success" for some reason. 116 'theme' => $stylesheet, 117 'file' => $relative_file, 118 ), 119 admin_url( 'theme-editor.php' ) 120 ) ); 121 exit; 162 122 } 163 wp_redirect( $location ); 164 exit; 165 166 default: 167 168 $settings = wp_enqueue_code_editor( compact( 'file' ) ); 169 if ( ! empty( $settings ) ) { 170 wp_enqueue_script( 'wp-theme-plugin-editor' ); 171 wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function() { wp.themePluginEditor.init( %s ); } )', wp_json_encode( $settings ) ) ); 172 } 123 } 124 125 $settings = array( 126 'codeEditor' => wp_enqueue_code_editor( compact( 'file' ) ), 127 ); 128 wp_enqueue_script( 'wp-theme-plugin-editor' ); 129 wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function( $ ) { wp.themePluginEditor.init( $( "#template" ), %s ); } )', wp_json_encode( $settings ) ) ); 173 130 174 131 require_once( ABSPATH . 'wp-admin/admin-header.php' ); … … 180 137 181 138 $content = ''; 182 if ( ! $error && filesize( $file ) > 0 ) { 139 if ( ! empty( $posted_content ) ) { 140 $content = $posted_content; 141 } elseif ( ! $error && filesize( $file ) > 0 ) { 183 142 $f = fopen($file, 'r'); 184 143 $content = fread($f, filesize($file)); … … 198 157 } 199 158 200 if ( isset( $_GET['updated'] ) ) : ?>201 <div id="message" class="updated notice is-dismissible"><p><?php _e( 'File edited successfully.' ) ?></p></div>202 <?php endif;203 204 159 $file_description = get_file_description( $relative_file ); 205 160 $file_show = array_search( $file, array_filter( $allowed_files ) ); … … 212 167 <h1><?php echo esc_html( $title ); ?></h1> 213 168 169 <?php if ( isset( $_GET['a'] ) ) : ?> 170 <div id="message" class="updated notice is-dismissible"> 171 <p><?php _e( 'File edited successfully.' ); ?></p> 172 </div> 173 <?php elseif ( is_wp_error( $edit_error ) ) : ?> 174 <div id="message" class="notice notice-error"> 175 <p><?php _e( 'There was an error while trying to update the file. You may need to fix something and try updating again.' ); ?></p> 176 <pre><?php echo esc_html( $edit_error->get_error_message() ? $edit_error->get_error_message() : $edit_error->get_error_code() ); ?></pre> 177 </div> 178 <?php endif; ?> 179 214 180 <div class="fileedit-sub"> 215 181 <div class="alignleft"> … … 217 183 </div> 218 184 <div class="alignright"> 219 <form action="theme-editor.php" method=" post">185 <form action="theme-editor.php" method="get"> 220 186 <strong><label for="theme"><?php _e('Select theme to edit:'); ?> </label></strong> 221 187 <select name="theme" id="theme"> … … 300 266 else : ?> 301 267 <form name="template" id="template" action="theme-editor.php" method="post"> 302 <?php wp_nonce_field( 'edit-theme_' . $file . $stylesheet); ?>268 <?php wp_nonce_field( 'edit-theme_' . $file . $stylesheet, 'nonce' ); ?> 303 269 <div> 304 270 <label for="newcontent" id="theme-plugin-editor-label"><?php _e( 'Selected file content:' ); ?></label> … … 307 273 <input type="hidden" name="file" value="<?php echo esc_attr( $relative_file ); ?>" /> 308 274 <input type="hidden" name="theme" value="<?php echo esc_attr( $theme->get_stylesheet() ); ?>" /> 309 <input type="hidden" name="scrollto" id="scrollto" value="<?php echo esc_attr( $scrollto ); ?>" />310 275 </div> 311 276 <?php if ( ! empty( $functions ) ) : ?> … … 317 282 <?php endif; ?> 318 283 319 <div> 320 <?php if ( is_child_theme() && $theme->get_stylesheet() == get_template() ) : ?> 321 <p><?php if ( is_writeable( $file ) ) { ?><strong><?php _e( 'Caution:' ); ?></strong><?php } ?> 322 <?php _e( 'This is a file in your current parent theme.' ); ?></p> 323 <?php endif; ?> 324 <?php 325 if ( is_writeable( $file ) ) : 326 submit_button( __( 'Update File' ), 'primary', 'submit', true ); 327 else : ?> 328 <p><em><?php _e('You need to make this file writable before you can save your changes. See <a href="https://codex.wordpress.org/Changing_File_Permissions">the Codex</a> for more information.'); ?></em></p> 329 <?php endif; ?> 284 <div> 285 <div class="editor-notices"> 286 <?php if ( is_child_theme() && $theme->get_stylesheet() == get_template() ) : ?> 287 <div class="notice notice-warning inline"> 288 <p> 289 <?php if ( is_writeable( $file ) ) { ?><strong><?php _e( 'Caution:' ); ?></strong><?php } ?> 290 <?php _e( 'This is a file in your current parent theme.' ); ?> 291 </p> 292 </div> 293 <?php endif; ?> 330 294 </div> 295 <?php if ( is_writeable( $file ) ) : ?> 296 <p class="submit"> 297 <?php submit_button( __( 'Update File' ), 'primary', 'submit', false ); ?> 298 <span class="spinner"></span> 299 </p> 300 <?php else : ?> 301 <p><em><?php _e('You need to make this file writable before you can save your changes. See <a href="https://codex.wordpress.org/Changing_File_Permissions">the Codex</a> for more information.'); ?></em></p> 302 <?php endif; ?> 303 </div> 304 <?php wp_print_file_editor_templates(); ?> 331 305 </form> 332 306 <?php … … 335 309 <br class="clear" /> 336 310 </div> 337 <script type="text/javascript"> 338 jQuery(document).ready(function($){ 339 $('#template').submit(function(){ $('#scrollto').val( $('#newcontent').scrollTop() ); }); 340 $('#newcontent').scrollTop( $('#scrollto').val() ); 341 }); 342 </script> 343 <?php 344 break; 345 } 311 <?php 346 312 347 313 include(ABSPATH . 'wp-admin/admin-footer.php' ); -
trunk/src/wp-includes/js/wp-a11y.js
r41351 r41721 15 15 * @since 4.3.0 Introduced the 'ariaLive' argument. 16 16 * 17 * @param {String} message The message to be announced by Assistive Technologies. 18 * @param {String} ariaLive Optional. The politeness level for aria-live. Possible values: 19 * polite or assertive. Default polite. 17 * @param {String} message The message to be announced by Assistive Technologies. 18 * @param {String} [ariaLive] The politeness level for aria-live. Possible values: 19 * polite or assertive. Default polite. 20 * @returns {void} 20 21 */ 21 22 function speak( message, ariaLive ) { -
trunk/src/wp-includes/load.php
r40992 r41721 1113 1113 return apply_filters( 'file_mod_allowed', ! defined( 'DISALLOW_FILE_MODS' ) || ! DISALLOW_FILE_MODS, $context ); 1114 1114 } 1115 1116 /** 1117 * Start scraping edited file errors. 1118 * 1119 * @since 4.9.0 1120 */ 1121 function wp_start_scraping_edited_file_errors() { 1122 if ( ! isset( $_REQUEST['wp_scrape_key'] ) || ! isset( $_REQUEST['wp_scrape_nonce'] ) ) { 1123 return; 1124 } 1125 $key = substr( sanitize_key( wp_unslash( $_REQUEST['wp_scrape_key'] ) ), 0, 32 ); 1126 $nonce = wp_unslash( $_REQUEST['wp_scrape_nonce'] ); 1127 1128 if ( get_transient( 'scrape_key_' . $key ) !== $nonce ) { 1129 echo "###### begin_scraped_error:$key ######"; 1130 echo wp_json_encode( array( 1131 'code' => 'scrape_nonce_failure', 1132 'message' => __( 'Scrape nonce check failed. Please try again.' ), 1133 ) ); 1134 die(); 1135 } 1136 register_shutdown_function( 'wp_finalize_scraping_edited_file_errors', $key ); 1137 } 1138 1139 /** 1140 * Finalize scraping for edited file errors. 1141 * 1142 * @since 4.9.0 1143 * 1144 * @param string $scrape_key Scrape key. 1145 */ 1146 function wp_finalize_scraping_edited_file_errors( $scrape_key ) { 1147 $error = error_get_last(); 1148 if ( empty( $error ) ) { 1149 return; 1150 } 1151 if ( ! in_array( $error['type'], array( E_CORE_ERROR, E_COMPILE_ERROR, E_ERROR, E_PARSE, E_USER_ERROR, E_RECOVERABLE_ERROR ), true ) ) { 1152 return; 1153 } 1154 $error = str_replace( ABSPATH, '', $error ); 1155 echo "###### begin_scraped_error:$scrape_key ######"; 1156 echo wp_json_encode( $error ); 1157 } -
trunk/src/wp-includes/script-loader.php
r41694 r41721 472 472 $scripts->add( 'htmlhint-kses', '/wp-includes/js/codemirror/htmlhint-kses.js', array( 'htmlhint' ) ); 473 473 $scripts->add( 'code-editor', "/wp-admin/js/code-editor$suffix.js", array( 'jquery', 'wp-codemirror' ) ); 474 $scripts->add( 'wp-theme-plugin-editor', "/wp-admin/js/theme-plugin-editor$suffix.js", array( 'code-editor', 'jquery', 'jquery-ui-core', 'wp-a11y', 'underscore' ) ); 475 did_action( 'init' ) && $scripts->add_inline_script( 'wp-theme-plugin-editor', sprintf( 'wp.themePluginEditor.l10n = %s;', wp_json_encode( wp_array_slice_assoc( 476 /* translators: %d: error count */ 477 _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.' ), 478 array( 'singular', 'plural' ) 474 $scripts->add( 'wp-theme-plugin-editor', "/wp-admin/js/theme-plugin-editor$suffix.js", array( 'wp-util', 'jquery', 'jquery-ui-core', 'wp-a11y', 'underscore' ) ); 475 did_action( 'init' ) && $scripts->add_inline_script( 'wp-theme-plugin-editor', sprintf( 'wp.themePluginEditor.l10n = %s;', wp_json_encode( array( 476 'saveAlert' => __( 'The changes you made will be lost if you navigate away from this page.' ), 477 'lintError' => wp_array_slice_assoc( 478 /* translators: %d: error count */ 479 _n_noop( 'There is %d error which must be fixed before you can update this file.', 'There are %d errors which must be fixed before you can update this file.' ), 480 array( 'singular', 'plural' ) 481 ), 479 482 ) ) ) ); 480 483 -
trunk/src/wp-settings.php
r41289 r41721 295 295 create_initial_post_types(); 296 296 297 wp_start_scraping_edited_file_errors(); 298 297 299 // Register the default theme directory root 298 300 register_theme_directory( get_theme_root() );
Note: See TracChangeset
for help on using the changeset viewer.