Make WordPress Core

Changeset 41806


Ignore:
Timestamp:
10/10/2017 05:33:57 AM (7 years ago)
Author:
pento
Message:

File Editor: Add support for more than one sub-directory level.

The theme and plugin editors now list all files in the selected theme or plugin, recursing through subdirectories as necessary.

Props WraithKenny, schlessera, chsxf, MikeHansenMe, Daedalon, valendesigns, westonruter, pento.
Fixes #6531.

Location:
trunk
Files:
1 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/file.php

    r41805 r41806  
    121121 *
    122122 * @since 2.6.0
     123 * @since 4.9.0 Added the `$exclusions` parameter.
    123124 *
    124125 * @param string $folder Optional. Full path to folder. Default empty.
    125126 * @param int    $levels Optional. Levels of folders to follow, Default 100 (PHP Loop limit).
     127 * @param array  $exclusions Optional. List of folders and files to skip.
    126128 * @return bool|array False on failure, Else array of files
    127129 */
    128 function list_files( $folder = '', $levels = 100 ) {
    129     if ( empty($folder) )
     130function list_files( $folder = '', $levels = 100, $exclusions = array() ) {
     131    if ( empty( $folder ) ) {
    130132        return false;
    131 
    132     if ( ! $levels )
     133    }
     134
     135    $folder = trailingslashit( $folder );
     136
     137    if ( ! $levels ) {
    133138        return false;
     139    }
    134140
    135141    $files = array();
    136     if ( $dir = @opendir( $folder ) ) {
    137         while (($file = readdir( $dir ) ) !== false ) {
    138             if ( in_array($file, array('.', '..') ) )
     142
     143    $dir = @opendir( $folder );
     144    if ( $dir ) {
     145        while ( ( $file = readdir( $dir ) ) !== false ) {
     146            // Skip current and parent folder links.
     147            if ( in_array( $file, array( '.', '..' ), true ) ) {
    139148                continue;
    140             if ( is_dir( $folder . '/' . $file ) ) {
    141                 $files2 = list_files( $folder . '/' . $file, $levels - 1);
    142                 if ( $files2 )
     149            }
     150
     151            // Skip hidden and excluded files.
     152            if ( '.' === $file[0] || in_array( $file, $exclusions, true ) ) {
     153                continue;
     154            }
     155
     156            if ( is_dir( $folder . $file ) ) {
     157                $files2 = list_files( $folder . $file, $levels - 1 );
     158                if ( $files2 ) {
    143159                    $files = array_merge($files, $files2 );
    144                 else
    145                     $files[] = $folder . '/' . $file . '/';
     160                } else {
     161                    $files[] = $folder . $file . '/';
     162                }
    146163            } else {
    147                 $files[] = $folder . '/' . $file;
     164                $files[] = $folder . $file;
    148165            }
    149166        }
    150167    }
    151168    @closedir( $dir );
     169
    152170    return $files;
    153171}
  • trunk/src/wp-admin/includes/plugin.php

    r41713 r41806  
    191191 * @return array List of files relative to the plugin root.
    192192 */
    193 function get_plugin_files($plugin) {
     193function get_plugin_files( $plugin ) {
    194194    $plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
    195     $dir = dirname($plugin_file);
    196     $plugin_files = array($plugin);
    197     if ( is_dir($dir) && $dir != WP_PLUGIN_DIR ) {
    198         $plugins_dir = @ opendir( $dir );
    199         if ( $plugins_dir ) {
    200             while (($file = readdir( $plugins_dir ) ) !== false ) {
    201                 if ( substr($file, 0, 1) == '.' )
    202                     continue;
    203                 if ( is_dir( $dir . '/' . $file ) ) {
    204                     $plugins_subdir = @ opendir( $dir . '/' . $file );
    205                     if ( $plugins_subdir ) {
    206                         while (($subfile = readdir( $plugins_subdir ) ) !== false ) {
    207                             if ( substr($subfile, 0, 1) == '.' )
    208                                 continue;
    209                             $plugin_files[] = plugin_basename("$dir/$file/$subfile");
    210                         }
    211                         @closedir( $plugins_subdir );
    212                     }
    213                 } else {
    214                     if ( plugin_basename("$dir/$file") != $plugin )
    215                         $plugin_files[] = plugin_basename("$dir/$file");
    216                 }
    217             }
    218             @closedir( $plugins_dir );
    219         }
    220     }
     195    $dir = dirname( $plugin_file );
     196
     197    $data = get_plugin_data( $plugin_file );
     198    $label = isset( $data['Version'] )
     199        ? sanitize_key( 'files_' . $plugin . '-' . $data['Version'] )
     200        : sanitize_key( 'files_' . $plugin );
     201    $transient_key = substr( $label, 0, 29 ) . md5( $label );
     202
     203    $plugin_files = get_transient( $transient_key );
     204    if ( false !== $plugin_files ) {
     205        return $plugin_files;
     206    }
     207
     208    $plugin_files = array( plugin_basename( $plugin_file ) );
     209
     210    if ( is_dir( $dir ) && WP_PLUGIN_DIR !== $dir ) {
     211
     212        /**
     213         * Filters the array of excluded directories and files while scanning the folder.
     214         *
     215         * @since 4.9.0
     216         *
     217         * @param array $exclusions Array of excluded directories and files.
     218         */
     219        $exclusions = (array) apply_filters( 'plugin_files_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) );
     220
     221        $list_files = list_files( $dir, 100, $exclusions );
     222        $list_files = array_map( 'plugin_basename', $list_files );
     223
     224        $plugin_files = array_merge( $plugin_files, $list_files );
     225        $plugin_files = array_values( array_unique( $plugin_files ) );
     226    }
     227
     228    set_transient( $transient_key, $plugin_files, HOUR_IN_SECONDS );
    221229
    222230    return $plugin_files;
  • trunk/src/wp-admin/theme-editor.php

    r41774 r41806  
    7676    switch ( $type ) {
    7777        case 'php':
    78             $allowed_files += $theme->get_files( 'php', 1 );
     78            $allowed_files += $theme->get_files( 'php', -1 );
    7979            $has_templates = ! empty( $allowed_files );
    8080            break;
    8181        case 'css':
    82             $style_files = $theme->get_files( 'css' );
     82            $style_files = $theme->get_files( 'css', -1 );
    8383            $allowed_files['style.css'] = $style_files['style.css'];
    8484            $allowed_files += $style_files;
    8585            break;
    8686        default:
    87             $allowed_files += $theme->get_files( $type );
     87            $allowed_files += $theme->get_files( $type, -1 );
    8888            break;
    8989    }
  • trunk/src/wp-includes/class-wp-theme.php

    r41688 r41806  
    982982     * @param bool $search_parent Optional. Whether to return parent files. Defaults to false.
    983983     * @return array Array of files, keyed by the path to the file relative to the theme's directory, with the values
    984      *               being absolute paths.
     984     *               being absolute paths.
    985985     */
    986986    public function get_files( $type = null, $depth = 0, $search_parent = false ) {
    987         $files = (array) self::scandir( $this->get_stylesheet_directory(), $type, $depth );
    988 
    989         if ( $search_parent && $this->parent() )
    990             $files += (array) self::scandir( $this->get_template_directory(), $type, $depth );
     987        // get and cache all theme files to start with.
     988        $label = sanitize_key( 'files_' . $this->cache_hash . '-' . $this->get( 'Version' ) );
     989        $transient_key = substr( $label, 0, 29 ) . md5( $label );
     990
     991        $all_files = get_transient( $transient_key );
     992        if ( false === $all_files ) {
     993            $all_files = (array) self::scandir( $this->get_stylesheet_directory(), null, -1 );
     994
     995            if ( $search_parent && $this->parent() ) {
     996                $all_files += (array) self::scandir( $this->get_template_directory(), null, -1 );
     997            }
     998
     999            set_transient( $transient_key, $all_files, HOUR_IN_SECONDS );
     1000        }
     1001
     1002        // Filter $all_files by $type & $depth.
     1003        $files = array();
     1004        if ( $type ) {
     1005            $type = (array) $type;
     1006            $_extensions = implode( '|', $type );
     1007        }
     1008        foreach ( $all_files as $key => $file ) {
     1009            if ( $depth >= 0 && substr_count( $key, '/' ) > $depth ) {
     1010                continue; // Filter by depth.
     1011            }
     1012            if ( ! $type || preg_match( '~\.(' . $_extensions . ')$~', $file ) ) { // Filter by type.
     1013                $files[ $key ] = $file;
     1014            }
     1015        }
    9911016
    9921017        return $files;
     
    11081133     */
    11091134    private static function scandir( $path, $extensions = null, $depth = 0, $relative_path = '' ) {
    1110         if ( ! is_dir( $path ) )
     1135        if ( ! is_dir( $path ) ) {
    11111136            return false;
     1137        }
    11121138
    11131139        if ( $extensions ) {
     
    11171143
    11181144        $relative_path = trailingslashit( $relative_path );
    1119         if ( '/' == $relative_path )
     1145        if ( '/' == $relative_path ) {
    11201146            $relative_path = '';
     1147        }
    11211148
    11221149        $results = scandir( $path );
     
    11261153         * Filters the array of excluded directories and files while scanning theme folder.
    11271154         *
    1128          * @since 4.7.4
     1155         * @since 4.7.4
    11291156         *
    11301157         * @param array $exclusions Array of excluded directories and files.
    11311158         */
    1132         $exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules' ) );
     1159        $exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) );
    11331160
    11341161        foreach ( $results as $result ) {
     
    11371164            }
    11381165            if ( is_dir( $path . '/' . $result ) ) {
    1139                 if ( ! $depth )
     1166                if ( ! $depth ) {
    11401167                    continue;
     1168                }
    11411169                $found = self::scandir( $path . '/' . $result, $extensions, $depth - 1 , $relative_path . $result );
    11421170                $files = array_merge_recursive( $files, $found );
  • trunk/tests/phpunit/tests/admin/includesPlugin.php

    r35242 r41806  
    9595
    9696    /**
     97     * @covers ::get_plugin_files
     98     */
     99    public function test_get_plugin_files_folder() {
     100        $plugin_dir = WP_PLUGIN_DIR . '/list_files_test_plugin';
     101        @mkdir( $plugin_dir );
     102        $plugin = $this->_create_plugin(null, 'list_files_test_plugin.php', $plugin_dir );
     103
     104        $sub_dir = trailingslashit( dirname( $plugin[1] ) ) . 'subdir';
     105        @mkdir( $sub_dir );
     106        @file_put_contents( $sub_dir . '/subfile.php', '<?php // Silence.' );
     107
     108        $plugin_files = get_plugin_files( plugin_basename( $plugin[1] ) );
     109        $expected = array(
     110            'list_files_test_plugin/list_files_test_plugin.php',
     111            'list_files_test_plugin/subdir/subfile.php',
     112        );
     113        $this->assertEquals( $expected, $plugin_files );
     114
     115        unlink( $sub_dir . '/subfile.php' );
     116        unlink( $plugin[1] );
     117        rmdir( $sub_dir );
     118        rmdir( $plugin_dir );
     119    }
     120
     121    /**
    97122     * @covers ::get_mu_plugins
    98123     */
Note: See TracChangeset for help on using the changeset viewer.