@version(2) {{-- 테마 내장 메인페이지 — 대문(메인 형태)에서 상품·게시글·카테고리 섹션을 렌더링한다. --}} {{-- 레이아웃 설정의 "메인 구성" 반복 필드로 조립하고, 비워두면 기본 구성을 쓴다. --}} @php // 커머스 스킨 공용 헬퍼(가격 표기)는 커머스 컨트롤러가 로드한다. // 대문은 다른 모듈이므로 여기서 직접 불러 둔다. 커머스 미설치면 그냥 넘어간다. if (!function_exists('shop_item_price')) { $hr_helper_file = \RX_BASEDIR . 'modules/commerce/helpers.php'; if (is_file($hr_helper_file)) { require_once $hr_helper_file; } } if (!function_exists('hr_home_price')) { // 상품 가격 한 줄 표기: 헬퍼가 있으면 통화 규칙을 따르고, 없으면 숫자만 찍는다 function hr_home_price($hr_amount) { if (function_exists('shop_money')) { return shop_money((int)$hr_amount); } return number_format((int)$hr_amount); } } if (!function_exists('hrh_flat_text')) { // 배너 문구를 한 줄 텍스트로: 줄바꿈 태그를 공백으로 풀고 태그를 걷어낸다 // (@php 문자열에 꺾쇠를 직접 쓰면 템플릿 컴파일이 깨지므로 chr 로 만든다) function hrh_flat_text($hrh_t) { $hrh_lt = chr(60); $hrh_gt = chr(62); $hrh_t = preg_replace('/' . $hrh_lt . 'br[^' . $hrh_gt . ']*' . $hrh_gt . '/i', ' ', (string)$hrh_t); return trim(strip_tags($hrh_t)); } } if (!function_exists('ss_cat_icon_keys')) { // 카테고리 아이콘 매칭 키: 분류 번호, 저장값, 현재 언어 이름, 다국어 연결 시 모든 언어의 이름 function ss_cat_icon_keys($ss_cat) { static $ss_lang_cache = []; $ss_keys = [(string)(int)($ss_cat->category_srl ?? 0)]; $ss_raw = (string)($ss_cat->title_raw ?? $ss_cat->title ?? ''); $ss_keys[] = trim($ss_raw); $ss_keys[] = trim(\Context::replaceUserLang($ss_raw)); if (preg_match('/\\$' . 'user_lang-' . chr(62) . '([A-Za-z0-9_]+)/', $ss_raw, $ss_m)) { if (!isset($ss_lang_cache[$ss_m[1]])) { $ss_lang_cache[$ss_m[1]] = []; $ss_lout = executeQueryArray('module.getLangListByName', (object)['name' => $ss_m[1]]); foreach (($ss_lout->toBool() ? ($ss_lout->data ?: []) : []) as $ss_lrow) { $ss_lang_cache[$ss_m[1]][] = trim((string)($ss_lrow->value ?? '')); } } $ss_keys = array_merge($ss_keys, $ss_lang_cache[$ss_m[1]]); } return array_values(array_unique(array_filter($ss_keys, function($k) { return $k !== '' && $k !== '0'; }))); } } if (!function_exists('ss_cat_icon_find')) { function ss_cat_icon_find(array $ss_map, $ss_cat) { foreach (ss_cat_icon_keys($ss_cat) as $ss_k) { if (isset($ss_map[$ss_k])) { return $ss_map[$ss_k]; } } return null; } } if (!function_exists('hr_home_ts_price')) { // 지금 열린 타임세일가. 없으면 0 (타임세일 중에는 커머스 규칙대로 등급 할인이 겹치지 않는다) function hr_home_ts_price($hrh_it) { if (!isset($hrh_it->ts_price)) { $hrh_it->ts_price = 0; if (class_exists('\\Zittme\\Modules\\Commerce\\Models\\Timesale') && method_exists('\\Zittme\\Modules\\Commerce\\Models\\Timesale', 'forItem')) { $hrh_tsr = \Zittme\Modules\Commerce\Models\Timesale::forItem($hrh_it); if ($hrh_tsr) { $hrh_it->ts_price = (int)$hrh_tsr->price; } } } return (int)$hrh_it->ts_price; } } if (!function_exists('hr_home_grade_price')) { // 로그인 회원의 등급 할인가 (기준 통화). 할인이 값을 낮추지 않으면 0 function hr_home_grade_price($hrh_it) { static $hrh_gd = false; if (hr_home_ts_price($hrh_it) > 0) { return 0; } if (!class_exists('\\Zittme\\Modules\\Commerce\\Models\\Grade') || ($hrh_it->grade_discount ?? 'Y') === 'N') { return 0; } if ($hrh_gd === false) { $hrh_logged = \Context::get('logged_info'); $hrh_gd = (int)($hrh_logged->member_srl ?? 0) > 0 ? \Zittme\Modules\Commerce\Models\Grade::discountFor((int)$hrh_logged->member_srl) : null; } if (!$hrh_gd) { return 0; } $hrh_sale = (int)($hrh_it->sale_price ?? 0); $hrh_base = $hrh_sale > 0 && $hrh_sale < (int)($hrh_it->price ?? 0) ? $hrh_sale : (int)($hrh_it->price ?? 0); $hrh_graded = \Zittme\Modules\Commerce\Models\Grade::applyDiscount($hrh_base, $hrh_gd); return $hrh_graded < $hrh_base ? $hrh_graded : 0; } } if (!function_exists('hr_home_cmp_off')) { // 할인율 큰 순서 function hr_home_cmp_off($hrh_a, $hrh_b) { $hrh_ap = (int)($hrh_a->price ?? 0); $hrh_bp = (int)($hrh_b->price ?? 0); $hrh_ao = $hrh_ap > 0 ? ($hrh_ap - (int)($hrh_a->sale_price ?? 0)) / $hrh_ap : 0; $hrh_bo = $hrh_bp > 0 ? ($hrh_bp - (int)($hrh_b->sale_price ?? 0)) / $hrh_bp : 0; if ($hrh_ao === $hrh_bo) { return 0; } return $hrh_ao < $hrh_bo ? 1 : -1; } } if (!function_exists('hr_home_cmp_rank')) { // 랭킹: 구매수 → 조회수 → 최신 순 function hr_home_cmp_rank($hrh_a, $hrh_b) { $hrh_av = (int)($hrh_a->buy_count ?? 0); $hrh_bv = (int)($hrh_b->buy_count ?? 0); if ($hrh_av !== $hrh_bv) { return $hrh_av < $hrh_bv ? 1 : -1; } $hrh_av = (int)($hrh_a->view_count ?? 0); $hrh_bv = (int)($hrh_b->view_count ?? 0); if ($hrh_av !== $hrh_bv) { return $hrh_av < $hrh_bv ? 1 : -1; } return (int)$hrh_a->item_srl < (int)$hrh_b->item_srl ? 1 : -1; } } if (!function_exists('hr_home_cmp_buy')) { function hr_home_cmp_buy($hr_a, $hr_b) { $hr_av = (int)($hr_a->buy_count ?? 0); $hr_bv = (int)($hr_b->buy_count ?? 0); if ($hr_av === $hr_bv) { return 0; } return $hr_av < $hr_bv ? 1 : -1; } } // 상점 mid — 헤더와 같은 규칙 (설정값 우선, 없으면 첫 커머스 모듈) $hrh_shop_mid = trim((string)($hr->shop_mid ?? '')); if ($hrh_shop_mid === '') { foreach (ModuleModel::getMidList((object)['module' => 'commerce'], ['mid']) ?: [] as $hrh_row) { $hrh_shop_mid = (string)($hrh_row->mid ?? ''); break; } } // 섹션 구성 — 설정이 비어 있으면 기본 구성 $hrh_sections = []; $hrh_section_rows = ss_repeat_items($hr, 'home_sections', ['stype', 'title', 'source', 'count']); if (!count($hrh_section_rows) && trim((string)($hr->home_sections_text ?? '')) !== '') { foreach (preg_split('/\r\n|\r|\n/', (string)$hr->home_sections_text) as $hrh_line) { $hrh_cols = array_map('trim', explode('|', $hrh_line)); if ($hrh_cols[0] === '') { continue; } $hrh_section_rows[] = ['stype' => $hrh_cols[0], 'title' => $hrh_cols[1] ?? '', 'source' => $hrh_cols[2] ?? '', 'count' => $hrh_cols[3] ?? 8]; } } foreach ($hrh_section_rows as $hrh_srow) { $hrh_srow = (array)$hrh_srow; if (empty($hrh_srow['stype'])) { continue; } $hrh_sections[] = [ 'stype' => (string)$hrh_srow['stype'], 'title' => trim((string)($hrh_srow['title'] ?? '')), 'source' => trim((string)($hrh_srow['source'] ?? '')), 'count' => max(2, min(12, (int)($hrh_srow['count'] ?? 8))), ]; } if (!count($hrh_sections)) { $hrh_sections = [ ['stype' => 'categories', 'title' => '', 'source' => '', 'count' => 10], ['stype' => 'daily', 'title' => '', 'source' => '', 'count' => 1], ['stype' => 'promos', 'title' => '', 'source' => '', 'count' => 4], ['stype' => 'items_recommend', 'title' => '', 'source' => '', 'count' => 8], ['stype' => 'banner', 'title' => '', 'source' => '2', 'count' => 1], ['stype' => 'ranking', 'title' => '', 'source' => '', 'count' => 6], ['stype' => 'items_new', 'title' => '', 'source' => '', 'count' => 8], ['stype' => 'banner', 'title' => '', 'source' => '3', 'count' => 1], ['stype' => 'items_sale', 'title' => '', 'source' => '', 'count' => 8], ['stype' => 'posts', 'title' => '', 'source' => 'notice,board', 'count' => 6], ]; } // 중간 배너 스트립 재료 — 레이아웃 비주얼(커머스 배너 승계 포함)을 그대로 쓴다 $hrh_banners = []; foreach ((is_array($hr_visuals ?? null) ? $hr_visuals : []) as $hrh_bv) { $hrh_bv = (array)$hrh_bv; if (!empty($hrh_bv['image'])) { $hrh_banners[] = $hrh_bv; } } // 상품 풀 — 상품 섹션이 하나라도 있으면 한 번만 조회 $hrh_pool = []; $hrh_need_items = false; foreach ($hrh_sections as $hrh_sec) { if (strpos($hrh_sec['stype'], 'items_') === 0 || $hrh_sec['stype'] === 'daily' || $hrh_sec['stype'] === 'ranking') { $hrh_need_items = true; } } if ($hrh_need_items && $hrh_shop_mid !== '') { $hrh_out = executeQuery('commerce.getItemList', (object)[ 'status_list' => 'sale,soldout', 'sort_index' => 'item_srl', 'order_type' => 'desc', 'list_count' => 120, ]); if ($hrh_out->toBool() && !empty($hrh_out->data)) { $hrh_rows = is_array($hrh_out->data) ? $hrh_out->data : [$hrh_out->data]; $hrh_now = date('YmdHis'); foreach ($hrh_rows as $hrh_it) { if (empty($hrh_it->item_srl)) { continue; } if (!empty($hrh_it->sale_start) && $hrh_now < $hrh_it->sale_start) { continue; } if (!empty($hrh_it->sale_end) && $hrh_now > $hrh_it->sale_end) { continue; } $hrh_it->item_name = \Context::replaceUserLang((string)$hrh_it->item_name); $hrh_pool[] = $hrh_it; } } } // 카테고리 — 카테고리 섹션이 있을 때만 $hrh_cats = []; foreach ($hrh_sections as $hrh_sec) { if ($hrh_sec['stype'] !== 'categories') { continue; } $hrh_cout = executeQuery('commerce.getCategoryList', (object)['is_active' => 'Y']); if ($hrh_cout->toBool() && !empty($hrh_cout->data)) { foreach ((is_array($hrh_cout->data) ? $hrh_cout->data : [$hrh_cout->data]) as $hrh_cat) { if (empty($hrh_cat->category_srl) || (int)($hrh_cat->parent_srl ?? 0) !== 0) { continue; } $hrh_cat->title_raw = (string)$hrh_cat->title; $hrh_cat->title = \Context::replaceUserLang((string)$hrh_cat->title); $hrh_cats[] = $hrh_cat; } } break; } // 게시판 최신 글 — posts 섹션마다 조회 (source 에 게시판 mid, 비우면 첫 게시판) if (!function_exists('hr_home_posts')) { function hr_home_posts($hrh_mid, $hrh_count) { if ($hrh_mid === '') { foreach (ModuleModel::getMidList((object)['module' => 'board'], ['mid']) ?: [] as $hrh_brow) { $hrh_mid = (string)($hrh_brow->mid ?? ''); break; } } if ($hrh_mid === '') { return ['mid' => '', 'rows' => []]; } $hrh_binfo = ModuleModel::getModuleInfoByMid($hrh_mid); if (!is_object($hrh_binfo) || empty($hrh_binfo->module_srl)) { return ['mid' => $hrh_mid, 'rows' => []]; } $hrh_dargs = new stdClass; $hrh_dargs->module_srl = (int)$hrh_binfo->module_srl; $hrh_dargs->list_count = $hrh_count; $hrh_dargs->sort_index = 'list_order'; $hrh_dargs->order_type = 'asc'; $hrh_dargs->statusList = ['PUBLIC']; $hrh_dmodel = getModel('document'); $hrh_dout = $hrh_dmodel->getDocumentList($hrh_dargs); $hrh_rows = []; foreach ((array)($hrh_dout->data ?? []) as $hrh_doc) { $hrh_rows[] = $hrh_doc; } // 게시판 스킨 설정의 제목(hr_board_title)은 module_info 에 합쳐지지 않으므로 직접 읽는다 $hrh_skin_vars = ModuleModel::getModuleSkinVars((int)$hrh_binfo->module_srl); $hrh_skin_title = ''; if (is_array($hrh_skin_vars) && isset($hrh_skin_vars['hr_board_title'])) { $hrh_sv = $hrh_skin_vars['hr_board_title']; $hrh_skin_title = is_object($hrh_sv) ? (string)($hrh_sv->value ?? '') : (string)$hrh_sv; } $hrh_title = trim($hrh_skin_title); if ($hrh_title === '') { $hrh_title = (string)($hrh_binfo->board_title ?? ''); } if ($hrh_title === '') { $hrh_title = (string)($hrh_binfo->browser_title ?? $hrh_mid); } return ['mid' => $hrh_mid, 'rows' => $hrh_rows, 'board_title' => \Context::replaceUserLang($hrh_title)]; } } @endphp
{{ $lang->ssh_daily_sub }}