first commit 2

This commit is contained in:
hmw1001
2026-06-11 18:47:38 +09:00
parent c768729ce6
commit 6f534e33a6
11095 changed files with 1595758 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
<?php
define('G5_IS_ADMIN', true);
require_once '../../common.php';
require_once G5_ADMIN_PATH . '/admin.lib.php';
// 💡 [추가] 코드 관리자 모듈 테이블 정의
$g5['ui_manager_table'] = G5_TABLE_PREFIX.'ui_manager';
$g5['form_category_table'] = G5_TABLE_PREFIX.'form_category';
$g5['common_lang_table'] = G5_TABLE_PREFIX.'common_lang';
$g5['form_option_history_table'] = G5_TABLE_PREFIX.'form_option_history';
if (isset($token)) {
$token = @htmlspecialchars(strip_tags($token), ENT_QUOTES);
}
run_event('admin_common');
@@ -0,0 +1,9 @@
<?php
if (!defined('_GNUBOARD_')) exit;
// 💡 [수정] 700번대 최상위 메뉴 배열에 아이콘 클래스('fa-puzzle-piece')를 추가합니다.
$menu['menu700'][] = array('700000', 'UI/폼 관리', G5_ADMIN_URL.'/code_manager/ui_manager_list.php', 'code_manager', 'fa-puzzle-piece');
// 'UI/폼 관리'의 하위 메뉴들을 정의합니다.
$menu['menu700'][] = array('700100', 'UI 리소스 관리', G5_ADMIN_URL.'/code_manager/ui_manager_list.php', 'ui_resource_manager');
$menu['menu700'][] = array('700900', '솔루션 설치', G5_ADMIN_URL.'/code_manager/install.php', 'ui_solution_install');
+282
View File
@@ -0,0 +1,282 @@
<?php
$sub_menu = '700100';
include_once('./_common.php');
// 💡 [추가] w 값을 먼저 정의합니다.
$w = isset($_REQUEST['w']) ? substr(trim($_REQUEST['w']), 0, 1) : '';
// ==================================================================
// 💡 [핵심 수정] 폼 제출 처리 로직 (신규/수정)
// ==================================================================
// POST 요청이고, w값이 넘어왔을 때 (신규='', 수정='u') 처리
if (isset($_POST['w']) && $_SERVER['REQUEST_METHOD'] === 'POST') {
auth_check_menu($auth, $sub_menu, 'w');
check_admin_token();
$w_from_post = $_POST['w']; // POST로 받은 w값을 기준으로 처리
$um_id = (int)$_POST['um_id'];
$parent_id = (int)$_POST['parent_id'];
$fc_key = trim($_POST['fc_key']);
$cl_name = trim($_POST['cl_name']);
$fc_order = (int)$_POST['fc_order'];
$is_used = (int)$_POST['is_used'];
if (!$um_id) {
alert('잘못된 접근입니다.');
}
if (!$fc_key) {
alert('옵션 키(Key)를 입력해주세요.');
}
if (!$cl_name) {
alert('옵션 이름을 입력해주세요.');
}
// 1. g5_form_category 테이블에 마스터 정보 저장
$sql_common = "
um_id = '{$um_id}',
parent_id = '{$parent_id}',
fc_key = '{$fc_key}',
fc_order = '{$fc_order}',
is_used = '{$is_used}',
updated_at = '" . G5_TIME_YMDHIS . "',
updated_by = '{$member['mb_id']}'
";
if ($w_from_post == 'u') { // 수정
$fc_id = (int)$_POST['fc_id'];
if (!$fc_id) alert('fc_id 값이 없습니다.');
$sql = "UPDATE {$g5['form_category_table']} SET {$sql_common} WHERE fc_id = '{$fc_id}'";
sql_query($sql);
} else { // 신규
$sql_common .= ", created_at = '" . G5_TIME_YMDHIS . "', created_by = '{$member['mb_id']}'";
$sql = "INSERT INTO {$g5['form_category_table']} SET {$sql_common}";
sql_query($sql);
$fc_id = sql_insert_id();
}
// 2. g5_common_lang 테이블에 다국어 이름 저장 (없으면 생성, 있으면 수정)
$sql = "SELECT cl_id FROM {$g5['common_lang_table']} WHERE target_table = '{$g5['form_category_table']}' AND target_id = '{$fc_id}' AND lang_code = 'ko'";
$lang_row = sql_fetch($sql);
if (isset($lang_row['cl_id'])) {
$sql = "UPDATE {$g5['common_lang_table']} SET cl_name = '{$cl_name}', updated_at = '" . G5_TIME_YMDHIS . "', updated_by = '{$member['mb_id']}' WHERE cl_id = '{$lang_row['cl_id']}'";
} else {
$sql = "INSERT INTO {$g5['common_lang_table']} SET target_table = '{$g5['form_category_table']}', target_id = '{$fc_id}', lang_code = 'ko', cl_name = '{$cl_name}', updated_at = '" . G5_TIME_YMDHIS . "', updated_by = '{$member['mb_id']}'";
}
sql_query($sql);
goto_url("./category_list.php?um_id=$um_id");
}
// ==================================================================
auth_check_menu($auth, $sub_menu, 'r');
// 그룹 ID가 없으면 되돌려 보냄
$um_id = isset($_GET['um_id']) ? (int)$_GET['um_id'] : 0;
if (!$um_id) {
alert('UI 리소스 ID가 올바르지 않습니다.', './ui_manager_list.php');
}
// 현재 관리하려는 리소스의 정보를 가져옴
$sql = "SELECT * FROM {$g5['ui_manager_table']} WHERE um_id = '{$um_id}'";
$ui_resource = sql_fetch($sql);
if (!isset($ui_resource['um_id']) || $ui_resource['resource_type'] != 'DATA') {
alert('존재하지 않거나 데이터 타입이 아닌 리소스입니다.', './ui_manager_list.php');
}
// 계층형으로 정렬된 카테고리 목록을 가져오는 함수
function get_category_view_list($um_id)
{
global $g5;
$sql = "SELECT
A.*,
B.cl_name
FROM
{$g5['form_category_table']} AS A
LEFT JOIN
{$g5['common_lang_table']} AS B
ON
(A.fc_id = B.target_id AND B.target_table = '{$g5['form_category_table']}' AND B.lang_code = 'ko')
WHERE
A.um_id = '{$um_id}' AND A.is_deleted = 0
ORDER BY
A.parent_id, A.fc_order, A.fc_id";
$result = sql_query($sql);
$categories = [];
while ($row = sql_fetch_array($result)) {
$categories[] = $row;
}
$view_list = [];
// 재귀 함수를 사용하여 계층 구조를 평탄화하고 depth를 추가
generate_category_list($categories, 0, 0, $view_list);
return $view_list;
}
function generate_category_list(&$source, $parent_id, $depth, &$result_list)
{
foreach ($source as $item) {
if ($item['parent_id'] == $parent_id) {
$item['depth'] = $depth;
$result_list[] = $item;
generate_category_list($source, $item['fc_id'], $depth + 1, $result_list);
}
}
}
$category_list = get_category_view_list($um_id);
$category_list_count = count($category_list);
$g5['title'] = get_text($ui_resource['resource_desc']) . ' : 옵션/카테고리 관리';
include_once(G5_ADMIN_PATH . '/admin.head.php');
add_stylesheet('<link rel="stylesheet" href="' . G5_ADMIN_URL . '/code_manager/css/code_manager.css?ver=1.1">', 0);
?>
<div class="local_desc01 local_desc">
<p>
<strong>'<?php echo get_text($ui_resource['resource_desc']); ?>'</strong> 리소스에 포함될 옵션 또는 카테고리를 관리합니다.<br>
'부모 카테고리'를 지정하여 대/중/소 분류와 같은 계층 구조를 만들 수 있습니다.
</p>
</div>
<section id="code_manager">
<div class="code-manager-header">
<h2 class="code-manager-title">
<a href="./ui_manager_list.php" class="btn btn_02">리소스 목록으로</a>
<?php echo get_text($ui_resource['resource_desc']); ?>
(<code><?php echo get_text($ui_resource['resource_code']); ?></code>)
</h2>
<div class="code-manager-actions">
<button type="button" id="add-category-btn" class="btn btn_01">
<i class="fa fa-plus" aria-hidden="true"></i> 새 옵션 추가
</button>
</div>
</div>
<!-- 새 옵션(카테고리) 추가 폼 -->
<div id="category-form-container" style="display: none; margin-top: 20px;">
<form name="fcategoryform" id="fcategoryform" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="post">
<input type="hidden" name="w" value="">
<input type="hidden" name="um_id" value="<?php echo $um_id; ?>">
<input type="hidden" name="fc_id" value="">
<input type="hidden" name="token" value="<?php echo get_admin_token(); ?>">
<div class="tbl_frm01 tbl_wrap">
<table>
<caption>옵션/카테고리 추가/수정 폼</caption>
<colgroup>
<col class="grid_4">
<col>
</colgroup>
<tbody>
<tr>
<th scope="row"><label for="parent_id">부모 카테고리</label></th>
<td>
<select name="parent_id" id="parent_id">
<option value="0">최상위 카테고리</option>
<?php foreach ($category_list as $cat) : ?>
<option value="<?php echo $cat['fc_id']; ?>">
<?php echo str_repeat('&nbsp;&nbsp;&nbsp;', $cat['depth']); ?><?php echo get_text($cat['cl_name']); ?>
</option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="fc_key">옵션 키(Key)</label></th>
<td>
<input type="text" name="fc_key" id="fc_key" required class="required frm_input" size="30">
<span class="frm_info">DB에 저장될 고유한 값입니다. (영문, 숫자, _ 사용)</span>
</td>
</tr>
<tr>
<th scope="row"><label for="cl_name">옵션 이름</label></th>
<td>
<input type="text" name="cl_name" id="cl_name" required class="required frm_input" size="50">
<span class="frm_info">화면에 표시될 이름입니다.</span>
</td>
</tr>
<tr>
<th scope="row"><label for="fc_order">정렬순서</label></th>
<td>
<input type="number" name="fc_order" value="0" id="fc_order" class="frm_input" size="5">
<span class="frm_info">숫자가 낮을수록 먼저 표시됩니다.</span>
</td>
</tr>
<tr>
<th scope="row">사용 여부</th>
<td>
<label><input type="radio" name="is_used" value="1" checked> 사용함</label>
<label><input type="radio" name="is_used" value="0"> 사용안함</label>
</td>
</tr>
</tbody>
</table>
</div>
<div class="btn_confirm01 btn_confirm">
<button type="button" id="cancel-category-btn" class="btn_cancel btn">취소</button>
<input type="submit" value="저장" class="btn_submit btn" accesskey="s">
</div>
</form>
</div>
<!-- 등록된 옵션(카테고리) 목록 테이블 -->
<div class="tbl_head01 tbl_wrap" style="margin-top: 20px;">
<table>
<caption>옵션/카테고리 목록</caption>
<thead>
<tr>
<th scope="col">카테고리 이름</th>
<th scope="col" style="width: 150px;">옵션 키 (Key)</th>
<th scope="col" style="width: 100px;">부모 ID</th>
<th scope="col" style="width: 60px;">순서</th>
<th scope="col" style="width: 60px;">사용</th>
<th scope="col" style="width: 180px;">관리</th>
</tr>
</thead>
<tbody>
<?php if ($category_list_count > 0) : ?>
<?php foreach ($category_list as $cat) : ?>
<tr>
<td class="td_left category-name-depth-<?php echo $cat['depth']; ?>">
<?php if ($cat['depth'] > 0) : ?>
<span class="depth-prefix">└</span>
<?php endif; ?>
<?php echo get_text($cat['cl_name']); ?>
</td>
<td><?php echo get_text($cat['fc_key']); ?></td>
<td><?php echo $cat['parent_id']; ?></td>
<td><?php echo $cat['fc_order']; ?></td>
<td><?php echo $cat['is_used'] ? 'Y' : 'N'; ?></td>
<td class="td_mng td_mng_s">
<button type="button" class="btn btn_02 btn_edit_category"
data-fc_id="<?php echo $cat['fc_id']; ?>"
data-parent_id="<?php echo $cat['parent_id']; ?>"
data-fc_key="<?php echo get_text($cat['fc_key']); ?>"
data-cl_name="<?php echo get_text($cat['cl_name']); ?>"
data-fc_order="<?php echo $cat['fc_order']; ?>"
data-is_used="<?php echo $cat['is_used']; ?>">수정
</button>
<a href="./lang_manager.php?target_table=<?php echo $g5['form_category_table']; ?>&amp;target_id=<?php echo $cat['fc_id']; ?>"
class="btn btn_01">다국어</a>
</td>
</tr>
<?php endforeach; ?>
<?php else : ?>
<tr class="empty_table">
<td colspan="6">등록된 옵션이 없습니다.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</section>
<script src="<?php echo G5_ADMIN_URL; ?>/code_manager/js/ui_manager.js?ver=1.4"></script>
<?php
include_once(G5_ADMIN_PATH . '/admin.tail.php');
+210
View File
@@ -0,0 +1,210 @@
<?php
$sub_menu = '100900'; // admin.menu100.php 에 정의된 메뉴 코드
include_once('./_common.php');
// ==================================================================
// 💡 [핵심] 폼 제출 처리 로직
// ==================================================================
if (isset($w) && $w == 'u' && $_SERVER['REQUEST_METHOD'] === 'POST') {
auth_check_menu($auth, $sub_menu, 'w');
check_admin_token();
// 입력값 정리
$screen_code = trim($_POST['screen_code']);
$group_code = trim($_POST['group_code']);
$resource_code = trim($_POST['resource_code']);
$resource_type = trim($_POST['resource_type']);
$resource_desc = trim($_POST['resource_desc']);
$cl_name = isset($_POST['cl_name']) ? trim($_POST['cl_name']) : ''; // LABEL 타입일 때만 넘어옴
// 유효성 검사
if (!$screen_code || !$group_code || !$resource_code || !$resource_type) {
alert('필수 항목을 모두 입력해주세요.');
}
if ($resource_type == 'LABEL' && !$cl_name) {
alert('UI 라벨 타입은 한국어 라벨명을 필수로 입력해야 합니다.');
}
// 중복 검사
$sql = "SELECT COUNT(*) as cnt FROM {$g5['ui_manager_table']} WHERE screen_code = '{$screen_code}' AND group_code = '{$group_code}' AND resource_code = '{$resource_code}'";
$row = sql_fetch($sql);
if ($row['cnt']) {
alert('이미 등록된 리소스 코드입니다.');
}
// 1. g5_ui_manager 테이블에 리소스 '설계' 정보 저장
$sql = "INSERT INTO {$g5['ui_manager_table']}
SET screen_code = '{$screen_code}',
group_code = '{$group_code}',
resource_code = '{$resource_code}',
resource_type = '{$resource_type}',
resource_desc = '{$resource_desc}',
is_used = '1',
created_at = '".G5_TIME_YMDHIS."',
created_by = '{$member['mb_id']}',
updated_at = '".G5_TIME_YMDHIS."',
updated_by = '{$member['mb_id']}'";
sql_query($sql);
$um_id = sql_insert_id();
// 2. 리소스 타입이 'LABEL'인 경우, g5_common_lang 테이블에 실제 텍스트 저장
if ($resource_type == 'LABEL' && $cl_name) {
$sql = "INSERT INTO {$g5['common_lang_table']}
SET target_table = '{$g5['ui_manager_table']}',
target_id = '{$um_id}',
lang_code = 'ko',
cl_name = '{$cl_name}',
updated_at = '".G5_TIME_YMDHIS."',
updated_by = '{$member['mb_id']}'";
sql_query($sql);
}
goto_url('./code_list.php');
}
auth_check_menu($auth, $sub_menu, 'r');
// 등록된 리소스 목록 조회
$sql = "SELECT * FROM {$g5['ui_manager_table']} ORDER BY screen_code, group_code, resource_code";
$result = sql_query($sql);
$resource_list = [];
while ($row = sql_fetch_array($result)) {
$resource_list[] = $row;
}
$resource_list_count = count($resource_list);
$g5['title'] = 'UI 리소스 관리';
include_once(G5_ADMIN_PATH . '/admin.head.php');
// 솔루션 전용 CSS/JS 파일을 불러옵니다.
add_stylesheet('<link rel="stylesheet" href="' . G5_ADMIN_URL . '/code_manager/css/code_manager.css">', 0);
?>
<div class="local_desc01 local_desc">
<p>
웹사이트의 모든 화면에 사용되는 텍스트(라벨)와 선택 옵션(데이터)을 체계적으로 관리합니다.
</p>
</div>
<section id="code_manager">
<div class="code-manager-header">
<h2 class="code-manager-title">UI 리소스 목록</h2>
<div class="code-manager-actions">
<button type="button" id="add-resource-btn" class="btn btn_01">
<i class="fa fa-plus" aria-hidden="true"></i> 새 리소스 추가
</button>
</div>
</div>
<!-- 새 리소스 추가 폼 -->
<div id="resource-form-container" style="display: none;">
<form name="fresourceform" id="fresourceform" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="post">
<input type="hidden" name="w" value="u">
<input type="hidden" name="token" value="<?php echo get_admin_token(); ?>">
<div class="tbl_frm01 tbl_wrap">
<table>
<caption>UI 리소스 추가 폼</caption>
<colgroup>
<col class="grid_4">
<col>
</colgroup>
<tbody>
<tr>
<th scope="row"><label for="screen_code">화면 코드</label></th>
<td>
<input type="text" name="screen_code" id="screen_code" required class="required frm_input" size="30">
<span class="frm_info">리소스가 사용될 화면의 고유 코드 (예: order_form, member_join)</span>
</td>
</tr>
<tr>
<th scope="row"><label for="group_code">그룹 코드</label></th>
<td>
<input type="text" name="group_code" id="group_code" required class="required frm_input" size="30">
<span class="frm_info">화면 내에서 리소스를 묶어줄 그룹 코드 (예: address_info, common_options)</span>
</td>
</tr>
<tr>
<th scope="row">리소스 타입</th>
<td>
<label><input type="radio" name="resource_type" value="LABEL" checked> UI 라벨 (단일 텍스트)</label>
<label><input type="radio" name="resource_type" value="DATA"> 데이터 (선택 옵션)</label>
</td>
</tr>
<tr class="resource-type-field" id="label-field">
<th scope="row"><label for="cl_name">한국어 라벨명</label></th>
<td>
<input type="text" name="cl_name" id="cl_name" class="frm_input" size="50">
<span class="frm_info">화면에 표시될 실제 텍스트 (예: 집의 유형, 창호 재질)</span>
</td>
</tr>
<tr>
<th scope="row"><label for="resource_code">리소스 코드</label></th>
<td>
<input type="text" name="resource_code" id="resource_code" required class="required frm_input" size="30">
<span class="frm_info">개발자가 이 리소스를 호출할 때 사용할 고유 코드 (예: house_type_label, house_type_data)</span>
</td>
</tr>
<tr>
<th scope="row"><label for="resource_desc">설명</label></th>
<td>
<input type="text" name="resource_desc" id="resource_desc" class="frm_input" size="80">
<span class="frm_info">이 리소스의 용도에 대한 설명 (관리자 참고용)</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="btn_confirm01 btn_confirm">
<button type="button" id="cancel-resource-btn" class="btn_cancel btn">취소</button>
<input type="submit" value="리소스 등록" class="btn_submit btn" accesskey="s">
</div>
</form>
</div>
<!-- 등록된 리소스 목록 테이블 -->
<div class="tbl_head01 tbl_wrap">
<table>
<caption>UI 리소스 목록</caption>
<thead>
<tr>
<th scope="col">화면 코드</th>
<th scope="col">그룹 코드</th>
<th scope="col">리소스 코드</th>
<th scope="col">타입</th>
<th scope="col">설명</th>
<th scope="col">관리</th>
</tr>
</thead>
<tbody>
<?php if ($resource_list_count > 0) : ?>
<?php foreach ($resource_list as $res) : ?>
<tr>
<td><?php echo get_text($res['screen_code']); ?></td>
<td><?php echo get_text($res['group_code']); ?></td>
<td><?php echo get_text($res['resource_code']); ?></td>
<td><?php echo $res['resource_type']; ?></td>
<td class="td_left"><?php echo get_text($res['resource_desc']); ?></td>
<td class="td_mng">
<?php if ($res['resource_type'] == 'DATA') : ?>
<a href="./category_list.php?um_id=<?php echo $res['um_id']; ?>" class="btn btn_03">옵션 관리</a>
<?php endif; ?>
<a href="./category_list.php?w=u&amp;um_id=<?php echo $res['um_id']; ?>" class="btn btn_02">수정</a>
</td>
</tr>
<?php endforeach; ?>
<?php else : ?>
<tr class="empty_table">
<td colspan="6">등록된 리소스가 없습니다.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</section>
<script src="<?php echo G5_ADMIN_URL; ?>/code_manager/js/code_manager.js"></script>
<?php
include_once(G5_ADMIN_PATH . '/admin.tail.php');
+196
View File
@@ -0,0 +1,196 @@
/* 폼 옵션 관리 솔루션 전용 스타일 */
#code_manager .code-manager-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
#code_manager .code-manager-title {
margin: 0;
font-size: 1.2em;
}
#code_manager .code-manager-actions .btn {
padding: 5px 12px;
font-size: 0.9em;
}
/* 새 리소스 추가 폼 */
#resource-form-container {
margin-bottom: 20px;
padding: 20px;
border: 1px solid #e0e0e0;
background-color: #f9f9f9;
border-radius: 5px;
}
#resource-form-container .frm_info {
display: block;
margin-top: 5px;
color: #666;
}
/* 💡 [추가] 아코디언 UI 스타일 */
#resource-list-accordion {
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
}
.accordion-item {
border-bottom: 1px solid #ddd;
}
.accordion-item:last-child {
border-bottom: none;
}
.accordion-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
background-color: #f7f7f7;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
}
.accordion-header:hover {
background-color: #efefef;
}
.accordion-header .screen-title {
font-size: 1.1em;
font-weight: 500;
}
.accordion-header .screen-title .fa {
margin-right: 8px;
color: #555;
}
.accordion-header .resource-count {
font-size: 0.9em;
color: #fff;
background-color: #888;
padding: 3px 8px;
border-radius: 10px;
}
.accordion-header .accordion-icon {
transition: transform 0.3s ease;
}
.accordion-item.active .accordion-header .accordion-icon {
transform: rotate(180deg);
}
.accordion-content {
display: none;
padding: 15px;
background-color: #fff;
border-top: 1px solid #ddd;
}
.accordion-content .tbl_wrap {
margin: 0;
}
/* 💡 [추가] 리소스 타입 시각적 구분 */
.res-type-label, .res-type-data {
display: inline-block;
padding: 3px 8px;
font-size: 0.85em;
font-weight: bold;
color: #fff;
border-radius: 4px;
text-align: center;
}
.res-type-label {
background-color: #3498db; /* 파란색 계열 */
}
.res-type-data {
background-color: #2ecc71; /* 녹색 계열 */
}
/* 기타 스타일 */
.tbl_head01 .td_left {
text-align: left;
padding-left: 10px;
}
/* ... 기존 CSS 코드 ... */
/* 💡 [추가] 카테고리 목록 들여쓰기 스타일 */
.category-name-depth-0 { font-weight: bold; }
.category-name-depth-1 { padding-left: 25px !important; }
.category-name-depth-2 { padding-left: 50px !important; }
.category-name-depth-3 { padding-left: 75px !important; }
.category-name-depth-4 { padding-left: 100px !important; }
.depth-prefix {
font-family: "Malgun Gothic", "Apple SD Gothic Neo", sans-serif;
font-weight: normal;
color: #aaa;
margin-right: 5px;
}
/* ... 기존 CSS 코드 ... */
/* 💡 [추가] 삭제 버튼 스타일 */
.btn_delete {
display: inline-block;
padding: 0 10px;
height: 28px;
line-height: 26px;
border: 1px solid #d43f3a;
background: #d9534f;
color: #fff;
text-decoration: none;
vertical-align: middle;
border-radius: 3px;
cursor: pointer;
}
.btn_delete:hover {
background: #c9302c;
border-color: #ac2925;
}
/* ... 기존 CSS 코드 ... */
/* 💡 [추가] 다국어 관리 페이지 스타일 */
.h2_frm {
margin: 20px 0 10px;
font-size: 1.1em;
font-weight: bold;
}
.td_alignc {
text-align: center;
}
#flangform textarea {
width: 100%;
padding: 5px;
}
/* ... 기존 CSS 코드 ... */
/* 💡 [추가] 검색 폼 스타일 */
#resource_search_form {
margin-bottom: 20px;
padding: 20px;
border: 1px solid #e9e9e9;
background: #fcfcfc;
}
#resource_search_form .h2_frm {
margin: 0 0 10px;
}
#resource_search_form .search-form-inner {
display: flex;
gap: 5px;
}
#resource_search_form select,
#resource_search_form .frm_input {
height: 35px;
padding: 0 10px;
border: 1px solid #ccc;
border-radius: 3px;
}
#resource_search_form .btn_submit {
height: 35px;
padding: 0 20px;
font-size: 1em;
}
/* ... 기존 CSS 코드 ... */
/* 💡 [추가] 페이징 스타일 */
.pagination_wrap {
margin-top: 20px;
text-align: center;
}
.pagination_wrap .pg_wrap {
display: inline-block;
float: none;
}
+192
View File
@@ -0,0 +1,192 @@
<?php
$sub_menu = '700900';
include_once('./_common.php');
include_once(__DIR__ . '/lib/SchemaManager.class.php');
if ($is_admin != 'super') {
alert('최고관리자만 접근 가능합니다.');
}
/**
* SQL 파일에서 테이블 이름을 추출하는 함수
*/
function get_tables_from_sql_file($filepath) {
$tables = [];
if (!file_exists($filepath)) {
return $tables;
}
$lines = file($filepath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (preg_match('/CREATE TABLE(?: IF NOT EXISTS)? `([^`]+)`/i', $line, $matches)) {
$tables[] = $matches[1];
}
}
return $tables;
}
$g5['title'] = 'UI 리소스 관리 솔루션 설치';
include_once(G5_ADMIN_PATH . '/admin.head.php');
$install_result = null;
$delete_result = null;
$action = $_POST['action'] ?? '';
// 💡 [수정] SQL 파일에서 테이블 목록 동적 로드
$tables_to_check = get_tables_from_sql_file(__DIR__ . '/install.sql');
if ($action === 'install') {
check_admin_token();
// ... (설치 로직은 기존과 동일)
$copy_results = [];
$solution_files = [
['source' => __DIR__ . '/lib/ui_manager.extend.php', 'target' => G5_EXTEND_PATH . '/ui_manager.extend.php', 'desc' => '핵심 기능 파일'],
['source' => __DIR__ . '/admin.menu700.code_manager.php', 'target' => G5_ADMIN_PATH . '/admin.menu700.code_manager.php', 'desc' => '관리자 메뉴 파일']
];
foreach ($solution_files as $file) {
$key = $file['target'];
if (file_exists($file['source']) && is_writable(dirname($file['target']))) {
@copy($file['source'], $file['target']);
}
}
$sql_file = __DIR__ . '/install.sql';
$db_results = [];
try {
$schemaManager = new SchemaManager($sql_file);
$schemaManager->execute();
$db_results = $schemaManager->get_results();
} catch (Exception $e) { $db_results['errors'][] = $e->getMessage(); }
$install_result = ['db' => $db_results];
} else if ($action === 'delete') {
check_admin_token();
$delete_result = ['tables' => [], 'menu' => ''];
// 💡 [수정] 삭제할 테이블 목록도 동적으로 가져옴
$tables_to_delete = get_tables_from_sql_file(__DIR__ . '/install.sql');
foreach ($tables_to_delete as $table) {
sql_query("DROP TABLE IF EXISTS `{$table}`", false);
$delete_result['tables'][] = $table;
}
// $menu_file = G5_ADMIN_PATH . '/admin.menu700.code_manager.php';
$solution_files = [
['source' => __DIR__ . '/lib/ui_manager.extend.php', 'target' => G5_EXTEND_PATH . '/ui_manager.extend.php', 'desc' => '핵심 기능 파일'],
['source' => __DIR__ . '/admin.menu700.code_manager.php', 'target' => G5_ADMIN_PATH . '/admin.menu700.code_manager.php', 'desc' => '관리자 메뉴 파일']
];
foreach ($solution_files as $file) {
$key = $file['target'];
var_dump($key);
if (file_exists($key)) {
if (@unlink($key)) {
$delete_result['menu'] = '메뉴 파일 삭제 성공';
} else {
$delete_result['menu'] = '메뉴 파일 삭제 실패 (권한 확인 필요)';
}
}
}
// if (file_exists($menu_file)) {
// if (@unlink($menu_file)) {
// $delete_result['menu'] = '메뉴 파일 삭제 성공';
// } else {
// $delete_result['menu'] = '메뉴 파일 삭제 실패 (권한 확인 필요)';
// }
// }
}
$existing_tables = array();
foreach ($tables_to_check as $table) {
if (sql_query("SHOW TABLES LIKE '$table'", false) && sql_num_rows(sql_query("SHOW TABLES LIKE '$table'", false)) > 0) {
$existing_tables[] = $table;
}
}
$is_installed = count($existing_tables) == count($tables_to_check);
?>
<style>
.install-container { max-width: 800px; margin: 20px auto; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.install-header { text-align: center; margin-bottom: 30px; padding-bottom: 20px; border-bottom: 2px solid #AA20FF; }
.install-header h1 { color: #AA20FF; margin-bottom: 10px; }
.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin: 30px 0; }
.feature-card { padding: 20px; border: 1px solid #e0e0e0; border-radius: 8px; text-align: center; }
.feature-card i { font-size: 2em; color: #AA20FF; margin-bottom: 10px; }
.status-table { width: 100%; border-collapse: collapse; margin: 20px 0; }
.status-table th, .status-table td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
.status-table th { background-color: #fff; font-weight: bold; }
.status-ok { color: #28a745; font-weight: bold; }
.status-missing { color: #dc3545; font-weight: bold; }
.install-btn { display: block; width: 200px; margin: 30px auto; padding: 15px 30px; background: #AA20FF; color: white; text-align: center; text-decoration: none; border-radius: 5px; font-size: 16px; font-weight: bold; border: none; cursor: pointer; transition: background-color 0.3s; }
.install-btn:hover { background: #8A1ACC; color: white; }
.install-btn:disabled { background: #ccc; cursor: not-allowed; }
.alert { padding: 15px; margin: 20px 0; border-radius: 5px; }
.alert-success { background-color: #d4edda; border: 1px solid #c3e6cb; color: #155724; }
.alert-info { background-color: #d1ecf1; border: 1px solid #bee5eb; color: #0c5460; }
.alert-danger { background-color: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; }
.btn-secondary { background: #6c757d; color: white; border-color: #6c757d; padding: 5px 10px; border-radius: 4px; text-decoration: none; }
.btn-secondary:hover { background: #5a6268; }
.btn-danger { background: #dc3545; color: white; border: none; padding: 10px 15px; border-radius: 4px; cursor: pointer; }
.btn-danger:hover { background: #c82333; }
.button-group { display: flex; justify-content: center; align-items: center; gap: 10px; }
</style>
<div class="install-container">
<div class="install-header">
<h1><i class="fa fa-code"></i> UI 리소스 관리 솔루션</h1>
<p>CSS, JS 등 UI 리소스를 효율적으로 관리하는 시스템</p>
</div>
<?php if ($install_result): ?>
<div class="alert alert-success"><h4><i class="fa fa-check-circle"></i> 설치 작업 완료</h4><p>데이터베이스 설치 작업이 완료되었습니다.</p><p><a href="./ui_manager_list.php" class="btn btn-primary">UI 리소스 관리로 이동</a></p></div>
<?php elseif ($delete_result): ?>
<div class="alert alert-danger"><h4><i class="fa fa-trash"></i> 삭제 작업 완료</h4><p>솔루션 관련 데이터와 파일이 삭제되었습니다.</p><ul><?php foreach($delete_result['tables'] as $tbl) echo "<li>{$tbl} 테이블 삭제됨</li>"; ?><li><?php echo $delete_result['menu']; ?></li></ul></div>
<?php elseif ($is_installed): ?>
<div class="alert alert-success"><h4><i class="fa fa-check-circle"></i> 설치 완료</h4><p>UI 리소스 관리 솔루션이 이미 설치되어 있습니다.</p><p><a href="./ui_manager_list.php" class="btn btn-primary">UI 리소스 관리로 이동</a></p></div>
<?php else: ?>
<div class="alert alert-info"><h4><i class="fa fa-info-circle"></i> 설치 필요</h4><p>UI 리소스 관리 솔루션을 사용하기 위해 설치가 필요합니다.</p></div>
<?php endif; ?>
<h3><i class="fa fa-database"></i> 설치 상태</h3>
<table class="status-table">
<thead><tr><th>테이블명</th><th>설명</th><th>상태</th></tr></thead>
<tbody>
<?php foreach ($tables_to_check as $table): ?>
<tr>
<td><code><?php echo $table; ?></code></td>
<td><?php echo array('g5_ui_manager' => 'UI 리소스 마스터', 'g5_form_category' => '계층형 폼 카테고리', 'g5_common_lang' => '공용 다국어 정보', 'g5_form_option_history' => '폼 옵션 변경 이력')[$table] ?? '데이터 테이블'; ?></td>
<td>
<?php if (in_array($table, $existing_tables)): ?>
<span class="status-ok"><i class="fa fa-check"></i> 설치됨</span>
<?php else: ?>
<span class="status-missing"><i class="fa fa-times"></i> 미설치</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php if (!$is_installed): ?>
<form method="post" onsubmit="return confirm('솔루션을 설치하시겠습니까?');">
<input type="hidden" name="action" value="install">
<input type="hidden" name="token" value="<?php echo get_token(); ?>">
<button type="submit" class="install-btn"><i class="fa fa-download"></i> 솔루션 설치하기</button>
</form>
<?php endif; ?>
<?php if ($is_installed && !$install_result && !$delete_result): ?>
<div class="button-group" style="text-align: center; margin-top: 20px;">
<form method="post" onsubmit="return confirm('기존 데이터는 유지되며, 변경된 DB 구조만 업데이트 됩니다. 진행하시겠습니까?');">
<input type="hidden" name="action" value="install">
<input type="hidden" name="token" value="<?php echo get_token(); ?>">
<button type="submit" class="btn btn-secondary"><i class="fa fa-sync"></i> 재설치 (업데이트)</button>
</form>
<form method="post" onsubmit="return confirm('정말로 솔루션을 삭제하시겠습니까? 모든 관련 데이터와 파일이 영구적으로 삭제됩니다.');">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="token" value="<?php echo get_token(); ?>">
<button type="submit" class="btn-danger"><i class="fa fa-trash"></i> 솔루션 삭제하기</button>
</form>
</div>
<?php endif; ?>
</div>
<?php
include_once(G5_ADMIN_PATH . '/admin.tail.php');
?>
+64
View File
@@ -0,0 +1,64 @@
-- 1. [핵심] UI 리소스 마스터 테이블
-- 이 테이블은 웹사이트의 모든 UI 요소(라벨, 데이터)의 '설계도' 역할을 합니다.
CREATE TABLE IF NOT EXISTS `g5_ui_manager` (
`um_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '고유 ID',
`screen_code` varchar(50) NOT NULL COMMENT '화면 코드 (e.g. order_form)',
`group_code` varchar(50) NOT NULL COMMENT '화면 내 그룹 코드 (e.g. address_info)',
`resource_code` varchar(50) NOT NULL COMMENT '리소스 코드 (개발자가 사용)',
`resource_type` enum('LABEL','DATA') NOT NULL COMMENT '리소스 타입 (라벨, 데이터)',
`resource_desc` varchar(255) DEFAULT NULL COMMENT '리소스에 대한 설명 (관리자용)',
`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '사용 여부',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
`created_by` varchar(20) NOT NULL COMMENT '생성자',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '수정일',
`updated_by` varchar(20) NOT NULL COMMENT '수정자',
PRIMARY KEY (`um_id`),
UNIQUE KEY `resource_identifier` (`screen_code`,`group_code`,`resource_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='[솔루션] UI 리소스 관리자';
-- 2. 계층형 카테고리 테이블 ('DATA' 타입 리소스가 사용할 데이터)
-- um_id를 통해 어떤 리소스에 속한 데이터인지 명시합니다.
CREATE TABLE IF NOT EXISTS `g5_form_category` (
`fc_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '카테고리 고유 ID',
`um_id` int(11) NOT NULL COMMENT 'UI 리소스 ID (g5_ui_manager.um_id)',
`parent_id` int(11) NOT NULL DEFAULT '0' COMMENT '부모 카테고리 ID (0이면 최상위)',
`fc_key` varchar(255) NOT NULL COMMENT 'DB에 저장될 값 (고유값)',
`fc_order` int(11) NOT NULL DEFAULT '0' COMMENT '정렬 순서',
`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '사용 여부',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '삭제 여부',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
`created_by` varchar(20) NOT NULL COMMENT '생성자',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '수정일',
`updated_by` varchar(20) NOT NULL COMMENT '수정자',
PRIMARY KEY (`fc_id`),
KEY `um_id` (`um_id`),
KEY `parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='[솔루션] 계층형 폼 카테고리';
-- 3. 공용 다국어 정보 테이블
-- 'LABEL' 타입의 실제 텍스트와 'DATA' 타입의 카테고리 이름을 모두 저장합니다.
CREATE TABLE IF NOT EXISTS `g5_common_lang` (
`cl_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '고유 ID',
`target_table` varchar(50) NOT NULL COMMENT '대상 테이블명',
`target_id` int(11) NOT NULL COMMENT '대상 레코드 ID',
`lang_code` varchar(10) NOT NULL COMMENT '언어 코드',
`cl_name` varchar(255) NOT NULL COMMENT '화면에 표시될 이름/값',
`cl_description` text COMMENT '부가 설명 (툴팁 등)',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '수정일',
`updated_by` varchar(20) NOT NULL COMMENT '수정자',
PRIMARY KEY (`cl_id`),
UNIQUE KEY `target_lang` (`target_table`,`target_id`,`lang_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='[솔루션] 공용 다국어 정보';
-- 4. 변경 이력 테이블
CREATE TABLE IF NOT EXISTS `g5_form_option_history` (
`fh_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '고유 ID',
`table_name` varchar(50) NOT NULL COMMENT '변경된 테이블명',
`record_id` int(11) NOT NULL COMMENT '변경된 레코드 ID',
`action_type` varchar(10) NOT NULL COMMENT '작업 종류',
`change_data` longtext COMMENT '변경된 데이터 (JSON)',
`changed_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '변경일',
`changed_by` varchar(20) NOT NULL COMMENT '변경자',
PRIMARY KEY (`fh_id`),
KEY `table_name_record_id` (`table_name`,`record_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='[솔루션] 폼 옵션 변경 이력';
+90
View File
@@ -0,0 +1,90 @@
-- 1. 마스터 테이블: 옵션 그룹 (예: '집 유형', '창호 색상')
CREATE TABLE IF NOT EXISTS `g5_form_group` (
`fg_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '고유 ID',
`project_code` varchar(50) NOT NULL DEFAULT 'default' COMMENT '프로젝트 코드',
`site_code` varchar(50) NOT NULL DEFAULT 'default' COMMENT '사이트 코드',
`fg_code` varchar(50) NOT NULL COMMENT '그룹 코드 (프로그램에서 사용)',
`fg_order` int(11) NOT NULL DEFAULT '0' COMMENT '정렬 순서',
`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '사용 여부 (1:사용, 0:미사용)',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '삭제 여부 (1:삭제, 0:정상)',
`created_at` datetime NOT NULL COMMENT '생성일',
`created_by` varchar(20) NOT NULL COMMENT '생성자',
`updated_at` datetime NOT NULL COMMENT '수정일',
`updated_by` varchar(20) NOT NULL COMMENT '수정자',
`fg_temp_1` varchar(255) DEFAULT NULL COMMENT '임시 필드 1',
`fg_temp_2` varchar(255) DEFAULT NULL COMMENT '임시 필드 2',
`fg_temp_3` varchar(255) DEFAULT NULL COMMENT '임시 필드 3',
`fg_temp_4` text DEFAULT NULL COMMENT '임시 필드 4',
`fg_temp_5` text DEFAULT NULL COMMENT '임시 필드 5',
`fg_extra_1` varchar(255) DEFAULT NULL COMMENT '여분 필드 1',
`fg_extra_2` varchar(255) DEFAULT NULL COMMENT '여분 필드 2',
`fg_extra_3` varchar(255) DEFAULT NULL COMMENT '여분 필드 3',
`fg_extra_4` text DEFAULT NULL COMMENT '여분 필드 4',
`fg_extra_5` text DEFAULT NULL COMMENT '여분 필드 5',
PRIMARY KEY (`fg_id`),
UNIQUE KEY `project_site_fg_code` (`project_code`,`site_code`,`fg_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='[솔루션] 폼 옵션 그룹';
-- 2. 디테일 테이블: 그룹에 속한 개별 옵션
CREATE TABLE IF NOT EXISTS `g5_form_option` (
`fo_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '고유 ID',
`fg_id` int(11) NOT NULL COMMENT '그룹 ID (g5_form_group.fg_id)',
`fo_key` varchar(255) NOT NULL COMMENT 'DB에 저장될 값 (고유값)',
`fo_order` int(11) NOT NULL DEFAULT '0' COMMENT '정렬 순서',
`is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT '기본 선택 여부 (1:기본값)',
`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '사용 여부 (1:사용, 0:미사용)',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '삭제 여부 (1:삭제, 0:정상)',
`created_at` datetime NOT NULL COMMENT '생성일',
`created_by` varchar(20) NOT NULL COMMENT '생성자',
`updated_at` datetime NOT NULL COMMENT '수정일',
`updated_by` varchar(20) NOT NULL COMMENT '수정자',
`fo_temp_1` varchar(255) DEFAULT NULL COMMENT '임시 필드 1',
`fo_temp_2` varchar(255) DEFAULT NULL COMMENT '임시 필드 2',
`fo_temp_3` varchar(255) DEFAULT NULL COMMENT '임시 필드 3',
`fo_temp_4` text DEFAULT NULL COMMENT '임시 필드 4',
`fo_temp_5` text DEFAULT NULL COMMENT '임시 필드 5',
`fo_extra_1` varchar(255) DEFAULT NULL COMMENT '여분 필드 1',
`fo_extra_2` varchar(255) DEFAULT NULL COMMENT '여분 필드 2',
`fo_extra_3` varchar(255) DEFAULT NULL COMMENT '여분 필드 3',
`fo_extra_4` text DEFAULT NULL COMMENT '여분 필드 4',
`fo_extra_5` text DEFAULT NULL COMMENT '여분 필드 5',
PRIMARY KEY (`fo_id`),
KEY `fg_id` (`fg_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='[솔루션] 폼 옵션 항목';
-- 3. [통합] 서브 디테일 테이블: 그룹과 옵션의 다국어 이름/설명
CREATE TABLE IF NOT EXISTS `g5_common_lang` (
`cl_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '고유 ID',
`target_table` varchar(50) NOT NULL COMMENT '대상 테이블명 (예: g5_form_group)',
`target_id` int(11) NOT NULL COMMENT '대상 레코드 ID',
`lang_code` varchar(10) NOT NULL COMMENT '언어 코드 (ko, en, ja)',
`cl_name` varchar(255) NOT NULL COMMENT '화면에 표시될 이름/값',
`cl_description` text COMMENT '부가 설명 (툴팁 등)',
`updated_at` datetime NOT NULL COMMENT '수정일',
`updated_by` varchar(20) NOT NULL COMMENT '수정자',
`cl_temp_1` varchar(255) DEFAULT NULL COMMENT '임시 필드 1',
`cl_temp_2` varchar(255) DEFAULT NULL COMMENT '임시 필드 2',
`cl_temp_3` varchar(255) DEFAULT NULL COMMENT '임시 필드 3',
`cl_temp_4` text DEFAULT NULL COMMENT '임시 필드 4',
`cl_temp_5` text DEFAULT NULL COMMENT '임시 필드 5',
`cl_extra_1` varchar(255) DEFAULT NULL COMMENT '여분 필드 1',
`cl_extra_2` varchar(255) DEFAULT NULL COMMENT '여분 필드 2',
`cl_extra_3` varchar(255) DEFAULT NULL COMMENT '여분 필드 3',
`cl_extra_4` text DEFAULT NULL COMMENT '여분 필드 4',
`cl_extra_5` text DEFAULT NULL COMMENT '여분 필드 5',
PRIMARY KEY (`cl_id`),
UNIQUE KEY `target_lang` (`target_table`,`target_id`,`lang_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='[솔루션] 공용 다국어 정보';
-- 4. 변경 이력 테이블
CREATE TABLE IF NOT EXISTS `g5_form_option_history` (
`fh_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '고유 ID',
`table_name` varchar(50) NOT NULL COMMENT '변경된 테이블명',
`record_id` int(11) NOT NULL COMMENT '변경된 레코드 ID',
`action_type` varchar(10) NOT NULL COMMENT '작업 종류 (INSERT, UPDATE, DELETE)',
`change_data` longtext COMMENT '변경된 데이터 (JSON)',
`changed_at` datetime NOT NULL COMMENT '변경일',
`changed_by` varchar(20) NOT NULL COMMENT '변경자',
PRIMARY KEY (`fh_id`),
KEY `table_name_record_id` (`table_name`,`record_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='[솔루션] 폼 옵션 변경 이력';
+41
View File
@@ -0,0 +1,41 @@
document.addEventListener('DOMContentLoaded', function() {
const addResourceButton = document.getElementById('add-resource-btn');
const resourceFormContainer = document.getElementById('resource-form-container');
const cancelResourceButton = document.getElementById('cancel-resource-btn');
const resourceTypeRadios = document.querySelectorAll('input[name="resource_type"]');
const labelField = document.getElementById('label-field');
// '새 리소스 추가' 버튼 클릭 이벤트
if (addResourceButton) {
addResourceButton.addEventListener('click', function() {
if (resourceFormContainer) resourceFormContainer.style.display = 'block';
this.style.display = 'none';
});
}
// '취소' 버튼 클릭 이벤트
if (cancelResourceButton) {
cancelResourceButton.addEventListener('click', function() {
if (resourceFormContainer) resourceFormContainer.style.display = 'none';
if (addResourceButton) addResourceButton.style.display = 'inline-block';
});
}
// 💡 [핵심] 리소스 타입 라디오 버튼 변경 이벤트
function toggleResourceTypeFields() {
const selectedType = document.querySelector('input[name="resource_type"]:checked').value;
if (selectedType === 'LABEL') {
labelField.style.display = ''; // 테이블 행이므로 기본값으로
} else {
labelField.style.display = 'none';
}
}
if (resourceTypeRadios.length > 0) {
resourceTypeRadios.forEach(radio => {
radio.addEventListener('change', toggleResourceTypeFields);
});
// 페이지 로드 시 초기 상태 설정
toggleResourceTypeFields();
}
});
+152
View File
@@ -0,0 +1,152 @@
document.addEventListener('DOMContentLoaded', function() {
// =================================================================
// 1. UI 리소스 목록 페이지 (ui_manager_list.php)
// =================================================================
const addResourceButton = document.getElementById('add-resource-btn');
const resourceFormContainer = document.getElementById('resource-form-container');
const cancelResourceButton = document.getElementById('cancel-resource-btn');
const resourceTypeRadios = document.querySelectorAll('input[name="resource_type"]');
const labelField = document.getElementById('label-field');
const accordionItems = document.querySelectorAll('.accordion-item');
const searchForm = document.getElementById('fsearch');
const pageRowsSelect = document.getElementById('page_rows');
if (addResourceButton) {
addResourceButton.addEventListener('click', function() {
if (resourceFormContainer) resourceFormContainer.style.display = 'block';
this.style.display = 'none';
});
}
if (cancelResourceButton) {
cancelResourceButton.addEventListener('click', function() {
if (resourceFormContainer) resourceFormContainer.style.display = 'none';
if (addResourceButton) addResourceButton.style.display = 'inline-block';
});
}
function toggleResourceTypeFields() {
if (!document.querySelector('input[name="resource_type"]:checked')) return;
const selectedType = document.querySelector('input[name="resource_type"]:checked').value;
if (labelField) {
labelField.style.display = (selectedType === 'LABEL') ? '' : 'none';
}
}
if (resourceTypeRadios.length > 0) {
resourceTypeRadios.forEach(radio => radio.addEventListener('change', toggleResourceTypeFields));
toggleResourceTypeFields();
}
if (accordionItems.length > 0) {
accordionItems.forEach(item => {
const header = item.querySelector('.accordion-header');
const content = item.querySelector('.accordion-content');
if (header && content) {
header.addEventListener('click', () => {
item.classList.toggle('active');
content.style.display = item.classList.contains('active') ? 'block' : 'none';
});
}
});
}
if (searchForm) {
searchForm.addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
// 엔터키 입력 시 기본 동작(폼 제출)을 막지 않음
}
});
}
if (pageRowsSelect) {
pageRowsSelect.addEventListener('change', function() {
if (searchForm) {
searchForm.submit();
}
});
}
// =================================================================
// 💡 [핵심] 옵션/카테고리 관리 페이지 (category_list.php) 로직 추가
// =================================================================
const addCategoryButton = document.getElementById('add-category-btn');
const categoryFormContainer = document.getElementById('category-form-container');
const cancelCategoryButton = document.getElementById('cancel-category-btn');
const categoryForm = document.getElementById('fcategoryform');
const editCategoryButtons = document.querySelectorAll('.btn_edit_category');
// "새 옵션 추가" 버튼 클릭
if (addCategoryButton && categoryFormContainer) {
addCategoryButton.addEventListener('click', function() {
if (categoryForm) {
categoryForm.reset();
categoryForm.w.value = ''; // 신규 등록 모드
categoryForm.fc_id.value = '';
const submitButton = categoryForm.querySelector('input[type="submit"]');
if (submitButton) submitButton.value = '저장';
}
categoryFormContainer.style.display = 'block';
this.style.display = 'none';
});
}
// "취소" 버튼 클릭
if (cancelCategoryButton && categoryFormContainer && addCategoryButton) {
cancelCategoryButton.addEventListener('click', function() {
categoryFormContainer.style.display = 'none';
addCategoryButton.style.display = 'inline-block';
});
}
// "수정" 버튼 클릭
if (editCategoryButtons.length > 0 && categoryForm) {
editCategoryButtons.forEach(button => {
button.addEventListener('click', function() {
const data = this.dataset;
// 폼에 데이터 채우기
categoryForm.w.value = 'u'; // 수정 모드
categoryForm.fc_id.value = data.fc_id;
categoryForm.parent_id.value = data.parent_id;
categoryForm.fc_key.value = data.fc_key;
categoryForm.cl_name.value = data.cl_name;
categoryForm.fc_order.value = data.fc_order;
const isUsedRadio = categoryForm.querySelector(`input[name="is_used"][value="${data.is_used}"]`);
if (isUsedRadio) isUsedRadio.checked = true;
const submitButton = categoryForm.querySelector('input[type="submit"]');
if (submitButton) submitButton.value = '수정';
if (categoryFormContainer) categoryFormContainer.style.display = 'block';
if (addCategoryButton) addCategoryButton.style.display = 'none';
categoryFormContainer.scrollIntoView({ behavior: 'smooth' });
});
});
}
// =================================================================
// 3. 리소스 삭제 기능
// =================================================================
const deleteResourceButtons = document.querySelectorAll('.btn_delete_resource');
if (deleteResourceButtons.length > 0) {
deleteResourceButtons.forEach(button => {
button.addEventListener('click', function(event) {
event.preventDefault();
const um_id = this.dataset.um_id;
const resource_desc_element = this.closest('tr').querySelector('.td_left');
const resource_desc = resource_desc_element ? resource_desc_element.textContent.trim() : `ID: ${um_id}`;
if (confirm(`'${resource_desc}' 리소스를 정말 삭제하시겠습니까?\n\n이 리소스와 관련된 모든 하위 옵션(카테고리) 및 언어 데이터가 함께 영구적으로 삭제됩니다.`)) {
const qstr = new URLSearchParams(window.location.search).toString();
location.href = `./ui_manager_list.php?mode=delete&um_id=${um_id}&token=${g5_admin_token}&${qstr}`;
}
});
});
}
});
+200
View File
@@ -0,0 +1,200 @@
<?php
$sub_menu = '700100';
include_once('./_common.php');
// ... (폼 제출 처리 로직은 변경 없음) ...
$target_table = isset($_REQUEST['target_table']) ? preg_replace('/[^a-z0-9_]/i', '', $_REQUEST['target_table']) : '';
$target_id = isset($_REQUEST['target_id']) ? (int)$_REQUEST['target_id'] : 0;
if (!$target_table || !$target_id) {
alert('잘못된 접근입니다.');
}
if (isset($w) && $w == 'u' && $_SERVER['REQUEST_METHOD'] === 'POST') {
auth_check_menu($auth, $sub_menu, 'w');
check_admin_token();
$lang_code = trim($_POST['lang_code']);
$cl_name = trim($_POST['cl_name']);
$cl_description = trim($_POST['cl_description']);
if (!$lang_code) alert('언어 코드를 선택해주세요.');
if (!$cl_name) alert('이름/값을 입력해주세요.');
$sql = "INSERT INTO {$g5['common_lang_table']}
SET target_table = '{$target_table}',
target_id = '{$target_id}',
lang_code = '{$lang_code}',
cl_name = '{$cl_name}',
cl_description = '{$cl_description}',
updated_at = '".G5_TIME_YMDHIS."',
updated_by = '{$member['mb_id']}'
ON DUPLICATE KEY UPDATE
cl_name = '{$cl_name}',
cl_description = '{$cl_description}',
updated_at = '".G5_TIME_YMDHIS."',
updated_by = '{$member['mb_id']}'";
sql_query($sql);
goto_url("./lang_manager.php?target_table=$target_table&target_id=$target_id");
}
if (isset($mode) && $mode == 'delete') {
auth_check_menu($auth, $sub_menu, 'd');
check_admin_token();
$cl_id = isset($_GET['cl_id']) ? (int)$_GET['cl_id'] : 0;
if (!$cl_id) alert('cl_id 값이 없습니다.');
$sql = "DELETE FROM {$g5['common_lang_table']} WHERE cl_id = '{$cl_id}' AND target_table = '{$target_table}' AND target_id = '{$target_id}'";
sql_query($sql);
goto_url("./lang_manager.php?target_table=$target_table&target_id=$target_id");
}
auth_check_menu($auth, $sub_menu, 'r');
// 💡 [핵심 추가] 이전 페이지로 돌아가기 위한 링크 생성
$back_link = './ui_manager_list.php'; // 기본 돌아가기 링크
if ($target_table == $g5['form_category_table']) {
// 카테고리 다국어 관리였다면, 해당 카테고리가 속한 리소스의 옵션 관리 페이지로 돌아가야 함
$sql_back = "SELECT um_id FROM {$g5['form_category_table']} WHERE fc_id = '{$target_id}'";
$back_row = sql_fetch($sql_back);
if (isset($back_row['um_id'])) {
$back_link = './category_list.php?um_id=' . $back_row['um_id'];
}
}
// UiManager 클래스를 사용하여 사용 가능한 언어 목록 가져오기
$active_languages = ui_manager()->get_data('language_list');
// 현재 관리 대상의 기본 정보(한국어)를 가져옴
$sql = "SELECT cl_name FROM {$g5['common_lang_table']} WHERE target_table = '{$target_table}' AND target_id = '{$target_id}' AND lang_code = 'ko'";
$parent_info = sql_fetch($sql);
$parent_name = isset($parent_info['cl_name']) ? get_text($parent_info['cl_name']) : "ID: {$target_id}";
// 등록된 다국어 목록 조회
$sql = "SELECT * FROM {$g5['common_lang_table']} WHERE target_table = '{$target_table}' AND target_id = '{$target_id}' ORDER BY lang_code";
$result = sql_query($sql);
$lang_list = [];
while ($row = sql_fetch_array($result)) {
$lang_list[] = $row;
}
$lang_list_count = count($lang_list);
$g5['title'] = '다국어 관리';
include_once(G5_ADMIN_PATH . '/admin.head.php');
add_stylesheet('<link rel="stylesheet" href="' . G5_ADMIN_URL . '/code_manager/css/code_manager.css?ver=1.1">', 0);
?>
<div class="local_desc01 local_desc">
<p>
<strong>'<?php echo $parent_name; ?>'</strong> 항목에 대한 다국어 이름과 설명을 관리합니다.<br>
'언어 코드'는 중복하여 등록할 수 없습니다.
</p>
</div>
<section id="code_manager">
<h2 class="h2_frm">다국어 등록/수정</h2>
<form name="flangform" id="flangform" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="post">
<input type="hidden" name="w" value="u">
<input type="hidden" name="target_table" value="<?php echo $target_table; ?>">
<input type="hidden" name="target_id" value="<?php echo $target_id; ?>">
<input type="hidden" name="token" value="<?php echo get_admin_token(); ?>">
<div class="tbl_frm01 tbl_wrap">
<table>
<caption>다국어 정보 추가/수정 폼</caption>
<colgroup>
<col class="grid_4">
<col>
</colgroup>
<tbody>
<tr>
<th scope="row"><label for="lang_code">언어 코드</label></th>
<td>
<select name="lang_code" id="lang_code" required>
<option value="">선택</option>
<?php foreach ($active_languages as $lang_item) : ?>
<option value="<?php echo $lang_item['fc_key']; ?>"><?php echo get_text($lang_item['cl_name']); ?> (<?php echo $lang_item['fc_key']; ?>)</option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="cl_name">이름/값</label></th>
<td>
<input type="text" name="cl_name" id="cl_name" required class="required frm_input" size="80">
</td>
</tr>
<tr>
<th scope="row"><label for="cl_description">부가 설명</label></th>
<td>
<textarea name="cl_description" id="cl_description" rows="5"></textarea>
<span class="frm_info">툴팁 등 부가적으로 사용될 설명을 입력합니다. (선택사항)</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="btn_confirm01 btn_confirm">
<!-- 💡 [핵심 수정] 목록으로 돌아가는 버튼 추가 -->
<a href="<?php echo $back_link; ?>" class="btn_cancel btn">목록으로</a>
<input type="submit" value="저장" class="btn_submit btn" accesskey="s">
</div>
</form>
<!-- 등록된 다국어 목록 테이블 -->
<div class="tbl_head01 tbl_wrap" style="margin-top: 20px;">
<h2 class="h2_frm">등록된 다국어 목록</h2>
<table>
<caption>등록된 다국어 목록</caption>
<thead>
<tr>
<th scope="col" style="width: 100px;">언어 코드</th>
<th scope="col">이름/값</th>
<th scope="col">부가 설명</th>
<th scope="col" style="width: 120px;">관리</th>
</tr>
</thead>
<tbody>
<?php if ($lang_list_count > 0) : ?>
<?php foreach ($lang_list as $lang) : ?>
<tr>
<td class="td_alignc"><?php echo get_text($lang['lang_code']); ?></td>
<td class="td_left"><?php echo get_text($lang['cl_name']); ?></td>
<td class="td_left"><?php echo get_text($lang['cl_description']); ?></td>
<td class="td_mng">
<button type="button" class="btn btn_02 btn_edit_lang"
data-lang_code="<?php echo get_text($lang['lang_code']); ?>"
data-cl_name="<?php echo get_text($lang['cl_name']); ?>"
data-cl_description="<?php echo get_text($lang['cl_description']); ?>">수정</button>
<a href="./lang_manager.php?mode=delete&amp;cl_id=<?php echo $lang['cl_id']; ?>&amp;target_table=<?php echo $target_table; ?>&amp;target_id=<?php echo $target_id; ?>&amp;token=<?php echo get_admin_token(); ?>"
class="btn btn_delete" onclick="return confirm('정말 삭제하시겠습니까?');">삭제</a>
</td>
</tr>
<?php endforeach; ?>
<?php else : ?>
<tr class="empty_table">
<td colspan="4">등록된 다국어 정보가 없습니다.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</section>
<script>
document.addEventListener('DOMContentLoaded', function() {
const editLangButtons = document.querySelectorAll('.btn_edit_lang');
const langForm = document.getElementById('flangform');
if (editLangButtons.length > 0 && langForm) {
editLangButtons.forEach(button => {
button.addEventListener('click', function() {
const data = this.dataset;
langForm.lang_code.value = data.lang_code;
langForm.cl_name.value = data.cl_name;
langForm.cl_description.value = data.cl_description;
langForm.scrollIntoView({ behavior: 'smooth' });
});
});
}
});
</script>
<?php
include_once(G5_ADMIN_PATH . '/admin.tail.php');
@@ -0,0 +1,194 @@
<?php
if (!defined('_GNUBOARD_')) exit;
/**
* SQL 파일을 기반으로 데이터베이스 스키마를 관리(생성/업데이트)하는 범용 클래스
*/
class SchemaManager
{
private $sql_file_path;
private $results;
/**
* 생성자
* @param string $sql_file_path install.sql 파일의 절대 경로
*/
public function __construct($sql_file_path)
{
if (!file_exists($sql_file_path)) {
throw new Exception($sql_file_path . ' 파일을 찾을 수 없습니다.');
}
$this->sql_file_path = $sql_file_path;
$this->results = [
'created' => [],
'existing' => [],
'updated' => [],
'failed' => [],
'errors' => [],
];
}
/**
* 스키마 설치/업데이트를 실행합니다.
*/
public function execute()
{
$sql_statements = $this->parse_sql_file();
foreach ($sql_statements as $stmt) {
// CREATE TABLE 문인지 확인
if (preg_match('/^CREATE\s+TABLE/i', $stmt)) {
$schema = $this->parse_create_table_sql($stmt);
if ($schema && !empty($schema['name'])) {
$this->process_table_schema($stmt, $schema);
}
} else {
// CREATE TABLE 문이 아닌 다른 SQL 문 (e.g. INSERT, UPDATE)
sql_query($stmt, false);
}
}
}
/**
* 처리 결과를 반환합니다.
* @return array
*/
public function get_results()
{
return $this->results;
}
/**
* 테이블 스키마를 처리합니다. (생성 또는 업데이트)
* @param string $create_sql 전체 CREATE TABLE 구문
* @param array $schema 파싱된 스키마 정보
*/
private function process_table_schema($create_sql, $schema)
{
$table_name = $schema['name'];
if ($this->table_exists($table_name)) {
// 테이블이 존재하면, 컬럼 비교 및 추가
$this->results['existing'][] = $table_name;
$this->update_table_columns($table_name, $schema['columns']);
} else {
// 테이블이 존재하지 않으면, 새로 생성
if (sql_query($create_sql, false)) {
$this->results['created'][] = $table_name;
} else {
$this->results['failed'][] = $table_name;
$this->results['errors'][] = "<strong>{$table_name} 테이블 생성 실패</strong>: " . sql_error();
}
}
}
/**
* 테이블의 컬럼 구조를 업데이트합니다.
* @param string $table_name
* @param array $target_columns .sql 파일에 정의된 컬럼 목록
*/
private function update_table_columns($table_name, $target_columns)
{
$current_columns = $this->get_current_columns($table_name);
$added_columns_in_table = [];
foreach ($target_columns as $col_name => $col_definition) {
// 현재 테이블에 해당 컬럼이 없으면 추가
if (!isset($current_columns[$col_name])) {
$alter_sql = "ALTER TABLE `{$table_name}` ADD COLUMN `{$col_name}` {$col_definition}";
if (sql_query($alter_sql, false)) {
$added_columns_in_table[] = $col_name;
} else {
$this->results['failed'][] = "{$table_name} (컬럼: {$col_name})";
$this->results['errors'][] = "<strong>{$table_name} 테이블에 '{$col_name}' 컬럼 추가 실패</strong>: " . sql_error();
}
}
}
if (!empty($added_columns_in_table)) {
$this->results['updated'][$table_name] = $added_columns_in_table;
}
}
/**
* SQL 파일을 읽고 각 구문으로 분리합니다.
* @return array
*/
private function parse_sql_file()
{
$sql = file_get_contents($this->sql_file_path);
// 주석 제거 (SQL 주석 '--' 와 C-style '/* ... */' 주석)
$sql = preg_replace('/--.*/', '', $sql);
$sql = preg_replace('!/\*.*?\*/!s', '', $sql);
$sql = trim($sql);
// 세미콜론(;)을 기준으로 쿼리 분리
return array_filter(array_map('trim', explode(';', $sql)));
}
/**
* 테이블 존재 여부를 확인합니다.
* @param string $table_name
* @return bool
*/
private function table_exists($table_name)
{
$res = sql_query("SHOW TABLES LIKE '{$table_name}'", false);
return sql_num_rows($res) > 0;
}
/**
* 현재 DB에 있는 테이블의 컬럼 목록을 가져옵니다.
* @param string $table_name
* @return array
*/
private function get_current_columns($table_name)
{
$res = sql_query("SHOW COLUMNS FROM `{$table_name}`", false);
$columns = [];
while ($row = sql_fetch_array($res)) {
$columns[$row['Field']] = true;
}
return $columns;
}
/**
* CREATE TABLE SQL 구문에서 테이블명과 컬럼 정의를 파싱합니다.
* @param string $query CREATE TABLE 구문
* @return array|null
*/
private function parse_create_table_sql($query)
{
$table_name = '';
if (preg_match('/CREATE\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?\s+`?(\w+)`?/i', $query, $matches)) {
$table_name = $matches[1];
} else {
return null;
}
// 괄호 안의 내용만 추출
$start = strpos($query, '(');
$end = strrpos($query, ')');
if ($start === false || $end === false) {
return ['name' => $table_name, 'columns' => []];
}
$content = substr($query, $start + 1, $end - $start - 1);
// 줄 단위로 분리
$lines = explode("\n", $content);
$columns = [];
foreach ($lines as $line) {
$line = trim($line, " \t\n\r\0\x0B,"); // 양쪽 공백과 마지막 쉼표 제거
// 컬럼 정의 라인인지 확인 (첫 단어가 `column_name` 형태)
if (preg_match('/^`(\w+)`\s+(.*)/i', $line, $match)) {
$col_name = $match[1];
$col_definition = $match[2];
$columns[$col_name] = $col_definition;
}
}
return ['name' => $table_name, 'columns' => $columns];
}
}
@@ -0,0 +1,133 @@
<?php
if (!defined('_GNUBOARD_')) exit;
/**
* UI 리소스 관리 클래스
* Singleton 패턴을 사용하여 인스턴스를 한 번만 생성하고,
* 불러온 데이터를 캐시하여 DB 조회를 최소화합니다.
*/
class UiManager
{
private static $instance = null;
private $resources = []; // 데이터를 캐시할 배열
// 외부에서 new 키워드로 인스턴스 생성을 막음
private function __construct() {}
/**
* 클래스의 유일한 인스턴스를 반환합니다.
* @return UiManager
*/
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 'LABEL' 타입의 UI 텍스트를 가져옵니다.
* @param string $resource_code 리소스 코드
* @param string $lang 언어 코드 (기본값: 'ko')
* @return string 라벨 텍스트 (없으면 resource_code 반환)
*/
public function get_label($resource_code, $lang = 'ko')
{
// 캐시 확인
if (isset($this->resources['labels'][$lang][$resource_code])) {
return $this->resources['labels'][$lang][$resource_code];
}
global $g5;
$resource_code_escaped = sql_real_escape_string($resource_code);
$lang_escaped = sql_real_escape_string($lang);
$sql = "SELECT B.cl_name
FROM {$g5['ui_manager_table']} AS A
LEFT JOIN {$g5['common_lang_table']} AS B
ON (A.um_id = B.target_id AND B.target_table = '{$g5['ui_manager_table']}' AND B.lang_code = '{$lang_escaped}')
WHERE A.resource_code = '{$resource_code_escaped}' AND A.resource_type = 'LABEL'";
$row = sql_fetch($sql);
$label_text = $row['cl_name'] ?? $resource_code;
// 결과 캐시
$this->resources['labels'][$lang][$resource_code] = $label_text;
return $label_text;
}
/**
* 'DATA' 타입의 옵션 목록을 배열로 가져옵니다.
* @param string $resource_code 리소스 코드
* @param string $lang 언어 코드 (기본값: 'ko')
* @return array 옵션 목록 배열
*/
public function get_data($resource_code, $lang = 'ko')
{
// 캐시 확인
if (isset($this->resources['data'][$lang][$resource_code])) {
return $this->resources['data'][$lang][$resource_code];
}
global $g5;
$resource_code_escaped = sql_real_escape_string($resource_code);
$lang_escaped = sql_real_escape_string($lang);
$sql_um = "SELECT um_id FROM {$g5['ui_manager_table']} WHERE resource_code = '{$resource_code_escaped}' AND resource_type = 'DATA'";
$um_row = sql_fetch($sql_um);
if (!isset($um_row['um_id'])) {
$this->resources['data'][$lang][$resource_code] = []; // 빈 결과도 캐시
return [];
}
$um_id = $um_row['um_id'];
$sql = "SELECT A.fc_id, A.parent_id, A.fc_key, A.fc_order, B.cl_name
FROM {$g5['form_category_table']} AS A
LEFT JOIN {$g5['common_lang_table']} AS B
ON (A.fc_id = B.target_id AND B.target_table = '{$g5['form_category_table']}' AND B.lang_code = '{$lang_escaped}')
WHERE A.um_id = '{$um_id}' AND A.is_used = 1 AND A.is_deleted = 0
ORDER BY A.fc_order, A.fc_id";
$result = sql_query($sql);
$options = [];
while ($row = sql_fetch_array($result)) {
$options[] = $row;
}
// 결과 캐시
$this->resources['data'][$lang][$resource_code] = $options;
return $options;
}
/**
* 'DATA' 타입 리소스를 사용하여 HTML <select> 태그를 생성합니다.
* @param string $resource_code 리소스 코드
* @param string $select_name <select> 태그의 name 속성
* @param string $selected_value 미리 선택될 옵션의 값(fc_key)
* @param string $attributes <select> 태그에 추가할 HTML 속성 (e.g., 'id="my-id" class="my-class"')
* @param string $lang 언어 코드 (기본값: 'ko')
* @return string 생성된 HTML <select> 태그
*/
public function render_select($resource_code, $select_name, $selected_value = '', $attributes = '', $lang = 'ko')
{
$options = $this->get_data($resource_code, $lang);
if (empty($options)) {
return "<select name=\"{$select_name}\" {$attributes}><option value=\"\">옵션 없음</option></select>";
}
$html = "<select name=\"{$select_name}\" {$attributes}>";
$html .= "<option value=\"\">선택</option>";
foreach ($options as $option) {
$selected = ($option['fc_key'] == $selected_value) ? ' selected' : '';
$html .= "<option value=\"" . htmlspecialchars($option['fc_key']) . "\"{$selected}>" . htmlspecialchars($option['cl_name']) . "</option>";
}
$html .= "</select>";
return $html;
}
}
+106
View File
@@ -0,0 +1,106 @@
<?php
if (!defined('_GNUBOARD_')) exit;
/**
* UI 리소스 관리자 클래스 인스턴스를 반환하는 헬퍼 함수
* @return UiManager
*/
function ui_manager() {
// 클래스가 아직 로드되지 않았다면 인스턴스 생성
if (!class_exists('UiManager')) {
// UiManager 클래스 정의
class UiManager
{
private static $instance = null;
private $resources = []; // 데이터를 캐시할 배열
private function __construct() {}
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function get_label($resource_code, $lang = 'ko')
{
if (isset($this->resources['labels'][$lang][$resource_code])) {
return $this->resources['labels'][$lang][$resource_code];
}
global $g5;
$resource_code_escaped = sql_real_escape_string($resource_code);
$lang_escaped = sql_real_escape_string($lang);
$sql = "SELECT B.cl_name
FROM {$g5['ui_manager_table']} AS A
LEFT JOIN {$g5['common_lang_table']} AS B
ON (A.um_id = B.target_id AND B.target_table = '{$g5['ui_manager_table']}' AND B.lang_code = '{$lang_escaped}')
WHERE A.resource_code = '{$resource_code_escaped}' AND A.resource_type = 'LABEL'";
$row = sql_fetch($sql);
$label_text = $row['cl_name'] ?? $resource_code;
$this->resources['labels'][$lang][$resource_code] = $label_text;
return $label_text;
}
public function get_data($resource_code, $lang = 'ko')
{
if (isset($this->resources['data'][$lang][$resource_code])) {
return $this->resources['data'][$lang][$resource_code];
}
global $g5;
$resource_code_escaped = sql_real_escape_string($resource_code);
$lang_escaped = sql_real_escape_string($lang);
$sql_um = "SELECT um_id FROM {$g5['ui_manager_table']} WHERE resource_code = '{$resource_code_escaped}' AND resource_type = 'DATA'";
$um_row = sql_fetch($sql_um);
if (!isset($um_row['um_id'])) {
$this->resources['data'][$lang][$resource_code] = [];
return [];
}
$um_id = $um_row['um_id'];
$sql = "SELECT A.fc_id, A.parent_id, A.fc_key, A.fc_order, B.cl_name
FROM {$g5['form_category_table']} AS A
LEFT JOIN {$g5['common_lang_table']} AS B
ON (A.fc_id = B.target_id AND B.target_table = '{$g5['form_category_table']}' AND B.lang_code = '{$lang_escaped}')
WHERE A.um_id = '{$um_id}' AND A.is_used = 1 AND A.is_deleted = 0
ORDER BY A.fc_order, A.fc_id";
$result = sql_query($sql);
$options = [];
while ($row = sql_fetch_array($result)) {
$options[] = $row;
}
$this->resources['data'][$lang][$resource_code] = $options;
return $options;
}
public function render_select($resource_code, $select_name, $selected_value = '', $attributes = '', $lang = 'ko')
{
$options = $this->get_data($resource_code, $lang);
if (empty($options)) {
return "<select name=\"".htmlspecialchars($select_name)."\" {$attributes}><option value=\"\">옵션 없음</option></select>";
}
$html = "<select name=\"".htmlspecialchars($select_name)."\" {$attributes}>";
$html .= "<option value=\"\">선택</option>";
foreach ($options as $option) {
$selected = ($option['fc_key'] == $selected_value) ? ' selected' : '';
$html .= "<option value=\"" . htmlspecialchars($option['fc_key']) . "\"{$selected}>" . htmlspecialchars($option['cl_name']) . "</option>";
}
$html .= "</select>";
return $html;
}
}
}
return UiManager::getInstance();
}
+174
View File
@@ -0,0 +1,174 @@
<?php
$sub_menu = '100350';
include_once('./_common.php');
$w = isset($_REQUEST['w']) ? substr(trim($_REQUEST['w']), 0, 1) : '';
$um_id = isset($_REQUEST['um_id']) ? (int)$_REQUEST['um_id'] : 0;
// ==================================================================
// 💡 [핵심] 폼 제출(수정) 처리 로직
// ==================================================================
if ($w == 'u' && $_SERVER['REQUEST_METHOD'] === 'POST') {
auth_check_menu($auth, $sub_menu, 'w');
check_admin_token();
if (!$um_id) {
alert('um_id 값이 없습니다.');
}
// 입력값 정리
$screen_code = trim($_POST['screen_code']);
$group_code = trim($_POST['group_code']);
$resource_code = trim($_POST['resource_code']);
$resource_type = trim($_POST['resource_type']);
$resource_desc = trim($_POST['resource_desc']);
$cl_name = isset($_POST['cl_name']) ? trim($_POST['cl_name']) : '';
// 유효성 검사
if (!$screen_code || !$group_code || !$resource_code || !$resource_type) {
alert('필수 항목을 모두 입력해주세요.');
}
if ($resource_type == 'LABEL' && !$cl_name) {
alert('UI 라벨 타입은 한국어 라벨명을 필수로 입력해야 합니다.');
}
// 1. g5_ui_manager 테이블 정보 업데이트
$sql = "UPDATE {$g5['ui_manager_table']}
SET screen_code = '{$screen_code}',
group_code = '{$group_code}',
resource_code = '{$resource_code}',
resource_type = '{$resource_type}',
resource_desc = '{$resource_desc}',
updated_at = '".G5_TIME_YMDHIS."',
updated_by = '{$member['mb_id']}'
WHERE um_id = '{$um_id}'";
sql_query($sql);
// 2. 리소스 타입이 'LABEL'인 경우, g5_common_lang 테이블 정보 업데이트 (없으면 생성)
if ($resource_type == 'LABEL') {
$sql = "SELECT cl_id FROM {$g5['common_lang_table']} WHERE target_table = '{$g5['ui_manager_table']}' AND target_id = '{$um_id}' AND lang_code = 'ko'";
$lang_row = sql_fetch($sql);
if (isset($lang_row['cl_id'])) { // 기존 데이터가 있으면 UPDATE
$sql = "UPDATE {$g5['common_lang_table']}
SET cl_name = '{$cl_name}',
updated_at = '".G5_TIME_YMDHIS."',
updated_by = '{$member['mb_id']}'
WHERE cl_id = '{$lang_row['cl_id']}'";
} else { // 기존 데이터가 없으면 INSERT
$sql = "INSERT INTO {$g5['common_lang_table']}
SET target_table = '{$g5['ui_manager_table']}',
target_id = '{$um_id}',
lang_code = 'ko',
cl_name = '{$cl_name}',
updated_at = '".G5_TIME_YMDHIS."',
updated_by = '{$member['mb_id']}'";
}
sql_query($sql);
}
goto_url('./ui_manager_list.php');
}
// ==================================================================
auth_check_menu($auth, $sub_menu, 'r');
// 수정할 리소스 데이터 조회
if ($w == 'u') {
if (!$um_id) {
alert('um_id 값이 없습니다.', './ui_manager_list.php');
}
$sql = "SELECT A.*, B.cl_name
FROM {$g5['ui_manager_table']} AS A
LEFT JOIN {$g5['common_lang_table']} AS B
ON (A.um_id = B.target_id AND B.target_table = '{$g5['ui_manager_table']}' AND B.lang_code = 'ko')
WHERE A.um_id = '{$um_id}'";
$ui_resource = sql_fetch($sql);
if (!isset($ui_resource['um_id'])) {
alert('존재하지 않는 리소스입니다.', './ui_manager_list.php');
}
} else {
alert('w 값이 올바르지 않습니다.', './ui_manager_list.php');
}
$g5['title'] = 'UI 리소스 수정';
include_once(G5_ADMIN_PATH . '/admin.head.php');
add_stylesheet('<link rel="stylesheet" href="' . G5_ADMIN_URL . '/code_manager/css/code_manager.css?ver=1.1">', 0);
?>
<section id="code_manager_form">
<h2 class="h2_frm">UI 리소스 수정</h2>
<form name="fresourceform" id="fresourceform" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="post">
<input type="hidden" name="w" value="u">
<input type="hidden" name="um_id" value="<?php echo $um_id; ?>">
<input type="hidden" name="token" value="<?php echo get_admin_token(); ?>">
<div class="tbl_frm01 tbl_wrap">
<table>
<caption>UI 리소스 수정 폼</caption>
<colgroup>
<col class="grid_4">
<col>
</colgroup>
<tbody>
<tr>
<th scope="row"><label for="screen_code">화면 코드</label></th>
<td>
<input type="text" name="screen_code" id="screen_code" required class="required frm_input" size="30" value="<?php echo get_text($ui_resource['screen_code']); ?>">
<span class="frm_info">리소스가 사용될 화면의 고유 코드 (예: order_form, member_join)</span>
</td>
</tr>
<tr>
<th scope="row"><label for="group_code">그룹 코드</label></th>
<td>
<input type="text" name="group_code" id="group_code" required class="required frm_input" size="30" value="<?php echo get_text($ui_resource['group_code']); ?>">
<span class="frm_info">화면 내에서 리소스를 묶어줄 그룹 코드 (예: address_info, common_options)</span>
</td>
</tr>
<tr>
<th scope="row">리소스 타입</th>
<td>
<label><input type="radio" name="resource_type" value="LABEL" <?php echo get_checked($ui_resource['resource_type'], 'LABEL'); ?>> UI 라벨 (단일 텍스트)</label>
<label><input type="radio" name="resource_type" value="DATA" <?php echo get_checked($ui_resource['resource_type'], 'DATA'); ?>> 데이터 (선택 옵션)</label>
</td>
</tr>
<tr class="resource-type-field" id="label-field">
<th scope="row"><label for="cl_name">한국어 라벨명</label></th>
<td>
<input type="text" name="cl_name" id="cl_name" class="frm_input" size="50" value="<?php echo get_text($ui_resource['cl_name']); ?>">
<span class="frm_info">화면에 표시될 실제 텍스트 (예: 집의 유형, 창호 재질)</span>
</td>
</tr>
<tr>
<th scope="row"><label for="resource_code">리소스 코드</label></th>
<td>
<input type="text" name="resource_code" id="resource_code" required class="required frm_input" size="30" value="<?php echo get_text($ui_resource['resource_code']); ?>">
<span class="frm_info">개발자가 이 리소스를 호출할 때 사용할 고유 코드 (예: house_type_label, house_type_data)</span>
</td>
</tr>
<tr>
<th scope="row"><label for="resource_desc">설명</label></th>
<td>
<input type="text" name="resource_desc" id="resource_desc" class="frm_input" size="80" value="<?php echo get_text($ui_resource['resource_desc']); ?>">
<span class="frm_info">이 리소스의 용도에 대한 설명 (관리자 참고용)</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="btn_confirm01 btn_confirm">
<a href="./ui_manager_list.php" class="btn_cancel btn">목록으로</a>
<input type="submit" value="수정" class="btn_submit btn" accesskey="s">
</div>
</form>
</section>
<script src="<?php echo G5_ADMIN_URL; ?>/code_manager/js/ui_manager.js?ver=1.2"></script>
<?php
include_once(G5_ADMIN_PATH . '/admin.tail.php');
+351
View File
@@ -0,0 +1,351 @@
<?php
$sub_menu = '700100';
include_once('./_common.php');
// ==================================================================
// 삭제 처리 로직
// ==================================================================
if (isset($_GET['mode']) && $_GET['mode'] == 'delete') {
auth_check_menu($auth, $sub_menu, 'd');
check_admin_token();
$um_id = isset($_GET['um_id']) ? (int)$_GET['um_id'] : 0;
if (!$um_id) {
alert('um_id 값이 없습니다.');
}
$sql = "SELECT resource_type FROM {$g5['ui_manager_table']} WHERE um_id = '{$um_id}'";
$row = sql_fetch($sql);
if (isset($row['resource_type'])) {
if ($row['resource_type'] == 'DATA') {
$sql_delete_lang = "DELETE FROM {$g5['common_lang_table']} WHERE target_table = '{$g5['form_category_table']}' AND target_id IN (SELECT fc_id FROM {$g5['form_category_table']} WHERE um_id = '{$um_id}')";
sql_query($sql_delete_lang);
$sql_delete_cat = "DELETE FROM {$g5['form_category_table']} WHERE um_id = '{$um_id}'";
sql_query($sql_delete_cat);
}
$sql_delete_main_lang = "DELETE FROM {$g5['common_lang_table']} WHERE target_table = '{$g5['ui_manager_table']}' AND target_id = '{$um_id}'";
sql_query($sql_delete_main_lang);
$sql_delete_main = "DELETE FROM {$g5['ui_manager_table']} WHERE um_id = '{$um_id}'";
sql_query($sql_delete_main);
}
// 삭제 후 현재 페이지와 검색 조건을 유지하도록 $qstr 사용
goto_url('./ui_manager_list.php?'.$qstr);
}
// ==================================================================
// 💡 [핵심 수정 1] 폼 제출 처리 로직 (신규 등록)
// ==================================================================
// GnuBoard 표준에 따라 'w' 값이 비어있을 때를 신규 등록으로 처리합니다.
if (empty($w) && $_SERVER['REQUEST_METHOD'] === 'POST') {
auth_check_menu($auth, $sub_menu, 'w');
check_admin_token();
// 입력값 정리
$screen_code = trim($_POST['screen_code']);
$group_code = trim($_POST['group_code']);
$resource_code = trim($_POST['resource_code']);
$resource_type = trim($_POST['resource_type']);
$resource_desc = trim($_POST['resource_desc']);
$cl_name = isset($_POST['cl_name']) ? trim($_POST['cl_name']) : '';
// 유효성 검사
if (!$screen_code || !$group_code || !$resource_code || !$resource_type) {
alert('필수 항목을 모두 입력해주세요.');
}
if ($resource_type == 'LABEL' && !$cl_name) {
alert('UI 라벨 타입은 한국어 라벨명을 필수로 입력해야 합니다.');
}
// 중복 체크
$sql = "SELECT COUNT(*) as cnt FROM {$g5['ui_manager_table']} WHERE screen_code = '{$screen_code}' AND group_code = '{$group_code}' AND resource_code = '{$resource_code}'";
$row = sql_fetch($sql);
if ($row['cnt']) {
alert('이미 동일한 화면/그룹/리소스 코드로 등록된 리소스가 존재합니다.');
}
// 1. g5_ui_manager 테이블에 정보 INSERT
$sql = "INSERT INTO {$g5['ui_manager_table']}
SET screen_code = '{$screen_code}',
group_code = '{$group_code}',
resource_code = '{$resource_code}',
resource_type = '{$resource_type}',
resource_desc = '{$resource_desc}',
is_used = '1',
created_at = '".G5_TIME_YMDHIS."',
created_by = '{$member['mb_id']}',
updated_at = '".G5_TIME_YMDHIS."',
updated_by = '{$member['mb_id']}'";
sql_query($sql);
$um_id = sql_insert_id();
// 2. 리소스 타입이 'LABEL'인 경우, g5_common_lang 테이블에 정보 INSERT
if ($resource_type == 'LABEL' && $cl_name) {
$sql = "INSERT INTO {$g5['common_lang_table']}
SET target_table = '{$g5['ui_manager_table']}',
target_id = '{$um_id}',
lang_code = 'ko',
cl_name = '{$cl_name}',
updated_at = '".G5_TIME_YMDHIS."',
updated_by = '{$member['mb_id']}'";
sql_query($sql);
}
goto_url('./ui_manager_list.php');
}
// ==================================================================
auth_check_menu($auth, $sub_menu, 'r');
// 검색 및 페이징 변수 처리
$sfl = isset($_GET['sfl']) ? trim($_GET['sfl']) : '';
$stx = isset($_GET['stx']) ? trim($_GET['stx']) : '';
$page_rows = isset($_GET['page_rows']) && (int)$_GET['page_rows'] > 0 ? (int)$_GET['page_rows'] : 15;
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
if ($page < 1) $page = 1;
// 페이지당 목록 수($page_rows)를 $qstr에 추가하여 다른 링크에서도 유지되도록 함
$qstr .= ($qstr ? '&amp;' : '') . 'page_rows=' . urlencode($page_rows);
$sql_search = "";
if ($sfl && $stx) {
$stx_escaped = sql_real_escape_string($stx);
$sql_search = " WHERE ";
switch ($sfl) {
case 'screen_code':
case 'group_code':
case 'resource_code':
case 'resource_desc':
$sql_search .= "A.{$sfl} LIKE '%{$stx_escaped}%'";
break;
case 'cl_name':
$sql_search .= "B.cl_name LIKE '%{$stx_escaped}%'";
break;
default: // '전체' 검색
$sql_search .= " ( A.screen_code LIKE '%{$stx_escaped}%' OR A.group_code LIKE '%{$stx_escaped}%' OR A.resource_code LIKE '%{$stx_escaped}%' OR A.resource_desc LIKE '%{$stx_escaped}%' OR B.cl_name LIKE '%{$stx_escaped}%' ) ";
break;
}
}
// 1. 검색 조건에 맞는 모든 리소스를 한 번에 가져옴
$sql = "SELECT A.*, B.cl_name
FROM {$g5['ui_manager_table']} AS A
LEFT JOIN {$g5['common_lang_table']} AS B
ON (A.um_id = B.target_id AND B.target_table = '{$g5['ui_manager_table']}' AND B.lang_code = 'ko')
{$sql_search}
ORDER BY A.screen_code, A.group_code, A.resource_code";
$result = sql_query($sql);
// 2. PHP에서 화면별로 그룹화
$all_grouped_resources = [];
$total_resource_count = 0;
while ($row = sql_fetch_array($result)) {
$all_grouped_resources[$row['screen_code']][] = $row;
$total_resource_count++;
}
// 3. 그룹화된 결과를 기준으로 페이징 처리
$total_count = count($all_grouped_resources); // 전체 '화면' 수
$total_page = ceil($total_count / $page_rows);
$from_record = ($page - 1) * $page_rows;
// 현재 페이지에 해당하는 그룹만 잘라냄
$paged_groups = array_slice($all_grouped_resources, $from_record, $page_rows, true);
$g5['title'] = 'UI 리소스 관리';
include_once(G5_ADMIN_PATH . '/admin.head.php');
add_stylesheet('<link rel="stylesheet" href="' . G5_ADMIN_URL . '/code_manager/css/code_manager.css?ver=1.3">', 0);
?>
<div class="local_desc01 local_desc">
<p>
웹사이트의 모든 화면에 사용되는 텍스트(라벨)와 선택 옵션(데이터)을 체계적으로 관리합니다.<br>
'화면 코드' 별로 그룹화되어 표시되며, 각 그룹을 클릭하여 내용을 확인하거나 수정할 수 있습니다.
</p>
</div>
<!-- 검색 폼 -->
<section id="resource_search_form">
<h2 class="h2_frm">리소스 검색</h2>
<form name="fsearch" id="fsearch" method="get">
<div class="search-form-inner">
<label for="page_rows" class="sound_only">페이지당 개수</label>
<select name="page_rows" id="page_rows" class="frm_input">
<option value="15" <?php echo get_selected($page_rows, 15); ?>>15개씩</option>
<option value="30" <?php echo get_selected($page_rows, 30); ?>>30개씩</option>
<option value="50" <?php echo get_selected($page_rows, 50); ?>>50개씩</option>
<option value="100" <?php echo get_selected($page_rows, 100); ?>>100개씩</option>
</select>
<label for="sfl" class="sound_only">검색대상</label>
<select name="sfl" id="sfl">
<option value="all" <?php echo get_selected($sfl, 'all'); ?>>전체</option>
<option value="screen_code" <?php echo get_selected($sfl, 'screen_code'); ?>>화면 코드</option>
<option value="group_code" <?php echo get_selected($sfl, 'group_code'); ?>>그룹 코드</option>
<option value="resource_code" <?php echo get_selected($sfl, 'resource_code'); ?>>리소스 코드</option>
<option value="resource_desc" <?php echo get_selected($sfl, 'resource_desc'); ?>>설명</option>
<option value="cl_name" <?php echo get_selected($sfl, 'cl_name'); ?>>라벨명</option>
</select>
<label for="stx" class="sound_only">검색어</label>
<input type="text" name="stx" value="<?php echo get_text($stx) ?>" id="stx" class="frm_input" size="30">
<input type="submit" value="검색" class="btn_submit">
</div>
</form>
</section>
<section id="code_manager">
<div class="code-manager-header">
<h2 class="code-manager-title">UI 리소스 목록 (총 <?php echo number_format($total_count); ?>개 화면)</h2>
<div class="code-manager-actions">
<button type="button" id="add-resource-btn" class="btn btn_01">
<i class="fa fa-plus" aria-hidden="true"></i> 새 리소스 추가
</button>
</div>
</div>
<!-- 새 리소스 추가 폼 -->
<div id="resource-form-container" style="display: none;">
<form name="fresourceform" id="fresourceform" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="post">
<!-- 💡 [핵심 수정 2] 신규 등록이므로 w 값을 비워둡니다. -->
<input type="hidden" name="w" value="">
<input type="hidden" name="token" value="<?php echo get_admin_token(); ?>">
<div class="tbl_frm01 tbl_wrap">
<table>
<caption>UI 리소스 추가 폼</caption>
<colgroup>
<col class="grid_4">
<col>
</colgroup>
<tbody>
<tr>
<th scope="row"><label for="screen_code">화면 코드</label></th>
<td>
<input type="text" name="screen_code" id="screen_code" required class="required frm_input" size="30">
<span class="frm_info">리소스가 사용될 화면의 고유 코드 (예: order_form, member_join)</span>
</td>
</tr>
<tr>
<th scope="row"><label for="group_code">그룹 코드</label></th>
<td>
<input type="text" name="group_code" id="group_code" required class="required frm_input" size="30">
<span class="frm_info">화면 내에서 리소스를 묶어줄 그룹 코드 (예: address_info, common_options)</span>
</td>
</tr>
<tr>
<th scope="row">리소스 타입</th>
<td>
<label><input type="radio" name="resource_type" value="LABEL" checked> UI 라벨 (단일 텍스트)</label>
<label><input type="radio" name="resource_type" value="DATA"> 데이터 (선택 옵션)</label>
</td>
</tr>
<tr class="resource-type-field" id="label-field">
<th scope="row"><label for="cl_name">한국어 라벨명</label></th>
<td>
<input type="text" name="cl_name" id="cl_name" class="frm_input" size="50">
<span class="frm_info">화면에 표시될 실제 텍스트 (예: 집의 유형, 창호 재질)</span>
</td>
</tr>
<tr>
<th scope="row"><label for="resource_code">리소스 코드</label></th>
<td>
<input type="text" name="resource_code" id="resource_code" required class="required frm_input" size="30">
<span class="frm_info">개발자가 이 리소스를 호출할 때 사용할 고유 코드 (예: house_type_label, house_type_data)</span>
</td>
</tr>
<tr>
<th scope="row"><label for="resource_desc">설명</label></th>
<td>
<input type="text" name="resource_desc" id="resource_desc" class="frm_input" size="80">
<span class="frm_info">이 리소스의 용도에 대한 설명 (관리자 참고용)</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="btn_confirm01 btn_confirm">
<button type="button" id="cancel-resource-btn" class="btn_cancel btn">취소</button>
<input type="submit" value="리소스 등록" class="btn_submit btn" accesskey="s">
</div>
</form>
</div>
<!-- 아코디언 형태의 리소스 목록 -->
<div id="resource-list-accordion">
<?php if (count($paged_groups) == 0) : ?>
<div class="empty_list">표시할 리소스가 없습니다.</div>
<?php else : ?>
<?php foreach ($paged_groups as $screen_code => $resources) : ?>
<div class="accordion-item">
<div class="accordion-header">
<span class="screen-title"><i class="fa fa-desktop"></i> 화면: <strong><?php echo get_text($screen_code); ?></strong></span>
<span class="resource-count"><?php echo count($resources); ?>개 리소스</span>
<i class="fa fa-chevron-down accordion-icon"></i>
</div>
<div class="accordion-content">
<div class="tbl_head01 tbl_wrap">
<table>
<colgroup>
<col style="width: 15%;">
<col style="width: 20%;">
<col style="width: 10%;">
<col>
<col style="width: 240px;">
</colgroup>
<thead>
<tr>
<th scope="col">그룹 코드</th>
<th scope="col">리소스 코드</th>
<th scope="col">타입</th>
<th scope="col">설명</th>
<th scope="col">관리</th>
</tr>
</thead>
<tbody>
<?php foreach ($resources as $res) : ?>
<tr>
<td><?php echo get_text($res['group_code']); ?></td>
<td><?php echo get_text($res['resource_code']); ?></td>
<td>
<?php if ($res['resource_type'] == 'LABEL'): ?>
<span class="res-type-label">라벨</span>
<?php else: ?>
<span class="res-type-data">데이터</span>
<?php endif; ?>
</td>
<td class="td_left"><?php echo get_text($res['resource_desc']); ?></td>
<td class="td_mng td_mng_s">
<?php if ($res['resource_type'] == 'DATA') : ?>
<a href="./category_list.php?um_id=<?php echo $res['um_id']; ?>" class="btn btn_03">옵션 관리</a>
<?php endif; ?>
<a href="./ui_manager_form.php?w=u&amp;um_id=<?php echo $res['um_id']; ?>&amp;<?php echo $qstr; ?>" class="btn btn_02">수정</a>
<a href="./lang_manager.php?target_table=<?php echo $g5['ui_manager_table']; ?>&amp;target_id=<?php echo $res['um_id']; ?>" class="btn btn_01">다국어</a>
<button type="button" class="btn btn_delete btn_delete_resource" data-um_id="<?php echo $res['um_id']; ?>">삭제</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<!-- 페이징 링크 출력 -->
<div class="pagination_wrap">
<?php
$paging_url = $_SERVER['SCRIPT_NAME'].'?'.$qstr;
echo get_paging(G5_IS_MOBILE ? $config['cf_mobile_pages'] : $config['cf_write_pages'], $page, $total_page, $paging_url);
?>
</div>
</section>
<script>
var g5_admin_token = "<?php echo get_admin_token(); ?>";
</script>
<script src="<?php echo G5_ADMIN_URL; ?>/code_manager/js/ui_manager.js?ver=1.3"></script>
<?php
include_once(G5_ADMIN_PATH . '/admin.tail.php');