-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #926 from Automattic/add/support-sub-sub-directory…
…-ms-file-uploads Support sub-sub directory multisite file uploads
- Loading branch information
Showing
3 changed files
with
213 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?php | ||
|
||
namespace Automattic\VIP\Files; | ||
|
||
class Path_Utils { | ||
public static function is_subdirectory_multisite_path( $file_path, $uploads_path ) { | ||
$pattern = '#^/[_0-9a-zA-Z-]+/' . $uploads_path . '/sites/[0-9]+/#'; | ||
|
||
return preg_match( $pattern, $file_path ); | ||
} | ||
|
||
public static function is_sub_subdirectory_multisite_path( $file_path, $uploads_path ) { | ||
$pattern = '#^/[_0-9a-zA-Z-]+/[_0-9a-zA-Z-]+/' . $uploads_path . '/sites/[0-9]+/#'; | ||
|
||
return preg_match( $pattern, $file_path ); | ||
} | ||
|
||
/** | ||
* Strips off sub- and sub-subdirectory from a valid, multisite file path. | ||
* | ||
* For example, given a URL like `/subsite-1/subsite_2/wp-content/uploads/sites/1/file.jpg`. | ||
* We will get back `/wp-content/uploads/sites/1/file.jpg`. | ||
* | ||
* Note: only supports 2 levels of subdirectories. | ||
* | ||
* @param string $file_path The file path to sanitize | ||
* @param string $uploads_path The relative path from ABSPATH to `uploads`, minus leading and trailing slashes (e.g. `wp-content/uploads`) | ||
* @return string|bool The sanitized path if it's a valid path, otherwise `false`. | ||
*/ | ||
public static function trim_leading_multisite_directory( $file_path, $uploads_path ) { | ||
if ( self::is_sub_subdirectory_multisite_path( $file_path, $uploads_path ) ) { | ||
return preg_replace( '#^/[_0-9a-zA-Z-]+/[_0-9a-zA-Z-]+#', '', $file_path ); | ||
} elseif ( self::is_subdirectory_multisite_path( $file_path, $uploads_path ) ) { | ||
return preg_replace( '#^/[_0-9a-zA-Z-]+#', '', $file_path ); | ||
} | ||
|
||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
<?php | ||
|
||
namespace Automattic\VIP\Files; | ||
|
||
class Path_Utils_Test extends \PHPUnit_Framework_TestCase { | ||
public static function setUpBeforeClass() { | ||
parent::setUpBeforeClass(); | ||
|
||
require_once( __DIR__ . '/../../files/class-path-utils.php' ); | ||
} | ||
|
||
public function get_test_data__is_subdirectory_multisite_path__nope() { | ||
return [ | ||
'missing_leading_slash' => [ | ||
'subsite1/wp-content/uploads/sites/1/file.jpg', | ||
], | ||
'no_subdirectory' => [ | ||
'/wp-content/uploads/sites/1/file.jpg', | ||
], | ||
'invalid_subdirectory' => [ | ||
'/subsité1/wp-content/uploads/sites/1/file.jpg', | ||
], | ||
'empty_subdirectory' => [ | ||
'//wp-content/uploads/sites/1/file.jpg', | ||
], | ||
'no_wp-content-uploads' => [ | ||
'/subsite1/sites/1/file.jpg', | ||
], | ||
'no_sites' => [ | ||
'/subsite1/wp-content/uploads/1/file.jpg', | ||
], | ||
'invalid_site-id' => [ | ||
'/subsite1/wp-content/uploads/sites/xyz/file.jpg', | ||
], | ||
]; | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__is_subdirectory_multisite_path__nope | ||
*/ | ||
public function test__is_subdirectory_multisite_path__nope( $test_path ) { | ||
$actual_return = Path_Utils::is_subdirectory_multisite_path( $test_path, 'wp-content/uploads' ); | ||
|
||
$this->assertEquals( 0, $actual_return ); | ||
} | ||
|
||
public function get_test_data__is_subdirectory_multisite_path__yep() { | ||
return [ | ||
'valid_path' => [ | ||
'/subsite1/wp-content/uploads/sites/1/file.jpg', | ||
], | ||
'all_allowed_characters' => [ | ||
'/1s_u-bSIT2e/wp-content/uploads/sites/4567/file.jpg', | ||
], | ||
]; | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__is_subdirectory_multisite_path__yep | ||
*/ | ||
public function test__is_subdirectory_multisite_path__yep( $test_path ) { | ||
$actual_return = Path_Utils::is_subdirectory_multisite_path( $test_path, 'wp-content/uploads' ); | ||
|
||
$this->assertEquals( 1, $actual_return ); | ||
} | ||
|
||
public function get_test_data__is_sub_subdirectory_multisite_path__nope() { | ||
return [ | ||
'missing_leading_slash' => [ | ||
'subsite1/subsite2/wp-content/uploads/sites/1/file.jpg', | ||
], | ||
// Can't test for no_subdirectory :) | ||
'no_sub_subdirectory' => [ | ||
'/subsite1/wp-content/uploads/sites/1/file.jpg', | ||
], | ||
'invalid_subdirectory' => [ | ||
'/subsité1/wp-content/uploads/sites/1/file.jpg', | ||
], | ||
'invalid_sub_subdirectory' => [ | ||
'/subsite1/subsité2/wp-content/uploads/sites/1/file.jpg', | ||
], | ||
'empty_subdirectory' => [ | ||
'//subsite2/wp-content/uploads/sites/1/file.jpg', | ||
], | ||
'empty_sub_subdirectory' => [ | ||
'/subsite1//wp-content/uploads/sites/1/file.jpg', | ||
], | ||
'no_wp-content-uploads' => [ | ||
'/subsite1/subsite2/sites/1/file.jpg', | ||
], | ||
'no_sites' => [ | ||
'/subsite1/subsite2/wp-content/uploads/1/file.jpg', | ||
], | ||
'invalid_site-id' => [ | ||
'/subsite1/subsite2/wp-content/uploads/sites/xyz/file.jpg', | ||
], | ||
]; | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__is_sub_subdirectory_multisite_path__nope | ||
*/ | ||
public function test__is_sub_subdirectory_multisite_path__nope( $test_path ) { | ||
$actual_return = Path_Utils::is_sub_subdirectory_multisite_path( $test_path, 'wp-content/uploads' ); | ||
|
||
$this->assertEquals( 0, $actual_return ); | ||
} | ||
|
||
public function get_test_data__is_sub_subdirectory_multisite_path__yep() { | ||
return [ | ||
'valid_path' => [ | ||
'/subsite1/subsite2/wp-content/uploads/sites/1/file.jpg', | ||
], | ||
'all_allowed_characters' => [ | ||
'/1s_u-bSIT2e/bSIT2e_1s_u-/wp-content/uploads/sites/4567/file.jpg', | ||
], | ||
]; | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__is_sub_subdirectory_multisite_path__yep | ||
*/ | ||
public function test__is_sub_subdirectory_multisite_path__yep( $test_path ) { | ||
$actual_return = Path_Utils::is_sub_subdirectory_multisite_path( $test_path, 'wp-content/uploads' ); | ||
|
||
$this->assertEquals( 1, $actual_return ); | ||
} | ||
|
||
public function get_test_data__trim_leading_multisite_directory() { | ||
return [ | ||
'sub_subdirectory' => [ | ||
'/subsite1/subsite2/wp-content/uploads/sites/10/file.jpg', | ||
'/wp-content/uploads/sites/10/file.jpg', | ||
], | ||
'subdirectory' => [ | ||
'/subsite1/wp-content/uploads/sites/2/file.jpg', | ||
'/wp-content/uploads/sites/2/file.jpg', | ||
], | ||
'other' => [ | ||
'', | ||
false, | ||
] | ||
]; | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__trim_leading_multisite_directory | ||
*/ | ||
public function test__trim_leading_multisite_directory( $test_path, $expected_result ) { | ||
$actual_result = Path_Utils::trim_leading_multisite_directory( $test_path, 'wp-content/uploads' ); | ||
|
||
$this->assertEquals( $expected_result, $actual_result ); | ||
} | ||
} |