WordPress.org

Make WordPress Core

Changeset 41806


Ignore:
Timestamp:
10/10/17 05:33:57 (2 months 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.