Merge pull request #384 from gnuboard/promote/v5.6.37

release: 그누보드 5.6.37 배포
This commit is contained in:
섭웨이b
2026-09-09 16:59:48 +09:00
committed by GitHub
177 changed files with 3947 additions and 4023 deletions
+3
View File
@@ -1,3 +1,6 @@
.gitattributes export-ignore
.gitignore export-ignore
*.php whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
/tests export-ignore
/bin export-ignore
+2
View File
@@ -2,6 +2,8 @@
/.htaccess
data/
test/
/tests/
/bin/
sirgle/
test.php
*.key
+7
View File
@@ -65,8 +65,15 @@ cd gnuboard5
> 기존 사이트에 재설치하면 데이터가 삭제될 수 있습니다. 진행하기 전에 파일과 데이터베이스를 백업하십시오.
### 기존 설치 업데이트
소스 코드를 업데이트한 뒤 데이터베이스 변경이 포함된 버전은 관리자 메뉴의 **환경설정 → DB업그레이드**에서 변경 내용을 확인하고 명시적으로 실행합니다.
운영 절차와 마이그레이션 작성 규칙은 [데이터베이스 마이그레이션 문서](docs/database-migrations.md)를 확인하십시오.
## 문서 및 지원
- [5.6.37 SIRK 전용 카카오페이 연동 종료 및 기존 거래 지원](docs/sirk-kakaopay-retirement.md)
- [그누보드5 공식 페이지](https://sir.kr/main/g5/)
- [그누보드5 공식 매뉴얼](https://sir.kr/manuals/g5/1)
- [그누보드5 Q&A](https://sir.kr/questions?s_tag=%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C5)
+24
View File
@@ -478,6 +478,30 @@ function check_log_folder($log_path, $is_delete = true)
}
}
// 회원 본인확인 식별값과 인증 이력만 정리한다. 호출부에서 관리자 권한을 검증한다.
function admin_clear_member_certification($mb_id)
{
global $g5;
$esc_mb_id = sql_real_escape_string($mb_id);
$history_table = isset($g5['member_cert_history_table']) ? $g5['member_cert_history_table'] : G5_TABLE_PREFIX.'member_cert_history';
// 일반 회원정보(이름·연락처)와 이메일 인증 상태는 유지한다.
if (!sql_query(" update {$g5['member_table']}
set mb_certify = '', mb_adult = 0, mb_dupinfo = '', mb_birth = '', mb_sex = ''
where mb_id = '{$esc_mb_id}' ", false)) {
return false;
}
if (!sql_query(" delete from {$history_table} where mb_id = '{$esc_mb_id}' ", false)) {
return false;
}
if (!sql_query(" delete from {$g5['cert_history_table']} where mb_id = '{$esc_mb_id}' ", false)) {
return false;
}
return true;
}
// POST로 넘어온 토큰과 세션에 저장된 토큰 비교
function check_admin_token()
{
+1 -62
View File
@@ -13,81 +13,20 @@ if (!$row['cnt']) {
$html_title = '게시판';
if (!isset($board['bo_device'])) {
// 게시판 사용 필드 추가
// both : pc, mobile 둘다 사용
// pc : pc 전용 사용
// mobile : mobile 전용 사용
// none : 사용 안함
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_device` ENUM( 'both', 'pc', 'mobile' ) NOT NULL DEFAULT 'both' AFTER `bo_subject` ", false);
}
if (!isset($board['bo_mobile_skin'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_mobile_skin` VARCHAR(255) NOT NULL DEFAULT '' AFTER `bo_skin` ", false);
}
if (!isset($board['bo_gallery_width'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_gallery_width` INT NOT NULL AFTER `bo_gallery_cols`, ADD `bo_gallery_height` INT NOT NULL DEFAULT '0' AFTER `bo_gallery_width`, ADD `bo_mobile_gallery_width` INT NOT NULL DEFAULT '0' AFTER `bo_gallery_height`, ADD `bo_mobile_gallery_height` INT NOT NULL DEFAULT '0' AFTER `bo_mobile_gallery_width` ", false);
}
if (!isset($board['bo_mobile_subject_len'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_mobile_subject_len` INT(11) NOT NULL DEFAULT '0' AFTER `bo_subject_len` ", false);
}
if (!isset($board['bo_mobile_page_rows'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_mobile_page_rows` INT(11) NOT NULL DEFAULT '0' AFTER `bo_page_rows` ", false);
}
if (!isset($board['bo_mobile_content_head'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_mobile_content_head` TEXT NOT NULL AFTER `bo_content_head`, ADD `bo_mobile_content_tail` TEXT NOT NULL AFTER `bo_content_tail`", false);
}
if (!isset($board['bo_use_cert'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_use_cert` ENUM('','cert','adult') NOT NULL DEFAULT '' AFTER `bo_use_email` ", false);
}
if (!isset($board['bo_use_sns'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_use_sns` TINYINT NOT NULL DEFAULT '0' AFTER `bo_use_cert` ", false);
$result = sql_query(" select bo_table from `{$g5['board_table']}` ");
for ($i=0; $row=sql_fetch_array($result); $i++) {
sql_query(
" ALTER TABLE `{$g5['write_prefix']}{$row['bo_table']}`
ADD `wr_facebook_user` VARCHAR(255) NOT NULL DEFAULT '' AFTER `wr_ip`,
ADD `wr_twitter_user` VARCHAR(255) NOT NULL DEFAULT '' AFTER `wr_facebook_user` ", false
);
}
}
$sql = " SHOW COLUMNS FROM `{$g5['board_table']}` LIKE 'bo_use_cert' ";
$row = sql_fetch($sql);
if (strpos($row['Type'], 'hp-') === false) {
sql_query(" ALTER TABLE `{$g5['board_table']}` CHANGE `bo_use_cert` `bo_use_cert` ENUM('','cert','adult','hp-cert','hp-adult') NOT NULL DEFAULT '' ", false);
}
if (!isset($board['bo_use_list_file'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_use_list_file` TINYINT NOT NULL DEFAULT '0' AFTER `bo_use_list_view` ", false);
$result = sql_query(" select bo_table from `{$g5['board_table']}` ");
for ($i=0; $row=sql_fetch_array($result); $i++) {
sql_query(
" ALTER TABLE `{$g5['write_prefix']}{$row['bo_table']}`
ADD `wr_file` TINYINT NOT NULL DEFAULT '0' AFTER `wr_datetime` ", false
);
}
}
if (!isset($board['bo_mobile_subject'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_mobile_subject` VARCHAR(255) NOT NULL DEFAULT '' AFTER `bo_subject` ", false);
}
if (!isset($board['bo_use_captcha'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_use_captcha` TINYINT NOT NULL DEFAULT '0' AFTER `bo_use_sns` ", false);
}
if (!isset($board['bo_select_editor'])) {
sql_query(" ALTER TABLE `{$g5['board_table']}` ADD `bo_select_editor` VARCHAR(50) NOT NULL DEFAULT '' AFTER `bo_use_dhtml_editor` ", false);
}
$board_default = array(
'bo_mobile_subject'=>'',
@@ -1500,4 +1439,4 @@ function fboardform_submit(f)
</script>
<?php
require_once './admin.tail.php';
require_once './admin.tail.php';
-3
View File
@@ -31,9 +31,6 @@ if ($w == '') {
alert('제대로 된 값이 넘어오지 않았습니다.');
}
if (!isset($group['gr_device'])) {
sql_query(" ALTER TABLE `{$g5['group_table']}` ADD `gr_device` ENUM('both','pc','mobile') NOT NULL DEFAULT 'both' AFTER `gr_subject` ", false);
}
// 접근회원수
$sql1 = " select count(*) as cnt from {$g5['group_member_table']} where gr_id = '{$gr_id}' ";
-8
View File
@@ -4,14 +4,6 @@ require_once './_common.php';
auth_check_menu($auth, $sub_menu, 'r');
if (!isset($group['gr_device'])) {
// 게시판 그룹 사용 필드 추가
// both : pc, mobile 둘다 사용
// pc : pc 전용 사용
// mobile : mobile 전용 사용
// none : 사용 안함
sql_query(" ALTER TABLE `{$g5['group_table']}` ADD `gr_device` ENUM( 'both', 'pc', 'mobile' ) NOT NULL DEFAULT 'both' AFTER `gr_subject` ", false);
}
$sql_common = " from {$g5['group_table']} ";
-1
View File
@@ -2,7 +2,6 @@
$sub_menu = "300200";
require_once './_common.php';
sql_query(" ALTER TABLE {$g5['group_member_table']} CHANGE `gm_id` `gm_id` INT( 11 ) DEFAULT '0' NOT NULL AUTO_INCREMENT ", false);
if ($w == '') {
auth_check_menu($auth, $sub_menu, 'w');
+9 -433
View File
@@ -12,440 +12,9 @@ if ($is_admin != 'super') {
$sql = " select * from {$g5['config_table']} limit 1";
$config = sql_fetch($sql);
if (!isset($config['cf_add_script'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_add_script` TEXT NOT NULL AFTER `cf_admin_email_name` ",
true
);
if (!isset($config['cf_email_certify_minutes'])) {
alert('DB 업그레이드가 필요합니다.', G5_ADMIN_URL . '/dbupgrade.php');
}
if (!isset($config['cf_mobile_new_skin'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_mobile_new_skin` VARCHAR(255) NOT NULL AFTER `cf_memo_send_point`,
ADD `cf_mobile_search_skin` VARCHAR(255) NOT NULL AFTER `cf_mobile_new_skin`,
ADD `cf_mobile_connect_skin` VARCHAR(255) NOT NULL AFTER `cf_mobile_search_skin`,
ADD `cf_mobile_member_skin` VARCHAR(255) NOT NULL AFTER `cf_mobile_connect_skin` ",
true
);
}
if (isset($config['cf_gcaptcha_mp3'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
CHANGE `cf_gcaptcha_mp3` `cf_captcha_mp3` VARCHAR(255) NOT NULL DEFAULT '' ",
true
);
} elseif (!isset($config['cf_captcha_mp3'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_captcha_mp3` VARCHAR(255) NOT NULL DEFAULT '' AFTER `cf_mobile_member_skin` ",
true
);
}
if (!isset($config['cf_editor'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_editor` VARCHAR(255) NOT NULL DEFAULT '' AFTER `cf_captcha_mp3` ",
true
);
}
if (!isset($config['cf_googl_shorturl_apikey'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_googl_shorturl_apikey` VARCHAR(255) NOT NULL DEFAULT '' AFTER `cf_captcha_mp3` ",
true
);
}
if (!isset($config['cf_mobile_pages'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_mobile_pages` INT(11) NOT NULL DEFAULT '0' AFTER `cf_write_pages` ",
true
);
sql_query(" UPDATE `{$g5['config_table']}` SET cf_mobile_pages = '5' ", true);
}
if (!isset($config['cf_facebook_appid'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_facebook_appid` VARCHAR(255) NOT NULL AFTER `cf_googl_shorturl_apikey`,
ADD `cf_facebook_secret` VARCHAR(255) NOT NULL AFTER `cf_facebook_appid`,
ADD `cf_twitter_key` VARCHAR(255) NOT NULL AFTER `cf_facebook_secret`,
ADD `cf_twitter_secret` VARCHAR(255) NOT NULL AFTER `cf_twitter_key` ",
true
);
}
// uniqid 테이블이 없을 경우 생성
if (!sql_query(" DESC {$g5['uniqid_table']} ", false)) {
sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['uniqid_table']}` (
`uq_id` bigint(20) unsigned NOT NULL,
`uq_ip` varchar(255) NOT NULL,
PRIMARY KEY (`uq_id`)
) ",
false
);
}
if (!sql_query(" SELECT uq_ip from {$g5['uniqid_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE {$g5['uniqid_table']} ADD `uq_ip` VARCHAR(255) NOT NULL ");
}
// 임시저장 테이블이 없을 경우 생성
if (!sql_query(" DESC {$g5['autosave_table']} ", false)) {
sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['autosave_table']}` (
`as_id` int(11) NOT NULL AUTO_INCREMENT,
`mb_id` varchar(20) NOT NULL,
`as_uid` bigint(20) unsigned NOT NULL,
`as_subject` varchar(255) NOT NULL,
`as_content` text NOT NULL,
`as_datetime` datetime NOT NULL,
PRIMARY KEY (`as_id`),
UNIQUE KEY `as_uid` (`as_uid`),
KEY `mb_id` (`mb_id`)
) ",
false
);
}
if (!isset($config['cf_admin_email'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_admin_email` VARCHAR(255) NOT NULL AFTER `cf_admin` ",
true
);
}
if (!isset($config['cf_admin_email_name'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_admin_email_name` VARCHAR(255) NOT NULL AFTER `cf_admin_email` ",
true
);
}
if (!isset($config['cf_cert_use'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_cert_use` TINYINT(4) NOT NULL DEFAULT '0' AFTER `cf_editor`,
ADD `cf_cert_ipin` VARCHAR(255) NOT NULL DEFAULT '' AFTER `cf_cert_use`,
ADD `cf_cert_hp` VARCHAR(255) NOT NULL DEFAULT '' AFTER `cf_cert_ipin`,
ADD `cf_cert_kcb_cd` VARCHAR(255) NOT NULL DEFAULT '' AFTER `cf_cert_hp`,
ADD `cf_cert_kcp_cd` VARCHAR(255) NOT NULL DEFAULT '' AFTER `cf_cert_kcb_cd`,
ADD `cf_cert_limit` INT(11) NOT NULL DEFAULT '0' AFTER `cf_cert_kcp_cd` ",
true
);
sql_query(
" ALTER TABLE `{$g5['member_table']}`
CHANGE `mb_hp_certify` `mb_certify` VARCHAR(20) NOT NULL DEFAULT '' ",
true
);
sql_query(" update {$g5['member_table']} set mb_certify = 'hp' where mb_certify = '1' ");
sql_query(" update {$g5['member_table']} set mb_certify = '' where mb_certify = '0' ");
sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['cert_history_table']}` (
`cr_id` int(11) NOT NULL auto_increment,
`mb_id` varchar(255) NOT NULL DEFAULT '',
`cr_company` varchar(255) NOT NULL DEFAULT '',
`cr_method` varchar(255) NOT NULL DEFAULT '',
`cr_ip` varchar(255) NOT NULL DEFAULT '',
`cr_date` date NOT NULL DEFAULT '0000-00-00',
`cr_time` time NOT NULL DEFAULT '00:00:00',
PRIMARY KEY (`cr_id`),
KEY `mb_id` (`mb_id`)
)",
true
);
}
if (!isset($config['cf_analytics'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_analytics` TEXT NOT NULL AFTER `cf_intercept_ip` ",
true
);
}
if (!isset($config['cf_add_meta'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_add_meta` TEXT NOT NULL AFTER `cf_analytics` ",
true
);
}
if (!isset($config['cf_syndi_token'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_syndi_token` VARCHAR(255) NOT NULL AFTER `cf_add_meta` ",
true
);
}
if (!isset($config['cf_syndi_except'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_syndi_except` TEXT NOT NULL AFTER `cf_syndi_token` ",
true
);
}
if (!isset($config['cf_sms_use'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_sms_use` varchar(255) NOT NULL DEFAULT '' AFTER `cf_cert_limit`,
ADD `cf_icode_id` varchar(255) NOT NULL DEFAULT '' AFTER `cf_sms_use`,
ADD `cf_icode_pw` varchar(255) NOT NULL DEFAULT '' AFTER `cf_icode_id`,
ADD `cf_icode_server_ip` varchar(255) NOT NULL DEFAULT '' AFTER `cf_icode_pw`,
ADD `cf_icode_server_port` varchar(255) NOT NULL DEFAULT '' AFTER `cf_icode_server_ip` ",
true
);
}
if (!isset($config['cf_mobile_page_rows'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_mobile_page_rows` int(11) NOT NULL DEFAULT '0' AFTER `cf_page_rows` ",
true
);
}
if (!isset($config['cf_cert_req'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_cert_req` tinyint(4) NOT NULL DEFAULT '0' AFTER `cf_cert_limit` ",
true
);
}
if (!isset($config['cf_faq_skin'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_faq_skin` varchar(255) NOT NULL DEFAULT '' AFTER `cf_connect_skin`,
ADD `cf_mobile_faq_skin` varchar(255) NOT NULL DEFAULT '' AFTER `cf_mobile_connect_skin` ",
true
);
}
// LG유플러스 본인확인 필드 추가
if (!isset($config['cf_lg_mid'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_lg_mid` varchar(255) NOT NULL DEFAULT '' AFTER `cf_cert_kcp_cd`,
ADD `cf_lg_mert_key` varchar(255) NOT NULL DEFAULT '' AFTER `cf_lg_mid` ",
true
);
}
if (!isset($config['cf_optimize_date'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_optimize_date` date NOT NULL default '0000-00-00' AFTER `cf_popular_del` ",
true
);
}
// 카카오톡링크 api 키
if (!isset($config['cf_kakao_js_apikey'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_kakao_js_apikey` varchar(255) NOT NULL DEFAULT '' AFTER `cf_googl_shorturl_apikey` ",
true
);
}
// SMS 전송유형 필드 추가
if (!isset($config['cf_sms_type'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_sms_type` varchar(10) NOT NULL DEFAULT '' AFTER `cf_sms_use` ",
true
);
}
// 접속자 정보 필드 추가
if (!sql_query(" select vi_browser from {$g5['visit_table']} limit 1 ")) {
sql_query(
" ALTER TABLE `{$g5['visit_table']}`
ADD `vi_browser` varchar(255) NOT NULL DEFAULT '' AFTER `vi_agent`,
ADD `vi_os` varchar(255) NOT NULL DEFAULT '' AFTER `vi_browser`,
ADD `vi_device` varchar(255) NOT NULL DEFAULT '' AFTER `vi_os` ",
true
);
}
//소셜 로그인 관련 필드 및 구글 리챕챠 필드 추가
if (!isset($config['cf_social_login_use'])) {
sql_query(
"ALTER TABLE `{$g5['config_table']}`
ADD `cf_social_login_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `cf_googl_shorturl_apikey`,
ADD `cf_google_clientid` varchar(100) NOT NULL DEFAULT '' AFTER `cf_twitter_secret`,
ADD `cf_google_secret` varchar(100) NOT NULL DEFAULT '' AFTER `cf_google_clientid`,
ADD `cf_naver_clientid` varchar(100) NOT NULL DEFAULT '' AFTER `cf_google_secret`,
ADD `cf_naver_secret` varchar(100) NOT NULL DEFAULT '' AFTER `cf_naver_clientid`,
ADD `cf_kakao_rest_key` varchar(100) NOT NULL DEFAULT '' AFTER `cf_naver_secret`,
ADD `cf_social_servicelist` varchar(255) NOT NULL DEFAULT '' AFTER `cf_social_login_use`,
ADD `cf_payco_clientid` varchar(100) NOT NULL DEFAULT '' AFTER `cf_social_servicelist`,
ADD `cf_payco_secret` varchar(100) NOT NULL DEFAULT '' AFTER `cf_payco_clientid`,
ADD `cf_captcha` varchar(100) NOT NULL DEFAULT '' AFTER `cf_kakao_js_apikey`,
ADD `cf_recaptcha_site_key` varchar(100) NOT NULL DEFAULT '' AFTER `cf_captcha`,
ADD `cf_recaptcha_secret_key` varchar(100) NOT NULL DEFAULT '' AFTER `cf_recaptcha_site_key`
",
true
);
}
//소셜 로그인 관련 필드 카카오 클라이언트 시크릿 추가
if (!isset($config['cf_kakao_client_secret'])) {
sql_query(
"ALTER TABLE `{$g5['config_table']}`
ADD `cf_kakao_client_secret` varchar(100) NOT NULL DEFAULT '' AFTER `cf_kakao_rest_key`
",
true
);
}
// 회원 이미지 관련 필드 추가
if (!isset($config['cf_member_img_size'])) {
sql_query(
"ALTER TABLE `{$g5['config_table']}`
ADD `cf_member_img_size` int(11) NOT NULL DEFAULT '0' AFTER `cf_member_icon_height`,
ADD `cf_member_img_width` int(11) NOT NULL DEFAULT '0' AFTER `cf_member_img_size`,
ADD `cf_member_img_height` int(11) NOT NULL DEFAULT '0' AFTER `cf_member_img_width`
",
true
);
$sql = " update {$g5['config_table']} set cf_member_img_size = 50000, cf_member_img_width = 60, cf_member_img_height = 60 ";
sql_query($sql, false);
$config['cf_member_img_size'] = 50000;
$config['cf_member_img_width'] = 60;
$config['cf_member_img_height'] = 60;
}
// 소셜 로그인 관리 테이블 없을 경우 생성
if (!sql_query(" DESC {$g5['social_profile_table']} ", false)) {
sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['social_profile_table']}` (
`mp_no` int(11) NOT NULL AUTO_INCREMENT,
`mb_id` varchar(255) NOT NULL DEFAULT '',
`provider` varchar(50) NOT NULL DEFAULT '',
`object_sha` varchar(45) NOT NULL DEFAULT '',
`identifier` varchar(255) NOT NULL DEFAULT '',
`profileurl` varchar(255) NOT NULL DEFAULT '',
`photourl` varchar(255) NOT NULL DEFAULT '',
`displayname` varchar(150) NOT NULL DEFAULT '',
`description` varchar(255) NOT NULL DEFAULT '',
`mp_register_day` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`mp_latest_day` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
UNIQUE KEY `mp_no` (`mp_no`),
KEY `mb_id` (`mb_id`),
KEY `provider` (`provider`)
) ",
true
);
}
// 짧은 URL 주소를 사용 여부 필드 추가
if (!isset($config['cf_bbs_rewrite'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_bbs_rewrite` tinyint(4) NOT NULL DEFAULT '0' AFTER `cf_link_target` ",
true
);
}
// 읽지 않은 메모 수 칼럼 추가
if (!isset($member['mb_memo_cnt'])) {
sql_query(
" ALTER TABLE `{$g5['member_table']}`
ADD `mb_memo_cnt` int(11) NOT NULL DEFAULT '0' AFTER `mb_memo_call`",
true
);
}
// 스크랩 읽은 수 추가
if (!isset($member['mb_scrap_cnt'])) {
sql_query(
" ALTER TABLE `{$g5['member_table']}`
ADD `mb_scrap_cnt` int(11) NOT NULL DEFAULT '0' AFTER `mb_memo_cnt`",
true
);
}
// 아이코드 토큰키 추가
if (!isset($config['cf_icode_token_key'])) {
$sql = "ALTER TABLE `{$g5['config_table']}`
ADD COLUMN `cf_icode_token_key` VARCHAR(100) NOT NULL DEFAULT '' AFTER `cf_icode_server_port`; ";
sql_query($sql, false);
}
// 아이디/비밀번호 찾기에 본인확인 사용 여부 필드 추가
if (!isset($config['cf_cert_find'])) {
$sql = "ALTER TABLE `{$g5['config_table']}`
ADD COLUMN `cf_cert_find` TINYINT(4) NOT NULL DEFAULT '0' AFTER `cf_cert_use`; ";
sql_query($sql, false);
}
// 간편인증 필드 추가
if (!isset($config['cf_cert_simple'])) {
$sql = "ALTER TABLE `{$g5['config_table']}`
ADD COLUMN `cf_cert_simple` VARCHAR(255) NOT NULL DEFAULT '' AFTER `cf_cert_hp`; ";
sql_query($sql, false);
}
if (!isset($config['cf_cert_kg_cd'])) {
$sql = "ALTER TABLE `{$g5['config_table']}`
ADD COLUMN `cf_cert_kg_cd` VARCHAR(255) NOT NULL DEFAULT '' AFTER `cf_cert_simple`; ";
sql_query($sql, false);
}
if (!isset($config['cf_cert_kg_mid'])) {
$sql = "ALTER TABLE `{$g5['config_table']}`
ADD COLUMN `cf_cert_kg_mid` VARCHAR(255) NOT NULL DEFAULT '' AFTER `cf_cert_kg_cd`; ";
sql_query($sql, false);
}
if (!isset($config['cf_cert_use_seed'])) {
$sql = "ALTER TABLE `{$g5['config_table']}`
ADD COLUMN `cf_cert_use_seed` TINYINT(4) NOT NULL DEFAULT '1' AFTER `cf_cert_kg_mid`; ";
sql_query($sql, false);
}
if (!isset($config['cf_cert_kcp_enckey'])) {
$sql = "ALTER TABLE `{$g5['config_table']}`
ADD COLUMN `cf_cert_kcp_enckey` VARCHAR(100) NOT NULL DEFAULT '' AFTER `cf_cert_kcp_cd`; ";
sql_query($sql, false);
$config['cf_cert_kcp_enckey'] = '';
}
// 광고성 정보 수신 동의 사용 필드 추가
if (!isset($config['cf_use_promotion'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_use_promotion` tinyint(1) NOT NULL DEFAULT '0' AFTER `cf_privacy` ",
true
);
}
// 광고성 정보 수신 동의 여부 필드 추가 + 메일 / SMS 수신 일자 추가
if (!isset($member['mb_marketing_agree'])) {
sql_query(
" ALTER TABLE `{$g5['member_table']}`
ADD `mb_marketing_agree` tinyint(1) NOT NULL DEFAULT '0' AFTER `mb_scrap_cnt`,
ADD `mb_marketing_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_marketing_agree`,
ADD `mb_thirdparty_agree` tinyint(1) NOT NULL DEFAULT '0' AFTER `mb_marketing_date`,
ADD `mb_thirdparty_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_thirdparty_agree`,
ADD `mb_agree_log` TEXT NOT NULL AFTER `mb_thirdparty_date`,
ADD `mb_mailling_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_mailling`,
ADD `mb_sms_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_sms` ",
true
);
}
if (!$config['cf_faq_skin']) {
$config['cf_faq_skin'] = "basic";
}
@@ -1189,6 +758,13 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
<input type="checkbox" name="cf_use_email_certify" value="1" id="cf_use_email_certify" <?php echo $config['cf_use_email_certify'] ? 'checked' : ''; ?>> 사용
</td>
</tr>
<tr>
<th scope="row"><label for="cf_email_certify_minutes">메일인증 유효시간</label></th>
<td>
<?php echo help('인증메일 발송 후 링크를 사용할 수 있는 시간을 분 단위로 설정합니다. 0은 만료시간과 미인증 만료 정리를 적용하지 않습니다.<br>만료된 미인증 회원은 최고관리자 접속 시 하루 한 번 실행되는 정리에서 탈퇴 처리하며, 본인확인 정보와 소셜 연결을 해제합니다. 나머지 회원자료는 회원탈퇴후 삭제일 설정에 따라 정리합니다.') ?>
<input type="number" name="cf_email_certify_minutes" value="<?php echo isset($config['cf_email_certify_minutes']) ? (int) $config['cf_email_certify_minutes'] : 60; ?>" id="cf_email_certify_minutes" class="frm_input" min="0"> 분
</td>
</tr>
<tr>
<th scope="row"><label for="cf_formmail_is_member">폼메일 사용 여부</label></th>
<td>
+4
View File
@@ -56,6 +56,7 @@ if (isset($_POST['cf_intercept_ip']) && $_POST['cf_intercept_ip']) {
$check_keys = array(
'cf_use_email_certify' => 'int',
'cf_email_certify_minutes' => 'int',
'cf_use_homepage' => 'int',
'cf_req_homepage' => 'int',
'cf_use_tel' => 'int',
@@ -178,6 +179,8 @@ foreach ($check_keys as $k => $v) {
}
}
$_POST['cf_email_certify_minutes'] = max(0, $_POST['cf_email_certify_minutes']);
// 본인확인을 사용할 경우 아이핀, 휴대폰인증 중 하나는 선택되어야 함
if ($_POST['cf_cert_use'] && !$_POST['cf_cert_ipin'] && !$_POST['cf_cert_hp'] && !$_POST['cf_cert_simple']) {
alert('본인확인을 위해 아이핀, 휴대폰 본인확인, KG이니시스 간편인증 서비스 중 하나 이상 선택해 주십시오.');
@@ -226,6 +229,7 @@ $sql = " update {$g5['config_table']}
cf_point_term = '{$_POST['cf_point_term']}',
cf_use_copy_log = '{$_POST['cf_use_copy_log']}',
cf_use_email_certify = '{$_POST['cf_use_email_certify']}',
cf_email_certify_minutes = '{$_POST['cf_email_certify_minutes']}',
cf_login_point = '{$_POST['cf_login_point']}',
cf_cut_name = '{$_POST['cf_cut_name']}',
cf_nick_modify = '{$_POST['cf_nick_modify']}',
-29
View File
@@ -8,41 +8,12 @@ auth_check_menu($auth, $sub_menu, "w");
$co_id = isset($_REQUEST['co_id']) ? preg_replace('/[^a-z0-9_]/i', '', $_REQUEST['co_id']) : '';
// 상단, 하단 파일경로 필드 추가
if (!sql_query(" select co_include_head from {$g5['content_table']} limit 1 ", false)) {
$sql = " ALTER TABLE `{$g5['content_table']}` ADD `co_include_head` VARCHAR( 255 ) NOT NULL ,
ADD `co_include_tail` VARCHAR( 255 ) NOT NULL ";
sql_query($sql, false);
}
// html purifier 사용여부 필드
if (!sql_query(" select co_tag_filter_use from {$g5['content_table']} limit 1 ", false)) {
sql_query(
" ALTER TABLE `{$g5['content_table']}`
ADD `co_tag_filter_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `co_content` ",
true
);
sql_query(" update {$g5['content_table']} set co_tag_filter_use = '1' ");
}
// 모바일 내용 추가
if (!sql_query(" select co_mobile_content from {$g5['content_table']} limit 1", false)) {
sql_query(
" ALTER TABLE `{$g5['content_table']}`
ADD `co_mobile_content` longtext NOT NULL AFTER `co_content` ",
true
);
}
// 스킨 설정 추가
if (!sql_query(" select co_skin from {$g5['content_table']} limit 1 ", false)) {
sql_query(
" ALTER TABLE `{$g5['content_table']}`
ADD `co_skin` varchar(255) NOT NULL DEFAULT '' AFTER `co_mobile_content`,
ADD `co_mobile_skin` varchar(255) NOT NULL DEFAULT '' AFTER `co_skin` ",
true
);
sql_query(" update {$g5['content_table']} set co_skin = 'basic', co_mobile_skin = 'basic' ");
}
$html_title = "내용";
$g5['title'] = $html_title . ' 관리';
-24
View File
@@ -8,30 +8,6 @@ if (!isset($g5['content_table'])) {
die('<meta charset="utf-8">/data/dbconfig.php 파일에 <strong>$g5[\'content_table\'] = G5_TABLE_PREFIX.\'content\';</strong> 를 추가해 주세요.');
}
//내용(컨텐츠)정보 테이블이 있는지 검사한다.
if (!sql_query(" DESCRIBE {$g5['content_table']} ", false)) {
if (sql_query(" DESCRIBE {$g5['g5_shop_content_table']} ", false)) {
sql_query(" ALTER TABLE {$g5['g5_shop_content_table']} RENAME TO `{$g5['content_table']}` ;", false);
} else {
$query_cp = sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['content_table']}` (
`co_id` varchar(20) NOT NULL DEFAULT '',
`co_html` tinyint(4) NOT NULL DEFAULT '0',
`co_subject` varchar(255) NOT NULL DEFAULT '',
`co_content` longtext NOT NULL,
`co_hit` int(11) NOT NULL DEFAULT '0',
`co_include_head` varchar(255) NOT NULL,
`co_include_tail` varchar(255) NOT NULL,
PRIMARY KEY (`co_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ",
true
);
// 내용관리 생성
sql_query(" insert into `{$g5['content_table']}` set co_id = 'company', co_html = '1', co_subject = '회사소개', co_content= '<p align=center><b>회사소개에 대한 내용을 입력하십시오.</b></p>' ", false);
sql_query(" insert into `{$g5['content_table']}` set co_id = 'privacy', co_html = '1', co_subject = '개인정보 처리방침', co_content= '<p align=center><b>개인정보 처리방침에 대한 내용을 입력하십시오.</b></p>' ", false);
sql_query(" insert into `{$g5['content_table']}` set co_id = 'provision', co_html = '1', co_subject = '서비스 이용약관', co_content= '<p align=center><b>서비스 이용약관에 대한 내용을 입력하십시오.</b></p>' ", false);
}
}
$g5['title'] = '내용관리';
require_once G5_ADMIN_PATH . '/admin.head.php';
-2
View File
@@ -696,7 +696,6 @@ a.scf_pgreg {display:inline-block;margin:5px 0 0;padding:5px 10px;background:#22
a.kcp_btn {display:inline-block;margin:5px 0 0;padding:5px 10px;background:#226C8B;color:#fff;font-weight:normal;text-decoration:none}
a.lg_btn{display:inline-block;margin:5px 0 0;padding:5px 10px;background:#ED008C;color:#fff;font-weight:normal;text-decoration:none}
a.kg_btn{display:inline-block;margin:5px 0 0;padding:5px 10px;background:#4A2C7C;color:#fff;font-weight:normal;text-decoration:none}
a.kakao_btn{display:inline-block;margin:5px 0 0;padding:5px 10px;background:#FDDC2F;color:#3B1E1E;font-weight:normal;text-decoration:none}
a.naver_btn {display:inline-block;margin:5px 0 0;padding:5px 10px;background:#00C73C;color:#fff;font-weight:normal;text-decoration:none}
a.nicepay_btn{display:inline-block;margin:5px 0 0;padding:5px 10px;background:#0057bf;color:#fff;font-weight:normal;text-decoration:none}
@@ -740,7 +739,6 @@ ul.de_pg_tab li.tab-current a{background:#2CC185;color:#fff}
.lg_info_fld th{background-color:#FFF4FA}
.lg_info_fld_v2 th{background-color:#ffe8f5}
.inicis_info_fld th{background-color:#F6F1FF}
.kakao_info_fld th{background-color:#FFFCED}
.naver_info_fld th{background-color:#F3FFF3}
.nicepay_info_fld th{background-color:#d1e6ff}
+199 -632
View File
@@ -1,646 +1,213 @@
<?php
$sub_menu = '100410';
include_once('./_common.php');
include_once(G5_LIB_PATH . '/migration.lib.php');
include_once(G5_LIB_PATH . '/shop_install.lib.php');
auth_check_menu($auth, $sub_menu, 'r');
if ($is_admin != 'super') {
alert('최고관리자만 접근 가능합니다.');
}
$is_execute = isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST';
$migration_result = array('success' => true, 'applied' => 0, 'skipped' => 0, 'errors' => array());
$shop_install_result = null;
$action = '';
if ($is_execute) {
check_demo();
check_admin_token();
$action = isset($_POST['action']) ? trim($_POST['action']) : 'migrate';
if ($action === 'install_shop') {
$shop_install_result = g5_shop_install_run();
} elseif ($action === 'record_existing') {
$migration_result = g5_migration_run('', true);
} else {
$migration_id = isset($_POST['migration_id']) ? trim($_POST['migration_id']) : '';
$migration_result = g5_migration_run($migration_id);
$migration_result = run_replace('admin_dbupgrade_result', $migration_result);
}
}
$migration_statuses = g5_migration_status();
$pending_count = 0;
$unrecorded_count = 0;
foreach ($migration_statuses as $migration_status) {
if (isset($migration_status['status']) && $migration_status['status'] === 'unrecorded') {
$unrecorded_count++;
} elseif (isset($migration_status['error']) || $migration_status['status'] !== 'success' || $migration_status['checksum_changed']) {
$pending_count++;
}
}
if ($shop_install_result !== null && $shop_install_result['success']) {
alert('쇼핑몰 설치가 완료되었습니다.', G5_ADMIN_URL . '/dbupgrade.php');
} elseif ($shop_install_result !== null) {
$db_upgrade_msg = '쇼핑몰 설치에 실패했습니다. 오류 내용을 확인해 주십시오.';
} elseif (!$is_execute) {
$db_upgrade_msg = $pending_count ? '확인·실행이 필요한 DB 마이그레이션이 있습니다. 아래 내용을 확인해 주십시오.' : ($unrecorded_count ? '현재 실행할 변경은 없지만 기존 상태 확인 이력이 등록되지 않았습니다.' : 'DB 마이그레이션이 모두 적용되어 있습니다.');
} elseif (!$migration_result['success']) {
$db_upgrade_msg = 'DB 마이그레이션에 실패했습니다. 오류 내용을 확인해 주십시오.';
} elseif ($action === 'record_existing') {
$db_upgrade_msg = '기존 상태 확인 이력 ' . (int) $migration_result['skipped'] . '건을 등록했습니다. 선행 항목에 실행이 필요하거나 실패 이력이 있으면 등록을 중단합니다.';
} elseif ($migration_result['applied'] || $migration_result['skipped']) {
$db_upgrade_msg = 'DB 마이그레이션이 완료되었습니다.';
} else {
$db_upgrade_msg = '적용할 DB 마이그레이션이 없습니다.';
}
$g5['title'] = 'DB 업그레이드';
include_once('./admin.head.php');
$is_check = false;
//소셜 로그인 관련 필드 및 구글 리챕챠 필드 추가
if(!isset($config['cf_social_login_use'])) {
sql_query("ALTER TABLE `{$g5['config_table']}`
ADD `cf_social_login_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `cf_googl_shorturl_apikey`,
ADD `cf_google_clientid` varchar(100) NOT NULL DEFAULT '' AFTER `cf_twitter_secret`,
ADD `cf_google_secret` varchar(100) NOT NULL DEFAULT '' AFTER `cf_google_clientid`,
ADD `cf_naver_clientid` varchar(100) NOT NULL DEFAULT '' AFTER `cf_google_secret`,
ADD `cf_naver_secret` varchar(100) NOT NULL DEFAULT '' AFTER `cf_naver_clientid`,
ADD `cf_kakao_rest_key` varchar(100) NOT NULL DEFAULT '' AFTER `cf_naver_secret`,
ADD `cf_social_servicelist` varchar(255) NOT NULL DEFAULT '' AFTER `cf_social_login_use`,
ADD `cf_payco_clientid` varchar(100) NOT NULL DEFAULT '' AFTER `cf_social_servicelist`,
ADD `cf_payco_secret` varchar(100) NOT NULL DEFAULT '' AFTER `cf_payco_clientid`,
ADD `cf_captcha` varchar(100) NOT NULL DEFAULT '' AFTER `cf_kakao_js_apikey`,
ADD `cf_recaptcha_site_key` varchar(100) NOT NULL DEFAULT '' AFTER `cf_captcha`,
ADD `cf_recaptcha_secret_key` varchar(100) NOT NULL DEFAULT '' AFTER `cf_recaptcha_site_key`
", true);
$is_check = true;
}
//소셜 로그인 관련 필드 카카오 클라이언트 시크릿 추가
if(!isset($config['cf_kakao_client_secret'])) {
sql_query("ALTER TABLE `{$g5['config_table']}`
ADD `cf_kakao_client_secret` varchar(100) NOT NULL DEFAULT '' AFTER `cf_kakao_rest_key`
", true);
$is_check = true;
}
// 회원 이미지 관련 필드 추가
if(!isset($config['cf_member_img_size'])) {
sql_query("ALTER TABLE `{$g5['config_table']}`
ADD `cf_member_img_size` int(11) NOT NULL DEFAULT '0' AFTER `cf_member_icon_height`,
ADD `cf_member_img_width` int(11) NOT NULL DEFAULT '0' AFTER `cf_member_img_size`,
ADD `cf_member_img_height` int(11) NOT NULL DEFAULT '0' AFTER `cf_member_img_width`
", true);
$sql = " update {$g5['config_table']} set cf_member_img_size = 50000, cf_member_img_width = 60, cf_member_img_height = 60 ";
sql_query($sql, false);
$is_check = true;
}
// 소셜 로그인 관리 테이블 없을 경우 생성
if( isset($g5['social_profile_table']) && !sql_query(" DESC {$g5['social_profile_table']} ", false)) {
sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['social_profile_table']}` (
`mp_no` int(11) NOT NULL AUTO_INCREMENT,
`mb_id` varchar(255) NOT NULL DEFAULT '',
`provider` varchar(50) NOT NULL DEFAULT '',
`object_sha` varchar(45) NOT NULL DEFAULT '',
`identifier` varchar(255) NOT NULL DEFAULT '',
`profileurl` varchar(255) NOT NULL DEFAULT '',
`photourl` varchar(255) NOT NULL DEFAULT '',
`displayname` varchar(150) NOT NULL DEFAULT '',
`description` varchar(255) NOT NULL DEFAULT '',
`mp_register_day` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`mp_latest_day` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
UNIQUE KEY `mp_no` (`mp_no`),
KEY `mb_id` (`mb_id`),
KEY `provider` (`provider`)
) ", true);
$is_check = true;
}
// 게시판 짧은 주소
$sql = " select bo_table from {$g5['board_table']} ";
$result = sql_query($sql);
while ($row = sql_fetch_array($result)) {
$write_table = $g5['write_prefix'] . $row['bo_table']; // 게시판 테이블 전체이름
$sql = " SHOW COLUMNS FROM {$write_table} LIKE 'wr_seo_title' ";
$row = sql_fetch($sql);
if( !$row ){
sql_query("ALTER TABLE `{$write_table}`
ADD `wr_seo_title` varchar(200) NOT NULL DEFAULT '' AFTER `wr_content`,
ADD INDEX `wr_seo_title` (`wr_seo_title`);
", false);
$is_check = true;
}
}
// 내용 관리 짧은 주소
$sql = " SHOW COLUMNS FROM `{$g5['content_table']}` LIKE 'co_seo_title' ";
$row = sql_fetch($sql);
if( !$row ){
sql_query("ALTER TABLE `{$g5['content_table']}`
ADD `co_seo_title` varchar(200) NOT NULL DEFAULT '' AFTER `co_content`,
ADD INDEX `co_seo_title` (`co_seo_title`);
", false);
$is_check = true;
}
$sql = "select * from {$g5['content_table']} limit 100 ";
$result = sql_query($sql);
while ($row = sql_fetch_array($result)) {
if( ! $row['co_seo_title']){
$co_seo_title = exist_seo_title_recursive('content', generate_seo_title($row['co_subject']), $g5['content_table'], $row['co_id']);
$sql = " update {$g5['content_table']}
set co_seo_title = '$co_seo_title'
where co_id = '{$row['co_id']}' ";
sql_query($sql);
}
}
// 메모 테이블
$sql = " SHOW COLUMNS FROM `{$g5['memo_table']}` LIKE 'me_send_id' ";
$row = sql_fetch($sql);
if( !$row ){
sql_query("ALTER TABLE `{$g5['memo_table']}`
ADD `me_send_id` INT(11) NOT NULL DEFAULT '0',
ADD `me_type` ENUM('send','recv') NOT NULL DEFAULT 'recv',
ADD `me_send_ip` VARCHAR(100) NOT NULL DEFAULT '',
CHANGE COLUMN `me_id` `me_id` INT(11) NOT NULL AUTO_INCREMENT;
", false);
$is_check = true;
}
// 읽지 않은 메모 수 칼럼
if(!isset($member['mb_memo_cnt'])) {
sql_query(" ALTER TABLE `{$g5['member_table']}`
ADD `mb_memo_cnt` int(11) NOT NULL DEFAULT '0' AFTER `mb_memo_call`", true);
$is_check = true;
}
// 스크랩 읽은 수 추가
if(!isset($member['mb_scrap_cnt'])) {
sql_query(" ALTER TABLE `{$g5['member_table']}`
ADD `mb_scrap_cnt` int(11) NOT NULL DEFAULT '0' AFTER `mb_memo_cnt`", true);
$is_check = true;
}
// 짧은 URL 주소를 사용 여부 필드 추가
if (!isset($config['cf_bbs_rewrite'])) {
sql_query(" ALTER TABLE `{$g5['config_table']}`
ADD `cf_bbs_rewrite` tinyint(4) NOT NULL DEFAULT '0' AFTER `cf_link_target` ", true);
$is_check = true;
}
// 파일테이블에 추가 칼럼
$sql = " SHOW COLUMNS FROM `{$g5['board_file_table']}` LIKE 'bf_fileurl' ";
$row = sql_fetch($sql);
if( !$row ) {
sql_query(" ALTER TABLE `{$g5['board_file_table']}`
ADD COLUMN `bf_fileurl` VARCHAR(255) NOT NULL DEFAULT '' AFTER `bf_content`,
ADD COLUMN `bf_thumburl` VARCHAR(255) NOT NULL DEFAULT '' AFTER `bf_fileurl`,
ADD COLUMN `bf_storage` VARCHAR(50) NOT NULL DEFAULT '' AFTER `bf_thumburl`", true);
$is_check = true;
}
if (defined('G5_USE_SHOP') && G5_USE_SHOP) {
// 임시저장 테이블이 없을 경우 생성
if(!sql_query(" DESC {$g5['g5_shop_post_log_table']} ", false)) {
sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['g5_shop_post_log_table']}` (
`log_id` int(11) NOT NULL AUTO_INCREMENT,
`oid` bigint(20) unsigned NOT NULL,
`mb_id` varchar(255) NOT NULL DEFAULT '',
`post_data` text NOT NULL,
`ol_code` varchar(255) NOT NULL DEFAULT '',
`ol_msg` text NOT NULL,
`ol_datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`ol_ip` varchar(25) NOT NULL DEFAULT '',
PRIMARY KEY (`log_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ", true);
$is_check = true;
}
$result = sql_query("describe `{$g5['g5_shop_post_log_table']}`");
while ($row = sql_fetch_array($result)){
if( isset($row['Field']) && $row['Field'] === 'ol_msg' && $row['Type'] === 'varchar(255)' ){
sql_query("ALTER TABLE `{$g5['g5_shop_post_log_table']}` MODIFY ol_msg TEXT NOT NULL;", false);
sql_query("ALTER TABLE `{$g5['g5_shop_post_log_table']}` DROP PRIMARY KEY;", false);
sql_query("ALTER TABLE `{$g5['g5_shop_post_log_table']}` ADD `log_id` int(11) NOT NULL AUTO_INCREMENT, ADD PRIMARY KEY (`log_id`);", false);
$is_check = true;
break;
}
}
if (!isset($default['de_id'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD COLUMN `de_id` INT(11) NOT NULL AUTO_INCREMENT FIRST,
ADD PRIMARY KEY (`de_id`); ", true);
$is_check = true;
}
}
// auth.au_menu 컬럼 크기 조정
$sql = " SHOW COLUMNS FROM `{$g5['auth_table']}` LIKE 'au_menu' ";
$row = sql_fetch($sql);
if (
stripos($row['Type'], 'varchar') !== false
&& (int) preg_replace('/[^0-9]/', '', $row['Type']) < 50
) {
sql_query(" ALTER TABLE `{$g5['auth_table']}` CHANGE `au_menu` `au_menu` VARCHAR(50) NOT NULL; ", true);
$is_check = true;
}
// qa config 테이블 auto id key 추가
$row = sql_fetch("select * from `{$g5['qa_config_table']}` limit 1");
if (!isset($row['qa_id'])) {
sql_query(" ALTER TABLE `{$g5['qa_config_table']}` ADD COLUMN `qa_id` INT(11) NOT NULL AUTO_INCREMENT FIRST,
ADD PRIMARY KEY (`qa_id`); ", true);
$is_check = true;
}
// config 기본 테이블 auto id key 추가
if (!isset($config['cf_id'])) {
sql_query(" ALTER TABLE `{$g5['config_table']}`
ADD COLUMN `cf_id` INT(11) NOT NULL AUTO_INCREMENT FIRST,
ADD PRIMARY KEY (`cf_id`); ", true);
$is_check = true;
}
// login 테이블 auto id key 추가
$row = sql_fetch("select * from `{$g5['login_table']}` limit 1");
if (!isset($row['lo_id'])) {
sql_query(" ALTER TABLE `{$g5['login_table']}`
ADD COLUMN `lo_id` INT(11) NOT NULL AUTO_INCREMENT FIRST,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`lo_id`),
ADD UNIQUE KEY `lo_ip_unique` (`lo_ip`) ", true);
$is_check = true;
}
// visit 테이블 auto id key 로 변경
$result = sql_query("describe `{$g5['visit_table']}`");
while ($row = sql_fetch_array($result)){
if (isset($row['Field']) && $row['Field'] === 'vi_id' && (isset($row['Default']) && $row['Default'] == 0)){
sql_query("ALTER TABLE `{$g5['visit_table']}`
CHANGE COLUMN `vi_id` `vi_id` INT(11) NOT NULL AUTO_INCREMENT;
", false);
$is_check = true;
}
}
// SMS5 테이블 G5_TABLE_PREFIX 적용
if($g5['sms5_prefix'] != 'sms5_' && sql_num_rows(sql_query("show tables like 'sms5_config'")))
{
$tables = array('config','write','history','book','book_group','form','form_group');
foreach($tables as $name){
$old_table = 'sms5_' . $name;
$new_table = $g5['sms5_prefix'] . $name;
// 기존 테이블이 있고, G5_TABLE_PREFIX 적용 테이블이 없을 경우 → 테이블명 변경
if(sql_num_rows(sql_query("SHOW TABLES LIKE '{$old_table}' "))){
if(!sql_num_rows(sql_query("SHOW TABLES LIKE '{$new_table}' "))){
sql_query("RENAME TABLE {$old_table} TO {$new_table}", false);
}
}
}
$is_check = true;
}
// 광고성 정보 수신 동의 사용 필드 추가
if (!isset($config['cf_use_promotion'])) {
sql_query(
" ALTER TABLE `{$g5['config_table']}`
ADD `cf_use_promotion` tinyint(1) NOT NULL DEFAULT '0' AFTER `cf_privacy` ",
true
);
$is_check = true;
}
// 광고성 정보 수신 동의 여부 필드 추가 + 메일 / SMS 수신 일자 추가
if (!isset($member['mb_marketing_agree'])) {
sql_query(
" ALTER TABLE `{$g5['member_table']}`
ADD `mb_marketing_agree` tinyint(1) NOT NULL DEFAULT '0' AFTER `mb_scrap_cnt`,
ADD `mb_marketing_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_marketing_agree`,
ADD `mb_thirdparty_agree` tinyint(1) NOT NULL DEFAULT '0' AFTER `mb_marketing_date`,
ADD `mb_thirdparty_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_thirdparty_agree`,
ADD `mb_agree_log` TEXT NOT NULL AFTER `mb_thirdparty_date`,
ADD `mb_mailling_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_mailling`,
ADD `mb_sms_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `mb_sms` ",
true
);
$is_check = true;
}
// 쿠폰 로그 테이블에 UNIQUE 인덱스 추가 (쿠폰 이중사용 방지)
if (defined('G5_USE_SHOP') && G5_USE_SHOP) {
$result = sql_query("SHOW INDEX FROM `{$g5['g5_shop_coupon_log_table']}` WHERE Key_name = 'idx_coupon_use'", false);
if (!$result || !sql_num_rows($result)) {
// 기존에 동일 쿠폰이 중복 사용된 데이터가 있으면 UNIQUE 인덱스 생성 실패하므로 중복 데이터 정리
$dup_sql = " SELECT cp_id, mb_id, MIN(cl_id) as keep_id
FROM `{$g5['g5_shop_coupon_log_table']}`
GROUP BY cp_id, mb_id
HAVING COUNT(*) > 1 ";
$dup_result = sql_query($dup_sql, false);
if ($dup_result && sql_num_rows($dup_result)) {
while ($dup_row = sql_fetch_array($dup_result)) {
echo $dup_row['cp_id']." 의 동일 쿠폰이 중복 사용된 데이터가 있으므로 인덱스 생성이 불가합니다. <br>";
$sql = " DELETE FROM `{$g5['g5_shop_coupon_log_table']}`
WHERE cp_id = '{$dup_row['cp_id']}'
AND mb_id = '{$dup_row['mb_id']}'
AND cl_id != '{$dup_row['keep_id']}' ";
if ($is_admin === 'super') {
echo "데이터베이스에서 검토후에 이 쿼리문을 실행해 주세요.<br>$sql<br>";
}
// sql_query($sql);
}
}
// MyISAM + utf8mb4 환경에서 키 길이 초과 방지: cp_id varchar(100), mb_id varchar(100)으로 조정
sql_query("ALTER TABLE `{$g5['g5_shop_coupon_log_table']}` MODIFY `cp_id` varchar(100) NOT NULL DEFAULT '', MODIFY `mb_id` varchar(100) NOT NULL DEFAULT ''", false);
sql_query("ALTER TABLE `{$g5['g5_shop_coupon_log_table']}` ADD UNIQUE KEY `idx_coupon_use` (`cp_id`, `mb_id`)", false);
$is_check = true;
}
}
// 자동 로그인 토큰 테이블 생성 (KVE-2026-0610: 추측 가능한 자동 로그인 쿠키 위조 방지)
// 다중 디바이스 지원을 위해 회원당 여러 토큰을 별도 테이블로 관리
if (!isset($g5['member_auto_login_table'])) {
$g5['member_auto_login_table'] = G5_TABLE_PREFIX.'member_auto_login';
}
if (!sql_query(" DESC `{$g5['member_auto_login_table']}` ", false)) {
sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['member_auto_login_table']}` (
`al_id` int(11) NOT NULL auto_increment,
`mb_id` varchar(20) NOT NULL default '',
`al_token` varchar(64) NOT NULL default '',
`al_user_agent` varchar(255) NOT NULL default '',
`al_ip` varchar(45) NOT NULL default '',
`al_created` datetime DEFAULT NULL,
`al_last_used` datetime DEFAULT NULL,
`al_expire` datetime DEFAULT NULL,
PRIMARY KEY (`al_id`),
UNIQUE KEY `al_token` (`al_token`),
KEY `mb_id` (`mb_id`),
KEY `al_expire` (`al_expire`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ", true);
$is_check = true;
}
// KG이니시스 결제 처리 현황 및 단계별 이력 테이블
if (defined('G5_USE_SHOP') && G5_USE_SHOP) {
$inicis_pro_config_columns = array(
'de_inicis_pro_alert_use' => "ADD COLUMN `de_inicis_pro_alert_use` tinyint(4) NOT NULL DEFAULT '1'",
'de_inicis_pro_reconcile_use' => "ADD COLUMN `de_inicis_pro_reconcile_use` tinyint(4) NOT NULL DEFAULT '0'",
'de_inicis_pro_log_days' => "ADD COLUMN `de_inicis_pro_log_days` int(11) NOT NULL DEFAULT '365'",
'de_inicis_pro_summary_days' => "ADD COLUMN `de_inicis_pro_summary_days` int(11) NOT NULL DEFAULT '1825'",
'de_inicis_pro_monitor_at' => "ADD COLUMN `de_inicis_pro_monitor_at` datetime DEFAULT NULL",
'de_inicis_pro_monitor_message' => "ADD COLUMN `de_inicis_pro_monitor_message` varchar(255) NOT NULL DEFAULT ''"
);
$inicis_pro_config_alter = array();
foreach ($inicis_pro_config_columns as $column => $alter) {
$column_result = sql_query(" SHOW COLUMNS FROM `{$g5['g5_shop_default_table']}` LIKE '$column' ", false);
if (!$column_result || sql_num_rows($column_result) === 0)
$inicis_pro_config_alter[] = $alter;
}
if (count($inicis_pro_config_alter)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}` ".implode(', ', $inicis_pro_config_alter), true);
$is_check = true;
}
if (!isset($g5['g5_shop_inicis_pay_table']))
$g5['g5_shop_inicis_pay_table'] = G5_SHOP_TABLE_PREFIX.'inicis_pay';
if (!isset($g5['g5_shop_inicis_pay_event_table']))
$g5['g5_shop_inicis_pay_event_table'] = G5_SHOP_TABLE_PREFIX.'inicis_pay_event';
if (!sql_query(" DESC `{$g5['g5_shop_inicis_pay_table']}` ", false)) {
sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['g5_shop_inicis_pay_table']}` (
`ip_id` int(11) NOT NULL AUTO_INCREMENT,
`ip_oid` varchar(64) NOT NULL DEFAULT '',
`ip_tid` varchar(80) NOT NULL DEFAULT '',
`ip_auth_tid` varchar(80) NOT NULL DEFAULT '',
`ip_mid` varchar(80) NOT NULL DEFAULT '',
`ip_environment` varchar(10) NOT NULL DEFAULT '',
`mb_id` varchar(20) NOT NULL DEFAULT '',
`ip_amount` int(11) NOT NULL DEFAULT '0',
`ip_pay_type` varchar(20) NOT NULL DEFAULT '',
`ip_easy_pay` varchar(20) NOT NULL DEFAULT '',
`ip_device` varchar(10) NOT NULL DEFAULT '',
`ip_order_type` varchar(10) NOT NULL DEFAULT '',
`ip_status` varchar(30) NOT NULL DEFAULT '',
`ip_result_code` varchar(30) NOT NULL DEFAULT '',
`ip_result_message` varchar(255) NOT NULL DEFAULT '',
`ip_noti_status` varchar(30) NOT NULL DEFAULT '',
`ip_noti_code` varchar(30) NOT NULL DEFAULT '',
`ip_noti_message` varchar(255) NOT NULL DEFAULT '',
`ip_noti_failed_count` int(11) NOT NULL DEFAULT '0',
`ip_noti_at` datetime DEFAULT NULL,
`ip_cancel_status` varchar(30) NOT NULL DEFAULT '',
`ip_cancel_code` varchar(30) NOT NULL DEFAULT '',
`ip_cancel_message` varchar(255) NOT NULL DEFAULT '',
`ip_cancel_checked_at` datetime DEFAULT NULL,
`ip_refund_required` tinyint(4) NOT NULL DEFAULT '0',
`ip_vbank_due_at` datetime DEFAULT NULL,
`ip_expired_at` datetime DEFAULT NULL,
`ip_order_exists` tinyint(4) NOT NULL DEFAULT '0',
`ip_approved_at` datetime DEFAULT NULL,
`ip_ordered_at` datetime DEFAULT NULL,
`ip_notified_at` datetime DEFAULT NULL,
`ip_canceled_at` datetime DEFAULT NULL,
`ip_created_at` datetime DEFAULT NULL,
`ip_updated_at` datetime DEFAULT NULL,
`ip_ip` varchar(45) NOT NULL DEFAULT '',
`ip_event_count` int(11) NOT NULL DEFAULT '0',
`ip_audit_error` tinyint(4) NOT NULL DEFAULT '0',
`ip_alerted_at` datetime DEFAULT NULL,
`ip_alert_key` varchar(64) NOT NULL DEFAULT '',
`ip_pg_status` varchar(30) NOT NULL DEFAULT '',
`ip_pg_amount` int(11) NOT NULL DEFAULT '0',
`ip_pg_tid` varchar(80) NOT NULL DEFAULT '',
`ip_pg_result_code` varchar(30) NOT NULL DEFAULT '',
`ip_pg_message` varchar(255) NOT NULL DEFAULT '',
`ip_pg_checked_at` datetime DEFAULT NULL,
PRIMARY KEY (`ip_id`),
UNIQUE KEY `ip_oid` (`ip_oid`),
KEY `ip_tid` (`ip_tid`),
KEY `ip_auth_tid` (`ip_auth_tid`),
KEY `ip_status` (`ip_status`),
KEY `ip_noti_status` (`ip_noti_status`),
KEY `ip_cancel_status` (`ip_cancel_status`),
KEY `ip_refund_required` (`ip_refund_required`),
KEY `ip_updated_at` (`ip_updated_at`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ", true);
$is_check = true;
}
if (sql_query(" DESC `{$g5['g5_shop_inicis_pay_table']}` ", false)) {
$inicis_pay_columns = array(
'ip_environment' => "ADD COLUMN `ip_environment` varchar(10) NOT NULL DEFAULT '' AFTER `ip_mid`",
'ip_easy_pay' => "ADD COLUMN `ip_easy_pay` varchar(20) NOT NULL DEFAULT '' AFTER `ip_pay_type`",
'ip_noti_status' => "ADD COLUMN `ip_noti_status` varchar(30) NOT NULL DEFAULT '' AFTER `ip_result_message`",
'ip_noti_code' => "ADD COLUMN `ip_noti_code` varchar(30) NOT NULL DEFAULT '' AFTER `ip_noti_status`",
'ip_noti_message' => "ADD COLUMN `ip_noti_message` varchar(255) NOT NULL DEFAULT '' AFTER `ip_noti_code`",
'ip_noti_failed_count' => "ADD COLUMN `ip_noti_failed_count` int(11) NOT NULL DEFAULT '0' AFTER `ip_noti_message`",
'ip_noti_at' => "ADD COLUMN `ip_noti_at` datetime DEFAULT NULL AFTER `ip_noti_failed_count`",
'ip_cancel_status' => "ADD COLUMN `ip_cancel_status` varchar(30) NOT NULL DEFAULT '' AFTER `ip_noti_at`",
'ip_cancel_code' => "ADD COLUMN `ip_cancel_code` varchar(30) NOT NULL DEFAULT '' AFTER `ip_cancel_status`",
'ip_cancel_message' => "ADD COLUMN `ip_cancel_message` varchar(255) NOT NULL DEFAULT '' AFTER `ip_cancel_code`",
'ip_cancel_checked_at' => "ADD COLUMN `ip_cancel_checked_at` datetime DEFAULT NULL AFTER `ip_cancel_message`",
'ip_refund_required' => "ADD COLUMN `ip_refund_required` tinyint(4) NOT NULL DEFAULT '0' AFTER `ip_cancel_checked_at`",
'ip_vbank_due_at' => "ADD COLUMN `ip_vbank_due_at` datetime DEFAULT NULL AFTER `ip_refund_required`",
'ip_expired_at' => "ADD COLUMN `ip_expired_at` datetime DEFAULT NULL AFTER `ip_vbank_due_at`",
'ip_audit_error' => "ADD COLUMN `ip_audit_error` tinyint(4) NOT NULL DEFAULT '0'",
'ip_alerted_at' => "ADD COLUMN `ip_alerted_at` datetime DEFAULT NULL",
'ip_alert_key' => "ADD COLUMN `ip_alert_key` varchar(64) NOT NULL DEFAULT ''",
'ip_pg_status' => "ADD COLUMN `ip_pg_status` varchar(30) NOT NULL DEFAULT ''",
'ip_pg_amount' => "ADD COLUMN `ip_pg_amount` int(11) NOT NULL DEFAULT '0'",
'ip_pg_tid' => "ADD COLUMN `ip_pg_tid` varchar(80) NOT NULL DEFAULT ''",
'ip_pg_result_code' => "ADD COLUMN `ip_pg_result_code` varchar(30) NOT NULL DEFAULT ''",
'ip_pg_message' => "ADD COLUMN `ip_pg_message` varchar(255) NOT NULL DEFAULT ''",
'ip_pg_checked_at' => "ADD COLUMN `ip_pg_checked_at` datetime DEFAULT NULL"
);
$inicis_pay_alter = array();
foreach ($inicis_pay_columns as $column => $alter) {
$column_result = sql_query(" SHOW COLUMNS FROM `{$g5['g5_shop_inicis_pay_table']}` LIKE '$column' ", false);
if (!$column_result || sql_num_rows($column_result) === 0)
$inicis_pay_alter[] = $alter;
}
if (count($inicis_pay_alter)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_inicis_pay_table']}` ".implode(', ', $inicis_pay_alter), true);
$is_check = true;
}
if (count($inicis_pay_alter)) {
$environment = !empty($default['de_card_test']) ? 'test' : 'live';
sql_query(" insert ignore into `{$g5['g5_shop_inicis_pay_table']}`
(ip_oid, ip_environment, ip_status, ip_noti_status, ip_noti_code, ip_noti_message,
ip_noti_failed_count, ip_noti_at, ip_created_at, ip_updated_at, ip_event_count)
select e.ip_oid, '$environment', e.pe_status, e.pe_status, e.pe_code, e.pe_message,
sum(if(e.pe_status = 'notification_failed', 1, 0)), max(e.pe_created_at),
min(e.pe_created_at), max(e.pe_created_at), count(*)
from `{$g5['g5_shop_inicis_pay_event_table']}` e
left join `{$g5['g5_shop_inicis_pay_table']}` p on p.ip_oid = e.ip_oid
where p.ip_id is null
and e.pe_stage = 'notification'
group by e.ip_oid ", false);
sql_query(" update `{$g5['g5_shop_inicis_pay_table']}`
set ip_environment = case
when lower(ip_mid) in ('inipaytest','iniescrow0') then 'test'
when ip_mid <> '' then 'live'
else '$environment'
end
where ip_environment = '' ", false);
sql_query(" update `{$g5['g5_shop_inicis_pay_table']}` p
inner join (
select ip_oid, max(pe_id) as pe_id,
sum(if(pe_status = 'notification_failed', 1, 0)) as fail_count
from `{$g5['g5_shop_inicis_pay_event_table']}`
where pe_stage = 'notification'
group by ip_oid
) x on x.ip_oid = p.ip_oid
inner join `{$g5['g5_shop_inicis_pay_event_table']}` e on e.pe_id = x.pe_id
set p.ip_noti_status = e.pe_status,
p.ip_noti_code = e.pe_code,
p.ip_noti_message = e.pe_message,
p.ip_noti_failed_count = x.fail_count,
p.ip_noti_at = e.pe_created_at ", false);
sql_query(" update `{$g5['g5_shop_inicis_pay_table']}` p
inner join (
select ip_oid, max(pe_id) as pe_id
from `{$g5['g5_shop_inicis_pay_event_table']}`
where pe_stage = 'cancel'
group by ip_oid
) x on x.ip_oid = p.ip_oid
inner join `{$g5['g5_shop_inicis_pay_event_table']}` e on e.pe_id = x.pe_id
set p.ip_cancel_status = e.pe_status,
p.ip_cancel_code = e.pe_code,
p.ip_cancel_message = e.pe_message,
p.ip_cancel_checked_at = e.pe_created_at ", false);
sql_query(" update `{$g5['g5_shop_inicis_pay_table']}` p
inner join `{$g5['g5_shop_inicis_pay_event_table']}` e on e.ip_oid = p.ip_oid and e.pe_stage = 'request'
set p.ip_easy_pay = case
when e.pe_message like '삼성페이%' then 'SAMSUNGPAY'
when e.pe_message like 'lpay%' then 'LPAY'
when e.pe_message like 'inicis_kakaopay%' then 'KAKAOPAY'
when e.pe_message like '간편결제%' then 'EASYPAY'
else p.ip_easy_pay
end
where p.ip_easy_pay = '' ", false);
sql_query(" update `{$g5['g5_shop_inicis_pay_table']}`
set ip_noti_status = case
when ip_status = 'paid' then 'paid'
when ip_status = 'vbank_issued' then 'vbank_issued'
when ip_status = 'notification_received' then 'notification_received'
else ip_noti_status
end,
ip_cancel_status = case
when ip_status in ('canceled','cancel_failed','partial_canceled','partial_cancel_failed') then ip_status
else ip_cancel_status
end ", false);
sql_query(" update `{$g5['g5_shop_inicis_pay_table']}` p
inner join {$g5['g5_shop_order_table']} o on o.od_id = p.ip_oid
set p.ip_status = 'paid_after_cancel',
p.ip_refund_required = '1',
p.ip_noti_status = 'paid_after_cancel',
p.ip_noti_message = '취소 주문에 가상계좌 입금 확인'
where p.ip_pay_type = 'VBANK'
and p.ip_status = 'paid'
and o.od_status = '취소'
and o.od_receipt_price > 0 ", false);
$inicis_pro_migrated_at = G5_TIME_YMDHIS;
$inicis_pro_migrated_message = '기존 결제·주문 이력 대조에서 취소 후 입금 확인';
sql_query(" insert into `{$g5['g5_shop_inicis_pay_event_table']}`
(ip_oid, ip_tid, pe_stage, pe_status, pe_code, pe_message, pe_source, pe_ip, pe_created_at)
select p.ip_oid, p.ip_tid, 'reconcile', 'paid_after_cancel', '',
'".sql_escape_string($inicis_pro_migrated_message)."', 'system', '', '$inicis_pro_migrated_at'
from `{$g5['g5_shop_inicis_pay_table']}` p
left join `{$g5['g5_shop_inicis_pay_event_table']}` e
on e.ip_oid = p.ip_oid and e.pe_status = 'paid_after_cancel'
where p.ip_status = 'paid_after_cancel'
and p.ip_refund_required = '1'
and e.pe_id is null ", false);
sql_query(" update `{$g5['g5_shop_inicis_pay_table']}` p
inner join `{$g5['g5_shop_inicis_pay_event_table']}` e
on e.ip_oid = p.ip_oid
and e.pe_status = 'paid_after_cancel'
and e.pe_message = '".sql_escape_string($inicis_pro_migrated_message)."'
and e.pe_created_at = '$inicis_pro_migrated_at'
set p.ip_event_count = p.ip_event_count + 1 ", false);
}
$inicis_pay_indexes = array(
'ip_noti_status' => "ADD KEY `ip_noti_status` (`ip_noti_status`)",
'ip_cancel_status' => "ADD KEY `ip_cancel_status` (`ip_cancel_status`)",
'ip_refund_required' => "ADD KEY `ip_refund_required` (`ip_refund_required`)"
);
foreach ($inicis_pay_indexes as $index_name => $index_sql) {
$index_result = sql_query(" SHOW INDEX FROM `{$g5['g5_shop_inicis_pay_table']}` WHERE Key_name = '$index_name' ", false);
if (!$index_result || sql_num_rows($index_result) === 0) {
sql_query(" ALTER TABLE `{$g5['g5_shop_inicis_pay_table']}` $index_sql ", true);
$is_check = true;
}
}
}
if (!sql_query(" DESC `{$g5['g5_shop_inicis_pay_event_table']}` ", false)) {
sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['g5_shop_inicis_pay_event_table']}` (
`pe_id` int(11) NOT NULL AUTO_INCREMENT,
`ip_oid` varchar(64) NOT NULL DEFAULT '',
`ip_tid` varchar(80) NOT NULL DEFAULT '',
`pe_stage` varchar(30) NOT NULL DEFAULT '',
`pe_status` varchar(30) NOT NULL DEFAULT '',
`pe_code` varchar(30) NOT NULL DEFAULT '',
`pe_message` varchar(255) NOT NULL DEFAULT '',
`pe_source` varchar(10) NOT NULL DEFAULT '',
`pe_ip` varchar(45) NOT NULL DEFAULT '',
`pe_created_at` datetime DEFAULT NULL,
PRIMARY KEY (`pe_id`),
KEY `ip_oid` (`ip_oid`),
KEY `ip_tid` (`ip_tid`),
KEY `pe_status` (`pe_status`),
KEY `pe_created_at` (`pe_created_at`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ", true);
$is_check = true;
}
}
$is_check = run_replace('admin_dbupgrade', $is_check);
$db_upgrade_msg = $is_check ? 'DB 업그레이드가 완료되었습니다.' : '더 이상 업그레이드 할 내용이 없습니다.<br>현재 DB 업그레이드가 완료된 상태입니다.';
$dbupgrade_token = get_admin_token();
?>
<div class="local_desc01 local_desc">
<p>
<?php echo $db_upgrade_msg; ?>
</p>
<p><?php echo $db_upgrade_msg; ?></p>
<?php if ($unrecorded_count) { ?>
<p>실행 불필요 <?php echo (int) $unrecorded_count; ?>건: 현재 실행 조건상 건너뛰는 항목입니다. 과거 실행 성공을 의미하지 않으며, 선행 작업 후에는 결과가 달라질 수 있습니다. 기존 상태 등록은 처음부터 연속해서 실행 불필요한 항목만 기록합니다.</p>
<?php } ?>
<p>대기 항목에는 데이터 보정 등 조회만으로 완료를 판단할 수 없는 작업도 포함됩니다. 조회 시 DB 변경이나 이력 등록은 수행하지 않습니다.</p>
</div>
<?php if ($shop_install_result !== null && !$shop_install_result['success']) { ?>
<div class="local_desc01 local_desc" style="color:#d00">
<p><?php echo htmlspecialchars($shop_install_result['error'], ENT_QUOTES, 'UTF-8'); ?></p>
</div>
<?php } elseif ($migration_result['errors']) { ?>
<div class="local_desc01 local_desc" style="color:#d00">
<?php foreach ($migration_result['errors'] as $migration_error) { ?>
<p><?php echo htmlspecialchars($migration_error, ENT_QUOTES, 'UTF-8'); ?></p>
<?php } ?>
</div>
<?php } ?>
<div class="dbupgrade_all_actions">
<?php if ($unrecorded_count) { ?>
<form class="dbupgrade_existing_action" method="post" action="<?php echo G5_ADMIN_URL; ?>/dbupgrade.php">
<input type="hidden" name="token" value="<?php echo $dbupgrade_token; ?>">
<input type="hidden" name="action" value="record_existing">
<button type="submit" class="btn_submit">기존 상태 확인 이력 등록</button>
</form>
<?php } ?>
<?php if (!defined('G5_USE_SHOP') || !G5_USE_SHOP) { ?>
<form method="post" action="<?php echo G5_ADMIN_URL; ?>/dbupgrade.php" onsubmit="return confirm('쇼핑몰을 설치하시겠습니까? 설치 전 데이터베이스와 data/dbconfig.php 백업을 권장합니다.');">
<input type="hidden" name="token" value="<?php echo $dbupgrade_token; ?>">
<input type="hidden" name="action" value="install_shop">
<button type="submit" class="btn_submit">쇼핑몰 설치</button>
</form>
<?php } ?>
<form method="post" action="<?php echo G5_ADMIN_URL; ?>/dbupgrade.php" onsubmit="return confirm('선택한 DB 마이그레이션을 실행하시겠습니까? 실행 전 데이터베이스 백업을 권장합니다.');">
<input type="hidden" name="token" value="<?php echo $dbupgrade_token; ?>">
<input type="hidden" name="action" value="migrate">
<button type="submit" name="migration_id" value="" class="btn_submit">전체 마이그레이션 실행</button>
</form>
</div>
<form method="post" action="<?php echo G5_ADMIN_URL; ?>/dbupgrade.php" onsubmit="return confirm('선택한 DB 마이그레이션을 실행하시겠습니까? 실행 전 데이터베이스 백업을 권장합니다.');">
<input type="hidden" name="token" value="<?php echo $dbupgrade_token; ?>">
<input type="hidden" name="action" value="migrate">
<div class="tbl_head01 tbl_wrap">
<table id="dbupgrade_migration_table">
<caption>버전형 DB 마이그레이션 목록</caption>
<thead>
<tr>
<th scope="col" aria-sort="none"><button type="button" class="dbupgrade_sort" data-column="0">마이그레이션 <span aria-hidden="true"></span></button></th>
<th scope="col" class="dbupgrade_description" aria-sort="none"><button type="button" class="dbupgrade_sort" data-column="1">설명 <span aria-hidden="true"></span></button></th>
<th scope="col" aria-sort="none"><button type="button" class="dbupgrade_sort" data-column="2">상태 <span aria-hidden="true"></span></button></th>
<th scope="col" aria-sort="none"><button type="button" class="dbupgrade_sort" data-column="3">적용 시각 <span aria-hidden="true"></span></button></th>
<th scope="col" aria-sort="none"><button type="button" class="dbupgrade_sort" data-column="4">실행 <span aria-hidden="true"></span></button></th>
</tr>
</thead>
<tbody>
<?php $previous_migrations_ready = true; ?>
<?php foreach ($migration_statuses as $migration_status) { ?>
<?php
$current_migration_status = isset($migration_status['status']) ? $migration_status['status'] : 'error';
$migration_succeeded = $current_migration_status === 'success' && !$migration_status['checksum_changed'];
$migration_can_run = in_array($current_migration_status, array('pending', 'failed'), true) && $previous_migrations_ready;
$migration_button_title = $current_migration_status === 'unrecorded' ? '기존 상태 확인 이력을 등록해 주십시오.' : ($current_migration_status === 'success' ? '이미 성공한 마이그레이션입니다.' : ($previous_migrations_ready ? '' : '선행 마이그레이션을 먼저 실행해야 합니다.'));
?>
<tr>
<?php if (isset($migration_status['error'])) { ?>
<td colspan="5"><?php echo htmlspecialchars($migration_status['error'], ENT_QUOTES, 'UTF-8'); ?></td>
<?php } else { ?>
<td><?php echo htmlspecialchars($migration_status['id'], ENT_QUOTES, 'UTF-8'); ?></td>
<td class="dbupgrade_description"><?php echo htmlspecialchars($migration_status['description'], ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo g5_migration_status_label($migration_status['status'], $migration_status['checksum_changed']); ?></td>
<td><?php echo htmlspecialchars($migration_status['applied_at'], ENT_QUOTES, 'UTF-8'); ?></td>
<td>
<button type="submit" name="migration_id" value="<?php echo htmlspecialchars($migration_status['id'], ENT_QUOTES, 'UTF-8'); ?>" class="btn_frmline"<?php echo $migration_can_run ? '' : ' disabled'; ?><?php echo $migration_button_title !== '' ? ' title="' . htmlspecialchars($migration_button_title, ENT_QUOTES, 'UTF-8') . '"' : ''; ?>>실행</button>
</td>
<?php } ?>
</tr>
<?php $previous_migrations_ready = $previous_migrations_ready && $migration_succeeded; ?>
<?php } ?>
</tbody>
</table>
</div>
</form>
<style>
.dbupgrade_all_actions {display:flex;width:100%;margin:0 0 20px;justify-content:flex-end;gap:5px}
.dbupgrade_all_actions form {margin:0}
.dbupgrade_all_actions .dbupgrade_existing_action {margin-right:auto}
.dbupgrade_all_actions .btn_submit {display:inline-block;float:none;position:static;width:auto;height:30px;margin:0;padding:0 15px;border:0}
.dbupgrade_sort {width:100%; border:0; background:transparent; color:inherit; font:inherit; cursor:pointer}
#dbupgrade_migration_table td.dbupgrade_description {text-align:left}
#dbupgrade_migration_table th.dbupgrade_description, #dbupgrade_migration_table th.dbupgrade_description .dbupgrade_sort {text-align:center}
.btn_frmline:disabled {background:#b7b7b7;color:#fff;cursor:not-allowed}
</style>
<script>
(function () {
var table = document.getElementById('dbupgrade_migration_table');
if (!table || !table.tBodies.length) {
return;
}
var buttons = table.querySelectorAll('.dbupgrade_sort');
var tbody = table.tBodies[0];
function getCellText(row, column) {
return row.cells[column] ? row.cells[column].textContent.replace(/^\s+|\s+$/g, '') : '';
}
function sortRows(button, initialAscending) {
var column = parseInt(button.getAttribute('data-column'), 10);
var header = button.parentNode;
var ascending = typeof initialAscending === 'boolean' ? initialAscending : header.getAttribute('aria-sort') !== 'ascending';
var rows = Array.prototype.slice.call(tbody.rows);
rows.sort(function (left, right) {
var leftText = getCellText(left, column);
var rightText = getCellText(right, column);
var compared = leftText.localeCompare(rightText, undefined, {numeric: true, sensitivity: 'base'});
return ascending ? compared : -compared;
});
for (var i = 0; i < buttons.length; i++) {
buttons[i].parentNode.setAttribute('aria-sort', 'none');
buttons[i].getElementsByTagName('span')[0].textContent = '';
}
header.setAttribute('aria-sort', ascending ? 'ascending' : 'descending');
button.getElementsByTagName('span')[0].textContent = ascending ? '▲' : '▼';
for (var j = 0; j < rows.length; j++) {
tbody.appendChild(rows[j]);
}
}
for (var i = 0; i < buttons.length; i++) {
buttons[i].onclick = function () {
sortRows(this);
};
}
if (buttons.length) {
sortRows(buttons[0], false);
}
}());
</script>
<?php
include_once ('./admin.tail.php');
include_once('./admin.tail.php');
-8
View File
@@ -26,14 +26,6 @@ if ($w == "u") {
$g5['title'] = $html_title . ' 관리';
// 모바일 상하단 내용 필드추가
if (!sql_query(" select fm_mobile_head_html from {$g5['faq_master_table']} limit 1 ", false)) {
sql_query(
" ALTER TABLE `{$g5['faq_master_table']}`
ADD `fm_mobile_head_html` text NOT NULL AFTER `fm_tail_html`,
ADD `fm_mobile_tail_html` text NOT NULL AFTER `fm_mobile_head_html` ",
true
);
}
require_once G5_ADMIN_PATH . '/admin.head.php';
?>
-3
View File
@@ -42,9 +42,6 @@ $sql_common = " set fm_subject = '$fm_subject',
fm_order = '$fm_order' ";
if ($w == "") {
$sql = " alter table {$g5['faq_master_table']} auto_increment=1 ";
sql_query($sql);
$sql = " insert {$g5['faq_master_table']} $sql_common ";
sql_query($sql);
-37
View File
@@ -10,45 +10,8 @@ if (!isset($g5['faq_table']) || !isset($g5['faq_master_table'])) {
}
//자주하시는 질문 마스터 테이블이 있는지 검사한다.
if (!sql_query(" DESCRIBE {$g5['faq_master_table']} ", false)) {
if (sql_query(" DESCRIBE {$g5['g5_shop_faq_master_table']} ", false)) {
sql_query(" ALTER TABLE {$g5['g5_shop_faq_master_table']} RENAME TO `{$g5['faq_master_table']}` ;", false);
} else {
$query_cp = sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['faq_master_table']}` (
`fm_id` int(11) NOT NULL AUTO_INCREMENT,
`fm_subject` varchar(255) NOT NULL DEFAULT '',
`fm_head_html` text NOT NULL,
`fm_tail_html` text NOT NULL,
`fm_order` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`fm_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ",
true
);
}
// FAQ Master
sql_query(" insert into `{$g5['faq_master_table']}` set fm_id = '1', fm_subject = '자주하시는 질문' ", false);
}
//자주하시는 질문 테이블이 있는지 검사한다.
if (!sql_query(" DESCRIBE {$g5['faq_table']} ", false)) {
if (sql_query(" DESCRIBE {$g5['g5_shop_faq_table']} ", false)) {
sql_query(" ALTER TABLE {$g5['g5_shop_faq_table']} RENAME TO `{$g5['faq_table']}` ;", false);
} else {
$query_cp = sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['faq_table']}` (
`fa_id` int(11) NOT NULL AUTO_INCREMENT,
`fm_id` int(11) NOT NULL DEFAULT '0',
`fa_subject` text NOT NULL,
`fa_content` text NOT NULL,
`fa_order` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`fa_id`),
KEY `fm_id` (`fm_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ",
true
);
}
}
$g5['title'] = 'FAQ관리';
require_once G5_ADMIN_PATH . '/admin.head.php';
+3 -1
View File
@@ -8,6 +8,8 @@ $html_title = '회원메일 발송';
check_demo();
$security_mail_url = g5_require_security_mail_url();
check_admin_token();
require_once './admin.head.php';
@@ -59,7 +61,7 @@ for ($i = 0; $i < count($member_list); $i++) {
$content = preg_replace("/{회원아이디}/", $mb_id, (string)$content);
$content = preg_replace("/{이메일}/", $to_email, (string)$content);
$content = $content . "<hr size=0><p><span style='font-size:9pt; font-family:굴림'>▶ 더 이상 정보 수신을 원치 않으시면 [<a href='" . G5_BBS_URL . "/email_stop.php?mb_id={$mb_id}&amp;mb_md5={$mb_md5}' target='_blank'>수신거부</a>] 해 주십시오.</span></p>";
$content = $content . "<hr size=0><p><span style='font-size:9pt; font-family:굴림'>▶ 더 이상 정보 수신을 원치 않으시면 [<a href='" . ($security_mail_url . '/' . G5_BBS_DIR) . "/email_stop.php?mb_id={$mb_id}&amp;mb_md5={$mb_md5}' target='_blank'>수신거부</a>] 해 주십시오.</span></p>";
mailer($config['cf_admin_email_name'], $config['cf_admin_email'], $to_email, $subject, $content, 1);
+3 -1
View File
@@ -12,6 +12,8 @@ auth_check_menu($auth, $sub_menu, 'w');
check_demo();
$security_mail_url = g5_require_security_mail_url();
$g5['title'] = '회원메일 테스트';
$name = get_text($member['mb_name']);
@@ -33,7 +35,7 @@ $content = preg_replace("/{이메일}/", $email, (string)$content);
$mb_md5 = md5($member['mb_id'] . $member['mb_email'] . $member['mb_datetime']);
$content = $content . '<p>더 이상 정보 수신을 원치 않으시면 [<a href="' . G5_BBS_URL . '/email_stop.php?mb_id=' . $mb_id . '&amp;mb_md5=' . $mb_md5 . '" target="_blank">수신거부</a>] 해 주십시오.</p>';
$content = $content . '<p>더 이상 정보 수신을 원치 않으시면 [<a href="' . ($security_mail_url . '/' . G5_BBS_DIR) . '/email_stop.php?mb_id=' . $mb_id . '&amp;mb_md5=' . $mb_md5 . '" target="_blank">수신거부</a>] 해 주십시오.</p>';
mailer($config['cf_title'], $member['mb_email'], $member['mb_email'], $subject, $content, 1);
+30
View File
@@ -0,0 +1,30 @@
<?php
$sub_menu = '200100';
require_once './_common.php';
check_demo();
auth_check_menu($auth, $sub_menu, 'w');
auth_check_menu($auth, $sub_menu, 'd');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
alert('올바른 방법으로 이용해 주십시오.');
}
check_admin_token();
admin_referer_check();
$mb_id = isset($_POST['mb_id']) && is_string($_POST['mb_id']) ? trim($_POST['mb_id']) : '';
$mb = get_member($mb_id);
if (empty($mb['mb_id'])) {
alert('존재하지 않는 회원자료입니다.');
}
if ($is_admin !== 'super' && ($mb['mb_level'] >= $member['mb_level'] || is_admin($mb['mb_id']) === 'super')) {
alert('자신보다 권한이 높거나 같은 회원의 본인확인 정보는 삭제할 수 없습니다.');
}
if (!admin_clear_member_certification($mb['mb_id'])) {
alert('본인확인 정보를 모두 삭제하지 못했습니다. DB 상태를 확인한 뒤 다시 시도해 주십시오.');
}
alert('본인확인 정보와 인증 내역을 삭제했습니다.', './member_form.php?w=u&mb_id='.urlencode($mb['mb_id']));
+21 -41
View File
@@ -150,62 +150,21 @@ $mb_marketing_agree_no = !$mb['mb_marketing_agree'] ? 'checked="checked"' :
$mb_thirdparty_agree_yes = $mb['mb_thirdparty_agree'] ? 'checked="checked"' : '';
$mb_thirdparty_agree_no = !$mb['mb_thirdparty_agree'] ? 'checked="checked"' : '';
if (isset($mb['mb_certify'])) {
// 날짜시간형이라면 drop 시킴
if (preg_match("/-/", $mb['mb_certify'])) {
sql_query(" ALTER TABLE `{$g5['member_table']}` DROP `mb_certify` ", false);
}
} else {
sql_query(" ALTER TABLE `{$g5['member_table']}` ADD `mb_certify` TINYINT(4) NOT NULL DEFAULT '0' AFTER `mb_hp` ", false);
}
if (isset($mb['mb_adult'])) {
sql_query(" ALTER TABLE `{$g5['member_table']}` CHANGE `mb_adult` `mb_adult` TINYINT(4) NOT NULL DEFAULT '0' ", false);
} else {
sql_query(" ALTER TABLE `{$g5['member_table']}` ADD `mb_adult` TINYINT NOT NULL DEFAULT '0' AFTER `mb_certify` ", false);
}
// 지번주소 필드추가
if (!isset($mb['mb_addr_jibeon'])) {
sql_query(" ALTER TABLE {$g5['member_table']} ADD `mb_addr_jibeon` varchar(255) NOT NULL DEFAULT '' AFTER `mb_addr2` ", false);
}
// 건물명필드추가
if (!isset($mb['mb_addr3'])) {
sql_query(" ALTER TABLE {$g5['member_table']} ADD `mb_addr3` varchar(255) NOT NULL DEFAULT '' AFTER `mb_addr2` ", false);
}
// 중복가입 확인필드 추가
if (!isset($mb['mb_dupinfo'])) {
sql_query(" ALTER TABLE {$g5['member_table']} ADD `mb_dupinfo` varchar(255) NOT NULL DEFAULT '' AFTER `mb_adult` ", false);
}
// 이메일인증 체크 필드추가
if (!isset($mb['mb_email_certify2'])) {
sql_query(" ALTER TABLE {$g5['member_table']} ADD `mb_email_certify2` varchar(255) NOT NULL DEFAULT '' AFTER `mb_email_certify` ", false);
}
// 본인인증 내역 테이블 정보가 dbconfig에 없으면 소셜 테이블 정의
if (!isset($g5['member_cert_history'])) {
$g5['member_cert_history_table'] = G5_TABLE_PREFIX . 'member_cert_history';
}
// 멤버 본인인증 정보 변경 내역 테이블 없을 경우 생성
if (isset($g5['member_cert_history_table']) && !sql_query(" DESC {$g5['member_cert_history_table']} ", false)) {
sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['member_cert_history_table']}` (
`ch_id` int(11) NOT NULL auto_increment,
`mb_id` varchar(20) NOT NULL DEFAULT '',
`ch_name` varchar(255) NOT NULL DEFAULT '',
`ch_hp` varchar(255) NOT NULL DEFAULT '',
`ch_birth` varchar(255) NOT NULL DEFAULT '',
`ch_type` varchar(20) NOT NULL DEFAULT '',
`ch_datetime` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`ch_id`),
KEY `mb_id` (`mb_id`)
) ",
true
);
}
$mb_cert_history = '';
if (isset($mb_id) && $mb_id) {
@@ -479,6 +438,20 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
<?php if ($cnt == 0) { ?>
본인인증 내역이 없습니다.
<?php } ?>
<?php
$has_certification_data = $cnt > 0 || !empty($mb['mb_dupinfo']) || !empty($mb['mb_certify'])
|| !empty($mb['mb_adult']) || !empty($mb['mb_birth']) || !empty($mb['mb_sex']);
if ($w === 'u' && !$has_certification_data) {
$cert_request = sql_fetch("select count(*) as cnt from {$g5['cert_history_table']} where mb_id = '".sql_real_escape_string($mb['mb_id'])."'");
$has_certification_data = !empty($cert_request['cnt']);
}
$can_cleanup_certification = $w === 'u' && $has_certification_data
&& ($is_admin === 'super' || (isset($auth[$sub_menu]) && strpos($auth[$sub_menu], 'd') !== false));
?>
<?php if ($can_cleanup_certification) { ?>
<p>본인확인 ‘아니오’는 인증 상태만 변경합니다. 정보 삭제는 CI/DI 관련 식별값, 본인확인·성인인증 상태, 생년월일·성별 및 본인인증 내역을 삭제합니다. 회원 이름과 연락처는 유지합니다.</p>
<button type="submit" form="fmembercertcleanup" class="btn btn_02">본인확인 정보 삭제</button>
<?php } ?>
</td>
</tr>
@@ -639,6 +612,13 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
</div>
</form>
<?php if ($can_cleanup_certification) { ?>
<form id="fmembercertcleanup" method="post" action="./member_cert_cleanup.php" onsubmit="return confirm('본인확인 정보와 인증 내역을 삭제하시겠습니까? 삭제 후 복구할 수 없으며, 회원정보의 다른 수정사항은 저장하지 않습니다.');">
<input type="hidden" name="mb_id" value="<?php echo get_text($mb['mb_id']); ?>">
<input type="hidden" name="token" value="">
</form>
<?php } ?>
<script>
function fmember_submit(f) {
if (!f.mb_icon.value.match(/\.(gif|jpe?g|png)$/i) && f.mb_icon.value) {
+2
View File
@@ -275,6 +275,8 @@ $colspan = 16;
<?php
if ($leave_msg || $intercept_msg) {
echo $leave_msg . ' ' . $intercept_msg;
} else if ($config['cf_use_email_certify'] && !preg_match('/[1-9]/', $row['mb_email_certify'])) {
echo '<span class="txt_false">메일 미인증</span>';
} else {
echo "정상";
}
-16
View File
@@ -11,22 +11,6 @@ if (!isset($g5['menu_table'])) {
die('<meta charset="utf-8">dbconfig.php 파일에 <strong>$g5[\'menu_table\'] = G5_TABLE_PREFIX.\'menu\';</strong> 를 추가해 주세요.');
}
if (!sql_query(" DESCRIBE {$g5['menu_table']} ", false)) {
sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['menu_table']}` (
`me_id` int(11) NOT NULL AUTO_INCREMENT,
`me_code` varchar(255) NOT NULL DEFAULT '',
`me_name` varchar(255) NOT NULL DEFAULT '',
`me_link` varchar(255) NOT NULL DEFAULT '',
`me_target` varchar(255) NOT NULL DEFAULT '0',
`me_order` int(11) NOT NULL DEFAULT '0',
`me_use` tinyint(4) NOT NULL DEFAULT '0',
`me_mobile_use` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`me_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ",
true
);
}
$sql = " select * from {$g5['menu_table']} order by me_id ";
$result = sql_query($sql);
-2
View File
@@ -17,8 +17,6 @@ $nw = array(
$html_title = "팝업레이어";
// 팝업레이어 테이블에 쇼핑몰, 커뮤니티 인지 구분하는 여부 필드 추가
$sql = " ALTER TABLE `{$g5['new_win_table']}` ADD `nw_division` VARCHAR(10) NOT NULL DEFAULT 'both' ";
sql_query($sql, false);
if ($w == "u") {
$html_title .= " 수정";
-25
View File
@@ -8,31 +8,6 @@ if (!isset($g5['new_win_table'])) {
die('<meta charset="utf-8">/data/dbconfig.php 파일에 <strong>$g5[\'new_win_table\'] = G5_TABLE_PREFIX.\'new_win\';</strong> 를 추가해 주세요.');
}
//내용(컨텐츠)정보 테이블이 있는지 검사한다.
if (!sql_query(" DESCRIBE {$g5['new_win_table']} ", false)) {
if (sql_query(" DESCRIBE {$g5['g5_shop_new_win_table']} ", false)) {
sql_query(" ALTER TABLE {$g5['g5_shop_new_win_table']} RENAME TO `{$g5['new_win_table']}` ;", false);
} else {
$query_cp = sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['new_win_table']}` (
`nw_id` int(11) NOT NULL AUTO_INCREMENT,
`nw_division` varchar(10) NOT NULL DEFAULT 'both',
`nw_device` varchar(10) NOT NULL DEFAULT 'both',
`nw_begin_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`nw_end_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`nw_disable_hours` int(11) NOT NULL DEFAULT '0',
`nw_left` int(11) NOT NULL DEFAULT '0',
`nw_top` int(11) NOT NULL DEFAULT '0',
`nw_height` int(11) NOT NULL DEFAULT '0',
`nw_width` int(11) NOT NULL DEFAULT '0',
`nw_subject` text NOT NULL,
`nw_content` text NOT NULL,
`nw_content_html` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`nw_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ",
true
);
}
}
$g5['title'] = '팝업레이어 관리';
require_once G5_ADMIN_PATH . '/admin.head.php';
-4
View File
@@ -23,10 +23,6 @@ if ($w == '') {
alert('w 값이 제대로 넘어오지 않았습니다.');
}
if (!isset($po['po_use'])) {
sql_query(" alter table `{$g5['poll_table']}` add `po_use` tinyint not null default '0' after `mb_ids` ", false);
}
$g5['title'] = $html_title;
require_once './admin.head.php';
?>
-108
View File
@@ -8,92 +8,6 @@ auth_check_menu($auth, $sub_menu, 'r');
$g5['title'] = '1:1문의 설정';
require_once './admin.head.php';
// DB 테이블 생성
if (!sql_query(" DESCRIBE `{$g5['qa_config_table']}` ", false)) {
sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['qa_config_table']}` (
`qa_id` int(11) NOT NULL auto_increment,
`qa_title` varchar(255) NOT NULL DEFAULT'',
`qa_category` varchar(255) NOT NULL DEFAULT'',
`qa_skin` varchar(255) NOT NULL DEFAULT '',
`qa_mobile_skin` varchar(255) NOT NULL DEFAULT '',
`qa_use_email` tinyint(4) NOT NULL DEFAULT '0',
`qa_req_email` tinyint(4) NOT NULL DEFAULT '0',
`qa_use_hp` tinyint(4) NOT NULL DEFAULT '0',
`qa_req_hp` tinyint(4) NOT NULL DEFAULT '0',
`qa_use_sms` tinyint(4) NOT NULL DEFAULT '0',
`qa_send_number` varchar(255) NOT NULL DEFAULT '',
`qa_admin_hp` varchar(255) NOT NULL DEFAULT '',
`qa_use_editor` tinyint(4) NOT NULL DEFAULT '0',
`qa_subject_len` int(11) NOT NULL DEFAULT '0',
`qa_mobile_subject_len` int(11) NOT NULL DEFAULT '0',
`qa_page_rows` int(11) NOT NULL DEFAULT '0',
`qa_mobile_page_rows` int(11) NOT NULL DEFAULT '0',
`qa_image_width` int(11) NOT NULL DEFAULT '0',
`qa_upload_size` int(11) NOT NULL DEFAULT '0',
`qa_insert_content` text NOT NULL,
`qa_include_head` varchar(255) NOT NULL DEFAULT '',
`qa_include_tail` varchar(255) NOT NULL DEFAULT '',
`qa_content_head` text NOT NULL,
`qa_content_tail` text NOT NULL,
`qa_mobile_content_head` text NOT NULL,
`qa_mobile_content_tail` text NOT NULL,
`qa_1_subj` varchar(255) NOT NULL DEFAULT '',
`qa_2_subj` varchar(255) NOT NULL DEFAULT '',
`qa_3_subj` varchar(255) NOT NULL DEFAULT '',
`qa_4_subj` varchar(255) NOT NULL DEFAULT '',
`qa_5_subj` varchar(255) NOT NULL DEFAULT '',
`qa_1` varchar(255) NOT NULL DEFAULT '',
`qa_2` varchar(255) NOT NULL DEFAULT '',
`qa_3` varchar(255) NOT NULL DEFAULT '',
`qa_4` varchar(255) NOT NULL DEFAULT '',
`qa_5` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`qa_id`)
)",
true
);
sql_query(
" CREATE TABLE IF NOT EXISTS `{$g5['qa_content_table']}` (
`qa_id` int(11) NOT NULL AUTO_INCREMENT,
`qa_num` int(11) NOT NULL DEFAULT '0',
`qa_parent` int(11) NOT NULL DEFAULT '0',
`qa_related` int(11) NOT NULL DEFAULT '0',
`mb_id` varchar(20) NOT NULL DEFAULT '',
`qa_name` varchar(255) NOT NULL DEFAULT '',
`qa_email` varchar(255) NOT NULL DEFAULT '',
`qa_hp` varchar(255) NOT NULL DEFAULT '',
`qa_type` tinyint(4) NOT NULL DEFAULT '0',
`qa_category` varchar(255) NOT NULL DEFAULT '',
`qa_email_recv` tinyint(4) NOT NULL DEFAULT '0',
`qa_sms_recv` tinyint(4) NOT NULL DEFAULT '0',
`qa_html` tinyint(4) NOT NULL DEFAULT '0',
`qa_subject` varchar(255) NOT NULL DEFAULT '',
`qa_content` text NOT NULL,
`qa_status` tinyint(4) NOT NULL DEFAULT '0',
`qa_file1` varchar(255) NOT NULL DEFAULT '',
`qa_source1` varchar(255) NOT NULL DEFAULT '',
`qa_file2` varchar(255) NOT NULL DEFAULT '',
`qa_source2` varchar(255) NOT NULL DEFAULT '',
`qa_ip` varchar(255) NOT NULL DEFAULT '',
`qa_datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`qa_1` varchar(255) NOT NULL DEFAULT '',
`qa_2` varchar(255) NOT NULL DEFAULT '',
`qa_3` varchar(255) NOT NULL DEFAULT '',
`qa_4` varchar(255) NOT NULL DEFAULT '',
`qa_5` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`qa_id`),
KEY `qa_num_parent` (`qa_num`,`qa_parent`)
)",
true
);
}
$sql = " SHOW COLUMNS FROM `{$g5['qa_content_table']}` LIKE 'qa_content' ";
$row = sql_fetch($sql);
if (strpos($row['Type'], 'text') === false) {
sql_query(" ALTER TABLE `{$g5['qa_content_table']}` CHANGE `qa_content` `qa_content` text NOT NULL ", true);
}
$qaconfig = get_qa_config();
if (empty($qaconfig)) {
@@ -106,28 +20,6 @@ if (empty($qaconfig)) {
$qaconfig = get_qa_config();
}
// 관리자 이메일필드 추가
if (!isset($qaconfig['qa_admin_email'])) {
sql_query(
" ALTER TABLE `{$g5['qa_config_table']}`
ADD `qa_admin_email` varchar(255) NOT NULL DEFAULT '' AFTER `qa_admin_hp` ",
true
);
}
// 상단 하단 설정 필드 추가
if (!isset($qaconfig['qa_include_head'])) {
sql_query(
" ALTER TABLE `{$g5['qa_config_table']}`
ADD `qa_include_head` varchar(255) NOT NULL DEFAULT '' AFTER `qa_insert_content`,
ADD `qa_include_tail` varchar(255) NOT NULL DEFAULT '' AFTER `qa_include_head`,
ADD `qa_content_head` text NOT NULL AFTER `qa_include_tail`,
ADD `qa_content_tail` text NOT NULL AFTER `qa_content_head`,
ADD `qa_mobile_content_head` text NOT NULL AFTER `qa_content_tail`,
ADD `qa_mobile_content_tail` text NOT NULL AFTER `qa_mobile_content_head` ",
true
);
}
?>
<form name="fqaconfigform" id="fqaconfigform" method="post" onsubmit="return fqaconfigform_submit(this);" autocomplete="off">
+3 -2
View File
@@ -88,7 +88,8 @@ function change_status($od_id, $current_status, $change_status)
$sql = " update {$g5['g5_shop_order_table']} set od_status = '{$change_status}' where od_id = '{$od_id}' and od_status = '{$current_status}' ";
sql_query($sql, true);
$sql = " update {$g5['g5_shop_cart_table']} set ct_status = '{$change_status}' where od_id = '{$od_id}' and ct_status = '{$current_status}' ";
$complete_time_sql = get_cart_complete_time_sql($change_status);
$sql = " update {$g5['g5_shop_cart_table']} set ct_complete_time = $complete_time_sql, ct_status = '{$change_status}' where od_id = '{$od_id}' and ct_status = '{$current_status}' ";
sql_query($sql, true);
}
@@ -170,7 +171,7 @@ function pg_setting_check($is_print=false){
if( $default['de_pg_service'] === 'kcp' && $default['de_kcp_mid'] && $default['de_kcp_site_key'] ){
$pg_msg = 'NHN KCP';
} else if ( $default['de_pg_service'] === 'lg' && $config['cf_lg_mid'] && $config['cf_lg_mert_key'] ){
$pg_msg = 'LG유플러스';
$msg .= '<div class="admin_pg_notice od_test_caution">(주의!) 토스페이먼츠(구버전) 결제의 결제 설정이 현재 테스트결제로 되어 있습니다.<br>반드시 <a href="#lg_info_anchor">MERT KEY</a>를 <u>[테스트]키</u>로 설정한 후 테스트결제를 진행해야합니다.<br>쇼핑몰 운영 시에는 실결제로 전환하여 <u>[라이브]키</u>로 설정해 주시기 바랍니다.<br>아래 링크를 클릭하여 실결제로 설정하여 운영해 주세요.<br><a href="'.$pg_test_conf_link.'" class="pg_test_conf_link">'.$pg_test_conf_link.'</a></div>';
} else if ( $default['de_pg_service'] === 'toss' && $config['cf_lg_mid'] && $config['cf_toss_client_key'] && $config['cf_toss_secret_key'] ){
$msg .= '<div class="admin_pg_notice od_test_caution">(주의!) 토스페이먼츠 결제의 결제 설정이 현재 테스트결제로 되어 있습니다.<br>반드시 <a href="#lg_info_anchor">상점 API키</a>를 <u>[테스트]키</u>로 설정한 후 테스트결제를 진행해야합니다.<br>쇼핑몰 운영 시에는 실결제로 전환하여 <u>[라이브]키</u>로 설정해 주시기 바랍니다.<br>아래 링크를 클릭하여 실결제로 설정하여 운영해 주세요.<br><a href="'.$pg_test_conf_link.'" class="pg_test_conf_link">'.$pg_test_conf_link.'</a></div>';
} else if ( $default['de_pg_service'] === 'inicis' && $default['de_inicis_mid'] && $default['de_inicis_sign_key'] ){
+1 -6
View File
@@ -33,11 +33,6 @@ else
}
// 접속기기 필드 추가
if(!sql_query(" select bn_device from {$g5['g5_shop_banner_table']} limit 0, 1 ")) {
sql_query(" ALTER TABLE `{$g5['g5_shop_banner_table']}`
ADD `bn_device` varchar(10) not null default '' AFTER `bn_url` ", true);
sql_query(" update {$g5['g5_shop_banner_table']} set bn_device = 'pc' ", true);
}
include_once (G5_ADMIN_PATH.'/admin.head.php');
?>
@@ -171,4 +166,4 @@ include_once (G5_ADMIN_PATH.'/admin.head.php');
</form>
<?php
include_once (G5_ADMIN_PATH.'/admin.tail.php');
include_once (G5_ADMIN_PATH.'/admin.tail.php');
-2
View File
@@ -49,8 +49,6 @@ if ($w=="")
{
if (!$bn_bimg_name) alert('배너 이미지를 업로드 하세요.');
sql_query(" alter table {$g5['g5_shop_banner_table']} auto_increment=1 ");
$sql = " insert into {$g5['g5_shop_banner_table']}
set bn_alt = '$bn_alt',
bn_url = '$bn_url',
+1 -19
View File
@@ -109,30 +109,12 @@ if ($w == 'u') $pg_anchor .= '<li><a href="#frm_etc">기타설정</a></li>';
$pg_anchor .= '</ul>';
// 쿠폰 적용 불가 설정 필드 추가
if(!sql_query(" select ca_nocoupon from {$g5['g5_shop_category_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_category_table']}`
ADD `ca_nocoupon` tinyint(4) NOT NULL DEFAULT '0' AFTER `ca_adult_use` ", true);
}
// 스킨 디렉토리 필드 추가
if(!sql_query(" select ca_skin_dir from {$g5['g5_shop_category_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_category_table']}`
ADD `ca_skin_dir` varchar(255) NOT NULL DEFAULT '' AFTER `ca_name`,
ADD `ca_mobile_skin_dir` varchar(255) NOT NULL DEFAULT '' AFTER `ca_skin_dir` ", true);
}
// 분류 출력순서 필드 추가
if(!sql_query(" select ca_order from {$g5['g5_shop_category_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_category_table']}`
ADD `ca_order` int(11) NOT NULL DEFAULT '0' AFTER `ca_name` ", true);
sql_query(" ALTER TABLE `{$g5['g5_shop_category_table']}` ADD INDEX(`ca_order`) ", true);
}
// 모바일 상품 출력줄수 필드 추가
if(!sql_query(" select ca_mobile_list_row from {$g5['g5_shop_category_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_category_table']}`
ADD `ca_mobile_list_row` int(11) NOT NULL DEFAULT '0' AFTER `ca_mobile_list_mod` ", true);
}
// 스킨 Path
if(!$ca['ca_skin_dir'])
@@ -636,4 +618,4 @@ jQuery(function($){
</script>
<?php
include_once (G5_ADMIN_PATH.'/admin.tail.php');
include_once (G5_ADMIN_PATH.'/admin.tail.php');
+30 -339
View File
@@ -27,249 +27,19 @@ $pg_anchor = '<ul class="anchor">
<li><a href="#anc_scf_sms">SMS설정</a></li>
</ul>';
// 무이자 할부 사용설정 필드 추가
if(!isset($default['de_card_noint_use'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_card_noint_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `de_card_use` ", true);
}
// 모바일 관련상품 설정 필드추가
if(!isset($default['de_mobile_rel_list_use'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_mobile_rel_list_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `de_rel_img_height`,
ADD `de_mobile_rel_list_skin` varchar(255) NOT NULL DEFAULT '' AFTER `de_mobile_rel_list_use`,
ADD `de_mobile_rel_img_width` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_rel_list_skin`,
ADD `de_mobile_rel_img_height` int(11) NOT NULL DEFAULT ' 0' AFTER `de_mobile_rel_img_width`", true);
}
// 신규회원 쿠폰 설정 필드 추가
if(!isset($default['de_member_reg_coupon_use'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_member_reg_coupon_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `de_tax_flag_use`,
ADD `de_member_reg_coupon_term` int(11) NOT NULL DEFAULT '0' AFTER `de_member_reg_coupon_use`,
ADD `de_member_reg_coupon_price` int(11) NOT NULL DEFAULT '0' AFTER `de_member_reg_coupon_term` ", true);
}
// 신규회원 쿠폰 주문 최소금액 필드추가
if(!isset($default['de_member_reg_coupon_minimum'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_member_reg_coupon_minimum` int(11) NOT NULL DEFAULT '0' AFTER `de_member_reg_coupon_price` ", true);
}
// lg 결제관련 필드 추가
if(!isset($default['de_pg_service'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_pg_service` varchar(255) NOT NULL DEFAULT '' AFTER `de_sms_hp` ", true);
}
// inicis 필드 추가
if(!isset($default['de_inicis_mid'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_inicis_mid` varchar(255) NOT NULL DEFAULT '' AFTER `de_kcp_site_key` ", true);
}
// 모바일 초기화면 이미지 줄 수 필드 추가
if(!isset($default['de_mobile_type1_list_row'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_mobile_type1_list_row` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_type1_list_mod`,
ADD `de_mobile_type2_list_row` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_type2_list_mod`,
ADD `de_mobile_type3_list_row` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_type3_list_mod`,
ADD `de_mobile_type4_list_row` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_type4_list_mod`,
ADD `de_mobile_type5_list_row` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_type5_list_mod` ", true);
}
// 모바일 관련상품 이미지 줄 수 필드 추가
if(!isset($default['de_mobile_rel_list_mod'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_mobile_rel_list_mod` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_rel_list_skin` ", true);
}
// 모바일 검색상품 이미지 줄 수 필드 추가
if(!isset($default['de_mobile_search_list_row'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_mobile_search_list_row` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_search_list_mod` ", true);
}
// PG 간펼결제 사용여부 필드 추가
if(!isset($default['de_easy_pay_use'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_easy_pay_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `de_iche_use` ", true);
}
// 이니시스 삼성페이 사용여부 필드 추가
if(!isset($default['de_samsung_pay_use'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_samsung_pay_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `de_easy_pay_use` ", true);
}
// 이니시스
if(!isset($default['de_inicis_cartpoint_use'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_inicis_cartpoint_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `de_samsung_pay_use` ", true);
}
// 이니시스 lpay 사용여부 필드 추가
if(!isset($default['de_inicis_lpay_use'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_inicis_lpay_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `de_samsung_pay_use` ", true);
}
// 이니시스 kakaopay 사용여부 필드 추가
if(!isset($default['de_inicis_kakaopay_use'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_inicis_kakaopay_use` tinyint(4) NOT NULL DEFAULT '0' AFTER `de_inicis_lpay_use` ", true);
}
// 카카오페이 필드 추가
if(!isset($default['de_kakaopay_mid'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_kakaopay_mid` varchar(255) NOT NULL DEFAULT '' AFTER `de_tax_flag_use`,
ADD `de_kakaopay_key` varchar(255) NOT NULL DEFAULT '' AFTER `de_kakaopay_mid`,
ADD `de_kakaopay_enckey` varchar(255) NOT NULL DEFAULT '' AFTER `de_kakaopay_key`,
ADD `de_kakaopay_hashkey` varchar(255) NOT NULL DEFAULT '' AFTER `de_kakaopay_enckey`,
ADD `de_kakaopay_cancelpwd` varchar(255) NOT NULL DEFAULT '' AFTER `de_kakaopay_hashkey` ", true);
}
// 이니시스 웹결제 사인키 필드 추가
if(!isset($default['de_inicis_sign_key'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_inicis_sign_key` varchar(255) NOT NULL DEFAULT '' ", true);
}
// 네이버페이 필드추가
if(!isset($default['de_naverpay_mid'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_naverpay_mid` varchar(255) NOT NULL DEFAULT '' AFTER `de_kakaopay_cancelpwd`,
ADD `de_naverpay_cert_key` varchar(255) NOT NULL DEFAULT '' AFTER `de_naverpay_mid`,
ADD `de_naverpay_button_key` varchar(255) NOT NULL DEFAULT '' AFTER `de_naverpay_cert_key`,
ADD `de_naverpay_test` tinyint(4) NOT NULL DEFAULT '0' AFTER `de_naverpay_button_key`,
ADD `de_naverpay_mb_id` varchar(255) NOT NULL DEFAULT '' AFTER `de_naverpay_test`,
ADD `de_naverpay_sendcost` varchar(255) NOT NULL DEFAULT '' AFTER `de_naverpay_mb_id`", true);
}
// 유형별상품리스트 설정필드 추가
if(!isset($default['de_listtype_list_skin'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_listtype_list_skin` varchar(255) NOT NULL DEFAULT '' AFTER `de_mobile_search_img_height`,
ADD `de_listtype_list_mod` int(11) NOT NULL DEFAULT '0' AFTER `de_listtype_list_skin`,
ADD `de_listtype_list_row` int(11) NOT NULL DEFAULT '0' AFTER `de_listtype_list_mod`,
ADD `de_listtype_img_width` int(11) NOT NULL DEFAULT '0' AFTER `de_listtype_list_row`,
ADD `de_listtype_img_height` int(11) NOT NULL DEFAULT '0' AFTER `de_listtype_img_width`,
ADD `de_mobile_listtype_list_skin` varchar(255) NOT NULL DEFAULT '' AFTER `de_listtype_img_height`,
ADD `de_mobile_listtype_list_mod` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_listtype_list_skin`,
ADD `de_mobile_listtype_list_row` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_listtype_list_mod`,
ADD `de_mobile_listtype_img_width` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_listtype_list_row`,
ADD `de_mobile_listtype_img_height` int(11) NOT NULL DEFAULT '0' AFTER `de_mobile_listtype_img_width` ", true);
}
// 임시저장 테이블이 없을 경우 생성
if(!sql_query(" DESC {$g5['g5_shop_post_log_table']} ", false)) {
sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['g5_shop_post_log_table']}` (
`log_id` int(11) NOT NULL AUTO_INCREMENT,
`oid` bigint(20) unsigned NOT NULL,
`mb_id` varchar(255) NOT NULL DEFAULT '',
`post_data` text NOT NULL,
`ol_code` varchar(255) NOT NULL DEFAULT '',
`ol_msg` text NOT NULL,
`ol_datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`ol_ip` varchar(25) NOT NULL DEFAULT '',
PRIMARY KEY (`log_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ", false);
}
// 현금영수증 발급 조건 추가
if(!isset($default['de_taxsave_types'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD `de_taxsave_types` set('account','vbank','transfer') NOT NULL DEFAULT 'account' AFTER `de_taxsave_use` ", true);
}
// 아이코드 토큰키 추가
if( ! isset($config['cf_icode_token_key']) ){
$sql = "ALTER TABLE `{$g5['config_table']}`
ADD COLUMN `cf_icode_token_key` VARCHAR(100) NOT NULL DEFAULT '' AFTER `cf_icode_server_port`; ";
sql_query($sql, false);
}
// PG 간편결제 추가 ( NHN_KCP 네이버페이, 카카오페이 )
if( ! isset($default['de_easy_pay_services']) ){
$sql = "ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD COLUMN `de_easy_pay_services` VARCHAR(255) NOT NULL DEFAULT '' AFTER `de_easy_pay_use`; ";
sql_query($sql, false);
}
// KG 이니시스 iniapi_key 추가
if( ! isset($default['de_inicis_iniapi_key']) ){
$sql = "ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD COLUMN `de_inicis_iniapi_key` VARCHAR(30) NOT NULL DEFAULT '' AFTER `de_inicis_sign_key`,
ADD COLUMN `de_inicis_iniapi_iv` VARCHAR(30) NOT NULL DEFAULT '' AFTER `de_inicis_iniapi_key`; ";
sql_query($sql, false);
}
// KG이니시스 INIpay PRO 설정 추가
if (!isset($default['de_inicis_pro_use'])) {
$sql = "ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD COLUMN `de_inicis_pro_use` TINYINT(4) NOT NULL DEFAULT '0' AFTER `de_inicis_sign_key`,
ADD COLUMN `de_inicis_hash_key` VARCHAR(255) NOT NULL DEFAULT '' AFTER `de_inicis_pro_use`; ";
sql_query($sql, false);
}
if (!isset($default['de_inicis_pro_alert_use'])) {
sql_query("ALTER TABLE `{$g5['g5_shop_default_table']}` ADD COLUMN `de_inicis_pro_alert_use` TINYINT(4) NOT NULL DEFAULT '1'", false);
$default['de_inicis_pro_alert_use'] = 1;
}
if (!isset($default['de_inicis_pro_reconcile_use'])) {
sql_query("ALTER TABLE `{$g5['g5_shop_default_table']}` ADD COLUMN `de_inicis_pro_reconcile_use` TINYINT(4) NOT NULL DEFAULT '0'", false);
$default['de_inicis_pro_reconcile_use'] = 0;
}
if (!isset($default['de_inicis_pro_log_days'])) {
sql_query("ALTER TABLE `{$g5['g5_shop_default_table']}` ADD COLUMN `de_inicis_pro_log_days` INT(11) NOT NULL DEFAULT '365'", false);
$default['de_inicis_pro_log_days'] = 365;
}
if (!isset($default['de_inicis_pro_summary_days'])) {
sql_query("ALTER TABLE `{$g5['g5_shop_default_table']}` ADD COLUMN `de_inicis_pro_summary_days` INT(11) NOT NULL DEFAULT '1825'", false);
$default['de_inicis_pro_summary_days'] = 1825;
}
if (!array_key_exists('de_inicis_pro_monitor_at', $default)) {
sql_query("ALTER TABLE `{$g5['g5_shop_default_table']}` ADD COLUMN `de_inicis_pro_monitor_at` DATETIME DEFAULT NULL", false);
$default['de_inicis_pro_monitor_at'] = '';
}
if (!isset($default['de_inicis_pro_monitor_message'])) {
sql_query("ALTER TABLE `{$g5['g5_shop_default_table']}` ADD COLUMN `de_inicis_pro_monitor_message` VARCHAR(255) NOT NULL DEFAULT ''", false);
$default['de_inicis_pro_monitor_message'] = '';
}
// KG이니시스 INIpay PRO 결제 감사 테이블이 없으면 생성한다.
include_once(G5_SHOP_PATH.'/inicis/pro/inicis_pro.lib.php');
if (function_exists('inicis_pro_audit_ensure_tables'))
inicis_pro_audit_ensure_tables();
// NICEPAY mid, key 추가
if (! isset($default['de_nicepay_mid'])) {
$sql = "ALTER TABLE `{$g5['g5_shop_default_table']}`
ADD COLUMN `de_nicepay_mid` VARCHAR(20) NOT NULL DEFAULT '' AFTER `de_inicis_cartpoint_use`,
ADD COLUMN `de_nicepay_key` VARCHAR(150) NOT NULL DEFAULT '' AFTER `de_nicepay_mid`; ";
sql_query($sql, false);
}
// 토스페이먼츠 client, secret key 추가
if( ! isset($config['cf_toss_client_key']) ){
$sql = "ALTER TABLE `{$g5['config_table']}`
ADD COLUMN `cf_toss_client_key` VARCHAR(100) NOT NULL DEFAULT '' AFTER `cf_lg_mert_key`,
ADD COLUMN `cf_toss_secret_key` VARCHAR(100) NOT NULL DEFAULT '' AFTER `cf_toss_client_key`; ";
sql_query($sql, false);
}
if( function_exists('pg_setting_check') ){
pg_setting_check(true);
}
if(!$default['de_kakaopay_cancelpwd']){
$default['de_kakaopay_cancelpwd'] = '1111';
}
?>
<?php if (!empty($default['de_kakaopay_mid']) || !empty($default['de_kakaopay_enckey'])) { ?>
<div class="local_desc01 local_desc">
<p>5.6.37부터 SIRK 전용 카카오페이 연동을 지원하지 않습니다. 기존 거래는 이니시스 상점관리자에서 승인·취소 내역을 확인해 주십시오.</p>
<p>기존 이용 고객 지원: 김민섭 <a href="mailto:minsup@sir.kr">minsup@sir.kr</a></p>
</div>
<?php } ?>
<form name="fconfig" action="./configformupdate.php" onsubmit="return fconfig_check(this)" method="post" enctype="MULTIPART/FORM-DATA">
<input type="hidden" name="token" value="">
<section id="anc_scf_info">
@@ -732,7 +502,7 @@ if(!$default['de_kakaopay_cancelpwd']){
<tr>
<th scope="row"><label for="de_easy_pay_use">PG사 간편결제 버튼 사용</label></th>
<td>
<?php echo help("주문서 작성 페이지에 PG사 간편결제(PAYCO, 토스, KPAY...) 버튼의 별도 사용 여부를 설정합니다.", 50); ?>
<?php echo help("주문서에 선택한 PG사의 사용 설정된 간편결제 수단을 개별 표시합니다. 아래 PG별 항목에서 계약된 수단을 선택하세요.", 50); ?>
<select id="de_easy_pay_use" name="de_easy_pay_use">
<option value="0" <?php echo get_selected($default['de_easy_pay_use'], 0); ?>>노출안함</option>
<option value="1" <?php echo get_selected($default['de_easy_pay_use'], 1); ?>>노출함</option>
@@ -817,8 +587,8 @@ if(!$default['de_kakaopay_cancelpwd']){
<tr>
<th scope="row"><label for="de_point_days">주문완료 포인트</label></th>
<td>
<?php echo help("주문자가 회원일 경우에만 주문완료시 포인트를 지급합니다. 주문취소, 반품 등을 고려하여 포인트를 지급할 적당한 기간을 입력하십시오. (기본값은 7일)\n0일로 설정하는 경우에는 주문완료와 동시에 포인트를 지급합니다."); ?>
주문 완료 <input type="text" name="de_point_days" value="<?php echo get_sanitize_input($default['de_point_days']); ?>" id="de_point_days" class="frm_input" size="2"> 일 이후에 포인트를 지급
<?php echo help("주문자가 회원일 경우에만 상품별 배송완료 시각부터 설정한 기간이 지난 후 포인트를 지급합니다. 주문취소, 반품 등을 고려하여 포인트를 지급할 적당한 기간을 입력하십시오. (기본값은 7일)\n0일로 설정하면 배송완료 처리 시 지급합니다. 그 외에는 기간 경과 후 관리자 주문 상세 조회 또는 완료 처리 시 지급합니다."); ?>
배송 완료 <input type="text" name="de_point_days" value="<?php echo get_sanitize_input($default['de_point_days']); ?>" id="de_point_days" class="frm_input" size="2"> 일 이후에 포인트를 지급
</td>
</tr>
<tr>
@@ -856,16 +626,16 @@ if(!$default['de_kakaopay_cancelpwd']){
<th scope="row"><label for="de_kcp_easy_pays">NHN KCP 간편결제</label></th>
<td>
<?php echo help("체크시 NHN KCP 간편결제들을 활성화 합니다.\nNHN_KCP > 네이버페이, 카카오페이는 테스트결제가 되지 않습니다.\n애플페이는 IOS 기기에 모바일결제만 가능합니다."); ?>
<input type="checkbox" id="de_easy_nhnkcp_payco" name="de_easy_pays[]" value="nhnkcp_payco" <?php if(stripos($default['de_easy_pay_services'], 'nhnkcp_payco') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nhnkcp_payco" disabled>PAYCO (페이코)</label><br>
<input type="checkbox" id="de_easy_nhnkcp_naverpay" name="de_easy_pays[]" value="nhnkcp_naverpay" <?php if(stripos($default['de_easy_pay_services'], 'nhnkcp_naverpay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nhnkcp_naverpay">NAVERPAY (네이버페이)</label><br>
<input type="checkbox" id="de_easy_nhnkcp_kakaopay" name="de_easy_pays[]" value="nhnkcp_kakaopay" <?php if(stripos($default['de_easy_pay_services'], 'nhnkcp_kakaopay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nhnkcp_kakaopay">KAKAOPAY (카카오페이)</label><br>
<input type="checkbox" id="de_easy_nhnkcp_applepay" name="de_easy_pays[]" value="nhnkcp_applepay" <?php if(stripos($default['de_easy_pay_services'], 'nhnkcp_applepay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nhnkcp_applepay">APPLEPAY (애플페이)</label>
<?php foreach (shop_easypay_catalog('kcp') as $easy_key => $easy_provider) { ?>
<input type="checkbox" name="de_easy_pays[]" id="de_easy_<?php echo $easy_key; ?>" value="<?php echo $easy_key; ?>"<?php echo in_array($easy_key, explode(',', $default['de_easy_pay_services']), true) ? ' checked' : ''; ?>>
<label for="de_easy_<?php echo $easy_key; ?>"><?php echo $easy_provider[0]; ?></label><br>
<?php } ?>
</td>
</tr>
<tr class="pg_info_fld kcp_info_fld">
<th scope="row"><label for="de_global_nhnkcp_naverpay">NHN KCP 네이버페이 사용</label></th>
<td>
<?php echo help("체크시 타 PG (토스페이먼츠, KG 이니시스) 사용중일때도 NHN_KCP 를 통한 네이버페이 간편결제를 사용할수 있습니다.\n실결제시 반드시 결제대행사 NHN_KCP 항목에 KCP SITE CODE와 NHN KCP SITE KEY를 입력해야 합니다."); ?>
<?php echo help("선택한 PG에서 네이버페이를 지원하지 않거나 사용 설정하지 않은 경우에만 NHN KCP 네이버페이를 보조 결제로 제공합니다. 기본 PG의 네이버페이가 활성화되어 있으면 병용 버튼과 요청 경로를 사용하지 않습니다. 기존 KCP 주문의 조회·취소는 유지됩니다.\n실결제시 반드시 결제대행사 NHN_KCP 항목에 KCP SITE CODE와 NHN KCP SITE KEY를 입력해야 합니다."); ?>
<input type="checkbox" id="de_global_nhnkcp_naverpay" name="de_easy_pays[]" value="global_nhnkcp_naverpay" <?php if(stripos($default['de_easy_pay_services'], 'global_nhnkcp_naverpay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_global_nhnkcp_naverpay">NAVERPAY (네이버페이)</label><br>
</td>
</tr>
@@ -889,7 +659,7 @@ if(!$default['de_kakaopay_cancelpwd']){
<tr class="pg_info_fld lg_info_fld">
<th scope="row"><label for="cf_lg_mert_key">토스페이먼츠(구버전) MERT KEY</label></th>
<td>
<?php echo help("토스페이먼츠(구버전) 상점 MertKey는 상점관리자 -> 개발자센터 -> API키 -> 머트 키에서 확인하실 수 있습니다.\n예) 95160cce09854ef44d2edb2bfb05f9f3"); ?>
<?php echo help("토스페이먼츠(구버전) 상점 MertKey는 상점관리자 -> 개발자센터 -> API키 -> 머트 키에서 확인하실 수 있습니다. 예) 95160cce09854ef44d2edb2bfb05f9f3\n실결제용 [라이브] 키와 테스트용 [테스트] 키는 서로 다르므로, <b>테스트로 결제시에는 [테스트] 키</b>로 변경하여 사용해주시기 바랍니다."); ?>
<input type="text" name="cf_lg_mert_key" value="<?php echo get_sanitize_input($config['cf_lg_mert_key']); ?>" id="cf_lg_mert_key" class="frm_input " size="36" maxlength="50">
</td>
</tr>
@@ -977,34 +747,18 @@ if(!$default['de_kakaopay_cancelpwd']){
<input type="text" name="de_inicis_pro_summary_days" value="<?php echo isset($default['de_inicis_pro_summary_days']) ? (int) $default['de_inicis_pro_summary_days'] : 1825; ?>" id="de_inicis_pro_summary_days" class="frm_input" size="6" maxlength="4"> 일
</td>
</tr>
<tr class="pg_info_fld inicis_info_fld">
<th scope="row">
<label for="de_samsung_pay_use">KG이니시스 삼성페이 사용</label>
<a href="http://sir.kr/main/service/samsungpay.php" target="_blank" class="kg_btn">삼성페이 서비스신청하기</a>
</th>
<?php foreach (array('inicis' => 'KG이니시스', 'toss' => '토스페이먼츠 API') as $easy_pg => $easy_title) { ?>
<tr class="pg_info_fld <?php echo $easy_pg; ?>_info_fld">
<th scope="row"><?php echo $easy_title; ?> 간편결제</th>
<td>
<?php echo help("KG이니시스와 별도로 <strong>삼성페이 사용 계약을 하신 경우</strong>에만 체크해주세요. INIpay PRO 사용 시 PC와 모바일 주문서에 삼성페이가 노출되며 삼성페이 결제창을 직접 호출합니다. 구버전 결제에서는 모바일 주문서에만 노출됩니다.<br>실결제 시 상점 아이디와 사용 중인 결제모듈의 인증키(PRO: HashKey, 구버전: 웹결제 사인키)를 입력해 주세요.", 50); ?>
<input type="checkbox" name="de_samsung_pay_use" value="1" id="de_samsung_pay_use"<?php echo $default['de_samsung_pay_use']?' checked':''; ?>> <label for="de_samsung_pay_use">사용</label>
</td>
</tr>
<tr class="pg_info_fld inicis_info_fld">
<th scope="row">
<label for="de_inicis_lpay_use">KG이니시스 L.pay 사용</label>
</th>
<td>
<?php echo help("체크 시 KG이니시스 L.pay를 사용합니다. INIpay PRO에서는 주문서에서 L.pay 선택 시 L.pay 결제창을 직접 호출합니다.<br>실결제 시 상점 아이디와 사용 중인 결제모듈의 인증키(PRO: HashKey, 구버전: 웹결제 사인키)를 입력해 주세요.", 50); ?>
<input type="checkbox" name="de_inicis_lpay_use" value="1" id="de_inicis_lpay_use"<?php echo $default['de_inicis_lpay_use']?' checked':''; ?>> <label for="de_inicis_lpay_use">사용</label>
</td>
</tr>
<tr class="pg_info_fld inicis_info_fld">
<th scope="row">
<label for="de_inicis_kakaopay_use">KG이니시스 카카오페이 사용</label>
</th>
<td>
<?php echo help("체크 시 KG이니시스 결제의 카카오페이를 사용합니다. INIpay PRO에서는 주문서에서 카카오페이 선택 시 카카오페이 결제창을 직접 호출합니다.<br>실결제 시 상점 아이디와 사용 중인 결제모듈의 인증키(PRO: HashKey, 구버전: 웹결제 사인키)를 입력해 주세요.", 50); ?>
<input type="checkbox" name="de_inicis_kakaopay_use" value="1" id="de_inicis_kakaopay_use"<?php echo $default['de_inicis_kakaopay_use']?' checked':''; ?>> <label for="de_inicis_kakaopay_use">사용</label>
<?php echo help("PG사 간편결제 버튼 사용을 '노출함'으로 설정하고, 해당 PG와 계약하여 사용할 수 있는 수단만 선택하세요. 계약 상태는 PG사에 확인해야 하며 이 화면에서 자동 조회하지 않습니다. 자체창 호출에 별도 계약이 필요할 수 있고 일부 수단은 테스트 결제를 지원하지 않습니다. 실제 MID에서 승인·취소를 확인한 후 제공하세요.\n애플페이는 iOS 모바일에서만 표시합니다. 삼성페이는 PC에서 휴대폰으로 연결하며 구 INIpay에서는 모바일에서만 표시합니다.\nKG이니시스는 삼성페이·L.pay·카카오페이를 지원하며 INIpay PRO는 HashKey, 구버전은 웹결제 사인키가 필요합니다."); ?>
<?php foreach (shop_easypay_catalog($easy_pg) as $easy_key => $easy_provider) { ?>
<input type="checkbox" name="de_easy_pays[]" id="de_easy_<?php echo $easy_key; ?>" value="<?php echo $easy_key; ?>"<?php echo in_array($easy_key, explode(',', $default['de_easy_pay_services']), true) ? ' checked' : ''; ?>>
<label for="de_easy_<?php echo $easy_key; ?>"><?php echo $easy_provider[0]; ?></label><br>
<?php } ?>
</td>
</tr>
<?php } ?>
<tr class="pg_info_fld inicis_info_fld">
<th scope="row">
<label for="de_inicis_cartpoint_use">KG이니시스 신용카드 포인트 결제</label>
@@ -1014,46 +768,6 @@ if(!$default['de_kakaopay_cancelpwd']){
<input type="checkbox" name="de_inicis_cartpoint_use" value="1" id="de_inicis_cartpoint_use"<?php echo $default['de_inicis_cartpoint_use']?' checked':''; ?>> <label for="de_inicis_cartpoint_use">사용</label>
</td>
</tr>
<tr class="kakao_info_fld">
<th scope="row">
<label for="de_kakaopay_mid">카카오페이 상점아이디<br>( KG이니시스 )</label>
<a href="http://sir.kr/main/service/kakaopay.php?kk=yc5" target="_blank" class="kakao_btn">카카오페이 서비스신청하기</a>
</th>
<td>
<?php echo help("KG이니시스로 부터 카카오페이 간편결제만 사용용도로 발급 받으신 상점아이디(MID) 10자리 중 SIRK 을 제외한 나머지 6자리를 입력 합니다."); ?>
<span class="sitecode">SIRK</span> <input type="text" name="de_kakaopay_mid" value="<?php echo get_sanitize_input($default['de_kakaopay_mid']); ?>" id="de_kakaopay_mid" class="frm_input code_input" size="10" maxlength="7">
</td>
</tr>
<tr class="kakao_info_fld">
<th scope="row"><label for="de_kakaopay_key">카카오페이 상점키<br>( KG이니시스 )</label></th>
<td>
<?php echo help("SIRK****** 아이디로 KG이니시스에서 발급받은 웹결제 사인키를 입력합니다.\nKG이니시스 상점관리자 > 상점정보 > 계약정보 > 부가정보의 웹결제 signkey생성 조회 버튼 클릭, 팝업창에서 생성 버튼 클릭 후 해당 값을 입력합니다."); ?>
<input type="text" name="de_kakaopay_key" value="<?php echo get_sanitize_input($default['de_kakaopay_key']); ?>" id="de_kakaopay_key" class="frm_input" size="100">
</td>
</tr>
<tr class="kakao_info_fld">
<th scope="row"><label for="de_kakaopay_cancelpwd">카카오페이 키패스워드<br>( KG이니시스 )</label></th>
<td>
<?php echo help("SIRK****** 아이디로 KG이니시스에서 발급받은 4자리 상점 키패스워드를 입력합니다.\nKG이니시스 상점관리자 패스워드와 관련이 없습니다.\n키패스워드 값을 확인하시려면 상점측에 발급된 키파일 안의 readme.txt 파일을 참조해 주십시오"); ?>
<input type="text" name="de_kakaopay_cancelpwd" value="<?php echo get_sanitize_input($default['de_kakaopay_cancelpwd']); ?>" id="de_kakaopay_cancelpwd" class="frm_input" size="20">
</td>
</tr>
<tr class="kakao_info_fld">
<th scope="row">
<label for="de_kakaopay_enckey">KG이니시스<br>카카오페이 사용</label>
</th>
<td>
<?php echo help("체크시 카카오페이 (KG 이니시스)를 사용합니다. <br >KG 이니시스의 SIRK****** 아이디를 받은 상점만 해당됩니다.", 50); ?>
<input type="checkbox" name="de_kakaopay_enckey" value="1" id="de_kakaopay_enckey"<?php echo $default['de_kakaopay_enckey']?' checked':''; ?>> <label for="de_kakaopay_enckey">사용</label>
</td>
</tr>
<tr class="kakao_info_fld" style="display:none">
<th scope="row"><label for="de_kakaopay_hashkey">카카오페이 상점 HashKey</label></th>
<td>
<?php echo help("카카오페이로 부터 발급 받으신 상점 인증 전용 HashKey를 입력합니다."); ?>
<input type="text" name="de_kakaopay_hashkey" value="<?php echo get_sanitize_input($default['de_kakaopay_hashkey']); ?>" id="de_kakaopay_hashkey" class="frm_input" size="20">
</td>
</tr>
<tr class="pg_info_fld nicepay_info_fld" id="nicepay_info_anchor">
<th scope="row"><label for="de_nicepay_mid">NICEPAY MID</label><br><a href="http://sir.kr/main/service/nicepayments_pg.php" target="_blank" id="scf_nicepay_reg" class="nicepay_btn">NICEPAY 신청하기</a></th>
@@ -1075,14 +789,10 @@ if(!$default['de_kakaopay_cancelpwd']){
<th scope="row"><label for="de_nicepay_easy_pays">NICEPAY 간편결제</label></th>
<td>
<?php echo help("체크시 NICEPAY 간편결제들을 활성화 합니다.\nNICEPAY > 간편결제는 테스트결제가 되지 않습니다. 실결제에만 정상작동 합니다.\n애플페이는 IOS 기기에 모바일결제만 가능합니다."); ?>
<input type="checkbox" id="de_easy_nicepay_samsungpay" name="de_easy_pays[]" value="nicepay_samsungpay" <?php if(stripos($default['de_easy_pay_services'], 'nicepay_samsungpay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nicepay_samsungpay" disabled>삼성페이</label><br>
<input type="checkbox" id="de_easy_nicepay_naverpay" name="de_easy_pays[]" value="nicepay_naverpay" <?php if(stripos($default['de_easy_pay_services'], 'nicepay_naverpay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nicepay_naverpay">NAVERPAY (네이버페이)</label><br>
<input type="checkbox" id="de_easy_nicepay_kakaopay" name="de_easy_pays[]" value="nicepay_kakaopay" <?php if(stripos($default['de_easy_pay_services'], 'nicepay_kakaopay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nicepay_kakaopay">KAKAOPAY (카카오페이)</label><br>
<input type="checkbox" id="de_easy_nicepay_applepay" name="de_easy_pays[]" value="nicepay_applepay" <?php if(stripos($default['de_easy_pay_services'], 'nicepay_applepay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nicepay_applepay">APPLEPAY (애플페이)</label><br>
<input type="checkbox" id="de_easy_nicepay_paycopay" name="de_easy_pays[]" value="nicepay_paycopay" <?php if(stripos($default['de_easy_pay_services'], 'nicepay_paycopay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nicepay_paycopay">페이코</label><br>
<input type="checkbox" id="de_easy_nicepay_skpay" name="de_easy_pays[]" value="nicepay_skpay" <?php if(stripos($default['de_easy_pay_services'], 'nicepay_skpay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nicepay_skpay">SK페이</label><br>
<input type="checkbox" id="de_easy_nicepay_ssgpay" name="de_easy_pays[]" value="nicepay_ssgpay" <?php if(stripos($default['de_easy_pay_services'], 'nicepay_ssgpay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nicepay_ssgpay">SSG페이</label><br>
<input type="checkbox" id="de_easy_nicepay_lpay" name="de_easy_pays[]" value="nicepay_lpay" <?php if(stripos($default['de_easy_pay_services'], 'nicepay_lpay') !== false){ echo 'checked="checked"'; } ?> > <label for="de_easy_nicepay_lpay">LPAY</label>
<?php foreach (shop_easypay_catalog('nicepay') as $easy_key => $easy_provider) { ?>
<input type="checkbox" name="de_easy_pays[]" id="de_easy_<?php echo $easy_key; ?>" value="<?php echo $easy_key; ?>"<?php echo in_array($easy_key, explode(',', $default['de_easy_pay_services']), true) ? ' checked' : ''; ?>>
<label for="de_easy_<?php echo $easy_key; ?>"><?php echo $easy_provider[0]; ?></label><br>
<?php } ?>
</td>
</tr>
@@ -1924,7 +1634,7 @@ function fconfig_check(f)
}
} else if ( f.de_pg_service.value == "lg" ) {
if( f.cf_lg_mid.value && f.cf_lg_mert_key.value && parseInt(f.de_card_test.value) > 0 ){
pg_msg = "토스페이먼츠(구버전)";
msg += "(주의!) 토스페이먼츠(구버전) 결제의 결제 설정이 현재 테스트결제로 되어 있습니다.\nMERT KEY를 [테스트]키로 설정한 후 테스트결제를 진행해주세요.\n쇼핑몰 운영중이면 반드시 실결제 전환 및 [라이브]키로 설정하여 운영하셔야 합니다.\n실결제로 변경하려면 결제설정 탭 -> 결제 테스트에서 실결제를 선택해 주세요.\n정말로 테스트결제로 설정하시겠습니까?";
}
} else if ( f.de_pg_service.value == "toss" ) {
if( f.cf_lg_mid.value && f.cf_toss_client_key.value && f.cf_toss_secret_key.value && parseInt(f.de_card_test.value) > 0 ){
@@ -2239,26 +1949,7 @@ if($default['de_iche_use'] || $default['de_vbank_use'] || $default['de_hp_use']
}
}
// 카카오페이의 경우 log 디렉토리 체크
if($default['de_kakaopay_mid'] && $default['de_kakaopay_key'] && $default['de_kakaopay_enckey'] && $default['de_kakaopay_hashkey'] && $default['de_kakaopay_cancelpwd']) {
$log_path = G5_SHOP_PATH.'/kakaopay/log';
if(!is_dir($log_path)) {
echo '<script>'.PHP_EOL;
echo 'alert("'.str_replace(G5_PATH.'/', '', G5_SHOP_PATH).'/kakaopay 폴더 안에 log 폴더를 생성하신 후 쓰기권한을 부여해 주십시오.\n> mkdir log\n> chmod 707 log");'.PHP_EOL;
echo '</script>'.PHP_EOL;
} else {
if(!is_writable($log_path)) {
echo '<script>'.PHP_EOL;
echo 'alert("'.str_replace(G5_PATH.'/', '',$log_path).' 폴더에 쓰기권한을 부여해 주십시오.\n> chmod 707 log");'.PHP_EOL;
echo '</script>'.PHP_EOL;
} else {
if( function_exists('check_log_folder') && is_writable($log_path) ){
check_log_folder($log_path);
}
}
}
}
}
+16 -25
View File
@@ -19,8 +19,8 @@ $logo_img_fields = array('logo_img', 'logo_img2', 'mobile_logo_img', 'mobile_log
foreach ($logo_img_fields as $logo_img_field) {
if (isset($_FILES[$logo_img_field]['name']) && $_FILES[$logo_img_field]['name']) {
$filename = get_safe_filename($_FILES[$logo_img_field]['name']);
if (is_disallowed_svg_filename($filename)) {
alert('허용되지 않는 파일 확장자입니다. (svg, svgz)');
if (is_disallowed_active_filename($filename)) {
alert('허용되지 않는 파일 확장자입니다.');
}
}
}
@@ -88,15 +88,16 @@ if(isset($_POST['de_taxsave_types_transfer']) && $_POST['de_taxsave_types_transf
$de_taxsave_types .= ',transfer';
}
// NHN_KCP 간편결제 체크
$de_easy_pay_services = '';
if(isset($_POST['de_easy_pays'])){
$tmps = array();
foreach( (array) $_POST['de_easy_pays'] as $v ){
$tmps[] = preg_replace('/[^0-9a-z_\-]/i', '', $v);
}
$de_easy_pay_services = implode(",", $tmps);
// 알려진 PG별 간편결제 설정만 저장한다. 선택 해제도 명시적으로 보존한다.
$easy_allowed = array('global_nhnkcp_naverpay', 'used_nhnkcp_naverpay_point');
foreach (array('kcp', 'inicis', 'toss', 'nicepay') as $easy_pg) {
$easy_allowed = array_merge($easy_allowed, array_keys(shop_easypay_catalog($easy_pg)));
}
$easy_selected = array();
foreach (isset($_POST['de_easy_pays']) ? (array) $_POST['de_easy_pays'] : array() as $easy_key) {
if (is_string($easy_key) && in_array($easy_key, $easy_allowed, true)) $easy_selected[] = $easy_key;
}
$de_easy_pay_services = implode(',', array_unique(array_merge($easy_selected, array('inicis_configured', 'toss_configured'))));
//KVE-2019-0689, KVE-2019-0691, KVE-2019-0694
$check_sanitize_keys = array(
@@ -189,11 +190,6 @@ $check_sanitize_keys = array(
'de_inicis_cartpoint_use', //KG이니시스 신용카드 포인트 결제
'de_nicepay_mid', //NICEPAY 상점아이디
'de_nicepay_key', //NICEPAY 상점키
'de_kakaopay_mid', //카카오페이 상점MID
'de_kakaopay_key', //카카오페이 상점키
'de_kakaopay_enckey', //카카오페이 상점 EncKey
'de_kakaopay_hashkey', //카카오페이 상점 HashKey
'de_kakaopay_cancelpwd', //카카오페이 결제취소 비밀번호
'de_naverpay_mid', //네이버페이 가맹점 아이디
'de_naverpay_cert_key', //네이버페이 가맹점 인증키
'de_naverpay_button_key', //네이버페이 버튼 인증키
@@ -269,6 +265,11 @@ foreach( $check_sanitize_keys as $key ){
}
}
// 구버전 결제 코드 및 기존 스킨과의 호환을 위해 개별 컬럼도 동기화한다.
foreach (shop_easypay_legacy_keys() as $easy_key => $legacy_key) {
$$legacy_key = (int) in_array($easy_key, $easy_selected, true);
}
$de_inicis_pro_use = !empty($de_inicis_pro_use) ? 1 : 0;
$de_inicis_hash_key = preg_replace('/[^A-Za-z0-9+\/=_-]/', '', $de_inicis_hash_key);
@@ -300,11 +301,6 @@ if($de_pg_service == 'kcp' && ! $de_card_test && ($de_iche_use || $de_vbank_use
alert('NHN KCP SITE KEY를 입력해 주십시오.');
}
if( $de_kakaopay_enckey && ($de_pg_service === 'inicis' || $de_inicis_lpay_use || $de_inicis_kakaopay_use) ){
$warning_msg = 'KG 이니시스 결제 또는 L.pay 또는 KG이니시스 카카오페이를 사용시 결제모듈 중복문제로 카카오페이를 활성화 할수 없습니다. \\n\\n카카오페이 사용을 비활성화 합니다.';
$de_kakaopay_enckey = '';
}
//
// 영카트 default
@@ -481,11 +477,6 @@ $sql = " update {$g5['g5_shop_default_table']}
de_hp_use = '{$de_hp_use}',
de_escrow_use = '{$de_escrow_use}',
de_tax_flag_use = '{$de_tax_flag_use}',
de_kakaopay_mid = '{$de_kakaopay_mid}',
de_kakaopay_key = '{$de_kakaopay_key}',
de_kakaopay_enckey = '{$de_kakaopay_enckey}',
de_kakaopay_hashkey = '{$de_kakaopay_hashkey}',
de_kakaopay_cancelpwd = '{$de_kakaopay_cancelpwd}',
de_member_reg_coupon_use = '{$de_member_reg_coupon_use}',
de_member_reg_coupon_term = '{$de_member_reg_coupon_term}',
de_member_reg_coupon_price = '{$de_member_reg_coupon_price}',
+26
View File
@@ -0,0 +1,26 @@
<?php
if (!defined('_GNUBOARD_')) exit;
// 날짜 없는 최초 조회와 잘못된 날짜 조회를 구분한다. 오류는 false로 반환한다.
function shop_admin_date_range($params, $default_to = '')
{
if (!array_key_exists('fr_date', $params) && !array_key_exists('to_date', $params)) {
return array('', $default_to);
}
foreach (array('fr_date', 'to_date') as $key) {
if (!isset($params[$key]) || !is_string($params[$key]) || !preg_match('/\A[0-9]{8}\z/', $params[$key])) {
return false;
}
$date = $params[$key];
if (!checkdate((int) substr($date, 4, 2), (int) substr($date, 6, 2), (int) substr($date, 0, 4))) {
return false;
}
}
if ($params['fr_date'] > $params['to_date']) {
return false;
}
return array($params['fr_date'], $params['to_date']);
}
+3 -2
View File
@@ -30,6 +30,9 @@ if($w == 'd') {
// 주문정보
$data = unserialize(base64_decode($od['dt_data']));
if (isset($data['od_settle_case']) && $data['od_settle_case'] === 'KAKAOPAY') {
alert('SIRK 전용 카카오페이 미완료 주문은 결제 상태를 이니시스에서 확인한 뒤 사후지원 담당자에게 문의해 주십시오.');
}
$data_od_cp_id = isset($data['od_cp_id']) ? safe_replace_regex($data['od_cp_id'], 'cp_id') : '';
$data_sc_cp_id = isset($data['sc_cp_id']) ? safe_replace_regex($data['sc_cp_id'], 'cp_id') : '';
@@ -210,8 +213,6 @@ if($default['de_tax_flag_use']) {
}
$od_pg = $default['de_pg_service'];
if($data['od_settle_case'] == 'KAKAOPAY')
$od_pg = 'KAKAOPAY';
$od_email = get_email_address($data['od_email']);
$od_name = addslashes(clean_xss_tags($data['od_name']));
+1 -5
View File
@@ -73,10 +73,6 @@ for ($i=0; $row=sql_fetch_array($result); $i++)
}
// 모바일 1줄당 이미지수 필드 추가
if(!sql_query(" select ev_mobile_list_row from {$g5['g5_shop_event_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_event_table']}`
ADD `ev_mobile_list_row` int(11) NOT NULL DEFAULT '0' AFTER `ev_mobile_list_mod` ", true);
}
include_once (G5_ADMIN_PATH.'/admin.head.php');
?>
@@ -455,4 +451,4 @@ function feventform_check(f)
<?php
include_once (G5_ADMIN_PATH.'/admin.tail.php');
include_once (G5_ADMIN_PATH.'/admin.tail.php');
-25
View File
@@ -158,29 +158,13 @@ for ($i=0; $row=sql_fetch_array($result); $i++)
}
// 재입고알림 설정 필드 추가
if(!sql_query(" select it_stock_sms from {$g5['g5_shop_item_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_item_table']}`
ADD `it_stock_sms` tinyint(4) NOT NULL DEFAULT '0' AFTER `it_stock_qty` ", true);
}
// 추가옵션 포인트 설정 필드 추가
if(!sql_query(" select it_supply_point from {$g5['g5_shop_item_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_item_table']}`
ADD `it_supply_point` int(11) NOT NULL DEFAULT '0' AFTER `it_point_type` ", true);
}
// 상품메모 필드 추가
if(!sql_query(" select it_shop_memo from {$g5['g5_shop_item_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_item_table']}`
ADD `it_shop_memo` text NOT NULL AFTER `it_use_avg` ", true);
}
// 지식쇼핑 PID 필드추가
// 상품메모 필드 추가
if(!sql_query(" select ec_mall_pid from {$g5['g5_shop_item_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_item_table']}`
ADD `ec_mall_pid` varchar(255) NOT NULL AFTER `it_shop_memo` ", true);
}
$pg_anchor ='<ul class="anchor">
<li><a href="#anc_sitfrm_cate">상품분류</a></li>
@@ -199,17 +183,8 @@ $pg_anchor ='<ul class="anchor">
// 쿠폰적용안함 설정 필드 추가
if(!sql_query(" select it_nocoupon from {$g5['g5_shop_item_table']} limit 1", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_item_table']}`
ADD `it_nocoupon` tinyint(4) NOT NULL DEFAULT '0' AFTER `it_use` ", true);
}
// 스킨필드 추가
if(!sql_query(" select it_skin from {$g5['g5_shop_item_table']} limit 1", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_item_table']}`
ADD `it_skin` varchar(255) NOT NULL DEFAULT '' AFTER `ca_id3`,
ADD `it_mobile_skin` varchar(255) NOT NULL DEFAULT '' AFTER `it_skin` ", true);
}
?>
<form name="fitemform" action="./itemformupdate.php" method="post" enctype="MULTIPART/FORM-DATA" autocomplete="off" onsubmit="return fitemformcheck(this)">
+19 -7
View File
@@ -4,13 +4,18 @@ include_once('./_common.php');
auth_check_menu($auth, $sub_menu, "r");
include_once('./date_filter.lib.php');
$date_range = shop_admin_date_range($_GET, date('Ymd'));
if ($date_range === false) {
http_response_code(400);
alert('조회 기간은 올바른 시작일과 종료일을 YYYYMMDD 형식으로 입력해 주십시오. 시작일은 종료일보다 늦을 수 없습니다.');
}
list($fr_date, $to_date) = $date_range;
$g5['title'] = '상품판매순위';
include_once (G5_ADMIN_PATH.'/admin.head.php');
include_once(G5_PLUGIN_PATH.'/jquery-ui/datepicker.php');
$fr_date = (isset($_GET['fr_date']) && preg_match("/[0-9]/", $_GET['fr_date'])) ? $_GET['fr_date'] : '';
$to_date = (isset($_GET['to_date']) && preg_match("/[0-9]/", $_GET['to_date'])) ? $_GET['to_date'] : date("Ymd", time());
$doc = isset($_GET['doc']) ? clean_xss_tags($_GET['doc'], 1, 1) : '';
$sort1 = (isset($_GET['sort1']) && in_array($_GET['sort1'], array('ct_status_1', 'ct_status_2', 'ct_status_3', 'ct_status_4', 'ct_status_5', 'ct_status_6', 'ct_status_7', 'ct_status_8', 'ct_status_9', 'ct_status_sum'))) ? $_GET['sort1'] : 'ct_status_sum';
$sort2 = (isset($_GET['sort2']) && in_array($_GET['sort2'], array('desc', 'asc'))) ? $_GET['sort2'] : 'desc';
@@ -57,7 +62,14 @@ $sql = $sql . " limit $from_record, $rows ";
$result = sql_query($sql);
//$qstr = 'page='.$page.'&amp;sort1='.$sort1.'&amp;sort2='.$sort2;
$qstr1 = $qstr.'&amp;fr_date='.$fr_date.'&amp;to_date='.$to_date.'&amp;sel_ca_id='.$sel_ca_id;
// 검증된 날짜만 URL에 전달한다. 최초 조회의 빈 날짜는 링크에서 생략한다.
$filter_query = array('sel_ca_id' => $sel_ca_id);
if ($fr_date !== '' && $to_date !== '') {
$filter_query['fr_date'] = $fr_date;
$filter_query['to_date'] = $to_date;
}
$qstr1 = htmlspecialchars(http_build_query($filter_query, '', '&'), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$paging_query = htmlspecialchars(http_build_query(array_merge($filter_query, array('sort1' => $sort1, 'sort2' => $sort2)), '', '&'), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목록</a>';
?>
@@ -92,9 +104,9 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
기간설정
<label for="fr_date" class="sound_only">시작일</label>
<input type="text" name="fr_date" value="<?php echo $fr_date; ?>" id="fr_date" required class="required frm_input" size="8" maxlength="8"> 에서
<input type="text" name="fr_date" value="<?php echo htmlspecialchars($fr_date, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?>" id="fr_date" required class="required frm_input" size="8" maxlength="8"> 에서
<label for="to_date" class="sound_only">종료일</label>
<input type="text" name="to_date" value="<?php echo $to_date; ?>" id="to_date" required class="required frm_input" size="8" maxlength="8"> 까지
<input type="text" name="to_date" value="<?php echo htmlspecialchars($to_date, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?>" id="to_date" required class="required frm_input" size="8" maxlength="8"> 까지
<input type="submit" value="검색" class="btn_submit">
</form>
@@ -162,7 +174,7 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
</table>
</div>
<?php echo get_paging(G5_IS_MOBILE ? $config['cf_mobile_pages'] : $config['cf_write_pages'], $page, $total_page, "{$_SERVER['SCRIPT_NAME']}?$qstr1&amp;page="); ?>
<?php echo get_paging(G5_IS_MOBILE ? $config['cf_mobile_pages'] : $config['cf_write_pages'], $page, $total_page, "{$_SERVER['SCRIPT_NAME']}?$paging_query&amp;page="); ?>
<script>
$(function() {
+1 -13
View File
@@ -11,18 +11,6 @@ include_once (G5_ADMIN_PATH.'/admin.head.php');
if(!isset($g5['g5_shop_item_stocksms_table']))
die('<meta charset="utf-8">dbconfig.php 파일에 <strong>$g5[\'g5_shop_item_stocksms_table\'] = G5_SHOP_TABLE_PREFIX.\'item_stocksms\';</strong> 를 추가해 주세요.');
if(!sql_query(" select ss_id from {$g5['g5_shop_item_stocksms_table']} limit 1", false)) {
sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['g5_shop_item_stocksms_table']}` (
`ss_id` int(11) NOT NULL AUTO_INCREMENT,
`it_id` varchar(20) NOT NULL DEFAULT '',
`ss_hp` varchar(255) NOT NULL DEFAULT '',
`ss_send` tinyint(4) NOT NULL DEFAULT '0',
`ss_send_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`ss_datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`ss_ip` varchar(25) NOT NULL DEFAULT '',
PRIMARY KEY (`ss_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ", true);
}
$doc = isset($_GET['doc']) ? clean_xss_tags($_GET['doc'], 1, 1) : '';
$sort1 = (isset($_GET['sort1']) && in_array($_GET['sort1'], array('it_id', 'ss_hp', 'ss_send', 'ss_send_time', 'ss_datetime'))) ? $_GET['sort1'] : 'ss_send';
@@ -184,4 +172,4 @@ function fitemstocksms_submit(f)
</script>
<?php
include_once (G5_ADMIN_PATH.'/admin.tail.php');
include_once (G5_ADMIN_PATH.'/admin.tail.php');
+1 -8
View File
@@ -18,13 +18,6 @@ if (!$is['is_id'])
alert('등록된 자료가 없습니다.');
// 사용후기 의 답변 필드 추가
if (!isset($is['is_reply_subject'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_item_use_table']}`
ADD COLUMN `is_reply_subject` VARCHAR(255) NOT NULL DEFAULT '' AFTER `is_confirm`,
ADD COLUMN `is_reply_content` TEXT NOT NULL AFTER `is_reply_subject`,
ADD COLUMN `is_reply_name` VARCHAR(25) NOT NULL DEFAULT '' AFTER `is_reply_content`
", true);
}
$name = get_sideview($is['mb_id'], get_text($is['is_name']), $is['mb_email'], $is['mb_homepage']);
@@ -117,4 +110,4 @@ function fitemuseform_submit(f)
</script>
<?php
include_once (G5_ADMIN_PATH.'/admin.tail.php');
include_once (G5_ADMIN_PATH.'/admin.tail.php');
+10 -23
View File
@@ -76,27 +76,10 @@ $sql = " select it_id,
$result = sql_query($sql);
// 주소 참고항목 필드추가
if(!isset($od['od_addr3'])) {
sql_query(" ALTER TABLE `{$g5['g5_shop_order_table']}`
ADD `od_addr3` varchar(255) NOT NULL DEFAULT '' AFTER `od_addr2`,
ADD `od_b_addr3` varchar(255) NOT NULL DEFAULT '' AFTER `od_b_addr2` ", true);
}
// 배송목록에 참고항목 필드추가
if(!sql_query(" select ad_addr3 from {$g5['g5_shop_order_address_table']} limit 1", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_order_address_table']}`
ADD `ad_addr3` varchar(255) NOT NULL DEFAULT '' AFTER `ad_addr2` ", true);
}
// 결제 PG 필드 추가
if(!sql_query(" select od_pg from {$g5['g5_shop_order_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_order_table']}`
ADD `od_pg` varchar(255) NOT NULL DEFAULT '' AFTER `od_mobile`,
ADD `od_casseqno` varchar(255) NOT NULL DEFAULT '' AFTER `od_escrow` ", true);
// 주문 결제 PG kcp로 설정
sql_query(" update {$g5['g5_shop_order_table']} set od_pg = 'kcp' ");
}
// LG 현금영수증 JS
if($od['od_pg'] == 'lg') {
@@ -256,6 +239,13 @@ if ($is_nicepay_vbank_pg_cancel) {
<input type="hidden" name="search" value="<?php echo $search; ?>">
<input type="hidden" name="page" value="<?php echo $page;?>">
<input type="hidden" name="pg_cancel" value="0">
<?php if ($od['od_pg'] === 'KAKAOPAY') { ?>
<div class="local_desc01 local_desc">
<p>SIRK 전용 카카오페이의 자동 취소 지원이 종료되었습니다. <a href="https://iniweb.inicis.com/" target="_blank" rel="noopener noreferrer">이니시스 상점관리자</a>에서 전체·부분취소를 처리한 뒤 주문 상태와 환불금액을 정리해 주십시오. 이 화면의 상태 변경은 PG 취소를 수행하지 않습니다.</p>
<p>기존 이용 고객 지원: 김민섭 <a href="mailto:minsup@sir.kr">minsup@sir.kr</a></p>
<label><input type="checkbox" name="sirk_cancel_confirmed" value="1"> 이니시스에서 실제 취소 여부와 금액을 확인했습니다. 선택한 상품의 주문 상태만 변경합니다.</label>
</div>
<?php } ?>
<input type="hidden" name="submit_ct_status" value="">
<?php if ($is_nicepay_vbank_pg_cancel) { ?>
<input type="hidden" name="RefundAcctNo" value="">
@@ -1035,7 +1025,7 @@ if ($is_nicepay_vbank_pg_cancel) {
<?php if($od['od_status'] == '주문' && $od['od_misu'] > 0) { ?>
<a href="./personalpayform.php?popup=yes&amp;od_id=<?php echo $od_id; ?>" id="personalpay_add" class="btn btn_02">개인결제추가</a>
<?php } ?>
<?php if($od['od_misu'] < 0 && ($od['od_receipt_price'] - $od['od_refund_price']) > 0 && ($od['od_settle_case'] == '신용카드' || $od['od_settle_case'] == '계좌이체' || $od['od_settle_case'] == 'KAKAOPAY' || ($od['od_pg'] == 'nicepay' && $od['od_settle_case'] == '간편결제'))) { ?>
<?php if($od['od_pg'] !== 'KAKAOPAY' && $od['od_misu'] < 0 && ($od['od_receipt_price'] - $od['od_refund_price']) > 0 && ($od['od_settle_case'] == '신용카드' || $od['od_settle_case'] == '계좌이체' || $od['od_settle_case'] == 'KAKAOPAY' || ($od['od_pg'] == 'nicepay' && $od['od_settle_case'] == '간편결제'))) { ?>
<a href="./orderpartcancel.php?od_id=<?php echo $od_id; ?>" id="orderpartcancel" class="btn btn_02"><?php echo get_text($od['od_settle_case']); ?> 부분취소</a>
<?php } ?>
<a href="./orderlist.php?<?php echo $qstr; ?>" class="btn btn_02">목록</a>
@@ -1472,16 +1462,13 @@ function form_submit(f)
var msg = "";
<?php if (is_cancel_shop_pg_order($od)) { ?>
<?php if ($od['od_pg'] !== 'KAKAOPAY' && is_cancel_shop_pg_order($od)) { ?>
if(status == "취소" || status == "반품" || status == "품절") {
var $ct_chk = $("input[name^=ct_chk]");
var chk_cnt = $ct_chk.length;
var chked_cnt = $ct_chk.filter(":checked").length;
<?php if($od['od_pg'] == 'KAKAOPAY') { ?>
var cancel_pg = "카카오페이";
<?php } else { ?>
var cancel_pg = "PG사의 <?php echo get_text($od['od_settle_case']); ?>";
<?php } ?>
// 체크하지 않은 나머지 품목이 모두 취소류 상태이면 이번 처리로 주문 전체가 취소된다.
var remain_active_cnt = $ct_chk.not(":checked").filter(function() {
+20 -7
View File
@@ -29,6 +29,16 @@ if (in_array($ct_status, $status_normal) || in_array($ct_status, $status_cancel)
alert('변경할 상태가 올바르지 않습니다.');
}
// SIRK 주문은 PG 취소를 호출하지 않고, 실제 취소를 확인한 운영자만 로컬 상태를 정리한다.
if (in_array($ct_status, $status_cancel, true)) {
$sirk_order = sql_fetch(" select od_pg from {$g5['g5_shop_order_table']} where od_id = '" . sql_escape_string($od_id) . "' ");
if (isset($sirk_order['od_pg']) && $sirk_order['od_pg'] === 'KAKAOPAY') {
if ($pg_cancel || empty($_POST['sirk_cancel_confirmed'])) {
alert('SIRK 전용 카카오페이는 이니시스 상점관리자에서 실제 취소 여부를 확인한 뒤 주문 상태만 변경해 주십시오.');
}
}
}
// INIpay PRO 전체취소는 로컬 상품상태를 바꾸기 전에 PG 취소를 먼저 확정한다.
// PG 취소 실패 후에도 주문만 취소되어 가상계좌 입금이 남는 상태를 방지한다.
$inicis_pro_cancel_preprocessed = false;
@@ -312,8 +322,10 @@ for ($i=0; $i<$cnt; $i++)
$now = G5_TIME_YMDHIS;
$ct_history="\n$ct_status|{$member['mb_id']}|$now|$REMOTE_ADDR";
$complete_time_sql = get_cart_complete_time_sql($ct_status);
$sql = " update {$g5['g5_shop_cart_table']}
set ct_point_use = '$point_use',
set ct_complete_time = $complete_time_sql,
ct_point_use = '$point_use',
ct_stock_use = '$stock_use',
ct_status = '$ct_status',
ct_history = CONCAT(ct_history,'$ct_history')
@@ -326,6 +338,11 @@ for ($i=0; $i<$cnt; $i++)
$arr_it_id[] = $ct['it_id'];
}
// 완료 처리 직후 실행하여 0일 설정도 즉시 적용한다.
if ($ct_status == '완료') {
save_order_point();
}
// 상품 판매수량 반영
if(is_array($arr_it_id) && !empty($arr_it_id)) {
$unq_it_id = array_unique($arr_it_id);
@@ -455,12 +472,8 @@ if (in_array($ct_status, $status_cancel)) {
break;
case 'KAKAOPAY':
include_once(G5_SHOP_PATH.'/settle_kakaopay.inc.php');
$_REQUEST['TID'] = $od['od_tno'];
$_REQUEST['Amt'] = $od['od_receipt_price'];
$_REQUEST['CancelMsg'] = '쇼핑몰 운영자 승인 취소';
$_REQUEST['PartialCancelCode'] = 0;
include G5_SHOP_PATH.'/kakaopay/kakaopay_cancel.php';
$pg_res_cd = 'SIRK_RETIRED';
$pg_res_msg = 'SIRK 전용 카카오페이는 이니시스 상점관리자에서 취소해 주십시오.';
break;
default:
include_once(G5_SHOP_PATH.'/settle_kcp.inc.php');
+1 -7
View File
@@ -140,12 +140,6 @@ $qstr = "$qstr1&amp;sort1=$sort1&amp;sort2=$sort2&amp;page=$page";
$listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목록</a>';
// 주문삭제 히스토리 테이블 필드 추가
if(!sql_query(" select mb_id from {$g5['g5_shop_order_delete_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_order_delete_table']}`
ADD `mb_id` varchar(20) NOT NULL DEFAULT '' AFTER `de_data`,
ADD `de_ip` varchar(255) NOT NULL DEFAULT '' AFTER `mb_id`,
ADD `de_datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `de_ip` ", true);
}
if( function_exists('pg_setting_check') ){
pg_setting_check(true);
@@ -681,4 +675,4 @@ function forderlist_submit(f)
</script>
<?php
include_once (G5_ADMIN_PATH.'/admin.tail.php');
include_once (G5_ADMIN_PATH.'/admin.tail.php');
+5
View File
@@ -161,6 +161,11 @@ for ($i=0; $i<$count_post_chk; $i++)
}
// 일괄 완료 처리도 0일 설정이면 즉시 적립한다.
if ($od_status == '완료') {
save_order_point();
}
// SMS
$sms_count = count($sms_messages);
if($sms_count > 0) {
+4
View File
@@ -12,6 +12,10 @@ $od = sql_fetch($sql);
if(! (isset($od['od_id']) && $od['od_id']))
alert_close('주문정보가 존재하지 않습니다.');
if ($od['od_pg'] === 'KAKAOPAY') {
alert_close('SIRK 전용 카카오페이의 자동 취소 지원이 종료되었습니다. 이니시스 상점관리자에서 실제 취소 후 주문 상태와 환불금액을 확인해 주십시오.');
}
if($od['od_pg'] == 'inicis' && $od['od_settle_case'] == '계좌이체')
alert_close('KG이니시스는 신용카드만 부분취소가 가능합니다.');
+4
View File
@@ -27,6 +27,10 @@ if(! (isset($od['od_id']) && $od['od_id']))
if($od['od_settle_case'] == '계좌이체' && substr($od['od_receipt_time'], 0, 10) >= G5_TIME_YMD)
alert_close('실시간 계좌이체건의 부분취소 요청은 결제일 익일에 가능합니다.');
if ($od['od_pg'] === 'KAKAOPAY') {
alert_close('SIRK 전용 카카오페이의 자동 취소 지원이 종료되었습니다. 이니시스 상점관리자에서 실제 취소 후 주문 상태와 환불금액을 확인해 주십시오.');
}
// 금액비교
$od_misu = abs($od['od_misu']);
+1 -17
View File
@@ -60,24 +60,8 @@ $pg_anchor = '<ul class="anchor">
</ul>';
// pg 설정 필드 추가
if(!sql_query(" select pp_pg from {$g5['g5_shop_personalpay_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_personalpay_table']}`
ADD `pp_pg` varchar(255) NOT NULL DEFAULT '' AFTER `pp_price` ", true);
// 개인결제 PG kcp로 설정
sql_query(" update {$g5['g5_shop_personalpay_table']} set pp_pg = 'kcp' ");
}
// 현금영수증 필드 추가
if(!sql_query(" select pp_cash from {$g5['g5_shop_personalpay_table']} limit 1 ", false)) {
sql_query(" ALTER TABLE `{$g5['g5_shop_personalpay_table']}`
ADD `pp_cash` tinyint(4) NOT NULL DEFAULT '0' AFTER `pp_shop_memo`,
ADD `pp_cash_no` varchar(255) NOT NULL DEFAULT '' AFTER `pp_cash`,
ADD `pp_cash_info` text NOT NULL AFTER `pp_cash_no`,
ADD `pp_email` varchar(255) NOT NULL DEFAULT '' AFTER `pp_name`,
ADD `pp_hp` varchar(255) NOT NULL DEFAULT '' AFTER `pp_email`,
ADD `pp_casseqno` varchar(255) NOT NULL DEFAULT '' AFTER `pp_app_no` ", true);
}
?>
<form name="fpersonalpayform" action="./personalpayformupdate.php" method="post" onsubmit="return form_check(this);">
@@ -288,4 +272,4 @@ if($popup == 'yes') {
include_once(G5_PATH.'/tail.sub.php');
} else {
include_once (G5_ADMIN_PATH.'/admin.tail.php');
}
}
+19 -7
View File
@@ -4,13 +4,18 @@ include_once('./_common.php');
auth_check_menu($auth, $sub_menu, "r");
include_once('./date_filter.lib.php');
$date_range = shop_admin_date_range($_GET);
if ($date_range === false) {
http_response_code(400);
alert('조회 기간은 올바른 시작일과 종료일을 YYYYMMDD 형식으로 입력해 주십시오. 시작일은 종료일보다 늦을 수 없습니다.');
}
list($fr_date, $to_date) = $date_range;
$g5['title'] = '보관함현황';
include_once (G5_ADMIN_PATH.'/admin.head.php');
include_once(G5_PLUGIN_PATH.'/jquery-ui/datepicker.php');
$fr_date = (isset($_GET['fr_date']) && preg_match("/[0-9]/", $_GET['fr_date'])) ? $_GET['fr_date'] : '';
$to_date = (isset($_GET['to_date']) && preg_match("/[0-9]/", $_GET['to_date'])) ? $_GET['to_date'] : '';
$doc = isset($_GET['doc']) ? clean_xss_tags($_GET['doc'], 1, 1) : '';
$sort1 = (isset($_GET['sort1']) && in_array($_GET['sort1'], array('mb_id', 'it_id', 'wi_time', 'wi_ip'))) ? $_GET['sort1'] : 'it_id_cnt';
$sort2 = (isset($_GET['sort2']) && in_array($_GET['sort2'], array('desc', 'asc'))) ? $_GET['sort2'] : 'desc';
@@ -47,7 +52,14 @@ $rank = ($page - 1) * $rows;
$sql = $sql . " limit $from_record, $rows ";
$result = sql_query($sql);
$qstr1 = $qstr.'&amp;fr_date='.$fr_date.'&amp;to_date='.$to_date.'&amp;sel_ca_id='.$sel_ca_id;
// 검증된 날짜만 URL에 전달한다. 최초 조회의 빈 날짜는 링크에서 생략한다.
$filter_query = array('sel_ca_id' => $sel_ca_id);
if ($fr_date !== '' && $to_date !== '') {
$filter_query['fr_date'] = $fr_date;
$filter_query['to_date'] = $to_date;
}
$qstr1 = htmlspecialchars(http_build_query($filter_query, '', '&'), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$paging_query = htmlspecialchars(http_build_query(array_merge($filter_query, array('sort1' => $sort1, 'sort2' => $sort2)), '', '&'), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목록</a>';
?>
@@ -79,10 +91,10 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
</select>
<label for="fr_date" class="sound_only">시작일</label>
<input type="text" name="fr_date" value="<?php echo $fr_date; ?>" id="fr_date" required class="required frm_input" size="8" maxlength="8">
<input type="text" name="fr_date" value="<?php echo htmlspecialchars($fr_date, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?>" id="fr_date" required class="required frm_input" size="8" maxlength="8">
~
<label for="to_date" class="sound_only">종료일</label>
<input type="text" name="to_date" value="<?php echo $to_date; ?>" id="to_date" required class="required frm_input" size="8" maxlength="8">
<input type="text" name="to_date" value="<?php echo htmlspecialchars($to_date, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?>" id="to_date" required class="required frm_input" size="8" maxlength="8">
<input type="submit" value="검색" class="btn_submit">
</form>
@@ -127,7 +139,7 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
</table>
</div>
<?php echo get_paging(G5_IS_MOBILE ? $config['cf_mobile_pages'] : $config['cf_write_pages'], $page, $total_page, "{$_SERVER['SCRIPT_NAME']}?$qstr1&amp;page="); ?>
<?php echo get_paging(G5_IS_MOBILE ? $config['cf_mobile_pages'] : $config['cf_write_pages'], $page, $total_page, "{$_SERVER['SCRIPT_NAME']}?$paging_query&amp;page="); ?>
<div class="local_desc01 local_desc">
<p>고객님들이 보관함에 가장 많이 넣은 순으로 순위를 출력합니다.</p>
+8 -14
View File
@@ -7,19 +7,13 @@ auth_check_menu($auth, $sub_menu, "r");
$g5['title'] = "SMS 기본설정";
if (!$config['cf_icode_server_ip']) $config['cf_icode_server_ip'] = '211.172.232.124';
if (!$config['cf_icode_server_port']) $config['cf_icode_server_port'] = '7295';
// 아이코드 토큰키 추가
if( ! isset($config['cf_icode_token_key']) ){
$sql = "ALTER TABLE `{$g5['config_table']}`
ADD COLUMN `cf_icode_token_key` VARCHAR(100) NOT NULL DEFAULT '' AFTER `cf_icode_server_port`; ";
sql_query($sql, false);
$config['cf_icode_token_key'] = '';
}
// 배열코드 초기화
$userinfo = array('payment'=>'', 'coin'=>'');
if (!$config['cf_icode_server_port']) $config['cf_icode_server_port'] = '7295';
// 아이코드 토큰키 추가
// 배열코드 초기화
$userinfo = array('payment'=>'', 'coin'=>'');
if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw'])
{
$userinfo = get_icode_userinfo($config['cf_icode_id'], $config['cf_icode_pw']);
@@ -147,4 +141,4 @@ if ($config['cf_sms_use'] == 'icode') { // 아이코드 사용
<?php } ?>
<?php
include_once(G5_ADMIN_PATH.'/admin.tail.php');
include_once(G5_ADMIN_PATH.'/admin.tail.php');
+8 -12
View File
@@ -5,17 +5,13 @@ include_once("./_common.php");
auth_check_menu($auth, $sub_menu, "w");
check_admin_token();
$result = sql_query("describe `{$g5['sms5_write_table']}`");
while ($row = sql_fetch_array($result)){
if( $row['Field'] === 'wr_message' && $row['Type'] === 'varchar(255)' ){
sql_query("ALTER TABLE `{$g5['sms5_write_table']}` MODIFY wr_message TEXT NOT NULL;", false);
break;
}
}
$g5['title'] = "문자전송중";
$result = sql_query("describe `{$g5['sms5_write_table']}`");
while ($row = sql_fetch_array($result)){
}
$g5['title'] = "문자전송중";
if ($config['cf_sms_use'] != 'icode') {
alert('기본환경설정에서 icode sms 사용이 비활성화 되어 있습니다.');
}
@@ -366,4 +362,4 @@ function win_close_alert($msg) {
location.href = 'history_view.php?wr_no=<?php echo $wr_no?>';
</script>
<?php
include_once(G5_ADMIN_PATH.'/admin.tail.php');
include_once(G5_ADMIN_PATH.'/admin.tail.php');
+1 -5
View File
@@ -6,10 +6,6 @@ if ($is_admin != 'super')
alert('최고관리자만 접근 가능합니다.');
// 테마 필드 추가
if(!isset($config['cf_theme'])) {
sql_query(" ALTER TABLE `{$g5['config_table']}`
ADD `cf_theme` varchar(255) NOT NULL DEFAULT '' AFTER `cf_title` ", true);
}
$theme = get_theme_dir();
if($config['cf_theme'] && in_array($config['cf_theme'], $theme))
@@ -75,4 +71,4 @@ include_once('./admin.head.php');
<?php } ?>
<?php
include_once ('./admin.tail.php');
include_once ('./admin.tail.php');
+8 -6
View File
@@ -1,10 +1,12 @@
<?php
include_once('./_common.php');
$token_case = isset($_POST['token_case']) ? preg_replace('/[^0-9a-z_]/i', '', $_POST['token_case']) : '';
if( $token_case ){
$token = _token();
set_session('ss_'.$token_case.'_token', $token);
die(json_encode(array('error'=>'', 'token'=>$token, 'url'=>'')));
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !$is_member ||
!isset($_POST['token_case']) || $_POST['token_case'] !== 'qa_write') {
die(json_encode(array('error'=>'올바른 방법으로 이용해 주십시오.', 'token'=>'', 'url'=>'')));
}
check_request_origin();
$token = _token();
set_session('ss_qa_write_token', $token);
die(json_encode(array('error'=>'', 'token'=>$token, 'url'=>'')));
+18 -1
View File
@@ -49,6 +49,23 @@ if($config['cf_memo_del'] > 0) {
}
}
// 메일 인증 기한이 지난 회원도 본인확인 정보를 해제하고 탈퇴 처리한다.
// 재발송된 토큰의 발급 시각을 사용하며 실제 변경 직전에 회원 상태를 다시 확인한다.
$email_certify_minutes = isset($config['cf_email_certify_minutes']) ? (int) $config['cf_email_certify_minutes'] : 60;
if ($config['cf_use_email_certify'] && $email_certify_minutes > 0) {
$sql = " select mb_id, mb_email_certify, mb_email_certify2, mb_datetime, mb_leave_date, mb_memo
from {$g5['member_table']}
where mb_leave_date = ''
and mb_email_certify not regexp '[1-9]'
and mb_memo not regexp '^[0-9]{8}.*삭제함' ";
$result = sql_query($sql);
while ($row = sql_fetch_array($result)) {
if (is_expired_email_certify_member($row)) {
g5_leave_member($row['mb_id'], true);
}
}
}
// 탈퇴회원 자동 삭제
if($config['cf_leave_day'] > 0) {
$sql = " select mb_id from {$g5['member_table']}
@@ -79,4 +96,4 @@ if(isset($config['cf_optimize_date'])) {
run_event('cf_optimize_date_update', $config);
}
run_event('db_table_optimize_end', $config);
run_event('db_table_optimize_end', $config);
+19 -7
View File
@@ -6,8 +6,9 @@ if(function_exists('check_mail_bot')){ check_mail_bot($_SERVER['REMOTE_ADDR']);
$mb_id = isset($_GET['mb_id']) ? trim($_GET['mb_id']) : '';
$mb_md5 = isset($_GET['mb_md5']) ? trim($_GET['mb_md5']) : '';
$esc_mb_id = sql_real_escape_string($mb_id);
$sql = " select mb_id, mb_email_certify2, mb_leave_date, mb_intercept_date from {$g5['member_table']} where mb_id = '{$mb_id}' ";
$sql = " select mb_id, mb_datetime, mb_email_certify2, mb_leave_date, mb_intercept_date from {$g5['member_table']} where mb_id = '{$esc_mb_id}' ";
$row = sql_fetch($sql);
if (!$row['mb_id'])
alert('존재하는 회원이 아닙니다.', G5_URL);
@@ -16,21 +17,32 @@ if ( $row['mb_leave_date'] || $row['mb_intercept_date'] ){
alert('탈퇴 또는 차단된 회원입니다.', G5_URL);
}
// 인증 링크는 한번만 처리가 되게 한다.
sql_query(" update {$g5['member_table']} set mb_email_certify2 = '' where mb_id = '$mb_id' ");
if ($mb_md5)
{
if ($mb_md5 == $row['mb_email_certify2'])
$valid_minutes = isset($config['cf_email_certify_minutes']) ? (int) $config['cf_email_certify_minutes'] : 60;
if (is_valid_email_certify_token($mb_md5, $row['mb_email_certify2'], $row['mb_datetime'], $valid_minutes))
{
sql_query(" update {$g5['member_table']} set mb_email_certify = '".G5_TIME_YMDHIS."' where mb_id = '{$mb_id}' ");
// 인증 링크는 한번만 처리되도록 인증과 동시에 토큰을 폐기한다.
$esc_mb_md5 = sql_real_escape_string($mb_md5);
sql_query(" update {$g5['member_table']} set mb_email_certify = '".G5_TIME_YMDHIS."', mb_email_certify2 = '' where mb_id = '{$esc_mb_id}' and mb_email_certify2 = '{$esc_mb_md5}' ");
if (get_sql_affected_rows() <= 0) {
alert('이미 처리되었거나 올바르지 않은 메일인증 요청입니다.', G5_URL);
}
alert("메일인증 처리를 완료 하였습니다.\\n\\n지금부터 {$mb_id} 아이디로 로그인 가능합니다.", G5_URL);
}
else
{
if ($mb_md5 === $row['mb_email_certify2']) {
$esc_mb_md5 = sql_real_escape_string($mb_md5);
sql_query(" update {$g5['member_table']} set mb_email_certify2 = '' where mb_id = '{$esc_mb_id}' and mb_email_certify2 = '{$esc_mb_md5}' ");
alert('메일인증 유효시간이 만료되었습니다. 인증메일을 다시 요청해 주십시오.', G5_URL);
}
alert('메일인증 요청 정보가 올바르지 않습니다.', G5_URL);
}
}
alert('제대로 된 값이 넘어오지 않았습니다.', G5_URL);
alert('제대로 된 값이 넘어오지 않았습니다.', G5_URL);
+2 -2
View File
@@ -34,8 +34,8 @@ for ($i=1; $i<=$attach; $i++) {
$file_key = 'file'.$i;
if (isset($_FILES[$file_key]['name']) && $_FILES[$file_key]['name']) {
$filename = get_safe_filename($_FILES[$file_key]['name']);
if (is_disallowed_svg_filename($filename)) {
alert_close('허용되지 않는 파일 확장자입니다. (svg, svgz)');
if (is_disallowed_active_filename($filename)) {
alert_close('허용되지 않는 파일 확장자입니다.');
}
}
}
+3 -12
View File
@@ -12,12 +12,8 @@ $post_mb_password = isset($_POST['mb_password']) ? trim($_POST['mb_password']) :
if (!($post_mb_password && check_password($post_mb_password, $member['mb_password'])))
alert('비밀번호가 틀립니다.');
// 회원탈퇴일을 저장
$date = date("Ymd");
$sql = " update {$g5['member_table']} set mb_leave_date = '{$date}', mb_memo = '".date('Ymd', G5_SERVER_TIME)." 탈퇴함\n".sql_real_escape_string($member['mb_memo'])."', mb_certify = '', mb_adult = 0, mb_dupinfo = '' where mb_id = '{$member['mb_id']}' ";
sql_query($sql);
run_event('member_leave', $member);
if (!g5_leave_member($member['mb_id']))
alert('회원탈퇴를 처리하지 못했습니다. 회원 상태를 확인해 주십시오.');
// 3.09 수정 (로그아웃)
unset($_SESSION['ss_mb_id']);
@@ -25,9 +21,4 @@ unset($_SESSION['ss_mb_id']);
if (!$url)
$url = G5_URL;
//소셜로그인 해제
if(function_exists('social_member_link_delete')){
social_member_link_delete($member['mb_id']);
}
alert(''.$member['mb_nick'].'님께서는 '. date("Y년 m월 d일") .'에 회원에서 탈퇴 하셨습니다.', $url);
alert(''.$member['mb_nick'].'님께서는 '. date("Y년 m월 d일") .'에 회원에서 탈퇴 하셨습니다.', $url);
+8 -2
View File
@@ -20,6 +20,12 @@ if (!$email)
// (이메일 열거 공격 방지)
$generic_message = $email.' 메일로 회원아이디와 비밀번호를 인증할 수 있는 메일이 발송 되었습니다.\\n\\n메일을 확인하여 주십시오.';
$security_mail_url = g5_security_mail_base_url();
if ($security_mail_url === false) {
error_log('[g5 security mail] Valid G5_DOMAIN is required.');
alert_close($generic_message);
}
$sql = " select count(*) as cnt from {$g5['member_table']} where mb_email = '$email' ";
$row = sql_fetch($sql);
if ($row['cnt'] > 1) {
@@ -48,7 +54,7 @@ $sql = " update {$g5['member_table']} set mb_lost_certify = '$mb_nonce $mb_lost_
sql_query($sql);
// 인증 링크 생성
$href = G5_BBS_URL.'/password_lost_certify.php?mb_no='.$mb['mb_no'].'&amp;mb_nonce='.$mb_nonce;
$href = $security_mail_url.'/'.G5_BBS_DIR.'/password_lost_certify.php?mb_no='.$mb['mb_no'].'&amp;mb_nonce='.$mb_nonce;
$subject = "[".$config['cf_title']."] 요청하신 회원정보 찾기 안내 메일입니다.";
@@ -60,7 +66,7 @@ $content .= '<h1 style="padding:30px 30px 0;background:#f7f7f7;color:#555;font-s
$content .= '회원정보 찾기 안내';
$content .= '</h1>';
$content .= '<span style="display:block;padding:10px 30px 30px;background:#f7f7f7;text-align:right">';
$content .= '<a href="'.G5_URL.'" target="_blank">'.$config['cf_title'].'</a>';
$content .= '<a href="'.htmlspecialchars($security_mail_url, ENT_QUOTES, 'UTF-8').'" target="_blank">'.$config['cf_title'].'</a>';
$content .= '</span>';
$content .= '<p style="margin:20px 0 0;padding:30px 30px 30px;border-bottom:1px solid #eee;line-height:1.7em">';
$content .= addslashes($mb['mb_name'])." (".addslashes($mb['mb_nick']).")"." 회원님은 ".G5_TIME_YMDHIS." 에 회원정보 찾기 요청을 하셨습니다.<br>";
+2 -2
View File
@@ -136,8 +136,8 @@ if($w == 'u' || $w == 'a' || $w == 'r') {
if (isset($_FILES['bf_file']['name']) && is_array($_FILES['bf_file']['name'])) {
foreach ($_FILES['bf_file']['name'] as $filename) {
$filename = get_safe_filename($filename);
if (is_disallowed_svg_filename($filename)) {
alert('허용되지 않는 파일 확장자입니다. (svg, svgz)');
if (is_disallowed_active_filename($filename)) {
alert('허용되지 않는 파일 확장자입니다.');
}
}
}
+5 -3
View File
@@ -40,17 +40,19 @@ if ($row['cnt']) {
alert("{$mb_email} 메일은 이미 존재하는 메일주소 입니다.\\n\\n다른 메일주소를 입력해 주십시오.");
}
$security_mail_url = g5_require_security_mail_url();
// 인증메일 발송
$subject = '['.$config['cf_title'].'] 인증확인 메일입니다.';
$mb_name = $mb['mb_name'];
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용 (CSPRNG 사용)
$mb_md5 = get_random_token_string(16);
$mb_md5 = get_email_certify_token();
sql_query(" update {$g5['member_table']} set mb_email_certify2 = '$mb_md5' where mb_id = '{$esc_mb_id}' ");
$certify_href = G5_BBS_URL.'/email_certify.php?mb_id='.$mb_id.'&amp;mb_md5='.$mb_md5;
$certify_href = $security_mail_url.'/'.G5_BBS_DIR.'/email_certify.php?mb_id='.$mb_id.'&amp;mb_md5='.$mb_md5;
ob_start();
include_once ('./register_form_update_mail3.php');
@@ -62,4 +64,4 @@ mailer($config['cf_admin_email_name'], $config['cf_admin_email'], $mb_email, $su
$sql = " update {$g5['member_table']} set mb_email = '$mb_email' where mb_id = '{$esc_mb_id}' ";
sql_query($sql);
alert("인증메일을 {$mb_email} 메일로 다시 보내 드렸습니다.\\n\\n잠시후 {$mb_email} 메일을 확인하여 주십시오.", G5_URL);
alert("인증메일을 {$mb_email} 메일로 다시 보내 드렸습니다.\\n\\n잠시후 {$mb_email} 메일을 확인하여 주십시오.", G5_URL);
+8 -4
View File
@@ -65,6 +65,10 @@ $mb_9 = isset($_POST['mb_9']) ? trim($_POST['mb_9'])
$mb_10 = isset($_POST['mb_10']) ? trim($_POST['mb_10']) : "";
$mb_name = addslashes(clean_xss_tags(stripslashes($mb_name), 1, 1));
$mb_email = get_email_address($mb_email);
$security_mail_url = g5_security_mail_base_url();
if ($config['cf_use_email_certify'] && ($w == '' || $member['mb_email'] != $mb_email)) {
$security_mail_url = g5_require_security_mail_url();
}
$mb_homepage = addslashes(clean_xss_tags(stripslashes($mb_homepage), 1, 1));
$mb_tel = addslashes(clean_xss_tags(stripslashes($mb_tel), 1, 1));
$mb_zip1 = preg_replace('/[^0-9]/', '', $mb_zip1);
@@ -325,9 +329,9 @@ if ($w == '') {
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용 (CSPRNG 사용)
if ($config['cf_use_email_certify']) {
$mb_md5 = get_random_token_string(16);
$mb_md5 = get_email_certify_token();
sql_query(" update {$g5['member_table']} set mb_email_certify2 = '$mb_md5' where mb_id = '$mb_id' ");
$certify_href = G5_BBS_URL.'/email_certify.php?mb_id='.$mb_id.'&amp;mb_md5='.$mb_md5;
$certify_href = $security_mail_url.'/'.G5_BBS_DIR.'/email_certify.php?mb_id='.$mb_id.'&amp;mb_md5='.$mb_md5;
}
ob_start();
@@ -609,11 +613,11 @@ if ($config['cf_use_email_certify'] && $old_email != $mb_email) {
$subject = '['.$config['cf_title'].'] 인증확인 메일입니다.';
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용 (CSPRNG 사용)
$mb_md5 = get_random_token_string(16);
$mb_md5 = get_email_certify_token();
sql_query(" update {$g5['member_table']} set mb_email_certify2 = '$mb_md5' where mb_id = '$mb_id' ");
$certify_href = G5_BBS_URL.'/email_certify.php?mb_id='.$mb_id.'&amp;mb_md5='.$mb_md5;
$certify_href = $security_mail_url.'/'.G5_BBS_DIR.'/email_certify.php?mb_id='.$mb_id.'&amp;mb_md5='.$mb_md5;
ob_start();
include_once ('./register_form_update_mail3.php');
+9 -3
View File
@@ -1,6 +1,7 @@
<?php
// 회원가입축하 메일 (회원님께 발송)
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
$mail_site_url = g5_security_mail_base_url();
?>
<!doctype html>
@@ -18,19 +19,24 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
회원가입을 축하합니다.
</h1>
<span style="display:block;padding:10px 30px 30px;background:#f7f7f7;text-align:right">
<a href="<?php echo G5_URL ?>" target="_blank"><?php echo $config['cf_title'] ?></a>
<?php if ($mail_site_url) { ?>
<a href="<?php echo htmlspecialchars($mail_site_url, ENT_QUOTES, 'UTF-8') ?>" target="_blank"><?php echo $config['cf_title'] ?></a>
<?php } else { echo $config['cf_title']; } ?>
</span>
<p style="margin:20px 0 0;padding:30px 30px 50px;min-height:200px;height:auto !important;height:200px;border-bottom:1px solid #eee">
<b><?php echo $mb_name ?></b> 님의 회원가입을 진심으로 축하합니다.<br>
회원님의 성원에 보답하고자 더욱 더 열심히 하겠습니다.<br>
<?php if ($config['cf_use_email_certify']) { ?>아래의 <strong>메일인증</strong>을 클릭하시면 회원가입이 완료됩니다.<br><?php } ?>
<?php if ($config['cf_use_email_certify']) { ?>아래의 <strong>메일인증</strong>을 클릭하시면 회원가입이 완료됩니다.<br>
<?php if (!empty($config['cf_email_certify_minutes'])) { ?>인증 링크는 발송 후 <?php echo (int) $config['cf_email_certify_minutes']; ?>분 동안 유효합니다.<br><?php } ?><?php } ?>
감사합니다.
</p>
<?php if ($config['cf_use_email_certify']) { ?>
<a href="<?php echo $certify_href ?>" target="_blank" style="display:block;padding:30px 0;background:#484848;color:#fff;text-decoration:none;text-align:center">메일인증</a>
<?php } else { ?>
<a href="<?php echo G5_URL ?>" target="_blank" style="display:block;padding:30px 0;background:#484848;color:#fff;text-decoration:none;text-align:center">사이트바로가기</a>
<?php if ($mail_site_url) { ?>
<a href="<?php echo htmlspecialchars($mail_site_url, ENT_QUOTES, 'UTF-8') ?>" target="_blank" style="display:block;padding:30px 0;background:#484848;color:#fff;text-decoration:none;text-align:center">사이트바로가기</a>
<?php } ?>
<?php } ?>
</div>
</div>
+4 -1
View File
@@ -1,6 +1,7 @@
<?php
// 회원가입 메일 (관리자 메일로 발송)
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
$mail_site_url = g5_security_mail_base_url();
?>
<!doctype html>
@@ -18,7 +19,9 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
회원가입 알림 메일
</h1>
<span style="display:block;padding:10px 30px 30px;background:#f7f7f7;text-align:right">
<a href="<?php echo G5_URL ?>" target="_blank"><?php echo $config['cf_title'] ?></a>
<?php if ($mail_site_url) { ?>
<a href="<?php echo htmlspecialchars($mail_site_url, ENT_QUOTES, 'UTF-8') ?>" target="_blank"><?php echo $config['cf_title'] ?></a>
<?php } else { echo $config['cf_title']; } ?>
</span>
<p style="margin:20px 0 0;padding:30px 30px 50px;min-height:200px;height:auto !important;height:200px;border-bottom:1px solid #eee">
<b><?php echo $mb_name ?></b> 님께서 회원가입 하셨습니다.<br>
+5 -1
View File
@@ -1,6 +1,7 @@
<?php
// E-mail 수정시 인증 메일 (회원님께 발송)
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
$mail_site_url = g5_security_mail_base_url();
?>
<!doctype html>
@@ -18,7 +19,9 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
회원 인증 메일입니다.
</h1>
<span style="display:block;padding:10px 30px 30px;background:#f7f7f7;text-align:right">
<a href="<?php echo G5_URL ?>" target="_blank"><?php echo $config['cf_title'] ?></a>
<?php if ($mail_site_url) { ?>
<a href="<?php echo htmlspecialchars($mail_site_url, ENT_QUOTES, 'UTF-8') ?>" target="_blank"><?php echo $config['cf_title'] ?></a>
<?php } else { echo $config['cf_title']; } ?>
</span>
<p style="margin:20px 0 0;padding:30px 30px 50px;min-height:200px;height:auto !important;height:200px;border-bottom:1px solid #eee">
<?php if($w == 'u') { ?>
@@ -26,6 +29,7 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
<?php } ?>
아래의 주소를 클릭하시면 인증이 완료됩니다.<br>
<?php if (!empty($config['cf_email_certify_minutes'])) { ?>인증 링크는 발송 후 <?php echo (int) $config['cf_email_certify_minutes']; ?>분 동안 유효합니다.<br><?php } ?>
<a href="<?php echo $certify_href ?>" target="_blank"><b><?php echo $certify_href ?></b></a><br><br>
회원님의 성원에 보답하고자 더욱 더 열심히 하겠습니다.<br>
+3
View File
@@ -309,12 +309,15 @@ if ($w == '') {
$password_required = 'required';
} else if ($w == 'u') {
$password_required = '';
set_session(g5_write_edit_auth_key($bo_table, $wr_id), '');
if (!$is_admin) {
if (!($is_member && $member['mb_id'] === $write['mb_id'])) {
if (!check_password($wr_password, $write['wr_password'])) {
$is_wrong = run_replace('invalid_password', false, 'write', $write);
if(!$is_wrong) alert('비밀번호가 틀립니다.');
} else if (empty($write['mb_id'])) {
g5_grant_write_edit_auth($bo_table, $write);
}
}
}
+6 -1
View File
@@ -1,9 +1,14 @@
<?php
include_once('./_common.php');
if(!$bo_table)
// 그누보드4 자료 이전은 최고관리자에게만 허용되는 별도 용도이다.
$is_import_token = $bo_table === 'g4_import' && $is_admin === 'super';
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !$bo_table ||
(!$is_import_token && (empty($board['bo_table']) || $board['bo_table'] !== $bo_table)))
die(json_encode(array('error'=>'게시판 정보가 올바르지 않습니다.', 'url'=>G5_URL)));
check_request_origin();
set_session('ss_write_'.$bo_table.'_token', '');
$token = get_write_token($bo_table);
+38 -31
View File
@@ -29,7 +29,7 @@ if($board['bo_use_category']) {
$wr_subject = '';
if (isset($_POST['wr_subject'])) {
$wr_subject = substr(trim($_POST['wr_subject']),0,255);
$wr_subject = cut_str(trim($_POST['wr_subject']), 255, '');
$wr_subject = preg_replace("#[\\\]+$#", "", $wr_subject);
if (function_exists('normalize_utf8_string')) {
$wr_subject = normalize_utf8_string($wr_subject);
@@ -96,6 +96,39 @@ if ($w == 'u' || $w == 'r') {
}
}
// 수정 권한은 스킨 및 저장 처리 전에 검증한다.
if ($w == 'u') {
if (($is_member || $is_admin) && (get_session('ss_bo_table') != $bo_table || get_session('ss_wr_id') != $wr_id)) {
alert('올바른 방법으로 수정하여 주십시오.', get_pretty_url($bo_table));
}
$return_url = get_pretty_url($bo_table, $wr_id);
if ($is_admin == 'super') // 최고관리자 통과
;
else if ($is_admin == 'group') { // 그룹관리자
$mb = get_member($wr['mb_id']);
if ($member['mb_id'] != $group['gr_admin']) // 자신이 관리하는 그룹인가?
alert('자신이 관리하는 그룹의 게시판이 아니므로 수정할 수 없습니다.', $return_url);
else if ($member['mb_level'] < $mb['mb_level']) // 자신의 레벨이 크거나 같다면 통과
alert('자신의 권한보다 높은 권한의 회원이 작성한 글은 수정할 수 없습니다.', $return_url);
} else if ($is_admin == 'board') { // 게시판관리자이면
$mb = get_member($wr['mb_id']);
if ($member['mb_id'] != $board['bo_admin']) // 자신이 관리하는 게시판인가?
alert('자신이 관리하는 게시판이 아니므로 수정할 수 없습니다.', $return_url);
else if ($member['mb_level'] < $mb['mb_level']) // 자신의 레벨이 크거나 같다면 통과
alert('자신의 권한보다 높은 권한의 회원이 작성한 글은 수정할 수 없습니다.', $return_url);
} else if ($member['mb_id']) {
if ($member['mb_id'] != $wr['mb_id'])
alert('자신의 글이 아니므로 수정할 수 없습니다.', $return_url);
} else {
if (!g5_has_write_edit_auth($bo_table, $wr))
alert('비밀번호 확인 후 다시 수정하여 주십시오.', $return_url);
if ($wr['mb_id'])
alert('로그인 후 수정하세요.', G5_BBS_URL.'/login.php?url='.urlencode($return_url));
}
}
// 외부에서 글을 등록할 수 있는 버그가 존재하므로 비밀글은 사용일 경우에만 가능해야 함
if (!$is_admin && !$board['bo_use_secret'] && (stripos($_POST['html'], 'secret') !== false || stripos($_POST['secret'], 'secret') !== false || stripos($_POST['mail'], 'secret') !== false)) {
alert('비밀글 미사용 게시판 이므로 비밀글로 등록할 수 없습니다.');
@@ -140,8 +173,8 @@ for ($i=1; $i<=10; $i++) {
if (isset($_FILES['bf_file']['name']) && is_array($_FILES['bf_file']['name'])) {
foreach ($_FILES['bf_file']['name'] as $filename) {
$filename = get_safe_filename($filename);
if (is_disallowed_svg_filename($filename)) {
alert('허용되지 않는 파일 확장자입니다. (svg, svgz)');
if (is_disallowed_active_filename($filename)) {
alert('허용되지 않는 파일 확장자입니다.');
}
}
}
@@ -335,34 +368,6 @@ if ($w == '' || $w == 'r') {
insert_point($member['mb_id'], $board['bo_comment_point'], "{$board['bo_subject']} {$wr_id} 글답변", $bo_table, $wr_id, '쓰기');
}
} else if ($w == 'u') {
if (get_session('ss_bo_table') != $_POST['bo_table'] || get_session('ss_wr_id') != $_POST['wr_id']) {
alert('올바른 방법으로 수정하여 주십시오.', get_pretty_url($bo_table));
}
$return_url = get_pretty_url($bo_table, $wr_id);
if ($is_admin == 'super') // 최고관리자 통과
;
else if ($is_admin == 'group') { // 그룹관리자
$mb = get_member($write['mb_id']);
if ($member['mb_id'] != $group['gr_admin']) // 자신이 관리하는 그룹인가?
alert('자신이 관리하는 그룹의 게시판이 아니므로 수정할 수 없습니다.', $return_url);
else if ($member['mb_level'] < $mb['mb_level']) // 자신의 레벨이 크거나 같다면 통과
alert('자신의 권한보다 높은 권한의 회원이 작성한 글은 수정할 수 없습니다.', $return_url);
} else if ($is_admin == 'board') { // 게시판관리자이면
$mb = get_member($write['mb_id']);
if ($member['mb_id'] != $board['bo_admin']) // 자신이 관리하는 게시판인가?
alert('자신이 관리하는 게시판이 아니므로 수정할 수 없습니다.', $return_url);
else if ($member['mb_level'] < $mb['mb_level']) // 자신의 레벨이 크거나 같다면 통과
alert('자신의 권한보다 높은 권한의 회원이 작성한 글은 수정할 수 없습니다.', $return_url);
} else if ($member['mb_id']) {
if ($member['mb_id'] != $write['mb_id'])
alert('자신의 글이 아니므로 수정할 수 없습니다.', $return_url);
} else {
if ($write['mb_id'])
alert('로그인 후 수정하세요.', G5_BBS_URL.'/login.php?url='.urlencode($return_url));
}
if ($member['mb_id']) {
// 자신의 글이라면
if ($member['mb_id'] === $wr['mb_id']) {
@@ -428,6 +433,8 @@ if ($w == '' || $w == 'r') {
where wr_id = '{$wr['wr_id']}' ";
sql_query($sql);
set_session(g5_write_edit_auth_key($bo_table, $wr_id), '');
// 분류가 수정되는 경우 해당되는 코멘트의 분류명도 모두 수정함
// 코멘트의 분류를 수정하지 않으면 검색이 제대로 되지 않음
$sql = " update {$write_table} set ca_name = '{$ca_name}' where wr_parent = '{$wr['wr_id']}' ";
+3 -2
View File
@@ -334,7 +334,6 @@ if( !function_exists('shop_check_is_pay_page') ){
$mobile_dir.'/'.$shop_dir.'/lg/returnurl.php',
$mobile_dir.'/'.$shop_dir.'/lg/xpay_approval.php',
$mobile_dir.'/'.$shop_dir.'/kcp/order_approval_form.php',
$shop_dir.'/kakaopay/inicis_kk_return.php', // 이니시스 카카오페이 (SIRK 로 시작하는 아이디 전용)
$plugin_dir."/inicert/ini_result.php", // 이니시스 간편인증 모듈 2021-09-10 http <-> https 간 세션 공유 문제로 인해 추가
$plugin_dir."/inicert/ini_find_result.php", // 이니시스 간편인증 모듈 2021-09-10 http <-> https 간 세션 공유 문제로 인해 추가
);
@@ -831,7 +830,9 @@ if (G5_IS_MOBILE) {
// 방문자수의 접속을 남김
include_once(G5_BBS_PATH.'/visit_insert.inc.php');
if (!defined('G5_IS_CLI') || !G5_IS_CLI) {
include_once(G5_BBS_PATH.'/visit_insert.inc.php');
}
// 일정 기간이 지난 DB 데이터 삭제 및 최적화
+59 -5
View File
@@ -816,11 +816,17 @@ box-shadow: 1px 2px 2px #eee;}
#sod_frm_paysel .samsungpay_icon{background:url('../img/samsungpay.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .ssgpay_icon{background:url('../img/ssgpay_icon.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .skpay_icon{background:url('../img/skpay11_icon.png') no-repeat 50% 50% #fff; background-size: 70px;display:inline-block;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .bank_icon{background:url('../img/pay_icon1.png') no-repeat 15px 50% #fff}
#sod_frm_paysel .vbank_icon{background:url('../img/pay_icon2.png') no-repeat 15px 50% #fff;padding-top:13px}
#sod_frm_paysel .iche_icon{background:url('../img/pay_icon2.png') no-repeat 15px 50% #fff;padding-top:13px}
#sod_frm_paysel .hp_icon{background:url('../img/pay_icon3.png') no-repeat 24px 50% #fff}
#sod_frm_paysel .card_icon{background:url('../img/pay_icon4.png') no-repeat 17px 50% #fff}
/* 일반 결제수단은 아이콘 없이 텍스트를 표시한다. */
#sod_frm_paysel .lb_icon.bank_icon,
#sod_frm_paysel .lb_icon.vbank_icon,
#sod_frm_paysel .lb_icon.iche_icon,
#sod_frm_paysel .lb_icon.hp_icon,
#sod_frm_paysel .lb_icon.card_icon{background-image:none;padding:0 12px;display:inline-flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;line-height:18px}
#m_sod_frm_paysel .lb_icon.bank_icon,
#m_sod_frm_paysel .lb_icon.vbank_icon,
#m_sod_frm_paysel .lb_icon.iche_icon,
#m_sod_frm_paysel .lb_icon.hp_icon,
#m_sod_frm_paysel .lb_icon.card_icon{background-image:none;display:inline-block;padding:0;min-height:24px;line-height:24px;vertical-align:middle}
#forderform .sod_right #sod_bsk_tot {border:1px solid #e3e5e8}
#forderform .od_pay_buttons_el{margin-top:20px;border:1px solid #e3e5e8;background:#fff;padding:10px}
#forderform .od_pay_buttons_el h3{font-size:1.167em;margin:0px 0 5px}
@@ -1280,3 +1286,51 @@ html.no-overflowscrolling #sc_coupon_frm, html.no-overflowscrolling #od_coupon_f
.chk_box input[type="radio"]:checked + label {color:#305af9}
.chk_box input[type="radio"]:checked + label span:before {width:7px;height:7px;background:#305af9;content:'';position:absolute;top:3px;left:3px;border-radius:50%}
/* 간편결제 로고: 작은 원본은 확대하지 않고 큰 로고만 축소한다. */
#sod_frm_paysel .lb_icon.PAYCO:not(.nhnkcp_icon),
#sod_frm_paysel .lb_icon.paycopay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.PAYCO:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.paycopay_icon:not(.nhnkcp_icon){background-image:url('../img/payco.png');background-size:59px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.PAYNOW:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.PAYNOW:not(.nhnkcp_icon){background-image:url('../img/paynow.png');background-size:48px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.lpay_icon:not(.nhnkcp_icon),
#sod_frm_paysel .lb_icon.inicis_lpay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.lpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.inicis_lpay:not(.nhnkcp_icon){background-image:url('../img/lpay_logo.png');background-size:35px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.kakaopay_icon:not(.nhnkcp_icon),
#sod_frm_paysel .lb_icon.inicis_kakaopay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.kakaopay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.inicis_kakaopay:not(.nhnkcp_icon){background-image:url('../img/kakao.png');background-size:43px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.naverpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.naverpay_icon:not(.nhnkcp_icon){background-image:url('../img/ico-default-naverpay.png');background-size:42px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.samsungpay_icon:not(.nhnkcp_icon),
#sod_frm_paysel .lb_icon.samsung_pay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.samsungpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.samsung_pay:not(.nhnkcp_icon){background-image:url('../img/samsungpay.png');background-size:72px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.ssgpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.ssgpay_icon:not(.nhnkcp_icon){background-image:url('../img/ssgpay_icon.png');background-size:64px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.skpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.skpay_icon:not(.nhnkcp_icon){background-image:url('../img/skpay11_icon.png');background-size:46px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.applepay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.applepay_icon:not(.nhnkcp_icon){background-image:url('../img/ico-mobile-applepay.png');background-size:32px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.tosspay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.tosspay_icon:not(.nhnkcp_icon){background-image:url('../img/tosspay-logo.svg');background-size:72px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.pinpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.pinpay_icon:not(.nhnkcp_icon){background-image:url('../img/pinpay-logo.png');background-size:60px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#m_sod_frm_paysel .lb_icon.PAYCO:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.paycopay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.PAYNOW:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.lpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.inicis_lpay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.kakaopay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.inicis_kakaopay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.naverpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.samsungpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.samsung_pay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.ssgpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.skpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.applepay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.tosspay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.pinpay_icon:not(.nhnkcp_icon){width:96px;height:28px;min-height:28px;margin:0;padding:0;border-radius:0;display:inline-block;vertical-align:middle}
#sod_frm_paysel .lb_icon.inicis_kakaopay em{display:none}
+59 -5
View File
@@ -919,11 +919,17 @@ box-shadow:0 0 8px rgba(65,98,255,0.8)}
#sod_frm_paysel .samsungpay_icon{background:url('../img/samsungpay.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .ssgpay_icon{background:url('../img/ssgpay_icon.png') no-repeat 50% 50% #fff;display:inline-block;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .skpay_icon{background:url('../img/skpay11_icon.png') no-repeat 50% 50% #fff; background-size: 70px;display:inline-block;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .bank_icon{background:url('../img/pay_icon1.png') no-repeat 15px 50% #fff}
#sod_frm_paysel .vbank_icon{background:url('../img/pay_icon2.png') no-repeat 15px 50% #fff;padding-top:13px}
#sod_frm_paysel .iche_icon{background:url('../img/pay_icon2.png') no-repeat 15px 50% #fff;padding-top:13px}
#sod_frm_paysel .hp_icon{background:url('../img/pay_icon3.png') no-repeat 24px 50% #fff}
#sod_frm_paysel .card_icon{background:url('../img/pay_icon4.png') no-repeat 17px 50% #fff}
/* 일반 결제수단은 아이콘 없이 텍스트를 표시한다. */
#sod_frm_paysel .lb_icon.bank_icon,
#sod_frm_paysel .lb_icon.vbank_icon,
#sod_frm_paysel .lb_icon.iche_icon,
#sod_frm_paysel .lb_icon.hp_icon,
#sod_frm_paysel .lb_icon.card_icon{background-image:none;padding:0 12px;display:inline-flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;line-height:18px}
#m_sod_frm_paysel .lb_icon.bank_icon,
#m_sod_frm_paysel .lb_icon.vbank_icon,
#m_sod_frm_paysel .lb_icon.iche_icon,
#m_sod_frm_paysel .lb_icon.hp_icon,
#m_sod_frm_paysel .lb_icon.card_icon{background-image:none;display:inline-block;padding:0;min-height:24px;line-height:24px;vertical-align:middle}
#forderform .sod_right #sod_bsk_tot {border:1px solid #e3e5e8}
#forderform .od_pay_buttons_el{margin-top:20px;border:1px solid #e3e5e8;background:#fff;padding:10px}
#forderform .od_pay_buttons_el h3{font-size:1.167em;margin:0px 0 5px}
@@ -1038,3 +1044,51 @@ box-shadow:0 0 8px rgba(65,98,255,0.8)}
.chk_box input[type="radio"]:checked + label {color:#305af9}
.chk_box input[type="radio"]:checked + label span:before {width:7px;height:7px;background:#305af9;content:'';position:absolute;top:3px;left:3px;border-radius:50%}
/* 간편결제 로고: 작은 원본은 확대하지 않고 큰 로고만 축소한다. */
#sod_frm_paysel .lb_icon.PAYCO:not(.nhnkcp_icon),
#sod_frm_paysel .lb_icon.paycopay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.PAYCO:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.paycopay_icon:not(.nhnkcp_icon){background-image:url('../img/payco.png');background-size:59px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.PAYNOW:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.PAYNOW:not(.nhnkcp_icon){background-image:url('../img/paynow.png');background-size:48px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.lpay_icon:not(.nhnkcp_icon),
#sod_frm_paysel .lb_icon.inicis_lpay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.lpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.inicis_lpay:not(.nhnkcp_icon){background-image:url('../img/lpay_logo.png');background-size:35px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.kakaopay_icon:not(.nhnkcp_icon),
#sod_frm_paysel .lb_icon.inicis_kakaopay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.kakaopay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.inicis_kakaopay:not(.nhnkcp_icon){background-image:url('../img/kakao.png');background-size:43px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.naverpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.naverpay_icon:not(.nhnkcp_icon){background-image:url('../img/ico-default-naverpay.png');background-size:42px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.samsungpay_icon:not(.nhnkcp_icon),
#sod_frm_paysel .lb_icon.samsung_pay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.samsungpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.samsung_pay:not(.nhnkcp_icon){background-image:url('../img/samsungpay.png');background-size:72px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.ssgpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.ssgpay_icon:not(.nhnkcp_icon){background-image:url('../img/ssgpay_icon.png');background-size:64px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.skpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.skpay_icon:not(.nhnkcp_icon){background-image:url('../img/skpay11_icon.png');background-size:46px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.applepay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.applepay_icon:not(.nhnkcp_icon){background-image:url('../img/ico-mobile-applepay.png');background-size:32px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.tosspay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.tosspay_icon:not(.nhnkcp_icon){background-image:url('../img/tosspay-logo.svg');background-size:72px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#sod_frm_paysel .lb_icon.pinpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.pinpay_icon:not(.nhnkcp_icon){background-image:url('../img/pinpay-logo.png');background-size:60px auto;background-repeat:no-repeat;background-position:50% 50%;background-color:#fff;overflow:hidden;text-indent:-999px}
#m_sod_frm_paysel .lb_icon.PAYCO:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.paycopay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.PAYNOW:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.lpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.inicis_lpay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.kakaopay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.inicis_kakaopay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.naverpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.samsungpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.samsung_pay:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.ssgpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.skpay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.applepay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.tosspay_icon:not(.nhnkcp_icon),
#m_sod_frm_paysel .lb_icon.pinpay_icon:not(.nhnkcp_icon){width:96px;height:28px;min-height:28px;margin:0;padding:0;border-radius:0;display:inline-block;vertical-align:middle}
#sod_frm_paysel .lb_icon.inicis_kakaopay em{display:none}
+33
View File
@@ -0,0 +1,33 @@
# 과거 데이터베이스 패치 조사
일반 실행 경로에서 제거한 DDL과 전체 Git 이력을 대조했다. 최초 공개 스키마에 이미 포함된 2012~2013년 관리 화면 보정 코드는 기준선으로 분류하고, 별도 업그레이드 진입점에 추가된 2014년 이후 변경은 도입 커밋과 날짜별 마이그레이션으로 옮겼다.
| 도입일 | 원본 커밋 | 마이그레이션 | 주요 변경 |
|---|---|---|---|
| 2014-03-31 | `3889e0235` | `20140331_001_shop_cart_shipping` | 장바구니 배송비 필드 |
| 2014-11-05 | `de2b73436` | `20141105_001_shop_cart_select_time` | 장바구니 선택 시각 |
| 2015-03-06 | `344b1983a` | `20150306_001_shop_cart_indexes` | 장바구니 조회 인덱스 |
| 2015-05-29 | `a7cfae126` | `20150529_001_inicis_mobile_tables` | 모바일 이니시스·주문 임시 테이블 |
| 2015-09-08 | `6495aa007` | `20150908_001_inicis_auth_number` | 이니시스 승인번호 |
| 2016-04-22 | `4c9ac8b6b` | `20160422_001_shop_test_order` | 테스트 주문 구분 |
| 2016-04-27 | `f1cd96dc3` | `20160427_001_shop_order_data_cart` | 미완료 주문 장바구니 정보 |
| 2016-09-09 | `6e1aa4ac0` | `20160909_001_shop_coupon_zone` | 쿠폰존 |
| 2017-09-21 | `db7e07da2` | `20170921_001_inicis_notification_data` | 이니시스 통지 원문 |
| 2018-03-30 | `f7ac06d7d` | `20180330_001_social_login` | 소셜 로그인 설정·프로필 |
| 2019-12-02 | `22aad37bf` | `20191202_001`, `20191202_002` | 5.4 SEO·메모·첨부 스키마 |
| 2019-12-23 | `54dc82a5d`, `ffc43056b` | `20191223_001_gnuboard_5413_schema` | 주문 로그·스크랩·짧은 URL |
| 2023-09-09 | `28bfcea9c` | `20230909_001_auth_menu_length` | 관리 메뉴 ID 길이 |
| 2024-04-02 | `1e72d7e81` | `20240402_001_primary_keys` | 단일 행 테이블 등의 기본키 |
| 2025-08-19 | `cb1fadba4` | `20250819_001_sms_table_prefix` | SMS5 테이블 접두어 |
| 2025-09-04 | `66f6a75a1`, `7e8eff539` | `20250904_001_marketing_consent` | 제거되지 않고 유지된 마케팅 동의 필드 |
| 2026-03-25 | `5054e2463` | `20260325_001_coupon_use_unique` | 쿠폰 중복 사용 방지 인덱스 |
| 2026-04-16 | `4d5c59766` | `20260416_001_member_auto_login` | 자동 로그인 토큰 테이블 |
| 2026-06-25 | `0301c45f5` | `20260625_001_order_cancel_log` | 주문 취소·환불 이력 |
| 2026-07-23 | `acdbcfd8b` | `20260723_001_inicis_pro` | INIpay PRO 설정·결제 이력 |
| 2026-09-03 | `530140902` | `20260904_001_add_email_certify_minutes` | 메일 인증 유효시간 |
2012~2013년에 `config_form.php`, `board_form.php`, `member_form.php`, `qa_config.php` 등에서 수행하던 보정은 그누보드5 최초 배포 스키마를 형성하는 과정이었다. 이 항목들은 위 연속 패치보다 앞선 기준선이며 현재 `install/gnuboard5.sql`과 `install/gnuboard5shop.sql`에 포함된다. 제거 후 설치 스키마에서 누락된 필드는 별도로 대조해 보완했다.
팝빌 알림톡 테이블·설정과 게시판 알림 필드는 `7e8eff539`에서 기능과 함께 제거됐으므로 현재 목표 스키마에 다시 만들지 않는다. 반면 같은 도입 커밋의 마케팅 동의 필드는 유지돼 별도 마이그레이션으로 남겼다. 데이터 삭제가 필요한 쿠폰 중복 정리는 자동 수행하지 않으며, 중복이 있으면 고유 인덱스 생성 실패를 그대로 보고해 운영자가 데이터를 검토하도록 한다.
초기 이전 작업에서 확인된 복합 `ALTER TABLE`의 부분 적용 가능성과 누락된 데이터 보정은 `20260904_002_reconcile_partial_schema`, `20260904_003_backfill_legacy_data`에서 보완한다. 두 마이그레이션은 누락 컬럼과 인덱스를 개별 검사하고, 장바구니 배송비·회원 이미지 기본값·콘텐츠 SEO 데이터를 보정하며 SEO 컬럼 길이를 신규 설치 정의와 일치시킨다.
+58
View File
@@ -0,0 +1,58 @@
# 데이터베이스 마이그레이션
데이터베이스 스키마 변경은 설정·목록·상세 화면이나 공통 라이브러리의 일반 실행 경로에서 수행하지 않는다. 신규 설치의 최종 스키마는 `install/gnuboard5.sql`에 반영하고, 기존 설치 변경은 `migrations` 디렉터리의 버전형 SQL 파일로 관리한다.
## 파일 규칙
- 파일명은 `YYYYMMDD_NNN_설명.sql` 형식을 사용한다.
- 파일명은 한 번 배포한 뒤 변경하거나 재사용하지 않는다.
- `-- @description`에 관리자에게 표시할 설명을 작성한다.
- 각 SQL 문 바로 앞에 `-- @if-table-missing`, `-- @if-table-exists`, `-- @if-column-missing`, `-- @if-index-missing` 조건을 둘 수 있다. 조건이 여러 개면 모두 만족할 때만 실행한다.
- 게시판별 글 테이블은 `-- @foreach-write-table-if-column-missing column_name`과 `{{write_table}}`을 사용한다.
- SQL에는 실제 접두어 대신 실행기가 지원하는 테이블 자리표시자를 사용한다.
- 한 파일은 가능한 한 하나의 목적만 포함한다.
자리표시자는 `$g5`에 등록된 `*_table` 키를 `{{config_table}}` 형식으로 사용한다. 실행기가 실제 테이블명으로 치환하며, 치환되지 않은 자리표시자는 오류로 처리한다.
## 실행 방법
공식 배포본에서는 관리자 화면으로 실행한다. `bin/`은 Git 추적 및 배포 대상에서 제외한 로컬 운영·검증 스크립트 디렉터리이므로 아래 검증 스크립트 설명은 해당 파일을 별도로 보유한 개발 환경에만 적용한다.
웹에서는 최고관리자가 **환경설정 → DB업그레이드**에서 전체 또는 특정 마이그레이션을 명시적으로 실행할 수 있다. 성공한 마이그레이션의 개별 실행 버튼은 비활성화되며, 전체 실행에서도 성공 이력과 체크섬이 일치하는 항목은 건너뛴다. 화면 조회만으로는 DDL이 실행되지 않는다.
기존 설치본처럼 이력이 비어 있으면 상태 조회 시 실제 실행 조건을 읽기 전용으로 검사한다. 일부 이력이 있는 경우에도 미기록 항목은 검사한다. 현재 조건에서 모든 SQL을 건너뛰는 항목은 `실행 불필요 (미기록)`로 표시하며 개별 실행 버튼을 비활성화한다. 이는 과거 실행 성공이나 컬럼 정의 전체의 일치를 보증하는 표시가 아니다. 게시판별 컬럼도 검사하며, 조건 없는 DDL·데이터 보정·검사 오류는 대기로 남긴다. 조회 요청 안에서는 메타데이터를 공유해 중복 조회를 줄이고, 다음 요청에서는 다시 검사하므로 외부 변경이나 선행 작업 후 상태가 반영된다.
`기존 상태 확인 이력 등록` 버튼은 실행 잠금 아래 다시 검사하고, 처음부터 연속해서 실행 불필요한 미기록 항목만 성공 이력으로 등록한다. 등록 시각은 확인한 시각이며 실제 과거 적용 시각이 아니다. 이 동작은 필요하면 이력 테이블을 생성하지만, 마이그레이션 SQL 자체는 실행하지 않는다. 변경이 필요한 항목이나 실패 이력을 만나면 중단하며, 성공 이력의 체크섬 불일치는 오류로 처리한다. 화면 진입만으로 등록하지 않으며 웹 등록은 최고관리자 권한과 POST 토큰 검증을 거친다. 전체 마이그레이션 실행도 기존처럼 실행 조건을 다시 확인하고 건너뛴 항목의 이력을 함께 기록한다.
전체 실행은 파일명 오름차순, 즉 오래된 마이그레이션부터 처리하며 하나가 실패하면 이후 실행을 중단한다. 개별 실행도 대상보다 앞선 모든 마이그레이션의 성공 이력과 체크섬이 유효해야 한다. 선행 항목이 누락·실패·변경된 경우 대상만 건너뛰기 성공으로 기록하지 않고 오류로 차단한다.
실행기는 `g5_migrations` 테이블에 마이그레이션 ID, 체크섬, 상태, 오류, 실행시간과 적용시각을 기록한다. 프로세스 간 잠금을 사용해 동시 실행을 막고, 실패하면 이후 마이그레이션을 중단한다. 적용이 끝난 파일의 체크섬이 바뀌면 오류로 처리한다.
현재 마이그레이션은 공통 실행 취소를 제공하지 않는다. 컬럼·테이블 삭제에 따른 데이터 손실과 후속 마이그레이션 의존성을 자동으로 안전하게 해결할 수 없기 때문이다. `g5_migrations`의 성공 이력만 삭제하는 것도 실제 스키마를 되돌리지 않으므로 취소로 취급하지 않는다. 롤백이 필요하면 데이터베이스 백업을 복원하거나, 해당 변경에 맞춘 별도 복구 절차를 검토한다.
`check-migration-schema.php`는 마이그레이션의 테이블·컬럼·인덱스가 신규 설치 SQL에 포함되는지와 최종 컬럼 정의가 일치하는지 검사한다. `check-migration-runner.php`는 격리된 임시 테이블로 부분 적용 스키마, 쇼핑몰 미설치 건너뛰기, 기존 데이터 보정을 검사한다. 마이그레이션을 추가하거나 설치 SQL을 변경한 뒤 두 검사를 반드시 실행한다.
`check-migration-existing.php`는 실제 DB 대신 SQL 응답을 제공해 조회 시 쓰기 금지, 순차 이력 등록, 재검사, 실패 이력·체크섬·잠금 보호를 검사한다. 기존 상태 검사나 이력 등록 로직을 변경한 뒤 실행한다.
쇼핑몰을 설치하지 않은 사이트에서는 DB 업그레이드 화면의 `쇼핑몰 설치` 버튼으로 최신 쇼핑몰 스키마를 추가할 수 있다. 같은 접두어의 쇼핑몰 테이블이 하나라도 있으면 기존 데이터 훼손을 막기 위해 설치를 중단하며, 기존 `data/dbconfig.php`의 DB 접속 정보와 토큰 키는 유지하고 쇼핑몰 설정만 추가한다. `php bin/check-shop-install.php`로 격리된 접두어에서 후설치, 설정 보존과 중복 실행 차단을 검사할 수 있다.
쇼핑몰 미설치 사이트의 업그레이드는 다음 두 방식으로 진행할 수 있다.
- **쇼핑몰 없이 업그레이드:** `쇼핑몰 설치`를 누르지 않고 기존 상태 이력 등록 또는 마이그레이션 실행을 진행한다. 쇼핑몰 관련 SQL은 기존 쇼핑몰 테이블의 존재 조건을 확인하므로 해당 테이블이 없는 설치에서는 실행하지 않는다. 공통 스키마와 쇼핑몰 변경이 한 파일에 있어도 쇼핑몰 문장만 건너뛴다. 쇼핑몰 항목의 성공 이력이 생겨도 쇼핑몰이 설치됐다는 뜻은 아니다.
- **쇼핑몰을 추가하고 업그레이드:** `쇼핑몰 설치`로 최신 쇼핑몰 스키마를 생성한 뒤 공통 DB 마이그레이션을 진행한다. 쇼핑몰 없이 업그레이드를 먼저 완료한 뒤 후설치해도 최신 설치 SQL을 사용하므로 과거에 건너뛴 쇼핑몰 이력을 삭제하거나 다시 실행할 필요가 없다.
이 구분은 `G5_USE_SHOP` 값만으로 판단하지 않고 실제 쇼핑몰 테이블 존재 여부를 사용한다. 기존 쇼핑몰 테이블을 남겨 둔 채 기능만 비활성화한 사이트는 미설치 사이트와 다르며, 기존 테이블의 스키마 보정 대상이 될 수 있다. `check-shop-install.php`는 전체 마이그레이션의 쇼핑몰 SQL을 검사해 미설치 상태의 모든 문장이 건너뛰어지는지, 후설치 뒤에는 오류 없이 처리되는지도 검증한다.
`check-runtime-ddl.php`는 관리자·게시판·라이브러리·쇼핑몰 등 일반 실행 경로에 DDL 문이 다시 추가되지 않았는지 검사한다. 테이블이나 컬럼 변경은 이 검사를 우회하지 말고 버전형 마이그레이션으로 작성한다.
게시판 삭제 시 게시판별 테이블을 함께 삭제하는 것처럼 사용자가 명시적으로 요청한 데이터 생명주기 작업은 마이그레이션과 구분해 제한된 허용 목록으로 관리한다. 일반 레코드 생성 과정에서 `AUTO_INCREMENT`를 초기화하는 DDL은 허용하지 않는다.
## 권한과 배포
관리자 DB업그레이드 실행 시에는 DB 계정에 필요한 DDL 권한이 있어야 한다. 평상시 DDL 권한을 제거한 운영 환경에서는 업그레이드 시점에 필요한 권한을 부여하고, 완료 후 운영 권한으로 복원한다.
배포 전에는 백업과 테이블 크기, 예상 잠금 시간을 확인한다. MySQL 계열 DB의 DDL은 트랜잭션으로 완전히 되돌릴 수 없으므로 실패한 마이그레이션의 오류를 확인하고 수동 복구 후 재실행한다.
## 기존 업그레이드 코드
과거 설치본을 화면 접근 시점에 보정하던 누적 DDL은 일반 실행 파일에서 제거했다. `orderupgrade.php`와 `adm/dbupgrade.php`에 누적됐던 배포 후 변경은 실제 도입 시점별 마이그레이션으로 이전했다. 조사 근거와 대응 목록은 `database-migration-history.md`에 기록한다.
+24
View File
@@ -0,0 +1,24 @@
# 메일 미인증 회원의 만료 및 탈퇴 처리
메일인증을 사용하는 사이트에서 인증 링크의 유효시간이 지난 미인증 회원은 기존 회원탈퇴 절차로 정리한다. 기본 유효시간은 60분이며 기본환경설정에서 분 단위로 변경할 수 있다. `0`은 인증 링크 만료와 미인증 만료 정리를 모두 사용하지 않는 설정이다.
## 실행 시점과 대상
- 기존 데이터 정리와 동일하게 최고관리자 접속 시 하루 한 번 실행한다. 만료 시각에 즉시 실행되는 예약 작업은 아니며 최고관리자가 접속하지 않으면 정리가 지연된다.
- 인증을 완료했거나 이미 탈퇴·삭제된 회원과 최고관리자는 제외한다.
- 재발송한 토큰은 새 발급 시각부터 유효시간을 계산한다. 기존 형식의 토큰이나 이미 폐기된 토큰은 회원가입 시각을 기준으로 한다. 유효한 기준 시각을 확인할 수 없는 회원은 자동 처리하지 않는다.
- 유효시간의 마지막 초까지는 인증할 수 있다. 실제 탈퇴 직전에 회원 상태를 다시 읽고, 그 이후 인증 완료·토큰 재발송·이메일 변경이 발생하면 해당 탈퇴 처리를 중단한다.
- 메일인증을 사용하지 않는 사이트는 미인증 만료 정리를 실행하지 않는다. 소셜 가입 시 이미 인증 처리된 회원도 대상이 아니다.
## 두 단계의 처리
1. **만료 회원 탈퇴 처리**: 탈퇴일과 만료 사유를 기록하고 `mb_certify`, `mb_adult`, `mb_dupinfo`를 초기화한다. 이메일 인증·비밀번호 재설정 토큰을 폐기하고 소셜 연결을 해제한다. 일반 탈퇴와 같은 `member_leave` 이벤트를 실행한다. 이후 로그인 및 기존 인증 링크 사용은 불가능하다.
2. **탈퇴 후 회원자료 정리**: 기존 `회원탈퇴후 삭제일` 설정과 `member_delete()`를 사용한다. 연락처·주소·비밀번호·포인트 및 기존 삭제 함수가 처리하는 연관 데이터를 정리한다. 이 설정이 `0`이면 두 번째 자동 정리는 하지 않지만 첫 단계의 본인확인 해제는 실행한다.
기존 정책대로 회원 아이디는 보존하여 재사용을 막는다. 첫 단계에서 회원 레코드를 삭제하거나 연락처·포인트를 즉시 제거하지 않는다. 개인정보 전체의 즉시 삭제 기능은 아니다.
## 기존 사이트 적용
적용 시 이미 만료된 미인증 회원도 다음 정리 대상이 된다. 기본환경설정의 메일인증 유효시간과 회원탈퇴후 삭제일을 확인하고, 운영 중인 확장의 `member_leave` 이벤트가 자동 정리에도 적합한지 점검한다. 자동 정리는 만료 회원 본인의 요청이 아니라 최고관리자 요청에서 실행되므로 이벤트 인수의 회원정보를 기준으로 처리해야 한다.
관리자 회원 목록은 정리 전에는 `메일 미인증`, 탈퇴 처리 후에는 `탈퇴함`으로 표시한다. 만료된 회원을 다시 정상 계정으로 복구하거나 동일 아이디로 재가입시키는 기능은 포함하지 않는다.
+107
View File
@@ -0,0 +1,107 @@
# 객체 캐시의 타입 격리와 호환성
`G5_object_cache`는 한 요청 안에서 사용하는 메모리 캐시다. `common.php`에서
요청마다 객체를 생성하며 요청 간 값 공유, TTL, Redis/Memcached 연동은 제공하지 않는다.
## 메서드 계약
캐시 주소는 `(type, group, key)`로 결정된다. 타입, 그룹, 키 중 하나라도 다르면
별도의 값을 저장한다. `bbs`, `content`, `shop`과 플러그인에서 정한 타입 모두에 적용된다.
인자 순서와 기본값은 기존과 같다.
```php
$cache->set($type, $key, $data = array(), $group = 'default');
$cache->get($type, $key, $group = 'default');
$cache->exists($type, $key, $group = 'default');
$cache->delete($type, $key, $group = 'default');
```
- `type`, `group`, `key`는 문자열 또는 정수 식별자를 사용한다. 배열과 객체를
식별자로 사용하는 것은 지원하지 않는다. PHP 배열 키 규칙에 따라 정수 `123`과
문자열 `'123'`은 같은 키다. 타입에도 같은 규칙이 적용된다.
- 빈 문자열은 각 차원에서 유효하다. 빈 그룹 `''`은 생략 시 사용하는 `'default'`와 다르다.
식별자에 별도의 정규화나 구분자 연결을 하지 않는다.
- `set()`은 값을 저장하고 명시적인 반환값 없이 `NULL`을 반환한다.
- `get()`은 저장한 값을 반환하며, 없는 값은 `false`를 반환한다.
저장한 `false`와 cache miss는 `exists()`로 구분한다.
- `exists()`는 `NULL`, `false`, `0`, `'0'`, `''`, 빈 배열을 저장한 경우에도 `true`다.
- `delete()`는 해당 주소만 제거하며 성공 시 `true`, 없는 값이면 `false`를 반환한다.
- 객체는 저장 시와 조회 시 각각 `clone`한다. 기존과 같은 얕은 복사이며,
객체 내부의 중첩 객체나 배열 내부 객체까지 재귀적으로 복사하지 않는다.
- 빈 타입·그룹 버킷은 정리하지 않는다. 기타 타입을 처음 조회할 때도 빈 타입 버킷이
만들어질 수 있으며 요청 종료 시 함께 해제된다.
```php
$cache->set('shop', 'same-key', 'shop-value', 'same-group');
$cache->set('plugin', 'same-key', 'plugin-value', 'same-group');
$cache->get('shop', 'same-key', 'same-group'); // 'shop-value'
$cache->delete('plugin', 'same-key', 'same-group');
$cache->exists('shop', 'same-key', 'same-group'); // true
```
## public 멤버 호환성과 업그레이드
세 public 멤버는 제거하지 않고 직접 접근을 deprecated로 표시했다.
`$writes[$group][$key]`와 `$contents[$group][$key]` 구조는 유지한다.
기타 타입의 충돌을 해결하기 위해 `$etcs`에는 타입 차원을 추가했다.
| 타입 | 기존 저장 구조 | 변경 후 저장 구조 |
| --- | --- | --- |
| `bbs` | `$writes[$group][$key]` | 동일 |
| `content` | `$contents[$group][$key]` | 동일 |
| 그 외 | `$etcs[$group][$key]` | `$etcs[$type][$group][$key]` |
메서드와 public 멤버는 같은 저장소를 사용한다. 호환용 데이터를 별도로 복제하거나
기존 `$etcs` 주소로 fallback하지 않는다. 기존 주소에는 타입 정보가 없어 어느 타입의
값인지 복원할 수 없기 때문이다. 메서드만 사용하는 호출부는 변경할 필요가 없다.
외부 테마·플러그인이 `$etcs`를 직접 읽거나 쓰거나 삭제한다면 다음과 같이 수정해야 한다.
기존의 서로 다른 타입 간 캐시 공유에 의존했다면 공유할 호출부에서 동일한 타입을
명시적으로 사용해야 한다.
```php
// 변경 전: shop 등 모든 기타 타입이 같은 주소를 사용했다.
$cache->etcs[$group][$key] = $item;
$item = $cache->etcs[$group][$key];
unset($cache->etcs[$group][$key]);
// 변경 후 권장: 메서드로 타입과 존재 여부를 명시한다.
$cache->set('shop', $key, $item, $group);
if ($cache->exists('shop', $key, $group)) {
$item = $cache->get('shop', $key, $group);
}
$cache->delete('shop', $key, $group);
// 직접 접근이 불가피한 기존 코드의 새 주소 (deprecated)
$cache->etcs['shop'][$group][$key] = $item;
```
2026-09-09 저장소 PHP 코드 검색에서는 클래스 외부의 캐시 public 멤버 직접 접근을
확인하지 못했다. 코어의 `bbs`, `content`, `shop` 호출은 메서드를 사용한다.
[공개 구현](https://github.com/gnuboard/gnuboard5/blob/master/lib/Cache/obj.class.php)과
[원 제보](https://sir.kr/boards/g5_issues/3)도 검토했다. 공개 웹에서
`g5_object->writes`, `g5_object->contents`, `g5_object->etcs`를 검색했으나
외부 플러그인의 직접 접근 사례는 확인하지 못했다. 검색에 잡히지 않는 코드의
호환성까지 보장하는 결과는 아니므로 public 멤버 제거는 이번 변경에 포함하지 않는다.
## 검증
```sh
php -l lib/Cache/obj.class.php
php -l tests/object_cache.php
php tests/object_cache.php
```
회귀 테스트는 타입·그룹·키 격리, 삭제 범위, 빈 값과 기본값, 객체 clone,
public 멤버와 메서드의 저장소 일치, 인스턴스 간 격리를 확인한다.
DB 대역을 사용해 실제 `get_shop_item()`의 빈 그룹 및 `shop_{query hash}` 그룹,
`get_content_by_field()`의 `bbs`/`content` 적재와 `get_content_db()`의 재사용도 확인한다.
실제 DB나 웹 세션은 필요하지 않다.
구현과 테스트는 PHP 5.2.17에서 지원하는 문법으로 작성했다. PHP 8.4.22와 PHP 5.2.17
CLI에서 변경 파일의 구문 검사와 회귀 테스트가 모두 통과했다. PHP 5.2.17은
[공식 소스 배포본](https://museum.php.net/php5/php-5.2.17.tar.gz)을 임시 디렉터리에서
CLI와 PCRE를 포함하도록 빌드해 검증했다. 시스템 PHP는 변경하지 않았다.
이 검증은 객체 캐시와 위 코어 호출부에 한정하며, 사이트 전체의 PHP 5.2 호환성을
검증한 것은 아니다.
+23
View File
@@ -0,0 +1,23 @@
# 배송완료 기준 주문 포인트
주문 포인트 지급 기간은 장바구니 상품·옵션 레코드(`ct_id`)별 배송완료 시각(`ct_complete_time`)부터 계산한다. 주문 접수 시각, 장바구니 생성 시각, 송장 등록 시각과는 구분한다.
## 상태 변경과 지급
- 관리자 주문 상세의 선택 상품 완료 처리와 주문 목록의 일괄 완료 처리에서 완료 시각을 기록한다.
- 이미 완료된 상품을 다시 완료로 저장하면 기존 완료 시각을 유지한다.
- 완료를 해제하면 완료 시각을 `NULL`로 초기화한다. 이후 다시 완료하면 새 시각부터 지급 기간을 계산한다.
- 회원의 포인트가 있는 상품 중 완료 상태이고 아직 적립되지 않은 상품에 지급한다.
- 설정이 0일이면 완료 처리 시 지급한다. 그 외에는 설정 기간 경과 후 관리자 주문 상세 조회 또는 완료 처리 시 지급한다. 별도 예약 작업으로 정시에 지급하는 방식은 아니다.
## 기존 설치 업그레이드
변경된 코드로 주문을 처리하기 전에 **환경설정 → DB업그레이드** 또는 CLI에서 `20260909_002_cart_complete_time` 마이그레이션을 적용한다. 선행 마이그레이션이 미적용 상태이면 먼저 적용해야 한다.
기존 레코드는 완료 시각을 `NULL`로 유지한다. 기존 배송완료·미지급 상품은 지급을 추가로 늦추지 않도록 종전의 `ct_time` 기준을 적용한다. 완료 시각을 추정하거나 업그레이드 시각으로 덮어쓰지 않는다. 기존 상품도 완료를 해제한 후 다시 완료하면 새 기준을 적용한다. 이미 지급한 포인트는 마이그레이션으로 변경하지 않는다.
신규 설치 스키마에도 같은 컬럼을 포함한다. 별도 확장 기능에서 상품 상태를 변경한다면 완료 시각 기록과 초기화도 함께 구현해야 한다. `get_cart_complete_time_sql()`의 반환식을 `ct_status` 대입보다 먼저 사용하면 기존 상태를 기준으로 완료 시각을 결정할 수 있다.
## 검증
`php tests/order_completion_point.php`는 `data/dbconfig.php`의 DB 연결로 세션 전용 임시 테이블을 만들어 상태 변경, 기존 상품 전환 및 지급 조건을 검증한다. 실제 주문·포인트 테이블은 변경하지 않으며 포인트 등록 함수는 테스트 대역을 사용한다. DB 계정에 임시 테이블 생성 권한이 필요하다.
+50
View File
@@ -0,0 +1,50 @@
# PG별 간편결제 설정과 검증
관리자 **쇼핑몰 설정 → 결제설정**에서 **PG사 간편결제 버튼 사용**을 `노출함`으로 설정한 뒤, 선택한 PG와 계약한 수단만 체크한다. 계약 상태는 PG사에 직접 확인해야 하며 관리자 화면에서 자동 조회하지 않는다.
| PG | 개별 수단 | 제한 |
| --- | --- | --- |
| NHN KCP | PAYCO, 네이버페이, 카카오페이, 애플페이 | 기존 직접 호출과 네이버페이 카드·머니/포인트 분리 유지 |
| KG이니시스 | 삼성페이, L.pay, 카카오페이 | 구 INIpay 삼성페이는 모바일만, PRO는 PC·모바일 지원. 범용 KPAY 버튼 제거 |
| 토스페이먼츠 API | 토스페이, 네이버페이, 삼성페이, 애플페이, L.pay, 카카오페이, 핀페이, PAYCO, SSG페이 | 계약된 수단의 자체창 호출 |
| NICEPAY | 삼성페이, 네이버페이, 카카오페이, 애플페이, PAYCO, SK페이, SSG페이, L.pay | 기존 요청 파라미터 유지. 네이버페이는 `E020=CARD` 카드 경로 |
애플페이는 모든 PG에서 iOS 모바일에만 표시한다. 삼성페이는 PC에서 휴대폰 연결이 가능하며 구 INIpay만 모바일 전용이다. 표시 조건은 결제 가능 여부를 보증하지 않으며, 실제 기기·브라우저·MID 계약 상태를 확인해야 한다. NICEPAY 간편결제 및 KCP 네이버페이·카카오페이는 기존 관리자 안내처럼 테스트 결제가 제한된다. 다른 수단도 PG에 테스트 지원 여부를 확인한다.
## KCP 네이버페이 병용 우선순위
선택한 PG가 네이버페이를 지원하고 해당 수단을 사용 설정했다면 기본 PG만 제공한다. 기본 PG가 네이버페이를 지원하지 않거나 사용 설정하지 않았다면 병용 설정과 KCP 인증정보를 확인하여 KCP 경로를 제공한다. 중복 버튼뿐 아니라 병용 스크립트·요청 처리에도 동일한 판정을 적용한다.
기본 PG의 네이버페이가 활성화되어 있으면 KCP 머니/포인트 버튼도 숨긴다. 기본 PG마다 카드·머니 지원 범위가 다르므로, 예를 들어 NICEPAY의 카드 경로 대신 KCP 머니/포인트가 필요하다면 NICEPAY 네이버페이를 해제하고 KCP 병용과 포인트 사용을 설정한다. 기존 주문에 저장된 `od_pg`, 거래번호, 인증정보와 조회·영수증·취소 코드는 유지한다.
## 설정 저장과 배포
`lib/shop.easypay.lib.php`의 목록을 관리자와 PC·모바일 주문서가 함께 사용한다. `de_easy_pay_services`에 PG별 키를 저장한다. 첫 저장 전에는 기존 이니시스 개별 설정과 토스 PAYCO를 승계한다. 저장 후에는 `inicis_configured`, `toss_configured` 표식으로 전체 선택 해제도 보존하며, 기존 이니시스 컬럼을 동기화하여 결제 모듈과의 호환을 유지한다.
기존 설치는 새 설정을 저장하기 전에 관리자 **환경설정 → DB업그레이드**에서 `20260909_001_pg_easypay_services`를 적용한다. 저장 공간을 `varchar(255)`에서 `varchar(1024)`로 확장하며 신규 설치 SQL도 같은 크기를 사용한다. 일반 페이지 접근으로 스키마를 변경하지 않는다.
## 검증
DB나 외부 PG 연결 없이 실행하는 회귀 테스트:
```bash
php tests/pg_easypay.php
node tests/pg_easypay.js
```
- PG별 버튼과 선택값, 기본 PG 우선과 KCP 병용 전환, 인증정보 누락, 전체 비활성, 기존 설정 승계 및 명시적 선택 해제
- PC·모바일 구분, iOS·Android 애플페이, 구 INIpay 삼성페이 노출
- 토스 9개 provider의 실제 SDK 요청 생성 함수에 SDK 대역을 연결하여 직접 호출과 일반 카드 재선택 검증
- NICEPAY 직접 호출 코드와 일반 카드 재선택 시 직접 호출 옵션·에스크로 복원
- 토스 승인 성공 처리에 카드 없는 전액 머니 결제와 복합결제 응답을 투입하여 총 승인금액, provider, 승인번호, 현금영수증 저장 검증
실제 PG 네트워크 요청과 운영 DB 마이그레이션은 이 테스트에서 실행하지 않는다. 배포 검수 시 계약된 MID에서 각 수단의 성공·실패·사용자 취소·재시도, 주문내역·관리자·영수증 표시, 전액 취소·부분 취소를 확인한다. 머니/포인트 및 카드 복합결제는 실제 총액·현금영수증·부분 취소 가능 여부도 확인한다. 커스텀 스킨이 주문서를 자체 구현했다면 공통 버튼 함수와 결제수단 변경 이벤트를 반영해야 한다.
토스 자체창 요청은 [SDK v2 결제창 문서](https://docs.tosspayments.com/sdk/v2/js/payment)의 `method=CARD`, `card.flowMode=DIRECT`, `card.easyPay` 규격을 사용한다. provider 코드는 [토스 ENUM 문서](https://docs.tosspayments.com/codes/enum-codes)를 따른다. 일반 카드로 변경하면 `DEFAULT`로 되돌리고 SDK 요청에서 `easyPay`를 제외한다.
## 결제 로고 출처
- `img/tosspay-logo.svg`: [토스페이 공식 연동 가이드](https://docs-pay.toss.im/)에서 제공하는 로고 ZIP의 `logo-toss-pay-blue.svg` 원본.
- `img/pinpay-logo.png`: [스마트로페이 간편결제 안내](https://pg.smartro.co.kr/service/pg-service/payment/easypayment/)에서 제공하는 핀페이 로고 원본.
로고의 비율과 색상을 유지하고 저장소의 로컬 파일을 사용한다. 무통장입금·가상계좌 등 일반 결제수단은 아이콘 없이 텍스트로 표시한다. 간편결제 로고는 작은 원본을 확대하지 않는다. 원본의 여백과 종횡비를 고려하여 큰 로고만 축소하고, 원본이 작은 L.pay·카카오페이·PAYCO는 원래 크기를 유지한다.
+48
View File
@@ -0,0 +1,48 @@
# 그누보드(영카트) 5.6.37 배포 안내 초안
배포 준비 중이며 공개 릴리스 게시 전 검토용 문서입니다. 변경 기준은 5.6.36입니다.
## 업데이트 절차
1. 소스·설정·DB를 백업합니다. `config.php`의 `G5_DOMAIN`을 실제 운영 주소로 설정합니다. 보안 메일은 이 주소만 사용하며 미설정·잘못된 설정에서는 발송이 중단됩니다. 자세한 설정과 기존 첨부 점검은 [보안 업데이트 안내](security-release-draft.md)를 확인하십시오.
2. SIRK 전용 카카오페이 이용 사이트는 [종료 및 기존 거래 지원 절차](sirk-kakaopay-retirement.md)에 따라 진행 중 거래와 취소 처리 경로를 정리한 뒤 업데이트합니다. 일반 PG의 카카오페이는 계속 지원합니다.
3. 변경 파일을 반영하고 아래 삭제 대상 파일을 정리합니다. 사용자 수정 파일과 설정은 먼저 비교·병합합니다. 패치 ZIP은 5.6.36에서 업데이트할 때 사용하며 파일 삭제를 자동 처리하지 않습니다.
4. 최고관리자로 **환경설정 → DB업그레이드**에서 상태를 확인하고 마이그레이션을 실행합니다. 일반 화면 접근 시 자동 DB 보정은 종료되었으므로 파일 복사만으로 업데이트가 끝나지 않습니다. 공식 배포본에는 `bin/db-migrate.php`가 포함되지 않습니다. [DB업그레이드 안내](database-migrations.md)를 확인하십시오.
5. 회원가입·메일 인증·비회원 게시물 수정과 PC·모바일 주문·취소·포인트 지급을 확인합니다. 커스텀 테마와 결제 연동은 변경된 공통 함수 및 설정을 반영하십시오.
## 보안 변경
- KVE-2026-2269, KVE-2026-2331: 보안 메일 링크를 운영자가 설정한 고정 도메인으로 제한합니다.
- KVE-2026-2330: 비회원 게시물 수정 인증과 토큰 발급 검증을 강화합니다.
- KVE-2026-2329: XHTML 등 실행형 첨부 파일의 저장을 차단합니다. 기존 첨부는 별도 점검이 필요합니다.
- KVE-2026-2344: 관리자 보관함·상품판매순위의 날짜 입력 검증과 속성 인코딩으로 XSS를 차단합니다.
## 기능 및 오류 수정
- 버전형 DB 마이그레이션, 기존 상태 확인 이력 등록, 쇼핑몰 후설치를 지원합니다.
- 메일 인증 링크 유효시간 설정과 메일 미인증 만료 회원 정리 기능을 추가합니다.
- 관리자 회원 수정 화면에서 본인확인 정보를 삭제할 수 있습니다.
- 소셜 회원가입에서 휴대폰번호 수집·입력·저장을 지원합니다.
- PG별 간편결제 개별 표시와 기본 PG 우선 네이버페이 처리를 적용하고 결제수단 표시를 정리합니다.
- SIRK 전용 카카오페이 연동을 종료하고 기존 주문의 취소·환불 지원 안내를 제공합니다.
- PC·모바일 주문서의 옵션가 표시에 수량을 반영합니다.
- 상품별 배송완료 시각을 기준으로 주문 포인트를 지급합니다. 기존 완료 상품의 처리 기준은 [주문 포인트 안내](order-completion-point.md)를 확인하십시오.
- 쇼핑몰 최하위 분류에서도 같은 단계 메뉴를 유지합니다.
- 객체 캐시의 타입별 저장소를 분리해 값 충돌을 방지합니다.
- 게시물 제목을 UTF-8 문자 단위로 제한해 저장 오류를 방지합니다.
- 구 토스페이먼츠 테스트 MERT KEY 안내를 보강합니다.
## 패치 적용 시 삭제할 파일
진행 중 SIRK 거래를 정리하고 사용자 코드의 직접 참조를 수정한 뒤 삭제하십시오. 목록에 없는 이전 승인·반환·취소 URL의 종료 응답 파일은 유지합니다.
- `orderupgrade.php`
- `shop/kakaopay/_common.php`
- `shop/kakaopay/incKakaopayCommon.php`
- `shop/kakaopay/kakaopay_result.php`
- `shop/kakaopay/mobile_orderform.1.php`
- `shop/kakaopay/mobile_pay_result.php`
- `shop/kakaopay/orderform.1.php`
- `shop/kakaopay/orderform.2.php`
- `shop/kakaopay/orderform.3.php`
- `shop/kakaopay/pc_pay_result.php`
+41
View File
@@ -0,0 +1,41 @@
# KVE-2026-2344 관리자 날짜 필터 보완
보관함(`wishlist.php`, 메뉴 `500140`)과 상품판매순위(`itemsellrank.php`, 메뉴 `500100`)의 날짜 필터를 보완했다. 기존에는 입력에 숫자가 하나라도 있으면 원문을 허용하고 HTML 속성에 직접 출력하여 반사 XSS가 발생할 수 있었다.
## 변경 동작
- 두 화면은 공통 `date_filter.lib.php`로 날짜를 검사한다. 관리자 권한 확인 뒤, 화면 헤더 출력과 조회 SQL 실행 전에 검증한다.
- `fr_date`와 `to_date`는 모두 문자열이며 8자리 ASCII 숫자여야 한다. `checkdate()`로 실제 날짜인지 확인하고 시작일이 종료일보다 늦으면 거부한다.
- 날짜 파라미터가 하나라도 전달되면 두 값 모두 유효해야 한다. 배열·빈 문자열·한쪽 누락·개행·잘못된 달력 날짜·기간 역전은 HTTP 400과 고정 안내 문구로 거부한다. 공격 문자열은 안내에 반영하지 않는다.
- 날짜 파라미터가 모두 없는 최초 조회는 전체 기간을 조회한다. 보관함은 빈 날짜, 판매순위는 기존과 같이 종료일에 오늘 날짜를 표시한다.
- 날짜 input은 `htmlspecialchars`의 `ENT_QUOTES | ENT_SUBSTITUTE`와 UTF-8로 인코딩한다.
- 페이지 이동·정렬 링크의 필터는 `http_build_query`로 URL 인코딩하고 HTML 속성에 맞게 인코딩한다. 필터를 사용하지 않은 조회에서는 빈 날짜를 링크에 넣지 않는다. 페이지 이동 중 분류·날짜·정렬 조건을 유지한다.
일반 관리자 권한 검사와 상품·회원·주문 데이터 저장 동작은 변경하지 않는다. 스키마 변경이나 데이터 마이그레이션은 필요하지 않다.
## 검증
```bash
php tests/admin_date_filter.php
node tests/admin_date_filter.js
```
브라우저 검증에는 Playwright와 Chromium이 필요하다. 별도 설치 경로를 사용하는 경우 `PLAYWRIGHT_MODULE`과 `CHROME_EXECUTABLE` 환경변수로 지정할 수 있다.
PHP 검증은 실제 두 페이지를 실행하며 인증·DB·공통 헤더는 대역을 사용한다. 실제 입력·출력·페이징 및 정렬 함수로 다음을 확인한다.
- 두 페이지와 두 날짜 필드에 대한 100개 잘못된 입력의 HTTP 400 거부 및 조회 SQL 미실행
- 한쪽 날짜 누락, 명시적 빈값, 기간 역전 거부
- 최초 전체 조회와 페이지 이동, 윤년 정상 조회, 분류 필터와 정렬 조건 유지
- Chromium에서 추가 이벤트 속성과 스크립트 실행 표식이 생성되지 않음
- 검증 단계와 별도로, 실제 input 출력문에 속성 주입 문자열을 넣어도 출력 인코딩으로 값 안에만 남고 포커스 이벤트가 실행되지 않음
운영 DB·실제 관리자 세션을 사용하거나 회원 권한을 변경하는 검증은 수행하지 않는다. 배포 전 운영과 동등한 격리 환경에서 관리자 로그인·권한별 접근과 정상 조회를 추가 확인한다.
## 배포 및 보고 상태
현재 상태는 코드 수정 및 격리 회귀 검증 완료다. 운영 반영과 수정 버전 배포 완료를 의미하지 않는다. 배포 버전·배포일은 확정 후 별도로 기록한다.
인터넷진흥원 중간 회신 문구:
> KVE-2026-2344와 관련하여 관리자 보관함 및 동일 원인이 확인된 상품판매순위의 날짜 입력 검증과 출력 인코딩을 보완하였습니다. 잘못된 날짜 입력은 조회 전에 거부하도록 수정하였으며, 정상 조회·정렬·페이지 이동과 브라우저의 이벤트 실행 차단을 격리 환경에서 검증하였습니다. 현재 코드 수정 및 회귀 검증을 완료한 상태이며, 운영 반영 및 배포 버전·배포일은 확정 후 조치 결과와 함께 별도 통보하겠습니다.
+41
View File
@@ -0,0 +1,41 @@
# 보안 업데이트 배포 게시물 초안
배포 버전과 배포일은 릴리스 확정 후 기재합니다.
## 변경 사항
- KVE-2026-2269 / KVE-2026-2331: 비밀번호 재설정·회원가입 인증·이메일 재인증·수신 거부 메일의 보안 링크를 운영자가 지정한 `G5_DOMAIN`으로 생성합니다. 요청의 Host 또는 프록시 전달 헤더를 대체 주소로 사용하지 않습니다.
- KVE-2026-2330: 비회원 게시물 수정 시 기존 비밀번호를 확인한 게시물별 세션 인증을 저장 단계에서 검증합니다. 인증은 30분 동안 유효하며 저장 후 폐기됩니다. 비밀번호가 변경된 경우에도 무효화됩니다. 답변 작성 이력이나 일반 쓰기 토큰만으로 수정할 수 없습니다. 1:1문의 토큰 발급은 로그인한 회원의 해당 용도로 제한합니다.
- KVE-2026-2329: PHP 첨부 처리에서 XHTML/XHT, SVG/SVGZ, XML/XSL/XSLT, MHT/MHTML, HTC 확장자를 저장 전에 거부합니다. 대소문자, 다중 확장자 및 파일명 정규화도 검사합니다. 게시판·1:1문의·폼메일·쇼핑몰 로고의 기존 SVG 검사 경로에 적용합니다.
## 업데이트 전 고정 도메인 설정
`config.php`의 기존 `G5_DOMAIN` 값을 사이트의 실제 공개 주소로 설정하십시오. HTTPS 운영 사이트는 HTTPS 주소를 사용합니다. 하위 디렉터리에 설치했다면 해당 경로까지 포함합니다.
```php
define('G5_DOMAIN', 'https://www.example.com/community');
```
스킴은 `http` 또는 `https`이며 필요하면 포트를 지정할 수 있습니다. 사용자 정보, 쿼리, fragment, 공백·제어문자는 포함할 수 없습니다. 다중 도메인으로 접속하더라도 보안 메일은 이 대표 주소를 사용합니다. 해당 주소에서 인증 페이지와 사이트를 실제로 이용할 수 있는지 확인하십시오.
`G5_HTTPS_DOMAIN`은 기존 HTTPS 페이지 전환 설정입니다. 보안 메일 주소는 `G5_DOMAIN`만 사용하므로 `G5_HTTPS_DOMAIN`만 설정하는 것으로 대체할 수 없습니다.
`G5_DOMAIN`이 비어 있거나 유효하지 않으면 비밀번호 재설정 인증값을 변경하거나 메일을 발송하지 않습니다. 회원 존재 여부를 노출하지 않도록 비밀번호 찾기의 응답 문구는 기존과 같습니다. 메일 인증이 필요한 가입·이메일 변경·재발송 및 수신 거부 링크를 포함한 회원메일 발송도 중단되므로 업데이트 전에 설정하십시오. 설정 오류는 서버 PHP 오류 로그에 기록됩니다.
## CAPTCHA 서비스 설정
기본환경설정의 캡챠 선택에서 기존 지원 기능인 Google reCAPTCHA V2 또는 Invisible reCAPTCHA 사용을 권장합니다. 서비스 관리 콘솔에서 실제 운영 도메인을 등록하고 선택한 유형에 맞는 Site key와 Secret key를 발급받아 설정하십시오. PC·모바일의 가입, 비밀번호 찾기 및 비회원 글쓰기에서 정상 동작을 확인하십시오.
이번 업데이트는 기본 Kcaptcha의 음성 자동 복원 문제 자체를 수정하지 않습니다. 게시물 수정 인증은 CAPTCHA 통과 여부와 별도로 검증합니다.
## 이미 저장된 첨부 및 웹 서버 점검
신규 업로드 차단만으로 기존 위험 파일이 제거되거나 직접 접근이 차단되지는 않습니다. 운영자는 업데이트 전에 백업을 확보하고 `data/file`, `data/qa` 등 첨부 보관 영역에서 위험 확장자와 다중 확장자 파일을 점검하십시오. DB의 첨부 정보와 실제 파일의 대응 관계를 확인한 뒤 필요한 파일을 웹 공개 영역 밖으로 격리하거나 직접 접근을 차단하십시오. 일괄 삭제로 정상 첨부나 DB 참조를 훼손하지 않도록 주의하십시오.
Apache에서는 첨부 영역의 실제 경로에 디렉터리 목록 비활성화(`Options -Indexes`), 위험 파일 직접 접근 거부 및 스크립트 실행 방지를 적용하십시오. `.htaccess`의 존재만으로 보호가 보장되지는 않습니다. 해당 경로의 `AllowOverride` 적용 여부와 규칙의 실제 차단 확장자를 확인해야 합니다.
nginx에서는 첨부 경로의 `autoindex off`와 위험 파일 접근 거부 규칙을 확인하고, PHP 등 실행 핸들러로 첨부 경로가 전달되지 않도록 location 우선순위를 점검하십시오. 서버별 PHP 연동 방식과 경로가 다르므로 운영 환경에 맞춰 적용해야 합니다.
서버 설정 변경은 운영자가 수행합니다. 적용 후 첨부 직접 URL이 실행되지 않고 디렉터리 목록이 노출되지 않는지 확인하십시오. 직접 URL은 PHP 다운로드 경로를 거치지 않으므로 `download.php`의 다운로드 헤더만으로 보호되지 않습니다. 정상 이미지 표시 및 권한에 따른 일반 첨부 다운로드도 함께 확인하십시오.
사용자 스킨이나 외부 플러그인이 별도 저장·메일·토큰 경로를 구현했다면 해당 경로에도 같은 검증이 적용되는지 별도 점검이 필요합니다.
+44
View File
@@ -0,0 +1,44 @@
# SIRK 전용 카카오페이 연동 종료와 기존 고객 지원
## 적용 범위
적용 예정 버전은 **5.6.37**이다. `SIRK`로 시작하는 KG이니시스 카카오페이 전용 MID와 주문의 `od_pg = 'KAKAOPAY'` 경로를 정리한다. 일반 KG이니시스/INIpay PRO, NHN KCP, NICEPAY 등 PG에 포함된 카카오페이는 계속 사용할 수 있다.
기존 이용 고객 사후지원 담당은 **김민섭(minsup@sir.kr)**이다. 실제 고객 안내·거래 확인·지원 완료 여부는 배포 담당자가 별도로 기록한다. 이 문서 작성이나 코드 변경만으로 고객 안내가 완료된 것은 아니다.
## 변경 내용
- 쇼핑몰설정의 전용 MID·웹결제 사인키·키패스워드·사용 여부·숨겨진 HashKey 입력과 저장을 제거한다. 기존 전용 설정이 남아 있는 사이트에는 종료와 지원 연락처를 안내한다.
- PC·모바일 주문서에서 전용 결제 선택지를 제거하고, 이전 주문서의 POST와 임시 주문 저장도 서버에서 차단한다.
- 전용 서명·승인 요청 코드를 제거한다. 이전 결제창의 서명·승인·반환·통보 URL은 HTTP 410과 종료 안내를 반환하며, PG 요청이나 DB 변경, 통보 성공 응답을 수행하지 않는다. 서명 URL은 오류 JSON을 반환한다.
- 기존 주문 조회·매출 표시·영수증 표시는 유지한다. 구매자 자동 취소와 관리자 자동 전체·부분취소는 지원 안내로 차단한다. 다른 PG의 기본 분기로 넘기지 않는다.
- 운영자가 실제 PG 취소를 확인한 경우에만 주문관리의 확인 항목을 체크하고 상품 상태를 취소·반품·품절로 정리할 수 있다. 이 동작은 PG 취소나 환불금액의 자동 반영을 수행하지 않는다.
- 신규 설치 SQL에서 전용 설정 컬럼 5개를 제외한다. 기존 DB의 컬럼·설정값·주문·임시 주문·결제 로그는 삭제하지 않는다. `de_kakaopay_iniapi_key`를 추가하거나 다른 인증키로 치환하지 않는다.
- `shop/settle_kakaopay.inc.php`는 이전 테마의 초기화 include 호환을 위해 비활성화 상태만 반환한다. 자동 취소 경로의 이전 파일도 실패 안내용으로 유지한다. 결제용 내부 폼·결과 파일은 제거하므로 커스텀 테마의 직접 참조는 배포 전에 정리한다.
## 배포 전 진행 중 거래 정리
1. 담당자가 기존 이용 고객에게 적용 버전과 실제 배포 시각, 지원 연락처를 안내하고 현재 사용 여부와 취소 처리 방식을 확인한다.
2. 전용 결제를 사용 중인 사이트는 배포 전 점검 시간을 잡고, 주문 접수 점검으로 새 주문서 진입과 신규 결제 시작을 막는다. 기존 승인·반환 URL은 점검 차단 대상에서 제외한다. 기존 코드에서는 사용 설정을 끄면 진행 중 승인도 차단되므로, 거래 정리 전에 전용 사용 설정부터 해제하지 않는다. 이미 열린 결제창은 별도로 확인한다.
3. 이 단계에서는 기존 승인·반환 경로를 유지한다. 진행 중 결제의 종료를 기다리고 이니시스 거래내역과 쇼핑몰 주문·임시 주문·결제 로그를 대조한다. 고정된 대기 시간만으로 완료됐다고 판단하지 않는다.
4. PG 승인만 있고 주문이 없는 거래, 처리 중 취소, 미확인 반환 요청이 있으면 담당자가 먼저 해결한다. 미완료 주문의 자동 복구는 종료 버전에서 차단하므로 배포 전에 정리하거나 담당자 지원 대상으로 명시한다.
5. 진행 중 거래가 정리된 것을 확인하고 DB·설정·소스를 백업한 뒤 종료 버전을 배포한다. 고객별 배포·안내·미해결 거래 여부를 기록한다.
6. 배포 후에도 이전 URL 요청이 도착하면 성공 처리하지 않는다. 재결제를 유도하지 않고 담당자가 PG 승인 여부를 확인해 주문 누락·중복 결제를 처리한다.
고객의 운영 상황을 확인하지 않은 상태에서 종료 코드를 먼저 배포하면 진행 중 거래의 주문 반영이 중단될 수 있다. 배포 전 확인이 끝나지 않은 고객은 일정을 조정한다.
## 기존 거래 취소·부분취소 지원
1. 운영자가 거래 당시의 SIRK MID로 이니시스 상점관리자에 접속하고 주문번호·거래번호·승인금액·기존 취소금액을 대조한다. 이미 취소된 건을 다시 취소하지 않는다.
2. 해당 거래의 전체 또는 부분취소 가능 여부를 확인해 처리한다. 상점관리자에서 처리할 수 없는 건은 사후지원 담당자가 KG이니시스와 확인한다.
3. 이니시스의 실제 완료 상태·취소금액·잔액을 확인한다. 쇼핑몰 로그의 `cancel`이나 주문 상태만으로 PG 취소 완료를 판단하지 않는다.
4. 쇼핑몰 주문관리에서 실제 취소 확인 항목을 체크한 뒤 해당 상품의 상태를 정리한다. 상태 변경만으로 PG 취소가 실행되거나 환불금액이 입력되지는 않는다.
5. 주문관리의 결제취소/환불 금액에 PG에서 확인한 **누적 환불금액**을 반영하고 미수금·잔액을 대조한다. 상점메모에 처리 시각·금액·담당자·PG 확인 결과를 기록한다.
6. 부분취소는 선택 상품과 세금·배송비·쿠폰 처리 범위를 함께 확인한다. 주문 상태 변경에 따른 재고 복원·포인트 환급과 실제 지급 내역도 대조하여 중복 처리하지 않는다.
7. 구매자에게 완료 결과를 안내한다. 주문·임시 주문·로그는 확인 자료로 보존하며, 인증키·개인정보·거래 식별정보를 공개 이슈나 변경 기록에 게시하지 않는다.
## 검증과 지원 기록
`php bin/check-sirk-retirement.php`로 이전 결제·취소 요청의 차단, PG 성공 오인 방지, 잔여 참조와 설정 제거를 검사한다. 신규 설치 SQL 변경 후에는 마이그레이션 스키마 검사와 쇼핑몰 설치 검사를 함께 수행한다.
배포 담당자는 고객별로 안내 일시, 적용 버전/시각, 진행 중 거래 확인 결과, 실제 취소 경로, 지원 상태와 후속 담당자를 비공개로 관리한다. 테스트 결제·실거래 확인과 고객 안내는 코드 검사와 구분해서 기록한다.
+1 -2
View File
@@ -80,5 +80,4 @@ add_replace('get_pretty_url', 'add_pretty_shop_url', 10, 5);
add_replace('false_short_url_clean', 'shop_short_url_clean', 10, 4);
add_replace('add_nginx_conf_rules', 'add_shop_nginx_conf_rules', 10, 3);
add_replace('add_mod_rewrite_rules', 'add_shop_mod_rewrite_rules', 10, 3);
add_replace('admin_dbupgrade', 'add_shop_admin_dbupgrade', 10, 3);
add_replace('exist_check_seo_title', 'shop_exist_check_seo_title', 10, 4);
add_replace('exist_check_seo_title', 'shop_exist_check_seo_title', 10, 4);
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1038.2 200" enable-background="new 0 0 1038.2 200" xml:space="preserve">
<g>
<path fill="#0064FF" d="M906.8,75.3c-9.5-11-23-17.1-38.1-17.1c-29.6,0-51.9,23.4-51.9,54.5s22.3,54.5,51.9,54.5
c15.6,0,29.8-6.7,39.2-18.3v16.9h15.7V59.7h-16.8L906.8,75.3z M871.3,150.8c-21.2,0-36.7-16-36.7-38.1s15.4-38.1,36.7-38.1
c20.2,0,35.2,14.4,36.6,35v6.2C906.5,136.4,891.5,150.8,871.3,150.8z"/>
<path fill="#0064FF" d="M1019.9,59.3l-34.8,81.5l-33.8-81.5h-18.6l43.1,101.1l-16.6,39.4h17.4l14.3-34.4l47.3-106.1L1019.9,59.3z"
/>
<path fill="#0064FF" d="M754.3,58.2c-15.6,0-29.8,6.7-39.2,18.3V59.9h-15.7v139.9h16.8v-49.7c9.5,11,23,17.1,38.1,17.1
c29.6,0,51.9-23.4,51.9-54.5S783.9,58.2,754.3,58.2L754.3,58.2z M751.7,150.8c-20.2,0-35.2-14.4-36.6-35v-6.2
c1.4-20.6,16.4-35,36.6-35c21.2,0,36.7,16,36.7,38.1S773,150.8,751.7,150.8z"/>
<path fill="#0064FF" d="M264,126.5V82.7h-22.8v-23H264V31.9l27.9-7.5v35.2h34.8v23h-34.8V126c0,11.5,6.2,18.3,16.9,18.3
c6.1,0,10.6-1.2,15.3-4.7l10,20.8c-7.4,5.7-17.1,8.4-27.7,8.4C279.3,168.9,264,153.6,264,126.5L264,126.5z"/>
<path fill="#0064FF" d="M335.5,112.8c0-31.9,24.7-55.7,58-55.7s58,23.9,58,55.7c0,31.9-24.7,55.8-58,55.8S335.5,144.7,335.5,112.8z
M393.5,143.7c17,0,29.8-13.2,29.8-30.8S410.7,82,393.5,82c-17,0-29.8,13.2-29.8,30.8S376.5,143.7,393.5,143.7L393.5,143.7z"/>
<path fill="#0064FF" d="M458.8,140.8l27.7-7.5c1.8,8.6,9.8,13.5,22.7,13.5c10.6,0,18.3-4.8,18.3-11.1c0-4.7-5.4-8-13.7-9.8
l-20.7-4.4c-19.6-4.2-31.7-14.6-31.7-29.6C461.4,71,479.5,57,506,57c23.7,0,41.7,10.8,46.5,28.2l-26.1,7.1
c-1.7-8.3-9.9-13.8-21.3-13.8c-9.6,0-16,4.5-16,10.8c0,4.9,4.4,7.5,11.5,9.1l24.5,5.6c18.5,4.2,29.7,14.5,29.7,29.5
c0,21.9-18.3,35.1-48,35.1C480.8,168.6,462.7,158.1,458.8,140.8L458.8,140.8z"/>
<path fill="#0064FF" d="M561.6,140.8l27.7-7.5c1.8,8.6,9.8,13.5,22.7,13.5c10.6,0,18.3-4.8,18.3-11.1c0-4.7-5.4-8-13.7-9.8
l-20.7-4.4c-19.6-4.2-31.7-14.6-31.7-29.6c0-20.9,18.1-34.9,44.6-34.9c23.7,0,41.7,10.8,46.5,28.2l-26.1,7.1
c-1.7-8.3-9.9-13.8-21.3-13.8c-9.6,0-16,4.5-16,10.8c0,4.9,4.4,7.5,11.5,9.1l24.2,5.5c18.5,4.2,29.7,14.5,29.7,29.5
c0,21.9-18.3,35.1-48,35.1C583.5,168.6,565.4,158.1,561.6,140.8z"/>
<path fill="#0064FF" d="M118.4,45.4l8.9-41.8c0.4-2.1-1.4-4-3.5-3.5c-12.9,2.3-26.3,6.3-39.6,12C24.4,37.4-11.7,87,3.5,122.9
c11.5,26.9,48.6,38.4,91.7,31.7l-8.9,41.8c-0.4,2.1,1.4,4,3.5,3.5c12.9-2.3,26.3-6.3,39.6-12c59.8-25.3,95.9-74.9,80.7-110.9
C198.7,50.2,161.6,38.7,118.4,45.4z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+22 -1
View File
@@ -200,6 +200,7 @@ CREATE TABLE IF NOT EXISTS `g5_config` (
`cf_point_term` int(11) NOT NULL DEFAULT '0',
`cf_use_copy_log` tinyint(4) NOT NULL DEFAULT '0',
`cf_use_email_certify` tinyint(4) NOT NULL DEFAULT '0',
`cf_email_certify_minutes` int(11) NOT NULL DEFAULT '60',
`cf_login_point` int(11) NOT NULL DEFAULT '0',
`cf_cut_name` tinyint(4) NOT NULL DEFAULT '0',
`cf_nick_modify` int(11) NOT NULL DEFAULT '0',
@@ -222,6 +223,8 @@ CREATE TABLE IF NOT EXISTS `g5_config` (
`cf_intercept_ip` text NOT NULL,
`cf_analytics` text NOT NULL,
`cf_add_meta` text NOT NULL,
`cf_syndi_token` varchar(255) NOT NULL DEFAULT '',
`cf_syndi_except` text NOT NULL,
`cf_member_skin` varchar(50) NOT NULL DEFAULT '',
`cf_use_homepage` tinyint(4) NOT NULL DEFAULT '0',
`cf_req_homepage` tinyint(4) NOT NULL DEFAULT '0',
@@ -987,4 +990,22 @@ CREATE TABLE IF NOT EXISTS `g5_menu` (
`me_use` tinyint(4) NOT NULL DEFAULT '0',
`me_mobile_use` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`me_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `g5_migrations`
--
DROP TABLE IF EXISTS `g5_migrations`;
CREATE TABLE IF NOT EXISTS `g5_migrations` (
`migration_id` varchar(100) NOT NULL,
`description` varchar(255) NOT NULL DEFAULT '',
`checksum` char(64) NOT NULL DEFAULT '',
`status` varchar(20) NOT NULL DEFAULT 'pending',
`error_message` text NOT NULL,
`execution_ms` int(11) NOT NULL DEFAULT '0',
`applied_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`migration_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+58 -6
View File
@@ -58,6 +58,7 @@ CREATE TABLE IF NOT EXISTS `g5_shop_cart` (
`ct_direct` tinyint(4) NOT NULL DEFAULT '0',
`ct_select` tinyint(4) NOT NULL DEFAULT '0',
`ct_select_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`ct_complete_time` datetime DEFAULT NULL,
PRIMARY KEY (`ct_id`),
KEY `od_id` (`od_id`),
KEY `it_id` (`it_id`),
@@ -364,7 +365,7 @@ CREATE TABLE IF NOT EXISTS `g5_shop_default` (
`de_inicis_pro_monitor_message` varchar(255) NOT NULL DEFAULT '',
`de_iche_use` tinyint(4) NOT NULL DEFAULT '0',
`de_easy_pay_use` tinyint(4) NOT NULL DEFAULT '0',
`de_easy_pay_services` varchar(255) NOT NULL DEFAULT '',
`de_easy_pay_services` varchar(1024) NOT NULL DEFAULT '',
`de_samsung_pay_use` tinyint(4) NOT NULL DEFAULT '0',
`de_inicis_lpay_use` tinyint(4) NOT NULL DEFAULT '0',
`de_inicis_kakaopay_use` tinyint(4) NOT NULL DEFAULT '0',
@@ -384,11 +385,6 @@ CREATE TABLE IF NOT EXISTS `g5_shop_default` (
`de_hp_use` tinyint(4) NOT NULL DEFAULT '0',
`de_escrow_use` tinyint(4) NOT NULL DEFAULT '0',
`de_tax_flag_use` tinyint(4) NOT NULL DEFAULT '0',
`de_kakaopay_mid` varchar(255) NOT NULL DEFAULT '',
`de_kakaopay_key` varchar(255) NOT NULL DEFAULT '',
`de_kakaopay_enckey` varchar(255) NOT NULL DEFAULT '',
`de_kakaopay_hashkey` varchar(255) NOT NULL DEFAULT '',
`de_kakaopay_cancelpwd` varchar(255) NOT NULL DEFAULT '',
`de_naverpay_mid` varchar(255) NOT NULL DEFAULT '',
`de_naverpay_cert_key` varchar(255) NOT NULL DEFAULT '',
`de_naverpay_button_key` varchar(255) NOT NULL DEFAULT '',
@@ -585,6 +581,9 @@ CREATE TABLE IF NOT EXISTS `g5_shop_item_use` (
`is_score` tinyint(4) NOT NULL DEFAULT '0',
`is_subject` varchar(255) NOT NULL DEFAULT '',
`is_content` text NOT NULL,
`is_reply_subject` varchar(255) NOT NULL DEFAULT '',
`is_reply_content` text NOT NULL,
`is_reply_name` varchar(25) NOT NULL DEFAULT '',
`is_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`is_ip` varchar(25) NOT NULL DEFAULT '',
`is_confirm` tinyint(4) NOT NULL DEFAULT '0',
@@ -899,6 +898,59 @@ CREATE TABLE IF NOT EXISTS `g5_shop_inicis_log` (
-- --------------------------------------------------------
--
-- Table structure for table `g5_shop_order_cancel_log`
--
DROP TABLE IF EXISTS `g5_shop_order_cancel_log`;
CREATE TABLE IF NOT EXISTS `g5_shop_order_cancel_log` (
`cl_id` int(11) NOT NULL AUTO_INCREMENT,
`od_id` bigint(20) unsigned NOT NULL,
`mb_id` varchar(255) NOT NULL DEFAULT '',
`cl_pg` varchar(30) NOT NULL DEFAULT '',
`cl_pg_version` varchar(30) NOT NULL DEFAULT '',
`cl_settle_case` varchar(100) NOT NULL DEFAULT '',
`cl_test` tinyint(4) NOT NULL DEFAULT '0',
`cl_source` varchar(20) NOT NULL DEFAULT '',
`cl_type` varchar(20) NOT NULL DEFAULT '',
`cl_status` varchar(30) NOT NULL DEFAULT '',
`cl_origin_tno` varchar(255) NOT NULL DEFAULT '',
`cl_cancel_tno` varchar(255) NOT NULL DEFAULT '',
`cl_pg_order_no` varchar(100) NOT NULL DEFAULT '',
`cl_seq` int(11) NOT NULL DEFAULT '0',
`cl_pg_seq` varchar(20) NOT NULL DEFAULT '',
`cl_idempotency_key` char(32) NOT NULL DEFAULT '',
`cl_request_amount` int(11) NOT NULL DEFAULT '0',
`cl_approved_amount` int(11) NOT NULL DEFAULT '0',
`cl_remaining_amount` int(11) NOT NULL DEFAULT '0',
`cl_tax_mny` int(11) NOT NULL DEFAULT '0',
`cl_vat_mny` int(11) NOT NULL DEFAULT '0',
`cl_free_mny` int(11) NOT NULL DEFAULT '0',
`cl_refund_before` int(11) NOT NULL DEFAULT '0',
`cl_refund_after` int(11) NOT NULL DEFAULT '0',
`cl_result_code` varchar(50) NOT NULL DEFAULT '',
`cl_result_msg` varchar(255) NOT NULL DEFAULT '',
`cl_reason` varchar(255) NOT NULL DEFAULT '',
`cl_request_data` mediumtext NOT NULL,
`cl_response_data` mediumtext NOT NULL,
`cl_noti_data` mediumtext NOT NULL,
`cl_ip` varchar(45) NOT NULL DEFAULT '',
`cl_requested_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`cl_approved_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`cl_local_applied_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`cl_created_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`cl_updated_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`cl_id`),
KEY `od_id` (`od_id`),
KEY `cl_pg` (`cl_pg`),
KEY `cl_pg_version` (`cl_pg_version`),
KEY `cl_status` (`cl_status`),
KEY `cl_idempotency_key` (`cl_idempotency_key`),
KEY `cl_origin_tno` (`cl_origin_tno`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `g5_shop_inicis_pay`
--
+1
View File
@@ -662,6 +662,7 @@ if($g5_shop_install) {
install_file_write($f, "\$g5['g5_shop_inicis_log_table'] = G5_SHOP_TABLE_PREFIX.'inicis_log'; // 이니시스 모바일 계좌이체 로그 테이블\n");
install_file_write($f, "\$g5['g5_shop_inicis_pay_table'] = G5_SHOP_TABLE_PREFIX.'inicis_pay'; // 이니시스 결제 처리 현황 테이블\n");
install_file_write($f, "\$g5['g5_shop_inicis_pay_event_table'] = G5_SHOP_TABLE_PREFIX.'inicis_pay_event'; // 이니시스 결제 처리 이력 테이블\n");
install_file_write($f, "\$g5['g5_shop_order_cancel_log_table'] = G5_SHOP_TABLE_PREFIX.'order_cancel_log'; // 주문 취소·환불 이력 테이블\n");
}
install_file_write($f, "?>");
+13 -4
View File
@@ -70,6 +70,15 @@ function is_disallowed_svg_filename(filename)
return /\.(svg|svgz)$/i.test(filename);
}
function is_disallowed_active_filename(filename)
{
filename = filename || "";
if (/[\x00-\x1f\x7f]/.test(filename)) return true;
filename = filename.replace(/["'<>=#&!%\\()*+?]/g, "");
filename = filename.substring(filename.lastIndexOf("/") + 1).replace(/[ .]+$/, "");
return /\.(svgz?|xhtml|xht|xml|xsl|xslt|mht|mhtml|htc)([. :]|$)/i.test(filename);
}
function is_svg_upload_target(input)
{
var name = input.name || "";
@@ -92,7 +101,7 @@ function has_disallowed_svg_file(input)
if (input.files && input.files.length) {
for (i=0; i<input.files.length; i++) {
if (is_disallowed_svg_filename(input.files[i].name)) {
if (is_disallowed_active_filename(input.files[i].name)) {
return true;
}
}
@@ -100,7 +109,7 @@ function has_disallowed_svg_file(input)
return false;
}
return is_disallowed_svg_filename(input.value);
return is_disallowed_active_filename(input.value);
}
function check_disallowed_svg_upload(form)
@@ -118,7 +127,7 @@ function check_disallowed_svg_upload(form)
return true;
}
alert("허용되지 않는 파일 확장자입니다. (svg, svgz)");
alert("허용되지 않는 파일 확장자입니다.");
$(form).find("input:submit, button:submit, input:image").prop("disabled", false);
invalid_file.focus();
@@ -799,7 +808,7 @@ $(function() {
return true;
}
alert("허용되지 않는 파일 확장자입니다. (svg, svgz)");
alert("허용되지 않는 파일 확장자입니다.");
this.value = "";
return false;
+35 -66
View File
@@ -2,98 +2,67 @@
if (!defined('_GNUBOARD_')) exit;
Class G5_object_cache {
/** @deprecated 직접 접근 대신 get/set/exists/delete 메서드를 사용한다. */
public $writes = array();
/** @deprecated 직접 접근 대신 get/set/exists/delete 메서드를 사용한다. */
public $contents = array();
/** @deprecated 타입별 $etcs[$type][$group][$key] 구조. 메서드 사용 권장. */
public $etcs = array();
function get($type, $key, $group ='default') {
switch ($type) {
case 'bbs':
$datas = $this->writes;
break;
case 'content' :
$datas = $this->contents;
break;
default :
$datas = $this->etcs;
break;
// 각 타입의 유일한 저장소를 참조로 반환한다. 빈 타입 버킷은 요청 종료까지 유지한다.
private function &get_type_cache($type) {
if ($type === 'bbs') {
return $this->writes;
}
if( $this->exists($type, $key, $group) ){
if ( is_object($datas[$group][$key]) )
return clone $datas[$group][$key];
else
return $datas[$group][$key];
if ($type === 'content') {
return $this->contents;
}
return false;
if (!isset($this->etcs[$type])) {
$this->etcs[$type] = array();
}
return $this->etcs[$type];
}
function exists($type, $key, $group = 'default' ) {
$return_data = '';
switch ($type) {
case 'bbs':
$datas = $this->writes;
break;
case 'content':
$datas = $this->contents;
break;
default :
$datas = $this->etcs;
break;
function get($type, $key, $group = 'default') {
if (!$this->exists($type, $key, $group)) {
return false;
}
return isset($datas[$group]) && ( isset($datas[$group][$key]) || array_key_exists($key, $datas[$group]) );
$datas = &$this->get_type_cache($type);
if (is_object($datas[$group][$key])) {
return clone $datas[$group][$key];
}
return $datas[$group][$key];
}
function set($type, $key, $data=array(), $group='default') {
if ( is_object( $data ) )
function exists($type, $key, $group = 'default') {
$datas = &$this->get_type_cache($type);
return isset($datas[$group]) && (isset($datas[$group][$key]) || array_key_exists($key, $datas[$group]));
}
function set($type, $key, $data = array(), $group = 'default') {
if (is_object($data)) {
$data = clone $data;
switch ($type) {
case 'bbs':
$this->writes[$group][$key] = $data;
break;
case 'content':
$this->contents[$group][$key] = $data;
break;
default :
$this->etcs[$group][$key] = $data;
break;
}
$datas = &$this->get_type_cache($type);
$datas[$group][$key] = $data;
}
/**
* cache 데이터 제거
* 지정한 타입, 그룹, 키의 캐시 데이터만 제거한다.
* @param string $type
* @param string $key
* @param string $group
* @return bool
*/
function delete($type, $key, $group = 'default')
{
function delete($type, $key, $group = 'default') {
if (!$this->exists($type, $key, $group)) {
return false;
}
switch ($type) {
case 'bbs':
$datas = &$this->writes;
break;
case 'content':
$datas = &$this->contents;
break;
default:
$datas = &$this->etcs;
break;
}
$datas = &$this->get_type_cache($type);
unset($datas[$group][$key]);
return true;
}
} //end Class;
}
+196 -33
View File
@@ -2696,6 +2696,121 @@ function get_email_cert_key($mb_id, $mb_datetime)
return hash_hmac('sha256', $payload, $key);
}
// 보안 메일은 요청 헤더가 아닌 운영자가 지정한 공개 URL만 사용한다.
function g5_security_mail_base_url($domain = null)
{
if ($domain === null) {
$domain = defined('G5_DOMAIN') ? G5_DOMAIN : '';
}
if (!is_string($domain) || $domain === '' || preg_match('/[\x00-\x20\x7f\\\\<>"\']/', $domain)) {
return false;
}
$parts = @parse_url($domain);
if (!$parts || empty($parts['scheme']) || !in_array(strtolower($parts['scheme']), array('http', 'https'), true)
|| empty($parts['host']) || isset($parts['user']) || isset($parts['pass'])
|| isset($parts['query']) || isset($parts['fragment'])) {
return false;
}
// 호스트에는 포트 구분용 IPv6 대괄호 외의 URL 구문을 허용하지 않는다.
$host = $parts['host'];
if ($host[0] === '[') {
if (substr($host, -1) !== ']' || !filter_var(substr($host, 1, -1), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) return false;
} elseif (!preg_match('/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9.])?$/i', $host)) {
return false;
}
if ($host[0] !== '[') {
if (strlen($host) > 254) return false;
$hostname = substr($host, -1) === '.' ? substr($host, 0, -1) : $host;
foreach (explode('.', $hostname) as $label) {
if (!preg_match('/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i', $label)) return false;
}
}
if (isset($parts['port']) && ($parts['port'] < 1 || $parts['port'] > 65535)) return false;
if (isset($parts['path']) && (preg_match('/%(?:0[0-9a-f]|1[0-9a-f]|7f|2f|5c)/i', $parts['path'])
|| preg_match('~(?:^|/)(?:\.|%2e){1,2}(?:/|$)~i', $parts['path']))) return false;
return rtrim($domain, '/');
}
function g5_require_security_mail_url()
{
$url = g5_security_mail_base_url();
if ($url === false) {
error_log('[g5 security mail] Valid G5_DOMAIN is required.');
alert('메일 인증을 위한 사이트 주소가 설정되지 않았습니다. 사이트 관리자에게 문의해 주십시오.');
}
return $url;
}
// 발급 시각을 포함한 메일 인증용 일회용 토큰을 생성한다.
function get_email_certify_token()
{
return get_random_token_string(16) . '.' . G5_SERVER_TIME;
}
// 기존 형식 또는 폐기된 토큰은 회원가입 시각을 기준으로 한다.
function get_email_certify_issued_at($stored_token, $mb_datetime)
{
if (preg_match('/\.([0-9]{10})$/', $stored_token, $matches)) {
return (int) $matches[1];
}
return strtotime($mb_datetime);
}
/**
* 메일 인증 토큰의 일치 여부와 유효시간을 확인한다.
*
* 발급 시각이 없는 기존 토큰은 회원가입 시각을 기준으로 만료 여부를 확인한다.
*
* @param string $token
* @param string $stored_token
* @param string $mb_datetime
* @param int $valid_minutes
* @param int|null $now
* @return bool
*/
function is_valid_email_certify_token($token, $stored_token, $mb_datetime, $valid_minutes, $now = null)
{
if (!$token || !$stored_token || !slow_equals((string) $stored_token, (string) $token)) {
return false;
}
$valid_minutes = (int) $valid_minutes;
if ($valid_minutes < 1) {
return true;
}
$issued_at = get_email_certify_issued_at($stored_token, $mb_datetime);
if (!$issued_at) {
return false;
}
$now = $now === null ? G5_SERVER_TIME : (int) $now;
return $issued_at + ($valid_minutes * 60) >= $now;
}
// 인증 완료·탈퇴·삭제 회원과 만료시간을 사용하지 않는 사이트는 제외한다.
function is_expired_email_certify_member($mb, $now = null)
{
global $config;
$valid_minutes = isset($config['cf_email_certify_minutes']) ? (int) $config['cf_email_certify_minutes'] : 60;
if (!$config['cf_use_email_certify'] || $valid_minutes < 1 || empty($mb['mb_id'])
|| $mb['mb_id'] === $config['cf_admin'] || !empty($mb['mb_leave_date'])
|| preg_match('/[1-9]/', $mb['mb_email_certify'])
|| preg_match('#^[0-9]{8}.*삭제함#', $mb['mb_memo'])) {
return false;
}
$issued_at = get_email_certify_issued_at($mb['mb_email_certify2'], $mb['mb_datetime']);
$now = $now === null ? G5_SERVER_TIME : (int) $now;
return $issued_at > 0 && $issued_at + ($valid_minutes * 60) < $now;
}
/**
* CSRF 방지용 Origin/Referer 검증 (OWASP 권장 패턴).
*
@@ -3615,21 +3730,6 @@ function insert_member_cert_history($mb_id, $name, $hp, $birth, $type)
$g5['member_cert_history_table'] = G5_TABLE_PREFIX.'member_cert_history';
}
// 멤버 본인인증 정보 변경 내역 테이블 없을 경우 생성
if(isset($g5['member_cert_history_table']) && !sql_query(" DESC {$g5['member_cert_history_table']} ", false)) {
sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['member_cert_history_table']}` (
`ch_id` int(11) NOT NULL auto_increment,
`mb_id` varchar(20) NOT NULL DEFAULT '',
`ch_name` varchar(255) NOT NULL DEFAULT '',
`ch_hp` varchar(255) NOT NULL DEFAULT '',
`ch_birth` varchar(255) NOT NULL DEFAULT '',
`ch_type` varchar(20) NOT NULL DEFAULT '',
`ch_datetime` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`ch_id`),
KEY `mb_id` (`mb_id`)
) ", true);
}
$sql = " insert into {$g5['member_cert_history_table']}
set mb_id = '{$mb_id}',
ch_name = '{$name}',
@@ -4016,6 +4116,50 @@ function conv_unescape_nl($str)
return str_replace($search, $replace, $str);
}
// 자진 탈퇴와 메일 미인증 만료 정리에서 동일한 탈퇴 처리를 사용한다.
function g5_leave_member($mb_id, $expired_email_only = false)
{
global $config, $g5;
$mb = get_member($mb_id);
if (empty($mb['mb_id']) || $mb['mb_id'] === $config['cf_admin'] || !empty($mb['mb_leave_date'])
|| preg_match('#^[0-9]{8}.*삭제함#', $mb['mb_memo'])) {
return false;
}
$condition = '';
if ($expired_email_only) {
if (!is_expired_email_certify_member($mb)) {
return false;
}
// 조회 이후 인증 완료·재발송·회원정보 변경이 있었다면 탈퇴시키지 않는다.
foreach (array('mb_email_certify', 'mb_email_certify2', 'mb_datetime', 'mb_email') as $field) {
$condition .= " and {$field} = '".sql_real_escape_string($mb[$field])."'";
}
}
$date = date('Ymd', G5_SERVER_TIME);
$memo = $date . ($expired_email_only ? ' 메일 미인증 만료로 탈퇴함' : ' 탈퇴함') . "\n";
$esc_mb_id = sql_real_escape_string($mb['mb_id']);
$sql = " update {$g5['member_table']}
set mb_leave_date = '{$date}',
mb_memo = CONCAT('".sql_real_escape_string($memo)."', IFNULL(mb_memo, '')),
mb_certify = '', mb_adult = 0, mb_dupinfo = '',
mb_email_certify2 = '', mb_lost_certify = ''
where mb_id = '{$esc_mb_id}' and mb_leave_date = '' {$condition} ";
if (!sql_query($sql) || get_sql_affected_rows() < 1) {
return false;
}
run_event('member_leave', $mb);
if (function_exists('social_member_link_delete')) {
social_member_link_delete($mb['mb_id']);
}
return true;
}
// 회원 삭제
function member_delete($mb_id)
{
@@ -4092,7 +4236,18 @@ function get_safe_filename($name)
return $name;
}
// 업로드 파일명이 SVG 또는 SVGZ 확장자인지 확인
// 브라우저에서 실행될 수 있는 첨부 확장자를 저장 전에 거부한다.
function is_disallowed_active_filename($filename)
{
if (!is_string($filename)) return true;
if (preg_match('/[\x00-\x1f\x7f]/', $filename)) return true;
$filename = basename(str_replace('\\', '/', $filename));
$filename = rtrim($filename, ' .');
// 다중 확장자 및 Windows 대체 데이터 스트림 표기도 차단한다.
return (bool) preg_match('/\.(?:svgz?|xhtml|xht|xml|xsl|xslt|mht|mhtml|htc)(?:[. :]|$)/i', $filename);
}
// 기존 스킨 및 플러그인과의 호환을 위한 SVG 검사 함수
function is_disallowed_svg_filename($filename)
{
if (!is_string($filename) || $filename === '') {
@@ -4187,21 +4342,6 @@ function login_password_check($mb, $pass, $hash)
if(!$mb_id)
return false;
if(G5_STRING_ENCRYPT_FUNCTION === 'create_hash' && (strlen($hash) === G5_MYSQL_PASSWORD_LENGTH || strlen($hash) === 16)) {
if( sql_password($pass) === $hash ){
if( ! isset($mb['mb_password2']) ){
$sql = "ALTER TABLE `{$g5['member_table']}` ADD `mb_password2` varchar(255) NOT NULL default '' AFTER `mb_password`";
sql_query($sql);
}
$new_password = create_hash($pass);
$sql = " update {$g5['member_table']} set mb_password = '$new_password', mb_password2 = '$hash' where mb_id = '$mb_id' ";
sql_query($sql);
return true;
}
}
return check_password($pass, $hash);
}
@@ -4562,7 +4702,29 @@ function get_sql_affected_rows($link=null)
}
}
// 불법접근을 막도록 토큰을 생성하면서 토큰값을 리턴
// 비회원의 기존 비밀번호 확인 이력을 게시판과 게시물에 결합한다.
function g5_write_edit_auth_key($bo_table, $wr_id)
{
return 'ss_write_edit_'.hash('sha256', $bo_table.':'.(int)$wr_id);
}
function g5_grant_write_edit_auth($bo_table, $write)
{
set_session(g5_write_edit_auth_key($bo_table, $write['wr_id']), array(
'expires' => G5_SERVER_TIME + 1800,
'password' => hash('sha256', $write['wr_password'])
));
}
function g5_has_write_edit_auth($bo_table, $write)
{
$auth = get_session(g5_write_edit_auth_key($bo_table, $write['wr_id']));
return empty($write['mb_id']) && is_array($auth) && isset($auth['expires'], $auth['password'])
&& $auth['expires'] >= G5_SERVER_TIME
&& slow_equals(hash('sha256', $write['wr_password']), $auth['password']);
}
// 글쓰기 요청의 CSRF 토큰을 발급한다. 수정 권한은 별도로 확인한다.
function get_write_token($bo_table)
{
$token = get_random_token_string(16);
@@ -4581,7 +4743,8 @@ function check_write_token($bo_table)
$token = get_session('ss_write_'.$bo_table.'_token');
set_session('ss_write_'.$bo_table.'_token', '');
if(!$token || !$_REQUEST['token'] || $token != $_REQUEST['token'])
if (!is_string($token) || !$token || !isset($_POST['token']) || !is_string($_POST['token'])
|| !slow_equals($token, $_POST['token']))
alert('올바른 방법으로 이용해 주십시오.', G5_URL);
return true;
+627
View File
@@ -0,0 +1,627 @@
<?php
if (!defined('_GNUBOARD_')) exit;
/**
* 버전형 DB 마이그레이션 파일을 읽고 실행한다.
*
* 마이그레이션 SQL은 G5_PATH/migrations 아래에 두며 파일명 순서대로 실행한다.
* 일반 웹 요청에서는 이 라이브러리를 불러와도 DDL을 실행하지 않는다.
*/
function g5_migration_table_name()
{
return G5_TABLE_PREFIX . 'migrations';
}
function g5_migration_path()
{
return G5_PATH . '/migrations';
}
function g5_migration_replace_placeholders($sql)
{
global $g5;
$replacements = array();
foreach ($g5 as $key => $table) {
if (substr($key, -6) === '_table' && is_string($table) && $table !== '') {
$replacements['{{' . $key . '}}'] = $table;
}
}
$shop_prefix = defined('G5_SHOP_TABLE_PREFIX') ? G5_SHOP_TABLE_PREFIX : G5_TABLE_PREFIX . 'shop_';
$fallbacks = array(
'member_auto_login_table' => G5_TABLE_PREFIX . 'member_auto_login',
'g5_shop_cart_table' => $shop_prefix . 'cart',
'g5_shop_item_table' => $shop_prefix . 'item',
'g5_shop_order_table' => $shop_prefix . 'order',
'g5_shop_default_table' => $shop_prefix . 'default',
'g5_shop_coupon_table' => $shop_prefix . 'coupon',
'g5_shop_coupon_log_table' => $shop_prefix . 'coupon_log',
'g5_shop_coupon_zone_table' => $shop_prefix . 'coupon_zone',
'g5_shop_inicis_log_table' => $shop_prefix . 'inicis_log',
'g5_shop_order_data_table' => $shop_prefix . 'order_data',
'g5_shop_post_log_table' => $shop_prefix . 'order_post_log',
'g5_shop_order_cancel_log_table' => $shop_prefix . 'order_cancel_log',
'g5_shop_inicis_pay_table' => $shop_prefix . 'inicis_pay',
'g5_shop_inicis_pay_event_table' => $shop_prefix . 'inicis_pay_event',
'sms5_config_table' => G5_TABLE_PREFIX . 'sms5_config',
'sms5_write_table' => G5_TABLE_PREFIX . 'sms5_write',
'sms5_history_table' => G5_TABLE_PREFIX . 'sms5_history',
'sms5_book_table' => G5_TABLE_PREFIX . 'sms5_book',
'sms5_book_group_table' => G5_TABLE_PREFIX . 'sms5_book_group',
'sms5_form_table' => G5_TABLE_PREFIX . 'sms5_form',
'sms5_form_group_table' => G5_TABLE_PREFIX . 'sms5_form_group'
);
foreach ($fallbacks as $key => $table) {
if (!isset($replacements['{{' . $key . '}}'])) {
$replacements['{{' . $key . '}}'] = $table;
}
}
return strtr($sql, $replacements);
}
function g5_migration_parse_condition($directive, $file)
{
if (!preg_match('/^--\s*@(if|skip-if)-(table|column|index)-(exists|missing)\s+(\S+)(?:\s+(\S+))?\s*$/i', $directive, $matches)) {
return array('error' => '지원하지 않는 실행 조건입니다: ' . basename($file) . ' - ' . trim($directive));
}
$condition = array(
'object' => strtolower($matches[2]),
'state' => strtolower($matches[3]),
'table' => g5_migration_replace_placeholders(trim($matches[4], '`')),
'name' => isset($matches[5]) ? trim($matches[5], '`') : ''
);
if ($condition['object'] !== 'table' && $condition['name'] === '') {
return array('error' => '컬럼 또는 인덱스 이름이 없습니다: ' . basename($file));
}
if (strpos($condition['table'], '{{') !== false || strpos($condition['table'], '}}') !== false) {
return array('error' => '지원하지 않는 조건 테이블 자리표시자가 있습니다: ' . basename($file));
}
return $condition;
}
function g5_migration_parse_file($file)
{
$contents = @file_get_contents($file);
if ($contents === false) {
return array('error' => '마이그레이션 파일을 읽을 수 없습니다: ' . basename($file));
}
$migration = array(
'id' => pathinfo($file, PATHINFO_FILENAME),
'description' => '',
'skip_table' => '',
'skip_column' => '',
'checksum' => hash('sha256', $contents),
'file' => $file,
'statements' => array()
);
if (!preg_match('/^[0-9]{8}_[0-9]{3}_[a-z0-9_]+$/', $migration['id'])) {
return array('error' => '마이그레이션 파일명이 규칙에 맞지 않습니다: ' . basename($file));
}
if (preg_match('/^--\s*@description\s+(.+)$/mi', $contents, $matches)) {
$migration['description'] = trim($matches[1]);
}
if (preg_match('/^--\s*@skip-if-column\s+(\S+)\s+(\S+)$/mi', $contents, $matches)) {
$migration['skip_table'] = g5_migration_replace_placeholders(trim($matches[1], '`'));
$migration['skip_column'] = trim($matches[2], '`');
}
$conditions = array();
$foreach_write_column = '';
$foreach_write_table = false;
$foreach_content_seo = false;
$buffer = '';
foreach (preg_split('/\r?\n/', $contents) as $line) {
if (preg_match('/^\s*--\s*@skip-if-column\s+/i', $line)) {
continue;
}
if (preg_match('/^\s*--\s*@foreach-write-table-if-column-missing\s+(\S+)\s*$/i', $line, $matches)) {
$foreach_write_column = trim($matches[1], '`');
$foreach_write_table = true;
continue;
}
if (preg_match('/^\s*--\s*@foreach-write-table\s*$/i', $line)) {
$foreach_write_table = true;
continue;
}
if (preg_match('/^\s*--\s*@foreach-content-missing-seo\s*$/i', $line)) {
$foreach_content_seo = true;
continue;
}
if (preg_match('/^\s*--\s*@(if|skip-if)-/i', $line)) {
$parsed_condition = g5_migration_parse_condition(trim($line), $file);
if (isset($parsed_condition['error'])) {
return $parsed_condition;
}
$conditions[] = $parsed_condition;
continue;
}
if (preg_match('/^\s*--/', $line)) {
continue;
}
$buffer .= $line . "\n";
while (($position = strpos($buffer, ';')) !== false) {
$statement = trim(substr($buffer, 0, $position));
$buffer = substr($buffer, $position + 1);
if ($statement === '') {
continue;
}
if (!$foreach_write_table && !$foreach_content_seo) {
$statement = g5_migration_replace_placeholders($statement);
}
$allowed_dynamic_placeholder = strpos($statement, '{{write_table}}') !== false || ($foreach_content_seo && strpos($statement, '{{content_table}}') !== false);
if ((strpos($statement, '{{') !== false || strpos($statement, '}}') !== false) && !$allowed_dynamic_placeholder) {
return array('error' => '지원하지 않는 테이블 자리표시자가 있습니다: ' . basename($file));
}
$migration['statements'][] = array('sql' => $statement, 'conditions' => $conditions, 'foreach_write_column' => $foreach_write_column, 'foreach_write_table' => $foreach_write_table, 'foreach_content_seo' => $foreach_content_seo);
$conditions = array();
$foreach_write_column = '';
$foreach_write_table = false;
$foreach_content_seo = false;
}
}
if (trim($buffer) !== '') {
return array('error' => 'SQL 문 끝에 세미콜론이 없습니다: ' . basename($file));
}
if (!$migration['statements']) {
return array('error' => '실행할 SQL이 없습니다: ' . basename($file));
}
return $migration;
}
function g5_migration_discover()
{
$files = glob(g5_migration_path() . '/*.sql');
$migrations = array();
if (!$files) {
return $migrations;
}
sort($files, SORT_STRING);
foreach ($files as $file) {
$migration = g5_migration_parse_file($file);
if (isset($migration['error'])) {
return array($migration);
}
$migrations[] = $migration;
}
return $migrations;
}
function g5_migration_table_exists($table)
{
$result = sql_query("SHOW TABLES LIKE '" . sql_real_escape_string($table) . "'", false);
if (!$result) {
return false;
}
while ($row = sql_fetch_array($result)) {
if (reset($row) === $table) {
return true;
}
}
return false;
}
function g5_migration_column_exists($table, $column)
{
$result = sql_query("SHOW COLUMNS FROM `" . str_replace('`', '``', $table) . "` LIKE '" . sql_real_escape_string($column) . "'", false);
return $result && sql_num_rows($result) > 0;
}
function g5_migration_index_exists($table, $index)
{
if (!g5_migration_table_exists($table)) {
return false;
}
$result = sql_query("SHOW INDEX FROM `" . str_replace('`', '``', $table) . "` WHERE Key_name = '" . sql_real_escape_string($index) . "'", false);
return $result && sql_num_rows($result) > 0;
}
function g5_migration_should_run_statement($conditions)
{
if (!$conditions) {
return true;
}
foreach ($conditions as $condition) {
if ($condition['object'] === 'table') {
$exists = g5_migration_table_exists($condition['table']);
} elseif ($condition['object'] === 'column') {
$exists = g5_migration_column_exists($condition['table'], $condition['name']);
} else {
$exists = g5_migration_index_exists($condition['table'], $condition['name']);
}
if (($condition['state'] === 'exists') !== $exists) {
return false;
}
}
return true;
}
function g5_migration_execute_statement($statement)
{
global $g5;
if ($statement['foreach_content_seo']) {
if (!function_exists('exist_seo_title_recursive') || !function_exists('generate_seo_title')) {
return array('error' => 'SEO 제목 생성 함수를 사용할 수 없습니다.', 'executed' => false);
}
$result = sql_query("SELECT co_id, co_subject FROM `{$g5['content_table']}` WHERE co_seo_title = ''", false);
if (!$result) {
return array('error' => sql_error_info(), 'executed' => false);
}
$executed = false;
while ($content = sql_fetch_array($result)) {
$seo_title = exist_seo_title_recursive('content', generate_seo_title($content['co_subject']), $g5['content_table'], $content['co_id']);
$sql = str_replace(
array('{{content_table}}', '{{content_id}}', '{{seo_title}}'),
array($g5['content_table'], sql_real_escape_string($content['co_id']), sql_real_escape_string($seo_title)),
$statement['sql']
);
if (!sql_query($sql, false)) {
return array('error' => $content['co_id'] . ': ' . sql_error_info(), 'executed' => $executed);
}
$executed = true;
}
return array('error' => '', 'executed' => $executed);
}
if (!$statement['foreach_write_table']) {
return array('error' => sql_query($statement['sql'], false) ? '' : sql_error_info(), 'executed' => true);
}
$result = sql_query("SELECT bo_table FROM `{$g5['board_table']}`", false);
if (!$result) {
return array('error' => sql_error_info(), 'executed' => false);
}
$executed = false;
while ($board = sql_fetch_array($result)) {
$write_table = $g5['write_prefix'] . $board['bo_table'];
if (!g5_migration_table_exists($write_table) || ($statement['foreach_write_column'] !== '' && g5_migration_column_exists($write_table, $statement['foreach_write_column']))) {
continue;
}
$sql = str_replace('{{write_table}}', $write_table, $statement['sql']);
if (!sql_query($sql, false)) {
return array('error' => $write_table . ': ' . sql_error_info(), 'executed' => $executed);
}
$executed = true;
}
return array('error' => '', 'executed' => $executed);
}
function g5_migration_get_records()
{
$table = g5_migration_table_name();
$records = array();
if (!g5_migration_table_exists($table)) {
return $records;
}
$result = sql_query("SELECT migration_id, checksum, status, error_message, applied_at FROM `{$table}` ORDER BY migration_id", false);
if (!$result) {
return $records;
}
while ($row = sql_fetch_array($result)) {
$records[$row['migration_id']] = $row;
}
return $records;
}
// 검사 실패는 객체 없음과 구분한다. 잘못된 건너뛰기 이력을 만들지 않는다.
function g5_migration_inspect_object($object, $table, $name, &$cache)
{
$key = $object . ':' . $table;
if (!array_key_exists($key, $cache)) {
$quoted = '`' . str_replace('`', '``', $table) . '`';
if ($object === 'table') {
$sql = "SHOW TABLES LIKE '" . sql_real_escape_string($table) . "'";
} elseif ($object === 'column') {
$sql = 'SHOW COLUMNS FROM ' . $quoted;
} else {
$sql = 'SHOW INDEX FROM ' . $quoted;
}
$result = sql_query($sql, false);
$cache[$key] = $result ? array() : null;
if ($result) {
while ($row = sql_fetch_array($result)) {
$value = $object === 'table' ? reset($row) : $row[$object === 'column' ? 'Field' : 'Key_name'];
$cache[$key][$value] = true;
}
}
}
return $cache[$key] === null ? null : isset($cache[$key][$object === 'table' ? $table : $name]);
}
// SQL을 실행하지 않고, 실행기의 모든 문이 건너뛰어질 때만 true를 반환한다.
function g5_migration_needs_no_execution($migration, &$cache)
{
global $g5;
if (isset($migration['error'])) {
return false;
}
if ($migration['skip_table'] && $migration['skip_column'] &&
g5_migration_inspect_object('column', $migration['skip_table'], $migration['skip_column'], $cache) === true) {
return true;
}
foreach ($migration['statements'] as $statement) {
$skip = false;
foreach ($statement['conditions'] as $condition) {
$exists = g5_migration_inspect_object($condition['object'], $condition['table'], $condition['name'], $cache);
if ($exists === null) {
return false;
}
if (($condition['state'] === 'exists') !== $exists) {
$skip = true;
break;
}
}
if ($skip) {
continue;
}
if (!$statement['foreach_write_table']) {
// 데이터 보정 및 조건 없는 DDL은 완료로 추정하지 않는다.
return false;
}
$result = sql_query("SELECT bo_table FROM `" . str_replace('`', '``', $g5['board_table']) . "`", false);
if (!$result) {
return false;
}
while ($board = sql_fetch_array($result)) {
$table = $g5['write_prefix'] . $board['bo_table'];
if (g5_migration_inspect_object('table', $table, '', $cache) !== true ||
$statement['foreach_write_column'] === '' ||
g5_migration_inspect_object('column', $table, $statement['foreach_write_column'], $cache) !== true) {
return false;
}
}
}
return true;
}
function g5_migration_status()
{
$migrations = g5_migration_discover();
$records = g5_migration_get_records();
$status = array();
$inspection_cache = array();
foreach ($migrations as $migration) {
if (isset($migration['error'])) {
$status[] = $migration;
continue;
}
$record = isset($records[$migration['id']]) ? $records[$migration['id']] : null;
$migration['status'] = $record ? $record['status'] : 'pending';
if (!$record && g5_migration_needs_no_execution($migration, $inspection_cache)) {
$migration['status'] = 'unrecorded';
}
$migration['checksum_changed'] = $record && $record['status'] === 'success' && $record['checksum'] !== $migration['checksum'];
$migration['error_message'] = $record ? $record['error_message'] : '';
$migration['applied_at'] = $record ? $record['applied_at'] : '';
$status[] = $migration;
}
return $status;
}
function g5_migration_status_label($status, $checksum_changed = false)
{
if ($checksum_changed) {
return '적용 후 파일 변경됨';
}
$labels = array(
'pending' => '대기',
'unrecorded' => '실행 불필요 (미기록)',
'success' => '성공',
'failed' => '실패'
);
return isset($labels[$status]) ? $labels[$status] : '알 수 없음';
}
function g5_migration_validate_target_dependencies($migrations, $records, $target_id)
{
$target_index = -1;
foreach ($migrations as $index => $migration) {
if (isset($migration['error'])) {
return $migration['error'];
}
if ($migration['id'] === $target_id) {
$target_index = $index;
break;
}
}
if ($target_index < 0) {
return '요청한 마이그레이션을 찾을 수 없습니다: ' . $target_id;
}
for ($index = 0; $index < $target_index; $index++) {
$migration = $migrations[$index];
$record = isset($records[$migration['id']]) ? $records[$migration['id']] : null;
if (!$record || $record['status'] !== 'success') {
return $target_id . ' 실행 전에 선행 마이그레이션을 먼저 실행해야 합니다: ' . $migration['id'];
}
if ($record['checksum'] !== $migration['checksum']) {
return '선행 마이그레이션 파일이 적용 후 변경되었습니다: ' . $migration['id'];
}
}
return '';
}
function g5_migration_ensure_table()
{
$table = g5_migration_table_name();
$sql = "CREATE TABLE IF NOT EXISTS `{$table}` (
`migration_id` varchar(100) NOT NULL,
`description` varchar(255) NOT NULL DEFAULT '',
`checksum` char(64) NOT NULL DEFAULT '',
`status` varchar(20) NOT NULL DEFAULT 'pending',
`error_message` text NOT NULL,
`execution_ms` int(11) NOT NULL DEFAULT '0',
`applied_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`migration_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8";
return (bool) sql_query($sql, false);
}
function g5_migration_save_record($migration, $status, $error_message, $execution_ms)
{
$table = g5_migration_table_name();
$id = sql_real_escape_string($migration['id']);
$description = sql_real_escape_string($migration['description']);
$checksum = sql_real_escape_string($migration['checksum']);
$status = sql_real_escape_string($status);
$error_message = sql_real_escape_string($error_message);
$execution_ms = (int) $execution_ms;
$sql = "INSERT INTO `{$table}`
SET migration_id = '{$id}', description = '{$description}', checksum = '{$checksum}',
status = '{$status}', error_message = '{$error_message}', execution_ms = '{$execution_ms}',
applied_at = '" . G5_TIME_YMDHIS . "'
ON DUPLICATE KEY UPDATE
description = VALUES(description), checksum = VALUES(checksum), status = VALUES(status),
error_message = VALUES(error_message), execution_ms = VALUES(execution_ms), applied_at = VALUES(applied_at)";
return (bool) sql_query($sql, false);
}
function g5_migration_run($target_id = '', $record_existing = false)
{
$result = array('success' => false, 'applied' => 0, 'skipped' => 0, 'errors' => array());
if (!g5_migration_ensure_table()) {
$result['errors'][] = '마이그레이션 이력 테이블을 생성하지 못했습니다: ' . sql_error_info();
return $result;
}
$lock_name = 'g5_migration_' . substr(md5(G5_TABLE_PREFIX), 0, 16);
$lock = sql_fetch("SELECT GET_LOCK('{$lock_name}', 10) AS acquired", false);
if (!$lock || (int) $lock['acquired'] !== 1) {
$result['errors'][] = '다른 DB 업그레이드가 실행 중이거나 실행 잠금을 얻지 못했습니다.';
return $result;
}
$migrations = g5_migration_discover();
$records = g5_migration_get_records();
$target_found = $target_id === '';
if ($record_existing) {
$history = sql_fetch('SELECT COUNT(*) AS total FROM `' . g5_migration_table_name() . '`', false);
if (!$history || !isset($history['total']) || (int) $history['total'] !== count($records)) {
$result['errors'][] = '기존 마이그레이션 이력을 확인하지 못했습니다.';
sql_query("SELECT RELEASE_LOCK('{$lock_name}')", false);
return $result;
}
}
if ($target_id !== '') {
$dependency_error = g5_migration_validate_target_dependencies($migrations, $records, $target_id);
if ($dependency_error !== '') {
$result['errors'][] = $dependency_error;
sql_query("SELECT RELEASE_LOCK('{$lock_name}')", false);
return $result;
}
}
foreach ($migrations as $migration) {
if (isset($migration['error'])) {
$result['errors'][] = $migration['error'];
break;
}
if ($target_id !== '' && $migration['id'] !== $target_id) {
continue;
}
$target_found = true;
$record = isset($records[$migration['id']]) ? $records[$migration['id']] : null;
if ($record && $record['status'] === 'success') {
if ($record['checksum'] !== $migration['checksum']) {
$result['errors'][] = $migration['id'] . ' 파일이 적용 후 변경되었습니다.';
break;
}
continue;
}
$started_at = get_microtime();
if ($record_existing) {
$inspection_cache = array();
// 앞선 변경이 후속 실행 조건을 바꿀 수 있으므로 연속된 항목만 등록한다.
if ($record || !g5_migration_needs_no_execution($migration, $inspection_cache)) {
break;
}
if (!g5_migration_save_record($migration, 'success', '', 0)) {
$result['errors'][] = $migration['id'] . ' 기존 상태 확인 이력을 저장하지 못했습니다: ' . sql_error_info();
break;
}
$result['skipped']++;
continue;
}
$skip = $migration['skip_table'] && $migration['skip_column'] && g5_migration_column_exists($migration['skip_table'], $migration['skip_column']);
$migration_error = '';
$executed = false;
if (!$skip) {
foreach ($migration['statements'] as $statement) {
if (!g5_migration_should_run_statement($statement['conditions'])) {
continue;
}
$statement_result = g5_migration_execute_statement($statement);
$executed = $executed || $statement_result['executed'];
if ($statement_result['error'] !== '') {
$migration_error = $statement_result['error'];
break;
}
}
}
$execution_ms = (int) round((get_microtime() - $started_at) * 1000);
if ($migration_error !== '') {
g5_migration_save_record($migration, 'failed', $migration_error, $execution_ms);
$result['errors'][] = $migration['id'] . ': ' . $migration_error;
break;
}
if (!g5_migration_save_record($migration, 'success', '', $execution_ms)) {
$result['errors'][] = $migration['id'] . ' 실행 이력을 저장하지 못했습니다: ' . sql_error_info();
break;
}
if ($skip || !$executed) {
$result['skipped']++;
} else {
$result['applied']++;
}
}
if (!$target_found) {
$result['errors'][] = '요청한 마이그레이션을 찾을 수 없습니다: ' . $target_id;
}
sql_query("SELECT RELEASE_LOCK('{$lock_name}')", false);
$result['success'] = !$result['errors'];
return $result;
}
+123
View File
@@ -0,0 +1,123 @@
<?php
if (!defined('_GNUBOARD_')) exit;
// 설정 키 => 표시명, PG 요청 코드, CSS 클래스, 기존 주문 결제수단 값(선택).
function shop_easypay_catalog($pg)
{
$catalog = array(
'kcp' => array(
'nhnkcp_payco' => array('PAYCO', 'payco', 'PAYCO'),
'nhnkcp_naverpay' => array('네이버페이', 'naverpay', 'naverpay_icon'),
'nhnkcp_kakaopay' => array('카카오페이', 'kakaopay', 'kakaopay_icon'),
'nhnkcp_applepay' => array('애플페이', 'applepay', 'applepay_icon'),
),
'inicis' => array(
'inicis_samsungpay' => array('삼성페이', 'samsungpay', 'samsungpay_icon', '삼성페이'),
'inicis_lpay' => array('L.pay', 'lpay', 'lpay_icon', 'lpay'),
'inicis_kakaopay' => array('카카오페이', 'inicis_kakaopay', 'kakaopay_icon', 'inicis_kakaopay'),
),
'toss' => array(
'toss_tosspay' => array('토스페이', 'TOSSPAY', 'tosspay_icon'),
'toss_naverpay' => array('네이버페이', 'NAVERPAY', 'naverpay_icon'),
'toss_samsungpay' => array('삼성페이', 'SAMSUNGPAY', 'samsungpay_icon'),
'toss_applepay' => array('애플페이', 'APPLEPAY', 'applepay_icon'),
'toss_lpay' => array('L.pay', 'LPAY', 'lpay_icon'),
'toss_kakaopay' => array('카카오페이', 'KAKAOPAY', 'kakaopay_icon'),
'toss_pinpay' => array('핀페이', 'PINPAY', 'pinpay_icon'),
'toss_payco' => array('PAYCO', 'PAYCO', 'PAYCO'),
'toss_ssgpay' => array('SSG페이', 'SSG', 'ssgpay_icon'),
),
'nicepay' => array(
'nicepay_samsungpay' => array('삼성페이', 'nice_samsungpay', 'samsungpay_icon'),
'nicepay_naverpay' => array('네이버페이', 'nice_naverpay', 'naverpay_icon'),
'nicepay_kakaopay' => array('카카오페이', 'nice_kakaopay', 'kakaopay_icon'),
'nicepay_applepay' => array('애플페이', 'nice_applepay', 'applepay_icon'),
'nicepay_paycopay' => array('PAYCO', 'nice_paycopay', 'PAYCO'),
'nicepay_skpay' => array('SK페이', 'nice_skpay', 'skpay_icon'),
'nicepay_ssgpay' => array('SSG페이', 'nice_ssgpay', 'ssgpay_icon'),
'nicepay_lpay' => array('L.pay', 'nice_lpay', 'lpay_icon'),
),
);
return isset($catalog[$pg]) ? $catalog[$pg] : array();
}
function shop_easypay_legacy_keys()
{
return array('inicis_samsungpay' => 'de_samsung_pay_use', 'inicis_lpay' => 'de_inicis_lpay_use', 'inicis_kakaopay' => 'de_inicis_kakaopay_use');
}
// 첫 설정 저장 전에는 기존 개별 설정과 토스 PAYCO 사용 상태를 승계한다.
function shop_easypay_normalize($settings)
{
$services = !empty($settings['de_easy_pay_services']) ? explode(',', $settings['de_easy_pay_services']) : array();
if (!in_array('inicis_configured', $services, true)) {
foreach (shop_easypay_legacy_keys() as $key => $legacy) {
if (!empty($settings[$legacy])) {
$services[] = $key;
if ($settings['de_pg_service'] === 'inicis') $settings['de_easy_pay_use'] = 1;
}
}
}
if (!in_array('toss_configured', $services, true) && $settings['de_pg_service'] === 'toss' && !empty($settings['de_easy_pay_use']) && !array_intersect(array_keys(shop_easypay_catalog('toss')), $services)) {
$services[] = 'toss_payco';
}
$settings['de_easy_pay_services'] = implode(',', array_unique($services));
foreach (shop_easypay_legacy_keys() as $key => $legacy) {
$settings[$legacy] = (int) (in_array($key, $services, true) && ($settings['de_pg_service'] !== 'inicis' || !empty($settings['de_easy_pay_use'])));
}
return $settings;
}
function shop_easypay_enabled($key)
{
global $default;
return !empty($default['de_easy_pay_use']) && in_array($key, explode(',', $default['de_easy_pay_services']), true);
}
function shop_easypay_available($key, $mobile)
{
global $default;
if (strpos($key, 'applepay') !== false) {
// KCP/NICEPAY와 동일하게 iOS 모바일 브라우저에만 제공한다.
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
return $mobile && preg_match('/iPhone|iPad|iPod/i', $ua);
}
// 삼성페이는 PC에서 휴대폰 인증으로 연결할 수 있다. 구 INIpay만 모바일 전용이다.
return $key !== 'inicis_samsungpay' || $mobile || !empty($default['de_inicis_pro_use']);
}
function shop_easypay_button($key, $provider, $mobile, $money = false)
{
$value = isset($provider[3]) ? $provider[3] : '간편결제';
$legacy_ids = array('inicis_samsungpay' => 'samsungpay', 'inicis_lpay' => 'inicislpay');
$id = 'od_settle_'.(isset($legacy_ids[$key]) ? $legacy_ids[$key] : $key);
$attrs = isset($provider[3]) ? ' data-case="'.$provider[1].'"' : '';
if ($money) $attrs .= ' data-money="1"';
$html = '<input type="radio" id="'.$id.'" name="od_settle_case" data-pay="'.$provider[1].'" value="'.$value.'"'.$attrs.'> <label for="'.$id.'" class="'.$provider[2].' '.$key.' lb_icon" title="'.$provider[0].'">'.$provider[0].'</label>';
return $mobile ? '<li>'.$html.'</li>' : $html;
}
function shop_easypay_buttons($mobile = false)
{
global $default;
$buttons = array();
$pg = $default['de_pg_service'];
foreach (shop_easypay_catalog($pg) as $key => $provider) {
if (!shop_easypay_enabled($key) || !shop_easypay_available($key, $mobile)) continue;
$buttons[$key] = shop_easypay_button($key, $provider, $mobile);
}
if ($pg === 'lg' && !empty($default['de_easy_pay_use'])) {
$buttons['paynow'] = shop_easypay_button('easy_pay', array('PAYNOW', '', 'PAYNOW'), $mobile);
}
if (is_use_easypay('global_nhnkcp')) {
$buttons['nhnkcp_naverpay'] = shop_easypay_button('nhnkcp_naverpay', array('네이버페이', 'naverpay', 'naverpay_icon'), $mobile);
}
if (isset($buttons['nhnkcp_naverpay'])) {
$services = explode(',', $default['de_easy_pay_services']);
if (in_array('used_nhnkcp_naverpay_point', $services, true)) {
$buttons['nhnkcp_naverpay'] = shop_easypay_button('nhnkcp_naverpay', array('네이버페이 카드결제', 'naverpay', 'naverpay_icon nhnkcp_icon nhnkcp_card'), $mobile);
$buttons['nhnkcp_naverpay_money'] = shop_easypay_button('nhnkcp_naverpay_money', array('네이버페이 머니/포인트', 'naverpay', 'naverpay_icon nhnkcp_icon nhnkcp_money'), $mobile, true);
}
}
return $buttons;
}
+25 -29
View File
@@ -2448,6 +2448,12 @@ function is_use_easypay($payname=''){
$de_easy_pay_service_array = (isset($default['de_easy_pay_services']) && $default['de_easy_pay_services']) ? explode(',', $default['de_easy_pay_services']) : array();
// 기본 PG에서 계약·사용 설정한 네이버페이를 우선한다.
$pg_easypay_catalog = shop_easypay_catalog($default['de_pg_service']);
if ($payname === 'global_nhnkcp' && isset($pg_easypay_catalog[$default['de_pg_service'].'_naverpay']) && shop_easypay_enabled($default['de_pg_service'].'_naverpay')) {
return false;
}
if($payname === 'global_nhnkcp' && $de_easy_pay_service_array && ('kcp' !== $default['de_pg_service'])){ // NHN_KCP 외 타PG 사용시
if( in_array('global_nhnkcp_naverpay', $de_easy_pay_service_array) && ($default['de_card_test'] || (!$default['de_card_test'] && $default['de_kcp_mid'] && $default['de_kcp_site_key']) ) ){
return true;
@@ -2494,17 +2500,33 @@ function exists_inicis_shop_order($oid, $pp=array(), $od_time='', $od_ip='')
return '';
}
// 상태 변경 UPDATE에서 ct_status를 대입하기 전에 사용한다.
// 완료 상태에서 다시 저장하면 최초 완료 시각을 유지하고, 완료 해제 시 초기화한다.
function get_cart_complete_time_sql($ct_status)
{
if ($ct_status != '완료') {
return 'NULL';
}
return "CASE WHEN ct_status = '완료' THEN ct_complete_time ELSE '".G5_TIME_YMDHIS."' END";
}
//------------------------------------------------------------------------------
// 주문포인트를 적립한다.
// 설정일이 지난 포인트 부여되지 않은 배송완료된 장바구니 자료에 포인트 부여
// 설정일이 0 이면 주문서 완료 설정 시점에서 포인트를 바로 부여합니다.
// 배송완료 시각(ct_complete_time)부터 설정일이 지난 미적립 상품에 포인트 부여
// 설정일이 0 이면 배송완료 처리 시 포인트를 바로 부여합니다.
//------------------------------------------------------------------------------
function save_order_point($ct_status="완료")
{
global $g5, $default;
// 완료 시각이 없는 기존 상품에만 종전의 장바구니 생성 시각 기준을 유지한다.
$beforedays = date("Y-m-d H:i:s", ( time() - (86400 * (int)$default['de_point_days']) ) ); // 86400초는 하루
$sql = " select * from {$g5['g5_shop_cart_table']} where ct_status = '$ct_status' and ct_point_use = '0' and ct_time <= '$beforedays' ";
$sql = " select * from {$g5['g5_shop_cart_table']}
where ct_status = '$ct_status'
and ct_point_use = '0'
and ((ct_complete_time is not null and ct_complete_time <= '$beforedays')
or (ct_complete_time is null and ct_time <= '$beforedays')) ";
$result = sql_query($sql);
for ($i=0; $row=sql_fetch_array($result); $i++) {
// 회원 ID 를 얻는다.
@@ -2814,18 +2836,6 @@ function add_order_post_log($msg='', $code='error'){
return;
}
if ( $code === 'error' ) {
$result = sql_query("describe `{$g5['g5_shop_post_log_table']}`");
while ($row = sql_fetch_array($result)){
if( $row['Field'] === 'ol_msg' && $row['Type'] === 'varchar(255)' ){
sql_query("ALTER TABLE `{$g5['g5_shop_post_log_table']}` MODIFY ol_msg TEXT NOT NULL;", false);
sql_query("ALTER TABLE `{$g5['g5_shop_post_log_table']}` DROP PRIMARY KEY;", false);
sql_query("ALTER TABLE `{$g5['g5_shop_post_log_table']}` ADD `log_id` int(11) NOT NULL AUTO_INCREMENT, ADD PRIMARY KEY (`log_id`);", false);
break;
}
}
}
$sql = "insert into `{$g5['g5_shop_post_log_table']}`
set oid = '$od_id',
mb_id = '{$member['mb_id']}',
@@ -2837,20 +2847,6 @@ function add_order_post_log($msg='', $code='error'){
if( $result = sql_query($sql, false) ){
sql_query(" delete from {$g5['g5_shop_post_log_table']} where ol_datetime < '".date('Y-m-d H:i:s', strtotime('-15 day', G5_SERVER_TIME))."' ", false);
} else {
if(!sql_query(" DESC {$g5['g5_shop_post_log_table']} ", false)) {
sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['g5_shop_post_log_table']}` (
`log_id` int(11) NOT NULL AUTO_INCREMENT,
`oid` bigint(20) unsigned NOT NULL,
`mb_id` varchar(255) NOT NULL DEFAULT '',
`post_data` text NOT NULL,
`ol_code` varchar(255) NOT NULL DEFAULT '',
`ol_msg` text NOT NULL,
`ol_datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`ol_ip` varchar(25) NOT NULL DEFAULT '',
PRIMARY KEY (`log_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ", false);
}
}
}
+1 -21
View File
@@ -177,26 +177,6 @@ function add_shop_mod_rewrite_rules($rules, $get_path_url, $base_path, $return_s
}
function add_shop_admin_dbupgrade($is_check){
global $g5;
// 내용 관리 짧은 주소
$sql = " SHOW COLUMNS FROM `{$g5['g5_shop_item_table']}` LIKE 'it_seo_title' ";
$row = sql_fetch($sql);
if( !$row ){
sql_query("ALTER TABLE `{$g5['g5_shop_item_table']}`
ADD `it_seo_title` varchar(200) NOT NULL DEFAULT '' AFTER `it_name`,
ADD INDEX `it_seo_title` (`it_seo_title`);
", false);
$is_check = true;
}
return $is_check;
}
function shop_exist_check_seo_title($seo_title, $type, $shop_item_table, $it_id){
$sql = "select it_seo_title FROM {$shop_item_table} WHERE it_seo_title = '".sql_real_escape_string($seo_title)."' AND it_id <> '$it_id' limit 1";
@@ -223,4 +203,4 @@ function shop_seo_title_update($it_id, $is_edit=false){
sql_query($sql);
}
}
}
}
+207
View File
@@ -0,0 +1,207 @@
<?php
if (!defined('_GNUBOARD_')) exit;
function g5_shop_install_prefix()
{
return G5_TABLE_PREFIX . 'shop_';
}
function g5_shop_install_table_names($prefix)
{
return array(
'banner', 'cart', 'category', 'coupon', 'coupon_log', 'coupon_zone', 'default',
'event', 'event_item', 'item', 'item_option', 'item_use', 'item_qa', 'item_relation',
'order', 'order_address', 'order_data', 'order_delete', 'personalpay', 'sendcost',
'wish', 'item_stocksms', 'order_post_log', 'inicis_log', 'order_cancel_log',
'inicis_pay', 'inicis_pay_event'
);
}
function g5_shop_install_existing_tables($prefix)
{
$tables = array();
$result = sql_query('SHOW TABLES', false);
if (!$result) {
return false;
}
while ($row = sql_fetch_array($result)) {
$table = reset($row);
if (is_string($table) && strpos($table, $prefix) === 0) {
$tables[] = $table;
}
}
return $tables;
}
function g5_shop_install_finish($lock_name, $success, $error)
{
sql_query("SELECT RELEASE_LOCK('" . sql_real_escape_string($lock_name) . "')", false);
return array('success' => $success, 'error' => $error);
}
function g5_shop_install_config_block($prefix)
{
$map = array(
'default', 'banner', 'cart', 'category', 'event', 'event_item', 'item',
'item_option', 'item_use', 'item_qa', 'item_relation', 'order', 'order_delete',
'wish', 'coupon', 'coupon_zone', 'coupon_log', 'sendcost', 'personalpay',
'order_address', 'item_stocksms', 'post_log' => 'order_post_log',
'order_data', 'inicis_log', 'inicis_pay', 'inicis_pay_event', 'order_cancel_log'
);
$block = "\n\ndefine('G5_USE_SHOP', true);\n\ndefine('G5_SHOP_TABLE_PREFIX', '" . addcslashes($prefix, "\\'") . "');\n\n";
foreach ($map as $key => $value) {
if (is_int($key)) {
$key = $value;
}
$block .= "\$g5['g5_shop_" . $key . "_table'] = G5_SHOP_TABLE_PREFIX.'" . $value . "';\n";
}
return $block;
}
function g5_shop_install_write_config($prefix, $file = '')
{
if ($file === '') {
$file = G5_DATA_PATH . '/' . G5_DBCONFIG_FILE;
}
$contents = @file_get_contents($file);
if ($contents === false || strpos($contents, "define('G5_USE_SHOP'") !== false) {
return 'DB 설정 파일을 읽을 수 없거나 쇼핑몰 설정이 이미 존재합니다.';
}
$closing = strrpos($contents, '?>');
$updated = $closing === false
? $contents . g5_shop_install_config_block($prefix)
: substr($contents, 0, $closing) . g5_shop_install_config_block($prefix) . "?>" . substr($contents, $closing + 2);
$temp = @tempnam(dirname($file), 'shop-config-');
if (!$temp) {
return 'DB 설정 임시 파일을 만들 수 없습니다.';
}
$written = @file_put_contents($temp, $updated, LOCK_EX);
if ($written === false || $written !== strlen($updated)) {
@unlink($temp);
return 'DB 설정 임시 파일을 저장하지 못했습니다.';
}
@chmod($temp, G5_FILE_PERMISSION);
if (!@rename($temp, $file)) {
@unlink($temp);
return 'DB 설정 파일을 교체하지 못했습니다.';
}
return '';
}
function g5_shop_install_default($table)
{
$sql = "INSERT INTO `{$table}` SET
de_admin_company_name = '회사명', de_admin_company_saupja_no = '123-45-67890',
de_admin_company_owner = '대표자명', de_admin_company_tel = '02-123-4567',
de_admin_company_fax = '02-123-4568', de_admin_tongsin_no = '제 OO구 - 123호',
de_admin_buga_no = '12345호', de_admin_company_zip = '123-456',
de_admin_company_addr = 'OO도 OO시 OO구 OO동 123-45', de_admin_info_name = '정보책임자명',
de_admin_info_email = '정보책임자 E-mail', de_shop_skin = 'basic', de_shop_mobile_skin = 'basic',
de_type1_list_use = '1', de_type1_list_skin = 'main.10.skin.php', de_type1_list_mod = '5', de_type1_list_row = '1', de_type1_img_width = '160', de_type1_img_height = '160',
de_type2_list_use = '1', de_type2_list_skin = 'main.20.skin.php', de_type2_list_mod = '4', de_type2_list_row = '1', de_type2_img_width = '215', de_type2_img_height = '215',
de_type3_list_use = '1', de_type3_list_skin = 'main.40.skin.php', de_type3_list_mod = '4', de_type3_list_row = '1', de_type3_img_width = '215', de_type3_img_height = '215',
de_type4_list_use = '1', de_type4_list_skin = 'main.50.skin.php', de_type4_list_mod = '5', de_type4_list_row = '1', de_type4_img_width = '215', de_type4_img_height = '215',
de_type5_list_use = '1', de_type5_list_skin = 'main.30.skin.php', de_type5_list_mod = '4', de_type5_list_row = '1', de_type5_img_width = '215', de_type5_img_height = '215',
de_mobile_type1_list_use = '1', de_mobile_type1_list_skin = 'main.30.skin.php', de_mobile_type1_list_mod = '2', de_mobile_type1_list_row = '4', de_mobile_type1_img_width = '230', de_mobile_type1_img_height = '230',
de_mobile_type2_list_use = '1', de_mobile_type2_list_skin = 'main.10.skin.php', de_mobile_type2_list_mod = '2', de_mobile_type2_list_row = '2', de_mobile_type2_img_width = '230', de_mobile_type2_img_height = '230',
de_mobile_type3_list_use = '1', de_mobile_type3_list_skin = 'main.10.skin.php', de_mobile_type3_list_mod = '2', de_mobile_type3_list_row = '4', de_mobile_type3_img_width = '300', de_mobile_type3_img_height = '300',
de_mobile_type4_list_use = '1', de_mobile_type4_list_skin = 'main.20.skin.php', de_mobile_type4_list_mod = '2', de_mobile_type4_list_row = '2', de_mobile_type4_img_width = '80', de_mobile_type4_img_height = '80',
de_mobile_type5_list_use = '1', de_mobile_type5_list_skin = 'main.10.skin.php', de_mobile_type5_list_mod = '2', de_mobile_type5_list_row = '2', de_mobile_type5_img_width = '230', de_mobile_type5_img_height = '230',
de_bank_use = '1', de_bank_account = 'OO은행 12345-67-89012 예금주명', de_vbank_use = '0', de_iche_use = '0', de_card_use = '0',
de_settle_min_point = '5000', de_settle_max_point = '50000', de_settle_point_unit = '100',
de_cart_keep_term = '15', de_card_point = '0', de_point_days = '7', de_pg_service = 'kcp', de_kcp_mid = '',
de_send_cost_case = '차등', de_send_cost_limit = '20000;30000;40000', de_send_cost_list = '4000;3000;2000',
de_hope_date_use = '0', de_hope_date_after = '3', de_baesong_content = '배송 안내 입력전입니다.', de_change_content = '교환/반품 안내 입력전입니다.',
de_rel_list_use = '1', de_rel_list_skin = 'relation.10.skin.php', de_rel_list_mod = '5', de_rel_img_width = '215', de_rel_img_height = '215',
de_mobile_rel_list_use = '1', de_mobile_rel_list_skin = 'relation.10.skin.php', de_mobile_rel_list_mod = '3', de_mobile_rel_img_width = '230', de_mobile_rel_img_height = '230',
de_search_list_skin = 'list.10.skin.php', de_search_img_width = '225', de_search_img_height = '225', de_search_list_mod = '5', de_search_list_row = '5',
de_mobile_search_list_skin = 'list.10.skin.php', de_mobile_search_img_width = '230', de_mobile_search_img_height = '230', de_mobile_search_list_mod = '2', de_mobile_search_list_row = '5',
de_listtype_list_skin = 'list.10.skin.php', de_listtype_img_width = '225', de_listtype_img_height = '225', de_listtype_list_mod = '5', de_listtype_list_row = '5',
de_mobile_listtype_list_skin = 'list.10.skin.php', de_mobile_listtype_img_width = '230', de_mobile_listtype_img_height = '230', de_mobile_listtype_list_mod = '2', de_mobile_listtype_list_row = '5',
de_simg_width = '230', de_simg_height = '230', de_mimg_width = '300', de_mimg_height = '300',
de_item_use_use = '1', de_level_sell = '1', de_code_dup_use = '1', de_card_test = '1',
de_sms_cont1 = '{이름}님의 회원가입을 축하드립니다.\nID:{회원아이디}\n{회사명}',
de_sms_cont2 = '{이름}님 주문해주셔서 고맙습니다.\n{주문번호}\n{주문금액}원\n{회사명}',
de_sms_cont3 = '{이름}님께서 주문하셨습니다.\n{주문번호}\n{주문금액}원\n{회사명}',
de_sms_cont4 = '{이름}님 입금 감사합니다.\n{입금액}원\n주문번호:\n{주문번호}\n{회사명}',
de_sms_cont5 = '{이름}님 배송합니다.\n택배:{택배회사}\n운송장번호:\n{운송장번호}\n{회사명}'";
return sql_query($sql, false) ? '' : sql_error_info();
}
function g5_shop_install_run($options = array())
{
$allow_enabled = isset($options['allow_enabled']) && $options['allow_enabled'];
if (!$allow_enabled && defined('G5_USE_SHOP') && G5_USE_SHOP) {
return array('success' => false, 'error' => '쇼핑몰이 이미 설치되어 있습니다.');
}
$prefix = isset($options['prefix']) && $options['prefix'] !== '' ? $options['prefix'] : g5_shop_install_prefix();
$data_path = isset($options['data_path']) && $options['data_path'] !== '' ? $options['data_path'] : G5_DATA_PATH;
$config_file = isset($options['config_file']) ? $options['config_file'] : '';
$lock_name = 'g5_shop_install_' . substr(md5(G5_TABLE_PREFIX), 0, 16);
$lock = sql_fetch("SELECT GET_LOCK('{$lock_name}', 10) AS acquired", false);
if (!$lock || (int) $lock['acquired'] !== 1) {
return array('success' => false, 'error' => '다른 쇼핑몰 설치 작업이 실행 중이거나 실행 잠금을 얻지 못했습니다.');
}
$existing = g5_shop_install_existing_tables($prefix);
if ($existing === false) {
return g5_shop_install_finish($lock_name, false, '쇼핑몰 테이블 존재 여부를 확인하지 못했습니다.');
}
if ($existing) {
return g5_shop_install_finish($lock_name, false, '같은 접두어의 쇼핑몰 테이블이 이미 존재합니다: ' . implode(', ', $existing));
}
include_once G5_PATH . '/install/install.function.php';
$queries = install_load_sql_file(G5_PATH . '/install/gnuboard5shop.sql', 'g5_shop_', $prefix);
if ($queries === false) {
return g5_shop_install_finish($lock_name, false, '쇼핑몰 설치 SQL을 읽을 수 없습니다.');
}
$created = array();
foreach ($queries as $sql) {
$sql = get_db_create_replace($sql);
if (!sql_query($sql, false)) {
$error = sql_error_info();
foreach (array_reverse($created) as $table) {
sql_query("DROP TABLE IF EXISTS `" . str_replace('`', '``', $table) . "`", false);
}
return g5_shop_install_finish($lock_name, false, '쇼핑몰 테이블 생성에 실패했습니다: ' . $error);
}
if (preg_match('/^CREATE TABLE(?: IF NOT EXISTS)?\s+`([^`]+)`/i', trim($sql), $matches)) {
$created[] = $matches[1];
}
}
$error = g5_shop_install_default($prefix . 'default');
if ($error === '') {
$dirs = array('banner', 'common', 'event', 'item');
foreach ($dirs as $dir) {
if (!install_ensure_dir($data_path . '/' . $dir)) {
$error = '쇼핑몰 데이터 디렉터리를 만들 수 없습니다: data/' . $dir;
break;
}
}
}
if ($error === '') {
$copies = array(
array(G5_PATH . '/install/logo_img', $data_path . '/common/logo_img'),
array(G5_PATH . '/install/logo_img', $data_path . '/common/logo_img2'),
array(G5_PATH . '/install/mobile_logo_img', $data_path . '/common/mobile_logo_img'),
array(G5_PATH . '/install/mobile_logo_img', $data_path . '/common/mobile_logo_img2')
);
foreach ($copies as $copy) {
if (!file_exists($copy[1]) && !@copy($copy[0], $copy[1])) {
$error = '쇼핑몰 기본 이미지를 복사할 수 없습니다: ' . $copy[1];
break;
}
}
}
if ($error === '') {
$error = g5_shop_install_write_config($prefix, $config_file);
}
if ($error !== '') {
foreach (array_reverse($created) as $table) {
sql_query("DROP TABLE IF EXISTS `" . str_replace('`', '``', $table) . "`", false);
}
return g5_shop_install_finish($lock_name, false, $error);
}
return g5_shop_install_finish($lock_name, true, '');
}
@@ -0,0 +1,17 @@
-- @description 장바구니 상품 배송비 정보 필드 추가 (3889e0235)
-- @if-table-exists {{g5_shop_cart_table}}
-- @if-column-missing {{g5_shop_cart_table}} it_sc_type
ALTER TABLE `{{g5_shop_cart_table}}`
ADD `it_sc_type` tinyint(4) NOT NULL DEFAULT '0' AFTER `it_name`;
-- @if-table-exists {{g5_shop_cart_table}}
-- @if-column-missing {{g5_shop_cart_table}} it_sc_method
ALTER TABLE `{{g5_shop_cart_table}}` ADD `it_sc_method` tinyint(4) NOT NULL DEFAULT '0' AFTER `it_sc_type`;
-- @if-table-exists {{g5_shop_cart_table}}
-- @if-column-missing {{g5_shop_cart_table}} it_sc_price
ALTER TABLE `{{g5_shop_cart_table}}` ADD `it_sc_price` int(11) NOT NULL DEFAULT '0' AFTER `it_sc_method`;
-- @if-table-exists {{g5_shop_cart_table}}
-- @if-column-missing {{g5_shop_cart_table}} it_sc_minimum
ALTER TABLE `{{g5_shop_cart_table}}` ADD `it_sc_minimum` int(11) NOT NULL DEFAULT '0' AFTER `it_sc_price`;
-- @if-table-exists {{g5_shop_cart_table}}
-- @if-column-missing {{g5_shop_cart_table}} it_sc_qty
ALTER TABLE `{{g5_shop_cart_table}}` ADD `it_sc_qty` int(11) NOT NULL DEFAULT '0' AFTER `it_sc_minimum`;
@@ -0,0 +1,4 @@
-- @description 장바구니 주문폼 선택 시각 필드 추가 (de2b73436)
-- @if-table-exists {{g5_shop_cart_table}}
-- @if-column-missing {{g5_shop_cart_table}} ct_select_time
ALTER TABLE `{{g5_shop_cart_table}}` ADD `ct_select_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `ct_select`;
@@ -0,0 +1,7 @@
-- @description 장바구니 상품·상태 조회 인덱스 추가 (344b1983a)
-- @if-table-exists {{g5_shop_cart_table}}
-- @if-index-missing {{g5_shop_cart_table}} it_id
ALTER TABLE `{{g5_shop_cart_table}}` ADD INDEX `it_id` (`it_id`);
-- @if-table-exists {{g5_shop_cart_table}}
-- @if-index-missing {{g5_shop_cart_table}} ct_status
ALTER TABLE `{{g5_shop_cart_table}}` ADD INDEX `ct_status` (`ct_status`);
@@ -0,0 +1,18 @@
-- @description 모바일 이니시스 및 결제 임시저장 테이블 추가 (a7cfae126)
-- @if-table-exists {{g5_shop_cart_table}}
-- @if-table-missing {{g5_shop_inicis_log_table}}
CREATE TABLE `{{g5_shop_inicis_log_table}}` (
`oid` bigint(20) unsigned NOT NULL, `P_TID` varchar(255) NOT NULL DEFAULT '',
`P_MID` varchar(255) NOT NULL DEFAULT '', `P_AUTH_DT` varchar(255) NOT NULL DEFAULT '',
`P_STATUS` varchar(255) NOT NULL DEFAULT '', `P_TYPE` varchar(255) NOT NULL DEFAULT '',
`P_OID` varchar(255) NOT NULL DEFAULT '', `P_FN_NM` varchar(255) NOT NULL DEFAULT '',
`P_AMT` int(11) NOT NULL DEFAULT '0', `P_RMESG1` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`oid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- @if-table-exists {{g5_shop_cart_table}}
-- @if-table-missing {{g5_shop_order_data_table}}
CREATE TABLE `{{g5_shop_order_data_table}}` (
`od_id` bigint(20) unsigned NOT NULL, `dt_pg` varchar(255) NOT NULL DEFAULT '',
`dt_data` text NOT NULL, `dt_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
KEY `od_id` (`od_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
@@ -0,0 +1,4 @@
-- @description 모바일 이니시스 승인번호 필드 추가 (6495aa007)
-- @if-table-exists {{g5_shop_inicis_log_table}}
-- @if-column-missing {{g5_shop_inicis_log_table}} P_AUTH_NO
ALTER TABLE `{{g5_shop_inicis_log_table}}` ADD `P_AUTH_NO` varchar(255) NOT NULL DEFAULT '' AFTER `P_FN_NM`;
@@ -0,0 +1,4 @@
-- @description 테스트 주문 구분 필드 추가 (4c9ac8b6b)
-- @if-table-exists {{g5_shop_order_table}}
-- @if-column-missing {{g5_shop_order_table}} od_test
ALTER TABLE `{{g5_shop_order_table}}` ADD `od_test` tinyint(4) NOT NULL DEFAULT '0' AFTER `od_settle_case`;
@@ -0,0 +1,8 @@
-- @description 미완료 주문의 장바구니·회원 필드 추가 (f1cd96dc3)
-- @if-table-exists {{g5_shop_order_data_table}}
-- @if-column-missing {{g5_shop_order_data_table}} cart_id
ALTER TABLE `{{g5_shop_order_data_table}}`
ADD `cart_id` bigint(20) unsigned NOT NULL AFTER `od_id`;
-- @if-table-exists {{g5_shop_order_data_table}}
-- @if-column-missing {{g5_shop_order_data_table}} mb_id
ALTER TABLE `{{g5_shop_order_data_table}}` ADD `mb_id` varchar(20) NOT NULL DEFAULT '' AFTER `cart_id`;

Some files were not shown because too many files have changed in this diff Show More