일반 화면(React)과 봇 화면(PHP)은 같은 레이아웃을 각각 렌더하는데 봇 화면 쪽 지원 범위가 뒤처져, 확장이 교체한 본문이 통째로 빈 채 나갔다 (공개 이슈 ). 같은 결손 계열을 전수 조사해 함께 닫는다. - 확장 주입 props 를 렌더 진입 시점에 해석해 자손 전체에 상속 → 게시글·답글 원글·댓글·페이지·상품 설명 5개 지점 복구 - 사용자 작성 HTML 정화기 신설 — isHtml 판정에 따라 이스케이프/정화. 미정화 상태에서 봇 경로로 저장형 스크립트가 실제 실행됐다 - 노드 키 패리티: conditions / type:iterator / responsive 매칭 규칙 / children 문자열 / text 우선순위 / $t:defer / 레이아웃 초기 상태 - 표현식: 단일 바인딩 판정 교체, ||·&& 를 값 반환(JS 시맨틱)으로, 비교식이 raw 경로에서 null 이 되던 문제 - 데이터소스: 엔드포인트 표현식 전면 해석 + 레이아웃 8곳 선언 보강 - 봇 페이지 캐시 1회 전량 무효화 업그레이드 스텝 지원 범위 SSoT 표를 문서에 고정하고, 한쪽만 고치는 것을 막는 검사 룰과 레이아웃 전수 순회 테스트로 잠갔다.
109 lines
3.5 KiB
JSON
109 lines
3.5 KiB
JSON
{
|
|
"version": "1.0.0",
|
|
"layout_name": "board/show",
|
|
"permissions": [],
|
|
"extends": "_user_base",
|
|
"meta": {
|
|
"seo": {
|
|
"enabled": true,
|
|
"extensions": [
|
|
{ "type": "module", "id": "sirsoft-board" }
|
|
],
|
|
"data_sources": ["post", "navigation"],
|
|
"page_type": "post",
|
|
"toggle_setting": "$module_settings:sirsoft-board:seo.seo_post_detail",
|
|
"vars": {
|
|
"site_name": "$core_settings:general.site_name",
|
|
"board_name": "{{post.data.board.name ?? ''}}",
|
|
"post_title": "{{post.data.title ?? ''}}"
|
|
},
|
|
"priority": 0.5,
|
|
"changefreq": "monthly",
|
|
"og": {
|
|
"type": "article",
|
|
"title": "{{_seo.post.title ?? post.data.title ?? ''}}",
|
|
"description": "{{_seo.post.description ?? post.data.content ?? ''}}"
|
|
}
|
|
}
|
|
},
|
|
"data_sources": [
|
|
{
|
|
"id": "post",
|
|
"label_key": "$t:editor.data_source.post",
|
|
"type": "api",
|
|
"endpoint": "/api/modules/sirsoft-board/boards/{{route.slug}}/posts/{{route.id}}",
|
|
"method": "GET",
|
|
"params": {
|
|
"del_cmt": "{{query.del_cmt ?? ''}}"
|
|
},
|
|
"auto_fetch": true,
|
|
"auth_mode": "optional",
|
|
"refetchOnMount": true,
|
|
"errorHandling": {
|
|
"401": {
|
|
"handler": "sequence",
|
|
"actions": [
|
|
{ "handler": "toast", "params": { "type": "info", "message": "$t:board.login_required" } },
|
|
{ "handler": "redirectToLoginWithReturn" }
|
|
]
|
|
},
|
|
"403": {
|
|
"handler": "showErrorPage",
|
|
"params": {
|
|
"target": "content"
|
|
}
|
|
},
|
|
"404": {
|
|
"handler": "showErrorPage",
|
|
"params": {
|
|
"target": "content"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"id": "navigation",
|
|
"label_key": "$t:editor.data_source.navigation",
|
|
"type": "api",
|
|
"endpoint": "/api/modules/sirsoft-board/boards/{{route.slug}}/posts/{{route.id}}/navigation",
|
|
"method": "GET",
|
|
"params": {
|
|
"del": "{{query.del ?? ''}}"
|
|
},
|
|
"auto_fetch": true,
|
|
"auth_mode": "optional",
|
|
"refetchOnMount": true
|
|
}
|
|
],
|
|
"computed": {
|
|
"commentRootMap": "{{(post?.data?.comments ?? []).reduce((map, c) => ({...map, [c.id]: c.depth === 0 ? c.id : (map[c.parent_id] ?? c.parent_id)}), {})}}"
|
|
},
|
|
"slots": {
|
|
"content": [
|
|
{
|
|
"type": "layout",
|
|
"name": "Container",
|
|
"props": {
|
|
"className": "py-8 max-w-4xl mx-auto"
|
|
},
|
|
"children": [
|
|
{
|
|
"partial": "partials/board/show/_type_renderer.json"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
"modals": [
|
|
{
|
|
"partial": "partials/board/show/modals/_modal_delete.json"
|
|
},
|
|
{
|
|
"partial": "partials/board/show/modals/_modal_report.json"
|
|
},
|
|
{
|
|
"partial": "partials/board/show/modals/_password_verify_modal.json"
|
|
}
|
|
]
|
|
}
|