<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[41721] trunk/src: File Editors: Introduce sandboxed live editing of PHP files with rollbacks for both themes and plugins.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/41721">41721</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/41721","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>westonruter</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2017-10-04 00:19:16 +0000 (Wed, 04 Oct 2017)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>File Editors: Introduce sandboxed live editing of PHP files with rollbacks for both themes and plugins.

* Edits to active plugins which cause PHP fatal errors will no longer auto-deactivate the plugin. Supersedes <a href="https://core.trac.wordpress.org/ticket/39766">#39766</a>.
* Introduce sandboxed PHP file edits for active themes, preventing accidental whitescreening of a user's site when introducing a fatal error.
* After writing a change to a PHP file for an active theme or plugin, perform loopback requests on the file editor admin screens and the homepage to check for fatal errors. If a fatal error is encountered, roll back the edited file and display the error to the user to fix and try again.
* Introduce a secure way to scrape PHP fatal errors from a site via `wp_start_scraping_edited_file_errors()` and `wp_finalize_scraping_edited_file_errors()`.
* Moves file modifications from `theme-editor.php` and `plugin-editor.php` to common `wp_edit_theme_plugin_file()` function.
* Refactor themes and plugin editors to submit file changes via Ajax instead of doing full page refreshes when JS is available.
* Use `get` method for theme/plugin dropdowns.
* Improve styling of plugin editors, including width of plugin/theme dropdowns.
* Improve notices API for theme/plugin editor JS component.
* Strip common base directory from plugin file list. See <a href="https://core.trac.wordpress.org/ticket/24048">#24048</a>.
* Factor out functions to list editable file types in `wp_get_theme_file_editable_extensions()` and `wp_get_plugin_file_editable_extensions()`.
* Scroll to line in editor that has linting error when attempting to save. See <a href="https://core.trac.wordpress.org/ticket/41886">#41886</a>.
* Add checkbox to dismiss lint errors to proceed with saving. See <a href="https://core.trac.wordpress.org/ticket/41887">#41887</a>.
* Only style the Update File button as disabled instead of actually disabling it for accessibility reasons.
* Ensure that value from CodeMirror is used instead of `textarea` when CodeMirror is present.
* Add "Are you sure?" check when leaving editor when there are unsaved changes.

Supersedes <a href="https://core.trac.wordpress.org/changeset/41560">[41560]</a>.
See <a href="https://core.trac.wordpress.org/ticket/39766">#39766</a>, <a href="https://core.trac.wordpress.org/ticket/24048">#24048</a>, <a href="https://core.trac.wordpress.org/ticket/41886">#41886</a>.
Props westonruter, Clorith, melchoyce, johnbillion, jjj, jdgrimes, azaozz.
Fixes <a href="https://core.trac.wordpress.org/ticket/21622">#21622</a>, <a href="https://core.trac.wordpress.org/ticket/41887">#41887</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadminadminajaxphp">trunk/src/wp-admin/admin-ajax.php</a></li>
<li><a href="#trunksrcwpadmincsscommoncss">trunk/src/wp-admin/css/common.css</a></li>
<li><a href="#trunksrcwpadminincludesajaxactionsphp">trunk/src/wp-admin/includes/ajax-actions.php</a></li>
<li><a href="#trunksrcwpadminincludesfilephp">trunk/src/wp-admin/includes/file.php</a></li>
<li><a href="#trunksrcwpadminjsthemeplugineditorjs">trunk/src/wp-admin/js/theme-plugin-editor.js</a></li>
<li><a href="#trunksrcwpadminplugineditorphp">trunk/src/wp-admin/plugin-editor.php</a></li>
<li><a href="#trunksrcwpadminthemeeditorphp">trunk/src/wp-admin/theme-editor.php</a></li>
<li><a href="#trunksrcwpincludesjswpa11yjs">trunk/src/wp-includes/js/wp-a11y.js</a></li>
<li><a href="#trunksrcwpincludesloadphp">trunk/src/wp-includes/load.php</a></li>
<li><a href="#trunksrcwpincludesscriptloaderphp">trunk/src/wp-includes/script-loader.php</a></li>
<li><a href="#trunksrcwpsettingsphp">trunk/src/wp-settings.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadminadminajaxphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/admin-ajax.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/admin-ajax.php 2017-10-04 00:00:47 UTC (rev 41720)
+++ trunk/src/wp-admin/admin-ajax.php   2017-10-04 00:19:16 UTC (rev 41721)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -64,7 +64,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        'parse-media-shortcode', 'destroy-sessions', 'install-plugin', 'update-plugin', 'crop-image',
</span><span class="cx" style="display: block; padding: 0 10px">        'generate-password', 'save-wporg-username', 'delete-plugin', 'search-plugins',
</span><span class="cx" style="display: block; padding: 0 10px">        'search-install-plugins', 'activate-plugin', 'update-theme', 'delete-theme', 'install-theme',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        'get-post-thumbnail-html', 'get-community-events',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'get-post-thumbnail-html', 'get-community-events', 'edit-theme-plugin-file',
</ins><span class="cx" style="display: block; padding: 0 10px"> );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> // Deprecated
</span></span></pre></div>
<a id="trunksrcwpadmincsscommoncss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/css/common.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/css/common.css 2017-10-04 00:00:47 UTC (rev 41720)
+++ trunk/src/wp-admin/css/common.css   2017-10-04 00:19:16 UTC (rev 41721)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2217,14 +2217,16 @@
</span><span class="cx" style="display: block; padding: 0 10px"> #template > div {
</span><span class="cx" style="display: block; padding: 0 10px">        margin-right: 190px;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#template .active-plugin-edit-warning {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#template .notice {
</ins><span class="cx" style="display: block; padding: 0 10px">         margin-top: 1em;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        margin-right: 30%;
-       margin-right: calc( 184px + 3% );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ margin-right: 3%;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#template .active-plugin-edit-warning p {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#template .notice p {
</ins><span class="cx" style="display: block; padding: 0 10px">         width: auto;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#template .submit .spinner {
+       float: none;
+}
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> .metabox-holder .stuffbox > h3, /* Back-compat for pre-4.4 */
</span><span class="cx" style="display: block; padding: 0 10px"> .metabox-holder .postbox > h3, /* Back-compat for pre-4.4 */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3032,10 +3034,14 @@
</span><span class="cx" style="display: block; padding: 0 10px"> #template textarea,
</span><span class="cx" style="display: block; padding: 0 10px"> #template .CodeMirror {
</span><span class="cx" style="display: block; padding: 0 10px">        width: 97%;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        height: calc( 100vh - 220px );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ height: calc( 100vh - 280px );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#templateside {
+       margin-top: 31px;
+       overflow: scroll;
+}
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#template label {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#theme-plugin-editor-label {
</ins><span class="cx" style="display: block; padding: 0 10px">         display: inline-block;
</span><span class="cx" style="display: block; padding: 0 10px">        margin-bottom: 1em;
</span><span class="cx" style="display: block; padding: 0 10px">        font-weight: 600;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3047,6 +3053,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">        direction: ltr;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.fileedit-sub #theme,
+.fileedit-sub #plugin {
+       max-width: 40%;
+}
+.fileedit-sub .alignright {
+       text-align: right;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> #template p {
</span><span class="cx" style="display: block; padding: 0 10px">        width: 97%;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3624,7 +3638,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        #template > div,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        #template  .active-plugin-edit-warning {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ #template .notice {
</ins><span class="cx" style="display: block; padding: 0 10px">                 float: none;
</span><span class="cx" style="display: block; padding: 0 10px">                margin: 1em 0;
</span><span class="cx" style="display: block; padding: 0 10px">                width: auto;
</span></span></pre></div>
<a id="trunksrcwpadminincludesajaxactionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/ajax-actions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/ajax-actions.php      2017-10-04 00:00:47 UTC (rev 41720)
+++ trunk/src/wp-admin/includes/ajax-actions.php        2017-10-04 00:19:16 UTC (rev 41721)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3966,3 +3966,26 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        wp_send_json_success( $status );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/**
+ * Ajax handler for editing a theme or plugin file.
+ *
+ * @since 4.9.0
+ * @see wp_edit_theme_plugin_file()
+ */
+function wp_ajax_edit_theme_plugin_file() {
+       $r = wp_edit_theme_plugin_file( wp_unslash( $_POST ) ); // Validation of args is done in wp_edit_theme_plugin_file().
+       if ( is_wp_error( $r ) ) {
+               wp_send_json_error( array_merge(
+                       array(
+                               'code' => $r->get_error_code(),
+                               'message' => $r->get_error_message(),
+                       ),
+                       (array) $r->get_error_data()
+               ) );
+       } else {
+               wp_send_json_success( array(
+                       'message' => __( 'File edited successfully.' ),
+               ) );
+       }
+}
</ins></span></pre></div>
<a id="trunksrcwpadminincludesfilephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/file.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/file.php      2017-10-04 00:00:47 UTC (rev 41720)
+++ trunk/src/wp-admin/includes/file.php        2017-10-04 00:19:16 UTC (rev 41721)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -70,7 +70,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 1.5.0
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @global array $wp_file_descriptions Theme file descriptions.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @global array $allowed_files        List of allowed files. 
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @global array $allowed_files        List of allowed files.
</ins><span class="cx" style="display: block; padding: 0 10px">  * @param string $file Filesystem path or filename
</span><span class="cx" style="display: block; padding: 0 10px">  * @return string Description of file from $wp_file_descriptions or basename of $file if description doesn't exist.
</span><span class="cx" style="display: block; padding: 0 10px">  *                Appends 'Page Template' to basename of $file if the file is a page template
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -153,6 +153,398 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Get list of file extensions that are editable in plugins.
+ *
+ * @since 4.9.0
+ *
+ * @param string $plugin Plugin.
+ * @return array File extensions.
+ */
+function wp_get_plugin_file_editable_extensions( $plugin ) {
+
+       $editable_extensions = array(
+               'bash',
+               'conf',
+               'css',
+               'diff',
+               'htm',
+               'html',
+               'http',
+               'inc',
+               'include',
+               'js',
+               'json',
+               'jsx',
+               'less',
+               'md',
+               'patch',
+               'php',
+               'php3',
+               'php4',
+               'php5',
+               'php7',
+               'phps',
+               'phtml',
+               'sass',
+               'scss',
+               'sh',
+               'sql',
+               'svg',
+               'text',
+               'txt',
+               'xml',
+               'yaml',
+               'yml',
+       );
+
+       /**
+        * Filters file type extensions editable in the plugin editor.
+        *
+        * @since 2.8.0
+        * @since 4.9.0 Adds $plugin param.
+        *
+        * @param string $plugin Plugin file.
+        * @param array $editable_extensions An array of editable plugin file extensions.
+        */
+       $editable_extensions = (array) apply_filters( 'editable_extensions', $editable_extensions, $plugin );
+
+       return $editable_extensions;
+}
+
+/**
+ * Get list of file extensions that are editable for a given theme.
+ *
+ * @param WP_Theme $theme Theme.
+ * @return array File extensions.
+ */
+function wp_get_theme_file_editable_extensions( $theme ) {
+
+       $default_types = array(
+               'bash',
+               'conf',
+               'css',
+               'diff',
+               'htm',
+               'html',
+               'http',
+               'inc',
+               'include',
+               'js',
+               'json',
+               'jsx',
+               'less',
+               'md',
+               'patch',
+               'php',
+               'php3',
+               'php4',
+               'php5',
+               'php7',
+               'phps',
+               'phtml',
+               'sass',
+               'scss',
+               'sh',
+               'sql',
+               'svg',
+               'text',
+               'txt',
+               'xml',
+               'yaml',
+               'yml',
+       );
+
+       /**
+        * Filters the list of file types allowed for editing in the Theme editor.
+        *
+        * @since 4.4.0
+        *
+        * @param array    $default_types List of file types. Default types include 'php' and 'css'.
+        * @param WP_Theme $theme         The current Theme object.
+        */
+       $file_types = apply_filters( 'wp_theme_editor_filetypes', $default_types, $theme );
+
+       // Ensure that default types are still there.
+       return array_unique( array_merge( $file_types, $default_types ) );
+}
+
+/**
+ * Print file editor templates (for plugins and themes).
+ *
+ * @since 4.9.0
+ */
+function wp_print_file_editor_templates() {
+       ?>
+       <script type="text/html" id="tmpl-wp-file-editor-notice">
+               <div class="notice inline notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }} {{ data.classes || '' }}">
+                       <# if ( 'php_error' === data.code ) { #>
+                               <p>
+                                       <?php
+                                       printf(
+                                               /* translators: %$1s is line number and %1$s is file path. */
+                                               __( '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.' ),
+                                               '{{ data.line }}',
+                                               '{{ data.file }}'
+                                       );
+                                       ?>
+                               </p>
+                               <pre>{{ data.message }}</pre>
+                       <# } else if ( 'file_not_writable' === data.code ) { #>
+                               <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>
+                       <# } else { #>
+                               <p>{{ data.message || data.code }}</p>
+
+                               <# if ( 'lint_errors' === data.code ) { #>
+                                       <p>
+                                               <# var elementId = 'el-' + String( Math.random() ); #>
+                                               <input id="{{ elementId }}"  type="checkbox">
+                                               <label for="{{ elementId }}"><?php _e( 'Update anyway, even though it might break your site?' ); ?></label>
+                                       </p>
+                               <# } #>
+                       <# } #>
+                       <# if ( data.dismissible ) { #>
+                               <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e( 'Dismiss' ); ?></span></button>
+                       <# } #>
+               </div>
+       </script>
+       <?php
+}
+
+/**
+ * Attempt to edit a file for a theme or plugin.
+ *
+ * When editing a PHP file, loopback requests will be made to the admin and the homepage
+ * to attempt to see if there is a fatal error introduced. If so, the PHP change will be
+ * reverted.
+ *
+ * @since 4.9.0
+ *
+ * @param array $args {
+ *     Args. Note that all of the arg values are already unslashed. They are, however,
+ *     coming straight from $_POST and are not validated or sanitized in any way.
+ *
+ *     @type string $file       Relative path to file.
+ *     @type string $plugin     Plugin being edited.
+ *     @type string $theme      Theme being edited.
+ *     @type string $newcontent New content for the file.
+ *     @type string $nonce      Nonce.
+ * }
+ * @return true|WP_Error True on success or `WP_Error` on failure.
+ */
+function wp_edit_theme_plugin_file( $args ) {
+       if ( empty( $args['file'] ) ) {
+               return new WP_Error( 'missing_file' );
+       }
+       $file = $args['file'];
+       if ( 0 !== validate_file( $file ) ) {
+               return new WP_Error( 'bad_file' );
+       }
+
+       if ( ! isset( $args['newcontent'] ) ) {
+               return new WP_Error( 'missing_content' );
+       }
+       $content = $args['newcontent'];
+
+       if ( ! isset( $args['nonce'] ) ) {
+               return new WP_Error( 'missing_nonce' );
+       }
+
+       $plugin = null;
+       $theme = null;
+       $real_file = null;
+       if ( ! empty( $args['plugin'] ) ) {
+               $plugin = $args['plugin'];
+
+               if ( ! current_user_can( 'edit_plugins' ) ) {
+                       return new WP_Error( 'unauthorized', __( 'Sorry, you are not allowed to edit plugins for this site.' ) );
+               }
+
+               if ( ! wp_verify_nonce( $args['nonce'], 'edit-plugin_' . $file ) ) {
+                       return new WP_Error( 'nonce_failure' );
+               }
+
+               if ( ! array_key_exists( $plugin, get_plugins() ) ) {
+                       return new WP_Error( 'invalid_plugin' );
+               }
+
+               if ( 0 !== validate_file( $file, get_plugin_files( $plugin ) ) ) {
+                       return new WP_Error( 'bad_plugin_file_path', __( 'Sorry, that file cannot be edited.' ) );
+               }
+
+               $editable_extensions = wp_get_plugin_file_editable_extensions( $plugin );
+
+               $real_file = WP_PLUGIN_DIR . '/' . $file;
+
+               $is_active = in_array(
+                       $plugin,
+                       (array) get_option( 'active_plugins', array() ),
+                       true
+               );
+
+       } elseif ( ! empty( $args['theme'] ) ) {
+               $stylesheet = $args['theme'];
+               if ( 0 !== validate_file( $stylesheet ) ) {
+                       return new WP_Error( 'bad_theme_path' );
+               }
+
+               if ( ! current_user_can( 'edit_themes' ) ) {
+                       return new WP_Error( 'unauthorized', __( 'Sorry, you are not allowed to edit templates for this site.' ) );
+               }
+
+               $theme = wp_get_theme( $stylesheet );
+               if ( ! $theme->exists() ) {
+                       return new WP_Error( 'non_existent_theme', __( 'The requested theme does not exist.' ) );
+               }
+
+               $real_file = $theme->get_stylesheet_directory() . '/' . $file;
+               if ( ! wp_verify_nonce( $args['nonce'], 'edit-theme_' . $real_file . $stylesheet ) ) {
+                       return new WP_Error( 'nonce_failure' );
+               }
+
+               if ( $theme->errors() && 'theme_no_stylesheet' === $theme->errors()->get_error_code() ) {
+                       return new WP_Error(
+                               'theme_no_stylesheet',
+                               __( 'The requested theme does not exist.' ) . ' ' . $theme->errors()->get_error_message()
+                       );
+               }
+
+               $editable_extensions = wp_get_theme_file_editable_extensions( $theme );
+
+               $allowed_files = array();
+               foreach ( $editable_extensions as $type ) {
+                       switch ( $type ) {
+                               case 'php':
+                                       $allowed_files = array_merge( $allowed_files, $theme->get_files( 'php', 1 ) );
+                                       break;
+                               case 'css':
+                                       $style_files = $theme->get_files( 'css' );
+                                       $allowed_files['style.css'] = $style_files['style.css'];
+                                       $allowed_files = array_merge( $allowed_files, $style_files );
+                                       break;
+                               default:
+                                       $allowed_files = array_merge( $allowed_files, $theme->get_files( $type ) );
+                                       break;
+                       }
+               }
+
+               if ( 0 !== validate_file( $real_file, $allowed_files ) ) {
+                       return new WP_Error( 'disallowed_theme_file', __( 'Sorry, that file cannot be edited.' ) );
+               }
+
+               $is_active = ( get_stylesheet() === $stylesheet || get_template() === $stylesheet );
+       } else {
+               return new WP_Error( 'missing_theme_or_plugin' );
+       }
+
+       // Ensure file is real.
+       if ( ! is_file( $real_file ) ) {
+               return new WP_Error( 'file_does_not_exist', __( 'No such file exists! Double check the name and try again.' ) );
+       }
+
+       // Ensure file extension is allowed.
+       $extension = null;
+       if ( preg_match( '/\.([^.]+)$/', $real_file, $matches ) ) {
+               $extension = strtolower( $matches[1] );
+               if ( ! in_array( $extension, $editable_extensions, true ) ) {
+                       return new WP_Error( 'illegal_file_type', __( 'Files of this type are not editable.' ) );
+               }
+       }
+
+       $previous_content = file_get_contents( $real_file );
+
+       if ( ! is_writeable( $real_file ) ) {
+               return new WP_Error( 'file_not_writable' );
+       }
+
+       $f = fopen( $real_file, 'w+' );
+       if ( false === $f ) {
+               return new WP_Error( 'file_not_writable' );
+       }
+
+       $written = fwrite( $f, $content );
+       fclose( $f );
+       if ( false === $written ) {
+               return new WP_Error( 'unable_to_write', __( 'Unable to write to file.' ) );
+       }
+       if ( 'php' === $extension && function_exists( 'opcache_invalidate' ) ) {
+               opcache_invalidate( $real_file, true );
+       }
+
+       if ( $is_active && 'php' === $extension ) {
+
+               $scrape_key = md5( rand() );
+               $transient = 'scrape_key_' . $scrape_key;
+               $scrape_nonce = strval( rand() );
+               set_transient( $transient, $scrape_nonce, 60 ); // It shouldn't take more than 60 seconds to make the two loopback requests.
+
+               $cookies = wp_unslash( $_COOKIE );
+               $scrape_params = array(
+                       'wp_scrape_key' => $scrape_key,
+                       'wp_scrape_nonce' => $scrape_nonce,
+               );
+               $headers = array(
+                       'Cache-Control' => 'no-cache',
+               );
+
+               $needle = "###### begin_scraped_error:$scrape_key ######";
+
+               // Attempt loopback request to editor to see if user just whitescreened themselves.
+               if ( $plugin ) {
+                       $url = add_query_arg( compact( 'plugin', 'file' ), admin_url( 'plugin-editor.php' ) );
+               } elseif ( isset( $stylesheet ) ) {
+                       $url = add_query_arg(
+                               array(
+                                       'theme' => $stylesheet,
+                                       'file' => $file,
+                               ),
+                               admin_url( 'theme-editor.php' )
+                       );
+               } else {
+                       $url = admin_url();
+               }
+               $url = add_query_arg( $scrape_params, $url );
+               $r = wp_remote_get( $url, compact( 'cookies', 'headers' ) );
+               $body = wp_remote_retrieve_body( $r );
+               $error_position = strpos( $body, $needle );
+
+               // Try making request to homepage as well to see if visitors have been whitescreened.
+               if ( false === $error_position ) {
+                       $url = home_url( '/' );
+                       $url = add_query_arg( $scrape_params, $url );
+                       $r = wp_remote_get( $url, compact( 'cookies', 'headers' ) );
+                       $body = wp_remote_retrieve_body( $r );
+                       $error_position = strpos( $body, $needle );
+               }
+
+               delete_transient( $transient );
+
+               if ( false !== $error_position ) {
+                       file_put_contents( $real_file, $previous_content );
+                       if ( function_exists( 'opcache_invalidate' ) ) {
+                               opcache_invalidate( $real_file, true );
+                       }
+
+                       $error_output = trim( substr( $body, $error_position + strlen( $needle ) ) );
+                       $error = json_decode( $error_output, true );
+                       if ( ! isset( $error['message'] ) ) {
+                               $message = $error_output;
+                       } else {
+                               $message = $error['message'];
+                               unset( $error['message'] );
+                       }
+                       return new WP_Error( 'php_error', $message, $error );
+               }
+       }
+
+       if ( $theme instanceof WP_Theme ) {
+               $theme->cache_delete();
+       }
+
+       return true;
+}
+
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px">  * Returns a filename of a Temporary unique file.
</span><span class="cx" style="display: block; padding: 0 10px">  * Please note that the calling function must unlink() this itself.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span></span></pre></div>
<a id="trunksrcwpadminjsthemeplugineditorjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/js/theme-plugin-editor.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/js/theme-plugin-editor.js      2017-10-04 00:00:47 UTC (rev 41720)
+++ trunk/src/wp-admin/js/theme-plugin-editor.js        2017-10-04 00:19:16 UTC (rev 41721)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -12,25 +12,227 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        lintError: {
</span><span class="cx" style="display: block; padding: 0 10px">                                singular: '',
</span><span class="cx" style="display: block; padding: 0 10px">                                plural: ''
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 },
+                       saveAlert: ''
</ins><span class="cx" style="display: block; padding: 0 10px">                 },
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                instance: null
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         codeEditor: {},
+               instance: null,
+               noticeElements: {},
+               dirty: false,
+               lintErrors: []
</ins><span class="cx" style="display: block; padding: 0 10px">         };
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Initialize component.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param {object} settings Settings.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @since 4.9.0
+        *
+        * @param {jQuery}         form - Form element.
+        * @param {object}         settings - Settings.
+        * @param {object|boolean} settings.codeEditor - Code editor settings (or `false` if syntax highlighting is disabled).
</ins><span class="cx" style="display: block; padding: 0 10px">          * @returns {void}
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        component.init = function( settings ) {
-               var codeEditorSettings, noticeContainer, errorNotice = [], editor;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ component.init = function init( form, settings ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                codeEditorSettings = $.extend( {}, settings );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         component.form = form;
+               if ( settings ) {
+                       $.extend( component, settings );
+               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                component.noticeTemplate = wp.template( 'wp-file-editor-notice' );
+               component.noticesContainer = component.form.find( '.editor-notices' );
+               component.submitButton = component.form.find( ':input[name=submit]' );
+               component.spinner = component.form.find( '.submit .spinner' );
+               component.form.on( 'submit', component.submit );
+               component.textarea = component.form.find( '#newcontent' );
+               component.textarea.on( 'change', component.onChange );
+
+               if ( false !== component.codeEditor ) {
+                       /*
+                        * Defer adding notices until after DOM ready as workaround for WP Admin injecting
+                        * its own managed dismiss buttons and also to prevent the editor from showing a notice
+                        * when the file had linting errors to begin with.
+                        */
+                       _.defer( function() {
+                               component.initCodeEditor();
+                       } );
+               }
+
+               $( window ).on( 'beforeunload', function() {
+                       if ( component.dirty ) {
+                               return component.l10n.saveAlert;
+                       }
+                       return undefined;
+               } );
+       };
+
+       /**
+        * Callback for when a change happens.
+        *
+        * @since 4.9.0
+        * @returns {void}
+        */
+       component.onChange = function() {
+               component.dirty = true;
+               component.removeNotice( 'file_saved' );
+       };
+
+       /**
+        * Submit file via Ajax.
+        *
+        * @since 4.9.0
+        * @param {jQuery.Event} event - Event.
+        * @returns {void}
+        */
+       component.submit = function( event ) {
+               var data = {}, request;
+               event.preventDefault(); // Prevent form submission in favor of Ajax below.
+               $.each( component.form.serializeArray(), function() {
+                       data[ this.name ] = this.value;
+               } );
+
+               // Use value from codemirror if present.
+               if ( component.instance ) {
+                       data.newcontent = component.instance.codemirror.getValue();
+               }
+
+               if ( component.isSaving ) {
+                       return;
+               }
+
+               // Scroll ot the line that has the error.
+               if ( component.lintErrors.length ) {
+                       component.instance.codemirror.setCursor( component.lintErrors[0].from.line );
+                       return;
+               }
+
+               component.isSaving = true;
+               component.textarea.prop( 'readonly', true );
+               if ( component.instance ) {
+                       component.instance.codemirror.setOption( 'readOnly', true );
+               }
+
+               component.spinner.addClass( 'is-active' );
+               request = wp.ajax.post( 'edit-theme-plugin-file', data );
+
+               // Remove previous save notice before saving.
+               if ( component.lastSaveNoticeCode ) {
+                       component.removeNotice( component.lastSaveNoticeCode );
+               }
+
+               request.done( function ( response ) {
+                       component.lastSaveNoticeCode = 'file_saved';
+                       component.addNotice({
+                               code: component.lastSaveNoticeCode,
+                               type: 'success',
+                               message: response.message,
+                               dismissible: true
+                       });
+                       component.dirty = false;
+               } );
+
+               request.fail( function ( response ) {
+                       var notice = $.extend(
+                               {
+                                       code: 'save_error'
+                               },
+                               response,
+                               {
+                                       type: 'error',
+                                       dismissible: true
+                               }
+                       );
+                       component.lastSaveNoticeCode = notice.code;
+                       component.addNotice( notice );
+               } );
+
+               request.always( function() {
+                       component.spinner.removeClass( 'is-active' );
+                       component.isSaving = false;
+
+                       component.textarea.prop( 'readonly', false );
+                       if ( component.instance ) {
+                               component.instance.codemirror.setOption( 'readOnly', false );
+                       }
+               } );
+       };
+
+       /**
+        * Add notice.
+        *
+        * @since 4.9.0
+        *
+        * @param {object}   notice - Notice.
+        * @param {string}   notice.code - Code.
+        * @param {string}   notice.type - Type.
+        * @param {string}   notice.message - Message.
+        * @param {boolean}  [notice.dismissible=false] - Dismissible.
+        * @param {Function} [notice.onDismiss] - Callback for when a user dismisses the notice.
+        * @returns {jQuery} Notice element.
+        */
+       component.addNotice = function( notice ) {
+               var noticeElement;
+
+               if ( ! notice.code ) {
+                       throw new Error( 'Missing code.' );
+               }
+
+               // Only let one notice of a given type be displayed at a time.
+               component.removeNotice( notice.code );
+
+               noticeElement = $( component.noticeTemplate( notice ) );
+               noticeElement.hide();
+
+               noticeElement.find( '.notice-dismiss' ).on( 'click', function() {
+                       component.removeNotice( notice.code );
+                       if ( notice.onDismiss ) {
+                               notice.onDismiss( notice );
+                       }
+               } );
+
+               wp.a11y.speak( notice.message );
+
+               component.noticesContainer.append( noticeElement );
+               noticeElement.slideDown( 'fast' );
+               component.noticeElements[ notice.code ] = noticeElement;
+               return noticeElement;
+       };
+
+       /**
+        * Remove notice.
+        *
+        * @since 4.9.0
+        *
+        * @param {string} code - Notice code.
+        * @returns {boolean} Whether a notice was removed.
+        */
+       component.removeNotice = function( code ) {
+               if ( component.noticeElements[ code ] ) {
+                       component.noticeElements[ code ].slideUp( 'fast', function() {
+                               $( this ).remove();
+                       } );
+                       delete component.noticeElements[ code ];
+                       return true;
+               }
+               return false;
+       };
+
+       /**
+        * Initialize code editor.
+        *
+        * @since 4.9.0
+        * @returns {void}
+        */
+       component.initCodeEditor = function initCodeEditor() {
+               var codeEditorSettings, editor;
+
+               codeEditorSettings = $.extend( {}, component.codeEditor );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 /**
</span><span class="cx" style="display: block; padding: 0 10px">                 * Handle tabbing to the field before the editor.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * @since 4.9.0
+                *
</ins><span class="cx" style="display: block; padding: 0 10px">                  * @returns {void}
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                codeEditorSettings.onTabPrevious = function() {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -40,48 +242,67 @@
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><span class="cx" style="display: block; padding: 0 10px">                 * Handle tabbing to the field after the editor.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * @since 4.9.0
+                *
</ins><span class="cx" style="display: block; padding: 0 10px">                  * @returns {void}
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                codeEditorSettings.onTabNext = function() {
</span><span class="cx" style="display: block; padding: 0 10px">                        $( '#template' ).find( ':tabbable:not(.CodeMirror-code)' ).first().focus();
</span><span class="cx" style="display: block; padding: 0 10px">                };
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Create the error notice container.
-               noticeContainer = $( '<div id="file-editor-linting-error"></div>' );
-               errorNotice = $( '<div class="inline notice notice-error"></div>' );
-               noticeContainer.append( errorNotice );
-               noticeContainer.hide();
-               $( 'p.submit' ).before( noticeContainer );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
+                * Handle change to the linting errors.
+                *
+                * @since 4.9.0
+                *
+                * @param {Array} errors - List of linting errors.
+                * @returns {void}
+                */
+               codeEditorSettings.onChangeLintingErrors = function( errors ) {
+                       component.lintErrors = errors;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        // Only disable the button in onUpdateErrorNotice when there are errors so users can still feel they can click the button.
+                       if ( 0 === errors.length ) {
+                               component.submitButton.toggleClass( 'disabled', false );
+                       }
+               };
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 /**
</span><span class="cx" style="display: block; padding: 0 10px">                 * Update error notice.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * @since 4.9.0
+                *
</ins><span class="cx" style="display: block; padding: 0 10px">                  * @param {Array} errorAnnotations - Error annotations.
</span><span class="cx" style="display: block; padding: 0 10px">                 * @returns {void}
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                codeEditorSettings.onUpdateErrorNotice = function onUpdateErrorNotice( errorAnnotations ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        var message;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 var message, noticeElement;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $( '#submit' ).prop( 'disabled', 0 !== errorAnnotations.length );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 component.submitButton.toggleClass( 'disabled', errorAnnotations.length > 0 );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( 0 !== errorAnnotations.length ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                errorNotice.empty();
</del><span class="cx" style="display: block; padding: 0 10px">                                 if ( 1 === errorAnnotations.length ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        message = component.l10n.singular.replace( '%d', '1' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 message = component.l10n.lintError.singular.replace( '%d', '1' );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        message = component.l10n.plural.replace( '%d', String( errorAnnotations.length ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 message = component.l10n.lintError.plural.replace( '%d', String( errorAnnotations.length ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                errorNotice.append( $( '<p></p>', {
-                                       text: message
-                               } ) );
-                               noticeContainer.slideDown( 'fast' );
-                               wp.a11y.speak( message );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         noticeElement = component.addNotice({
+                                       code: 'lint_errors',
+                                       type: 'error',
+                                       message: message,
+                                       dismissible: false
+                               });
+                               noticeElement.find( 'input[type=checkbox]' ).on( 'click', function() {
+                                       codeEditorSettings.onChangeLintingErrors( [] );
+                                       component.removeNotice( 'lint_errors' );
+                               } );
</ins><span class="cx" style="display: block; padding: 0 10px">                         } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                noticeContainer.slideUp( 'fast' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         component.removeNotice( 'lint_errors' );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                };
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                editor = wp.codeEditor.initialize( $( '#newcontent' ), codeEditorSettings );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                editor.codemirror.on( 'change', component.onChange );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Improve the editor accessibility.
</span><span class="cx" style="display: block; padding: 0 10px">                $( editor.codemirror.display.lineDiv )
</span></span></pre></div>
<a id="trunksrcwpadminplugineditorphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/plugin-editor.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/plugin-editor.php      2017-10-04 00:00:47 UTC (rev 41720)
+++ trunk/src/wp-admin/plugin-editor.php        2017-10-04 00:19:16 UTC (rev 41721)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -68,114 +68,39 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> $plugin_files = get_plugin_files($plugin);
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-if ( empty($file) )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+if ( empty( $file ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">         $file = $plugin_files[0];
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+}
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> $file = validate_file_to_edit($file, $plugin_files);
</span><span class="cx" style="display: block; padding: 0 10px"> $real_file = WP_PLUGIN_DIR . '/' . $file;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-$scrollto = isset($_REQUEST['scrollto']) ? (int) $_REQUEST['scrollto'] : 0;
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-if ( isset( $_REQUEST['action'] ) && 'update' === $_REQUEST['action'] ) {
-
-       check_admin_referer('edit-plugin_' . $file);
-
-       $newcontent = wp_unslash( $_POST['newcontent'] );
-       if ( is_writeable($real_file) ) {
-               $f = fopen($real_file, 'w+');
-               fwrite($f, $newcontent);
-               fclose($f);
-
-               if ( preg_match( '/\.php$/', $real_file ) && function_exists( 'opcache_invalidate' ) ) {
-                       opcache_invalidate( $real_file, true );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Handle fallback editing of file when JavaScript is not available.
+$edit_error = null;
+$posted_content = null;
+if ( 'POST' === $_SERVER['REQUEST_METHOD'] ) {
+       $r = wp_edit_theme_plugin_file( wp_unslash( $_POST ) );
+       if ( is_wp_error( $r ) ) {
+               $edit_error = $r;
+               if ( check_ajax_referer( 'edit-plugin_' . $file, 'nonce', false ) && isset( $_POST['newcontent'] ) ) {
+                       $posted_content = wp_unslash( $_POST['newcontent'] );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-               $network_wide = is_plugin_active_for_network( $file );
-
-               // Deactivate so we can test it.
-               if ( is_plugin_active( $plugin ) || isset( $_POST['phperror'] ) ) {
-                       if ( is_plugin_active( $plugin ) ) {
-                               deactivate_plugins( $plugin, true );
-                       }
-
-                       if ( ! is_network_admin() ) {
-                               update_option( 'recently_activated', array( $file => time() ) + (array) get_option( 'recently_activated' ) );
-                       } else {
-                               update_site_option( 'recently_activated', array( $file => time() ) + (array) get_site_option( 'recently_activated' ) );
-                       }
-
-                       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 ) );
-                       exit;
-               }
-               wp_redirect( self_admin_url( "plugin-editor.php?file=$file&plugin=$plugin&a=te&scrollto=$scrollto" ) );
</del><span class="cx" style="display: block; padding: 0 10px">         } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp_redirect( self_admin_url( "plugin-editor.php?file=$file&plugin=$plugin&scrollto=$scrollto" ) );
-       }
-       exit;
-
-} else {
-
-       if ( isset($_GET['liveupdate']) ) {
-               check_admin_referer('edit-plugin-test_' . $file);
-
-               $error = validate_plugin( $plugin );
-
-               if ( is_wp_error( $error ) ) {
-                       wp_die( $error );
-               }
-
-               if ( ( ! empty( $_GET['networkwide'] ) && ! is_plugin_active_for_network( $file ) ) || ! is_plugin_active( $file ) ) {
-                       activate_plugin( $plugin, "plugin-editor.php?file=" . urlencode( $file ) . "&phperror=1", ! empty( $_GET['networkwide'] ) );
-               } // we'll override this later if the plugin can be included without fatal error
-
-               wp_redirect( self_admin_url( 'plugin-editor.php?file=' . urlencode( $file ) . '&plugin=' . urlencode( $plugin ) . "&a=te&scrollto=$scrollto" ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         wp_redirect( add_query_arg(
+                       array(
+                               'a' => 1, // This means "success" for some reason.
+                               'plugin' => $plugin,
+                               'file' => $file,
+                       ),
+                       admin_url( 'plugin-editor.php' )
+               ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 exit;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+}
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        // List of allowable extensions
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $editable_extensions = array(
-               'bash',
-               'conf',
-               'css',
-               'diff',
-               'htm',
-               'html',
-               'http',
-               'inc',
-               'include',
-               'js',
-               'json',
-               'jsx',
-               'less',
-               'md',
-               'patch',
-               'php',
-               'php3',
-               'php4',
-               'php5',
-               'php7',
-               'phps',
-               'phtml',
-               'sass',
-               'scss',
-               'sh',
-               'sql',
-               'svg',
-               'text',
-               'txt',
-               'xml',
-               'yaml',
-               'yml',
-       );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $editable_extensions = wp_get_plugin_file_editable_extensions( $plugin );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        /**
-        * Filters file type extensions editable in the plugin editor.
-        *
-        * @since 2.8.0
-        *
-        * @param array $editable_extensions An array of editable plugin file extensions.
-        */
-       $editable_extensions = (array) apply_filters( 'editable_extensions', $editable_extensions );
-
</del><span class="cx" style="display: block; padding: 0 10px">         if ( ! is_file($real_file) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                wp_die(sprintf('<p>%s</p>', __('No such file exists! Double check the name and try again.')));
</span><span class="cx" style="display: block; padding: 0 10px">        } else {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -212,17 +137,21 @@
</span><span class="cx" style="display: block; padding: 0 10px">                '<p>' . __('<a href="https://wordpress.org/support/">Support Forums</a>') . '</p>'
</span><span class="cx" style="display: block; padding: 0 10px">        );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $settings = wp_enqueue_code_editor( array( 'file' => $real_file ) );
-       if ( ! empty( $settings ) ) {
-               wp_enqueue_script( 'wp-theme-plugin-editor' );
-               wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function() { wp.themePluginEditor.init( %s ); } )', wp_json_encode( $settings ) ) );
-       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $settings = array(
+               'codeEditor' => wp_enqueue_code_editor( array( 'file' => $real_file ) ),
+       );
+       wp_enqueue_script( 'wp-theme-plugin-editor' );
+       wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function( $ ) { wp.themePluginEditor.init( $( "#template" ), %s ); } )', wp_json_encode( $settings ) ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        require_once(ABSPATH . 'wp-admin/admin-header.php');
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        update_recently_edited(WP_PLUGIN_DIR . '/' . $file);
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $content = file_get_contents( $real_file );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( ! empty( $posted_content ) ) {
+               $content = $posted_content;
+       } else {
+               $content = file_get_contents( $real_file );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        if ( '.php' == substr( $real_file, strrpos( $real_file, '.' ) ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $functions = wp_doc_link_parse( $content );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -239,25 +168,20 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        $content = esc_textarea( $content );
</span><span class="cx" style="display: block; padding: 0 10px">        ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php if (isset($_GET['a'])) : ?>
- <div id="message" class="updated notice is-dismissible"><p><?php _e('File edited successfully.') ?></p></div>
-<?php elseif (isset($_GET['phperror'])) : ?>
- <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>
-       <?php
-               if ( wp_verify_nonce( $_GET['_error_nonce'], 'plugin-activation-error_' . $plugin ) ) {
-                       $iframe_url = add_query_arg( array(
-                               'action'   => 'error_scrape',
-                               'plugin'   => urlencode( $plugin ),
-                               '_wpnonce' => urlencode( $_GET['_error_nonce'] ),
-                       ), admin_url( 'plugins.php' ) );
-                       ?>
-       <iframe style="border:0" width="100%" height="70px" src="<?php echo esc_url( $iframe_url ); ?>"></iframe>
-       <?php } ?>
-</div>
-<?php endif; ?>
</del><span class="cx" style="display: block; padding: 0 10px"> <div class="wrap">
</span><span class="cx" style="display: block; padding: 0 10px"> <h1><?php echo esc_html( $title ); ?></h1>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php if ( isset( $_GET['a'] ) ) : ?>
+       <div id="message" class="updated notice is-dismissible">
+               <p><?php _e( 'File edited successfully.' ); ?></p>
+       </div>
+<?php elseif ( is_wp_error( $edit_error ) ) : ?>
+       <div id="message" class="notice notice-error">
+               <p><?php _e( 'There was an error while trying to update the file. You may need to fix something and try updating again.' ); ?></p>
+               <pre><?php echo esc_html( $edit_error->get_error_message() ? $edit_error->get_error_message() : $edit_error->get_error_code() ); ?></pre>
+       </div>
+<?php endif; ?>
+
</ins><span class="cx" style="display: block; padding: 0 10px"> <div class="fileedit-sub">
</span><span class="cx" style="display: block; padding: 0 10px"> <div class="alignleft">
</span><span class="cx" style="display: block; padding: 0 10px"> <h2>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -283,7 +207,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> </h2>
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px"> <div class="alignright">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <form action="plugin-editor.php" method="post">
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <form action="plugin-editor.php" method="get">
</ins><span class="cx" style="display: block; padding: 0 10px">                 <strong><label for="plugin"><?php _e('Select plugin to edit:'); ?> </label></strong>
</span><span class="cx" style="display: block; padding: 0 10px">                <select name="plugin" id="plugin">
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -308,66 +232,53 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <div id="templateside">
</span><span class="cx" style="display: block; padding: 0 10px">        <h2><?php _e( 'Plugin Files' ); ?></h2>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <ul>
-<?php
-foreach ( $plugin_files as $plugin_file ) :
-       // Get the extension of the file
-       if ( preg_match('/\.([^.]+)$/', $plugin_file, $matches) ) {
-               $ext = strtolower($matches[1]);
-               // If extension is not in the acceptable list, skip it
-               if ( !in_array( $ext, $editable_extensions ) )
-                       continue;
-       } else {
-               // No extension found
-               continue;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <?php
+       $plugin_editable_files = array();
+       foreach ( $plugin_files as $plugin_file ) {
+               if ( preg_match('/\.([^.]+)$/', $plugin_file, $matches ) && in_array( $matches[1], $editable_extensions ) ) {
+                       $plugin_editable_files[] = $plugin_file;
+               }
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px">        ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <li class="<?php echo esc_attr( $file === $plugin_file ? 'notice notice-info' : '' ); ?>"><a href="plugin-editor.php?file=<?php echo urlencode( $plugin_file ); ?>&amp;plugin=<?php echo urlencode( $plugin ); ?>"><?php echo esc_html( $plugin_file ); ?></a></li>
-<?php endforeach; ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <ul>
+       <?php foreach ( $plugin_editable_files as $plugin_file ) : ?>
+               <li class="<?php echo esc_attr( $file === $plugin_file ? 'notice notice-info' : '' ); ?>">
+                       <a href="plugin-editor.php?file=<?php echo urlencode( $plugin_file ); ?>&amp;plugin=<?php echo urlencode( $plugin ); ?>"><?php echo esc_html( preg_replace( '#^.+?/#', '', $plugin_file ) ); ?></a>
+               </li>
+       <?php endforeach; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">         </ul>
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px"> <form name="template" id="template" action="plugin-editor.php" method="post">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <?php wp_nonce_field('edit-plugin_' . $file) ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <?php wp_nonce_field( 'edit-plugin_' . $file, 'nonce' ); ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <div>
</span><span class="cx" style="display: block; padding: 0 10px">                        <label for="newcontent" id="theme-plugin-editor-label"><?php _e( 'Selected file content:' ); ?></label>
</span><span class="cx" style="display: block; padding: 0 10px">                        <textarea cols="70" rows="25" name="newcontent" id="newcontent" aria-describedby="editor-keyboard-trap-help-1 editor-keyboard-trap-help-2 editor-keyboard-trap-help-3 editor-keyboard-trap-help-4"><?php echo $content; ?></textarea>
</span><span class="cx" style="display: block; padding: 0 10px">                        <input type="hidden" name="action" value="update" />
</span><span class="cx" style="display: block; padding: 0 10px">                        <input type="hidden" name="file" value="<?php echo esc_attr( $file ); ?>" />
</span><span class="cx" style="display: block; padding: 0 10px">                        <input type="hidden" name="plugin" value="<?php echo esc_attr( $plugin ); ?>" />
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <input type="hidden" name="scrollto" id="scrollto" value="<?php echo esc_attr( $scrollto ); ?>" />
</del><span class="cx" style="display: block; padding: 0 10px">                 </div>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php if ( !empty( $docs_select ) ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                <div id="documentation" class="hide-if-no-js"><label for="docs-list"><?php _e('Documentation:') ?></label> <?php echo $docs_select ?> <input type="button" class="button" value="<?php esc_attr_e( 'Look Up' ) ?> " onclick="if ( '' != jQuery('#docs-list').val() ) { window.open( 'https://api.wordpress.org/core/handbook/1.0/?function=' + escape( jQuery( '#docs-list' ).val() ) + '&amp;locale=<?php echo urlencode( get_user_locale() ) ?>&amp;version=<?php echo urlencode( get_bloginfo( 'version' ) ) ?>&amp;redirect=true'); }" /></div>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px"> <?php if ( is_writeable($real_file) ) : ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <?php if ( in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) ) { ?>
-               <div class="notice notice-warning inline active-plugin-edit-warning">
-                       <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>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <div class="editor-notices">
+               <?php if ( in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) ) { ?>
+                       <div class="notice notice-warning inline active-plugin-edit-warning">
+                       <p><?php _e('<strong>Warning:</strong> Making changes to active plugins is not recommended.'); ?></p>
</ins><span class="cx" style="display: block; padding: 0 10px">                 </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <?php } ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php } ?>
+       </div>
</ins><span class="cx" style="display: block; padding: 0 10px">         <p class="submit">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <?php
-               if ( isset($_GET['phperror']) ) {
-                       echo "<input type='hidden' name='phperror' value='1' />";
-                       submit_button( __( 'Update File and Attempt to Reactivate' ), 'primary', 'submit', false );
-               } else {
-                       submit_button( __( 'Update File' ), 'primary', 'submit', false );
-               }
-       ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php submit_button( __( 'Update File' ), 'primary', 'submit', false ); ?>
+               <span class="spinner"></span>
</ins><span class="cx" style="display: block; padding: 0 10px">         </p>
</span><span class="cx" style="display: block; padding: 0 10px"> <?php else : ?>
</span><span class="cx" style="display: block; padding: 0 10px">        <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>
</span><span class="cx" style="display: block; padding: 0 10px"> <?php endif; ?>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php wp_print_file_editor_templates(); ?>
</ins><span class="cx" style="display: block; padding: 0 10px"> </form>
</span><span class="cx" style="display: block; padding: 0 10px"> <br class="clear" />
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<script type="text/javascript">
-jQuery(document).ready(function($){
-       $('#template').submit(function(){ $('#scrollto').val( $('#newcontent').scrollTop() ); });
-       $('#newcontent').scrollTop( $('#scrollto').val() );
-});
-</script>
</del><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-}
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> include(ABSPATH . "wp-admin/admin-footer.php");
</span></span></pre></div>
<a id="trunksrcwpadminthemeeditorphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/theme-editor.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/theme-editor.php       2017-10-04 00:00:47 UTC (rev 41720)
+++ trunk/src/wp-admin/theme-editor.php 2017-10-04 00:19:16 UTC (rev 41721)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -69,54 +69,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> $allowed_files = $style_files = array();
</span><span class="cx" style="display: block; padding: 0 10px"> $has_templates = false;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-$default_types = array(
-       'bash',
-       'conf',
-       'css',
-       'diff',
-       'htm',
-       'html',
-       'http',
-       'inc',
-       'include',
-       'js',
-       'json',
-       'jsx',
-       'less',
-       'md',
-       'patch',
-       'php',
-       'php3',
-       'php4',
-       'php5',
-       'php7',
-       'phps',
-       'phtml',
-       'sass',
-       'scss',
-       'sh',
-       'sql',
-       'svg',
-       'text',
-       'txt',
-       'xml',
-       'yaml',
-       'yml',
-);
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * Filters the list of file types allowed for editing in the Theme editor.
- *
- * @since 4.4.0
- *
- * @param array    $default_types List of file types. Default types include 'php' and 'css'.
- * @param WP_Theme $theme         The current Theme object.
- */
-$file_types = apply_filters( 'wp_theme_editor_filetypes', $default_types, $theme );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+$file_types = wp_get_theme_file_editable_extensions( $theme );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// Ensure that default types are still there.
-$file_types = array_unique( array_merge( $file_types, $default_types ) );
-
</del><span class="cx" style="display: block; padding: 0 10px"> foreach ( $file_types as $type ) {
</span><span class="cx" style="display: block; padding: 0 10px">        switch ( $type ) {
</span><span class="cx" style="display: block; padding: 0 10px">                case 'php':
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -143,34 +98,36 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> validate_file_to_edit( $file, $allowed_files );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-$scrollto = isset( $_REQUEST['scrollto'] ) ? (int) $_REQUEST['scrollto'] : 0;
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-switch( $action ) {
-case 'update':
-       check_admin_referer( 'edit-theme_' . $file . $stylesheet );
-       $newcontent = wp_unslash( $_POST['newcontent'] );
-       $location = 'theme-editor.php?file=' . urlencode( $relative_file ) . '&theme=' . urlencode( $stylesheet ) . '&scrollto=' . $scrollto;
-       if ( is_writeable( $file ) ) {
-               // is_writable() not always reliable, check return value. see comments @ https://secure.php.net/is_writable
-               $f = fopen( $file, 'w+' );
-               if ( $f !== false ) {
-                       fwrite( $f, $newcontent );
-                       fclose( $f );
-                       $location .= '&updated=true';
-                       $theme->cache_delete();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Handle fallback editing of file when JavaScript is not available.
+$edit_error = null;
+$posted_content = null;
+if ( 'POST' === $_SERVER['REQUEST_METHOD'] ) {
+       $r = wp_edit_theme_plugin_file( wp_unslash( $_POST ) );
+       if ( is_wp_error( $r ) ) {
+               $edit_error = $r;
+               if ( check_ajax_referer( 'edit-theme_' . $file . $stylesheet, 'nonce', false ) && isset( $_POST['newcontent'] ) ) {
+                       $posted_content = wp_unslash( $_POST['newcontent'] );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        } else {
+               wp_redirect( add_query_arg(
+                       array(
+                               'a' => 1, // This means "success" for some reason.
+                               'theme' => $stylesheet,
+                               'file' => $relative_file,
+                       ),
+                       admin_url( 'theme-editor.php' )
+               ) );
+               exit;
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        wp_redirect( $location );
-       exit;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+}
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-default:
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $settings = array(
+               'codeEditor' => wp_enqueue_code_editor( compact( 'file' ) ),
+       );
+       wp_enqueue_script( 'wp-theme-plugin-editor' );
+       wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function( $ ) { wp.themePluginEditor.init( $( "#template" ), %s ); } )', wp_json_encode( $settings ) ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $settings = wp_enqueue_code_editor( compact( 'file' ) );
-       if ( ! empty( $settings ) ) {
-               wp_enqueue_script( 'wp-theme-plugin-editor' );
-               wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function() { wp.themePluginEditor.init( %s ); } )', wp_json_encode( $settings ) ) );
-       }
-
</del><span class="cx" style="display: block; padding: 0 10px">         require_once( ABSPATH . 'wp-admin/admin-header.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        update_recently_edited( $file );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -179,7 +136,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $error = true;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        $content = '';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( ! $error && filesize( $file ) > 0 ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( ! empty( $posted_content ) ) {
+               $content = $posted_content;
+       } elseif ( ! $error && filesize( $file ) > 0 ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $f = fopen($file, 'r');
</span><span class="cx" style="display: block; padding: 0 10px">                $content = fread($f, filesize($file));
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -197,10 +156,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $content = esc_textarea( $content );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( isset( $_GET['updated'] ) ) : ?>
- <div id="message" class="updated notice is-dismissible"><p><?php _e( 'File edited successfully.' ) ?></p></div>
-<?php endif;
-
</del><span class="cx" style="display: block; padding: 0 10px"> $file_description = get_file_description( $relative_file );
</span><span class="cx" style="display: block; padding: 0 10px"> $file_show = array_search( $file, array_filter( $allowed_files ) );
</span><span class="cx" style="display: block; padding: 0 10px"> $description = esc_html( $file_description );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -211,12 +166,23 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <div class="wrap">
</span><span class="cx" style="display: block; padding: 0 10px"> <h1><?php echo esc_html( $title ); ?></h1>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php if ( isset( $_GET['a'] ) ) : ?>
+       <div id="message" class="updated notice is-dismissible">
+               <p><?php _e( 'File edited successfully.' ); ?></p>
+       </div>
+<?php elseif ( is_wp_error( $edit_error ) ) : ?>
+       <div id="message" class="notice notice-error">
+               <p><?php _e( 'There was an error while trying to update the file. You may need to fix something and try updating again.' ); ?></p>
+               <pre><?php echo esc_html( $edit_error->get_error_message() ? $edit_error->get_error_message() : $edit_error->get_error_code() ); ?></pre>
+       </div>
+<?php endif; ?>
+
</ins><span class="cx" style="display: block; padding: 0 10px"> <div class="fileedit-sub">
</span><span class="cx" style="display: block; padding: 0 10px"> <div class="alignleft">
</span><span class="cx" style="display: block; padding: 0 10px"> <h2><?php echo $theme->display( 'Name' ); if ( $description ) echo ': ' . $description; ?></h2>
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px"> <div class="alignright">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <form action="theme-editor.php" method="post">
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <form action="theme-editor.php" method="get">
</ins><span class="cx" style="display: block; padding: 0 10px">                 <strong><label for="theme"><?php _e('Select theme to edit:'); ?> </label></strong>
</span><span class="cx" style="display: block; padding: 0 10px">                <select name="theme" id="theme">
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -299,14 +265,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">        echo '<div class="error"><p>' . __('Oops, no such file exists! Double check the name and try again, merci.') . '</p></div>';
</span><span class="cx" style="display: block; padding: 0 10px"> else : ?>
</span><span class="cx" style="display: block; padding: 0 10px">        <form name="template" id="template" action="theme-editor.php" method="post">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <?php wp_nonce_field( 'edit-theme_' . $file . $stylesheet ); ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php wp_nonce_field( 'edit-theme_' . $file . $stylesheet, 'nonce' ); ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <div>
</span><span class="cx" style="display: block; padding: 0 10px">                        <label for="newcontent" id="theme-plugin-editor-label"><?php _e( 'Selected file content:' ); ?></label>
</span><span class="cx" style="display: block; padding: 0 10px">                        <textarea cols="70" rows="30" name="newcontent" id="newcontent" aria-describedby="editor-keyboard-trap-help-1 editor-keyboard-trap-help-2 editor-keyboard-trap-help-3 editor-keyboard-trap-help-4"><?php echo $content; ?></textarea>
</span><span class="cx" style="display: block; padding: 0 10px">                        <input type="hidden" name="action" value="update" />
</span><span class="cx" style="display: block; padding: 0 10px">                        <input type="hidden" name="file" value="<?php echo esc_attr( $relative_file ); ?>" />
</span><span class="cx" style="display: block; padding: 0 10px">                        <input type="hidden" name="theme" value="<?php echo esc_attr( $theme->get_stylesheet() ); ?>" />
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <input type="hidden" name="scrollto" id="scrollto" value="<?php echo esc_attr( $scrollto ); ?>" />
</del><span class="cx" style="display: block; padding: 0 10px">                 </div>
</span><span class="cx" style="display: block; padding: 0 10px">        <?php if ( ! empty( $functions ) ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                <div id="documentation" class="hide-if-no-js">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -316,32 +281,33 @@
</span><span class="cx" style="display: block; padding: 0 10px">                </div>
</span><span class="cx" style="display: block; padding: 0 10px">        <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <div>
-               <?php if ( is_child_theme() && $theme->get_stylesheet() == get_template() ) : ?>
-                       <p><?php if ( is_writeable( $file ) ) { ?><strong><?php _e( 'Caution:' ); ?></strong><?php } ?>
-                       <?php _e( 'This is a file in your current parent theme.' ); ?></p>
-               <?php endif; ?>
-<?php
-       if ( is_writeable( $file ) ) :
-               submit_button( __( 'Update File' ), 'primary', 'submit', true );
-       else : ?>
-<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>
-<?php endif; ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <div>
+               <div class="editor-notices">
+                       <?php if ( is_child_theme() && $theme->get_stylesheet() == get_template() ) : ?>
+                               <div class="notice notice-warning inline">
+                                       <p>
+                                               <?php if ( is_writeable( $file ) ) { ?><strong><?php _e( 'Caution:' ); ?></strong><?php } ?>
+                                               <?php _e( 'This is a file in your current parent theme.' ); ?>
+                                       </p>
+                               </div>
+                       <?php endif; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                 </div>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        <?php if ( is_writeable( $file ) ) : ?>
+               <p class="submit">
+                       <?php submit_button( __( 'Update File' ), 'primary', 'submit', false ); ?>
+                       <span class="spinner"></span>
+               </p>
+       <?php else : ?>
+               <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>
+       <?php endif; ?>
+       </div>
+       <?php wp_print_file_editor_templates(); ?>
</ins><span class="cx" style="display: block; padding: 0 10px">         </form>
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><span class="cx" style="display: block; padding: 0 10px"> endif; // $error
</span><span class="cx" style="display: block; padding: 0 10px"> ?>
</span><span class="cx" style="display: block; padding: 0 10px"> <br class="clear" />
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<script type="text/javascript">
-jQuery(document).ready(function($){
-       $('#template').submit(function(){ $('#scrollto').val( $('#newcontent').scrollTop() ); });
-       $('#newcontent').scrollTop( $('#scrollto').val() );
-});
-</script>
</del><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-break;
-}
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> include(ABSPATH . 'wp-admin/admin-footer.php' );
</span></span></pre></div>
<a id="trunksrcwpincludesjswpa11yjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/js/wp-a11y.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/js/wp-a11y.js       2017-10-04 00:00:47 UTC (rev 41720)
+++ trunk/src/wp-includes/js/wp-a11y.js 2017-10-04 00:19:16 UTC (rev 41721)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -14,9 +14,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.2.0
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.3.0 Introduced the 'ariaLive' argument.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param {String} message  The message to be announced by Assistive Technologies.
-        * @param {String} ariaLive Optional. The politeness level for aria-live. Possible values:
-        *                          polite or assertive. Default polite.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param {String} message    The message to be announced by Assistive Technologies.
+        * @param {String} [ariaLive] The politeness level for aria-live. Possible values:
+        *                            polite or assertive. Default polite.
+        * @returns {void}
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        function speak( message, ariaLive ) {
</span><span class="cx" style="display: block; padding: 0 10px">                // Clear previous messages to allow repeated strings being read out.
</span></span></pre></div>
<a id="trunksrcwpincludesloadphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/load.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/load.php    2017-10-04 00:00:47 UTC (rev 41720)
+++ trunk/src/wp-includes/load.php      2017-10-04 00:19:16 UTC (rev 41721)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1112,3 +1112,46 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        return apply_filters( 'file_mod_allowed', ! defined( 'DISALLOW_FILE_MODS' ) || ! DISALLOW_FILE_MODS, $context );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/**
+ * Start scraping edited file errors.
+ *
+ * @since 4.9.0
+ */
+function wp_start_scraping_edited_file_errors() {
+       if ( ! isset( $_REQUEST['wp_scrape_key'] ) || ! isset( $_REQUEST['wp_scrape_nonce'] ) ) {
+               return;
+       }
+       $key = substr( sanitize_key( wp_unslash( $_REQUEST['wp_scrape_key'] ) ), 0, 32 );
+       $nonce = wp_unslash( $_REQUEST['wp_scrape_nonce'] );
+
+       if ( get_transient( 'scrape_key_' . $key ) !== $nonce ) {
+               echo "###### begin_scraped_error:$key ######";
+               echo wp_json_encode( array(
+                       'code' => 'scrape_nonce_failure',
+                       'message' => __( 'Scrape nonce check failed. Please try again.' ),
+               ) );
+               die();
+       }
+       register_shutdown_function( 'wp_finalize_scraping_edited_file_errors', $key );
+}
+
+/**
+ * Finalize scraping for edited file errors.
+ *
+ * @since 4.9.0
+ *
+ * @param string $scrape_key Scrape key.
+ */
+function wp_finalize_scraping_edited_file_errors( $scrape_key ) {
+       $error = error_get_last();
+       if ( empty( $error ) ) {
+               return;
+       }
+       if ( ! in_array( $error['type'], array( E_CORE_ERROR, E_COMPILE_ERROR, E_ERROR, E_PARSE, E_USER_ERROR, E_RECOVERABLE_ERROR ), true ) ) {
+               return;
+       }
+       $error = str_replace( ABSPATH, '', $error );
+       echo "###### begin_scraped_error:$scrape_key ######";
+       echo wp_json_encode( $error );
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesscriptloaderphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/script-loader.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/script-loader.php   2017-10-04 00:00:47 UTC (rev 41720)
+++ trunk/src/wp-includes/script-loader.php     2017-10-04 00:19:16 UTC (rev 41721)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -471,11 +471,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">        $scripts->add( 'htmlhint', '/wp-includes/js/codemirror/htmlhint.js', array(), '0.9.14-xwp' );
</span><span class="cx" style="display: block; padding: 0 10px">        $scripts->add( 'htmlhint-kses', '/wp-includes/js/codemirror/htmlhint-kses.js', array( 'htmlhint' ) );
</span><span class="cx" style="display: block; padding: 0 10px">        $scripts->add( 'code-editor', "/wp-admin/js/code-editor$suffix.js", array( 'jquery', 'wp-codemirror' ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $scripts->add( 'wp-theme-plugin-editor', "/wp-admin/js/theme-plugin-editor$suffix.js", array( 'code-editor', 'jquery', 'jquery-ui-core', 'wp-a11y', 'underscore' ) );
-       did_action( 'init' ) && $scripts->add_inline_script( 'wp-theme-plugin-editor', sprintf( 'wp.themePluginEditor.l10n = %s;', wp_json_encode( wp_array_slice_assoc(
-               /* translators: %d: error count */
-               _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.' ),
-               array( 'singular', 'plural' )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $scripts->add( 'wp-theme-plugin-editor', "/wp-admin/js/theme-plugin-editor$suffix.js", array( 'wp-util', 'jquery', 'jquery-ui-core', 'wp-a11y', 'underscore' ) );
+       did_action( 'init' ) && $scripts->add_inline_script( 'wp-theme-plugin-editor', sprintf( 'wp.themePluginEditor.l10n = %s;', wp_json_encode( array(
+               'saveAlert' => __( 'The changes you made will be lost if you navigate away from this page.' ),
+               'lintError' => wp_array_slice_assoc(
+                       /* translators: %d: error count */
+                       _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.' ),
+                       array( 'singular', 'plural' )
+               ),
</ins><span class="cx" style="display: block; padding: 0 10px">         ) ) ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        $scripts->add( 'wp-playlist', "/wp-includes/js/mediaelement/wp-playlist$suffix.js", array( 'wp-util', 'backbone', 'mediaelement' ), false, 1 );
</span></span></pre></div>
<a id="trunksrcwpsettingsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-settings.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-settings.php 2017-10-04 00:00:47 UTC (rev 41720)
+++ trunk/src/wp-settings.php   2017-10-04 00:19:16 UTC (rev 41721)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -294,6 +294,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> create_initial_taxonomies();
</span><span class="cx" style="display: block; padding: 0 10px"> create_initial_post_types();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+wp_start_scraping_edited_file_errors();
+
</ins><span class="cx" style="display: block; padding: 0 10px"> // Register the default theme directory root
</span><span class="cx" style="display: block; padding: 0 10px"> register_theme_directory( get_theme_root() );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre>
</div>
</div>

</body>
</html>