diff --git a/app/Enums/PageMetaRobots.php b/app/Enums/PageMetaRobots.php new file mode 100644 index 000000000..e9077792e --- /dev/null +++ b/app/Enums/PageMetaRobots.php @@ -0,0 +1,43 @@ + '検索結果に表示させない(noindex)', + self::nofollow => 'ページ内のリンク先を検索エンジンに辿らせない(nofollow)', + self::noarchive => '検索結果にキャッシュ(保存版)を出さない(noarchive)', + self::nosnippet => '検索結果にページの説明を表示させない(nosnippet)', + self::noimageindex => 'ページ内の画像を画像検索に載せない(noimageindex)', + ]; + + /** + * 指定された値を説明文の配列に変換 + */ + public static function descriptions(array $values): array + { + $members = static::getMembers(); + + $descriptions = []; + foreach ($values as $value) { + if (array_key_exists($value, $members)) { + $descriptions[] = $members[$value]; + } + } + + return $descriptions; + } +} diff --git a/app/Models/Common/Page.php b/app/Models/Common/Page.php index 42159f280..789dc16ac 100644 --- a/app/Models/Common/Page.php +++ b/app/Models/Common/Page.php @@ -40,6 +40,7 @@ class Page extends Model 'class', 'othersite_url', 'othersite_url_target', + 'meta_robots', 'transfer_lower_page_flag', 'password', ]; @@ -539,6 +540,26 @@ public function getLayoutTitle() } } + /** + * メタrobotsの有効値を取得(自ページから親を遡って検索) + */ + public function getMetaRobots(?Collection $page_tree = null): ?string + { + if (empty($this->id)) { + return null; + } + + $page_tree = $this->getPageTreeByGoingBackParent($page_tree); + + foreach ($page_tree as $page) { + if (!empty($page->meta_robots)) { + return $page->meta_robots; + } + } + + return null; + } + /** * パスワードを要求するかの判断 */ diff --git a/app/Plugins/Manage/PageManage/PageManage.php b/app/Plugins/Manage/PageManage/PageManage.php index 8f3d81c82..276d6bfcf 100644 --- a/app/Plugins/Manage/PageManage/PageManage.php +++ b/app/Plugins/Manage/PageManage/PageManage.php @@ -3,6 +3,7 @@ namespace App\Plugins\Manage\PageManage; use App\Enums\PageCvsIndex; +use App\Enums\PageMetaRobots; use App\Enums\WebsiteType; use App\Models\Common\Buckets; use App\Models\Common\Frame; @@ -12,6 +13,7 @@ use App\Models\Common\PageRole; use App\Models\User\Contents\Contents; use App\Plugins\Manage\ManagePluginBase; +use App\Rules\CustomValiMetaRobots; use App\Rules\CustomValiTextMax; use App\Rules\CustomValiUrlMax; use App\Traits\Migration\MigrationTrait; @@ -183,6 +185,7 @@ private function pageValidator($request, $page_id = null) 'ip_address' => ['nullable', new CustomValiTextMax()], 'othersite_url' => ['nullable', new CustomValiUrlMax()], 'class' => ['nullable', 'max:255'], + 'meta_robots' => ['nullable', 'array', new CustomValiMetaRobots()], ]); $validator->setAttributeNames([ 'page_name' => 'ページ名', @@ -193,10 +196,46 @@ private function pageValidator($request, $page_id = null) 'ip_address' => 'IPアドレス制限', 'othersite_url' => '外部サイトURL', 'class' => 'メニュークラス名', + 'meta_robots' => '検索避け設定', ]); return $validator; } + /** + * meta robotsの入力値を正規化 + */ + private function normalizeMetaRobots($request): ?string + { + $meta_robots = $request->input('meta_robots'); + + if (is_array($meta_robots)) { + $meta_robots = array_filter($meta_robots, function ($value) { + return $value !== null && $value !== ''; + }); + + if (empty($meta_robots)) { + return null; + } + + $meta_robots = array_unique($meta_robots); + + $allowed = PageMetaRobots::getMemberKeys(); + $meta_robots = array_values(array_intersect($allowed, $meta_robots)); + + if (empty($meta_robots)) { + return null; + } + + return implode(',', $meta_robots); + } + + if (is_string($meta_robots) && $meta_robots !== '') { + return in_array($meta_robots, PageMetaRobots::getMemberKeys(), true) ? $meta_robots : null; + } + + return null; + } + /** * CSVインポート時のエラーチェックルール */ @@ -235,6 +274,8 @@ public function store($request) return redirect()->back()->withErrors($validator)->withInput(); } + $meta_robots = $this->normalizeMetaRobots($request); + // ページデータの登録 $page = new Page; $page->page_name = $request->page_name; @@ -251,6 +292,7 @@ public function store($request) $page->othersite_url = $request->othersite_url; $page->othersite_url_target = (isset($request->othersite_url_target) ? $request->othersite_url_target : 0); $page->transfer_lower_page_flag = $request->transfer_lower_page_flag ?? 0; + $page->meta_robots = $meta_robots; $page->class = $request->class; $page->save(); @@ -278,6 +320,8 @@ public function update($request, $page_id) return redirect()->back()->withErrors($validator)->withInput(); } + $meta_robots = $this->normalizeMetaRobots($request); + // ページデータの更新 Page::where('id', $page_id) ->update([ @@ -295,6 +339,7 @@ public function update($request, $page_id) 'othersite_url' => $request->othersite_url, 'othersite_url_target' => (isset($request->othersite_url_target) ? $request->othersite_url_target : 0), 'transfer_lower_page_flag' => $request->transfer_lower_page_flag ?? 0, + 'meta_robots' => $meta_robots, 'class' => $request->class, ]); diff --git a/app/Rules/CustomValiMetaRobots.php b/app/Rules/CustomValiMetaRobots.php new file mode 100644 index 000000000..61c797a07 --- /dev/null +++ b/app/Rules/CustomValiMetaRobots.php @@ -0,0 +1,61 @@ + + */ + private $invalidValues = []; + + /** + * Determine if the validation rule passes. + */ + public function passes($attribute, $value) + { + if (is_null($value) || $value === '') { + return true; + } + + $values = []; + + if (is_array($value)) { + $values = $value; + } else { + $values = [$value]; + } + + $values = array_filter($values, function ($item) { + return $item !== null && $item !== ''; + }); + + if (empty($values)) { + return true; + } + + $allowed = PageMetaRobots::getMemberKeys(); + + $this->invalidValues = array_diff($values, $allowed); + + return empty($this->invalidValues); + } + + /** + * Get the validation error message. + */ + public function message() + { + if (!empty($this->invalidValues)) { + return ':attributeに不正な値(' . implode(', ', $this->invalidValues) . ')が含まれています。'; + } + + return ':attributeに不正な値が含まれています。'; + } +} diff --git a/database/migrations/2025_09_25_101737_add_meta_robots_to_pages_table.php b/database/migrations/2025_09_25_101737_add_meta_robots_to_pages_table.php new file mode 100644 index 000000000..f017ccac0 --- /dev/null +++ b/database/migrations/2025_09_25_101737_add_meta_robots_to_pages_table.php @@ -0,0 +1,32 @@ +string('meta_robots')->nullable()->after('transfer_lower_page_flag')->comment('検索避け設定(robots meta)'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('pages', function (Blueprint $table) { + $table->dropColumn('meta_robots'); + }); + } +} diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 1afa40296..b53eaa31f 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -41,6 +41,17 @@ @if (Configs::getConfigsValue($cc_configs, 'description')) @endif +{{-- 検索避け設定 --}} +@php + $meta_robots = null; + if (isset($page)) { + $page_tree = app('request')->attributes->get('page_tree'); + $meta_robots = $page->getMetaRobots($page_tree); + } +@endphp +@if ($meta_robots) + +@endif {{-- OGP Settings --}} @if (Configs::getConfigsValue($cc_configs, 'og_site_name')) diff --git a/resources/views/plugins/manage/page/page.blade.php b/resources/views/plugins/manage/page/page.blade.php index 878da2c11..4c07d84a5 100644 --- a/resources/views/plugins/manage/page/page.blade.php +++ b/resources/views/plugins/manage/page/page.blade.php @@ -6,6 +6,7 @@ * @category ページ管理 --}} @php +use App\Enums\PageMetaRobots; use App\Models\Common\Page; @endphp @@ -196,6 +197,7 @@ function select_page(source_id, page_name) {