Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
a88dada
多言語設定の修正で、HTMLヘッドエリアのTITLEタグの対応が漏れていたことへの修正です。
nagasheep Jan 4, 2026
bd776f1
トップページの表示でサイト名が表示されないバグを修正しました。
nagasheep Jan 4, 2026
bb0ba1e
perf(js): app.jsからTinyMCE・CodeMirror・lodashを分離してバンドルを分割
Feb 20, 2026
a390fc2
fix: static行はワンライナー構文ではパース出来ない為、ブロック構文に変更
Feb 24, 2026
ae915f4
bugfix: フォーム, 選択肢の末尾に全学空白があると、入力エラー時に選択したものが外れるバグ修正
akagane99 Feb 24, 2026
3f1a08c
chore: app.jsのコメントを実態に合わせて修正
Feb 24, 2026
288f286
refactor: フォーム, ファイルタイプ, 選択肢タイプ以外のif文をリファクタリング
akagane99 Feb 25, 2026
43ea0ad
refactor: フォーム, isChoicesColumnType()では同名のメソッドがUsersColumnsモデルであるため、i…
akagane99 Feb 25, 2026
9bb9638
refactor: フォーム, FormsColumnsモデルのメソッドに戻り値型宣言を追加
akagane99 Feb 25, 2026
df8566b
bugfix: データベース, 選択肢の末尾に全学空白があると、入力エラー時に選択したものが外れるバグ修正
akagane99 Feb 25, 2026
be857a0
chore(deps): bump immutable from 5.1.1 to 5.1.5
dependabot[bot] Mar 4, 2026
b283933
chore(deps-dev): bump svgo from 2.8.0 to 2.8.2
dependabot[bot] Mar 5, 2026
5dc1295
refactor: データベース, ステータス表示+ボタンの共通部品化、及びdefaultテンプレート対応
akagane99 Mar 5, 2026
810e1b2
add: データベース, tableテンプレートのステータスラベル表示対応
akagane99 Mar 5, 2026
e8cc38d
add: データベース, design-table-dlテンプレートのステータスラベル表示対応
akagane99 Mar 5, 2026
b8fdae7
add: データベース, card_02テンプレートのステータスラベル表示対応
akagane99 Mar 5, 2026
6e6c96e
[ユーザ管理] ログイン状態のユーザーを利用不可・仮削除に変えたら、強制ログアウト
akagane99 Mar 12, 2026
3afb0d1
[ログ管理] 絞り込み条件に「値など」「IPアドレス」の追加
akagane99 Mar 12, 2026
3608418
add: サイト内検索, 検索結果の固定記事にフレームのアンカーを追加
akagane99 Mar 18, 2026
7d08128
add: サイト内検索, 検索結果0件のときにメッセージを表示する
akagane99 Mar 18, 2026
f81fcde
chore(deps): bump league/commonmark from 2.7.0 to 2.8.2
dependabot[bot] Mar 19, 2026
c200244
Merge pull request #2381 from opensource-workshop/dependabot/npm_and_…
masaton0216 Mar 24, 2026
3015219
Merge pull request #2380 from opensource-workshop/dependabot/npm_and_…
masaton0216 Mar 24, 2026
2e3adca
Merge pull request #2373 from akagane99/issues/2369
masaton0216 Mar 24, 2026
1ea3292
chore(deps): bump serialize-javascript and terser-webpack-plugin
dependabot[bot] Mar 24, 2026
b70d134
Update 2023_09_04_153112_add_full_text_search_to_databases.php
dreamer9999 Mar 24, 2026
434a2e9
Merge pull request #2371 from opensource-workshop/perf/optimize-app-js
masaton0216 Mar 25, 2026
a0062b6
Merge pull request #2385 from akagane99/connect-cms-ideas/issues/184
masaton0216 Mar 25, 2026
e3122c8
Merge pull request #2333 from opensource-workshop/Multi-Language_rel=…
masaton0216 Mar 25, 2026
b7e8713
Update 2023_09_04_153112_add_full_text_search_to_databases.php
dreamer9999 Mar 25, 2026
c911a36
Create 2026_03_25_000000_fix_mariadb_fulltext_index_on_databases_inpu…
dreamer9999 Mar 25, 2026
13120e9
chore(deps): bump picomatch from 2.3.1 to 2.3.2
dependabot[bot] Mar 25, 2026
e397eaf
Merge pull request #2392 from opensource-workshop/dependabot/npm_and_…
masaton0216 Mar 25, 2026
58f76ad
change: データベーステンプレート, 不要な行末空白を削除
akagane99 Mar 26, 2026
a2ae08d
fix: データベースプラグイン、施設予約プラグイン ウィジウィグ型の項目が2つで値がPOSTされない不具合を修正
Mar 28, 2026
0b7fd04
fix: 施設予約 WYSIWYG項目が複数ある場合に値がPOSTされない不具合を修正。submit_booking_store関数でtr…
Mar 28, 2026
d9d89f3
fix: 施設予約 WYSIWYG項目が複数ある場合に値がPOSTされない不具合を修正。triggerSave() は submit_bo…
Mar 28, 2026
d0ffa36
chore: コメント修正
Mar 28, 2026
ff9f951
Merge pull request #2393 from opensource-workshop/fix-pr-2371-perf-appjs
masaton0216 Mar 28, 2026
f1eabcc
chore(deps-dev): bump node-forge from 1.3.3 to 1.4.0
dependabot[bot] Mar 28, 2026
ed46b4f
fix: キャスト演算子の後にスペースを追加(phpcs対応)
dreamer9999 Mar 29, 2026
db64b91
Merge pull request #2391 from dreamer9999/master
masaton0216 Mar 29, 2026
62fc789
Merge pull request #2394 from opensource-workshop/dependabot/npm_and_…
masaton0216 Mar 30, 2026
e301547
Merge pull request #2390 from opensource-workshop/dependabot/npm_and_…
masaton0216 Mar 30, 2026
36d58b2
Merge pull request #2388 from opensource-workshop/dependabot/composer…
masaton0216 Mar 30, 2026
bbfab0f
chore(deps): npm audit fix によるパッケージ更新とフロントエンドの再ビルド
Mar 30, 2026
5227b54
fix(core): validate inherited common-area frames in ConnectPage
gakigaki Mar 30, 2026
114da80
fix(core): tighten inherited common area frame validation
gakigaki Mar 31, 2026
b63f818
docs(core): clarify common area frame validation comments
gakigaki Apr 2, 2026
81cb91d
fix: カテゴリ管理 表示順に数値バリデーションを追加
Apr 3, 2026
fdf357d
Merge pull request #2396 from opensource-workshop/fix/connect-page-in…
gakigaki Apr 3, 2026
fdb8198
fix(core): restore upload page context fallback
gakigaki Apr 3, 2026
76612d2
Merge pull request #2399 from opensource-workshop/fix/connect-page-up…
gakigaki Apr 3, 2026
4e67392
change: データベース:テンプレート, ステータスラベルはaタグ内から外す
akagane99 Apr 5, 2026
876a7c9
change: [ユーザ管理] ログイン状態のユーザーを利用不可・仮削除に変えたら、強制ログアウト処理の見直し
akagane99 Apr 5, 2026
5e4bf92
Revert "add: サイト内検索, 検索結果0件のときにメッセージを表示する"
akagane99 Apr 5, 2026
6cec92b
add: サイト内検索, 検索結果0件のときにメッセージを表示する(レビュー改善版)
akagane99 Apr 5, 2026
1c8ae37
Merge pull request #2398 from opensource-workshop/fix-category-add-va…
masaton0216 Apr 6, 2026
7f04ffd
chore: フォーム/スパム管理 テキスト修正(スパムリスト -> ブロックリスト)
Apr 6, 2026
33d9b87
Merge pull request #2400 from opensource-workshop/fix-text-spamlist
masaton0216 Apr 6, 2026
8576c38
fix: ユーザ管理 条件付き表示の演算子処理をswitch文に変更(php7系対応)
Apr 6, 2026
1b12004
Merge pull request #2401 from opensource-workshop/fix-user-manage-mat…
masaton0216 Apr 6, 2026
e745299
Merge pull request #2387 from akagane99/connect-cms-ideas/issues/188
masaton0216 Apr 6, 2026
c69e9f8
Merge pull request #2384 from akagane99/connect-cms-ideas/issues/185
masaton0216 Apr 6, 2026
0d959aa
Merge pull request #2382 from akagane99/connect-cms-ideas/issues/186
masaton0216 Apr 6, 2026
cc02a96
chore: package-lock.json 意図していない修正を元に戻す(name:html -> name:connect-cms)
Apr 6, 2026
8b93412
Merge pull request #2395 from opensource-workshop/npm-audit-fix-20260330
masaton0216 Apr 6, 2026
963fec8
chore: Bump version to v1.42.0
github-actions[bot] Apr 8, 2026
51c029d
Merge branch '2' into 2.42.0
gakigaki Apr 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
337 changes: 323 additions & 14 deletions app/Http/Middleware/ConnectPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Closure;

use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\View;
Expand All @@ -15,6 +16,7 @@
use App\Models\Common\PageRole;
use App\Models\Common\Permalink;
use App\Models\Migration\MigrationMapping;
use App\Enums\AreaType;

class ConnectPage
{
Expand Down Expand Up @@ -57,14 +59,7 @@ public function handle($request, Closure $next)
}

// ページの特定
$route_page_id = $request->route('page_id');
if (!empty($route_page_id)) {
// ページID が渡ってきた場合
$this->page = Page::where('id', $route_page_id)->first();
} else {
// ページID が渡されなかった場合、URL から取得
$this->page = $this->getCurrentPage();
}
$this->page = $this->resolveRequestPage($request);

// 下層ページへ自動転送
if ($this->page && $this->page->transfer_lower_page_flag) {
Expand Down Expand Up @@ -205,6 +200,66 @@ private function getCurrentPage()
return $page;
}

/**
* リクエストから現在ページを特定する。
*/
private function resolveRequestPage($request)
{
$route_page_id = $request->route('page_id');
if ($this->isValidPageId($route_page_id)) {
return Page::where('id', (int)$route_page_id)->first();
}

$upload_page_id = $this->getUploadFallbackPageId($request);
if (!is_null($upload_page_id)) {
return Page::where('id', $upload_page_id)->first();
}

return $this->getCurrentPage();
}

/**
* /upload/{method?} 限定で body の page_id を補完する。
*/
private function getUploadFallbackPageId($request): ?int
{
if (!$this->isUploadPostRoute($request)) {
return null;
}

$page_id = $request->input('page_id');
if (!$this->isValidPageId($page_id)) {
return null;
}

return (int)$page_id;
}

/**
* body の page_id fallback を許可する upload POST か判定する。
*/
private function isUploadPostRoute($request): bool
{
if (!$request->isMethod('post')) {
return false;
}

$route = $request->route();
return !is_null($route) && $route->getName() === 'post_upload';
}

/**
* page_id として扱える正の整数か判定する。
*/
private function isValidPageId($page_id): bool
{
if (filter_var($page_id, FILTER_VALIDATE_INT) === false) {
return false;
}

return (int)$page_id > 0;
}

/**
* 404 判定
* (ConnectController から移動してきた)
Expand Down Expand Up @@ -392,7 +447,7 @@ private function checkPageForbidden($request, $page_tree, $router)
}

// page_id と frame_id の組み合わせが不整合なら、不正アクセスとして 403 扱いにする。
if (!$this->isValidPageAndFrame($request)) {
if (!$this->isValidPageAndFrame($request, $page_tree)) {
return $this->doForbidden();
}

Expand Down Expand Up @@ -430,28 +485,282 @@ private function isPageLimitCheckRoute($route_name)
/**
* page_id と frame_id の整合性判定
*/
private function isValidPageAndFrame($request)
private function isValidPageAndFrame($request, $page_tree)
{
$route_page_id = $request->route('page_id');
$route_frame_id = $request->route('frame_id');

// frame_id がなければ判定不要
// frame_id を伴わないルートは、ページとフレームの組み合わせ判定自体が不要。
if (empty($route_frame_id)) {
return true;
}

// frame_id があるのに page_id がない場合は不正
// frame_id があるのに page_id がない組み合わせは、対象ページを特定できないので不正扱い。
if (empty($route_page_id)) {
return false;
}

// frameはhandle()で事前に取得済み
// frame は handle() で先に読み込んでいる前提。取得できない frame_id は不正扱い。
$frame = $request->attributes->get('frame');
if (empty($frame)) {
return false;
}

return ((int)$frame->page_id === (int)$route_page_id);
// メインエリアは継承しないため、配置ページと現在ページが完全一致する場合だけ許可する。
if ((int)$frame->area_id === AreaType::main) {
return ((int)$frame->page_id === (int)$route_page_id);
}

// 共通エリアでも、配置ページ本人からの操作は従来通り許可する。
if ((int)$frame->page_id === (int)$route_page_id) {
return true;
}

// 共通エリアは「現在ページの祖先ツリー上で実際に採用されるフレームか」を判定する。
// そのため、現在ページと祖先ツリーが解決できない場合は許可できない。
if (empty($page_tree) || empty($this->page) || empty($this->page->id)) {
return false;
}
// この画面で実際に適用される共通エリアフレーム群を取得する。
$effective_frames = $this->getEffectiveCommonAreaFrames($page_tree, (int)$frame->area_id);
// 要求された frame_id が、この画面で実際に適用されるフレーム群に含まれている場合だけ許可する。
return $effective_frames->contains(function ($effective_frame) use ($frame) {
return (int)($effective_frame->frame_id ?? $effective_frame->id) === (int)$frame->id;
});
}

/**
* 現ページで実際に採用される共通エリアフレームを取得する。
*/
private function getEffectiveCommonAreaFrames($page_tree, int $area_id): Collection
{
if (empty($this->page) || empty($this->page->id)) {
return collect();
}

$normalized_page_tree = $this->normalizePageTreeForCommonArea($page_tree);
if ($normalized_page_tree->isEmpty()) {
return collect();
}

if (!$this->isCommonAreaVisibleOnCurrentPage($normalized_page_tree, $area_id)) {
return collect();
}

$page_ids = $normalized_page_tree->pluck('id')->filter()->all();
if (empty($page_ids)) {
return collect();
}

$effective_page_id = null;
$effective_frames = collect();
foreach ($this->queryCommonAreaFrames($page_ids, $area_id) as $frame) {
if (is_null($effective_page_id)) {
$effective_page_id = (int)$frame->page_id;
}

if ((int)$frame->page_id !== $effective_page_id) {
break;
}

$effective_frames->push($frame);
}

return $effective_frames;
}

/**
* 共通エリア判定用にページツリーを正規化する。
*/
private function normalizePageTreeForCommonArea($page_tree): Collection
{
if (empty($page_tree)) {
return collect();
}

// 元の page_tree は壊さず、共通エリア判定用の並びを別コレクションで組み直す。
$normalized_page_tree = collect($page_tree->all());
$top_page = Page::getTopPage();
$language_top_page = $this->getLanguageTopPage();

// root 系ページは末尾へ正しい順序で付け直すため、いったん除外対象として集める。
$excluded_page_ids = collect([
$top_page->id ?? null,
$language_top_page->id ?? null,
])->filter()->map(function ($page_id) {
return (int)$page_id;
})->all();

// 通常の祖先チェーンだけを残し、root 系ページはこの後に付け直す。
$normalized_page_tree = $normalized_page_tree->filter(function ($tree_page) use ($excluded_page_ids) {
return !empty($tree_page)
&& !empty($tree_page->id)
&& !in_array((int)$tree_page->id, $excluded_page_ids, true);
})->values();

// 表示側の解決順に合わせて、言語トップ -> 全体トップの順で末尾に戻す。
if (!empty($language_top_page) && !empty($language_top_page->id)) {
$normalized_page_tree->push($language_top_page);
}

if (!empty($top_page) && !empty($top_page->id)) {
if (empty($language_top_page) || (int)$language_top_page->id !== (int)$top_page->id) {
$normalized_page_tree->push($top_page);
}
}

return $normalized_page_tree;
}

/**
* 現在ページで共通エリアが描画対象か判定する。
*/
private function isCommonAreaVisibleOnCurrentPage(Collection $page_tree, int $area_id): bool
{
$layout_array = $this->getLayoutArrayForCommonArea($page_tree);

if ($area_id === AreaType::header) {
return $layout_array[0] == '1';
}
if ($area_id === AreaType::left) {
return $layout_array[1] == '1';
}
if ($area_id === AreaType::right) {
return $layout_array[2] == '1';
}
if ($area_id === AreaType::footer) {
return $layout_array[3] == '1';
}

return false;
}

/**
* 共通エリア判定用のレイアウト配列を取得する。
*/
private function getLayoutArrayForCommonArea(Collection $page_tree): array
{
$layout_array = explode('|', $this->getLayoutForCommonArea($page_tree));
if (count($layout_array) !== 4) {
return [1, 1, 1, 1];
}

return $layout_array;
}

/**
* 共通エリア判定用のレイアウトを取得する。
*/
private function getLayoutForCommonArea(Collection $page_tree): string
{
$layout_default = config('connect.BASE_LAYOUT_DEFAULT');
if (empty($this->page)) {
return $layout_default;
}

$layout = null;
foreach ($page_tree as $tree_page) {
if (empty($tree_page) || empty($tree_page->layout)) {
continue;
}

if ($tree_page->id != $this->page->id
&& !is_null($tree_page->layout_inherit_flag)
&& (int)$tree_page->layout_inherit_flag === 0) {
continue;
}

$layout = $tree_page->layout;
break;
}

if (empty($layout)) {
$layout = Configs::getSharedConfigsValue('base_layout', $layout_default);
}
if (empty($layout)) {
$layout = $layout_default;
}

return $layout;
}

/**
* 共通エリアの継承候補フレームを取得する。
*/
private function queryCommonAreaFrames(array $page_ids, int $area_id): Collection
{
return Frame::whereIn('frames.page_id', $page_ids)
->where('frames.area_id', $area_id)
->select('frames.*', 'frames.id as frame_id', 'pages.id as page_id')
->join('pages', 'pages.id', '=', 'frames.page_id')
->where(function ($query) {
$query->where('page_only', 0)
->orWhere(function ($query2) {
$query2->where('page_only', 1)
->where('page_id', $this->page->id);
})
->orWhere('page_only', 2);
})
->orderBy('pages._lft', 'desc')
->orderBy('frames.display_sequence', 'asc')
->get();
}

/**
* 多言語トップページを取得する。
*/
private function getLanguageTopPage(): ?Page
{
if (empty($this->page) || empty($this->page->permanent_link) || !$this->isLanguageMultiOn()) {
return null;
}

$languages = Configs::getLanguages();
if (empty($languages)) {
return null;
}

$page_language = $this->getPageLanguageFromPage($languages);
if (empty($page_language)) {
return null;
}

return Page::where('permanent_link', '/' . $page_language)->first();
}

/**
* ページオブジェクトから言語を取得する。
*/
private function getPageLanguageFromPage($languages)
{
$page_language = null;
$page_paths = explode('/', $this->page->permanent_link);
if ($page_paths && is_array($page_paths) && array_key_exists(1, $page_paths)) {
foreach ($languages as $language) {
if (trim($language->additional1, '/') == $page_paths[1]) {
$page_language = $page_paths[1];
break;
}
}
}

return $page_language;
}

/**
* 多言語設定が有効か判定する。
*/
private function isLanguageMultiOn(): bool
{
foreach (Configs::getSharedConfigs() as $config) {
if ($config->name !== 'language_multi_on') {
continue;
}

return $config->value == '1';
}

return false;
}

/**
Expand Down
Loading
Loading