feat: DB업그레이드에서 쇼핑몰 후설치 지원

쇼핑몰 미설치 사이트에서 최신 쇼핑몰 스키마를 명시적으로 추가한다. 기존 DB 설정을 보존하고 중복 설치를 차단하며, 기본 SMS 문구 길이를 보정한다.

격리된 접두어로 후설치·설정 보존·중복 실행 차단을 검증한다.
This commit is contained in:
whitedot
2026-09-09 07:58:05 +00:00
parent 5bdbf360b0
commit 1da82df500
5 changed files with 404 additions and 12 deletions
+39 -10
View File
@@ -2,6 +2,7 @@
$sub_menu = '100410';
include_once('./_common.php');
include_once(G5_LIB_PATH . '/migration.lib.php');
include_once(G5_LIB_PATH . '/shop_install.lib.php');
auth_check_menu($auth, $sub_menu, 'r');
@@ -11,13 +12,19 @@ if ($is_admin != 'super') {
$is_execute = isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST';
$migration_result = array('success' => true, 'applied' => 0, 'skipped' => 0, 'errors' => array());
$shop_install_result = null;
if ($is_execute) {
check_demo();
check_admin_token();
$migration_id = isset($_POST['migration_id']) ? trim($_POST['migration_id']) : '';
$migration_result = g5_migration_run($migration_id);
$migration_result = run_replace('admin_dbupgrade_result', $migration_result);
$action = isset($_POST['action']) ? trim($_POST['action']) : 'migrate';
if ($action === 'install_shop') {
$shop_install_result = g5_shop_install_run();
} else {
$migration_id = isset($_POST['migration_id']) ? trim($_POST['migration_id']) : '';
$migration_result = g5_migration_run($migration_id);
$migration_result = run_replace('admin_dbupgrade_result', $migration_result);
}
}
$migration_statuses = g5_migration_status();
@@ -28,7 +35,11 @@ foreach ($migration_statuses as $migration_status) {
}
}
if (!$is_execute) {
if ($shop_install_result !== null && $shop_install_result['success']) {
alert('쇼핑몰 설치가 완료되었습니다.', G5_ADMIN_URL . '/dbupgrade.php');
} elseif ($shop_install_result !== null) {
$db_upgrade_msg = '쇼핑몰 설치에 실패했습니다. 오류 내용을 확인해 주십시오.';
} elseif (!$is_execute) {
$db_upgrade_msg = $pending_count ? '적용하지 않은 DB 마이그레이션이 있습니다. 아래 내용을 확인한 뒤 실행해 주십시오.' : 'DB 마이그레이션이 모두 적용되어 있습니다.';
} elseif (!$migration_result['success']) {
$db_upgrade_msg = 'DB 마이그레이션에 실패했습니다. 오류 내용을 확인해 주십시오.';
@@ -40,13 +51,18 @@ if (!$is_execute) {
$g5['title'] = 'DB 업그레이드';
include_once('./admin.head.php');
$dbupgrade_token = get_admin_token();
?>
<div class="local_desc01 local_desc">
<p><?php echo $db_upgrade_msg; ?></p>
</div>
<?php if ($migration_result['errors']) { ?>
<?php if ($shop_install_result !== null && !$shop_install_result['success']) { ?>
<div class="local_desc01 local_desc" style="color:#d00">
<p><?php echo htmlspecialchars($shop_install_result['error'], ENT_QUOTES, 'UTF-8'); ?></p>
</div>
<?php } elseif ($migration_result['errors']) { ?>
<div class="local_desc01 local_desc" style="color:#d00">
<?php foreach ($migration_result['errors'] as $migration_error) { ?>
<p><?php echo htmlspecialchars($migration_error, ENT_QUOTES, 'UTF-8'); ?></p>
@@ -54,11 +70,23 @@ include_once('./admin.head.php');
</div>
<?php } ?>
<form method="post" action="<?php echo G5_ADMIN_URL; ?>/dbupgrade.php" onsubmit="return confirm('선택한 DB 마이그레이션을 실행하시겠습니까? 실행 전 데이터베이스 백업을 권장합니다.');">
<input type="hidden" name="token" value="<?php echo get_admin_token(); ?>">
<div class="dbupgrade_all_actions">
<div class="dbupgrade_all_actions">
<?php if (!defined('G5_USE_SHOP') || !G5_USE_SHOP) { ?>
<form method="post" action="<?php echo G5_ADMIN_URL; ?>/dbupgrade.php" onsubmit="return confirm('쇼핑몰을 설치하시겠습니까? 설치 전 데이터베이스와 data/dbconfig.php 백업을 권장합니다.');">
<input type="hidden" name="token" value="<?php echo $dbupgrade_token; ?>">
<input type="hidden" name="action" value="install_shop">
<button type="submit" class="btn_submit">쇼핑몰 설치</button>
</form>
<?php } ?>
<form method="post" action="<?php echo G5_ADMIN_URL; ?>/dbupgrade.php" onsubmit="return confirm('선택한 DB 마이그레이션을 실행하시겠습니까? 실행 전 데이터베이스 백업을 권장합니다.');">
<input type="hidden" name="token" value="<?php echo $dbupgrade_token; ?>">
<input type="hidden" name="action" value="migrate">
<button type="submit" name="migration_id" value="" class="btn_submit">전체 마이그레이션 실행</button>
</div>
</form>
</div>
<form method="post" action="<?php echo G5_ADMIN_URL; ?>/dbupgrade.php" onsubmit="return confirm('선택한 DB 마이그레이션을 실행하시겠습니까? 실행 전 데이터베이스 백업을 권장합니다.');">
<input type="hidden" name="token" value="<?php echo $dbupgrade_token; ?>">
<input type="hidden" name="action" value="migrate">
<div class="tbl_head01 tbl_wrap">
<table id="dbupgrade_migration_table">
<caption>버전형 DB 마이그레이션 목록</caption>
@@ -101,7 +129,8 @@ include_once('./admin.head.php');
</form>
<style>
.dbupgrade_all_actions {display:block;width:100%;margin:0 0 20px;overflow:hidden;text-align:right}
.dbupgrade_all_actions {display:flex;width:100%;margin:0 0 20px;justify-content:flex-end;gap:5px}
.dbupgrade_all_actions form {margin:0}
.dbupgrade_all_actions .btn_submit {display:inline-block;float:none;position:static;width:auto;height:30px;margin:0;padding:0 15px;border:0}
.dbupgrade_sort {width:100%; border:0; background:transparent; color:inherit; font:inherit; cursor:pointer}
#dbupgrade_migration_table td.dbupgrade_description {text-align:left}
+4 -2
View File
@@ -12,10 +12,12 @@ $excluded = array(
'data/',
'install/',
'migrations/',
'bin/check-migration-runner.php'
'bin/check-migration-runner.php',
'bin/check-shop-install.php'
);
$lifecycle_ddl_files = array(
'adm/board_delete.inc.php'
'adm/board_delete.inc.php',
'lib/shop_install.lib.php'
);
$errors = array();
+151
View File
@@ -0,0 +1,151 @@
#!/usr/bin/env php
<?php
if (PHP_SAPI !== 'cli') {
exit("CLI에서만 실행할 수 있습니다.\n");
}
$root = dirname(dirname(__FILE__));
chdir($root);
define('G5_IS_CLI', true);
$_SERVER['SCRIPT_FILENAME'] = $root . '/index.php';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['REQUEST_URI'] = '/';
$_SERVER['SERVER_NAME'] = 'localhost';
$_SERVER['SERVER_PORT'] = 80;
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['REQUEST_METHOD'] = 'CLI';
require_once $root . '/common.php';
require_once G5_LIB_PATH . '/migration.lib.php';
require_once G5_LIB_PATH . '/shop_install.lib.php';
function g5_shop_install_test_default_values($source)
{
preg_match_all("/\\b(de_[a-z0-9_]+)\\s*=\\s*'([^']*)'/i", $source, $matches, PREG_SET_ORDER);
$values = array();
foreach ($matches as $match) {
$values[$match[1]] = $match[2];
}
return $values;
}
$install_source = @file_get_contents(G5_PATH . '/install/install_db.php');
$shop_install_source = @file_get_contents(G5_LIB_PATH . '/shop_install.lib.php');
if ($install_source === false || $shop_install_source === false ||
!preg_match('/insert into `\{\$g5_shop_prefix\}default`\s+set(.*?)\n\s+";/s', $install_source, $install_defaults) ||
!preg_match('/\$sql = "INSERT INTO `\{\$table\}` SET(.*?)";\n\s+return sql_query/s', $shop_install_source, $shop_defaults)) {
fwrite(STDERR, "[실패] 기본 설치와 후설치의 쇼핑몰 설정을 비교할 수 없습니다.\n");
exit(1);
}
preg_match_all('/\b(de_[a-z0-9_]+)\s*=/i', $install_defaults[1], $install_fields);
preg_match_all('/\b(de_[a-z0-9_]+)\s*=/i', $shop_defaults[1], $shop_fields);
$install_fields = array_values(array_unique($install_fields[1]));
$shop_fields = array_values(array_unique($shop_fields[1]));
sort($install_fields, SORT_STRING);
sort($shop_fields, SORT_STRING);
if ($install_fields !== $shop_fields) {
fwrite(STDERR, "[실패] 기본 설치와 후설치의 쇼핑몰 기본 설정 필드가 다릅니다.\n");
exit(1);
}
$install_values = g5_shop_install_test_default_values($install_defaults[1]);
$shop_values = g5_shop_install_test_default_values($shop_defaults[1]);
$install_variables = array(
'$ssimg_width' => '160', '$ssimg_height' => '160', '$simg_width' => '215', '$simg_height' => '215',
'$mimg_width' => '230', '$mimg_height' => '230', '$mmimg_width' => '300', '$mmimg_height' => '300',
'$msimg_width' => '80', '$msimg_height' => '80', '$list_img_width' => '225', '$list_img_height' => '225'
);
foreach ($install_values as $field => $value) {
$value = strtr($value, $install_variables);
if (!isset($shop_values[$field]) || $shop_values[$field] !== $value) {
fwrite(STDERR, '[실패] 기본 설치와 후설치의 쇼핑몰 기본 설정값이 다릅니다: ' . $field . "\n");
exit(1);
}
}
$suffix = substr(md5(getmypid() . microtime()), 0, 10);
$prefix = G5_TABLE_PREFIX . 'shop_install_test_' . $suffix . '_';
$data_path = sys_get_temp_dir() . '/g5-shop-install-' . $suffix;
$config_file = $data_path . '/dbconfig.php';
$tables = g5_shop_install_table_names($prefix);
function g5_shop_install_test_cleanup()
{
global $tables, $prefix, $data_path;
foreach ($tables as $name) {
sql_query("DROP TABLE IF EXISTS `" . str_replace('`', '``', $prefix . $name) . "`", false);
}
if (is_dir($data_path)) {
$dirs = array('banner', 'common', 'event', 'item');
foreach ($dirs as $dir) {
$path = $data_path . '/' . $dir;
if (is_dir($path)) {
$files = scandir($path);
foreach ($files as $file) {
if ($file !== '.' && $file !== '..') {
@unlink($path . '/' . $file);
}
}
@rmdir($path);
}
}
@unlink($data_path . '/dbconfig.php');
@rmdir($data_path);
}
}
function g5_shop_install_test_fail($message)
{
fwrite(STDERR, '[실패] ' . $message . "\n");
exit(1);
}
register_shutdown_function('g5_shop_install_test_cleanup');
g5_shop_install_test_cleanup();
if (!@mkdir($data_path, G5_DIR_PERMISSION) || @file_put_contents($config_file, "<?php\n// 기존 설정 보존 표식\n?>") === false) {
g5_shop_install_test_fail('격리 테스트 설정 파일을 준비하지 못했습니다.');
}
$result = g5_shop_install_run(array(
'allow_enabled' => true,
'prefix' => $prefix,
'data_path' => $data_path,
'config_file' => $config_file
));
if (!$result['success']) {
g5_shop_install_test_fail($result['error']);
}
foreach ($tables as $name) {
if (!g5_migration_table_exists($prefix . $name)) {
g5_shop_install_test_fail('쇼핑몰 테이블이 누락되었습니다: ' . $prefix . $name);
}
}
$default = sql_fetch("SELECT de_shop_skin, de_shop_mobile_skin, de_sms_cont1, de_sms_cont2, de_sms_cont3, de_sms_cont4, de_sms_cont5 FROM `{$prefix}default` LIMIT 1", false);
if (!isset($default['de_shop_skin']) || $default['de_shop_skin'] !== 'basic' || $default['de_shop_mobile_skin'] !== 'basic') {
g5_shop_install_test_fail('쇼핑몰 기본 설정이 올바르게 생성되지 않았습니다.');
}
for ($index = 1; $index <= 5; $index++) {
preg_match_all('/./us', $default['de_sms_cont' . $index], $characters);
$bytes = 0;
foreach ($characters[0] as $character) {
$bytes += strlen($character) > 1 ? 2 : 1;
}
if ($bytes > 80) {
g5_shop_install_test_fail('기본 SMS 문구가 80바이트를 초과합니다: de_sms_cont' . $index);
}
}
$config = @file_get_contents($config_file);
if ($config === false || strpos($config, '기존 설정 보존 표식') === false || strpos($config, "define('G5_USE_SHOP', true)") === false || strpos($config, $prefix) === false) {
g5_shop_install_test_fail('기존 DB 설정을 보존하면서 쇼핑몰 설정을 추가하지 못했습니다.');
}
$retry = g5_shop_install_run(array(
'allow_enabled' => true,
'prefix' => $prefix,
'data_path' => $data_path,
'config_file' => $config_file
));
if ($retry['success']) {
g5_shop_install_test_fail('이미 설치된 쇼핑몰의 중복 설치를 차단하지 못했습니다.');
}
echo "쇼핑몰 후설치·설정 보존·중복 실행 차단 검사가 통과했습니다.\n";
+3
View File
@@ -24,6 +24,7 @@ php bin/db-migrate.php migrate
php bin/db-migrate.php migrate 20260416_001_member_auto_login
php bin/check-migration-schema.php
php bin/check-migration-runner.php
php bin/check-shop-install.php
php bin/check-runtime-ddl.php
```
@@ -37,6 +38,8 @@ php bin/check-runtime-ddl.php
`check-migration-schema.php`는 마이그레이션의 테이블·컬럼·인덱스가 신규 설치 SQL에 포함되는지와 최종 컬럼 정의가 일치하는지 검사한다. `check-migration-runner.php`는 격리된 임시 테이블로 부분 적용 스키마, 쇼핑몰 미설치 건너뛰기, 기존 데이터 보정을 검사한다. 마이그레이션을 추가하거나 설치 SQL을 변경한 뒤 두 검사를 반드시 실행한다.
쇼핑몰을 설치하지 않은 사이트에서는 DB 업그레이드 화면의 `쇼핑몰 설치` 버튼으로 최신 쇼핑몰 스키마를 추가할 수 있다. 같은 접두어의 쇼핑몰 테이블이 하나라도 있으면 기존 데이터 훼손을 막기 위해 설치를 중단하며, 기존 `data/dbconfig.php`의 DB 접속 정보와 토큰 키는 유지하고 쇼핑몰 설정만 추가한다. `php bin/check-shop-install.php`로 격리된 접두어에서 후설치, 설정 보존과 중복 실행 차단을 검사할 수 있다.
`check-runtime-ddl.php`는 관리자·게시판·라이브러리·쇼핑몰 등 일반 실행 경로에 DDL 문이 다시 추가되지 않았는지 검사한다. 테이블이나 컬럼 변경은 이 검사를 우회하지 말고 버전형 마이그레이션으로 작성한다.
게시판 삭제 시 게시판별 테이블을 함께 삭제하는 것처럼 사용자가 명시적으로 요청한 데이터 생명주기 작업은 마이그레이션과 구분해 제한된 허용 목록으로 관리한다. 일반 레코드 생성 과정에서 `AUTO_INCREMENT`를 초기화하는 DDL은 허용하지 않는다.
+207
View File
@@ -0,0 +1,207 @@
<?php
if (!defined('_GNUBOARD_')) exit;
function g5_shop_install_prefix()
{
return G5_TABLE_PREFIX . 'shop_';
}
function g5_shop_install_table_names($prefix)
{
return array(
'banner', 'cart', 'category', 'coupon', 'coupon_log', 'coupon_zone', 'default',
'event', 'event_item', 'item', 'item_option', 'item_use', 'item_qa', 'item_relation',
'order', 'order_address', 'order_data', 'order_delete', 'personalpay', 'sendcost',
'wish', 'item_stocksms', 'order_post_log', 'inicis_log', 'order_cancel_log',
'inicis_pay', 'inicis_pay_event'
);
}
function g5_shop_install_existing_tables($prefix)
{
$tables = array();
$result = sql_query('SHOW TABLES', false);
if (!$result) {
return false;
}
while ($row = sql_fetch_array($result)) {
$table = reset($row);
if (is_string($table) && strpos($table, $prefix) === 0) {
$tables[] = $table;
}
}
return $tables;
}
function g5_shop_install_finish($lock_name, $success, $error)
{
sql_query("SELECT RELEASE_LOCK('" . sql_real_escape_string($lock_name) . "')", false);
return array('success' => $success, 'error' => $error);
}
function g5_shop_install_config_block($prefix)
{
$map = array(
'default', 'banner', 'cart', 'category', 'event', 'event_item', 'item',
'item_option', 'item_use', 'item_qa', 'item_relation', 'order', 'order_delete',
'wish', 'coupon', 'coupon_zone', 'coupon_log', 'sendcost', 'personalpay',
'order_address', 'item_stocksms', 'post_log' => 'order_post_log',
'order_data', 'inicis_log', 'inicis_pay', 'inicis_pay_event', 'order_cancel_log'
);
$block = "\n\ndefine('G5_USE_SHOP', true);\n\ndefine('G5_SHOP_TABLE_PREFIX', '" . addcslashes($prefix, "\\'") . "');\n\n";
foreach ($map as $key => $value) {
if (is_int($key)) {
$key = $value;
}
$block .= "\$g5['g5_shop_" . $key . "_table'] = G5_SHOP_TABLE_PREFIX.'" . $value . "';\n";
}
return $block;
}
function g5_shop_install_write_config($prefix, $file = '')
{
if ($file === '') {
$file = G5_DATA_PATH . '/' . G5_DBCONFIG_FILE;
}
$contents = @file_get_contents($file);
if ($contents === false || strpos($contents, "define('G5_USE_SHOP'") !== false) {
return 'DB 설정 파일을 읽을 수 없거나 쇼핑몰 설정이 이미 존재합니다.';
}
$closing = strrpos($contents, '?>');
$updated = $closing === false
? $contents . g5_shop_install_config_block($prefix)
: substr($contents, 0, $closing) . g5_shop_install_config_block($prefix) . "?>" . substr($contents, $closing + 2);
$temp = @tempnam(dirname($file), 'shop-config-');
if (!$temp) {
return 'DB 설정 임시 파일을 만들 수 없습니다.';
}
$written = @file_put_contents($temp, $updated, LOCK_EX);
if ($written === false || $written !== strlen($updated)) {
@unlink($temp);
return 'DB 설정 임시 파일을 저장하지 못했습니다.';
}
@chmod($temp, G5_FILE_PERMISSION);
if (!@rename($temp, $file)) {
@unlink($temp);
return 'DB 설정 파일을 교체하지 못했습니다.';
}
return '';
}
function g5_shop_install_default($table)
{
$sql = "INSERT INTO `{$table}` SET
de_admin_company_name = '회사명', de_admin_company_saupja_no = '123-45-67890',
de_admin_company_owner = '대표자명', de_admin_company_tel = '02-123-4567',
de_admin_company_fax = '02-123-4568', de_admin_tongsin_no = '제 OO구 - 123호',
de_admin_buga_no = '12345호', de_admin_company_zip = '123-456',
de_admin_company_addr = 'OO도 OO시 OO구 OO동 123-45', de_admin_info_name = '정보책임자명',
de_admin_info_email = '정보책임자 E-mail', de_shop_skin = 'basic', de_shop_mobile_skin = 'basic',
de_type1_list_use = '1', de_type1_list_skin = 'main.10.skin.php', de_type1_list_mod = '5', de_type1_list_row = '1', de_type1_img_width = '160', de_type1_img_height = '160',
de_type2_list_use = '1', de_type2_list_skin = 'main.20.skin.php', de_type2_list_mod = '4', de_type2_list_row = '1', de_type2_img_width = '215', de_type2_img_height = '215',
de_type3_list_use = '1', de_type3_list_skin = 'main.40.skin.php', de_type3_list_mod = '4', de_type3_list_row = '1', de_type3_img_width = '215', de_type3_img_height = '215',
de_type4_list_use = '1', de_type4_list_skin = 'main.50.skin.php', de_type4_list_mod = '5', de_type4_list_row = '1', de_type4_img_width = '215', de_type4_img_height = '215',
de_type5_list_use = '1', de_type5_list_skin = 'main.30.skin.php', de_type5_list_mod = '4', de_type5_list_row = '1', de_type5_img_width = '215', de_type5_img_height = '215',
de_mobile_type1_list_use = '1', de_mobile_type1_list_skin = 'main.30.skin.php', de_mobile_type1_list_mod = '2', de_mobile_type1_list_row = '4', de_mobile_type1_img_width = '230', de_mobile_type1_img_height = '230',
de_mobile_type2_list_use = '1', de_mobile_type2_list_skin = 'main.10.skin.php', de_mobile_type2_list_mod = '2', de_mobile_type2_list_row = '2', de_mobile_type2_img_width = '230', de_mobile_type2_img_height = '230',
de_mobile_type3_list_use = '1', de_mobile_type3_list_skin = 'main.10.skin.php', de_mobile_type3_list_mod = '2', de_mobile_type3_list_row = '4', de_mobile_type3_img_width = '300', de_mobile_type3_img_height = '300',
de_mobile_type4_list_use = '1', de_mobile_type4_list_skin = 'main.20.skin.php', de_mobile_type4_list_mod = '2', de_mobile_type4_list_row = '2', de_mobile_type4_img_width = '80', de_mobile_type4_img_height = '80',
de_mobile_type5_list_use = '1', de_mobile_type5_list_skin = 'main.10.skin.php', de_mobile_type5_list_mod = '2', de_mobile_type5_list_row = '2', de_mobile_type5_img_width = '230', de_mobile_type5_img_height = '230',
de_bank_use = '1', de_bank_account = 'OO은행 12345-67-89012 예금주명', de_vbank_use = '0', de_iche_use = '0', de_card_use = '0',
de_settle_min_point = '5000', de_settle_max_point = '50000', de_settle_point_unit = '100',
de_cart_keep_term = '15', de_card_point = '0', de_point_days = '7', de_pg_service = 'kcp', de_kcp_mid = '',
de_send_cost_case = '차등', de_send_cost_limit = '20000;30000;40000', de_send_cost_list = '4000;3000;2000',
de_hope_date_use = '0', de_hope_date_after = '3', de_baesong_content = '배송 안내 입력전입니다.', de_change_content = '교환/반품 안내 입력전입니다.',
de_rel_list_use = '1', de_rel_list_skin = 'relation.10.skin.php', de_rel_list_mod = '5', de_rel_img_width = '215', de_rel_img_height = '215',
de_mobile_rel_list_use = '1', de_mobile_rel_list_skin = 'relation.10.skin.php', de_mobile_rel_list_mod = '3', de_mobile_rel_img_width = '230', de_mobile_rel_img_height = '230',
de_search_list_skin = 'list.10.skin.php', de_search_img_width = '225', de_search_img_height = '225', de_search_list_mod = '5', de_search_list_row = '5',
de_mobile_search_list_skin = 'list.10.skin.php', de_mobile_search_img_width = '230', de_mobile_search_img_height = '230', de_mobile_search_list_mod = '2', de_mobile_search_list_row = '5',
de_listtype_list_skin = 'list.10.skin.php', de_listtype_img_width = '225', de_listtype_img_height = '225', de_listtype_list_mod = '5', de_listtype_list_row = '5',
de_mobile_listtype_list_skin = 'list.10.skin.php', de_mobile_listtype_img_width = '230', de_mobile_listtype_img_height = '230', de_mobile_listtype_list_mod = '2', de_mobile_listtype_list_row = '5',
de_simg_width = '230', de_simg_height = '230', de_mimg_width = '300', de_mimg_height = '300',
de_item_use_use = '1', de_level_sell = '1', de_code_dup_use = '1', de_card_test = '1',
de_sms_cont1 = '{이름}님의 회원가입을 축하드립니다.\nID:{회원아이디}\n{회사명}',
de_sms_cont2 = '{이름}님 주문해주셔서 고맙습니다.\n{주문번호}\n{주문금액}원\n{회사명}',
de_sms_cont3 = '{이름}님께서 주문하셨습니다.\n{주문번호}\n{주문금액}원\n{회사명}',
de_sms_cont4 = '{이름}님 입금 감사합니다.\n{입금액}원\n주문번호:\n{주문번호}\n{회사명}',
de_sms_cont5 = '{이름}님 배송합니다.\n택배:{택배회사}\n운송장번호:\n{운송장번호}\n{회사명}'";
return sql_query($sql, false) ? '' : sql_error_info();
}
function g5_shop_install_run($options = array())
{
$allow_enabled = isset($options['allow_enabled']) && $options['allow_enabled'];
if (!$allow_enabled && defined('G5_USE_SHOP') && G5_USE_SHOP) {
return array('success' => false, 'error' => '쇼핑몰이 이미 설치되어 있습니다.');
}
$prefix = isset($options['prefix']) && $options['prefix'] !== '' ? $options['prefix'] : g5_shop_install_prefix();
$data_path = isset($options['data_path']) && $options['data_path'] !== '' ? $options['data_path'] : G5_DATA_PATH;
$config_file = isset($options['config_file']) ? $options['config_file'] : '';
$lock_name = 'g5_shop_install_' . substr(md5(G5_TABLE_PREFIX), 0, 16);
$lock = sql_fetch("SELECT GET_LOCK('{$lock_name}', 10) AS acquired", false);
if (!$lock || (int) $lock['acquired'] !== 1) {
return array('success' => false, 'error' => '다른 쇼핑몰 설치 작업이 실행 중이거나 실행 잠금을 얻지 못했습니다.');
}
$existing = g5_shop_install_existing_tables($prefix);
if ($existing === false) {
return g5_shop_install_finish($lock_name, false, '쇼핑몰 테이블 존재 여부를 확인하지 못했습니다.');
}
if ($existing) {
return g5_shop_install_finish($lock_name, false, '같은 접두어의 쇼핑몰 테이블이 이미 존재합니다: ' . implode(', ', $existing));
}
include_once G5_PATH . '/install/install.function.php';
$queries = install_load_sql_file(G5_PATH . '/install/gnuboard5shop.sql', 'g5_shop_', $prefix);
if ($queries === false) {
return g5_shop_install_finish($lock_name, false, '쇼핑몰 설치 SQL을 읽을 수 없습니다.');
}
$created = array();
foreach ($queries as $sql) {
$sql = get_db_create_replace($sql);
if (!sql_query($sql, false)) {
$error = sql_error_info();
foreach (array_reverse($created) as $table) {
sql_query("DROP TABLE IF EXISTS `" . str_replace('`', '``', $table) . "`", false);
}
return g5_shop_install_finish($lock_name, false, '쇼핑몰 테이블 생성에 실패했습니다: ' . $error);
}
if (preg_match('/^CREATE TABLE(?: IF NOT EXISTS)?\s+`([^`]+)`/i', trim($sql), $matches)) {
$created[] = $matches[1];
}
}
$error = g5_shop_install_default($prefix . 'default');
if ($error === '') {
$dirs = array('banner', 'common', 'event', 'item');
foreach ($dirs as $dir) {
if (!install_ensure_dir($data_path . '/' . $dir)) {
$error = '쇼핑몰 데이터 디렉터리를 만들 수 없습니다: data/' . $dir;
break;
}
}
}
if ($error === '') {
$copies = array(
array(G5_PATH . '/install/logo_img', $data_path . '/common/logo_img'),
array(G5_PATH . '/install/logo_img', $data_path . '/common/logo_img2'),
array(G5_PATH . '/install/mobile_logo_img', $data_path . '/common/mobile_logo_img'),
array(G5_PATH . '/install/mobile_logo_img', $data_path . '/common/mobile_logo_img2')
);
foreach ($copies as $copy) {
if (!file_exists($copy[1]) && !@copy($copy[0], $copy[1])) {
$error = '쇼핑몰 기본 이미지를 복사할 수 없습니다: ' . $copy[1];
break;
}
}
}
if ($error === '') {
$error = g5_shop_install_write_config($prefix, $config_file);
}
if ($error !== '') {
foreach (array_reverse($created) as $table) {
sql_query("DROP TABLE IF EXISTS `" . str_replace('`', '``', $table) . "`", false);
}
return g5_shop_install_finish($lock_name, false, $error);
}
return g5_shop_install_finish($lock_name, true, '');
}