diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql index e5eebae36d..2c888bac75 100644 --- a/phpBB/install/schemas/schema_data.sql +++ b/phpBB/install/schemas/schema_data.sql @@ -289,13 +289,13 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('exts_composer_enab INSERT INTO phpbb_config (config_name, config_value) VALUES ('exts_composer_purge_on_remove', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\attachment\provider', 'phpbb\storage\provider\local'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\attachment\config\path', 'files'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\attachment\config\depth', '0'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\attachment\config\subfolders', '0'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\avatar\provider', 'phpbb\storage\provider\local'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\avatar\config\path', 'images/avatars/upload'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\avatar\config\depth', '0'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\avatar\config\subfolders', '0'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\backup\provider', 'phpbb\storage\provider\local'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\backup\config\path', 'store'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\backup\config\depth', '0'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('storage\backup\config\subfolders', '0'); INSERT INTO phpbb_config (config_name, config_value, is_dynamic) VALUES ('cache_last_gc', '0', 1); INSERT INTO phpbb_config (config_name, config_value, is_dynamic) VALUES ('cron_lock', '0', 1); diff --git a/phpBB/language/en/acp/storage.php b/phpBB/language/en/acp/storage.php index 5e171ac6f0..69faa58030 100644 --- a/phpBB/language/en/acp/storage.php +++ b/phpBB/language/en/acp/storage.php @@ -55,9 +55,10 @@ $lang = array_merge($lang, array( 'STORAGE_BACKUP_TITLE' => 'Backup storage', // Local adapter - 'STORAGE_ADAPTER_LOCAL_NAME' => 'Local', - 'STORAGE_ADAPTER_LOCAL_OPTION_PATH' => 'Path', - 'STORAGE_ADAPTER_LOCAL_OPTION_DEPTH' => 'Depth', + 'STORAGE_ADAPTER_LOCAL_NAME' => 'Local', + 'STORAGE_ADAPTER_LOCAL_OPTION_PATH' => 'Path', + 'STORAGE_ADAPTER_LOCAL_OPTION_SUBFOLDERS' => 'Organize in subfolders', + 'STORAGE_ADAPTER_LOCAL_OPTION_SUBFOLDERS_EXPLAIN' => 'Some web servers may have problems storing large number of files in a single directory. Enable this option to distribute files in different directories.', // Form validation 'STORAGE_UPDATE_SUCCESSFUL' => 'All storage types were successfully updated.', diff --git a/phpBB/phpbb/db/migration/data/v330/storage_adapter_local_depth.php b/phpBB/phpbb/db/migration/data/v330/storage_adapter_local_subfolders.php similarity index 75% rename from phpBB/phpbb/db/migration/data/v330/storage_adapter_local_depth.php rename to phpBB/phpbb/db/migration/data/v330/storage_adapter_local_subfolders.php index 2da4c1954a..491388054c 100644 --- a/phpBB/phpbb/db/migration/data/v330/storage_adapter_local_depth.php +++ b/phpBB/phpbb/db/migration/data/v330/storage_adapter_local_subfolders.php @@ -13,7 +13,7 @@ namespace phpbb\db\migration\data\v330; -class storage_adapter_local_depth extends \phpbb\db\migration\migration +class storage_adapter_local_subfolders extends \phpbb\db\migration\migration { static public function depends_on() { @@ -29,15 +29,15 @@ class storage_adapter_local_depth extends \phpbb\db\migration\migration return array( array('if', array( ($this->config['storage\\attachment\\provider'] == \phpbb\storage\provider\local::class), - array('config.add', array('storage\\attachment\\config\\depth', '0')), + array('config.add', array('storage\\attachment\\config\\subfolders', '0')), )), array('if', array( ($this->config['storage\\avatar\\provider'] == \phpbb\storage\provider\local::class), - array('config.add', array('storage\\avatar\\config\\depth', '0')), + array('config.add', array('storage\\avatar\\config\\subfolders', '0')), )), array('if', array( ($this->config['storage\\backup\\provider'] == \phpbb\storage\provider\local::class), - array('config.add', array('storage\\backup\\config\\depth', '0')), + array('config.add', array('storage\\backup\\config\\subfolders', '0')), )), ); } diff --git a/phpBB/phpbb/storage/adapter/local.php b/phpBB/phpbb/storage/adapter/local.php index c4a95da25b..c7fbe544e9 100644 --- a/phpBB/phpbb/storage/adapter/local.php +++ b/phpBB/phpbb/storage/adapter/local.php @@ -73,10 +73,17 @@ class local implements adapter_interface, stream_interface * This is for those who have problems storing a large number of files in * a single directory. * More info: https://tracker.phpbb.com/browse/PHPBB3-15371 - * + */ + + /* + * @var bool subfolders + */ + protected $subfolders; + + /* * @var int dir_depth */ - protected $dir_depth; + protected $dir_depth = 2; /** * Constructor @@ -101,7 +108,7 @@ class local implements adapter_interface, stream_interface $this->path = $options['path']; $this->root_path = $this->phpbb_root_path . $options['path']; - $this->dir_depth = (int) $options['depth']; + $this->subfolders = (bool) $options['subfolders']; } /** @@ -249,17 +256,20 @@ class local implements adapter_interface, stream_interface */ protected function remove_empty_dirs($path) { - $dirpath = dirname($this->root_path . $path); - $filepath = dirname($this->root_path . $this->get_path($path) . $this->get_filename($path)); - $path = filesystem_helper::make_path_relative($filepath, $dirpath); - - do + if ($this->subfolders) { - $parts = explode('/', $path); - $parts = array_slice($parts, 0, -1); - $path = implode('/', $parts); + $dirpath = dirname($this->root_path . $path); + $filepath = dirname($this->root_path . $this->get_path($path) . $this->get_filename($path)); + $path = filesystem_helper::make_path_relative($filepath, $dirpath); + + do + { + $parts = explode('/', $path); + $parts = array_slice($parts, 0, -1); + $path = implode('/', $parts); + } + while ($path && @rmdir($dirpath . '/' . $path)); } - while ($path && @rmdir($dirpath . '/' . $path)); } /** @@ -271,21 +281,22 @@ class local implements adapter_interface, stream_interface protected function get_path($path) { $dirname = dirname($path); + $dirname = ($dirname != '.') ? $dirname . DIRECTORY_SEPARATOR : ''; - $hash = md5(basename($path)); - - $parts = str_split($hash, 2); - $parts = array_slice($parts, 0, $this->dir_depth); - - // Create path - $path = $dirname . DIRECTORY_SEPARATOR; - - if (!empty($parts)) + if ($this->subfolders) { - $path .= implode(DIRECTORY_SEPARATOR, $parts) . DIRECTORY_SEPARATOR; + $hash = md5(basename($path)); + + $parts = str_split($hash, 2); + $parts = array_slice($parts, 0, $this->dir_depth); + + if (!empty($parts)) + { + $dirname .= implode(DIRECTORY_SEPARATOR, $parts) . DIRECTORY_SEPARATOR; + } } - return $path; + return $dirname; } /** diff --git a/phpBB/phpbb/storage/provider/local.php b/phpBB/phpbb/storage/provider/local.php index 09e5bf9ac4..8e0de2b6fd 100644 --- a/phpBB/phpbb/storage/provider/local.php +++ b/phpBB/phpbb/storage/provider/local.php @@ -37,8 +37,14 @@ class local implements provider_interface public function get_options() { return [ - 'path' => array('type' => 'text'), - 'depth' => array('type' => 'text'), + 'path' => ['type' => 'text'], + 'subfolders' => [ + 'type' => 'radio', + 'options' => [ + 'ENABLE' => '1', + 'DISABLE' => '0', + ], + ], ]; } diff --git a/tests/storage/adapter/local_subfolders_test.php b/tests/storage/adapter/local_subfolders_test.php new file mode 100644 index 0000000000..250e1973c7 --- /dev/null +++ b/tests/storage/adapter/local_subfolders_test.php @@ -0,0 +1,136 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + + class phpbb_storage_adapter_local_subfolders_test extends phpbb_test_case + { + protected $adapter; + + protected $path; + + protected $filesystem; + + public function setUp() + { + parent::setUp(); + + $this->filesystem = new \phpbb\filesystem\filesystem(); + $phpbb_root_path = getcwd() . DIRECTORY_SEPARATOR; + + $this->adapter = new \phpbb\storage\adapter\local($this->filesystem, new \FastImageSize\FastImageSize(), new \phpbb\mimetype\guesser(array(new \phpbb\mimetype\extension_guesser)), $phpbb_root_path); + $this->adapter->configure(['path' => 'test_path', 'subfolders' => true]); + + $this->path = $phpbb_root_path . 'test_path/'; + mkdir($this->path); + } + + public function tearDown() + { + $this->adapter = null; + rmdir($this->path); + } + + public function test_put_contents() + { + $this->adapter->put_contents('file.txt', 'abc'); + $this->assertTrue(file_exists($this->path . '3d/8e/file.txt')); + $this->assertEquals(file_get_contents($this->path . '3d/8e/file.txt'), 'abc'); + unlink($this->path . '3d/8e/file.txt'); + rmdir($this->path . '3d/8e'); + rmdir($this->path . '3d'); + } + + public function test_get_contents() + { + mkdir($this->path . '3d/8e', 0777, true); + file_put_contents($this->path . '3d/8e/file.txt', 'abc'); + $this->assertEquals($this->adapter->get_contents('file.txt'), 'abc'); + unlink($this->path . '3d/8e/file.txt'); + rmdir($this->path . '3d/8e'); + rmdir($this->path . '3d'); + } + + public function test_exists() + { + mkdir($this->path . '3d/8e', 0777, true); + touch($this->path . '3d/8e/file.txt'); + $this->assertTrue($this->adapter->exists('file.txt')); + $this->assertFalse($this->adapter->exists('3d/8e/file.txt')); + unlink($this->path . '3d/8e/file.txt'); + rmdir($this->path . '3d/8e'); + rmdir($this->path . '3d'); + } + + public function test_delete_file() + { + mkdir($this->path . '3d/8e', 0777, true); + touch($this->path . '3d/8e/file.txt'); + $this->assertTrue(file_exists($this->path . '3d/8e/file.txt')); + $this->adapter->delete('file.txt'); + $this->assertFalse(file_exists($this->path . '3d/8e/file.txt')); + $this->assertFalse(file_exists($this->path . '3d')); + } + + public function test_rename() + { + mkdir($this->path . '3d/8e', 0777, true); + touch($this->path . '3d/8e/file.txt'); + $this->adapter->rename('file.txt', 'file2.txt'); + $this->assertFalse(file_exists($this->path . '3d/8e/file.txt')); + $this->assertTrue(file_exists($this->path . '27/36/file2.txt')); + $this->assertFalse(file_exists($this->path . '3d')); + unlink($this->path . '27/36/file2.txt'); + rmdir($this->path . '27/36'); + rmdir($this->path . '27'); + } + + public function test_copy() + { + mkdir($this->path . '3d/8e', 0777, true); + file_put_contents($this->path . '3d/8e/file.txt', 'abc'); + $this->adapter->copy('file.txt', 'file2.txt'); + $this->assertEquals(file_get_contents($this->path . '3d/8e/file.txt'), 'abc'); + $this->assertEquals(file_get_contents($this->path . '27/36/file2.txt'), 'abc'); + unlink($this->path . '3d/8e/file.txt'); + rmdir($this->path . '3d/8e'); + rmdir($this->path . '3d'); + unlink($this->path . '27/36/file2.txt'); + rmdir($this->path . '27/36'); + rmdir($this->path . '27'); + } + + public function test_read_stream() + { + mkdir($this->path . '3d/8e', 0777, true); + touch($this->path . '3d/8e/file.txt'); + $stream = $this->adapter->read_stream('file.txt'); + $this->assertTrue(is_resource($stream)); + fclose($stream); + unlink($this->path . '3d/8e/file.txt'); + rmdir($this->path . '3d/8e'); + rmdir($this->path . '3d'); + } + + public function test_write_stream() + { + file_put_contents($this->path . 'file.txt', 'abc'); + $stream = fopen($this->path . 'file.txt', 'rb'); + $this->adapter->write_stream('file2.txt', $stream); + fclose($stream); + $this->assertEquals(file_get_contents($this->path . '27/36/file2.txt'), 'abc'); + unlink($this->path . 'file.txt'); + unlink($this->path . '27/36/file2.txt'); + rmdir($this->path . '27/36'); + rmdir($this->path . '27'); + } + + } diff --git a/tests/storage/adapter/local_test.php b/tests/storage/adapter/local_test.php index a4092e303b..eb4b5e0dcf 100644 --- a/tests/storage/adapter/local_test.php +++ b/tests/storage/adapter/local_test.php @@ -27,7 +27,7 @@ $phpbb_root_path = getcwd() . DIRECTORY_SEPARATOR; $this->adapter = new \phpbb\storage\adapter\local($this->filesystem, new \FastImageSize\FastImageSize(), new \phpbb\mimetype\guesser(array(new \phpbb\mimetype\extension_guesser)), $phpbb_root_path); - $this->adapter->configure(['path' => 'test_path', 'depth' => 2]); + $this->adapter->configure(['path' => 'test_path', 'subfolders' => false]); $this->path = $phpbb_root_path . 'test_path/'; mkdir($this->path); @@ -42,82 +42,61 @@ public function test_put_contents() { $this->adapter->put_contents('file.txt', 'abc'); - $this->assertTrue(file_exists($this->path . '3d/8e/file.txt')); - $this->assertEquals(file_get_contents($this->path . '3d/8e/file.txt'), 'abc'); - unlink($this->path . '3d/8e/file.txt'); - rmdir($this->path . '3d/8e'); - rmdir($this->path . '3d'); + $this->assertTrue(file_exists($this->path . 'file.txt')); + $this->assertEquals(file_get_contents($this->path . 'file.txt'), 'abc'); + unlink($this->path . 'file.txt'); } public function test_get_contents() { - mkdir($this->path . '3d/8e', 0777, true); - file_put_contents($this->path . '3d/8e/file.txt', 'abc'); + file_put_contents($this->path . 'file.txt', 'abc'); $this->assertEquals($this->adapter->get_contents('file.txt'), 'abc'); - unlink($this->path . '3d/8e/file.txt'); - rmdir($this->path . '3d/8e'); - rmdir($this->path . '3d'); + unlink($this->path . 'file.txt'); } public function test_exists() { - mkdir($this->path . '3d/8e', 0777, true); - touch($this->path . '3d/8e/file.txt'); + touch($this->path . 'file.txt'); $this->assertTrue($this->adapter->exists('file.txt')); - $this->assertFalse($this->adapter->exists('3d/8e/file.txt')); - unlink($this->path . '3d/8e/file.txt'); - rmdir($this->path . '3d/8e'); - rmdir($this->path . '3d'); + $this->assertFalse($this->adapter->exists('noexist.txt')); + unlink($this->path . 'file.txt'); } public function test_delete_file() { - mkdir($this->path . '3d/8e', 0777, true); - touch($this->path . '3d/8e/file.txt'); - $this->assertTrue(file_exists($this->path . '3d/8e/file.txt')); + touch($this->path . 'file.txt'); + $this->assertTrue(file_exists($this->path . 'file.txt')); $this->adapter->delete('file.txt'); - $this->assertFalse(file_exists($this->path . '3d/8e/file.txt')); - $this->assertFalse(file_exists($this->path . '3d')); + $this->assertFalse(file_exists($this->path . 'file.txt')); } public function test_rename() { - mkdir($this->path . '3d/8e', 0777, true); - touch($this->path . '3d/8e/file.txt'); + touch($this->path . 'file.txt'); $this->adapter->rename('file.txt', 'file2.txt'); - $this->assertFalse(file_exists($this->path . '3d/8e/file.txt')); - $this->assertTrue(file_exists($this->path . '27/36/file2.txt')); - $this->assertFalse(file_exists($this->path . '3d')); - unlink($this->path . '27/36/file2.txt'); - rmdir($this->path . '27/36'); - rmdir($this->path . '27'); + $this->assertFalse(file_exists($this->path . 'file.txt')); + $this->assertTrue(file_exists($this->path . 'file2.txt')); + $this->assertFalse(file_exists($this->path . 'file.txt')); + unlink($this->path . 'file2.txt'); } public function test_copy() { - mkdir($this->path . '3d/8e', 0777, true); - file_put_contents($this->path . '3d/8e/file.txt', 'abc'); + file_put_contents($this->path . 'file.txt', 'abc'); $this->adapter->copy('file.txt', 'file2.txt'); - $this->assertEquals(file_get_contents($this->path . '3d/8e/file.txt'), 'abc'); - $this->assertEquals(file_get_contents($this->path . '27/36/file2.txt'), 'abc'); - unlink($this->path . '3d/8e/file.txt'); - rmdir($this->path . '3d/8e'); - rmdir($this->path . '3d'); - unlink($this->path . '27/36/file2.txt'); - rmdir($this->path . '27/36'); - rmdir($this->path . '27'); + $this->assertEquals(file_get_contents($this->path . 'file.txt'), 'abc'); + $this->assertEquals(file_get_contents($this->path . 'file2.txt'), 'abc'); + unlink($this->path . 'file.txt'); + unlink($this->path . 'file2.txt'); } public function test_read_stream() { - mkdir($this->path . '3d/8e', 0777, true); - touch($this->path . '3d/8e/file.txt'); + touch($this->path . 'file.txt'); $stream = $this->adapter->read_stream('file.txt'); $this->assertTrue(is_resource($stream)); fclose($stream); - unlink($this->path . '3d/8e/file.txt'); - rmdir($this->path . '3d/8e'); - rmdir($this->path . '3d'); + unlink($this->path . 'file.txt'); } public function test_write_stream() @@ -126,11 +105,9 @@ $stream = fopen($this->path . 'file.txt', 'rb'); $this->adapter->write_stream('file2.txt', $stream); fclose($stream); - $this->assertEquals(file_get_contents($this->path . '27/36/file2.txt'), 'abc'); + $this->assertEquals(file_get_contents($this->path . 'file2.txt'), 'abc'); unlink($this->path . 'file.txt'); - unlink($this->path . '27/36/file2.txt'); - rmdir($this->path . '27/36'); - rmdir($this->path . '27'); + unlink($this->path . 'file2.txt'); } }