diff --git a/app/Enums/PageCvsIndex.php b/app/Enums/PageCvsIndex.php index cef26b78e..1deb24656 100755 --- a/app/Enums/PageCvsIndex.php +++ b/app/Enums/PageCvsIndex.php @@ -21,7 +21,8 @@ final class PageCvsIndex extends EnumsBase const header_color = 3; const theme = 4; const layout = 5; - const base_display_flag = 6; + const layout_inherit_flag = 6; + const base_display_flag = 7; /** key/valueの連想配列 */ const enum = [ @@ -31,6 +32,7 @@ final class PageCvsIndex extends EnumsBase self::header_color => 'header_color', self::theme => 'theme', self::layout => 'layout', + self::layout_inherit_flag => 'layout_inherit_flag', self::base_display_flag => 'base_display_flag', ]; } diff --git a/app/Http/Controllers/Core/DefaultController.php b/app/Http/Controllers/Core/DefaultController.php index 9ecfbd97d..adfd903bd 100644 --- a/app/Http/Controllers/Core/DefaultController.php +++ b/app/Http/Controllers/Core/DefaultController.php @@ -335,7 +335,7 @@ private function getTopPage($page, $languages = null) private function getLayout($page, $page_tree) { // レイアウトの初期値 - $layout_default = '1|1|0|1'; + $layout_default = config('connect.BASE_LAYOUT_DEFAULT'); // if (empty($this->page)) { if (empty($page)) { @@ -344,15 +344,24 @@ private function getLayout($page, $page_tree) // レイアウト $layout = null; - foreach ($page_tree as $tmp_page) { // レイアウト - if (empty($layout) && $tmp_page->layout) { - $layout = $tmp_page->layout; + if (empty($tmp_page->layout)) { + continue; + } + if ($tmp_page->id != $page->id && !is_null($tmp_page->layout_inherit_flag) && (int)$tmp_page->layout_inherit_flag === 0) { + // 祖先のレイアウトが「このページのみ」指定なら下層への継承対象から外す。 + continue; } + $layout = $tmp_page->layout; + break; + } + // 親も含めて空の場合は、基本レイアウトを使い、未設定なら初期値を返却 + if (empty($layout)) { + $layout = Configs::getSharedConfigsValue('base_layout', $layout_default); } - // 親も含めて空の場合は、初期値を返却 if (empty($layout)) { + // DBに不正な値が入るなどして予期せず空になった場合の安全弁 $layout = $layout_default; } return $layout; diff --git a/app/Models/Common/Page.php b/app/Models/Common/Page.php index 437f93341..548f17567 100644 --- a/app/Models/Common/Page.php +++ b/app/Models/Common/Page.php @@ -34,6 +34,7 @@ class Page extends Model 'header_color', 'theme', 'layout', + 'layout_inherit_flag', 'base_display_flag', 'membership_flag', 'container_flag', diff --git a/app/Plugins/Manage/PageManage/PageManage.php b/app/Plugins/Manage/PageManage/PageManage.php index 0aba57bad..3c85ffbd5 100644 --- a/app/Plugins/Manage/PageManage/PageManage.php +++ b/app/Plugins/Manage/PageManage/PageManage.php @@ -185,6 +185,7 @@ private function pageValidator($request, $page_id = null) 'ip_address' => ['nullable', new CustomValiTextMax()], 'othersite_url' => ['nullable', new CustomValiUrlMax()], 'class' => ['nullable', 'max:255'], + 'layout_inherit_flag' => ['nullable', Rule::in(['0', '1'])], 'meta_robots' => ['nullable', 'array', new CustomValiMetaRobots()], ]); $validator->setAttributeNames([ @@ -196,6 +197,7 @@ private function pageValidator($request, $page_id = null) 'ip_address' => 'IPアドレス制限', 'othersite_url' => '外部サイトURL', 'class' => 'メニュークラス名', + 'layout_inherit_flag' => 'レイアウトの適用範囲', 'meta_robots' => '検索避け設定', ]); return $validator; @@ -248,6 +250,7 @@ private function pageUploadValidatorRules() PageCvsIndex::header_color => ['required', 'max:255'], PageCvsIndex::theme => ['required'], PageCvsIndex::layout => ['required'], + PageCvsIndex::layout_inherit_flag => ['required', Rule::in(['0', '1'])], PageCvsIndex::base_display_flag => ['required', Rule::in(['0', '1'])], ]; @@ -285,6 +288,7 @@ public function store($request) $page->header_color = $request->header_color; $page->theme = $request->theme; $page->layout = $request->layout; + $page->layout_inherit_flag = $request->layout_inherit_flag ?? 1; $page->base_display_flag = (isset($request->base_display_flag) ? $request->base_display_flag : 0); $page->membership_flag = (isset($request->membership_flag) ? $request->membership_flag : 0); $page->container_flag = (isset($request->container_flag) ? $request->container_flag : 0); @@ -334,6 +338,7 @@ public function update($request, $page_id) 'header_color' => $request->header_color, 'theme' => $request->theme, 'layout' => $request->layout, + 'layout_inherit_flag' => $request->layout_inherit_flag ?? 1, 'base_display_flag' => (isset($request->base_display_flag) ? $request->base_display_flag : 0), 'membership_flag' => (isset($request->membership_flag) ? $request->membership_flag : 0), 'container_flag' => (isset($request->container_flag) ? $request->container_flag : 0), @@ -509,6 +514,7 @@ public function downloadCsvSample($request, $id = null) PageCvsIndex::header_color => 'NULL', PageCvsIndex::theme => 'NULL', PageCvsIndex::layout => 'NULL', + PageCvsIndex::layout_inherit_flag => '1', PageCvsIndex::base_display_flag => '1', ]; $csv_array[2] = [ @@ -518,6 +524,7 @@ public function downloadCsvSample($request, $id = null) PageCvsIndex::header_color => 'NULL', PageCvsIndex::theme => 'NULL', PageCvsIndex::layout => 'NULL', + PageCvsIndex::layout_inherit_flag => '1', PageCvsIndex::base_display_flag => '1', ]; @@ -543,16 +550,20 @@ private function checkHeader($header_columns) return array("CSVファイルが空です。"); } - // ヘッダーカラム - $header_column_format = $this->getCsvHeader(); + // 必須ヘッダーカラム + $required_header_columns = $this->getCsvRequiredHeader(); // 項目の不足チェック - $shortness = array_diff($header_column_format, $header_columns); + $shortness = array_diff($required_header_columns, $header_columns); if (!empty($shortness)) { return array(implode(",", $shortness) . " が不足しています。"); } + + // 許可されるヘッダーカラム(必須 + 任意) + $allowed_header_columns = $this->getCsvHeader(); + // 項目の不要チェック - $excess = array_diff($header_columns, $header_column_format); + $excess = array_diff($header_columns, $allowed_header_columns); if (!empty($excess)) { return array(implode(",", $excess) . " は不要です。"); } @@ -574,10 +585,61 @@ private function getCsvHeader(): array return $header_column_format; } + /** + * CSV必須ヘッダー取得 + */ + private function getCsvRequiredHeader(): array + { + $required_header = $this->getCsvHeader(); + + foreach (array_keys($this->getOptionalCsvHeaderDefaults()) as $optional_header) { + $optional_header_index = array_search($optional_header, $required_header, true); + if ($optional_header_index !== false) { + unset($required_header[$optional_header_index]); + } + } + + return $required_header; + } + + /** + * CSV任意ヘッダーの既定値 + */ + private function getOptionalCsvHeaderDefaults(): array + { + return [ + PageCvsIndex::getDescription(PageCvsIndex::layout_inherit_flag) => '1', + ]; + } + + /** + * CSV列をヘッダー名ベースで正規化(任意ヘッダー欠落時は既定値で補完) + */ + private function normalizeCsvColumns(array $header_columns, array $csv_columns): array + { + $header_name_values = []; + foreach ($header_columns as $column_index => $header_name) { + $header_name_values[$header_name] = $csv_columns[$column_index] ?? null; + } + + $normalized_columns = []; + $optional_defaults = $this->getOptionalCsvHeaderDefaults(); + foreach ($this->getCsvHeader() as $column_index => $header_name) { + if (array_key_exists($header_name, $header_name_values)) { + $normalized_columns[$column_index] = $header_name_values[$header_name]; + continue; + } + + $normalized_columns[$column_index] = $optional_defaults[$header_name] ?? null; + } + + return $normalized_columns; + } + /** * CSVデータ行チェック */ - private function checkPageline($fp) + private function checkPageline($fp, array $header_columns) { // CSVインポート時のエラーチェックルール $rules = $this->pageUploadValidatorRules(); @@ -587,6 +649,8 @@ private function checkPageline($fp) $permanent_links = []; while (($csv_columns = fgetcsv($fp, 0, ",")) !== false) { + $csv_columns = $this->normalizeCsvColumns($header_columns, $csv_columns); + // バリデーション $validator = Validator::make($csv_columns, $rules); @@ -695,7 +759,7 @@ public function upload($request, $page_id) } // データ項目のエラーチェック - $error_msgs = $this->checkPageline($fp); + $error_msgs = $this->checkPageline($fp, $header_columns); if (!empty($error_msgs)) { // 一時ファイルの削除 fclose($fp); @@ -714,6 +778,8 @@ public function upload($request, $page_id) try { // データ while (($csv_columns = fgetcsv($fp, 0, ",")) !== false) { + $csv_columns = $this->normalizeCsvColumns($header_columns, $csv_columns); + // --- 入力値変換 // 入力値をトリム(preg_replace(/u)で置換. /u = UTF-8 として処理) $csv_columns = StringUtils::trimInput($csv_columns); @@ -736,6 +802,7 @@ public function upload($request, $page_id) 'header_color' => $csv_columns[PageCvsIndex::header_color], 'theme' => $csv_columns[PageCvsIndex::theme], 'layout' => $csv_columns[PageCvsIndex::layout], + 'layout_inherit_flag' => $csv_columns[PageCvsIndex::layout_inherit_flag], 'base_display_flag' => $csv_columns[PageCvsIndex::base_display_flag] ]); diff --git a/app/Plugins/Manage/SiteManage/SiteManage.php b/app/Plugins/Manage/SiteManage/SiteManage.php index 22d7ef1a4..c9f1f9110 100644 --- a/app/Plugins/Manage/SiteManage/SiteManage.php +++ b/app/Plugins/Manage/SiteManage/SiteManage.php @@ -317,6 +317,13 @@ public function update($request, $page_id = null) 'value' => $request->base_theme] ); + // 基本レイアウト + $configs = Configs::updateOrCreate( + ['name' => 'base_layout'], + ['category' => 'general', + 'value' => $request->base_layout] + ); + // 追加テーマ $configs = Configs::updateOrCreate( ['name' => 'additional_theme'], diff --git a/config/connect.php b/config/connect.php index 7e551c0a3..e7bf1a847 100644 --- a/config/connect.php +++ b/config/connect.php @@ -64,6 +64,9 @@ // 設定メニューの折り畳みcol 'CC_SETTING_EXPAND_COL' => 6, + // レイアウトの初期値 + 'BASE_LAYOUT_DEFAULT' => '1|1|0|1', + // Cache-Control 'CACHE_CONTROL' => env('CACHE_CONTROL', 'max-age=604800'), diff --git a/database/migrations/2026_01_13_170722_add_base_layout_to_configs.php b/database/migrations/2026_01_13_170722_add_base_layout_to_configs.php new file mode 100644 index 000000000..2df7cc192 --- /dev/null +++ b/database/migrations/2026_01_13_170722_add_base_layout_to_configs.php @@ -0,0 +1,44 @@ +where('name', 'base_layout')->exists()) { + return; + } + + $layout_default = config('connect.BASE_LAYOUT_DEFAULT'); + $top_page_layout = DB::table('pages')->orderBy('_lft', 'asc')->value('layout'); + $layout = $top_page_layout ?: $layout_default; + + DB::table('configs')->insert([ + 'name' => 'base_layout', + 'value' => $layout, + 'category' => 'general', + 'additional1' => null, + 'additional2' => null, + 'additional3' => null, + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + DB::table('configs')->where('name', 'base_layout')->delete(); + } +} diff --git a/database/migrations/2026_01_13_173712_add_layout_inherit_flag_to_pages_table.php b/database/migrations/2026_01_13_173712_add_layout_inherit_flag_to_pages_table.php new file mode 100644 index 000000000..075daafcb --- /dev/null +++ b/database/migrations/2026_01_13_173712_add_layout_inherit_flag_to_pages_table.php @@ -0,0 +1,44 @@ +tinyInteger('layout_inherit_flag')->default(1)->after('layout'); + }); + + DB::table('pages') + ->whereNull('layout_inherit_flag') + ->update(['layout_inherit_flag' => 1]); + + $top_page_id = DB::table('pages')->orderBy('_lft', 'asc')->value('id'); + if ($top_page_id) { + DB::table('pages') + ->where('id', $top_page_id) + ->update(['layout_inherit_flag' => 0]); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('pages', function (Blueprint $table) { + $table->dropColumn('layout_inherit_flag'); + }); + } +} diff --git a/database/seeders/DefaultConfigsTableSeeder.php b/database/seeders/DefaultConfigsTableSeeder.php index 1403e944e..78d36829a 100644 --- a/database/seeders/DefaultConfigsTableSeeder.php +++ b/database/seeders/DefaultConfigsTableSeeder.php @@ -32,6 +32,7 @@ private function getDefaultConfigs() 'base_touch_callout' => ['value' => '0'], 'base_header_login_link' => ['value' => '1'], 'base_theme' => ['value' => null], + 'base_layout' => ['value' => config('connect.BASE_LAYOUT_DEFAULT')], 'smartphone_menu_template' => ['value' => SmartphoneMenuTemplateType::opencurrenttree], 'base_login_password_reset' => ['value' => 1], 'use_mypage' => ['value' => 1], diff --git a/database/seeders/DefaultPagesTableSeeder.php b/database/seeders/DefaultPagesTableSeeder.php index 9920c0770..dce15482f 100644 --- a/database/seeders/DefaultPagesTableSeeder.php +++ b/database/seeders/DefaultPagesTableSeeder.php @@ -22,6 +22,7 @@ public function run() 'page_name' => 'home', 'permanent_link' => '/', 'base_display_flag' => '1', + 'layout_inherit_flag' => 0, 'depth' => 0, // bugfix: tree構造で _lft, _rgtが両方0は間違った値で、上から2番目のページを上に移動するとNode must exists.エラーになるため修正 // '_lft'=>'0', diff --git a/database/seeders/ElementaryschoolSeeder.php b/database/seeders/ElementaryschoolSeeder.php index aca1ef6ee..3e7cf0daf 100644 --- a/database/seeders/ElementaryschoolSeeder.php +++ b/database/seeders/ElementaryschoolSeeder.php @@ -683,6 +683,7 @@ private function insertGetPageId($row) $header_color = (isset($row['header_color'])) ? $row['header_color'] : NULL; $theme = (isset($row['theme'])) ? $row['theme'] : NULL; $layout = (isset($row['layout'])) ? $row['layout'] : NULL; + $layout_inherit_flag = (isset($row['layout_inherit_flag'])) ? $row['layout_inherit_flag'] : NULL; $base_display_flag = (isset($row['base_display_flag'])) ? $row['base_display_flag'] : 1;// 1:表示する $membership_flag = (isset($row['membership_flag'])) ? $row['membership_flag'] : 0; $ip_address = (isset($row['ip_address'])) ? $row['ip_address'] : NULL; @@ -694,6 +695,9 @@ private function insertGetPageId($row) $_lft = (isset($row['_lft'])) ? $row['_lft'] : NULL; $_rgt = (isset($row['_rgt'])) ? $row['_rgt'] : NULL; $parent_id = (isset($row['parent_id'])) ? $row['parent_id'] : NULL; + if (is_null($layout_inherit_flag)) { + $layout_inherit_flag = ($permanent_link === '/') ? 0 : 1; + } return DB::table('pages')->insertGetId([ 'page_name' => $page_name, 'permanent_link' => $permanent_link, @@ -701,6 +705,7 @@ private function insertGetPageId($row) 'header_color' => $header_color, 'theme' => $theme, 'layout' => $layout, + 'layout_inherit_flag' => $layout_inherit_flag, 'base_display_flag' => $base_display_flag, 'membership_flag' => $membership_flag, 'ip_address' => $ip_address, diff --git a/database/seeders/PTASeeder.php b/database/seeders/PTASeeder.php index 02238615a..ea6a632bd 100644 --- a/database/seeders/PTASeeder.php +++ b/database/seeders/PTASeeder.php @@ -843,6 +843,7 @@ private function insertGetPageId($row) $header_color = (isset($row['header_color'])) ? $row['header_color'] : NULL; $theme = (isset($row['theme'])) ? $row['theme'] : NULL; $layout = (isset($row['layout'])) ? $row['layout'] : NULL; + $layout_inherit_flag = (isset($row['layout_inherit_flag'])) ? $row['layout_inherit_flag'] : NULL; $base_display_flag = (isset($row['base_display_flag'])) ? $row['base_display_flag'] : 1;// 1:表示する $membership_flag = (isset($row['membership_flag'])) ? $row['membership_flag'] : 0; $ip_address = (isset($row['ip_address'])) ? $row['ip_address'] : NULL; @@ -854,6 +855,9 @@ private function insertGetPageId($row) $_lft = (isset($row['_lft'])) ? $row['_lft'] : NULL; $_rgt = (isset($row['_rgt'])) ? $row['_rgt'] : NULL; $parent_id = (isset($row['parent_id'])) ? $row['parent_id'] : NULL; + if (is_null($layout_inherit_flag)) { + $layout_inherit_flag = ($permanent_link === '/') ? 0 : 1; + } return DB::table('pages')->insertGetId([ 'page_name' => $page_name, 'permanent_link' => $permanent_link, @@ -861,6 +865,7 @@ private function insertGetPageId($row) 'header_color' => $header_color, 'theme' => $theme, 'layout' => $layout, + 'layout_inherit_flag' => $layout_inherit_flag, 'base_display_flag' => $base_display_flag, 'membership_flag' => $membership_flag, 'ip_address' => $ip_address, diff --git a/resources/views/plugins/manage/page/page.blade.php b/resources/views/plugins/manage/page/page.blade.php index 4c07d84a5..2db1eb14f 100644 --- a/resources/views/plugins/manage/page/page.blade.php +++ b/resources/views/plugins/manage/page/page.blade.php @@ -8,6 +8,13 @@ @php use App\Enums\PageMetaRobots; use App\Models\Common\Page; +use App\Models\Core\Configs; + +$layout_default = config('connect.BASE_LAYOUT_DEFAULT'); +$base_layout = Configs::getSharedConfigsValue('base_layout', $layout_default); +$base_layout = $base_layout ?: $layout_default; +$base_layout_page = new Page(); +$base_layout_page->layout = $base_layout; @endphp {{-- 管理画面ベース画面 --}} @@ -374,7 +381,14 @@ function select_page(source_id, page_name) {
 . '.png')}})
+  . '.png')}})
 . '.png')}})