Merge remote-tracking branch '그누보드/master'

# Conflicts:
#	adm/shop_admin/orderform.php
#	bbs/register_form_update.php
#	lib/common.lib.php
#	mobile/shop/personalpayform.sub.php
#	plugin/htmlpurifier/extend.video.php
#	plugin/kcaptcha/kcaptcha.lib.php
#	shop/orderformupdate.php
#	skin/social/social_register_member.skin.php
#	version.php
This commit is contained in:
2025-11-25 16:06:26 +09:00
119 changed files with 7445 additions and 368 deletions

View File

@ -1,11 +1,115 @@
function check_all(f)
{
var chk = document.getElementsByName("chk[]");
/** 공통 UI 모듈 */
window.CommonUI = {
bindTabs(tabSelector, contentSelector, options = {}) {
const tabs = document.querySelectorAll(tabSelector);
const contents = document.querySelectorAll(contentSelector);
for (i=0; i<chk.length; i++)
chk[i].checked = f.chkall.checked;
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const tabName = tab.dataset.tab;
const target = document.getElementById(`tab-${tabName}`);
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
contents.forEach(c => c.classList.add('is-hidden'));
if (target) target.classList.remove('is-hidden');
options.onChange?.(tabName, target);
});
});
}
};
function setHtml(el, markup) {
if (!el) return;
if (markup == null || markup === '') {
el.textContent = '';
return;
}
const range = document.createRange();
range.selectNodeContents(el);
el.replaceChildren(range.createContextualFragment(markup));
}
/** 팝업 관리 모듈 */
window.PopupManager = {
open(id, options = {}) {
const el = document.getElementById(id);
if (el) {
el.classList.remove('is-hidden');
this.bindOutsideClickClose(id);
if (!options.disableOutsideClose) {
this.bindOutsideClickClose(id);
} else {
this.unbindOutsideClickClose(id);
}
}
},
close(id) {
const el = document.getElementById(id);
if (el) el.classList.add('is-hidden');
},
toggle(id) {
const el = document.getElementById(id);
if (el) el.classList.toggle('is-hidden');
},
bindOutsideClickClose(id) {
const el = document.getElementById(id);
if (!el) return;
el.onclick = () => this.close(id);
},
unbindOutsideClickClose(id) {
const el = document.getElementById(id);
if (!el) return;
el.onclick = null;
},
/**
* 팝업 콘텐츠 렌더링 (타이틀, 바디, 푸터 구성)
* @param {string} title - 팝업 제목
* @param {string} body - 팝업 본문 HTML
* @param {string} [footer] - 푸터 HTML
* @param {object} [options] - 팝업 열기 옵션
*/
render(title, body, footer = '', options = {}) {
const titleEl = document.getElementById('popupTitle');
const bodyEl = document.getElementById('popupBody');
const footerEl = document.getElementById('popupFooter');
if (titleEl) titleEl.textContent = title;
if (bodyEl) setHtml(bodyEl, body);
if (footerEl) setHtml(footerEl, footer);
this.open('popupOverlay', options);
}
};
/** 형식 체크 */
function check_all(target) {
const chkboxes = document.getElementsByName("chk[]");
let chkall;
if (target && target.tagName === "FORM") {
chkall = target.querySelector('input[name="chkall"]');
} else if (target && target.type === "checkbox") {
chkall = target;
}
if (!chkall) return;
for (const checkbox of chkboxes) {
checkbox.checked = chkall.checked;
}
}
function btn_check(f, act)
{
if (act == "update") // 선택수정

View File

@ -11,6 +11,7 @@ $menu['menu100'] = array(
array('100900', '캐시파일 일괄삭제', G5_ADMIN_URL . '/cache_file_delete.php', 'cf_cache', 1),
array('100910', '캡챠파일 일괄삭제', G5_ADMIN_URL . '/captcha_file_delete.php', 'cf_captcha', 1),
array('100920', '썸네일파일 일괄삭제', G5_ADMIN_URL . '/thumbnail_file_delete.php', 'cf_thumbnail', 1),
array('100930', '회원관리파일 일괄삭제', G5_ADMIN_URL . '/member_list_file_delete.php', 'cf_memberlist', 1),
array('100500', 'phpinfo()', G5_ADMIN_URL . '/phpinfo.php', 'cf_phpinfo')
);

View File

@ -2,6 +2,7 @@
$menu['menu200'] = array(
array('200000', '회원관리', G5_ADMIN_URL . '/member_list.php', 'member'),
array('200100', '회원관리', G5_ADMIN_URL . '/member_list.php', 'mb_list'),
array('200400', '회원관리파일', G5_ADMIN_URL . '/member_list_exel.php', 'mb_list'),
array('200300', '회원메일발송', G5_ADMIN_URL . '/mail_list.php', 'mb_mail'),
array('200800', '접속자집계', G5_ADMIN_URL . '/visit_list.php', 'mb_visit', 1),
array('200810', '접속자검색', G5_ADMIN_URL . '/visit_search.php', 'mb_search', 1),

View File

@ -14,7 +14,6 @@ $print_version = ($is_admin == 'super') ? 'Version ' . G5_GNUBOARD_VER : '';
<strong>자바스크립트를 사용하지 않음</strong>으로 설정하신 경우는 수정이나 삭제시 별도의 경고창이 나오지 않으므로 이점 주의하시기 바랍니다.
</p>
</noscript>
</div>
<footer id="ft">
<p>
@ -22,8 +21,25 @@ $print_version = ($is_admin == 'super') ? 'Version ' . G5_GNUBOARD_VER : '';
<button type="button" class="scroll_top"><span class="top_img"></span><span class="top_txt">TOP</span></button>
</p>
</footer>
</div>
<!-- 공통 레이어 팝업 컨테이너 -->
<div id="adminPopupContainer">
<div id="popupOverlay" class="popup-overlay is-hidden" onclick="PopupManager.close('popupOverlay')">
<div class="popup-content" onclick="event.stopPropagation()">
<div class="popup-header">
<strong id="popupTitle" class="popup-title"></strong>
<button type="button" class="popup-close-btn" onclick="PopupManager.close('popupOverlay')">
<i class="fa fa-close"></i><span class="sound_only">팝업 닫기</span>
</button>
</div>
<div class="popup-body" id="popupBody">
<!-- 동적으로 내용 주입 -->
</div>
<div class="popup-footer" id="popupFooter">
<!-- 버튼 등 동적으로 -->
</div>
</div>
</div>
</div>
<script>

View File

@ -13,7 +13,7 @@ if ($is_admin != 'super') {
}
$mb = get_member($mb_id);
if (!$mb['mb_id']) {
if (!(isset($mb['mb_id']) && $mb['mb_id'])) {
alert('존재하는 회원아이디가 아닙니다.');
}

View File

@ -1439,7 +1439,7 @@ function frm_check_file(){
return false;
} else {
jQuery("#admin_captcha_box").hide();
// jQuery("#admin_captcha_box").hide();
}
return true;
@ -1447,12 +1447,12 @@ function frm_check_file(){
jQuery(function($){
if( window.self !== window.top ){ // frame 또는 iframe을 사용할 경우 체크
$("#bo_include_head, #bo_include_tail").on("change paste keyup", function(e) {
frm_check_file();
});
use_captcha_check();
}
$("#bo_include_head, #bo_include_tail").on("change paste keyup", function(e) {
frm_check_file();
});
});
function fboardform_submit(f)
@ -1487,10 +1487,14 @@ function fboardform_submit(f)
return false;
}
if (frm_check_file() == false) {
jQuery(window).scrollTop($('#bo_include_tail').offset().top - 30);
}
if( captcha_chk ) {
<?php echo isset($captcha_js) ? $captcha_js : ''; // 캡챠 사용시 자바스크립트에서 입력된 캡챠를 검사함 ?>
}
return true;
}
</script>

View File

@ -36,10 +36,24 @@ if ($w == '' && in_array($bo_table, get_bo_table_banned_word())) {
$bo_include_head = isset($_POST['bo_include_head']) ? preg_replace(array("#[\\\]+$#", "#(<\?php|<\?)#i"), "", substr($_POST['bo_include_head'], 0, 255)) : '';
$bo_include_tail = isset($_POST['bo_include_tail']) ? preg_replace(array("#[\\\]+$#", "#(<\?php|<\?)#i"), "", substr($_POST['bo_include_tail'], 0, 255)) : '';
// 관리자가 자동등록방지를 사용해야 할 경우
if ($board && (isset($board['bo_include_head']) && $board['bo_include_head'] !== $bo_include_head || $board['bo_include_tail'] !== $bo_include_tail) && function_exists('get_admin_captcha_by') && get_admin_captcha_by()) {
include_once(G5_CAPTCHA_PATH . '/captcha.lib.php');
$check_captcha = false;
// 관리자가 자동등록방지 CAPTCHA를 사용해야 할 경우
if ($w === 'u') {
if (isset($board['bo_include_head'], $board['bo_include_tail']) &&
($board['bo_include_head'] !== $bo_include_head || $board['bo_include_tail'] !== $bo_include_tail)) {
$check_captcha = true;
}
} elseif ($w === '') {
if ($bo_include_head !== '_head.php' || $bo_include_tail !== '_tail.php') {
$check_captcha = true;
}
}
// 실제 CAPTCHA 검증
if ($check_captcha) {
include_once(G5_CAPTCHA_PATH . '/captcha.lib.php');
if (!chk_captcha()) {
alert('자동등록방지 숫자가 틀렸습니다.');
}

View File

@ -422,6 +422,30 @@ if (!isset($config['cf_cert_kcp_enckey'])) {
$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";
}
@ -963,6 +987,17 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
<th scope="row"><label for="cf_privacy">개인정보처리방침</label></th>
<td colspan="3"><textarea id="cf_privacy" name="cf_privacy" rows="10"><?php echo html_purifier($config['cf_privacy']); ?></textarea></td>
</tr>
<tr>
<th scope="row"><label for="cf_use_promotion">회원가입 약관 동의에<br>광고성 정보 수신 동의 표시 여부</label></th>
<td colspan="3">
<?php echo help('<b>광고성 정보 수신 · 마케팅 목적의 개인정보 수집 및 이용 · 개인정보 제 3자 제공</b> 여부를 설정합니다. <b>SMS 또는 카카오톡</b> 사용 시 <b>개인정보 제3자 제공</b>이 활성화됩니다.'); ?>
<?php echo help('동의한 회원에게 <b>카카오톡(친구톡)·문자</b>로 광고성 메시지를 발송할 수 있습니다.'); ?>
<?php echo help('<b>휴대전화번호</b> 사용을 위해서는 <b>기본환경설정 > 회원가입 > 휴대전화번호 입력</b>을 <b>[보이기]</b> 또는 <b>[필수입력]</b>으로 설정해야 하며, 미설정 시 수집이 불가합니다.'); ?>
<?php echo help('* 「정보통신망이용촉진및정보보호등에관한법률」에 따라 <b>광고성 정보 수신 동의</b>를 매 2년마다 반드시 확인해야 합니다.'); ?>
<input type="checkbox" name="cf_use_promotion" value="1" id="cf_use_promotion" <?php echo $config['cf_use_promotion'] ? 'checked' : ''; ?>>
<label for="cf_use_promotion">사용</label>
</td>
</tr>
</tbody>
</table>
</div>
@ -1138,6 +1173,7 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
<input type="checkbox" name="cf_formmail_is_member" value="1" id="cf_formmail_is_member" <?php echo $config['cf_formmail_is_member'] ? 'checked' : ''; ?>> 회원만 사용
</td>
</tr>
</tbody>
</table>
</div>
</section>
@ -1526,7 +1562,6 @@ if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
</div>
</section>
<section id="anc_cf_extra">
<h2 class="h2_frm">여분필드 기본 설정</h2>
<?php echo $pg_anchor ?>

View File

@ -141,6 +141,7 @@ $check_keys = array(
'cf_visit' => 'char',
'cf_stipulation' => 'text',
'cf_privacy' => 'text',
'cf_use_promotion' => 'int',
'cf_open_modify' => 'int',
'cf_memo_send_point' => 'int',
'cf_mobile_new_skin' => 'char',
@ -300,6 +301,7 @@ $sql = " update {$g5['config_table']}
cf_mobile_page_rows = '{$_POST['cf_mobile_page_rows']}',
cf_stipulation = '{$_POST['cf_stipulation']}',
cf_privacy = '{$_POST['cf_privacy']}',
cf_use_promotion = '{$_POST['cf_use_promotion']}',
cf_open_modify = '{$_POST['cf_open_modify']}',
cf_memo_send_point = '{$_POST['cf_memo_send_point']}',
cf_mobile_new_skin = '{$_POST['cf_mobile_new_skin']}',

View File

@ -28,6 +28,39 @@ box-sizing: border-box;
h2{font-size: 1.083em;font-weight: bold;margin:10px 0}
#wrapper {min-height:480px}
/* admin 공통 */
/* 공통 - display none/block */
.is-hidden { display: none !important; }
.is-visible { display: block !important; }
/* 공통 - 뷰포트 (pc / mobile) 별 display none/block */
.pc-only { display: none; }
@media (min-width: 769px) { .pc-only { display: block !important; }}
.mobile-only { display: block; }
@media (min-width: 769px) { .mobile-only { display: none !important; }}
/* 공통 - 레이어 팝업 */
.popup-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.3); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); z-index: 9999; display: flex; justify-content: center; align-items: center; }
.popup-content { background: #fff; border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.15); width: 800px; overflow: hidden; }
.popup-header, .popup-footer { padding: 18px 20px; display: flex; align-items: center; }
.popup-header { justify-content: space-between; border-bottom: 1px solid #e0e0e0; }
.popup-footer { gap: 20px; border-top: 1px solid #e0e0e0;}
.popup-close-btn { background: none; border: none; color: #888; font-size: 20px; cursor: pointer; padding: 4px; display: flex; align-items: center; justify-content: center; transition: color 0.2s ease; }
.popup-close-btn:hover { color: #333; }
.popup-title { font-size: 18px; font-weight: 600; }
.popup-body { padding: 20px; max-height: 400px; overflow-y: auto; color: #333; }
.popup-footer button { background: #3d70ff; color: white; border: 1px solid #3d70ff; padding: 8px 16px; border-radius: 6px; font-weight: 600; cursor: pointer; transition: background 0.2s ease, border-color 0.2s ease; }
.popup-footer button:hover { background: #2b3d9f; border-color: #2b3d9f; }
/* 공통 - tab */
.tab-container { display: flex; flex-direction: column; width: 100%; }
.tab-header { position: relative; bottom: -1px; display: flex; }
.tab-btn { padding: 10px 14px; background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer; color: inherit; font: inherit; }
.tab-btn.active { border-bottom-color: #000; font-weight: bold; }
.tab-body { width: 100%; border-top: 1px solid #ccc; }
.tab-content { padding: 16px 0; }
/* 레이아웃 */
#hd h1 {position:absolute;font-size:0;line-height:0;overflow:hidden}
#hd_top{position:fixed;top:0;left:0;width:100%;height:50px;background:#3f51b5;z-index:1000}
@ -95,9 +128,11 @@ box-shadow: 2px 0 2px rgba(150,150,150,0.1);}
#container.container-small #container_title{padding-left:70px}
.container_wr{padding:20px}
/* 화면낭독기 사용자용 */
/* 화면낭독기 사용자용 (스크린 리더 대응) */
/* 일반적인 .blind/.sr-only 사용시에 .sound_only 사용 권장 */
#hd_login_msg {position:absolute;top:0;left:0;width:1px;height:1px;overflow:hidden}
.msg_sound_only, .sound_only {display:inline-block !important;position:absolute;top:0;left:0;margin:0 !important;padding:0 !important;width:1px !important;height:1px !important;font-size:0;line-height:0;border:0 !important;overflow:hidden !important}
.sound_only, .msg_sound_only {overflow:hidden;position:absolute;width:1px;height:1px;margin:-1px;padding:0;clip:rect(0,0,0,0)}
/* 본문 바로가기 */
#to_content a {z-index:100000;position:absolute;top:0;left:0;font-size:0;line-height:0;overflow:hidden}
#to_content a:focus, #to_content a:active {width:100%;height:70px;background:#fff;font-size:2em;font-weight:bold;text-align:center;text-decoration:none;line-height:3.1em}
@ -247,18 +282,14 @@ legend {position:absolute;width:0;height:0;font-size:0;line-height:0;text-indent
.anchor a {display:inline-block;padding:5px 10px;border:1px solid #c8ced1;background:#d6dde1;text-decoration:none}
.anchor .selected{background:#3f51b5}
#sort_mb {width:800px}
#sort_sodr {width:600px}
/* 하단 레이아웃 */
#ft{background:#f3f3f3;padding:0 25px;color:#777;text-align:center}
#ft p{line-height:50px;}
.scroll_top{position:fixed;bottom:10px;right:10px;width:50px;height:50px;border:0;text-align:center;background:#ddd;background:rgba(0,0,0,0.1)}
.scroll_top{position:fixed;bottom:10px;right:10px;width:50px;height:50px;border:0;text-align:center;background:#ddd;background:rgba(0,0,0,0.1);z-index:50;}
.scroll_top span.top_img{display:inline-block;width: 0; height: 0; border-left: 5px solid transparent;border-right: 5px solid transparent;
border-bottom: 5px solid black;}
.scroll_top span.top_txt{display:block}
@ -281,9 +312,59 @@ border-bottom: 5px solid black;}
.local_sch03 button{height:30px;padding:0 5px;border:0;background:#9eacc6;color:#fff;}
.local_sch03 .btn_submit{height:30px;padding:0 5px;border:0;color:#fff;}
.local_sch03 .frm_input{height:30px;border:1px solid #dcdcdc;padding:0 5px;}
/* 회원 관리 데이터 필터링 */
.member_list_data { display: flex; flex-direction: column; padding: 20px; margin: 20px 0 40px; background: #f9f9f9; border: 1px solid #f2f2f2; color: #333; }
.sch_table { display: flex; flex-direction: column; gap: 10px; font-size: 11.5px; color: #333; }
.member_list_data .sch_row { display: flex; align-items: center; gap: 12px; min-height: 30px; }
.label { min-width: 120px; font-weight: 500; white-space: nowrap; display: flex; align-items: center; }
.label label {display: flex; gap: 10px;}
.field { flex: 1; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.field input[type="text"], .field input[type="number"], .field input[type="date"], .field select { height: 30px; min-width: 100px; padding: 0 10px; font-size: 11.5px; border: 1px solid #ddd; border-radius: 8px; background: #fff; transition: border-color 0.2s ease, box-shadow 0.2s ease; }
.field input[type="text"]:focus, .field input[type="number"]:focus, .field input[type="date"]:focus, .field select:focus { border-color: #6f809a; box-shadow: 0 0 0 2px rgba(63,81,181,0.1); outline: none; }
.field input::placeholder { color: #aaa; }
.field input[type="checkbox"], .field input[type="radio"] { width: 14px; height: 14px; accent-color: #536177; }
.radio_group { display: flex; gap: 15px; align-items: center; padding: 0 10px;}
.radio_group label {display: flex; align-items: center; gap: 5px;}
.ad_range_wrap {flex: 1; padding-left: 20px;}
.ad_range_box {display: flex;}
.ad_range_box .label {width: 109px;}
.sch_notice { font-size: 11px; color: #999; }
.sch_btn { display: flex; gap: 20px; justify-content: center; margin-top: 40px; }
.sch_btn { display: flex; gap: 10px; }
.btn_reset { display: flex; align-items: center; gap: 6px; padding: 0 20px; height: 40px; background: #9eacc6; color: #fff; font-weight: 600; border: none; border-radius: 8px; cursor: pointer; transition: background 0.2s ease, transform 0.15s ease; }
.btn_reset:hover { background: #5f6e89; }
.sch_btn button:not(.btn_reset) { padding: 0 20px; height: 40px; border: 1px solid #ccd1d8; background-color: #fff; color: #444; font-weight: 600; border-radius: 8px; cursor: pointer; user-select: none; transition: border-color 0.2s ease, box-shadow 0.2s ease; box-shadow: 0 1px 2px rgba(0,0,0,0.03); }
.sch_btn button:not(.btn_reset):hover { border-color: #6f809a; box-shadow: 0 2px 4px rgba(111, 128, 154, 0.15); }
.sch_btn button:not(.btn_reset):active { box-shadow: inset 0 1px 2px rgba(0,0,0,0.1); }
/* 회원 관리 다운로드 진행 팝업 */
.excel-download-progress p { color: #374151; }
.excel-download-progress .progress-desc { padding: 40px 0 32px; text-align: center; }
.excel-download-progress .progress-summary { margin-bottom: 6px; font-size: 16px; font-weight: 500; color: #111827; }
.excel-download-progress .progress-message { font-size: 20px; font-weight: 600; color: #3b82f6; }
.excel-download-progress .progress-error { color:red; }
.progress-spinner { display: flex; flex-direction: column; align-items: center; gap: 45px; padding: 24px 0; transition: all 0.2s ease; }
.spinner { width: 48px; height: 48px; border: 5px solid #3b82f6; border-top: 5px solid #fff; border-radius: 50%; animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.loading-message { text-align: center; font-size: 14px; color: #374151; }
.excel-download-progress .progress-download-box { margin-top: 24px; background: #f9fafb; padding: 20px; border-radius: 8px; box-shadow: 0 1px 2px rgba(0,0,0,0.03); }
.excel-download-progress .progress-download-box a { display: block; width: 100%; height: auto; text-align: center; margin-top: 8px; font-weight: 600; font-size: 14px; padding: 10px 20px; background: #fff; border: 1px solid #ccd1d8; border-radius: 8px; color: #444; cursor: pointer; transition: border-color 0.2s ease, box-shadow 0.2s ease; box-shadow: 0 1px 2px rgba(0,0,0,0.03); }
.excel-download-progress .progress-download-box a:hover { border-color: #6f809a; box-shadow: 0 2px 4px rgba(111, 128, 154, 0.15); }
.excel-download-progress .progress-download-box a:active { box-shadow: inset 0 1px 2px rgba(0,0,0,0.1); }
.field-select-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 15px; gap: 0px 10px; padding: 10px; background-color: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; color: #374151; }
.field-select-form label { display: flex; align-items: center; cursor: pointer; padding: 6px 10px; border-radius: 4px; }
.field-select-form label:hover { background-color: #f3f4f6; }
.field-select-form input[type="checkbox"] { margin-right: 8px; transform: scale(1.2); }
.field-separator { grid-column: 1 / -1; border-top: 1px solid #d1d5db; margin: 8px 0; }
.selected-fields-preview { padding: 8px; background-color: #eef2f7; border: 1px solid #d1d5db; border-radius: 6px; margin: 10px 0px; color: #1f2937; display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.selected-fields-preview strong { padding: 4px 8px; }
.selected-fields-preview .field-tag { background-color: #dbeafe; color: #1e40af; padding: 4px 8px; border-radius: 4px; }
/* 페이지 내 실행 */
.local_cmd {min-width:960px}
.local_cmd01 {margin:0 0 10px;padding:0 }
.local_cmd01 .cmd_tit {font-weight:bold}
.local_cmd01 .btn_submit {padding:3px 5px;border:1px solid #ff3061;color:#fff;font-size:0.95em;vertical-align:middle}
@ -298,7 +379,7 @@ border-bottom: 5px solid black;}
.local_desc01 {margin:10px 0 10px ;padding:10px 20px;border:1px solid #f2f2f2;background:#f9f9f9}
.local_desc01 strong {color:#ff3061}
.local_desc01 a {text-decoration:underline}
.local_desc01 a {text-decoration:underline;text-underline-offset:2px;}
.local_desc02 {margin:10px 0 ;min-width:960px} /* 주로 온라인 서식 관련 안내 내용에 사용 */
.local_desc02 p {padding:0;line-height:1.8em}
@ -401,6 +482,7 @@ tfoot th {}
.mb_leave_msg {color:#b6b6b6}
.mb_intercept_msg {color:#ff0000}
#point_mng {margin-top:50px}
.ad_agree_log {max-height: 150px !important;}
/* 게시판추가/수정 */
#anc_bo_extra .td_grpset label {width:auto}
@ -504,6 +586,7 @@ td.td_grpset {width:160px;border-left:1px solid #e9ecee;text-align:center}
.td_time{text-align:center;width:130px}
.td_center{text-align:center;}
.td_type{width:120px}
.td_consent{width:200px}
.td_mng_s{width:60px}
.td_mng_m{width:100px}
@ -656,14 +739,15 @@ a.nicepay_btn{display:inline-block;margin:5px 0 0;padding:5px 10px;background:#0
ul.de_pg_tab{margin:0;padding:0;zoom:1}
ul.de_pg_tab:after{display:block;visibility:hidden;clear:both;content:"";}
ul.de_pg_tab li{position:relative;display:inline-block;float:left;text-align:center;margin:0;padding:0;width:120px}
ul.de_pg_tab li a{margin:0 2px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:2.5;background-color:#f7f7f7;color:#74777b;font-weight:bold;font-size:1.2em;text-decoration:none}
ul.de_pg_tab li{position:relative;display:inline-block;float:left;text-align:center;margin:0;padding:0;min-width:130px}
ul.de_pg_tab li a{margin:0 2px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:2.5;background-color:#f7f7f7;color:#74777b;font-weight:bold;font-size:1.2em;text-decoration:none; padding:0px 10px;}
ul.de_pg_tab li a:hover{text-decoration:none}
ul.de_pg_tab li.tab-current a{background:#2CC185;color:#fff}
.pg_info_fld{position:relative}
.kcp_info_fld th{background-color:#F6FCFF}
.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}
@ -1133,4 +1217,4 @@ input[type="text"]{max-width:200px}
@media only screen and (max-device-width : 480px) and (orientation : portrait){
/* Styles */
input[type="text"]{max-width:200px}
}
}

View File

@ -269,6 +269,54 @@ while ($row = sql_fetch_array($result)){
}
}
// 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;
}
$is_check = run_replace('admin_dbupgrade', $is_check);
$db_upgrade_msg = $is_check ? 'DB 업그레이드가 완료되었습니다.' : '더 이상 업그레이드 할 내용이 없습니다.<br>현재 DB 업그레이드가 완료된 상태입니다.';

View File

@ -52,8 +52,11 @@ if ($w == '') {
$sound_only = '<strong class="sound_only">필수</strong>';
$mb['mb_mailling'] = 1;
$mb['mb_sms'] = 1;
$mb['mb_open'] = 1;
$mb['mb_level'] = $config['cf_register_level'];
$mb['mb_marketing_agree'] = 0;
$mb['mb_thirdparty_agree'] = 0;
$html_title = '추가';
} elseif ($w == 'u') {
$mb = get_member($mb_id);
@ -139,6 +142,14 @@ $mb_sms_no = !$mb['mb_sms'] ? 'checked="checked"' : '';
$mb_open_yes = $mb['mb_open'] ? 'checked="checked"' : '';
$mb_open_no = !$mb['mb_open'] ? 'checked="checked"' : '';
// 마케팅 목적의 개인정보 수집 및 이용
$mb_marketing_agree_yes = $mb['mb_marketing_agree'] ? 'checked="checked"' : '';
$mb_marketing_agree_no = !$mb['mb_marketing_agree'] ? 'checked="checked"' : '';
// 개인정보 제3자 제공 동의
$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'])) {
@ -354,21 +365,64 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
</td>
</tr>
<tr>
<th scope="row">메일 수신</th>
<th scope="row">광고성 이메일 수신</th>
<td>
<input type="radio" name="mb_mailling" value="1" id="mb_mailling_yes" <?php echo $mb_mailling_yes; ?>>
<label for="mb_mailling_yes">예</label>
<input type="radio" name="mb_mailling" value="0" id="mb_mailling_no" <?php echo $mb_mailling_no; ?>>
<label for="mb_mailling_no">아니오</label>
<?php if($w == "u" && $mb['mb_mailling_date'] != "0000-00-00 00:00:00"){
echo $mb['mb_mailling'] == 1 ? "<br>(동의 일자: ".$mb['mb_mailling_date'].")" : '';
} ?>
</td>
<th scope="row"><label for="mb_sms_yes">SMS 수신</label></th>
<th scope="row"><label for="mb_sms_yes">광고성 SMS/카카오톡 수신</label></th>
<td>
<input type="radio" name="mb_sms" value="1" id="mb_sms_yes" <?php echo $mb_sms_yes; ?>>
<label for="mb_sms_yes">예</label>
<input type="radio" name="mb_sms" value="0" id="mb_sms_no" <?php echo $mb_sms_no; ?>>
<label for="mb_sms_no">아니오</label>
<?php if($w == "u" && $mb['mb_sms_date'] != "0000-00-00 00:00:00"){
echo $mb['mb_sms'] == 1 ? "<br>(동의 일자: ".$mb['mb_sms_date'].")" : '';
} ?>
</td>
</tr>
<tr>
<th scope="row">마케팅 목적의<br>개인정보 수집 및 이용</th>
<td>
<input type="radio" name="mb_marketing_agree" value="1" id="mb_marketing_agree_yes" <?php echo $mb_marketing_agree_yes; ?>>
<label for="mb_marketing_agree_yes">예</label>
<input type="radio" name="mb_marketing_agree" value="0" id="mb_marketing_agree_no" <?php echo $mb_marketing_agree_no; ?>>
<label for="mb_marketing_agree_no">아니오</label>
<?php if($w == "u" && $mb['mb_marketing_date'] != "0000-00-00 00:00:00"){
echo $mb['mb_marketing_agree'] == 1 ? "<br>(동의 일자: ".$mb['mb_marketing_date'].")" : '';
} ?>
</td>
<th scope="row"><label for="mb_sms_yes">개인정보 제3자 제공</label></th>
<td>
<input type="radio" name="mb_thirdparty_agree" value="1" id="mb_thirdparty_agree_yes" <?php echo $mb_thirdparty_agree_yes; ?>>
<label for="mb_thirdparty_agree_yes">예</label>
<input type="radio" name="mb_thirdparty_agree" value="0" id="mb_thirdparty_agree_no" <?php echo $mb_thirdparty_agree_no; ?>>
<label for="mb_thirdparty_agree_no">아니오</label>
<?php if($w == "u" && $mb['mb_thirdparty_date'] != "0000-00-00 00:00:00"){
echo $mb['mb_thirdparty_agree'] == 1 ? "<br>(동의 일자: ".$mb['mb_thirdparty_date'].")" : '';
} ?>
</td>
</tr>
<?php if($w == "u"){?>
<tr>
<th scope="row">약관동의 변경내역</th>
<td colspan="3">
<section id="sodr_request_log_wrap" class="ad_agree_log">
<div>
<?php echo conv_content($mb['mb_agree_log'], 0); ?>
</div>
</section>
</td>
</tr>
<?php } ?>
<tr>
<th scope="row">정보 공개</th>
<td colspan="3">
@ -376,6 +430,9 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
<label for="mb_open_yes">예</label>
<input type="radio" name="mb_open" value="0" id="mb_open_no" <?php echo $mb_open_no; ?>>
<label for="mb_open_no">아니오</label>
<?php if($w == "u" && $mb['mb_open_date'] != "0000-00-00 00:00:00"){
echo $mb['mb_open'] == 1 ? "<br>(동의 일자: ".$mb['mb_open_date'].")" : '';
} ?>
</td>
</tr>
<tr>

View File

@ -18,6 +18,10 @@ $mb_certify_case = isset($_POST['mb_certify_case']) ? preg_replace('/[^0-9a-z_]/
$mb_certify = isset($_POST['mb_certify']) ? preg_replace('/[^0-9a-z_]/i', '', $_POST['mb_certify']) : '';
$mb_zip = isset($_POST['mb_zip']) ? preg_replace('/[^0-9a-z_]/i', '', $_POST['mb_zip']) : '';
// 광고성 정보 수신
$mb_marketing_agree = isset($_POST['mb_marketing_agree']) ? clean_xss_tags($_POST['mb_marketing_agree'], 1, 1) : '0';
$mb_thirdparty_agree = isset($_POST['mb_thirdparty_agree']) ? clean_xss_tags($_POST['mb_thirdparty_agree'], 1, 1) : '0';
// 관리자가 자동등록방지를 사용해야 할 경우 ( 회원의 비밀번호 변경시 캡챠를 체크한다 )
if ($mb_password) {
include_once(G5_CAPTCHA_PATH . '/captcha.lib.php');
@ -86,8 +90,6 @@ foreach ($check_keys as $key) {
}
}
$mb_memo = isset($_POST['mb_memo']) ? $_POST['mb_memo'] : '';
$sql_common = " mb_name = '{$posts['mb_name']}',
mb_nick = '{$mb_nick}',
mb_email = '{$mb_email}',
@ -109,8 +111,11 @@ $sql_common = " mb_name = '{$posts['mb_name']}',
mb_mailling = '{$posts['mb_mailling']}',
mb_sms = '{$posts['mb_sms']}',
mb_open = '{$posts['mb_open']}',
mb_open_date = '".G5_TIME_YMDHIS."',
mb_profile = '{$posts['mb_profile']}',
mb_level = '{$posts['mb_level']}',
mb_marketing_agree = '{$mb_marketing_agree}',
mb_thirdparty_agree = '{$mb_thirdparty_agree}',
mb_1 = '{$posts['mb_1']}',
mb_2 = '{$posts['mb_2']}',
mb_3 = '{$posts['mb_3']}',
@ -142,6 +147,36 @@ if ($w == '') {
alert('이미 존재하는 이메일입니다.\\n : ' . $row['mb_id'] . '\\n이름 : ' . $row['mb_name'] . '\\n닉네임 : ' . $row['mb_nick'] . '\\n메일 : ' . $row['mb_email']);
}
$agree_items = [];
// 마케팅 목적의 개인정보 수집 및 이용
if ($mb_marketing_agree == 1) {
$sql_common .= " , mb_marketing_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "마케팅 목적의 개인정보 수집 및 이용(동의)";
}
// 광고성 이메일 수신
if ($mb_mailling == 1) {
$sql_common .= " , mb_mailling_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "광고성 이메일 수신(동의)";
}
// 광고성 SMS/카카오톡 수신
if ($mb_sms == 1) {
$sql_common .= " , mb_sms_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "광고성 SMS/카카오톡 수신(동의)";
}
// 개인정보 제3자 제공
if ($mb_thirdparty_agree == 1) {
$sql_common .= " , mb_thirdparty_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "개인정보 제3자 제공(동의)";
}
// 동의 로그 추가
if (!empty($agree_items)) {
$agree_log = "[".G5_TIME_YMDHIS.", 관리자 회원추가] " . implode(' | ', $agree_items) . "\n";
$sql_common .= " , mb_agree_log = CONCAT('{$agree_log}', IFNULL(mb_agree_log, ''))";
}
sql_query(" insert into {$g5['member_table']} set mb_id = '{$mb_id}', mb_password = '" . get_encrypt_string($mb_password) . "', mb_datetime = '" . G5_TIME_YMDHIS . "', mb_ip = '{$_SERVER['REMOTE_ADDR']}', mb_email_certify = '" . G5_TIME_YMDHIS . "', {$sql_common} ");
} elseif ($w == 'u') {
$mb = get_member($mb_id);
@ -193,10 +228,54 @@ if ($w == '') {
$sql_certify = "";
}
// 현재 데이터 조회
$row = sql_fetch("select * from {$g5['member_table']} where mb_id = '{$mb_id}' ");
$agree_items = [];
// 마케팅 목적의 개인정보 수집 및 이용
$sql_marketing_date = "";
if ($row['mb_marketing_agree'] !== $mb_marketing_agree) {
$sql_marketing_date .= " , mb_marketing_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "마케팅 목적의 개인정보 수집 및 이용(" . ($mb_marketing_agree == 1 ? "동의" : "철회") . ")";
}
// 광고성 이메일 수신
$sql_mailling_date = "";
if ($row['mb_mailling'] !== $mb_mailling) {
$sql_mailling_date .= " , mb_mailling_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "광고성 이메일 수신(" . ($mb_mailling == 1 ? "동의" : "철회") . ")";
}
// 광고성 SMS/카카오톡 수신
$sql_sms_date = "";
if ($row['mb_sms'] !== $mb_sms) {
$sql_sms_date .= " , mb_sms_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "광고성 SMS/카카오톡 수신(" . ($mb_sms == 1 ? "동의" : "철회") . ")";
}
// 개인정보 제3자 제공
$sql_thirdparty_date = "";
if ($row['mb_thirdparty_agree'] !== $mb_thirdparty_agree) {
$sql_thirdparty_date .= " , mb_thirdparty_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "개인정보 제3자 제공(" . ($mb_thirdparty_agree == 1 ? "동의" : "철회") . ")";
}
// 동의 로그 추가
$sql_agree_log = "";
if (!empty($agree_items)) {
$agree_log = "[".G5_TIME_YMDHIS.", 관리자 회원수정] " . implode(' | ', $agree_items) . "\n";
$sql_agree_log .= " , mb_agree_log = CONCAT('{$agree_log}', IFNULL(mb_agree_log, ''))";
}
$sql = " update {$g5['member_table']}
set {$sql_common}
{$sql_password}
{$sql_certify}
{$sql_mailling_date}
{$sql_sms_date}
{$sql_marketing_date}
{$sql_thirdparty_date}
{$sql_agree_log}
where mb_id = '{$mb_id}' ";
sql_query($sql);
} else {

View File

@ -127,7 +127,7 @@ $colspan = 16;
<th scope="col" rowspan="2" id="mb_list_cert"><?php echo subject_sort_link('mb_certify', '', 'desc') ?>본인확인</a></th>
<th scope="col" id="mb_list_mailc"><?php echo subject_sort_link('mb_email_certify', '', 'desc') ?>메일인증</a></th>
<th scope="col" id="mb_list_open"><?php echo subject_sort_link('mb_open', '', 'desc') ?>정보공개</a></th>
<th scope="col" id="mb_list_mailr"><?php echo subject_sort_link('mb_mailling', '', 'desc') ?>메일수신</a></th>
<th scope="col" id="mb_list_mailr"><?php echo subject_sort_link('mb_mailling', '', 'desc') ?>광고성이메일수신</a></th>
<th scope="col" id="mb_list_auth">상태</th>
<th scope="col" id="mb_list_mobile">휴대폰</th>
<th scope="col" id="mb_list_lastcall"><?php echo subject_sort_link('mb_today_login', '', 'desc') ?>최종접속</a></th>
@ -137,7 +137,7 @@ $colspan = 16;
<tr>
<th scope="col" id="mb_list_name"><?php echo subject_sort_link('mb_name') ?>이름</a></th>
<th scope="col" id="mb_list_nick"><?php echo subject_sort_link('mb_nick') ?>닉네임</a></th>
<th scope="col" id="mb_list_sms"><?php echo subject_sort_link('mb_sms', '', 'desc') ?>SMS수신</a></th>
<th scope="col" id="mb_list_sms"><?php echo subject_sort_link('mb_sms', '', 'desc') ?>광고성SMS/카카오톡수신</a></th>
<th scope="col" id="mb_list_adultc"><?php echo subject_sort_link('mb_adult', '', 'desc') ?>성인인증</a></th>
<th scope="col" id="mb_list_auth"><?php echo subject_sort_link('mb_intercept_date', '', 'desc') ?>접근차단</a></th>
<th scope="col" id="mb_list_deny"><?php echo subject_sort_link('mb_level', '', 'desc') ?>권한</a></th>
@ -252,14 +252,15 @@ $colspan = 16;
<input type="radio" name="mb_certify[<?php echo $i; ?>]" value="ipin" id="mb_certify_ipin_<?php echo $i; ?>" <?php echo $row['mb_certify'] == 'ipin' ? 'checked' : ''; ?>>
<label for="mb_certify_ipin_<?php echo $i; ?>">아이핀</label>
</td>
<td headers="mb_list_mailc"><?php echo preg_match('/[1-9]/', $row['mb_email_certify']) ? '<span class="txt_true">Yes</span>' : '<span class="txt_false">No</span>'; ?></td>
<td headers="mb_list_open">
<td headers="mb_list_mailc" class="td_consent"><?php echo preg_match('/[1-9]/', $row['mb_email_certify']) ? '<span class="txt_true">Yes</span>' : '<span class="txt_false">No</span>'; ?></td>
<td headers="mb_list_open" class="td_consent">
<label for="mb_open_<?php echo $i; ?>" class="sound_only">정보공개</label>
<input type="checkbox" name="mb_open[<?php echo $i; ?>]" <?php echo $row['mb_open'] ? 'checked' : ''; ?> value="1" id="mb_open_<?php echo $i; ?>">
</td>
<td headers="mb_list_mailr">
<td headers="mb_list_mailr" class="td_consent">
<label for="mb_mailling_<?php echo $i; ?>" class="sound_only">메일수신</label>
<input type="checkbox" name="mb_mailling[<?php echo $i; ?>]" <?php echo $row['mb_mailling'] ? 'checked' : ''; ?> value="1" id="mb_mailling_<?php echo $i; ?>">
<input type="hidden" name="mb_mailling_default[<?php echo $i; ?>]" value="<?php echo isset($row['mb_mailling']) ? $row['mb_mailling'] : '0'; ?> " id="mb_mailling_default_<?php echo $i; ?>">
</td>
<td headers="mb_list_auth" class="td_mbstat">
<?php
@ -284,6 +285,7 @@ $colspan = 16;
<td headers="mb_list_sms">
<label for="mb_sms_<?php echo $i; ?>" class="sound_only">SMS수신</label>
<input type="checkbox" name="mb_sms[<?php echo $i; ?>]" <?php echo $row['mb_sms'] ? 'checked' : ''; ?> value="1" id="mb_sms_<?php echo $i; ?>">
<input type="hidden" name="mb_sms_default[<?php echo $i; ?>]" value="<?php echo isset($row['mb_sms']) ? $row['mb_sms'] : '0'; ?> " id="mb_sms_default_<?php echo $i; ?>">
</td>
<td headers="mb_list_adultc">
<label for="mb_adult_<?php echo $i; ?>" class="sound_only">성인인증</label>

View File

@ -0,0 +1,288 @@
<?php
/*************************************************************************
**
** 내보내기 관련 상수 정의
**
*************************************************************************/
define('MEMBER_EXPORT_PAGE_SIZE', 10000); // 파일당 처리할 회원 수
define('MEMBER_EXPORT_MAX_SIZE', 300000); // 최대 처리할 회원 수
define('MEMBER_BASE_DIR', "member_list"); // 엑셀 베이스 폴더
define('MEMBER_BASE_DATE', date('YmdHis')); // 폴더/파일명용 날짜
define('MEMBER_EXPORT_DIR', G5_DATA_PATH . "/" . MEMBER_BASE_DIR . "/" . MEMBER_BASE_DATE); // 엑셀 파일 저장 경로
define('MEMBER_LOG_DIR', G5_DATA_PATH . "/" . MEMBER_BASE_DIR . "/" . "log"); // 로그 파일 저장 경로
/*************************************************************************
**
** 공통 함수 정의
**
*************************************************************************/
/**
* 검색 옵션 설정
*/
function get_export_config($type = null)
{
$config = [
'sfl_list' => [
'mb_id'=>'아이디',
'mb_name'=>'이름',
'mb_nick'=>'닉네임',
'mb_email'=>'이메일',
'mb_tel'=>'전화번호',
'mb_hp'=>'휴대폰번호',
'mb_addr1'=>'주소'
],
'point_cond_map' => [
'gte'=>'≥',
'lte'=>'≤',
'eq'=>'='
],
'intercept_list' => [
'exclude'=>'차단회원 제외',
'only'=>'차단회원만'
],
'ad_range_list' => [
'all' => '수신동의 회원 전체',
'mailling_only' => '이메일 수신동의 회원만',
'sms_only' => 'SMS/카카오톡 수신동의 회원만',
'month_confirm' => date('m월').' 수신동의 확인 대상만',
'custom_period' => '수신동의 기간 직접 입력'
],
];
return $type ? (isset($config[$type]) ? $config[$type] : []) : $config;
}
/**
* 파라미터 수집 및 유효성 검사
*/
function get_member_export_params()
{
// 친구톡 양식 - 엑셀 양식에 포함할 항목
$fieldArray = array_map('trim', explode(',', isset($_GET['fields']) ? $_GET['fields'] : ''));
$vars = [];
foreach ($fieldArray as $index => $field) {
if(!empty($field)){
$vars['var' . ($index + 1)] = $field;
}
}
$params = [
'page' => 1,
'formatType' => (int)(isset($_GET['formatType']) ? $_GET['formatType'] : 1),
'use_stx' => isset($_GET['use_stx']) ? $_GET['use_stx'] : 0,
'stx_cond' => clean_xss_tags(isset($_GET['stx_cond']) ? $_GET['stx_cond'] : 'like'),
'sfl' => clean_xss_tags(isset($_GET['sfl']) ? $_GET['sfl'] : ''),
'stx' => clean_xss_tags(isset($_GET['stx']) ? $_GET['stx'] : ''),
'use_level' => isset($_GET['use_level']) ? $_GET['use_level'] : 0,
'level_start' => (int)(isset($_GET['level_start']) ? $_GET['level_start'] : 1),
'level_end' => (int)(isset($_GET['level_end']) ? $_GET['level_end'] : 10),
'use_date' => isset($_GET['use_date']) ? $_GET['use_date'] : 0,
'date_start' => clean_xss_tags(isset($_GET['date_start']) ? $_GET['date_start'] : ''),
'date_end' => clean_xss_tags(isset($_GET['date_end']) ? $_GET['date_end'] : ''),
'use_point' => isset($_GET['use_point']) ? $_GET['use_point'] : 0,
'point' => isset($_GET['point']) ? $_GET['point'] : '',
'point_cond' => isset($_GET['point_cond']) ? $_GET['point_cond'] : 'gte',
'use_hp_exist' => isset($_GET['use_hp_exist']) ? $_GET['use_hp_exist'] : 0,
'ad_range_only' => isset($_GET['ad_range_only']) ? $_GET['ad_range_only'] : 0,
'ad_range_type' => clean_xss_tags(isset($_GET['ad_range_type']) ? $_GET['ad_range_type'] : 'all'),
'ad_mailling' => isset($_GET['ad_mailling']) ? $_GET['ad_mailling'] : 0,
'ad_sms' => isset($_GET['ad_sms']) ? $_GET['ad_sms'] : 0,
'agree_date_start' => clean_xss_tags(isset($_GET['agree_date_start']) ? $_GET['agree_date_start'] : ''),
'agree_date_end' => clean_xss_tags(isset($_GET['agree_date_end']) ? $_GET['agree_date_end'] : ''),
'use_intercept' => isset($_GET['use_intercept']) ? $_GET['use_intercept'] : 0,
'intercept' => clean_xss_tags(isset($_GET['intercept']) ? $_GET['intercept'] : 'exclude'),
'vars' => $vars,
];
// 레벨 범위 검증
if ($params['level_start'] > $params['level_end']) {
[$params['level_start'] , $params['level_end']] = [$params['level_end'], $params['level_start']];
}
// 가입기간 - 날짜 범위 검증
if ($params['use_date'] && $params['date_start'] && $params['date_end']) {
if ($params['date_start'] > $params['date_end']) {
[$params['date_start'] , $params['date_end']] = [$params['date_end'], $params['date_start']];
}
}
// 수신동의기간 - 날짜 범위 검증
if ($params['ad_range_type'] == 'custom_period' && $params['agree_date_start'] && $params['agree_date_end']) {
if ($params['agree_date_start'] > $params['agree_date_end']) {
[$params['agree_date_start'] , $params['agree_date_end']] = [$params['agree_date_end'], $params['agree_date_start']];
}
}
return $params;
}
/**
* 전체 데이터 개수 조회
*/
function member_export_get_total_count($params)
{
global $g5;
$where = member_export_build_where($params);
$sql = "SELECT COUNT(*) as cnt FROM {$g5['member_table']} {$where}";
$result = sql_query($sql);
if (!$result) {
throw new Exception("데이터 조회에 실패하였습니다. 다시 시도해주세요.");
}
$row = sql_fetch_array($result);
return (int)$row['cnt'];
}
/**
* WHERE 조건절 생성
*/
function member_export_build_where($params)
{
global $config;
$conditions = [];
// 기본 조건 - 탈퇴하지 않은 사용자
$conditions[] = "mb_leave_date = ''";
// 검색어 조건 (sql_escape_string 사용으로 보안 강화)
if (!empty($params['use_stx']) && $params['use_stx'] === '1') {
$sfl_list = get_export_config('sfl_list');
$sfl = in_array($params['sfl'], array_keys($sfl_list)) ? $params['sfl'] : '';
$stx = sql_escape_string($params['stx']);
if(!empty($sfl) && !empty($stx)){
if ($params['stx_cond'] === 'like') {
$conditions[] = "{$sfl} LIKE '%{$stx}%'";
} else {
$conditions[] = "{$sfl} = '{$stx}'";
}
}
}
// 권한 조건
if (!empty($params['use_level']) && $params['use_level'] === '1') {
$level_start = max(1, (int)$params['level_start']);
$level_end = min(10, (int)$params['level_end']);
$conditions[] = "(mb_level BETWEEN {$level_start} AND {$level_end})";
}
// 가입기간 조건
if (!empty($params['use_date']) && $params['use_date'] === '1') {
$date_start = isset($params['date_start']) ? sql_escape_string(trim($params['date_start'])) : '';
$date_end = isset($params['date_end']) ? sql_escape_string(trim($params['date_end'])) : '';
if ($date_start && $date_end) {
$conditions[] = "mb_datetime BETWEEN '{$date_start} 00:00:00' AND '{$date_end} 23:59:59'";
} elseif ($date_start) {
$conditions[] = "mb_datetime >= '{$date_start} 00:00:00'";
} elseif ($date_end) {
$conditions[] = "mb_datetime <= '{$date_end} 23:59:59'";
}
}
// 포인트 조건
if (!empty($params['use_point']) && $params['use_point'] === '1') {
$point = $params['point'];
$point_cond = $params['point_cond'];
if ($point != '') {
$point = (int)$point; // 정수로 캐스팅
switch ($point_cond) {
case 'lte':
$conditions[] = "mb_point <= {$point}";
break;
case 'eq':
$conditions[] = "mb_point = {$point}";
break;
default:
$conditions[] = "mb_point >= {$point}";
break;
}
}
}
// 휴대폰 번호 존재 조건
if (!empty($params['use_hp_exist']) && $params['use_hp_exist'] === '1') {
$conditions[] = "(mb_hp is not null and mb_hp != '')";
}
// 정보수신동의 조건
if (!empty($params['ad_range_only']) && $params['ad_range_only'] === '1') {
$range = isset($params['ad_range_type']) ? $params['ad_range_type'] : '';
// 공통: 마케팅 목적 수집·이용 동의 + (필요 시) 제3자 동의
$thirdparty_clause = $config['cf_sms_use'] !== '' ? " AND mb_thirdparty_agree = 1" : "";
$base_marketing = "mb_marketing_agree = 1{$thirdparty_clause}";
if ($range === 'all') {
// 마케팅 동의 + (이메일 OR SMS 동의)
$conditions[] = "({$base_marketing} AND (mb_mailling = 1 OR mb_sms = 1))";
} elseif ($range === 'mailling_only') {
// 마케팅 동의 + 이메일 동의
$conditions[] = "({$base_marketing} AND mb_mailling = 1)";
} elseif ($range === 'sms_only') {
// 마케팅 동의 + SMS/카카오톡 동의
$conditions[] = "({$base_marketing} AND mb_sms = 1)";
} elseif ($range === 'month_confirm' || $range === 'custom_period') {
// 채널 필터 체크
$useEmail = !empty($params['ad_mailling']);
$useSms = !empty($params['ad_sms']);
if ($range === 'month_confirm') {
// 23개월 전 그 달
$start = date('Y-m-01 00:00:00', strtotime('-23 months'));
$end = date('Y-m-t 23:59:59', strtotime('-23 months'));
$emailDateCond = "mb_mailling_date BETWEEN '{$start}' AND '{$end}'";
$smsDateCond = "mb_sms_date BETWEEN '{$start}' AND '{$end}'";
} else {
// 수신동의기간 직접 입력 - custom_period
$date_start = isset($params['agree_date_start']) ? $params['agree_date_start'] : '';
$date_end = isset($params['agree_date_end']) ? $params['agree_date_end'] : '';
if ($date_start && $date_end) {
$emailDateCond = "mb_mailling_date BETWEEN '{$date_start} 00:00:00' AND '{$date_end} 23:59:59'";
$smsDateCond = "mb_sms_date BETWEEN '{$date_start} 00:00:00' AND '{$date_end} 23:59:59'";
} elseif ($date_start) {
$emailDateCond = "mb_mailling_date >= '{$date_start} 00:00:00'";
$smsDateCond = "mb_sms_date >= '{$date_start} 00:00:00'";
} elseif ($date_end) {
$emailDateCond = "mb_mailling_date <= '{$date_end} 23:59:59'";
$smsDateCond = "mb_sms_date <= '{$date_end} 23:59:59'";
} else {
$emailDateCond = "mb_mailling_date <> '0000-00-00 00:00:00'";
$smsDateCond = "mb_sms_date <> '0000-00-00 00:00:00'";
}
}
if (!$useEmail && !$useSms) {
$conditions[] = "0=1"; // 둘 다 해제 ⇒ 결과 0건
} else {
// 조건 조립
$parts = [];
if ($useEmail) $parts[] = "(mb_mailling = 1 AND {$emailDateCond})";
if ($useSms) $parts[] = "(mb_sms = 1 AND {$smsDateCond})";
$conditions[] = !empty($parts) ? '(' . implode(' OR ', $parts) . ')' : '';
}
}
}
// 차단 회원 조건
if (!empty($params['use_intercept']) && $params['use_intercept'] === '1') {
switch ($params['intercept']) {
case 'exclude':
$conditions[] = "mb_intercept_date = ''";
break;
case 'only':
$conditions[] = "mb_intercept_date != ''";
break;
}
}
return empty($conditions) ? '' : 'WHERE ' . implode(' AND ', $conditions);
}

483
adm/member_list_exel.php Normal file
View File

@ -0,0 +1,483 @@
<?php
$sub_menu = "200400";
require_once './_common.php';
require_once './member_list_exel.lib.php'; // 회원관리파일 공통 라이브러리
auth_check_menu($auth, $sub_menu, 'r');
// 파라미터 수집 및 유효성 검사
$params = get_member_export_params();
// 총건수
$total_count = 0;
$total_error = "";
try {
$total_count = member_export_get_total_count($params);
} catch (Exception $e) {
$total_error = $e->getMessage(); // 메서드 호출 괄호 필수
}
$g5['title'] = '회원관리파일';
require_once './admin.head.php';
$colspan = 14;
?>
<h2>회원 엑셀 생성</h2>
<div class="local_desc01 local_desc">
<p><b>회원수 <?php echo number_format(MEMBER_EXPORT_PAGE_SIZE);?>건 초과 시</b> <?php echo number_format(MEMBER_EXPORT_PAGE_SIZE);?>건 단위로 분리 저장되며, <b>엑셀 생성 최대 건수는 <?php echo number_format(MEMBER_EXPORT_MAX_SIZE);?>건</b>입니다. 초과 시 조건 추가 설정 후 재시도하시기 바랍니다.</p>
<p><b>수신동의 확인 대상은 만료일까지 1달 미만인 회원</b>을 기준으로 필터링됩니다.</p>
<br>
<p>파일 생성 시 서버에 임시 생성된 파일 중 <b>오늘 날짜를 제외 한 파일은 자동 삭제</b>되며, 수동 삭제 필요 시 <a href="<?php echo G5_ADMIN_URL;?>/member_list_file_delete.php"><b>회원관리파일 일괄삭제</b></a>에서 진행하시기 바랍니다.</p>
<p>회원 정보 수정은 <a href="<?php echo G5_ADMIN_URL;?>/member_list.php" class="link"><b>회원 관리</b></a>에서 진행하실 수 있습니다.</p>
</div>
<div class="local_ov01 local_ov">
<span class="btn_ov01">
<span class="ov_txt">총건수 </span>
<?php if($total_error != "") { ?>
<span class="ov_num"> <?php echo $total_error ?></span>
<?php } else {?>
<span class="ov_num"> <?php echo number_format($total_count) ?>건</span>
<?php } ?>
</span>
</div>
<!-- 회원 검색 필터링 폼 -->
<form id="fsearch" name="fsearch" class="member_list_data" method="get">
<input type="hidden" name="token" value="<?php echo get_token(); ?>">
<fieldset>
<legend class="sound_only">회원 검색 필터링</legend>
<div class="sch_table">
<!-- 검색어 적용 -->
<div class="sch_row">
<div class="label">
<label>
<input type="checkbox" name="use_stx" value="1" <?php echo isset($_GET['use_stx']) ? 'checked' : ''; ?>>
검색어 적용
</label>
</div>
<div class="field">
<select name="sfl">
<?php
// 검색어 옵션 : [정의] get_export_config() - adm/member_list_exel.lib.php;
foreach (get_export_config('sfl_list') as $val => $label) {
$selected = (isset($_GET['sfl']) && $_GET['sfl'] === $val) ? 'selected' : '';
echo "<option value=\"$val\" $selected>$label</option>";
}
?>
</select>
<input type="text" name="stx" value="<?php echo htmlspecialchars(isset($_GET['stx']) ? $_GET['stx'] : ''); ?>" placeholder="검색어 입력">
<span class="radio_group">
<label><input type="radio" name="stx_cond" value="like" <?php echo (isset($_GET['stx_cond']) ? $_GET['stx_cond'] : 'like') === 'like' ? 'checked' : ''; ?>> 포함</label>
<label><input type="radio" name="stx_cond" value="equal" <?php echo (isset($_GET['stx_cond']) ? $_GET['stx_cond'] : '') === 'equal' ? 'checked' : ''; ?>> 일치</label>
</span>
</div>
</div>
<!-- 레벨 적용 -->
<div class="sch_row">
<div class="label">
<label><input type="checkbox" name="use_level" value="1" <?php echo isset($_GET['use_level']) ? 'checked' : ''; ?>> 레벨 적용</label>
</div>
<div class="field">
<select name="level_start">
<?php for ($i = 1; $i <= 10; $i++): ?>
<option value="<?php echo $i; ?>" <?php echo (isset($_GET['level_start']) && $_GET['level_start'] == $i) ? 'selected' : ''; ?>><?php echo $i; ?></option>
<?php endfor; ?>
</select> ~
<select name="level_end">
<?php for ($i = 1; $i <= 10; $i++): ?>
<option value="<?php echo $i; ?>" <?php echo (isset($_GET['level_end']) && $_GET['level_end'] == $i) ? 'selected' : ''; ?>><?php echo $i; ?></option>
<?php endfor; ?>
</select>
</div>
</div>
<!-- 가입기간 적용 -->
<div class="sch_row">
<div class="label">
<label><input type="checkbox" name="use_date" value="1" <?php echo isset($_GET['use_date']) ? 'checked' : ''; ?>> 가입기간 적용</label>
</div>
<div class="field">
<input type="date" name="date_start" max="9999-12-31" value="<?php echo htmlspecialchars(isset($_GET['date_start']) ? $_GET['date_start'] : ''); ?>"> ~
<input type="date" name="date_end" max="9999-12-31" value="<?php echo htmlspecialchars(isset($_GET['date_end']) ? $_GET['date_end'] : ''); ?>">
</div>
</div>
<!-- 포인트 적용 -->
<div class="sch_row">
<div class="label">
<label><input type="checkbox" name="use_point" value="1" <?php echo isset($_GET['use_point']) ? 'checked' : ''; ?>> 포인트 적용</label>
</div>
<div class="field">
<input type="number" name="point" value="<?php echo htmlspecialchars(isset($_GET['point']) ? $_GET['point'] : ''); ?>" placeholder="포인트 입력">
<span class="radio_group">
<label><input type="radio" name="point_cond" value="gte" <?php echo (isset($_GET['point_cond']) ? $_GET['point_cond'] : 'gte') === 'gte' ? 'checked' : ''; ?>> 이상</label>
<label><input type="radio" name="point_cond" value="lte" <?php echo (isset($_GET['point_cond']) ? $_GET['point_cond'] : '') === 'lte' ? 'checked' : ''; ?>> 이하</label>
<label><input type="radio" name="point_cond" value="eq" <?php echo (isset($_GET['point_cond']) ? $_GET['point_cond'] : '') === 'eq' ? 'checked' : ''; ?>> 일치</label>
</span>
</div>
</div>
<!-- 차단회원 조건 -->
<div class="sch_row">
<div class="label">
<label><input type="checkbox" name="use_intercept" value="1" <?php echo isset($_GET['use_intercept']) ? 'checked' : ''; ?>> 차단회원</label>
</div>
<div class="field">
<select name="intercept" id="intercept">
<?php
// 차단회원 옵션 : [정의] get_export_config() - adm/member_list_exel.lib.php
foreach (get_export_config('intercept_list') as $val => $label) {
$selected = ((isset($_GET['intercept']) ? $_GET['intercept'] : '') === $val) ? 'selected' : '';
echo "<option value=\"$val\" $selected>$label</option>";
}
?>
</select>
</div>
</div>
<!-- 휴대폰 번호 조건 - 초기세팅(설정에 휴대폰번호가 보이기/필수입력이면 기본값 checked로 설정) -->
<div class="sch_row">
<div class="label">
<label>
<?php $use_hp_checked = isset($_GET['token']) ? (isset($_GET['use_hp_exist']) ? 'checked' : '') : (($config['cf_use_hp'] || $config['cf_req_hp']) ? 'checked' : '');?>
<input type="checkbox" name="use_hp_exist" value="1" <?php echo $use_hp_checked; ?>> 휴대폰 번호 있는 경우만
</label>
</div>
</div>
<!-- 정보수신동의 조건 -->
<div class="sch_row">
<div class="label">
<label><input type="checkbox" name="ad_range_only" value="1" <?php echo isset($_GET['ad_range_only']) ? 'checked' : ''; ?>> 정보수신동의에 동의한 경우만</label>
</div>
<!-- 안내 문구 -->
<div class="field">
<p class="sch_notice">「정보통신망이용촉진및정보보호등에관한법률」에 따라 <b>광고성 정보 수신동의 여부</b>를 <b>매2년</b>마다 확인해야 합니다.</p>
</div>
</div>
<div class="sch_row <?php echo isset($_GET['ad_range_only']) ? '' : 'is-hidden'; ?>">
<div class="ad_range_wrap">
<div class="ad_range_box">
<div class="label">
<label for="ad_range_type">회원범위</label>
</div>
<div class="field">
<select name="ad_range_type" id="ad_range_type">
<?php
foreach (get_export_config('ad_range_list') as $val => $label) {
$selected = ((isset($_GET['ad_range_type']) ? $_GET['ad_range_type'] : '') === $val) ? 'selected' : '';
echo "<option value=\"$val\" $selected>$label</option>";
}
?>
</select>
<div class="ad_range_wrap">
<!-- 기간 직접 입력 -->
<div class="ad_range_box <?php echo isset($_GET['ad_range_only']) && (isset($_GET['ad_range_type']) ? $_GET['ad_range_type'] : '') == 'custom_period' ? '' : 'is-hidden'; ?>">
<div class="field">
<input type="date" name="agree_date_start" max="9999-12-31" value="<?php echo htmlspecialchars(isset($_GET['agree_date_start']) ? $_GET['agree_date_start'] : date('Y-m-d', strtotime('-1 month'))); ?>"> ~
<input type="date" name="agree_date_end" max="9999-12-31" value="<?php echo htmlspecialchars(isset($_GET['agree_date_end']) ? $_GET['agree_date_end'] : date('Y-m-d')); ?>">
<p>* 광고성 정보 수신(<b>이메일 또는 SMS/카카오톡</b>) 동의일자 기준</p>
</div>
</div>
<!-- 설명 문구 -->
<?php
$thirdpartyLbl = (!empty($config['cf_sms_use'])) ? ' / <b>개인정보 제3자 제공</b>' : '';
$ad_range_text = [
'all' => "* <b>광고성 정보 수신(이메일 또는 SMS/카카오톡)</b> / <b>마케팅 목적의 개인정보 수집 및 이용</b>{$thirdpartyLbl}에 모두 동의한 회원을 선택합니다.",
'mailling_only' => "* <b>광고성 이메일 수신</b> / <b>마케팅 목적의 개인정보 수집 및 이용</b>{$thirdpartyLbl}에 모두 동의한 회원을 선택합니다.",
'sms_only' => "* <b>광고성 SMS/카카오톡 수신</b> / <b>마케팅 목적의 개인정보 수집 및 이용</b>{$thirdpartyLbl}에 모두 동의한 회원을 선택합니다.",
'month_confirm' => "* 23개월 전(" . date('Y년 m월', strtotime('-23 month')) . ") <b>광고성 정보 수신 동의(이메일 또는 SMS/카카오톡)</b>한 회원을 선택합니다."
];
if (isset($_GET['ad_range_only'], $_GET['ad_range_type']) && isset($ad_range_text[$_GET['ad_range_type']])) {
echo '<div class="ad_range_box"><p>' . $ad_range_text[$_GET['ad_range_type']] . '</p></div>';
}
?>
</div>
<br>
</div>
</div>
</div>
</div>
<!-- 채널 체크박스 -->
<div class="sch_row <?php echo isset($_GET['ad_range_only']) && in_array($_GET['ad_range_type'], ['month_confirm', 'custom_period']) ? '' : 'is-hidden'; ?>">
<div class="ad_range_wrap">
<div class="ad_range_box">
<div class="label">
</div>
<div class="field">
<?php $ad_mailling_checked = isset($_GET['token']) ? (isset($_GET['ad_mailling']) ? 'checked' : '') : 'checked';?>
<?php $ad_sms_checked = isset($_GET['token']) ? (isset($_GET['ad_sms']) ? 'checked' : '') : 'checked';?>
<label><input type="checkbox" name="ad_mailling" value="1" <?php echo $ad_mailling_checked; ?>> 광고성 이메일 수신</label>
<label><input type="checkbox" name="ad_sms" value="1" <?php echo $ad_sms_checked; ?>> 광고성 SMS/카카오톡 수신</label>
</div>
</div>
</div>
</div>
<div class="sch_btn">
<button type="button" id="btnExcelDownload">엑셀파일 다운로드</button>
<button type="button" class="btn_reset" onclick="location.href='?'">초기화</button>
</div>
</div>
</fieldset>
</form>
<script>
document.querySelector('input[name="ad_range_only"]').addEventListener('change', function () {
document.querySelectorAll('.ad_range_wrap').forEach(el => {
el.classList.toggle('is-hidden', !this.checked);
});
});
document.querySelectorAll('#fsearch input, #fsearch select').forEach(el => {
const submit = () => document.getElementById('fsearch').submit();
el.addEventListener(el.type === 'date' ? 'blur' : 'change', submit);
el.addEventListener('keydown', e => {
if (e.key === 'Enter') {
e.preventDefault();
submit();
}
});
});
</script>
<script>
let eventSource = null;
// 일반 엑셀 다운로드 버튼 클릭
document.getElementById('btnExcelDownload').addEventListener('click', () => {
startExcelDownload();
});
// 엑셀 다운로드 실행
// 1. 기존 SSE 종료
function closePreviousEventSource() {
if (eventSource) {
eventSource.close();
eventSource = null;
}
}
// 2. FormData QueryString 변환
function buildDownloadParams(selectedFields = []) {
const formData = new FormData(document.getElementById('fsearch'));
const params = new URLSearchParams(formData);
params.append('mode', 'start');
return params.toString();
}
// 3. 메인 함수
function startExcelDownload(selectedFields = []) {
closePreviousEventSource();
const query = buildDownloadParams(selectedFields);
showDownloadPopup();
eventSource = new EventSource(`member_list_exel_export.php?${query}`);
eventSource.onmessage = handleProgressUpdate();
eventSource.onerror = handleDownloadError();
}
// 다운로드 팝업 표시
function showDownloadPopup() {
const bodyHTML = `
<div class="excel-download-progress">
<div class="progress-desc">
<p class="progress-summary">총 <strong>0</strong>개 파일로 분할됩니다</p>
<p class="progress-message"><strong>(0 / 0)</strong> 파일 다운로드 중</p>
<p class="progress-error"></p>
</div>
<div class="progress-spinner">
<div class="spinner"></div>
<p class="loading-message">
엑셀 파일을 생성 중입니다. 잠시만 기다려주세요.<br>
현재 데이터 기준으로 <strong id="estimatedTimeText"></strong> 정도 소요될 수 있습니다.<br>
<strong>페이지를 벗어나거나 닫으면 다운로드가 중단</strong>되니, 작업 완료까지 기다려 주세요.
</p>
</div>
<div class="progress-box">
<div class="progress-download-box"></div>
</div>
</div>
`;
PopupManager.render('엑셀 다운로드 진행 중', bodyHTML, '', { disableOutsideClose: true });
// 닫기 버튼 이벤트 핸들링
const closeBtn = document.querySelector('.popup-close-btn');
if (closeBtn) {
closeBtn.removeAttribute('onclick');
closeBtn.addEventListener('click', handlePopupCloseWithConfirm);
}
}
// 닫기 버튼 클릭 시 다운로드 중단 여부 확인
function handlePopupCloseWithConfirm(e) {
if (eventSource) {
const confirmClose = confirm("엑셀 다운로드가 진행 중입니다.\n정말 중지하시겠습니까?");
if (!confirmClose) {
e.preventDefault();
return;
}
eventSource.close();
eventSource = null;
alert("엑셀 다운로드가 중단되었습니다.");
}
PopupManager.close('popupOverlay');
}
// 체크박스 선택 시 최대 3개 제한 및 선택된 항목 미리보기 표시
function bindFieldSelectEvents() {
const fieldSelectForm = document.getElementById('fieldSelectForm');
if (!fieldSelectForm) return;
fieldSelectForm.addEventListener('change', function (e) {
if (e.target.name === 'fields') {
const selected = fieldSelectForm.querySelectorAll('input[name="fields"]:checked');
if (selected.length > 3) {
alert("최대 3개까지 선택 가능합니다.");
e.target.checked = false;
return;
}
// 선택된 항목 표시
const previewContainer = document.getElementById('selectedFieldsPreview');
let spans = '<strong>선택된 항목:</strong>';
selected.forEach(field => {
const label = field.parentElement.textContent.trim();
spans += `<span class="field-tag">${label}</span>`;
});
previewContainer.innerHTML = spans;
}
});
}
// 엑셀 생성 및 다운로드 실행
function handleProgressUpdate() {
return function(e) {
const data = JSON.parse(e.data);
const { status, downloadType, message, total, current, totalChunks, currentChunk, zipFile, files, filePath } = data;
// DOM 요소 캐싱
const titleEl = document.getElementById('popupTitle');
const summaryEl = document.querySelector('.progress-summary');
const messageEl = document.querySelector('.progress-message');
const spinnerEl = document.querySelector('.progress-spinner');
const resultEl = document.querySelector('.loading-message');
const downloadBoxEl = document.querySelector('.progress-download-box');
const errorEl = document.querySelector('.progress-error');
if (status === "progress")
{
summaryEl.innerHTML = `총 <strong>${totalChunks}</strong>개 파일로 ` + (downloadType === 2 ? `분할 생성됩니다` : `다운로드됩니다`) + ` (총 ${total.toLocaleString('ko-KR')}건)`;
messageEl.innerHTML = downloadType === 2 ? `<strong>(${currentChunk} / ${totalChunks})</strong> 파일 생성 중` : `엑셀 파일 생성 중`;
/* 작업 소요 시간 : 예상 시간 (1만건당 10초) */
const sec = Math.max(5, Math.ceil(total * 0.0012 * 1.2)); // 최소 5초 보장
const text = `예상 처리 시간은 약 ${sec >= 60 ? `${Math.floor(sec / 60)}분 ${sec % 60}초` : `${sec}초`}`;
document.getElementById('estimatedTimeText').innerText = text;
}
else if (status === "zipping")
{
summaryEl.innerHTML = `총 <strong>${totalChunks}</strong>개 파일이 압축파일로 생성됩니다`;
messageEl.innerHTML = `<strong>${totalChunks}</strong> 파일 압축하는 중`;
}
else if (status === "zippingError")
{
errorEl.innerHTML = message;
}
else if (status === "error")
{
summaryEl.innerHTML = `엑셀 파일 다운로드 실패`;
resultEl.innerHTML = '';
spinnerEl?.classList.add('is-hidden');
const parts = message.split(/<br\s*\/?>/i);
messageEl.innerHTML = parts[0] || '';
errorEl.innerHTML = parts.slice(1).join('<br>') || '';
// SSE 작업 닫기
eventSource?.close();
eventSource = null;
}
else if (status === "done")
{
// SSE 작업 닫기
eventSource?.close();
eventSource = null;
titleEl.textContent = '엑셀 파일 다운로드 완료';
messageEl.innerHTML = `<strong>총 ${total.toLocaleString('ko-KR')}건의 데이터 다운로드가 완료되었습니다!</strong>`;
spinnerEl?.classList.add('is-hidden');
let html = '<p>* 자동으로 다운로드가 되지 않았다면 아래 버튼을 클릭해주세요.</p>';
const baseUrl = `<?php echo G5_DATA_URL; ?>/member_list/<?php echo date('Ymdhis'); ?>/`; // 공통 URL 분리
if (zipFile) {
const url = `${filePath}/${zipFile}`;
html += `<a href="${url}" class="btn btn_03" download>압축파일 다운로드</a>`;
downloadBoxEl.innerHTML = html;
triggerAutoDownload(url, zipFile);
} else if (files?.length) {
files.forEach((file, index) => {
const url = `${filePath}/${file}`;
html += `<a class="btn btn_03" href="${url}" download>엑셀파일 다운로드 ${index + 1}</a>`;
});
downloadBoxEl.innerHTML = html;
if (files.length === 1) {
const url = `${filePath}/${files[0]}`;
triggerAutoDownload(url, files[0]);
} else {
summaryEl.innerHTML = `총 <strong>${totalChunks}</strong>개 파일이 생성되었습니다. 아래 버튼을 눌러 다운로드 받아주세요.`;
}
}
}
}
}
// SSE 오류 처리
function handleDownloadError() {
return function(e){
const errorMessage = e?.message || e?.data || '알 수 없는 오류가 발생했습니다.';
document.querySelector('.progress-summary').innerHTML = `엑셀 파일 다운로드 실패`;
document.querySelector('.progress-message').innerHTML = `엑셀 파일 다운로드에 실패하였습니다`;
document.querySelector('.progress-error').innerHTML = errorMessage;
document.querySelector('.loading-message').innerHTML = '';
document.querySelector('.progress-spinner').classList.add('is-hidden');
if (eventSource) {
eventSource.close();
eventSource = null;
}
}
}
// 자동 다운로드 실행
function triggerAutoDownload(url, filename) {
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
</script>
<?php
require_once './admin.tail.php';

View File

@ -0,0 +1,551 @@
<?php
$sub_menu = "200400";
require_once './_common.php';
require_once './member_list_exel.lib.php'; // 회원관리파일 공통 라이브러리 (상수, 검색 옵션 설정, SQL WHERE 등)
include_once(G5_LIB_PATH.'/PHPExcel.php');
check_demo();
auth_check_menu($auth, $sub_menu, 'w');
ini_set('memory_limit', '-1');
session_write_close(); // 세션 종료 및 잠금 해제 (백그라운드 작업을 위해 필요)
// 파라미터 수집 및 유효성 검사
$params = get_member_export_params();
if (!$params || !is_array($params)) {
member_export_send_progress("error", "데이터가 올바르게 전달되지 않아 작업에 실패하였습니다.");
member_export_write_log([], ['success' => false, 'error' => '데이터가 올바르게 전달되지 않아 작업에 실패하였습니다.']);
exit;
}
// 기존 생성된 엑셀 파일 삭제 - LOG 및 오늘 날짜 폴더 제외
$resultExcelDelete = member_export_delete();
// 서버 전송 이벤트(SSE)를 위한 헤더 설정
member_export_set_sse_headers();
// 모드 확인
$mode = isset($_GET['mode']) ? $_GET['mode'] : '';
if ($mode !== 'start') {
member_export_send_progress("error", "잘못된 요청 입니다.");
member_export_write_log($params, ['success' => false, 'error' => '잘못된 요청 입니다.']);
exit;
}
/**
* 회원 내보내기 처리 실행 (예외 처리 포함)
*/
try {
main_member_export($params);
}
catch (Exception $e)
{
// 에러 로그 저장 및 SSE 에러 전송
error_log("[Member Export Error] " . $e->getMessage());
member_export_send_progress("error", $e->getMessage());
member_export_write_log($params, ['success' => false, 'error' => $e->getMessage()]);
}
/**
* 메인 내보내기 프로세스
*/
function main_member_export($params)
{
$total = member_export_get_total_count($params);
if($total > MEMBER_EXPORT_MAX_SIZE){
throw new Exception("엑셀 다운로드 가능 범위(최대 " . number_format(MEMBER_EXPORT_MAX_SIZE) . "건)를 초과했습니다.<br>조건을 추가로 설정하신 후 다시 시도해 주세요.");
}
if($total <= 0){
throw new Exception("조회된 데이터가 없어 엑셀 파일을 생성할 수 없습니다.<br>조건을 추가로 설정하신 후 다시 시도해 주세요.");
}
$fileName = 'member_'.MEMBER_BASE_DATE;
$fileList = [];
$zipFileName = '';
if ($total > MEMBER_EXPORT_PAGE_SIZE) {
// 대용량 데이터 - 분할 처리
$pages = (int)ceil($total / MEMBER_EXPORT_PAGE_SIZE);
member_export_send_progress("progress", "", 2, $total, 0, $pages, 0);
for ($i = 1; $i <= $pages; $i++) {
$params['page'] = $i;
member_export_send_progress("progress", "", 2, $total, ($pages == $i ? $total : $i * MEMBER_EXPORT_PAGE_SIZE), $pages, $i);
try {
$data = member_export_get_data($params);
$fileList[] = member_export_create_excel($data, $fileName, $i);
} catch (Exception $e) {
throw new Exception("{$pages}개 중 {$i}번째 파일을 생성하지 못했습니다<br>" . $e->getMessage());
}
}
// 압축 파일 생성
if (count($fileList) > 1) {
member_export_send_progress("zipping", "", 2, $total, $total, $pages, $i);
$zipResult = member_export_create_zip($fileList, $fileName); // 압축 파일 생성
if($zipResult['error']){
member_export_write_log($params, ['success' => false, 'error' => $zipResult['error']]);
member_export_send_progress("zippingError", $zipResult['error']);
}
if ($zipResult && $zipResult['result']) {
member_export_delete($fileList); // 압축 후 엑셀 파일 제거
$zipFileName = $zipResult['zipFile'];
}
}
} else {
// 소용량 데이터 - 단일 파일
member_export_send_progress("progress", "", 1, $total, 0);
$data = member_export_get_data($params);
member_export_send_progress("progress", "", 1, $total, $total/2);
$fileList[] = member_export_create_excel($data, $fileName, 0);
member_export_send_progress("progress", "", 1, $total, $total);
}
member_export_write_log($params, ['success' => true, 'total' => $total, 'files' => $fileList, 'zip' => isset($zipFileName) ? $zipFileName : null]);
member_export_send_progress("done", "", 2, $total, $total, $pages, $pages, $fileList, $zipFileName);
}
/**
* 진행률 전송
*/
function member_export_send_progress($status, $message = "", $downloadType = 1, $total = 1, $current = 1, $totalChunks = 1, $currentChunk = 1, $files = [], $zipFile = '')
{
// 연결 상태 확인
if (connection_aborted()) return;
$data = [
'status' => $status,
'message' => $message,
'downloadType' => $downloadType,
'total' => $total,
'current' => $current,
'totalChunks' => $totalChunks,
'currentChunk' => $currentChunk,
'files' => $files,
'zipFile' => $zipFile,
'filePath' => G5_DATA_URL . "/" . MEMBER_BASE_DIR . "/" . MEMBER_BASE_DATE,
];
echo "data: " . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n\n";
// 더 안정적인 플러시
if (ob_get_level()) ob_end_flush();
flush();
}
/**
* 엑셀 내보내기 설정
*/
function member_export_get_config()
{
$type = 1;
$configs = [
1 => [
'title' => ["회원관리파일(일반)"],
'headers' => ['아이디', '이름', '닉네임', '휴대폰번호', '전화번호', '이메일', '주소', '회원권한', '포인트', '가입일', '차단',
'광고성 이메일 수신동의', '광고성 이메일 동의일자', '광고성 SMS/카카오톡 수신동의', '광고성 SMS/카카오톡 동의일자',
'마케팅목적의개인정보수집및이용동의', '마케팅목적의개인정보수집및이용동의일자', '개인정보제3자제공동의', '개인정보제3자제공동의일자'],
'fields' => ['mb_id', 'mb_name', 'mb_nick', 'mb_hp', 'mb_tel', 'mb_email', 'mb_addr1', 'mb_level', 'mb_point', 'mb_datetime', 'mb_intercept_date',
'mb_mailling','mb_mailling_date', 'mb_sms','mb_sms_date', 'mb_marketing_agree',
'mb_marketing_date', 'mb_thirdparty_agree', 'mb_thirdparty_date'],
'widths' => [20, 20, 20, 20, 20, 30, 30, 10, 15, 25, 10, 20, 25, 20, 25, 20, 25, 20, 25],
],
];
return isset($configs[$type]) ? $configs[$type] : $configs[1];
}
/**
* SSE 헤더 설정
*/
function member_export_set_sse_headers()
{
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no');
if (ob_get_level()) ob_end_flush();
ob_implicit_flush(true);
}
/**
* 엑셀 컬럼 문자 반환
*/
function member_export_column_char($i)
{
return chr(65 + $i);
}
/**
* 회원 데이터 조회
*/
function member_export_get_data($params)
{
global $g5;
$config = member_export_get_config();
$fields = $config['fields'];
$fields = array_unique($fields);
// SQL 변환 맵 (가공이 필요한 필드만 정의)
$sqlTransformMap = [
'mb_datetime' => "IF(mb_datetime = '0000-00-00 00:00:00', '', mb_datetime) AS mb_datetime",
'mb_intercept_date' => "IF(mb_intercept_date != '', '차단됨', '정상') AS mb_intercept_date",
'mb_sms' => "IF(mb_sms = '1', '동의', '미동의') AS mb_sms",
'mb_sms_date' => "IF(mb_sms != '1' OR mb_sms_date = '0000-00-00 00:00:00', '', mb_sms_date) AS mb_sms_date",
'mb_mailling' => "IF(mb_mailling = '1', '동의', '미동의') AS mb_mailling",
'mb_mailling_date' => "IF(mb_mailling != '1' OR mb_mailling_date = '0000-00-00 00:00:00', '', mb_mailling_date) AS mb_mailling_date",
'mb_marketing_agree' => "IF(mb_marketing_agree = '1', '동의', '미동의') AS mb_marketing_agree",
'mb_marketing_date' => "IF(mb_marketing_agree != '1' OR mb_marketing_date = '0000-00-00 00:00:00', '', mb_marketing_date) AS mb_marketing_date",
'mb_thirdparty_agree' => "IF(mb_thirdparty_agree = '1', '동의', '미동의') AS mb_thirdparty_agree",
'mb_thirdparty_date' => "IF(mb_thirdparty_agree != '1' OR mb_thirdparty_date = '0000-00-00 00:00:00', '', mb_thirdparty_date) AS mb_thirdparty_date",
];
// SQL 필드 생성
$sqlFields = [];
foreach ($fields as $field) {
$sqlFields[] = isset($sqlTransformMap[$field]) ? $sqlTransformMap[$field] : $field;
}
$field_list = implode(', ', $sqlFields);
$where = member_export_build_where($params);
$page = (int)(isset($params['page']) ? $params['page'] : 1);
if ($page < 1) $page = 1;
$offset = ($page - 1) * MEMBER_EXPORT_PAGE_SIZE;
$sql = "SELECT {$field_list} FROM {$g5['member_table']} {$where} ORDER BY mb_no DESC LIMIT {$offset}, " . MEMBER_EXPORT_PAGE_SIZE;
$result = sql_query($sql);
if (!$result) {
throw new Exception("데이터 조회에 실패하였습니다");
}
$excelData = [$config['title'], $config['headers']];
while ($row = sql_fetch_array($result)) {
$rowData = [];
foreach ($fields as $field) {
$rowData[] = isset($row[$field]) ? $row[$field] : '';
}
$excelData[] = $rowData;
}
return $excelData;
}
/**
* 엑셀 파일 생성
*/
function member_export_create_excel($data, $fileName, $index = 0)
{
$config = member_export_get_config();
if (!class_exists('PHPExcel')) {
error_log('[Member Export Error] PHPExcel 라이브러리를 찾을 수 없습니다.');
throw new Exception('파일 생성 중 내부 오류가 발생했습니다: PHPExcel 라이브러리를 찾을 수 없습니다.');
}
// 현재 설정값 백업
$currentCache = PHPExcel_Settings::getCacheStorageMethod();
// 캐싱 모드 설정 (엑셀 생성 전용)
$cacheMethods = [
PHPExcel_CachedObjectStorageFactory::cache_to_discISAM,
PHPExcel_CachedObjectStorageFactory::cache_in_memory_serialized
];
foreach ($cacheMethods as $method) {
if (PHPExcel_Settings::setCacheStorageMethod($method)) {
break;
}
}
try {
$excel = new PHPExcel();
$sheet = $excel->setActiveSheetIndex(0);
// 헤더 스타일 적용
$last_char = member_export_column_char(count($config['headers']) - 1);
$sheet->getStyle("A2:{$last_char}2")->applyFromArray([
'fill' => [
'type' => PHPExcel_Style_Fill::FILL_SOLID,
'startcolor' => ['rgb' => 'D9E1F2'], // 연파랑 배경
],
]);
// 셀 정렬 및 줄바꿈 설정
$sheet->getStyle("A:{$last_char}")->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER)->setWrapText(true);
// 컬럼 너비 설정
foreach ($config['widths'] as $i => $width) {
$sheet->getColumnDimension(member_export_column_char($i))->setWidth($width);
}
// 데이터 입력
$sheet->fromArray($data, NULL, 'A1');
// 디렉토리 확인
member_export_ensure_directory(MEMBER_EXPORT_DIR);
// 파일명 생성
$subname = $index == 0 ? 'all' : sprintf("%02d", $index);
$filename = $fileName . "_" . $subname . ".xlsx";
$filePath = MEMBER_EXPORT_DIR . "/" . $filename;
// 파일 저장
$writer = PHPExcel_IOFactory::createWriter($excel, 'Excel2007');
$writer->setPreCalculateFormulas(false);
$writer->save($filePath);
unset($excel, $sheet, $writer); // 생성 완료 후 메모리 해제
}
catch (Exception $e)
{
throw new Exception("엑셀 파일 생성에 실패하였습니다: " . $e->getMessage());
}
finally
{
// 캐싱 모드 원래 상태로 복원
if ($currentCache) {
PHPExcel_Settings::setCacheStorageMethod($currentCache);
}
}
return $filename;
}
/**
* 압축 파일 생성
*/
function member_export_create_zip($files, $zipFileName)
{
if (!class_exists('ZipArchive')) {
error_log('[Member Export Error] ZipArchive 클래스를 사용할 수 없습니다.');
return ['error' => '파일을 압축하는 중 문제가 발생했습니다. 개별 파일로 제공됩니다.<br>: ZipArchive 클래스를 사용할 수 없습니다.'];
}
member_export_ensure_directory(MEMBER_EXPORT_DIR);
$destinationZipPath = rtrim(MEMBER_EXPORT_DIR, "/") . "/" . $zipFileName . ".zip";
$zip = new ZipArchive();
if ($zip->open($destinationZipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
return ['error' => "파일을 압축하는 중 문제가 발생했습니다. 개별 파일로 제공됩니다."];
}
foreach ($files as $file) {
$filePath = MEMBER_EXPORT_DIR . "/" . $file;
if (file_exists($filePath)) {
$zip->addFile($filePath, basename($filePath));
}
}
$result = $zip->close();
return [
'result' => $result,
'zipFile' => $zipFileName . ".zip",
'zipPath' => $destinationZipPath,
];
}
/**
* 디렉토리 생성 및 확인
*/
function member_export_ensure_directory($dir)
{
if (!is_dir($dir)) {
if (!@mkdir($dir, G5_DIR_PERMISSION, true)) {
throw new Exception("디렉토리 생성 실패");
}
@chmod($dir, G5_DIR_PERMISSION);
}
if (!is_writable($dir)) {
throw new Exception("디렉토리 쓰기 권한 없음");
}
}
/**
* 파일 삭제 - 값이 있으면 해당 파일만 삭제, 없으면 디렉토리 내 모든 파일 삭제
* - 알집 생성 완료 시 엑셀 파일 제거
* - 작업 전 오늘 날짜 폴더 및 log 폴더를 제외한 나머지 파일 모두 제거
*/
function member_export_delete($fileList = [])
{
$cnt = 0;
// 파일 리스트가 있는 경우 -> 해당 파일만 삭제
if (!empty($fileList)) {
foreach ($fileList as $file) {
$filePath = rtrim(MEMBER_EXPORT_DIR, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file;
if (file_exists($filePath) && is_file($filePath) && @unlink($filePath)) {
$cnt++;
}
}
}
// 파일 리스트가 없는 경우 -> 디렉토리 내 모든 파일 삭제
else {
$files = glob(rtrim(G5_DATA_PATH . "/" . MEMBER_BASE_DIR, '/') . '/*');
function deleteFolder($dir) {
foreach (glob($dir . '/{.,}*', GLOB_BRACE) as $item) {
if (in_array(basename($item), ['.', '..'])) continue;
is_dir($item) ? deleteFolder($item) : unlink($item);
}
rmdir($dir);
}
foreach ($files as $file) {
$name = basename($file);
// log 폴더와 오늘 날짜로 시작하는 폴더는 제외
if ($name === 'log' || preg_match('/^' . date('Ymd') . '\d{6}$/', $name)) continue;
if (is_file($file) && pathinfo($file, PATHINFO_EXTENSION) !== 'log' && @unlink($file)) {
$cnt++;
} elseif (is_dir($file)) {
deleteFolder($file); // 재귀 폴더 삭제 함수 사용
$cnt++;
}
}
}
return $cnt;
}
/**
* 로그 작성
*/
function member_export_write_log($params, $result = [])
{
global $member;
$maxSize = 1024 * 1024 * 2; // 2MB
$maxFiles = 10; // 최대 로그 파일 수 (필요시 조정)
$username = isset($member['mb_id']) ? $member['mb_id'] : 'guest';
$datetime = date("Y-m-d H:i:s");
if (!is_dir(MEMBER_LOG_DIR)) {
@mkdir(MEMBER_LOG_DIR, G5_DIR_PERMISSION, true);
@chmod(MEMBER_LOG_DIR, G5_DIR_PERMISSION);
}
$logFiles = glob(MEMBER_LOG_DIR . "/export_log_*.log") ?: [];
// 최신 파일 기준 정렬 (최신 → 오래된)
usort($logFiles, fn($a, $b) => filemtime($b) - filemtime($a));
$latestLogFile = isset($logFiles[0]) ? $logFiles[0] : null;
// 용량 기준으로 새 파일 생성
if (!$latestLogFile || filesize($latestLogFile) >= $maxSize) {
$latestLogFile = MEMBER_LOG_DIR . "/export_log_" . date("YmdHi") . ".log";
file_put_contents($latestLogFile, '');
array_unshift($logFiles, $latestLogFile);
}
// 최대 파일 수 초과 시 오래된 파일 제거
if (count($logFiles) > $maxFiles) {
$filesToDelete = array_slice($logFiles, $maxFiles);
foreach ($filesToDelete as $file) {
@unlink($file);
}
}
$success = isset($result['success']) && $result['success'] === true;
$status = $success ? '성공' : '실패';
// 조건 정리
$condition = [];
// 검색 조건
if ($params['use_stx'] == 1 && !empty($params['stx'])) {
$sfl_list = get_export_config('sfl_list');
$label = isset($sfl_list[$params['sfl']]) ? $sfl_list[$params['sfl']] : '';
$condition[] = "검색({$params['stx_cond']}) : {$label} - {$params['stx']}";
}
// 레벨 조건
if ($params['use_level'] == 1 && ($params['level_start'] || $params['level_end'])) {
$condition[] = "레벨: {$params['level_start']}~{$params['level_end']}";
}
// 가입일 조건
if ($params['use_date'] == 1 && ($params['date_start'] || $params['date_end'])) {
$condition[] = "가입일: {$params['date_start']}~{$params['date_end']}";
}
// 포인트 조건
if ($params['use_point'] == 1 && $params['point'] !== '') {
$point_cond_map = get_export_config('point_cond_map');
$symbol = isset($point_cond_map[$params['point_cond']]) ? $point_cond_map[$params['point_cond']] : '≥';
$condition[] = "포인트 {$symbol} {$params['point']}";
}
// 휴대폰 여부
if ($params['use_hp_exist'] == 1) {
$condition[] = "휴대폰번호 있는 경우만";
}
// 광고 수신 동의
if ($params['ad_range_only'] == 1) {
$ad_range_list = get_export_config('ad_range_list');
$label = isset($ad_range_list[$params['ad_range_type']]) ? $ad_range_list[$params['ad_range_type']] : '';
$condition[] = "수신동의: 예 ({$label})";
if ($params['ad_range_type'] == "custom_period" && ($params['agree_date_start'] || $params['agree_date_end'])) {
$condition[] = "수신동의일: {$params['agree_date_start']}~{$params['agree_date_end']}";
}
if (in_array($params['ad_range_type'], ["month_confirm", "custom_period"])){
$channels = array_filter([
!empty($params['ad_mailling']) && (int)$params['ad_mailling'] === 1 ? '이메일' : null,
!empty($params['ad_sms']) && (int)$params['ad_sms'] === 1 ? 'SMS/카카오톡' : null,
]);
if ($channels) {
$condition[] = '수신채널: ' . implode(', ', $channels);
}
}
}
// 차단회원 처리
if ($params['use_intercept'] == 1) {
$intercept_list = get_export_config('intercept_list');
$label = isset($intercept_list[$params['intercept']]) ? $intercept_list[$params['intercept']] : '';
if ($label) $condition[] = $label;
}
$conditionStr = !empty($condition) ? implode(', ', $condition) : '없음';
$line1 = "[{$datetime}] [{$status}] 관리자: {$username}";
// 성공일 경우 추가 정보
if ($success) {
$total = isset($result['total']) ? $result['total'] : 0;
$fileCount = isset($result['zip']) ? 1 : count(isset($result['files']) ? $result['files'] : []);
$line1 .= " | 총 {$total}건 | 파일: {$fileCount}";
}
$logEntry = $line1 . PHP_EOL;
$logEntry .= "조건: {$conditionStr}" . PHP_EOL;
if (!$success && !empty($result['error'])) {
$logEntry .= "오류 메시지: {$result['error']}" . PHP_EOL;
}
$logEntry .= PHP_EOL;
// 파일에 기록
if (@file_put_contents($latestLogFile, $logEntry, FILE_APPEND | LOCK_EX) === false) {
error_log("[Member Export Error] 로그 파일 기록 실패: {$latestLogFile}");
}
}

View File

@ -0,0 +1,72 @@
<?php
$sub_menu = '100930';
include_once('./_common.php');
if ($is_admin != 'super')
alert('최고관리자만 접근 가능합니다.', G5_URL);
$g5['title'] = '회원관리파일 일괄삭제';
include_once(G5_ADMIN_PATH.'/admin.head.php');
?>
<div class="local_desc02 local_desc">
<p>
완료 메세지가 나오기 전에 프로그램의 실행을 중지하지 마십시오.
</p>
</div>
<?php
flush();
if (!$dir = @opendir(G5_DATA_PATH . '/member_list')) {
echo '<p>회원관리파일를 열지못했습니다.</p>';
}
$cnt = 0;
echo '<ul class="session_del">' . PHP_EOL;
$files = glob(G5_DATA_PATH . '/member_list/*');
$cnt = 0;
// 폴더 및 하위 파일 재귀 삭제 함수
function deleteFolder($folderPath) {
$items = glob($folderPath . '/*');
foreach ($items as $item) {
if (is_dir($item)) {
deleteFolder($item);
} else {
unlink($item);
}
}
rmdir($folderPath); // 폴더 자체 삭제
}
if (is_array($files)) {
foreach ($files as $member_list_file) {
// log 확장자가 아닌 파일/디렉토리 처리
$ext = strtolower(pathinfo($member_list_file, PATHINFO_EXTENSION));
$basename = basename($member_list_file);
if (is_file($member_list_file) && $ext !== 'log') {
unlink($member_list_file);
echo '<li>파일 삭제: ' . $member_list_file . '</li>' . PHP_EOL;
$cnt++;
} elseif (is_dir($member_list_file) && $basename !== 'log') {
deleteFolder($member_list_file);
echo '<li>폴더 삭제: ' . $member_list_file . '</li>' . PHP_EOL;
$cnt++;
}
flush();
if ($cnt % 10 == 0) {
echo PHP_EOL;
}
}
}
echo '<li>완료됨</li></ul>' . PHP_EOL;
echo '<div class="local_desc01 local_desc"><p><strong>회원관리파일 ' . $cnt . '건 삭제 완료됐습니다.</strong><br>프로그램의 실행을 끝마치셔도 좋습니다.</p></div>' . PHP_EOL;
?>
<?php
include_once(G5_ADMIN_PATH.'/admin.tail.php');

View File

@ -27,6 +27,30 @@ if ($_POST['act_button'] == "선택수정") {
$post_mb_sms = isset($_POST['mb_sms'][$k]) ? (int) $_POST['mb_sms'][$k] : 0;
$post_mb_open = isset($_POST['mb_open'][$k]) ? (int) $_POST['mb_open'][$k] : 0;
$agree_items = [];
// 광고성 이메일 수신동의 일자 추가
$post_mb_mailling_default = isset($_POST['mb_mailling_default'][$k]) ? (int) $_POST['mb_mailling_default'][$k] : 0;
$sql_mailling_date = "";
if ($post_mb_mailling_default != $post_mb_mailling) {
$sql_mailling_date = " , mb_mailling_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "광고성 이메일 수신(" . ($post_mb_mailling == 1 ? "동의" : "철회") . ")";
}
// 광고성 SMS/카카오톡 수신동의 일자 추가
$post_mb_sms_default = isset($_POST['mb_sms_default'][$k]) ? (int) $_POST['mb_sms_default'][$k] : 0;
$sql_sms_date = "";
if ($post_mb_sms_default != $post_mb_sms) {
$sql_sms_date = " , mb_sms_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "광고성 SMS/카카오톡 수신(" . ($post_mb_sms == 1 ? "동의" : "철회") . ")";
}
// 동의 로그 추가
$sql_agree_log = "";
if (!empty($agree_items)) {
$agree_log = "[".G5_TIME_YMDHIS.", 회원관리 선택수정] " . implode(' | ', $agree_items) . "\n";
$sql_agree_log .= " , mb_agree_log = CONCAT('{$agree_log}', IFNULL(mb_agree_log, ''))";
}
$mb_datas[] = $mb = get_member($_POST['mb_id'][$k]);
if (!(isset($mb['mb_id']) && $mb['mb_id'])) {
@ -50,6 +74,9 @@ if ($_POST['act_button'] == "선택수정") {
mb_open = '" . $post_mb_open . "',
mb_certify = '" . sql_real_escape_string($post_mb_certify) . "',
mb_adult = '{$mb_adult}'
{$sql_mailling_date}
{$sql_sms_date}
{$sql_agree_log}
where mb_id = '" . sql_real_escape_string($mb['mb_id']) . "' ";
sql_query($sql);
}

View File

@ -125,12 +125,15 @@ function pg_setting_check($is_print=false){
$msg = '';
$pg_msg = '';
$pg_test_conf_link = G5_ADMIN_URL.'/shop_admin/configform.php#de_card_test1';
if( $default['de_card_test'] ){
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유플러스';
} 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'] ){
$pg_msg = 'KG이니시스';
} else if ( $default['de_pg_service'] === 'nicepay' && $default['de_nicepay_mid'] && $default['de_nicepay_key'] ){
@ -147,7 +150,6 @@ function pg_setting_check($is_print=false){
}
if( $pg_msg ){
$pg_test_conf_link = G5_ADMIN_URL.'/shop_admin/configform.php#de_card_test1';
$msg .= '<div class="admin_pg_notice od_test_caution">(주의!) '.$pg_msg.' 결제의 결제 설정이 현재 테스트결제 로 되어 있습니다.<br>테스트결제시 실제 결제가 되지 않으므로, 쇼핑몰 운영중이면 반드시 실결제로 설정하여 운영하셔야 합니다.<br>아래 링크를 클릭하여 실결제로 설정하여 운영해 주세요.<br><a href="'.$pg_test_conf_link.'" class="pg_test_conf_link">'.$pg_test_conf_link.'</a></div>';
}
@ -170,6 +172,10 @@ function is_cancel_shop_pg_order($od){
$is_od_pg_cancel = true;
}
if($od['od_pg'] === 'toss' && in_array($od['od_settle_case'], array('계좌이체', '휴대폰'))) {
$is_od_pg_cancel = true;
}
return $is_od_pg_cancel;
}

View File

@ -215,6 +215,14 @@ if (! isset($default['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);
}
@ -636,17 +644,23 @@ if(!$default['de_kakaopay_cancelpwd']){
<?php echo G5_SHOP_URL; ?>/settle_kcp_common.php</td>
</tr>
<tr id="inicis_vbank_url" class="pg_vbank_url">
<th scope="row">KG이니시스 가상계좌 입금통보 URL</th>
<th scope="row">KG이니시스 가상계좌<br>입금통보 URL</th>
<td>
<?php echo help("KG이니시스 가상계좌 사용시 다음 주소를 <strong><a href=\"https://iniweb.inicis.com/\" target=\"_blank\">KG이니시스 관리자</a> &gt; 거래내역 &gt; 가상계좌 &gt; 입금통보방식선택 &gt; URL 수신 설정</strong>에 넣으셔야 상점에 자동으로 입금 통보됩니다."); ?>
<?php echo G5_SHOP_URL; ?>/settle_inicis_common.php</td>
</tr>
<tr id="nicepay_vbank_url" class="pg_vbank_url">
<th scope="row">NICEPAY 가상계좌 입금통보 URL</th>
<th scope="row">NICEPAY 가상계좌<br>입금통보 URL</th>
<td>
<?php echo help("NICEPAY 가상계좌 사용시 다음 주소를 <strong><a href=\"https://npg.nicepay.co.kr/\" target=\"_blank\">NICEPAY 관리자</a> &gt; 가맹점관리자페이지 설정 (메인화면 → 가맹점정보 클릭)</strong>에 넣으셔야 상점에 자동으로 입금 통보됩니다."); ?>
<?php echo G5_SHOP_URL; ?>/settle_nicepay_common.php</td>
</tr>
<tr id="toss_vbank_url" class="pg_vbank_url">
<th scope="row">토스페이먼츠 가상계좌<br>입금통보 URL</th>
<td>
<?php echo help("토스페이먼츠 가상계좌 사용시 다음 주소를 <strong><a href=\"https://app.tosspayments.com/\" target=\"_blank\">토스페이먼츠 상점관리자</a> &gt; 개발자센터 &gt; 웹훅 &gt; 웹훅 등록하기에 URL</strong>에 넣으시고, <strong>구독할 이벤트를 [DEPOSIT_CALLBACK]</strong>을 선택하셔야 상점에 자동으로 입금 통보됩니다."); ?>
<?php echo G5_SHOP_URL; ?>/settle_toss_common.php</td>
</tr>
<tr>
<th scope="row"><label for="de_hp_use">휴대폰결제사용</label></th>
<td>
@ -687,6 +701,7 @@ if(!$default['de_kakaopay_cancelpwd']){
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="de_taxsave_use">현금영수증<br>발급사용</label></th>
<td>
@ -775,7 +790,8 @@ if(!$default['de_kakaopay_cancelpwd']){
<?php echo help('쇼핑몰에서 사용할 결제대행사를 선택합니다.'); ?>
<ul class="de_pg_tab">
<li class="<?php if($default['de_pg_service'] == 'kcp') echo 'tab-current'; ?>"><a href="#kcp_info_anchor" data-value="kcp" title="NHN KCP 선택하기" >NHN KCP</a></li>
<li class="<?php if($default['de_pg_service'] == 'lg') echo 'tab-current'; ?>"><a href="#lg_info_anchor" data-value="lg" title="토스페이먼츠 선택하기">토스페이먼츠</a></li>
<li class="<?php if($default['de_pg_service'] == 'lg') echo 'tab-current'; ?>"><a href="#lg_info_anchor" data-value="lg" title="토스페이먼츠(구버전) 선택하기">토스페이먼츠(구버전)</a></li>
<li class="<?php if($default['de_pg_service'] == 'toss') echo 'tab-current'; ?>"><a href="#lg_info_anchor" data-value="toss" title="토스페이먼츠 선택하기">토스페이먼츠</a></li>
<li class="<?php if($default['de_pg_service'] == 'inicis') echo 'tab-current'; ?>"><a href="#inicis_info_anchor" data-value="inicis" title="KG이니시스 선택하기">KG이니시스</a></li>
<li class="<?php if($default['de_pg_service'] == 'nicepay') echo 'tab-current'; ?>"><a href="#nicepay_info_anchor" data-value="nicepay" title="NICEPAY 선택하기">NICEPAY</a></li>
</ul>
@ -833,12 +849,26 @@ if(!$default['de_kakaopay_cancelpwd']){
</td>
</tr>
<tr class="pg_info_fld lg_info_fld">
<th scope="row"><label for="cf_lg_mert_key">토스페이먼츠 MERT KEY</label></th>
<th scope="row"><label for="cf_lg_mert_key">토스페이먼츠(구버전) MERT KEY</label></th>
<td>
<?php echo help("토스페이먼츠 상점MertKey는 상점관리자 -> 계약정보 -> 상점정보관리에서 확인하실 수 있습니다.\n예) 95160cce09854ef44d2edb2bfb05f9f3\n<a href=\"".G5_ADMIN_URL."/config_form.php#anc_cf_cert\">기본환경설정 &gt; 본인확인</a> 설정의 토스페이먼츠 MERT KEY와 동일합니다."); ?>
<?php echo help("토스페이먼츠(구버전) 상점 MertKey는 상점관리자 -> 개발자센터 -> API키 -> 머트 키에서 확인하실 수 있습니다.\n예) 95160cce09854ef44d2edb2bfb05f9f3"); ?>
<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>
<tr class="pg_info_fld lg_info_fld_v2">
<th scope="row"><label for="cf_toss_client_key">토스페이먼츠 API Client Key</label></th>
<td>
<?php echo help("토스페이먼츠 API 클라이언트 키는 상점관리자 -> 개발자센터 -> API키 -> 클라이언트 키에서 확인하실 수 있습니다. 예) live_ck_tosspayment\n실결제용 [라이브] 키와 테스트용 [테스트] 키는 서로 다르므로, <b>테스트로 결제시에는 [테스트] 키</b>로 변경하여 사용해주시기 바랍니다. 예) 테스트 키: test_ck_tosspayment"); ?>
<input type="text" name="cf_toss_client_key" value="<?php echo get_sanitize_input($config['cf_toss_client_key']); ?>" id="cf_toss_client_key" class="frm_input " size="40" maxlength="50">
</td>
</tr>
<tr class="pg_info_fld lg_info_fld_v2">
<th scope="row"><label for="cf_toss_secret_key">토스페이먼츠 API Secret Key</label></th>
<td>
<?php echo help("토스페이먼츠 API 시크릿 키는 상점관리자 -> 개발자센터 -> API키 -> 시크릿 키에서 확인하실 수 있습니다. 예) live_sk_tosspayment\n실결제용 [라이브] 키와 테스트용 [테스트] 키는 서로 다르므로, <b>테스트로 결제시에는 [테스트] 키</b>로 변경하여 사용해주시기 바랍니다. 예) 테스트 키: test_sk_tosspayment"); ?>
<input type="text" name="cf_toss_secret_key" value="<?php echo get_sanitize_input($config['cf_toss_secret_key']); ?>" id="cf_toss_secret_key" class="frm_input " size="40" maxlength="50">
</td>
</tr>
<tr class="pg_info_fld inicis_info_fld" id="inicis_info_anchor">
<th scope="row">
<label for="de_inicis_mid">KG이니시스 상점아이디</label><br>
@ -1070,6 +1100,9 @@ if(!$default['de_kakaopay_cancelpwd']){
<a href="https://app.tosspayments.com/" target="_blank" class="btn_frmline">실결제 관리자</a>
<a href="https://pgweb.tosspayments.com/tmert" target="_blank" class="btn_frmline">테스트 관리자</a>
</div>
<div class="scf_cardtest toss_cardtest">
<a href="https://app.tosspayments.com/" target="_blank" class="btn_frmline">상점 관리자</a>
</div>
<div class="scf_cardtest inicis_cardtest">
<a href="https://iniweb.inicis.com/" target="_blank" class="btn_frmline">상점 관리자</a>
</div>
@ -1095,6 +1128,9 @@ if(!$default['de_kakaopay_cancelpwd']){
<ul id="lg_cardtest_tip" class="scf_cardtest_tip_adm scf_cardtest_tip_adm_hide">
<li>테스트결제의 <a href="https://pgweb.tosspayments.com/tmert" target="_blank">상점관리자</a> 로그인 정보는 토스페이먼츠 상점아이디 첫 글자에 t를 추가해서 로그인하시기 바랍니다. 예) tsi_lguplus</li>
</ul>
<ul id="toss_cardtest_tip" class="scf_cardtest_tip_adm scf_cardtest_tip_adm_hide">
<li>테스트 결제 시 <a href="https://app.tosspayments.com/" target="_blank">상점관리자</a> 로그인 정보는 실결제용 키와는 다르니 반드시 <b>[테스트] API 연동 키</b>로 로그인해야 합니다. 예) test_ck_toss</li>
</ul>
<ul id="inicis_cardtest_tip" class="scf_cardtest_tip_adm scf_cardtest_tip_adm_hide">
<li><b>일반결제</b>의 테스트 사이트 mid는 <b>INIpayTest</b> 이며, <b>에스크로 결제</b>의 테스트 사이트 mid는 <b>iniescrow0</b> 입니다.</li>
</ul>
@ -1769,7 +1805,11 @@ 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 = "토스페이먼츠";
pg_msg = "토스페이먼츠(구버전)";
}
} 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 ){
msg += "(주의!) 토스페이먼츠 결제의 결제 설정이 현재 테스트결제로 되어 있습니다.\n상점 API키를 [테스트]키로 설정한 후 테스트결제를 진행해주세요.\n쇼핑몰 운영중이면 반드시 실결제 전환 및 [라이브]키로 설정하여 운영하셔야 합니다.\n실결제로 변경하려면 결제설정 탭 -> 결제 테스트에서 실결제를 선택해 주세요.\n정말로 테스트결제로 설정하시겠습니까?";
}
} else if ( f.de_pg_service.value == "inicis" ) {
if( f.de_inicis_mid.value && f.de_inicis_sign_key.value && parseInt(f.de_card_test.value) > 0 ){

View File

@ -159,6 +159,8 @@ $check_sanitize_keys = array(
'de_kcp_site_key', //NHN KCP SITE KEY
'cf_lg_mid', //LG유플러스 상점아이디
'cf_lg_mert_key', //LG유플러스 MERT KEY
'cf_toss_client_key', //토스페이먼츠 MERT KEY
'cf_toss_secret_key', //토스페이먼츠 MERT KEY
'de_inicis_mid', //KG이니시스 상점아이디
'de_inicis_iniapi_key', //KG이니시스 INIAPI KEY
'de_inicis_iniapi_iv', //KG이니시스 INIAPI IV
@ -465,7 +467,9 @@ $sql = " update {$g5['config_table']}
cf_icode_server_port = '{$_POST['cf_icode_server_port']}',
cf_icode_token_key = '{$cf_icode_token_key}',
cf_lg_mid = '{$cf_lg_mid}',
cf_lg_mert_key = '{$cf_lg_mert_key}' ";
cf_lg_mert_key = '{$cf_lg_mert_key}',
cf_toss_client_key = '{$cf_toss_client_key}',
cf_toss_secret_key = '{$cf_toss_secret_key}' ";
sql_query($sql);
run_event('shop_admin_configformupdate');

View File

@ -235,6 +235,14 @@ sql_query(" delete from {$g5['g5_shop_event_item_table']} where it_id = '$it_id'
// 선택옵션
sql_query(" delete from {$g5['g5_shop_item_option_table']} where io_type = '0' and it_id = '$it_id' "); // 기존선택옵션삭제
// 금지할 패턴 목록
$forbidden_patterns = array(
'/<\s*script/i', // <script>
'/<\s*iframe/i', // <iframe>
'/on\w+\s*=/i', // onclick=, onerror= 등 이벤트 핸들러
'/javascript:/i' // javascript: 프로토콜
);
$option_count = (isset($_POST['opt_id']) && is_array($_POST['opt_id'])) ? count($_POST['opt_id']) : array();
$it_option_subject = '';
$it_supply_subject = '';
@ -243,8 +251,18 @@ if($option_count) {
// 옵션명
$opt1_cnt = $opt2_cnt = $opt3_cnt = 0;
for($i=0; $i<$option_count; $i++) {
$post_opt_id = isset($_POST['opt_id'][$i]) ? preg_replace(G5_OPTION_ID_FILTER, '', strip_tags($_POST['opt_id'][$i])) : '';
$post_opt_id = isset($_POST['opt_id'][$i]) ? $_POST['opt_id'][$i] : '';
foreach ($forbidden_patterns as $pattern) {
if (preg_match($pattern, $post_opt_id)) {
$post_opt_id = '';
$_POST['opt_id'][$i] = '';
continue 2;
}
}
$post_opt_id = preg_replace(G5_OPTION_ID_FILTER, '', strip_tags($post_opt_id));
$opt_val = explode(chr(30), $post_opt_id);
if(isset($opt_val[0]) && $opt_val[0])
$opt1_cnt++;
@ -271,8 +289,18 @@ if($supply_count) {
// 추가옵션명
$arr_spl = array();
for($i=0; $i<$supply_count; $i++) {
$post_spl_id = isset($_POST['spl_id'][$i]) ? preg_replace(G5_OPTION_ID_FILTER, '', strip_tags($_POST['spl_id'][$i])) : '';
$post_spl_id = isset($_POST['spl_id'][$i]) ? $_POST['spl_id'][$i] : '';
foreach ($forbidden_patterns as $pattern) {
if (preg_match($pattern, $post_spl_id)) {
$post_spl_id = '';
$_POST['spl_id'][$i] = '';
continue 2;
}
}
$post_spl_id = preg_replace(G5_OPTION_ID_FILTER, '', strip_tags($post_spl_id));
$spl_val = explode(chr(30), $post_spl_id);
if(!in_array($spl_val[0], $arr_spl))
$arr_spl[] = $spl_val[0];

View File

@ -84,11 +84,11 @@ if($po_run) {
?>
<tr>
<td class="td_chk">
<input type="hidden" name="opt_id[]" value="<?php echo $opt_id; ?>">
<input type="hidden" name="opt_id[]" value="<?php echo get_text($opt_id); ?>">
<label for="opt_chk_<?php echo $i; ?>" class="sound_only"></label>
<input type="checkbox" name="opt_chk[]" id="opt_chk_<?php echo $i; ?>" value="1">
</td>
<td class="opt-cell"><?php echo $opt_1; if ($opt_2_len) echo ' <small>&gt;</small> '.$opt_2; if ($opt_3_len) echo ' <small>&gt;</small> '.$opt_3; ?></td>
<td class="opt-cell"><?php echo get_text($opt_1); if ($opt_2_len) echo ' <small>&gt;</small> '.get_text($opt_2); if ($opt_3_len) echo ' <small>&gt;</small> '.get_text($opt_3); ?></td>
<td class="td_numsmall">
<label for="opt_price_<?php echo $i; ?>" class="sound_only"></label>
<input type="text" name="opt_price[]" value="<?php echo $opt_price; ?>" id="opt_price_<?php echo $i; ?>" class="frm_input" size="9">

View File

@ -170,13 +170,16 @@ function fitemstocksms_submit(f)
return false;
}
if(document.pressed == "선택삭제") {
if(!confirm("선택한 자료를 정말 삭제하시겠습니까?")) {
return false;
}
}
var action = document.pressed;
return true;
switch (action) {
case "선택삭제":
return confirm("선택한 자료를 정말 삭제하시겠습니까?");
case "선택SMS전송":
return confirm("선택한 자료에 대해서 SMS로 재입고 알림을 전송하시겠습니까?");
default:
return true;
}
}
</script>

View File

@ -55,12 +55,12 @@ if($ps_run) {
?>
<tr>
<td class="td_chk">
<input type="hidden" name="spl_id[]" value="<?php echo $spl_id; ?>">
<label for="spl_chk_<?php echo $i; ?>" class="sound_only"><?php echo $spl_subject.' '.$spl; ?></label>
<input type="hidden" name="spl_id[]" value="<?php echo get_text($spl_id); ?>">
<label for="spl_chk_<?php echo $i; ?>" class="sound_only"><?php echo get_text($spl_subject.' '.$spl); ?></label>
<input type="checkbox" name="spl_chk[]" id="spl_chk_<?php echo $i; ?>" value="1">
</td>
<td class="spl-subject-cell"><?php echo $spl_subject; ?></td>
<td class="spl-cell"><?php echo $spl; ?></td>
<td class="spl-subject-cell"><?php echo get_text($spl_subject); ?></td>
<td class="spl-cell"><?php echo get_text($spl); ?></td>
<td class="td_numsmall">
<label for="spl_price_<?php echo $i; ?>" class="sound_only">상품금액</label>
<input type="text" name="spl_price[]" value="<?php echo $spl_price; ?>" id="spl_price_<?php echo $i; ?>" class="frm_input" size="5">

View File

@ -607,7 +607,7 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
switch($od['od_pg']) {
case 'lg':
$pg_url = 'https://app.tosspayments.com';
$pg_test = '토스페이먼츠';
$pg_test = '토스페이먼츠(구버전)';
if ($default['de_card_test']) {
$pg_url = 'https://pgweb.tosspayments.com/tmert';
$pg_test .= ' 테스트 ';
@ -625,6 +625,10 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
$pg_url = 'https://npg.nicepay.co.kr/';
$pg_test = 'NICEPAY';
break;
case 'toss':
$pg_url = 'https://app.tosspayments.com';
$pg_test = '토스페이먼츠 ';
break;
default:
$pg_url = 'http://admin8.kcp.co.kr';
$pg_test = 'KCP';
@ -737,6 +741,8 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
break;
}
$cash_receipt_script = 'javascript:showCashReceipts(\''.$LGD_MID.'\',\''.$od['od_id'].'\',\''.$od['od_casseqno'].'\',\''.$trade_type.'\',\''.$CST_PLATFORM.'\');';
} else if($od['od_pg'] == 'toss') {
$cash_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/mids/si_'.$config['cf_lg_mid'].'/orders/'.$od['od_id'].'/cash-receipt?ref=dashboard\',\'receipt\',\'width=430,height=700\');';
} else if($od['od_pg'] == 'inicis') {
$cash = unserialize($od['od_cash_info']);
$cash_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/Cash_mCmReceipt.jsp?noTid='.$cash['TID'].'&clpaymethod=22\',\'showreceipt\',\'width=380,height=540,scrollbars=no,resizable=no\');';
@ -825,6 +831,7 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
<input type="checkbox" name="od_sms_ipgum_check" id="od_sms_ipgum_check">
<label for="od_sms_ipgum_check">SMS 입금 문자 및 알림전송</label>
<br>
<?php } ?>
<input type="text" name="od_deposit_name" value="<?php echo get_text($od['od_deposit_name']); ?>" id="od_deposit_name" class="frm_input">
</td>
@ -939,6 +946,7 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
<input type="checkbox" name="od_sms_baesong_check" id="od_sms_baesong_check">
<label for="od_sms_baesong_check">SMS 배송 문자 및 알림전송</label>
<br>
<?php } ?>
<input type="text" name="od_invoice" value="<?php echo $od['od_invoice']; ?>" id="od_invoice" class="frm_input">
</td>

View File

@ -251,6 +251,10 @@ if (in_array($_POST['ct_status'], $status_cancel)) {
$pg_res_msg = $xpay->Response_Msg();
}
break;
case 'toss':
$cancel_msg = '쇼핑몰 운영자 승인 취소';
include_once(G5_SHOP_PATH.'/toss/toss_cancel.php');
break;
case 'inicis':
include_once(G5_SHOP_PATH.'/settle_inicis.inc.php');
$cancel_msg = '쇼핑몰 운영자 승인 취소';

View File

@ -210,6 +210,8 @@ if(!sql_query(" select pp_cash from {$g5['g5_shop_personalpay_table']} limit 1 "
break;
}
$cash_receipt_script = 'javascript:showCashReceipts(\''.$LGD_MID.'\',\''.$pp['pp_id'].'\',\''.$pp['pp_casseqno'].'\',\''.$trade_type.'\',\''.$CST_PLATFORM.'\');';
} else if($pp['pp_pg'] == 'toss') {
$cash_receipt_script = 'window.open(\'https://dashboard.tosspayments.com/receipt/mids/si_'.$config['cf_lg_mid'].'/orders/'.$pp['pp_id'].'/cash-receipt?ref=dashboard\',\'receipt\',\'width=430,height=700\');';
} else if($pp['pp_pg'] == 'inicis') {
$cash = unserialize($pp['pp_cash_info']);
$cash_receipt_script = 'window.open(\'https://iniweb.inicis.com/DefaultWebApp/mall/cr/cm/Cash_mCmReceipt.jsp?noTid='.$cash['TID'].'&clpaymethod=22\',\'showreceipt\',\'width=380,height=540,scrollbars=no,resizable=no\');';

View File

@ -5,6 +5,16 @@ include_once(G5_ADMIN_PATH.'/admin.lib.php');
include_once(G5_SMS5_PATH.'/sms5.lib.php');
if (!strstr($_SERVER['SCRIPT_NAME'], 'install.php')) {
// SMS5 테이블 G5_TABLE_PREFIX 적용
if($g5['sms5_prefix'] != 'sms5_' && sql_num_rows(sql_query("show tables like 'sms5_config'")))
{
echo '<script>
alert("기존 SMS5 테이블을 sms5 prefix 기준으로 변경합니다.\n(DB 업그레이드에서 자동 적용됩니다.)");
location.href = "'.G5_ADMIN_URL.'/dbupgrade.php";
</script>';
exit;
}
if(!sql_num_rows(sql_query(" show tables like '{$g5['sms5_config_table']}' ")))
goto_url('install.php');