Plugin Directory

Changeset 2021576


Ignore:
Timestamp:
01/30/2019 12:37:41 AM (7 years ago)
Author:
paulswarthout
Message:

Updated to 1.3.2. Added the ability to edit and save files in the child theme, and the ability to view (edit but not save) files in the parent theme.

Location:
child-themes-helper
Files:
54 added
10 edited

Legend:

Unmodified
Added
Removed
  • child-themes-helper/trunk/README.md

    r1971785 r2021576  
    1 # Child Themes Helper plugin Version 1.2
     1# Child Themes Helper plugin Version 1.3.2
    22The Child Themes Helper plugin was developed to solve a problem that I was struggling with: the copying of files from a template theme to a child theme. It was almost never easy.
    331. Copy the template theme file down to my local computer via FTP. That was easy enough.
     
    1919The Child Theme Helper plugin only manipulates the child theme files. The template theme is never touched. A new feature that is currently planned, but not yet implemented, is the ability to edit the files for a quick peek or a quick modification. Once that feature has been completed, the developer will be able to open the template theme files, but they will not be able to save any modifications. Only changes to the child theme files may be saved.
    2020
     21## Edit Child Themes Files
     22Starting with version 1.3.2, you can edit child theme files directly, and save your changes. You can open parent theme files in an editor, but parent theme files are read-only. If you make changes to a parent theme file, you will not be able to save theme.
     23
    2124## Create a New Child Theme
    2225The Child Theme Helper plugin was intended for one purpose: to copy files to the child theme. But what should it do when the currently active theme isn't a child theme? My first thought was to disable it somehow. But then I thought, why not give the developer the opportunity to create a new child theme.
  • child-themes-helper/trunk/classes/class_ajax_functions.php

    r1971785 r2021576  
    4141            $inputs = [
    4242                        'directory' => sanitize_text_field( $_POST['directory'] ),
    43                         'file'      => sanitize_file_name( $_POST['file'] )
     43                        'file'      => $this->MyFilenameSanitize( $_POST['file'] )
    4444                    ];
    4545
     
    111111            $inputs =[
    112112                        'directory' => sanitize_text_field( $_POST['directory'] ),
    113                         'file'      => sanitize_file_name(  $_POST['file'] ),
     113                        'file'      => $this->MyFilenameSanitize( $_POST['file'] ),
     114                        'action'    => 'copyFile',
    114115                     ];
    115116
     
    133134                $args[$key] = $inputs[$key];
    134135            }
    135             $args['action'] = 'copyFile';
    136 
    137             /* If file doesn't exist. Copy it. We're done.
    138              * If the file does exist, and the child theme file and the template theme file
    139              * are already identical. We're done. No need to copy it.
    140              * If the file does exist, and the files are not identical,
    141              * prompt the user to overwrite.
     136//          $args['action'] = 'copyFile';
     137
     138            /*
     139             * File does not exist in the child theme.          Copy it.
     140             * File EXISTS in both child and parent themes.
     141             *      Files are identical.                        Nothing to do.
     142             *      Files are not identical.                    Prompt the user before overwriting the existing child file.
    142143             */
    143144            if ( ! file_exists( $childThemeFile ) ) {
     
    152153                /*
    153154                 * The file already exists in the child theme and the files are NOT identical.
    154                  * Prompt the user to overwrite.
     155                 * Prompt the user to allow overwrite.
     156                 * Return the prompt to the AJAX call that got us here.
    155157                 */
    156158                $JSData = json_encode( $args );
     
    172174                echo "</div>";
    173175            }
     176        }
     177        /*
     178         * The MyFilenameSanitize() is identical to sanitize_file_name except for a single change.
     179         * The WordPress core function 'sanitize_file_name' strips leading underscores in file names.
     180         * FILE NAMES ARE ALLOWED TO HAVE LEADING UNDERSCORES.
     181         * The TwentyNineteen theme, released with WordPress 5.0 demonstrates this.
     182         * Because the Child Themes Helper plugin copies files from the template or parent theme to the child theme,
     183         * and themes like TwentyNineteen have files with leading underscores, the Child Themes Helper plugin
     184         * cannot use a sanitize function that strips leading underscores from file names.
     185         *
     186         * The line below:
     187         *    $filename = trim( $filename, '.-_' );
     188         * was changed to:
     189         *    $filename = trim( $filename, '.-' );
     190         * Otherwise the MyFilenameSanitize() function is line-for-line identical to the sanitize_file_name() function
     191         * as it appears in the /wp-includes/formatting.php file, as of WordPress 5.0.3.
     192         */
     193        function MyFilenameSanitize($filename) {
     194            $filename_raw = $filename;
     195            $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
     196            /**
     197             * Filters the list of characters to remove from a filename.
     198             *
     199             * @since 2.8.0
     200             *
     201             * @param array  $special_chars Characters to remove.
     202             * @param string $filename_raw  Filename as it was passed into sanitize_file_name().
     203             */
     204            $special_chars = apply_filters( 'sanitize_file_name_chars', $special_chars, $filename_raw );
     205            $filename = preg_replace( "#\x{00a0}#siu", ' ', $filename );
     206            $filename = str_replace( $special_chars, '', $filename );
     207            $filename = str_replace( array( '%20', '+' ), '-', $filename );
     208            $filename = preg_replace( '/[\r\n\t -]+/', '-', $filename );
     209            $filename = trim( $filename, '.-' );
     210
     211            if ( false === strpos( $filename, '.' ) ) {
     212                $mime_types = wp_get_mime_types();
     213                $filetype = wp_check_filetype( 'test.' . $filename, $mime_types );
     214                if ( $filetype['ext'] === $filename ) {
     215                    $filename = 'unnamed-file.' . $filetype['ext'];
     216                }
     217            }
     218
     219            // Split the filename into a base and extension[s]
     220            $parts = explode('.', $filename);
     221
     222            // Return if only one extension
     223            if ( count( $parts ) <= 2 ) {
     224                /**
     225                 * Filters a sanitized filename string.
     226                 *
     227                 * @since 2.8.0
     228                 *
     229                 * @param string $filename     Sanitized filename.
     230                 * @param string $filename_raw The filename prior to sanitization.
     231                 */
     232                return apply_filters( 'sanitize_file_name', $filename, $filename_raw );
     233            }
     234
     235            // Process multiple extensions
     236            $filename = array_shift($parts);
     237            $extension = array_pop($parts);
     238            $mimes = get_allowed_mime_types();
     239
     240            /*
     241             * Loop over any intermediate extensions. Postfix them with a trailing underscore
     242             * if they are a 2 - 5 character long alpha string not in the extension whitelist.
     243             */
     244            foreach ( (array) $parts as $part) {
     245                $filename .= '.' . $part;
     246
     247                if ( preg_match("/^[a-zA-Z]{2,5}\d?$/", $part) ) {
     248                    $allowed = false;
     249                    foreach ( $mimes as $ext_preg => $mime_match ) {
     250                        $ext_preg = '!^(' . $ext_preg . ')$!i';
     251                        if ( preg_match( $ext_preg, $part ) ) {
     252                            $allowed = true;
     253                            break;
     254                        }
     255                    }
     256                    if ( !$allowed )
     257                        $filename .= '_';
     258                }
     259            }
     260            $filename .= '.' . $extension;
     261            /** This filter is documented in wp-includes/formatting.php */
     262            return apply_filters('sanitize_file_name', $filename, $filename_raw);
    174263        }
    175264        /*
     
    187276                $templateThemeRoot  = $this->activeThemeInfo->templateThemeRoot;
    188277                $templateStylesheet = $this->activeThemeInfo->templateStylesheet;
    189                 $directory          = $args['directory'];
    190                 $fileToCopy         = $args['file'];
     278                $directory          = sanitize_text_field($args['directory']);
     279                $fileToCopy         = $this->MyFilenameSanitize($args['file']);
    191280            } else {
    192281                $childThemeRoot     = $this->activeThemeInfo->childThemeRoot;
     
    195284                $templateStylesheet = $this->activeThemeInfo->templateStylesheet;
    196285                $directory          = sanitize_text_field( $_POST['directory'] );
    197                 $fileToCopy         = sanitize_file_name( $_POST['file'] );
     286                $fileToCopy         = $this->MyFilenameSanitize( $_POST['file'] );
    198287            }
    199288
     
    331420
    332421            $functionsFile = fopen( $childThemePath . PAS_CTH_SEPARATOR . "functions.php", "w" );
     422
     423            $functionsFileOutput = '<?PHP ' . $newlineChar;
     424            $functionsFileOutput .= <<< "FUNCTIONSFILEOUTPUT"
     425add_action('wp_enqueue_scripts', '{$childThemeStylesheet}_theme_styles' );
     426
     427function {$childThemeStylesheet}_theme_styles() {
     428    wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' );
     429    wp_enqueue_style( '{$childThemeStylesheet}-style', WP_CONTENT_URL . '/themes/{$childThemeStylesheet}/style.css' );
     430}
     431
     432FUNCTIONSFILEOUTPUT;
     433            fwrite( $functionsFile, $functionsFileOutput);
     434            fclose( $functionsFile );
     435
     436
     437
     438/*
    333439            fwrite( $functionsFile, "<" . "?" . "PHP" . $newlineChar );
    334440            fwrite( $functionsFile, "add_action( 'wp_enqueue_scripts', '" . $childThemeStylesheet . "_theme_styles' );" . $newlineChar );
     
    346452            fwrite( $functionsFile, "?>" );
    347453            fclose( $functionsFile );
    348 
     454*/
    349455            // Handshake with the Javascript AJAX call that got us here.
    350456            // When "SUCCESS:url" is returned, Javascript will redirect to the url.
     
    378484            update_option( 'pas_cth_font', [ 'fontName'=>$fontName, 'fontFile-base'=>$fontFile ] );
    379485        }
     486
     487        function editFile() {
     488            $inputs =
     489                [
     490                    'directory' => sanitize_text_field( $_POST['directory'] ),
     491                    'file'  => sanitize_file_name( $_POST['file'] ),
     492                    'themeType' => sanitize_text_field( $_POST['themeType'] ),
     493                ];
     494            switch (strtolower($inputs['themeType'])) {
     495                case PAS_CTH_CHILDTHEME:
     496                    $file = $this->activeThemeInfo->childThemeRoot . PAS_CTH_SEPARATOR . $this->activeThemeInfo->childStylesheet . PAS_CTH_SEPARATOR . $inputs['directory'] . PAS_CTH_SEPARATOR . $inputs['file'];
     497                    $readOnly = 'false';
     498                    break;
     499                case PAS_CTH_TEMPLATETHEME:
     500                    $file = $this->activeThemeInfo->templateThemeRoot . PAS_CTH_SEPARATOR . $this->activeThemeInfo->templateStylesheet . PAS_CTH_SEPARATOR . $inputs['directory'] . PAS_CTH_SEPARATOR . $inputs['file'];
     501                    $readOnly = 'true';
     502                    break;
     503            }
     504            $inputs['readOnlyFlag'] = $readOnly;
     505
     506            $fileContents = stripslashes(str_replace(">", "&gt;", str_replace("<", "&lt;", file_get_contents($file))));
     507            echo "EDITFILEOUTPUT:{";
     508            echo "ARGS<:>" . json_encode($inputs);
     509            echo '+|++|+';
     510            echo "EDITBOX<:>{$fileContents}";
     511            echo "}";
     512        }
     513        function saveFile() {
     514            $inputs =
     515                [
     516                    'fileContents'  => $_POST['fileContents'],
     517                    'directory'     => sanitize_text_field( $_POST['directory'] ),
     518                    'file'          => sanitize_file_name( $_POST['file'] ),
     519                    'themeType'     => sanitize_text_field( $_POST['themeType'] ),
     520                ];
     521
     522            switch ($inputs['themeType']) {
     523                case PAS_CTH_CHILDTHEME:
     524                    $file = $this->activeThemeInfo->childThemeRoot . PAS_CTH_SEPARATOR . $this->activeThemeInfo->childStylesheet . PAS_CTH_SEPARATOR . $inputs['directory'] . PAS_CTH_SEPARATOR . $inputs['file'];
     525                    break;
     526                case PAS_CTH_TEMPLATETHEME:
     527                    $file = $this->activeThemeInfo->templateThemeRoot . PAS_CTH_SEPARATOR . $this->activeThemeInfo->templateStylesheet . PAS_CTH_SEPARATOR . $inputs['directory'] . PAS_CTH_SEPARATOR . $inputs['file'];
     528                    break;
     529            }
     530            $result = file_put_contents($file, stripslashes($_POST['fileContents']));
     531            if ($result === false) {
     532                echo "Failed to write file:<br>";
     533                echo "FILE: $file<br>";
     534                echo "Length of file: " . strlen($inputs['fileContents']);
     535            } else {
     536            }
     537        }
    380538    }
    381539}
  • child-themes-helper/trunk/classes/class_childThemesHelper.php

    r1971785 r2021576  
    4242                                $this->pluginDirectory['url'] . "css/style.css" . $uniqStr,
    4343                                false );
     44            if (defined('WP_DEBUG') && constant('WP_DEBUG') && defined('PLUGIN_DEVELOPMENT') && constant('PLUGIN_DEVELOPMENT') == "YES") {
     45                wp_enqueue_style(   'pasChildThemes3',
     46                                    $this->pluginDirectory['url'] . 'css/hexdump.css' . $uniqStr,
     47                                    false );
     48            }
    4449        }
    4550
     
    5459                                $this->pluginDirectory['url'] . "js/js_common_fn.js" . $uniqStr,
    5560                                false );
     61            wp_enqueue_script( 'pas_cth_Script3',
     62                               $this->pluginDirectory['url'] . "js/edit_file.js" . $uniqStr,
     63                               false );
     64            if (defined('WP_DEBUG') && constant('WP_DEBUG') && defined('PLUGIN_DEVELOPMENT') && constant('PLUGIN_DEVELOPMENT') == "YES") {
     65                wp_enqueue_script( 'pas_cth_Script4',
     66                                   $this->pluginDirectory['url'] . 'js/hexdump.js' . $uniqStr,
     67                                   false );
     68                if (defined('WP_DEBUG') && constant('WP_DEBUG')) {
     69                    $obj = ['WP_DEBUG' => "ENABLED"];
     70                    wp_localize_script( 'pas_cth_Script4', 'pas_cth_debugMode', $obj );
     71                }
     72            }
    5673        }
    5774
     
    456473                echo "<p class='actionReminder'>";
    457474                echo "Left Click a file to <u>REMOVE</u> it from the child theme.<br>";
    458                 echo "Right Click a file to <u>EDIT</u> it.";
     475                echo "Right Click or long press a file to <u>EDIT</u> it.";
    459476                echo "</p>";
    460477            }
     
    474491                echo "<p class='actionReminder'>";
    475492                echo "Left Click a file to <u>COPY</u> it to the child theme.<br>";
    476                 echo "Right Click a file to <u>EDIT</u> it.";
     493                echo "Right Click or long press a file to <u>EDIT</u> it.";
    477494                echo "</p>";
    478495            echo "<p class='themeName'>" . $this->activeThemeInfo->templateThemeName . "</p>";
     
    507524            echo "<div class='createChildThemeBox'>";
    508525
    509             echo "<p class='warningHeading'>Warning</p><br><br>";
    510             echo "The current theme: '<i><b>" . $this->activeThemeInfo->childThemeName . "</i></b>'";
    511             echo " is <u>not</u> a child theme.";
    512             echo "<br><br>"; // replace with CSS in future release;
    513             echo "Do you want to create a child theme?";
    514             echo "<br><br>"; // replace with CSS in future release;
    515             echo "Fill out the following form to create a child theme.<br>";
    516             echo "The only required fields are the <i>Child Theme Name</i> and the <i>Template Theme Name</i>";
    517             echo "<div class='createChildThemeBoxForm'>";
    518             echo "<div class='createChildThemeBoxForm_HDR'>Create Child Theme</div>";
    519             echo "<form>";
    520             echo "<input type='hidden' name='themeRoot' value='" . $this->activeThemeInfo->childThemeRoot . "'>";
    521             echo "<input type='hidden' name='action' value='createChildTheme'>";
    522             echo "<input type='hidden' name='href' value='" . admin_url( "themes.php" ) . "'>";
    523             echo "<label for='childThemeName'>";
    524             echo "Child Theme Name:";
    525             echo "<br>";
    526             echo "<input type='text' name='childThemeName' id='childThemeName' value=''>";
    527             echo "</label>";
    528             echo "<br>";
    529             echo $select . "<br>"; // displays a list of installed, active, non-child, themes
    530             echo "<label for='ThemeURI'>";
    531             echo "Theme URI<br>";
    532             echo "<input type='text' name='themeURI' id='themeURI' value=''>";
    533             echo "</label><br>";
    534             echo "<label for='Description'>";
    535             echo "Theme Description<br>";
    536             echo "<textarea id='description' name='description'></textarea>";
    537             echo "</label><br>";
    538             echo "<label for='authorName'>";
    539             echo "Author Name:<br>";
    540             echo "<input type='text' id='authorName' name='authorName' value=''>";
    541             echo "</label><br>";
    542             echo "<label for='authorURI'>";
    543             echo "Author URI:<br>";
    544             echo "<input type='text' id='authorURI' name='authorURI' value=''>";
    545             echo "</label><br>";
    546             echo "<label for='version'>";
    547             echo "Version:<br>";
    548             echo "<input type='text' id='version' name='version' value='0.0.1' readonly>";
    549             echo "</label><br>";
    550 
    551             echo "<br>";
    552             echo "<div class='buttonRow'>";
    553             echo "<input type='button' ";
    554             echo " value='Create Child Theme' ";
    555             echo " class='blueButton' ";
    556             echo " onclick='javascript:pas_cth_js_createChildTheme( this );'>";
    557             echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
    558             echo "<input type='button' ";
    559             echo " value='Reset' ";
    560             echo " class='blueButton' ";
    561             echo " onclick='javascript:this.form.reset();'>";
    562             echo "</div>";
    563 
    564             echo "</div>";
    565             echo "</form>";
    566             echo "</div>";
     526            $adminThemes = admin_url("themes.php");
     527
     528            $createChildTheme = <<< "CREATECHILDTHEME"
     529
     530<p class='warningHeading'>Warning</p><br><br>
     531    The current theme: '<i><b>{$this->activeThemeInfo->childThemeName}</i></b>' is <u>not</u> a child theme.
     532    <br><br>
     533    Do you want to create a child theme?
     534    <br><br>
     535    Fill out the following form to create a child theme.<br>
     536    The only required fields are the <i>Child Theme Name</i> and the <i>Template Theme Name</i>
     537    <div class='createChildThemeBoxForm'>
     538        <div class='createChildThemeBoxForm_HDR'>Create Child Theme</div>
     539            <form>
     540                <input type='hidden' name='themeRoot' value='{$this->activeThemeInfo->childThemeRoot}'>
     541                <input type='hidden' name='action' value='createChildTheme'>
     542                <input type='hidden' name='href' value='{$adminThemes}'>
     543                <label for='childThemeName'>
     544                    Child Theme Name:
     545                    <br>
     546                    <input type='text' name='childThemeName' id='childThemeName' value='' required pattern='^[a-zA-Z][a-zA-Z0-9\-\ ]+' data-message='Child Theme Names must begin with a letter' onblur='javascript:pas_cth_validateField(this);'>
     547                </label>
     548            <br>
     549            {$select}<br> <!-- displays list of non-child installed themes -->
     550            <label for='ThemeURI'>
     551            Theme URI<br>
     552            <input type='text' name='themeURI' id='themeURI' value='' pattern='^[a-zA-Z]{4,5}\://[a-zA-Z0-9:/\-\.\&\=\?]+$' data-message='Invalid URI' onblur='javascript:pas_cth_validateField(this);'>
     553            </label><br>
     554            <label for='Description'>
     555            Theme Description<br>
     556            <textarea required id='description' name='description' pattern='^[a-zA-Z0-9\.:;?#\%,\(\)/ ]+$' data-message='You may use letters, numbers, and special characters that you would normally use in writing, only. No HTML or other scripts are allowed here.' onblur='javascript:pas_cth_validateField(this);'></textarea>
     557            </label><br>
     558            <label for='authorName'>
     559            Author Name:<br>
     560            <input type='text' id='authorName' name='authorName' value='' pattern='^[a-zA-Z \.]+$' data-message='upper or lower case, spaces, or periods, only.' onblur='javascript:pas_cth_validateField(this);'>
     561            </label><br>
     562            <label for='authorURI'>
     563            Author URI:<br>
     564            <input type='text' id='authorURI' name='authorURI' value=''  pattern='^[a-zA-Z]{4,5}\://[a-zA-Z0-9:/\-\.\&\=\?]+$' data-message='Invalid URI' onblur='javascript:pas_cth_validateField(this);'>
     565            </label><br>
     566            <input type='hidden' id='version' name='version' value='0.0.1' readonly>
     567            <br>
     568
     569            <div class='buttonRow'>
     570                <input type='button'
     571                    value='Create Child Theme'
     572                    class='blueButton'
     573                    onclick='javascript:pas_cth_js_createChildTheme( this );'
     574                >
     575                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
     576                <input type='button'
     577                    value='Reset'
     578                    class='blueButton'
     579                    onclick='javascript:this.form.reset();'
     580                >
     581            </div>
     582            <br>
     583        </div>
     584        </form>
     585    </div>
     586CREATECHILDTHEME;
     587            echo $createChildTheme;
    567588        }
    568589        /*
     
    603624            echo "<div id='jsdata' style='display:none;' data-jsdata='$jsdata'></div>";
    604625
    605             echo "<div class='pas-grid-container'>";
     626            echo "<div id='pas_cth_content'>";
     627
     628            echo "<div id='themeGrid' class='pas-grid-container'>";
    606629            echo "<div class='pas-grid-left-column'>";
    607630            echo "  <div class='childPrompt' id='childPrompt' onclick='javascript:showChild();'>CHILD</div>";
     
    620643            $this->showActiveParentTheme( );
    621644
    622             echo "</div>"; // end grid item 2
    623             echo "</div>"; // end grid container
    624             echo "<div id='hoverPrompt'></div>";
     645            echo    "</div>"; // end grid item 2
     646            echo    "</div>"; // end grid container
     647            // HoverPrompt is used during mouseovers on devices wider than 829px;
     648            // editFile is used when editting a file.
     649            // Both will be sized and positioned dynamically with Javascript
     650            echo    "<div id='hoverPrompt'></div>";
     651
     652            echo    "<div id='editFile' data-gramm='false' >"
     653                .   "   <input type='hidden' id='directory' value=''>"
     654                .   "   <input type='hidden' id='file'  value=''>"
     655                .   "   <input type='hidden' id='themeType' value=''>"
     656                .   "   <input type='hidden' id='readOnlyFlag' value='false'>"
     657                .   "   <input type='hidden' id='currentFileExtension' value=''>"
     658                .   "<input type='button' value='Save File' disabled id='ef_saveButton' onclick='javascript:pas_cth_js_saveFile();'>"
     659                .   "<p id='ef_readonly_msg'>File is READ ONLY. Changes WILL NOT BE SAVED.</p>"
     660                .   "<p id='ef_filename'>FILENAME</p>"
     661                .   "<input type='button' value='Close File' id='ef_closeButton' onclick='javascript:pas_cth_js_closeEditFile();'>"
     662                .   (constant('WP_DEBUG') && defined('PLUGIN_DEVELOPMENT') && constant('PLUGIN_DEVELOPMENT') == "YES" ? "<input type='button' value='DEBUG' id='ef_debug_button' onclick='javascript:debug(this);'>" : "")
     663                .   (constant('WP_DEBUG') && defined('PLUGIN_DEVELOPMENT') && constant('PLUGIN_DEVELOPMENT') == "YES" ? "<input type='button' value='HEXDUMP' id='ef_hexdump_button' onclick='javascript:pas_cth_js_hexdump();'>" : "")
     664                .   "   <div id='editBox' data-gramm='false' spellcheck='false' autocapitalize='false' autocorrect='false' role='textbox' oninput='javascript:editBoxChange();'>"
     665                .   "   </div>"
     666                .   "</div>";
     667
     668            echo "</div>"; // end of <div id='pas_cth_content'>
     669
     670            echo    "<div id='savePrompt'>"
     671                .   "File has changed.<br>Do you want to save it?<br><br>"
     672                .   "   <input id='sp_saveButton' type='button' onclick='javascript:pas_cth_js_saveFile();' value='Save'>"
     673                .   "   &nbsp;&nbsp;&nbsp;"
     674                .   "   <input id='sp_closeButton' type='button' onclick='javascript:pas_cth_js_closeEditFile();' value='No Save'>"
     675                .   "</div>";
    625676        }
    626677
     
    696747                                             'directory'=>$shortDir,
    697748                                             'file'=>$ff,
    698                                              'themeType'=>$themeType
     749                                             'themeType'=>$themeType,
     750                                             'extension'=>pathinfo( $dir.PAS_CTH_SEPARATOR.$ff )['extension'],
     751                                             'allowedFileTypes'=>get_option('pas_cth_edit_allowedFileTypes'),
    699752                                            ]
    700753                                         );
     
    703756                         . " data-jsdata='" . esc_attr( $jsdata ) . "' "
    704757                         . " onclick='javascript:pas_cth_js_selectFile( this );' "
    705                          . " oncontextmenu='javascript:pas_cth_js_noEditYet();return false;' "
     758                         . " oncontextmenu='javascript:pas_cth_js_editFile( this );return false;' "
    706759                         . " onmouseover='javascript:pas_cth_js_mouseOver( this );' "
    707760                         . " onmouseout='javascript:pas_cth_js_mouseOut( this );' "
  • child-themes-helper/trunk/classes/class_common_functions.php

    r1971785 r2021576  
    140140        function killChildFile( $args ) {
    141141            $activeThemeInfo = $args['activeThemeInfo'];
    142             $themeStyle      = $args['stylesheet'];      // Stylesheet - theme's folder name
    143142            $directory       = $args['directory'];           // Path within the theme
    144143            $childFile       = $args['file']; // Which file are we deleting.
     
    408407            }
    409408        }
    410 
     409        function hexDump($str) {
     410            $blockArray = str_split($str, 4);
     411            $loopCount = 0;
     412            echo "<div style='font-family:monospace ! important;font-size:12pt ! important;'>";
     413
     414            while (count($blockArray) > 0) {
     415                $loopCount++;
     416                $arrayOfLines = array_splice($blockArray, 0, 8);
     417                $char = '';
     418
     419                $outputLine = "<div id='hexDump' style='border:solid 1pt red;'>";
     420
     421                foreach ($arrayOfLines as $line) {
     422                    $arrayOfCharacters = str_split($line, 1);
     423                    $out = '';
     424                    for ($ndx = 0; $ndx < count($arrayOfCharacters); $ndx = $ndx + 4) {
     425                        $out .= $this->digits((string) dechex(ord($arrayOfCharacters[$ndx])), 2);
     426                        $char .= $arrayOfCharacters[$ndx];
     427
     428                        $out .= $this->digits((string) dechex(ord($arrayOfCharacters[$ndx+1])), 2);
     429                        $char .= $arrayOfCharacters[$ndx+1];
     430
     431                        $out .= $this->digits((string) dechex(ord($arrayOfCharacters[$ndx+2])), 2);
     432                        $char .= $arrayOfCharacters[$ndx+2];
     433
     434                        $out .= $this->digits((string) dechex(ord($arrayOfCharacters[$ndx+3])), 2);
     435                        $char .= $arrayOfCharacters[$ndx+3];
     436
     437                        $outputLine .= strtoupper($out) . "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
     438                    }
     439                }
     440                $char = preg_replace("[^\x01-\x19\x80-\xFF. ]", ".", $char);
     441                $outputLine .= "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" . $char . "</div>";
     442                echo $outputLine;
     443                $outputLine = "";
     444                $char = '';
     445            }
     446            echo "</div>";
     447        }
     448
     449        function digits($v, $n) {
     450            while (strlen($v) < $n) {
     451                $v = "0" . $v;
     452            }
     453            return $v;
     454        }
    411455    }
    412456}
  • child-themes-helper/trunk/css/style.css

    r1971785 r2021576  
    66    --default-createTheme-field-background:#FFFFD8;
    77    --default-createTheme-fieldWidth:95%;
     8    --default-border:solid 1pt black;
     9}
     10/*
     11 ***************************************
     12 *                                     *
     13 * Child Themes Helper main page       *
     14 *                                     *
     15 ***************************************
     16*/
     17#pas_cth_content {
     18    width:100%;
     19    height:100%;
     20}
     21#themegrid {
     22    height:95%;
     23    overflow:scroll;
    824}
    925.pas-grid-container {
     
    2036  padding: 20px;
    2137  text-align: left;
     38}
     39.pas-grid-item-child {
     40    border-left:var(--default-border);
     41    border-top:var(--default-border);
     42    border-bottom:var(--default-border);
     43    border-right:dashed 1pt rgba(200,200,200,0.5);
     44}
     45.pas-grid-item-parent {
     46    border-top:var(--default-border);
     47    border-right:var(--default-border);
     48    border-bottom:var(--default-border);
     49    border-left:dashed 1pt rgba(200,200,200,0.5);
    2250}
    2351.pas-grid-left-column {
     
    586614 * This plugin will show as blank for anything smaller than 240px in the horizontal or vertical direction.
    587615 */
    588 @media (max-width:239px), (max-height:239px) {
     616@media (max-width:239px) and (max-height:239px) {
    589617    .pas-grid-container {
    590618        display:none;
     
    594622    }
    595623}
     624/*
     625 ***************************************
     626 *                                     *
     627 *  File Listing styles main page      *
     628 *                                     *
     629 ***************************************
     630*/
     631.clt, .clt ul, .clt li {
     632     position: relative;
     633}
     634.clt ul {
     635    list-style: none;
     636    padding-left: 32px;
     637}
     638.clt li::before, .clt li::after {
     639    content: "";
     640    position: absolute;
     641    left: -12px;
     642}
     643.clt li::before {
     644    border-top: 1px solid #000;
     645    top: 9px;
     646    width: 8px;
     647    height: 0;
     648}
     649.clt li::after {
     650    border-left: 1px solid #000;
     651    height: 100%;
     652    width: 0px;
     653    top: 2px;
     654}
     655.clt ul > li:last-child::after {
     656    height: 8px;
     657}
     658/*
     659 ***************************************
     660 *                                     *
     661 *  Generate Screen Shot Options page  *
     662 *                                     *
     663 ***************************************
     664*/
    596665.generateScreenShot_general {
    597666    font-size:12pt;
     
    669738    bottom:0px;
    670739}
    671 .clt, .clt ul, .clt li {
    672      position: relative;
    673 }
    674 .clt ul {
    675     list-style: none;
    676     padding-left: 32px;
    677 }
    678 .clt li::before, .clt li::after {
    679     content: "";
    680     position: absolute;
    681     left: -12px;
    682 }
    683 .clt li::before {
    684     border-top: 1px solid #000;
    685     top: 9px;
    686     width: 8px;
    687     height: 0;
    688 }
    689 .clt li::after {
    690     border-left: 1px solid #000;
    691     height: 100%;
    692     width: 0px;
    693     top: 2px;
    694 }
    695 .clt ul > li:last-child::after {
    696     height: 8px;
    697 }
    698 // Font List on Options page.
     740/*
     741 ***************************************
     742 *                                     *
     743 *  Font List Screen shot options page *
     744 *                                     *
     745 ***************************************
     746 */
    699747#imageDropDown {
    700748    width:470px;
     
    750798    background-color:white;
    751799}
     800/*
     801 ***************************************
     802 *                                     *
     803 *  File Edit styles                   *
     804 *                                     *
     805 ***************************************
     806 */
     807#editFile {
     808    background-color:rgb(255, 255, 255);
     809    color:black;
     810    font-weight:normal;
     811    font-family:monospace;
     812    white-space:pre;
     813    display: none;
     814    grid-template-columns:25% 25% 25% 25%;
     815    grid-template-rows:25px 25px 30px auto;
     816    grid-column-gap:10px;
     817    overflow:hidden;
     818    border:var(--default-border);
     819    position:fixed;
     820    box-shadow:15px 15px 15px darkgray;
     821    border-radius:15px;
     822    padding:10px;
     823}
     824#editFile p {
     825    margin:0px;
     826    padding:0px;
     827}
     828#ef_saveButton {
     829    grid-column:2;
     830    grid-row:3;
     831}
     832#ef_closeButton {
     833    grid-column:1;
     834    grid-row:3;
     835}
     836#ef_hexdump_button {
     837    grid-column:4;
     838    grid-row:3;
     839}
     840#ef_debug_button {
     841    grid-column:3;
     842    grid-row:3;
     843}
     844#ef_saveButton, #ef_closeButton, #sp_saveButton, #sp_closeButton, #ef_debug_button, #ef_modify, #ef_hexdump_button {
     845    background-color:blue;
     846    color:white;
     847    width:100%;
     848}
     849#ef_saveButton:disabled {
     850    display:none;
     851}
     852#ef_filename {
     853    background-color:white;
     854    color:black;
     855    font-size:14pt;
     856    font-weight:bold;
     857    grid-row:2;
     858    grid-column:1 / span 4;
     859}
     860#ef_readonly_msg {
     861    display:none;
     862    color:red;
     863    background-color:rgb(255, 255, 120);
     864    font-size:12pt;
     865    font-weight:bold;
     866    width:100%;
     867    grid-row:1;
     868    grid-column:1 / span 4;
     869}
     870#editBox {
     871    background-color:white;
     872    color:black;
     873    font-size:12pt;
     874    font-weight:normal;
     875    font-family:monospace;
     876    overflow-x:scroll;
     877    overflow-y:scroll;
     878    white-space:pre;
     879    margin-top:10px;
     880    grid-row:4;
     881    grid-column:1 / span 4;
     882}
     883#savePrompt {
     884    width:100%;
     885    text-align:center;
     886    position:fixed;
     887    left:50%;
     888    top:50%;
     889    width:300px;
     890    height:100px;
     891    margin-left:-150px;
     892    margin-top:-50px;
     893    background-color:white;
     894    color:black;
     895    border:solid 1pt black;
     896    border-radius:20px;
     897    box-shadow:20px 20px 20px darkgray;
     898    padding:15px;
     899    display:none;
     900    font-size:12pt;
     901    font-weight:bold;
     902}
     903#sp_saveButton, #sp_closeButton {
     904    background-color:blue;
     905    color:white;
     906    font-size:12pt;
     907    display:inline;
     908    width:100px;
     909    height:30px;
     910    text-align:center;
     911    border-radius:10px;
     912}
     913@media (min-height:700px) and (min-width:1100px) {
     914    #editFile {
     915        width:900px;
     916        height:700px;
     917        margin-left:-450px;
     918        margin-top:-350px;
     919        font-size:12pt;
     920        left:50%;
     921        top:50%;
     922    }
     923    #ef_saveButton, #ef_closeButton, #sp_saveButton, #sp_closeButton, #ef_debug_button, #ef_modify, #ef_hexdump_button {
     924        font-size:12pt;
     925    }
     926}
     927@media (max-height:699px), (max-width:1099px) {
     928    #editFile {
     929        width:98%;
     930        height:90%;
     931        margin-left:-49%;
     932        margin-top:-5%;
     933        font-size:10pt;
     934        left:50%;
     935        top:10%;
     936    }
     937    #ef_saveButton, #ef_closeButton, #sp_saveButton, #sp_closeButton, #ef_debug_button, #ef_modify, #ef_hexdump_button {
     938        font-size:10pt;
     939    }
     940}
     941/* css trick to remember, might want to use this in the future to provide line numbers while editing.
     942
     943pre
     944{
     945    counter-reset: thecodenumbering;
     946}
     947
     948code
     949{
     950    counter-increment: thecodenumbering;
     951}
     952code:before
     953{
     954    padding-right:5px;
     955    content: counter(thecodenumbering);
     956}
     957
     958*/
  • child-themes-helper/trunk/js/js_common_fn.js

    r1971785 r2021576  
    2121        var parentGrid = document.getElementById("parentGrid")
    2222
     23        // Clears inline styles
    2324        if (childGrid != null) {
    2425            childGrid.style = "";
     
    2930    }
    3031}
    31 /*
    32 if (window.location.href.toLowerCase().indexOf("screenshotoptions") >= 0) {
    33     var fn = null;
    34     if (window.onload != null) {
    35         fn = window.onload;
    36     }
    37     window.onload = function () {
    38         updateColorPicker();
    39         document.getElementById("bcc_cpOuter").style.display = "inline";
    40         document.getElementById("fcc_cpOuter").style.display = "inline";
    41 
    42 
    43         if (fn != null) {
    44             window[fn]();
    45         }
    46     }
    47 }
    48 */
    4932function getElementTree(element) {
    5033    if (element == null) {
     
    148131        pas_cth_js_popupMessage(abbr, "color saved");
    149132        document.getElementById("popupMessageBox").style.display = "inline";
     133    } else if ("EDITFILEOUTPUT:{" == response.left("EDITFILEOUTPUT:{".length).toUpperCase()) {
     134        response = response.right(response.length - "EDITFILEOUTPUT:{".length);
     135        response = response.left(response.length - 1);
     136        processEditFile(response);
    150137    } else if ("DEBUG:{" == response.left("DEBUG:{".length).toUpperCase()) {
    151138        actionBox = document.getElementById("actionBox");
     
    171158    return pas_cth_js_createBox("actionBox", "");
    172159}
    173 function pas_cth_js_createBox(id,className) {
     160function pas_cth_js_createBox(id, className, parent = document.getElementsByTagName("body")[0], clickClose = false) {
    174161    var box = document.getElementById(id);
    175162    if (box != null && box != undefined) {
     
    184171        box.className = className;
    185172    }
    186     theBody = document.getElementsByTagName("body")[0];
    187     theBody.appendChild(box);
     173
     174    parent.appendChild(box);
     175
     176    if (clickClose) {
     177        box.onclick= function () {
     178            if (this.parentNode != null) {
     179                this.parentNode.removeChild(this);
     180            }
     181            this.remove();
     182        }
     183    } else {
     184        var dismissBTN = document.createElement("p")
     185        dismissBTN.setAttribute("id", "dismissBox");
     186        box.appendChild(dismissBTN);
     187        dismissBTN.innerHTML = "DISMISS";
     188        dismissBTN.onclick = function () {
     189            var ab = document.getElementById("actionBox");
     190            if (ab.parentNode != null) {
     191                ab.parentNode.removeChild(ab);
     192            }
     193            ab.remove();
     194        }
     195
     196
    188197/*
    189     box.onclick= function () {
    190         if (this.parentNode != null) {
    191             this.parentNode.removeChild(this);
    192         }
    193         this.remove();
    194     }
     198        box.oncontextmenu = function () {
     199            box.style.width = "100%";
     200            box.style.height = "100%";
     201            box.style.position = "absolute";
     202            box.style.left = "180px";
     203            box.style.top  = "40px";
     204            box.style.zIndex = 9999999;
     205            box.style.overflow = "scroll";
     206            box.style.marginTop = "0px";
     207            box.style.marginLeft = "0px";
     208            return false;
     209        }
    195210*/
     211    }
    196212    return box;
    197213}
     214/*
     215function findPos(obj) {
     216    var curleft = curtop = 0;
     217    if (obj.offsetParent) {
     218        do {
     219            curleft += obj.offsetLeft;
     220            curtop += obj.offsetTop;
     221        } while (obj = obj.offsetParent);
     222    }
     223    return {left:curleft ,top:curtop};
     224}
     225*/
     226function getPosition(element) {
     227    var rect = element.getBoundingClientRect();
     228
     229    return {left : rect.left, top: rect.top, width : Math.abs(rect.right - rect.left), height : Math.abs(rect.bottom - rect.top) };
     230}
     231function pas_cth_js_addCloseButton(id, parent, text) {
     232    var element = document.createElement("p");
     233    element.setAttribute("id", id);
     234//  element.setAttribute("contentEditable", false);
     235    parent.appendChild(element);
     236    element.innerHTML = text;
     237    element.onclick = function () {
     238        var myParent = this.parentNode;
     239        var myGrandParent = myParent.parentNode;
     240
     241        if (myGrandParent != null) {
     242            myGrandParent.removeChild(myParent);
     243            myParent.remove();
     244        }
     245    }
     246}
     247function getTopLeftPosition(obj = null) {
     248    if (obj != null) {
     249        return getPosition(obj);
     250    } else {
     251        return null;
     252    }
     253}
  • child-themes-helper/trunk/js/pasChildThemes.js

    r1971785 r2021576  
    322322}
    323323
    324 function pas_cth_js_editFile(element) {
    325     alert("Coming soon. This feature is not yet implemented.");
    326 }
    327 
    328324// Responds to an onblur event on the ScreenShot Options page.
    329325function pas_cth_js_SetOption(element) {
     
    428424    }
    429425}
     426function pas_cth_validateField(element) {
     427    if (element.value.trim().length == 0) {
     428        return;
     429    }
     430    if (element.pattern.trim().length == 0) {
     431        return;
     432    }
     433    var re = new RegExp(element.pattern);
     434    var box;
     435    var boxID = "actionBox";
     436    var parent = document.getElementsByTagName("body")[0];
     437    var onclickFunction = true;
     438    var className = "";
     439
     440    if (! re.test(element.value)) {
     441        box = pas_cth_js_createBox(boxID, className, parent, onclickFunction);
     442        box.innerHTML = element.getAttribute("data-message") + "<br><br>Click on this messagebox to close it";
     443    }
     444}
  • child-themes-helper/trunk/lib/plugin_constants.php

    r1947069 r2021576  
    1616define( 'DOTS', '...............................................................................' );
    1717
     18update_option('pas_cth_edit_allowedFileTypes', get_option('pas_cth_edit_allowedFileTypes', ['php', 'js', 'css', 'txt']));
     19
    1820/*
    1921 * As has been the case for many years, Windows uses the folder delimiter of a backslash.
  • child-themes-helper/trunk/pasChildThemes.php

    r1971785 r2021576  
    44    Plugin URI: http://www.paulswarthout.com/Child-Themes-Helper/
    55    Description: ( 1 ) Copies files from the template theme to the child theme, perfectly duplicating the path structure. ( 2 ) Removes file from the child theme, and removes any empty folders that were made empty by the removal of the child theme file. ( 3 ) Creates new child themes from installed template themes.
    6     Version: 1.2
     6    Version: 1.3.2
    77    Author: Paul A. Swarthout
    88    Author URI: http://www.PaulSwarthout.com
     
    166166add_action( 'wp_ajax_saveDefaultFont', Array( $pas_cth_AJAXFunctions, "saveFont" ) );
    167167
     168add_action( 'wp_ajax_editFile', Array( $pas_cth_AJAXFunctions, "editFile" ) );
     169add_action( 'wp_ajax_saveFile', Array( $pas_cth_AJAXFunctions, "saveFile" ) );
     170
    168171// Plugin Deactivation
    169172function pas_cth_deactivate( ) {
  • child-themes-helper/trunk/readme.txt

    r1980824 r2021576  
    55Tags: child themes helper, child themes, child theme, child, theme, template theme, parent theme, developers, IIS, Linux, copy files to child theme, create a child theme
    66Requires at least: 4.7.0
    7 Tested up to: 5.0
    8 Stable tag: 1.2
    9 Requires PHP: 5.5.38
     7Tested up to: 5.0.3
     8Stable tag: 1.3.2
     9Requires PHP: 5.6.31
    1010License: GPLv2 or later
    1111License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1212
    13 Copies parent theme files to a child theme while maintaining the correct folder/subfolder structure in the child theme as in the parent theme.
     13Copies parent theme files to a child theme while maintaining the correct folder/subfolder structure in the child theme as in the parent theme, and more....
    1414
    1515== Description ==
     
    17171. **Copy files from Parent Theme to Child Theme**
    1818    The primary purpose of the Child Themes Helper plugin is to copy files from a parent theme (also called the template theme) to a child theme. The folder path in the parent theme is duplicated in the child theme during the copy.
     19
     201. **Edit Child Theme Files**
     21    Starting with the Child Themes Helper version 1.3, you can now edit your child theme files and save the changes. You can also "edit" your parent theme files, but they are marked read-only and you will not be able to save any changes that you make.
    1922
    20231. **Remove files from the Child Theme**
     
    4346
    4447    - *Child Themes Helper access*
    45         The Child Themes Helper is accessed from the WordPress dashboard under the heading "Child Themes Helper". It is located immediately below the *Appearance* Dashboard menu item.
     48        The Child Themes Helper is accessed from the WordPress dashboard under the heading "Child Themes Helper". The menu item may be found immediately below the *Appearance* Dashboard menu item.
    4649
    4750    - *Platform Support*
    4851        The Child Themes Helper plugin has been tested with both WordPress running on a Windows server and with WordPress running on a Linux server. Development is done on a Windows platform. If you find any compatibility issues with Linux, please let me know.
    4952   
    50     - *Child Themes Helper plugin code*
    51         The GitHub repository for this plugin can be found [here](https://github.com/PaulSwarthout/pasChildThemes).
     53    - *Child Themes Helper on GitHub*
     54        The GitHub repository for this plugin can be found [here](https://github.com/PaulSwarthout/pasChildThemes). Stable versions are usually found on the WordPress SVN repository. Intermediate versions are often found on GitHub.
    5255
    5356    - *Development versions*
    54         Versions 1\.1\.3 and 1\.0 are available for download and install.
     57        Versions 1\.2, 1\.1\.3 and 1\.0 are available for download and install.
     58
     59    - *Known Bug*
     60        Although the Child Themes Helper plugin is mostly responsive, the Edit File functionality doesn't work very well on small screens. But does anybody actually modify themes on smartphones and tablets? (Please say 'No').
     61
     62    - *Future Thoughts*
     63        When I created this plugin, I created it for how I worked -- on a development website on a development server. As such, all of the functionality of this plugin shines when the child theme is the Active theme. Recently, I discovered that, that might not be an accurate statement for everybody else. So, the next release -- version 1.4 -- will let the Child Themes Helper work on any child theme, whether it's active or not.
     64
    5565
    5666== Installation ==
     
    7888I am glad you asked.
    7989
    80 - The ability to directly edit a file will be available in version 1.3.
    81 - The ability to edit (through a form prompt) the different fields that define a child theme. Basically this will let your directly edit the style.css header without opening the file and editting it directly.
     90- The ability to directly edit a file will be available in version 1.3 -- DONE. Added to this release.
     91- The ability to edit and modify your child theme WITHOUT it being the active theme.
     92- The ability to edit (through a form prompt) the different fields that define a child theme. Basically this will let you directly edit the style.css header without opening the file and editing it directly.
    8293- I'm considering a drag and drop feature for files, rather than just click on them for the added user feedback that it supports. For now, users of wide screens see a hovering prompt that tells them what they're about to do. But users of small screens, do see that. I think a drag and drop feature would help the users of smalls screens be more clear on what's about to happen.
    8394
     
    101112You did nothing wrong. Your browser will cache the screenshot file to help your website to load more quickly. The solution is to clear your browser's image cache. [Here's a good article](https://kb.iu.edu/d/ahic) that I found that gives great directions on how to accomplish that.
    102113
    103 = I generated a screenshot but all I see is the background color. What did I do wrong? =
    104 
    105 Updated Answer: This issue has been fixed in version 1.2. Please update.
    106 
    107 Original Answer: You did nothing wrong. Just generate the screenshot again and it should work fine. This is a nagging issue that happens when the chosen font doesn't exist in the assets/fonts folder. If you have deleted any fonts from that folder, or updated the Child Themes Helper to a newer version, the font that you chose (or defaulted to) on the Options page, may no longer exist. The first time you generate a screenshot, it cannot find the chosen font, so it resets it to the default font. The second time you generate a screenshot, it will use the default. Also, note the question above.
    108 
    109114= What does the "Generate ScreenShot" option do? =
    110115
    111116The WordPress Themes page displays a graphic for each theme. A newly created child theme does not have a graphic. The Generate ScreenShot menu option creates a temporary graphic. Generally, developers will use a copy of a header image for their screenshot. But rather than leaving it blank until later, the Child Themes Helper plugin will create a temporary graphic which displays the child theme name, the parent theme name, a message indicating that the child theme was created using the Child Themes Helper and the Child Themes Helper's developer's web address. It is expected that the developer will want to replace this temporary graphic with their own custom graphic at a later time. Please check out [my demo page](http://www.1acsi.com) where you are welcome to take the Child Themes Helper plugin for a test drive.
     117
     118= Why create a screenshot? I'm just going to delete it later anyway. =
     119
     120I hate the fact that nothing appears as a graphic in a newly created child theme. It just bugs me. So I thought I'd do something about it. But most of all, it was a learning experience. I got to learn about CSS grids, and the GD graphics library, and handling fonts.
    112121
    113122== Screenshots ==
     
    119128
    120129== Changelog ==
     130
     131= 1.3.2 =
     132- Tested with WP 5.0.3.
     133
     134- Added the ability to edit files in the child theme or the parent theme. You have the ability to save any changes that you make to the child theme, but the parent theme files are read-only. You can, of course, click on the parent theme file to copy it to the child theme, and then right click on the file to edit it, make your changes, and save those changes in the child theme file.
     135
     136- Fixed a bug where you could create a child theme whose name didn't start with a letter, and then crash it when you tried to set it active..
     137
     138- Fixed a bug where files whose names started with an underscore couldn't be copied. The problem is caused by the WordPress core function sanitize_file_name() which strips (trims) periods, dashes, and underscores from the beginning of a filename. The solution was to create a new function to sanitize file names that didn't strip the underscores. I kept the functionality that strips leading periods and dashes. I tripped on this bug when testing a newly created child theme of TwentyNineteen, which has filenames that begin with an underscore.
    121139
    122140= 1.2 =
Note: See TracChangeset for help on using the changeset viewer.