diff --git a/AvocadoAmber/adm/admin.amber.lib.php b/AvocadoAmber/adm/admin.amber.lib.php
new file mode 100644
index 0000000..663bcb5
--- /dev/null
+++ b/AvocadoAmber/adm/admin.amber.lib.php
@@ -0,0 +1,402 @@
+
+function addListItem(containerId, placeholder) {
+ var container = document.getElementById(containerId);
+ var newItem = document.createElement('div');
+ var adder = document.getElementById(`\${containerId}_new`);
+ var content = adder?.value;
+
+ newItem.className = 'adm-listedit-item';
+ newItem.innerHTML = ' ' +
+ '삭제 ';
+ container.appendChild(newItem);
+ adder.value = '';
+
+ var newInput = newItem.querySelector('input[type=\"text\"]');
+ var optionKey = containerId.replace('_items', '');
+
+ newInput.addEventListener('input', function() {
+ updateListHiddenInput(optionKey);
+ });
+
+ // Enter 키 이벤트 추가
+ newInput.addEventListener('keydown', function(e) {
+ if (e.key === 'Enter' || e.keyCode === 13) {
+ e.preventDefault();
+ var placeholder = newInput.getAttribute('placeholder');
+ addListItem(containerId, placeholder);
+ }
+ });
+
+ // 포커스를 새로 추가된 입력 필드에 설정
+ adder.focus();
+
+ updateListHiddenInput(optionKey);
+}
+
+function removeListItem(button) {
+ var item = button.parentElement;
+ var container = item.parentElement;
+ var optionKey = container.id.replace('_items', '');
+
+ // 최소 하나의 입력 필드는 유지 (추가 버튼 제외)
+ var inputItems = container.querySelectorAll('.adm-listedit-item:not(.adm-listedit-add-row)');
+ if (inputItems.length > 1) {
+ item.remove ? item.remove() : item.parentNode.removeChild(item);
+ } else {
+ // 마지막 항목인 경우 입력값만 비우기
+ var input = item.querySelector('input[type=\"text\"]');
+ input.value = '';
+ }
+ updateListHiddenInput(optionKey);
+}
+
+function updateListHiddenInput(optionKey) {
+ var container = document.getElementById(optionKey + '_items');
+ var listContainer = document.getElementById(optionKey + '_container');
+ var separator = listContainer.getAttribute('data-separator');
+ var inputs = container.querySelectorAll('input[type=\"text\"]');
+ var values = [];
+
+ for (var i = 0; i < inputs.length; i++) {
+ var value = inputs[i].value.trim();
+ if (value) {
+ values.push(value);
+ }
+ }
+
+ var hiddenInput = document.getElementById(optionKey + '_hidden');
+ hiddenInput.value = values.join(separator);
+}
+
+// DOM 로드 후 이벤트 리스너 설정
+function initializeListEdit() {
+ var containers = document.querySelectorAll('.adm-listedit-items');
+
+ for (var i = 0; i < containers.length; i++) {
+ var container = containers[i];
+ var optionKey = container.id.replace('_items', '');
+ var inputs = container.querySelectorAll('input[type=\"text\"]');
+
+ for (var j = 0; j < inputs.length; j++) {
+ var input = inputs[j];
+
+ (function(inp, key) {
+ inp.addEventListener('input', function() {
+ updateListHiddenInput(key);
+ });
+ })(input, optionKey);
+ }
+
+ // 새 입력칸에 Enter 키 이벤트 추가
+ var newInput = document.getElementById(optionKey + '_new_input');
+ if (newInput) {
+ newInput.addEventListener('keydown', function(e) {
+ if (e.key === 'Enter' || e.keyCode === 13) {
+ e.preventDefault();
+ var optKey = this.id.replace('_new_input', '');
+ addListItemFromInput(optKey);
+ }
+ });
+ }
+ }
+
+ // 폼 제출 전에 한 번 더 업데이트
+ var forms = document.querySelectorAll('form');
+ for (var k = 0; k < forms.length; k++) {
+ forms[k].addEventListener('submit', function() {
+ for (var l = 0; l < containers.length; l++) {
+ var container = containers[l];
+ var optionKey = container.id.replace('_items', '');
+ updateListHiddenInput(optionKey);
+ }
+ });
+ }
+}
+
+// DOM 로드 완료 시 초기화
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initializeListEdit);
+} else {
+ initializeListEdit();
+}
+";
+
+ add_javascript($scripts, 10);
+ }
+}
+
+function getCurrentValue($key, $value)
+{
+ initCheckNeedUpdate();
+ return is_array($value) ? ( isset($value[$key]) ? $value[$key] : "" ) : $value;
+}
+
+function getSkinDir($skin, $skin_path = G5_SKIN_PATH)
+{
+ global $g5;
+ $result_array = [];
+ $dirname = implode(DIRECTORY_SEPARATOR, [$skin_path, $skin, ""]);
+ if (!is_dir($dirname))
+ return;
+
+ $handle = opendir($dirname);
+ while ($file = readdir($handle)) {
+ if ($file == '.' || $file == '..')
+ continue;
+
+ if (is_dir($dirname . $file))
+ $result_array[$file] = $file;
+ }
+ closedir($handle);
+ sort($result_array);
+
+ return $result_array;
+}
+
+function getSkinList($skin_type)
+{
+ global $config;
+ $skins = [];
+ if (defined('G5_THEME_PATH') && $config['cf_theme']) {
+ $dirs = getSkinDir($skin_type, G5_THEME_PATH . '/' . G5_SKIN_DIR);
+ if (!empty($dirs)) {
+ foreach ($dirs as $dir) {
+ $skins[$dir] = "theme/{$dir}";
+ }
+ }
+ }
+
+ $ret = [];
+ $dir = array_merge($skins, getSkinDir($skin_type));
+ foreach($dir as $k => $v) {
+ $ret[$v] = $v;
+ }
+
+ return $ret;
+}
+
+function getMemberLevels($include_admin = false)
+{
+ global $g5;
+
+ $level_name[1] = "방문자";
+ $level_name[2] = "멤버";
+ $level_name[3] = "상위멤버";
+ if ($include_admin) $level_name[10] = "운영자";
+
+ return $level_name;
+}
+
+function setCheckbox($optionName, $optionDesc, $optionKey, $optionValue, $currentValue, $standalone = false)
+{
+ $currentValue = getCurrentValue($optionKey, $currentValue);
+ $checked = "";
+ $v = "";
+ $desc = $optionDesc ? "
{$optionDesc}
" : "";
+ if ($currentValue) $checked = " checked";
+
+ if ($standalone) $v .= "";
+
+ echo $v;
+}
+
+function setNumberInput($optionName, $optionDesc, $optionKey, $currentValue, $preDesc = "", $postDesc = "", $minValue = 0, $maxValue = 0, $standalone = false)
+{
+ $currentValue = getCurrentValue($optionKey, $currentValue);
+ $v = "";
+ $min = $minValue ? " min=\"{$minValue}\"" : "";
+ $max = $maxValue ? " max=\"{$maxValue}\"" : "";
+ $desc = $optionDesc ? "{$optionDesc}
" : "";
+
+ if ($standalone) $v .= "";
+
+ echo $v;
+}
+
+function setTextInput($optionName, $optionDesc, $optionKey, $currentValue, $standalone = false)
+{
+ $currentValue = getCurrentValue($optionKey, $currentValue);
+ $v = "";
+ $desc = $optionDesc ? "{$optionDesc}
" : "";
+
+ if ($standalone) $v .= "";
+
+ echo $v;
+}
+
+function setAccountList($optionName, $optionDesc, $level, $optionKey, $optionValue, $currentValue, $required = false, $standalone = false)
+{
+ global $g5;
+
+ $sql = "SELECT mb_id, mb_nick FROM {$g5['member_table']} WHERE mb_level >= '{$level}' ";
+ $required_option = $required ? " required" : "";
+ $currentValue = getCurrentValue($optionKey, $currentValue);
+ $v = "";
+ $desc = $optionDesc ? "{$optionDesc}
" : "";
+
+ $result = sql_query($sql);
+ if ($standalone) $v .= "";
+
+ echo $v;
+}
+
+function setOptionList($optionName, $optionDesc, $optionListObject, $optionKey, $optionValue, $currentValue, $required = false, $standalone = false)
+{
+ global $g5;
+
+ $required_option = $required ? " required" : "";
+ $currentValue = getCurrentValue($optionKey, $currentValue);
+ $v = "";
+ $desc = $optionDesc ? "{$optionDesc}
" : "";
+
+ if ($standalone) $v .= "";
+
+ echo $v;
+}
+
+function setImageFile($optionName, $optionDesc, $optionKey, $currentValue, $standalone = false)
+{
+ $currentValue = getCurrentValue($optionKey, $currentValue);
+ $v = "";
+ $desc = $optionDesc ? "{$optionDesc} 파일을 선택하거나 주소를 입력하여 등록할 수 있습니다. 주소 입력을 비워두면 설정이 해제됩니다.
" : "";
+
+ if ($standalone) $v .= "";
+
+ echo $v;
+}
+
+function setListEditInput($optionName, $optionDesc, $optionKey, $currentValue, $separator = "\n", $placeholder = "", $standalone = false)
+{
+ $currentValue = getCurrentValue($optionKey, $currentValue);
+ $v = "";
+ $desc = $optionDesc ? "{$optionDesc}
" : "";
+
+ $itemList = [];
+ if (!empty($currentValue)) {
+ $itemList = array_filter(array_map('trim', explode($separator, $currentValue)));
+ }
+
+ $jsSeparator = addslashes($separator);
+ if ($standalone) $v .= "";
+
+ echo $v;
+}
\ No newline at end of file
diff --git a/AvocadoAmber/adm/admin.lib.php b/AvocadoAmber/adm/admin.lib.php
index c022795..b1c2a39 100644
--- a/AvocadoAmber/adm/admin.lib.php
+++ b/AvocadoAmber/adm/admin.lib.php
@@ -2,6 +2,8 @@
if (!defined('_GNUBOARD_'))
exit;
+include_once "./admin.amber.lib.php";
+
function get_skin_select($skin_gubun, $id, $name, $selected = '', $event = '')
{
global $config;
diff --git a/AvocadoAmber/adm/community_form.php b/AvocadoAmber/adm/community_form.php
index 521bb8e..7cb2e84 100644
--- a/AvocadoAmber/adm/community_form.php
+++ b/AvocadoAmber/adm/community_form.php
@@ -69,7 +69,7 @@ if (!$config["cf_community"] || !$is_community_init) {
@@ -87,38 +87,13 @@ if (!$config["cf_community"] || !$is_community_init) {
커뮤니티용 기능에 필요한 설정입니다.
-
+ 관련된 플러그인, 애드온, 모듈과 그것을 지원하는 스킨이 설치되어 있어야 합니다.", "cf_4", "1", $config);
+ setCheckbox("조합 기능 사용", "조합 기능을 사용할 지 설정합니다.
관련된 플러그인, 애드온, 모듈과 그것을 지원하는 스킨이 설치되어 있어야 합니다.", "cf_5", "1", $config);
+ setCheckbox("탐색 수행 가능 여부 설정", "탐색을 수행할 수 있는지 여부를 설정합니다.
관련된 플러그인, 애드온, 모듈과 그것을 지원하는 스킨이 설치되어 있어야 합니다.", "cf_6", "1", $config);
+ ?>
@@ -126,36 +101,11 @@ if (!$config["cf_community"] || !$is_community_init) {
기능 설정
-
+ 관련된 플러그인, 애드온, 모듈과 그것을 지원하는 스킨이 설치되어 있어야 합니다.", "cf_search_count", $config, "", "회", 0);
+ ?>
@@ -163,89 +113,17 @@ if (!$config["cf_community"] || !$is_community_init) {
기타 항목명 설정
-
+ 설정하지 않는 경우 사용하지 않는 것으로 간주됩니다.", "cf_side_title", $config);
+ setTextInput("클래스 명칭", "클래스의 이름을 설정합니다.
설정하지 않는 경우 사용하지 않는 것으로 간주됩니다.", "cf_class_title", $config);
+ setListEditInput("상점 카테고리", "상점 아이템의 카테고리를 설정합니다.", "cf_shop_category", $config, "||");
+ setListEditInput("아이템 기능", "아이템 기능을 설정합니다.
기능에 할당된 아이템이 있는 상태에서 삭제할 경우 정상 동작을 보장할 수 없습니다.", "cf_item_category", $config, "||");
+ ?>
diff --git a/AvocadoAmber/adm/css/admin.amber.lib.css b/AvocadoAmber/adm/css/admin.amber.lib.css
new file mode 100644
index 0000000..c5de122
--- /dev/null
+++ b/AvocadoAmber/adm/css/admin.amber.lib.css
@@ -0,0 +1,257 @@
+@charset "utf-8";
+
+.adm-form {
+ padding: 16px;
+ border-bottom: 1px solid var(--theme-color-e, #efeff5);
+}
+
+.adm-form input,
+.adm-form textarea,
+.adm-form select {
+ outline-color: var(--theme-color-e);
+}
+
+.adm-form:hover {
+ background: var(--theme-color-0);
+}
+
+.adm-form:last-child {
+ margin-bottom: 8px;
+}
+
+.adm-form-postfix {
+ min-width: 40px;
+ text-align: center;
+}
+
+.adm-form-colflex {
+ display: flex;
+ gap: 8px;
+}
+
+.adm-form-rowflex {
+ display: flex;
+ gap: 8px;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.adm-form-colflex .adm-form-flex-full {
+ flex-grow: 1;
+}
+
+.adm-form-name {
+ font-size: 16px;
+ font-weight: bold;
+}
+
+.adm-form-desc {
+ margin: 8px 0 0 0;
+ font-size: 14px;
+}
+
+.adm-form-check {
+ position: relative;
+ width: 48px;
+ height: 24px;
+ margin: 4px 0;
+}
+
+.adm-form-check input[type=checkbox] {
+ width: 48px;
+ height: 24px;
+ position: relative;
+ cursor: pointer;
+ opacity: 0;
+}
+
+.adm-form-check input[type=checkbox]+.adm-form-checkbox-cover {
+ pointer-events: none;
+ box-shadow: inset 0 0 3px #0004;
+ background: #DDD;
+ width: 48px;
+ height: 24px;
+ border-radius: 24px;
+ position: absolute;
+ left: 0;
+ top: 0;
+ display: flex;
+ justify-content: center;
+ transition: .25s;
+}
+
+.adm-form-check input[type=checkbox]+.adm-form-checkbox-cover::before {
+ content: "";
+ height: 18px;
+ width: 18px;
+ border-radius: 50%;
+ background: #FFF;
+ box-shadow: 0 0 3px #0004;
+ transform: translateX(-12px) translateY(3px);
+ box-sizing: border-box;
+ transition: .25s;
+}
+
+.adm-form-check input[type=checkbox]:checked+.adm-form-checkbox-cover {
+ background: var(--theme-color-e, #06C);
+}
+
+.adm-form-check input[type=checkbox]:checked+.adm-form-checkbox-cover::before {
+ transform: translateX(12px) translateY(3px);
+}
+
+.adm-form-select {
+ padding: 4px;
+}
+
+.adm-form-select select {
+ width: 100%;
+ box-sizing: border-box;
+ cursor: pointer;
+ border: 1px solid var(--theme-color-e, #efeff5);
+ border-radius: 4px;
+ padding: 8px;
+ height: auto
+}
+
+.adm-form-text {
+ padding: 4px;
+}
+
+.adm-form-text input[type=text] {
+ border: 1px solid var(--theme-color-e, #efeff5);
+ padding: 8px;
+ border-radius: 4px;
+ height: auto;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.adm-form-file {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.adm-form-file>div {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+}
+
+.adm-form-file div>span {
+ display: inline-block;
+ min-width: 80px;
+ font-size: 14px;
+}
+
+.adm-form-file div>input {
+ flex-grow: 1;
+ box-sizing: border-box;
+ border: 1px solid var(--theme-color-e, #efeff5);
+ padding: 8px;
+ border-radius: 4px;
+ height: auto;
+}
+
+.adm-form-file div>input[type=file] {
+ cursor: pointer;
+}
+
+.adm-form-listedit {
+ width: 100%;
+}
+
+.adm-form-listedit .adm-listedit-controls {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.adm-listedit-items {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin-bottom: 8px;
+ overflow-y: auto;
+ max-height: 360px;
+ box-sizing: border-box;
+ border: 1px solid var(--theme-color-e, #efeff5);
+ border-radius: 4px;
+ padding: 8px;
+}
+
+.adm-listedit-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.adm-form-listedit input[type=text] {
+ flex-grow: 1;
+ padding: 8px;
+ flex-grow: 1;
+ box-sizing: border-box;
+ border: 1px solid var(--theme-color-e, #efeff5);
+ border-radius: 4px;
+}
+
+.adm-listedit-remove,
+.adm-listedit-add {
+ min-width: 100px;
+ align-self: stretch;
+ border: 1px solid #ddd;
+ background: #f8f8f8;
+ cursor: pointer;
+ border-radius: 4px;
+ font-size: 14px;
+ white-space: nowrap;
+}
+
+.adm-listedit-remove:hover,
+.adm-listedit-add:hover {
+ background: #e8e8e8;
+}
+
+.adm-listedit-remove {
+ background: var(--theme-color-sub-b, #ff6b6b);
+ color: #000;
+ border-color: var(--theme-color-sub, #ff5252);
+}
+
+.adm-listedit-remove:hover {
+ color: #FFF;
+ background: var(--theme-color-sub, #ff6b6b);
+}
+
+.adm-listedit-add {
+ background: var(--theme-color-e, #4CAF50);
+ color: #000;
+ border-color: var(--theme-color-d, #45a049);
+}
+
+.adm-listedit-add:hover {
+ color: #FFF;
+ background: var(--theme-color-d, #45a049);
+}
+
+.adm-listedit-controls {
+ text-align: left;
+}
+
+.adm-form-number {
+ padding: 4px;
+ display: flex;
+ align-items: center;
+ gap: 8px
+}
+
+.adm-form-number input[type=number] {
+ border: 1px solid var(--theme-color-e, #efeff5);
+ padding: 8px;
+ border-radius: 4px;
+ height: auto;
+ width: 80px;
+ min-width: 60px;
+ box-sizing: border-box;
+}
\ No newline at end of file
diff --git a/AvocadoAmber/adm/migrate/_common.php b/AvocadoAmber/adm/migrate/_common.php
new file mode 100644
index 0000000..3b7ba70
--- /dev/null
+++ b/AvocadoAmber/adm/migrate/_common.php
@@ -0,0 +1,12 @@
+
- 기본환경
- 게시판/회원
+ 서버정보
+ 기본환경
+ 게시판/회원
레이아웃 추가설정
';
@@ -244,6 +245,37 @@ if (!$config['cf_icode_server_port'])
if ($config['cf_sms_use'] && $config['cf_icode_id'] && $config['cf_icode_pw']) {
$userinfo = get_icode_userinfo($config['cf_icode_id'], $config['cf_icode_pw']);
}
+
+function getDiskUsage($path = '/')
+{
+ $bytes = disk_total_space($path);
+ $free = disk_free_space($path);
+ $used = $bytes - $free;
+
+ return [
+ 'total' => $bytes,
+ 'free' => $free,
+ 'used' => $used,
+ 'usage_percent' => round(($used / $bytes) * 100, 2)
+ ];
+}
+
+function formatBytes($bytes, $precision = 2)
+{
+ $units = ['B', 'KB', 'MB', 'GB', 'TB'];
+
+ for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
+ $bytes /= 1024;
+ }
+
+ return round($bytes, $precision) . ' ' . $units[$i];
+}
+
+function getWebServerDiskUsage()
+{
+ // $documentRoot = $_SERVER['DOCUMENT_ROOT'] ?? '/var/www/html';
+ return getDiskUsage();
+}
?>