diff --git a/src/wp-includes/fonts.php b/src/wp-includes/fonts.php index 690aaa5ffc343..7c89186d0e40c 100644 --- a/src/wp-includes/fonts.php +++ b/src/wp-includes/fonts.php @@ -113,7 +113,7 @@ function wp_get_font_dir() { $site_path = '/sites/' . get_current_blog_id(); } - $defaults = array( + $default_dir = array( 'path' => path_join( WP_CONTENT_DIR, 'fonts' ) . $site_path, 'url' => untrailingslashit( content_url( 'fonts' ) ) . $site_path, 'subdir' => '', @@ -131,7 +131,44 @@ function wp_get_font_dir() { * * @param array $defaults The original fonts directory data. */ - return apply_filters( 'font_dir', $defaults ); + $font_dir = apply_filters( 'font_dir', $default_dir ); + + wp_mkdir_p( $font_dir['path'] ); + + if ( ! is_dir( $font_dir['path'] ) || ! wp_is_writable( $font_dir['path'] ) ) { + + // If the default directory was filtered it uses the filtered values and doesnt fall back to the wp-content/uploads/fonts. + if ( $font_dir !== $default_dir ) { + $font_dir['error'] = sprintf( + /* translators: %s: Directory path. */ + __( 'Unable to create directory %s. Is its parent directory writable by the server?' ), + esc_html( str_replace( ABSPATH, '', $font_dir['path'] ) ) + ); + return $font_dir; + } + + $upload_dir = wp_get_upload_dir(); + $font_dir = array( + 'path' => path_join( $upload_dir['basedir'], 'fonts' ), + 'url' => $upload_dir['baseurl'] . '/fonts', + 'subdir' => '', + 'basedir' => path_join( $upload_dir['basedir'], 'fonts' ), + 'baseurl' => $upload_dir['baseurl'] . '/fonts', + 'error' => false, + ); + + wp_mkdir_p( $font_dir['path'] ); + + if ( ! is_dir( $font_dir['path'] ) || ! wp_is_writable( $font_dir['path'] ) ) { + $font_dir['error'] = sprintf( + /* translators: %s: Directory path. */ + __( 'Unable to create directory %s. Is its parent directory writable by the server?' ), + esc_html( str_replace( ABSPATH, '', $font_dir['path'] ) ) + ); + } + } + + return $font_dir; } /** diff --git a/tests/phpunit/tests/fonts/font-library/wpFontsDir.php b/tests/phpunit/tests/fonts/font-library/wpFontsDir.php index a8f79888315bd..7c371f823ec28 100644 --- a/tests/phpunit/tests/fonts/font-library/wpFontsDir.php +++ b/tests/phpunit/tests/fonts/font-library/wpFontsDir.php @@ -16,30 +16,37 @@ class Tests_Fonts_WpFontDir extends WP_UnitTestCase { public static function set_up_before_class() { parent::set_up_before_class(); - static::$dir_defaults = array( - 'path' => path_join( WP_CONTENT_DIR, 'fonts' ), - 'url' => content_url( 'fonts' ), + $path = path_join( WP_CONTENT_DIR, 'fonts' ); + $url = content_url( 'fonts' ); + self::$dir_defaults = array( + 'path' => $path, + 'url' => $url, 'subdir' => '', - 'basedir' => path_join( WP_CONTENT_DIR, 'fonts' ), - 'baseurl' => content_url( 'fonts' ), + 'basedir' => $path, + 'baseurl' => $url, 'error' => false, ); } + public function tear_down() { + $this->remove_font_paths(); + parent::tear_down(); + } + public function test_fonts_dir() { $font_dir = wp_get_font_dir(); $this->assertSame( $font_dir, static::$dir_defaults ); } - public function test_fonts_dir_with_filter() { + public function test_font_dir_filter() { // Define a callback function to pass to the filter. function set_new_values( $defaults ) { - $defaults['path'] = '/custom-path/fonts/my-custom-subdir'; - $defaults['url'] = 'http://example.com/custom-path/fonts/my-custom-subdir'; - $defaults['subdir'] = 'my-custom-subdir'; - $defaults['basedir'] = '/custom-path/fonts'; - $defaults['baseurl'] = 'http://example.com/custom-path/fonts'; + $defaults['path'] = path_join( WP_CONTENT_DIR, 'custom_dir' ); + $defaults['url'] = 'http://example.com/custom-path/fonts/my-custom-dir'; + $defaults['subdir'] = ''; + $defaults['basedir'] = path_join( WP_CONTENT_DIR, 'custom_dir' ); + $defaults['baseurl'] = 'http://example.com/custom-path/fonts/my-custom-dir'; $defaults['error'] = false; return $defaults; } @@ -51,11 +58,11 @@ function set_new_values( $defaults ) { $font_dir = wp_get_font_dir(); $expected = array( - 'path' => '/custom-path/fonts/my-custom-subdir', - 'url' => 'http://example.com/custom-path/fonts/my-custom-subdir', - 'subdir' => 'my-custom-subdir', - 'basedir' => '/custom-path/fonts', - 'baseurl' => 'http://example.com/custom-path/fonts', + 'path' => path_join( WP_CONTENT_DIR, 'custom_dir' ), + 'url' => 'http://example.com/custom-path/fonts/my-custom-dir', + 'subdir' => '', + 'basedir' => path_join( WP_CONTENT_DIR, 'custom_dir' ), + 'baseurl' => 'http://example.com/custom-path/fonts/my-custom-dir', 'error' => false, ); @@ -69,4 +76,113 @@ function set_new_values( $defaults ) { $this->assertSame( static::$dir_defaults, $font_dir, 'The wp_get_font_dir() method should return the default values.' ); } + + public function test_non_writable_filtered_dir() { + // Define a callback function to pass to the filter. + function set_custom_dir( $defaults ) { + $defaults['path'] = path_join( WP_CONTENT_DIR, 'custom_dir' ); + $defaults['url'] = 'http://example.com/custom-path/fonts/my-custom-dir'; + $defaults['basedir'] = path_join( WP_CONTENT_DIR, 'custom_dir' ); + $defaults['baseurl'] = 'http://example.com/custom-path/fonts/my-custom-dir'; + return $defaults; + } + + // Add the filter. + add_filter( 'font_dir', 'set_custom_dir' ); + + // make 'wp-content/custom_dir' non-writable + $this->create_fake_file_to_avoid_dir_creation( path_join( WP_CONTENT_DIR, 'custom_dir' ) ); + + // Gets the fonts dir. + $font_dir = wp_get_font_dir(); + + $expected = array( + 'path' => path_join( WP_CONTENT_DIR, 'custom_dir' ), + 'url' => 'http://example.com/custom-path/fonts/my-custom-dir', + 'subdir' => '', + 'basedir' => path_join( WP_CONTENT_DIR, 'custom_dir' ), + 'baseurl' => 'http://example.com/custom-path/fonts/my-custom-dir', + 'error' => 'Unable to create directory wp-content/custom_dir. Is its parent directory writable by the server?', + ); + + $this->assertSame( $expected, $font_dir, 'The wp_get_font_dir() method should return the filtered values with an error message.' ); + } + + public function test_should_create_fonts_dir_in_uploads_when_fails_in_wp_content() { + // Set the expected results. + $upload_dir = wp_upload_dir(); + $expected = array( + 'path' => path_join( $upload_dir['basedir'], 'fonts' ), + 'url' => $upload_dir['baseurl'] . '/fonts', + 'subdir' => '', + 'basedir' => path_join( $upload_dir['basedir'], 'fonts' ), + 'baseurl' => $upload_dir['baseurl'] . '/fonts', + 'error' => false, + ); + + $this->create_fake_file_to_avoid_dir_creation( static::$dir_defaults['path'] ); + $this->assertFileExists( static::$dir_defaults['path'] ); + + $font_dir = wp_get_font_dir(); + + $this->assertDirectoryDoesNotExist( static::$dir_defaults['path'], 'The `wp-content/fonts` directory should not exist.' ); + $this->assertDirectoryExists( $font_dir['path'], 'The `uploads/fonts` directory should exist.' ); + $this->assertSame( $expected, $font_dir, 'The `fonts` directory should be a subdir in the `uploads` directory.' ); + } + + public function test_should_return_error_if_unable_to_create_fonts_dir_in_uploads() { + // Disallow the creation of the `wp-content/fonts` directory. + $this->create_fake_file_to_avoid_dir_creation( static::$dir_defaults['path'] ); + $this->assertFileExists( static::$dir_defaults['path'] ); + + // Disallow the creation of the `uploads/fonts` directory. + $upload_dir = wp_upload_dir(); + $font_upload_path = path_join( $upload_dir['basedir'], 'fonts' ); + $this->create_fake_file_to_avoid_dir_creation( $font_upload_path ); + $this->assertFileExists( $font_upload_path ); + + $expected = array( + 'path' => $font_upload_path, + 'url' => $upload_dir['baseurl'] . '/fonts', + 'subdir' => '', + 'basedir' => $font_upload_path, + 'baseurl' => $upload_dir['baseurl'] . '/fonts', + 'error' => 'Unable to create directory wp-content/uploads/fonts. Is its parent directory writable by the server?', + ); + + $font_dir = wp_get_font_dir(); + + $this->assertDirectoryDoesNotExist( $font_upload_path, 'The `uploads/fonts` directory should not exist.' ); + $this->assertSame( $expected, $font_dir, 'As /wp-content/uplods/fonts is not writable the error key should be populated with an error message.' ); + } + + private function remove_font_paths() { + $paths = array( + path_join( WP_CONTENT_DIR, 'fonts' ), + path_join( WP_CONTENT_DIR, 'custom_dir' ), + path_join( WP_CONTENT_DIR, 'uploads/fonts' ), + ); + + foreach ( $paths as $path ) { + if ( ! is_dir( $path ) ) { + if ( file_exists( $path ) ) { + unlink( $path ); + } + } else { + $this->rmdir( $path ); + rmdir( $path ); + } + } + } + + /** + * A placeholder "fake" file at $path triggers `wp_mkdir_p()` to fail into the `file_exists()` bail out, causing `is_dir()` to return `false`. + * This effectively makes $path unwritable. + */ + private function create_fake_file_to_avoid_dir_creation( $path ) { + file_put_contents( + $path, + 'fake file' + ); + } }