diff --git a/README.md b/README.md index b1cce59f0..8eb6379f9 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ cd gnuboard5 ## 문서 및 지원 +- [5.6.391 쇼핑몰 분류 표시 수정 안내](docs/release-5.6.391.md) - [5.6.39 보안 업데이트 및 필수 DB업그레이드 안내](docs/release-5.6.39.md) - [5.6.37 SIRK 전용 카카오페이 연동 종료 및 기존 거래 지원](docs/sirk-kakaopay-retirement.md) diff --git a/docs/release-5.6.391.md b/docs/release-5.6.391.md new file mode 100644 index 000000000..d9f4dee6c --- /dev/null +++ b/docs/release-5.6.391.md @@ -0,0 +1,26 @@ +# 그누보드(영카트) 5.6.391 배포 안내 + +배포일: 2026-09-15. 변경 기준은 5.6.39입니다. + +쇼핑몰 4·5단계 분류의 현재 경로와 서브메뉴 표시를 보완하는 버그 수정 버전입니다. + +## 변경 사항 + +- PC 기본 스킨과 basic 테마의 breadcrumb에 4·5단계 분류 경로를 표시합니다. 모든 단계에서 기존 위치와 스타일을 유지합니다. +- PC·모바일 기본 스킨과 basic 테마의 서브메뉴는 선택한 4단계에 사용 중인 5단계가 있으면 5단계 목록만 표시하고, 없으면 4단계 형제 목록만 표시합니다. +- 5단계에서는 같은 부모 아래의 5단계 형제 목록과 현재 선택을 표시합니다. +- 공통 전체 분류 트리의 기존 3단계 구조는 유지하고 현재 경로에 필요한 깊은 단계만 추가 조회합니다. + +## 업데이트 방법 + +1. 기존 소스와 사용자 수정 파일을 백업합니다. +2. 5.6.39 설치본에 전체 소스 또는 `gnuboard5.6.391.patch.zip`을 반영합니다. 공통 라이브러리와 스킨 파일을 함께 업데이트합니다. 사용자 수정 스킨은 변경 내용을 비교하여 병합합니다. +3. 4·5단계 분류의 breadcrumb, 하위 분류 유무에 따른 메뉴, 현재 선택 표시를 확인합니다. + +이번 버전의 추가 DB 마이그레이션과 삭제 대상 파일은 없습니다. 5.6.39보다 오래된 설치본은 [5.6.39 보안 업데이트 및 DB업그레이드 안내](release-5.6.39.md)를 포함한 선행 업데이트를 적용해야 합니다. + +## 검증 + +8개 분류 경로에서 PC·basic 테마의 목록/상세 breadcrumb와 네 스킨의 메뉴·선택 상태를 확인했습니다. Chromium에서 1·3·4·5단계 전환 시 breadcrumb 위치·높이와 메뉴·상품 정렬 영역 위치가 동일함을 확인했습니다. 변경된 운영 PHP 7개 파일은 현재 PHP와 PHP 5.2 구문 검사를 통과했습니다. + +분류 데이터 대역을 사용한 회귀 검사이며 실제 운영 DB와 모든 사용자 스킨을 검증한 결과는 아닙니다. diff --git a/lib/shop.data.lib.php b/lib/shop.data.lib.php index 4eef513b1..1cc638768 100644 --- a/lib/shop.data.lib.php +++ b/lib/shop.data.lib.php @@ -82,9 +82,49 @@ function get_shop_navigation_data($is_cache, $ca_id, $ca_id2='', $ca_id3=''){ } } + // 공통 전체 분류 트리는 기존 3단계 구조를 유지하고 현재 경로의 깊은 단계만 조회한다. + for ($depth = 4; $depth <= 5 && strlen($ca_id) >= $depth * 2; $depth++) { + $parent = substr($ca_id, 0, ($depth - 1) * 2); + $result = sql_query(get_shop_category_sql($parent, $depth * 2)); + while ($row = sql_fetch_array($result)) { + $row['url'] = shop_category_url($row['ca_id']); + $datas[$depth - 1][] = $row; + } + } + return $datas; } +// 4·5단계에서는 선택 경로의 5단계를 우선 표시하고, 없으면 4단계 형제를 표시한다. +function get_shop_category_menu_groups($ca_id) +{ + $length = strlen($ca_id); + $groups = array(); + if ($length >= 8) { + $queries = array( + array('5단계 분류', substr($ca_id, 0, 8), 10), + array('4단계 분류', substr($ca_id, 0, 6), 8), + ); + } else { + $queries = array(array('하위 분류', $ca_id, $length + 2)); + } + foreach ($queries as $query) { + $rows = array(); + $result = sql_query(get_shop_category_sql($query[1], $query[2])); + while ($row = sql_fetch_array($result)) $rows[] = $row; + if (!$rows && $length < 8) { + $query[0] = '현재 단계 분류'; + $result = sql_query(get_shop_category_sql(substr($ca_id, 0, -2), $length)); + while ($row = sql_fetch_array($result)) $rows[] = $row; + } + if ($rows) { + $groups[] = array('label' => $query[0], 'categories' => $rows); + break; + } + } + return $groups; +} + function get_shop_category_by($is_cache, $case, $value){ if( $case === 'ca_id' ){ @@ -268,4 +308,4 @@ function get_shop_item_options($it_id, $subject, $no) } return $str; -} \ No newline at end of file +} diff --git a/mobile/skin/shop/basic/listcategory.skin.php b/mobile/skin/shop/basic/listcategory.skin.php index ddd995594..f966ab733 100644 --- a/mobile/skin/shop/basic/listcategory.skin.php +++ b/mobile/skin/shop/basic/listcategory.skin.php @@ -1,44 +1,25 @@ '.$row['ca_name'].' '.$row2['cnt'].''; - $exists = true; -} - -if ($exists) { - - // add_stylesheet('css 구문', 출력순서); 숫자가 작을 수록 먼저 출력됨 +$category_groups = get_shop_category_menu_groups($ca_id); +if ($category_groups) { add_stylesheet('', 0); ?> - - - - -'.$row['ca_name'].' ('.$row2['cnt'].')'; - $exists = true; -} - -if ($exists) { - - // add_stylesheet('css 구문', 출력순서); 숫자가 작을 수록 먼저 출력됨 +$category_groups = get_shop_category_menu_groups($ca_id); +if ($category_groups) { add_stylesheet('', 0); ?> - - - - - substr($ca_id,0,2), - 'ca_id2' => substr($ca_id,0,4), - 'ca_id3' => substr($ca_id,0,6), - ); -} else if( $is_item_view && isset($it) && is_array($it) ) { - $navi_datas = get_shop_navigation_data(true, $it['ca_id']); - $ca_ids = array( - 'ca_id' => substr($it['ca_id'],0,2), - 'ca_id2' => substr($it['ca_id'],0,4), - 'ca_id3' => substr($it['ca_id'],0,6) - ); -} +$is_item_view = (isset($it_id, $it['it_id']) && $it_id === $it['it_id']); +$navigation_ca_id = $is_item_view ? $it['ca_id'] : $ca_id; +$navi_datas = $navigation_ca_id ? get_shop_navigation_data(true, $navigation_ca_id) : array(); $location_class = array(); if($is_item_view){ @@ -27,7 +12,7 @@ if($is_item_view){ $location_class[] = 'is_list is_right'; // view_location는 리스트 말고 상품보기에서만 표시 } -// add_stylesheet('css 구문', 출력순서); 숫자가 작을 수록 먼저 출력됨 +// add_stylesheet('css 구문', 출력순서); add_stylesheet('', 0); add_javascript('', 10); ?> @@ -37,26 +22,14 @@ add_javascript(' - - - - + $categories) { + $selected_ca_id = substr($navigation_ca_id, 0, ($depth + 1) * 2); + if ($depth > 0) { ?> - - - - + + diff --git a/tests/shop_category_layout.js b/tests/shop_category_layout.js new file mode 100644 index 000000000..541c09d1e --- /dev/null +++ b/tests/shop_category_layout.js @@ -0,0 +1,44 @@ +// 실행: NODE_PATH=/node_modules CHROME_PATH= node tests/shop_category_layout.js +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const {execFileSync} = require('child_process'); +const {chromium} = require('playwright'); +const root = path.dirname(__dirname); +const read = file => fs.readFileSync(path.join(root, file), 'utf8'); +const fixtures = JSON.parse(execFileSync('php', [path.join(__dirname, 'shop_category_navigation.php'), '--json'], {encoding:'utf8'})); +(async () => { + const browser = await chromium.launch({headless:true, ...(process.env.CHROME_PATH ? {executablePath:process.env.CHROME_PATH} : {}), args:['--no-sandbox']}); + try { + for (const skin of ['skin/shop/basic', 'theme/basic/skin/shop/basic']) { + for (const view of ['list', 'item']) { + const page = await browser.newPage({viewport:{width:1920, height:1080}}); + let baseline; + for (const id of ['10', '101010', '10101010', '1010101010']) { + let html = fixtures[skin][id].replace(/', 10); ?> @@ -37,26 +22,14 @@ add_javascript(' - - - - + $categories) { + $selected_ca_id = substr($navigation_ca_id, 0, ($depth + 1) * 2); + if ($depth > 0) { ?> - - - - + + diff --git a/version.php b/version.php index 95f6f19a9..c5d39cbd6 100644 --- a/version.php +++ b/version.php @@ -2,7 +2,7 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가 define('G5_VERSION', '그누보드5'); -define('G5_GNUBOARD_VER', '5.6.39'); +define('G5_GNUBOARD_VER', '5.6.391'); // 그누보드5.4.5.5 버전과 영카트5.4.5.5.1 버전을 합쳐서 그누보드5.4.6 버전에서 시작함 (kagla-210617) // G5_YOUNGCART_VER 이 상수를 사용하는 곳이 있으므로 주석 처리 해제함 // 그누보드5.4.6 이상 버전 부터는 영카트를 그누보드에 포함하여 배포하므로 영카트5의 버전은 의미가 없습니다.