diff --git a/plugins/baser-core/src/Utility/BcFileUploader.php b/plugins/baser-core/src/Utility/BcFileUploader.php index 4b3fecdc2f..9d2ca72521 100644 --- a/plugins/baser-core/src/Utility/BcFileUploader.php +++ b/plugins/baser-core/src/Utility/BcFileUploader.php @@ -12,11 +12,14 @@ namespace BaserCore\Utility; use ArrayObject; +use Cake\Core\Configure; +use Cake\Core\Plugin; use Cake\Http\Session; use Cake\ORM\Entity; use Cake\ORM\Table; use Cake\Routing\Router; use Cake\Utility\Hash; +use Cake\Utility\Inflector; use BaserCore\Vendor\Imageresizer; use Cake\Datasource\EntityInterface; use BaserCore\Annotation\Note; @@ -742,10 +745,17 @@ public function renameToBasenameField($setting, $file, $entity, $copy = false) $oldSaveDir = ''; if (file_exists($saveDir . $oldName)) { $oldSaveDir = $saveDir; - } elseif (file_exists($saveDirInTheme . $oldName)) { + } elseif ($saveDirInTheme !== false && file_exists($saveDirInTheme . $oldName)) { $oldSaveDir = $saveDirInTheme; + } else { + // 管理画面からの操作時など getSaveDir(true) が正しいテーマを参照しない場合に + // コアフロントテーマの files ディレクトリも確認する(初期データ複製対応) + $coreFrontThemeSaveDir = $this->getCoreFrontThemeSaveDir(); + if ($coreFrontThemeSaveDir && file_exists($coreFrontThemeSaveDir . $oldName)) { + $oldSaveDir = $coreFrontThemeSaveDir; + } } - if (!file_exists($oldSaveDir . $oldName)) { + if (!$oldSaveDir || !file_exists($oldSaveDir . $oldName)) { return ''; } $newName = $this->getFieldBasename($setting, $file, $entity); @@ -937,6 +947,42 @@ public function getUniqueFileName(array $setting, array $file, EntityInterface $ } } + /** + * コアフロントテーマの files 保存ディレクトリを取得する + * + * 管理画面からファイルを操作する場合など getSaveDir(true) が正しいテーマを + * 参照できないケースのフォールバックとして使用する + * (初期データのアイキャッチはコアフロントテーマ側に保存されているため) + * + * @return string|false + */ + private function getCoreFrontThemeSaveDir(): string|false + { + $frontTheme = Configure::read('BcApp.coreFrontTheme'); + if (!$frontTheme) return false; + $basePath = false; + $themeCandidates = array_unique([ + $frontTheme, + Inflector::camelize($frontTheme, '-'), + ]); + foreach ($themeCandidates as $themeCandidate) { + try { + $candidatePath = Plugin::path($themeCandidate) . 'webroot' . DS . 'files' . DS; + } catch (\Throwable $e) { + continue; + } + if (is_dir($candidatePath)) { + $basePath = $candidatePath; + break; + } + } + if (!$basePath) return false; + if ($this->settings['saveDir']) { + return $basePath . $this->settings['saveDir'] . DS; + } + return $basePath; + } + /** * 保存先のフォルダを設定し、取得する * @param bool $isTheme diff --git a/plugins/bc-blog/tests/TestCase/Model/BlogPostsTableTest.php b/plugins/bc-blog/tests/TestCase/Model/BlogPostsTableTest.php index 50f389f61a..6db127dd1b 100755 --- a/plugins/bc-blog/tests/TestCase/Model/BlogPostsTableTest.php +++ b/plugins/bc-blog/tests/TestCase/Model/BlogPostsTableTest.php @@ -23,8 +23,12 @@ use BcBlog\Test\Factory\BlogContentFactory; use BcBlog\Test\Factory\BlogPostFactory; use BcBlog\Test\Scenario\MultiSiteBlogPostScenario; +use Cake\Core\Configure; +use Cake\Core\Plugin; use Cake\Event\Event; +use Cake\Utility\Inflector; use CakephpFixtureFactories\Scenario\ScenarioAwareTrait; +use Laminas\Diactoros\UploadedFile; use ArrayObject; /** * Class BlogPostsTableTest @@ -649,78 +653,157 @@ public function testCreatePreviewData() */ public function testCopyEyeCatch() { - $this->markTestIncomplete('こちらのテストはまだ未確認です'); - if (is_dir(WWW_ROOT . '/files/blog/999')) { - $folder = new BcFolder(WWW_ROOT . '/files/blog/999'); - $folder->delete(); - } - copy(__DIR__ . '/../../Images/File/test1.png', __DIR__ . '/../../Images/File/test1_.png'); - $this->loadFixtureScenario(InitAppScenario::class); - BlogContentFactory::make()->forCopyEyeCatch()->persist(); - - $this->loginAdmin($this->getRequest()); - - $this->BlogPostsTable->setupUpload(999); - - $data = [ - 'no' => 1, - 'name' => 'test-name', - 'blog_content_id' => 999, - 'posts_date' => '2022-07-16 00:00:00', - 'content' => 'test-content', - 'detail' => 'test-detail', - 'status' => 0, - 'publish_begin' => null, - 'publish_end' => null, - 'user_id' => 1, - 'eye_catch' => [ - 'name' => 'test.png', - 'type' => 'image/png', - 'tmp_name' => __DIR__ . '/../../Images/File/test1_.png', - 'error' => 0, - 'size' => 1, - ], - ]; - - // 作成 - $blogPost1 = $this->BlogPostsTable->save($this->BlogPostsTable->newEntity($data)); - $blogPost1no = $blogPost1->no; - $ym = date('Y/m'); - $fileDir = WWW_ROOT . '/files/blog/999/blog_posts/' . $ym; - - $this->assertEquals($ym . '/0000000' . $blogPost1no . '_eye_catch.png', $blogPost1->eye_catch); - $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch.png')); - $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch__thumb.png')); - $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch__mobile_thumb.png')); - - // コピー - $blogPost2 = $this->BlogPostsTable->copy(null, clone $blogPost1); - $blogPost2no = $blogPost2->no; - - // 複製元が影響を受けていないか - $this->assertEquals($ym . '/0000000' . $blogPost1no . '_eye_catch.png', $blogPost1->eye_catch); - $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch.png')); - $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch__thumb.png')); - $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch__mobile_thumb.png')); - - // 複製できているか - $this->assertEquals($ym . '/0000000' . $blogPost2no . '_eye_catch.png', $blogPost2->eye_catch); - $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch.png')); - $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch__thumb.png')); - $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch__mobile_thumb.png')); - - // 削除 - $this->BlogPostsTable->delete($blogPost2); - $blogPost2 = $this->BlogPostsTable->find()->where([ - 'BlogPosts.id' => $blogPost2->id - ])->first(); - $this->assertEmpty($blogPost2); - $this->assertFalse(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch.png')); - $this->assertFalse(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch__thumb.png')); - $this->assertFalse(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch__mobile_thumb.png')); - - $dir = new BcFolder(WWW_ROOT . '/files/blog/999'); - $dir->delete(); + $frontTheme = Configure::read('BcApp.coreFrontTheme'); + $themeFilesDir = false; + if ($frontTheme) { + $themeCandidates = array_unique([$frontTheme, Inflector::camelize($frontTheme, '-')]); + foreach ($themeCandidates as $themeCandidate) { + try { + $themeFilesDir = Plugin::path($themeCandidate) . 'webroot' . DS . 'files' . DS . 'blog' . DS . '999'; + break; + } catch (\Throwable $e) { + continue; + } + } + } + + $tmpUploadPath = __DIR__ . '/../../Images/File/test1_.png'; + + try { + if (is_dir(WWW_ROOT . 'files/blog/999')) { + $folder = new BcFolder(WWW_ROOT . 'files/blog/999'); + $folder->delete(); + } + if ($themeFilesDir && is_dir($themeFilesDir)) { + $folder = new BcFolder($themeFilesDir); + $folder->delete(); + } + + copy(__DIR__ . '/../../Images/File/test1.png', $tmpUploadPath); + $this->loadFixtureScenario(InitAppScenario::class); + BlogContentFactory::make()->forCopyEyeCatch()->persist(); + + $this->loginAdmin($this->getRequest()); + + $this->BlogPostsTable->setupUpload(999); + + $data = [ + 'no' => 1, + 'name' => 'test-name', + 'title' => 'Test Blog Post', + 'blog_content_id' => 999, + 'posted' => '2022-07-16 00:00:00', + 'content' => 'test-content', + 'detail' => 'test-detail', + 'status' => 0, + 'publish_begin' => null, + 'publish_end' => null, + 'user_id' => 1, + 'eye_catch' => new UploadedFile( + $tmpUploadPath, + 1, + UPLOAD_ERR_OK, + 'test.png', + 'image/png' + ), + ]; + + // 作成 + $blogPost1 = $this->BlogPostsTable->save($this->BlogPostsTable->newEntity($data)); + $blogPost1no = $blogPost1->no; + $ym = date('Y/m'); + $fileDir = WWW_ROOT . 'files/blog/999/blog_posts/' . $ym; + + $this->assertEquals($ym . '/0000000' . $blogPost1no . '_eye_catch.png', $blogPost1->eye_catch); + $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch.png')); + $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch__thumb.png')); + $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch__mobile_thumb.png')); + + // コピー + $blogPost2 = $this->BlogPostsTable->copy(null, clone $blogPost1); + $blogPost2no = $blogPost2->no; + + // 複製元が影響を受けていないか + $this->assertEquals($ym . '/0000000' . $blogPost1no . '_eye_catch.png', $blogPost1->eye_catch); + $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch.png')); + $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch__thumb.png')); + $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost1no . '_eye_catch__mobile_thumb.png')); + + // 複製できているか + $this->assertEquals($ym . '/0000000' . $blogPost2no . '_eye_catch.png', $blogPost2->eye_catch); + $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch.png')); + $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch__thumb.png')); + $this->assertTrue(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch__mobile_thumb.png')); + + // 削除 + $this->BlogPostsTable->delete($blogPost2); + $blogPost2 = $this->BlogPostsTable->find()->where([ + 'BlogPosts.id' => $blogPost2->id + ])->first(); + $this->assertEmpty($blogPost2); + $this->assertFalse(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch.png')); + $this->assertFalse(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch__thumb.png')); + $this->assertFalse(is_file($fileDir . '/0000000' . $blogPost2no . '_eye_catch__mobile_thumb.png')); + + // ===== 初期記事(テーマ内 files)のアイキャッチ複製テスト ===== + $this->assertNotFalse($themeFilesDir); + + // テーマの files ディレクトリに初期データ用のアイキャッチファイルを配置する + $themePostDir = $themeFilesDir . DS . 'blog_posts' . DS . '2023' . DS . '02' . DS; + if (!is_dir($themePostDir)) { + mkdir($themePostDir, 0777, true); + } + $initEyeCatch = '2023/02/00000099_eye_catch.png'; + copy(__DIR__ . '/../../Images/File/test1.png', $themePostDir . '00000099_eye_catch.png'); + copy(__DIR__ . '/../../Images/File/test1.png', $themePostDir . '00000099_eye_catch__thumb.png'); + copy(__DIR__ . '/../../Images/File/test1.png', $themePostDir . '00000099_eye_catch__mobile_thumb.png'); + + // DB に初期記事を直接作成(eye_catch にはテーマ内のパスをセット) + BlogPostFactory::make([ + 'no' => 99, + 'name' => 'test-initial', + 'blog_content_id' => 999, + 'title' => 'Initial Test Post', + 'content' => '', + 'detail' => '', + 'status' => 0, + 'posted' => '2023-02-01 00:00:00', + 'user_id' => 1, + 'eye_catch' => $initEyeCatch, + ])->persist(); + $initialPost = $this->BlogPostsTable->find()->where([ + 'BlogPosts.blog_content_id' => 999, + 'BlogPosts.no' => 99, + ])->first(); + + // 初期記事を複製 → テーマ内ファイルから webroot/files へコピーされる + $copiedInitPost = $this->BlogPostsTable->copy(null, clone $initialPost); + + // アイキャッチが webroot/files 配下に複製されているか + $this->assertNotEmpty($copiedInitPost->eye_catch); + $copiedEyeCatch = $copiedInitPost->eye_catch; + $this->assertTrue(is_file(WWW_ROOT . 'files/blog/999/blog_posts/' . $copiedEyeCatch)); + $thumbPath = str_replace('_eye_catch.', '_eye_catch__thumb.', $copiedEyeCatch); + $this->assertTrue(is_file(WWW_ROOT . 'files/blog/999/blog_posts/' . $thumbPath)); + $mobileThumbPath = str_replace('_eye_catch.', '_eye_catch__mobile_thumb.', $copiedEyeCatch); + $this->assertTrue(is_file(WWW_ROOT . 'files/blog/999/blog_posts/' . $mobileThumbPath)); + + // 複製元(テーマ内)のファイルは残っているか + $this->assertEquals($initEyeCatch, $initialPost->eye_catch); + $this->assertTrue(is_file($themePostDir . '00000099_eye_catch.png')); + } finally { + if (is_file($tmpUploadPath)) { + unlink($tmpUploadPath); + } + if (is_dir(WWW_ROOT . 'files/blog/999')) { + $dir = new BcFolder(WWW_ROOT . 'files/blog/999'); + $dir->delete(); + } + if ($themeFilesDir && is_dir($themeFilesDir)) { + $themeDir = new BcFolder($themeFilesDir); + $themeDir->delete(); + } + } } /**