๐ฅ
๐
๐ป
Z
U
๐f
(โฆ)
๐n
(โฆ)
โฐ
๐พS
๐ฅ
๐C
๐
๐
ฦ
yE
Fn
St
Fi
Hd
<?php // ํ์ผ โจ/var/www/html/y/yyy_zip_apply.php // ๊ธฐ๋ฅ: ZIP ์ ๋ก๋ โ /data/_bak/yyy_zip ๊ณ์ด ๋ณด๊ด/ํด์ โ ๊ธฐ์กด ํ์ผ ๋น๊ต โ ๋ฐฑ์ ํ ๊ฒฝ๋ก๋๋ก ์ ์ฉ // ๋ฒ์ : v049-loadmeter-bs5-bars define('_CMD_', true); // @include_once __DIR__ . '/yyy_config_host.php'; @include_once __DIR__ . '/yyy_allowed_ips.php'; @require_once __DIR__ . '/file_grep_conf.php'; @include_once __DIR__ . '/yyy_cmd_config_ext.php'; if (!isset($CMD_EXT_STYLES) || !is_array($CMD_EXT_STYLES)) { $CMD_EXT_STYLES = array( 'php' => array('dark_color' => '#8be9fd', 'light_color' => '#0d6efd', 'icon' => '๐'), 'js' => array('dark_color' => '#f1fa8c', 'light_color' => '#b8860b', 'icon' => '๐'), 'css' => array('dark_color' => '#ff79c6', 'light_color' => '#d63384', 'icon' => '๐จ'), 'py' => array('dark_color' => '#50fa7b', 'light_color' => '#198754', 'icon' => '๐'), 'json' => array('dark_color' => '#bd93f9', 'light_color' => '#6f42c1', 'icon' => '๐ง'), 'default' => array('dark_color' => '#e5e7eb', 'light_color' => '#333333', 'icon' => '๐'), 'dir' => array('dark_color' => '#fbbf24', 'light_color' => '#b45309', 'icon' => '๐'), ); } function yyza_h($s) { //return (sring)$s; //ํ์ผ๋ช ๋ฌธ์ ์์ด๊ฒ์์ ์ผ๋ถ๋ฌ์ผ๋ช ๋๋ฝํ์ง์๊ธฐ์ํด์์.๋๋ณด๋ค ์ค์ด๋์ปค์์. return htmlspecialchars((string)$s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } function yyza_url($s) { // yyy ๊ณ์ด์ ํ์ผ๊ฒฝ๋ก๊ฐ URL์์ ๊ทธ๋๋ก ๋ณด์ฌ์ผ ๊ด๋ฆฌ๊ฐ ํธํจ. // ๊ณต๋ฐฑ/ํ๊ธ ๊ฒฝ๋ก๋ฅผ ์ผ๋ถ๋ฌ ์ธ์ฝ๋ฉํ์ง ์๋๋ค. return (string)$s; } function yyza_json($data, $code = 200) { http_response_code($code); header('Content-Type: application/json; charset=utf-8'); echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); exit; } function yyza_backup_root() { if (defined('BACKUP_ROOT') && trim((string)BACKUP_ROOT) !== '') { return rtrim((string)BACKUP_ROOT, '/'); } return '/data/_bak'; } function yyza_zip_root() { return yyza_backup_root() . '/yyy_zip'; } function yyza_zip_keep_default_root() { return yyza_backup_root() . '/yyy_zip_keep'; } function yyza_view_drop_root() { return yyza_backup_root() . '/yyy_view'; } function yyza_norm_slash($path) { $path = str_replace('\\', '/', (string)$path); $path = preg_replace('#/+#', '/', $path); return $path; } function yyza_clean_base_dir($path) { $raw = trim((string)$path); $hadTailSlash = (strlen($raw) > 1 && preg_match('#[\\/]$#', $raw)); $path = yyza_norm_slash($raw); if ($path === '') return ''; if ($path === '/') return '/'; $path = rtrim($path, '/'); if ($hadTailSlash) $path .= '/'; return $path; } function yyza_is_bad_zip_name($name) { $name = yyza_norm_slash($name); if ($name === '') return true; if (strpos($name, "\0") !== false) return true; if (preg_match('#(^|/)\.\.(/|$)#', $name)) return true; if (preg_match('#^[A-Za-z]:/#', $name)) return true; return false; } function yyza_script_dir() { $dir = isset($_SERVER['SCRIPT_FILENAME']) ? dirname((string)$_SERVER['SCRIPT_FILENAME']) : __DIR__; $real = realpath($dir); return $real ? yyza_norm_slash($real) : yyza_norm_slash($dir); } function yyza_conf_flat_base_dir() { if (defined('YYY_ZIP_APPLY_FLAT_BASE_DIR') && trim((string)YYY_ZIP_APPLY_FLAT_BASE_DIR) !== '') { return yyza_clean_base_dir((string)YYY_ZIP_APPLY_FLAT_BASE_DIR); } return ''; } function yyza_host_key() { $host = isset($_SERVER['HTTP_HOST']) ? (string)$_SERVER['HTTP_HOST'] : 'cli'; $host = strtolower(trim($host)); $host = preg_replace('/[^a-z0-9._-]+/i', '_', $host); $host = str_replace(':', '_', $host); return $host !== '' ? $host : 'default'; } function yyza_favi_emoji() { return yyza_host_key() === 'home.yjm.kr' ? '๐ฆ' : '๐'; } function yyza_favi_href($emoji) { $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">' . (string)$emoji . '</text></svg>'; return 'data:image/svg+xml,' . rawurlencode($svg); } function yyza_flat_conf_path() { // yyy_๋ ๋จ๋ ์คํ ํ์ผ, yy_๋ ์ค์ /๊ณต์ฉ ์ฑ๊ฒฉ ํ์ผ๋ช ๊ท์น ์ ์ฉ. return __DIR__ . '/yy_zip_apply_' . yyza_host_key() . '.conf'; } function yyza_zip_keep_conf_path() { return __DIR__ . '/yy_zip_apply_keep_' . yyza_host_key() . '.conf'; } function yyza_zip_keep_path_list() { $paths = array(); $file = yyza_zip_keep_conf_path(); if (is_file($file)) { $lines = @file($file, FILE_IGNORE_NEW_LINES); if (is_array($lines)) { foreach ($lines as $line) { $line = trim((string)$line); if ($line === '' || substr($line, 0, 1) === '#') continue; $line = yyza_clean_base_dir($line); if ($line !== '' && !in_array($line, $paths, true)) $paths[] = $line; } } } $default = yyza_zip_keep_default_root(); if (!in_array($default, $paths, true)) $paths[] = $default; return $paths; } function yyza_default_zip_keep_root() { $paths = yyza_zip_keep_path_list(); return $paths ? $paths[0] : yyza_zip_keep_default_root(); } function yyza_zip_keep_root_from_request() { $dir = isset($_REQUEST['zip_keep_root']) ? trim((string)$_REQUEST['zip_keep_root']) : ''; if ($dir === '') $dir = yyza_default_zip_keep_root(); return yyza_clean_base_dir($dir); } function yyza_write_zip_keep_path_list($paths) { $clean = array(); foreach ((array)$paths as $path) { $path = yyza_clean_base_dir($path); if ($path === '') continue; if (!in_array($path, $clean, true)) $clean[] = $path; } if (!$clean) $clean[] = yyza_zip_keep_default_root(); return @file_put_contents(yyza_zip_keep_conf_path(), implode("\n", $clean) . "\n", LOCK_EX) !== false; } function yyza_remember_zip_keep_root($dir) { $dir = yyza_clean_base_dir($dir); if ($dir === '') return false; $paths = yyza_zip_keep_path_list(); $new = array($dir); foreach ($paths as $p) { if ($p !== $dir) $new[] = $p; } return yyza_write_zip_keep_path_list($new); } function yyza_save_zip_keep_paths_text($text, $selected = '') { $lines = preg_split('/\r\n|\r|\n/', (string)$text); $selected = yyza_clean_base_dir($selected); if ($selected !== '') array_unshift($lines, $selected); return yyza_write_zip_keep_path_list($lines); } function yyza_b64url_encode($s) { return rtrim(strtr(base64_encode((string)$s), '+/', '-_'), '='); } function yyza_b64url_decode($s) { $s = (string)$s; $pad = strlen($s) % 4; if ($pad) $s .= str_repeat('=', 4 - $pad); $out = base64_decode(strtr($s, '-_', '+/'), true); return $out === false ? '' : $out; } function yyza_zip_keep_allowed_roots() { $roots = yyza_zip_keep_path_list(); $default = yyza_zip_keep_default_root(); if (!in_array($default, $roots, true)) $roots[] = $default; $out = array(); foreach ($roots as $root) { $root = yyza_clean_base_dir($root); if ($root === '' || $root === '/') continue; if (!in_array($root, $out, true)) $out[] = $root; } return $out; } function yyza_path_under_root($path, $root) { $path = yyza_norm_slash($path); $root = rtrim(yyza_norm_slash($root), '/'); if ($path === '' || $root === '' || $root === '/') return false; return ($path === $root || strpos($path, $root . '/') === 0); } function yyza_zip_keep_file_allowed($path) { $path = yyza_norm_slash($path); if ($path === '' || strpos($path, "\0") !== false) return false; if (!preg_match('/\.zip$/i', $path)) return false; if (!is_file($path)) return false; $real = realpath($path); if ($real === false) return false; $real = yyza_norm_slash($real); foreach (yyza_zip_keep_allowed_roots() as $root) { $rootReal = realpath($root); if ($rootReal === false) continue; $rootReal = rtrim(yyza_norm_slash($rootReal), '/'); if (yyza_path_under_root($real, $rootReal)) return true; } return false; } function yyza_zip_keep_rel_path($path, $root) { $path = yyza_norm_slash($path); $root = rtrim(yyza_norm_slash($root), '/'); $pathReal = realpath($path); $rootReal = realpath($root); if ($pathReal !== false && $rootReal !== false) { $path = yyza_norm_slash($pathReal); $root = rtrim(yyza_norm_slash($rootReal), '/'); } if ($root !== '' && yyza_path_under_root($path, $root)) { $rel = ltrim(substr($path, strlen($root)), '/'); return $rel !== '' ? $rel : basename($path); } return basename($path); } function yyza_list_zip_keep_files($root) { $root = yyza_clean_base_dir($root); if ($root === '' || $root === '/' || !is_dir($root)) return array(); $rootReal = realpath($root); if ($rootReal === false) return array(); $rootReal = rtrim(yyza_norm_slash($rootReal), '/'); $allowed = false; foreach (yyza_zip_keep_allowed_roots() as $allowRoot) { $allowReal = realpath($allowRoot); if ($allowReal === false) continue; $allowReal = rtrim(yyza_norm_slash($allowReal), '/'); if ($rootReal === $allowReal || yyza_path_under_root($rootReal, $allowReal) || yyza_path_under_root($allowReal, $rootReal)) { $allowed = true; break; } } if (!$allowed) return array(); $rows = array(); $it = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($rootReal, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($it as $fi) { if (!$fi->isFile()) continue; $path = yyza_norm_slash($fi->getPathname()); if (!preg_match('/\.zip$/i', $path)) continue; if (!yyza_zip_keep_file_allowed($path)) continue; $rows[] = array( 'path' => $path, 'rel' => yyza_zip_keep_rel_path($path, $rootReal), 'name' => basename($path), 'size' => (int)$fi->getSize(), 'mtime' => (int)$fi->getMTime(), ); } usort($rows, function($a, $b) { $ma = isset($a['mtime']) ? (int)$a['mtime'] : 0; $mb = isset($b['mtime']) ? (int)$b['mtime'] : 0; if ($ma !== $mb) return $mb <=> $ma; return strcmp((string)$a['rel'], (string)$b['rel']); }); return $rows; } function yyza_download_file_response($file, $downloadName = '') { if (!yyza_zip_keep_file_allowed($file)) { http_response_code(403); echo '๋ค์ด๋ก๋ ํ์ฉ ๊ฒฝ๋ก ๋ฐ์ด๊ฑฐ๋ ZIP ํ์ผ์ด ์๋๋๋ค.'; exit; } $file = yyza_norm_slash(realpath($file)); if ($downloadName === '') $downloadName = basename($file); $downloadName = basename(yyza_norm_slash($downloadName)); if ($downloadName === '') $downloadName = 'download.zip'; while (ob_get_level() > 0) @ob_end_clean(); header('Content-Type: application/zip'); header('Content-Length: ' . filesize($file)); header('Content-Disposition: attachment; filename="' . str_replace('"', '', $downloadName) . '"; filename*=UTF-8\'\'' . rawurlencode($downloadName)); header('X-Content-Type-Options: nosniff'); readfile($file); exit; } function yyza_zip_keep_download_url($file, $zipKeepRoot) { $qs = array( 'action' => 'zip_keep_download', 'file' => yyza_b64url_encode($file), 'zip_keep_root' => $zipKeepRoot, ); return basename($_SERVER['PHP_SELF']) . '?' . http_build_query($qs, '', '&'); } function yyza_zip_keep_selected_download($files, $zipKeepRoot) { if (!class_exists('ZipArchive')) { http_response_code(500); echo 'PHP ZipArchive ํ์ฅ์ด ์์ต๋๋ค.'; exit; } $zipKeepRoot = yyza_clean_base_dir($zipKeepRoot); $clean = array(); foreach ((array)$files as $file) { $file = yyza_norm_slash((string)$file); if ($file === '') continue; if (!yyza_zip_keep_file_allowed($file)) continue; $real = realpath($file); if ($real === false) continue; $real = yyza_norm_slash($real); if (!in_array($real, $clean, true)) $clean[] = $real; } if (!$clean) { http_response_code(400); echo '์์ถ ๋ค์ด๋ก๋ํ ZIP ํ์ผ์ ์ ํํ์ธ์.'; exit; } $tmp = tempnam(sys_get_temp_dir(), 'yyza_keep_'); if ($tmp === false) { http_response_code(500); echo '์์ ZIP ํ์ผ ์์ฑ ์คํจ'; exit; } @unlink($tmp); $tmpZip = $tmp . '.zip'; $zip = new ZipArchive(); if ($zip->open($tmpZip, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { http_response_code(500); echo '์ ํ ZIP ์์ถ ํ์ผ ์์ฑ ์คํจ'; exit; } $used = array(); foreach ($clean as $file) { $rel = yyza_zip_keep_rel_path($file, $zipKeepRoot); $rel = ltrim(yyza_norm_slash($rel), '/'); if ($rel === '' || yyza_is_bad_zip_name($rel)) $rel = basename($file); $baseRel = $rel; $n = 2; while (isset($used[$rel])) { $pi = pathinfo($baseRel); $dir = isset($pi['dirname']) && $pi['dirname'] !== '.' ? $pi['dirname'] . '/' : ''; $name = isset($pi['filename']) ? $pi['filename'] : basename($baseRel); $ext = isset($pi['extension']) && $pi['extension'] !== '' ? '.' . $pi['extension'] : ''; $rel = $dir . $name . '_' . $n . $ext; $n++; } $used[$rel] = true; $zip->addFile($file, $rel); } $zip->close(); $downloadName = 'zip_keep_selected_' . date('Ymd_His') . '.zip'; while (ob_get_level() > 0) @ob_end_clean(); header('Content-Type: application/zip'); header('Content-Length: ' . filesize($tmpZip)); header('Content-Disposition: attachment; filename="' . $downloadName . '"; filename*=UTF-8\'\'' . rawurlencode($downloadName)); header('X-Content-Type-Options: nosniff'); readfile($tmpZip); @unlink($tmpZip); exit; } function yyza_flat_path_list() { $paths = array(); $file = yyza_flat_conf_path(); if (is_file($file)) { $lines = @file($file, FILE_IGNORE_NEW_LINES); if (is_array($lines)) { foreach ($lines as $line) { $line = trim((string)$line); if ($line === '' || substr($line, 0, 1) === '#') continue; $line = yyza_clean_base_dir($line); if ($line !== '' && !in_array($line, $paths, true)) $paths[] = $line; } } } $confDir = yyza_conf_flat_base_dir(); if ($confDir !== '' && !in_array($confDir, $paths, true)) array_unshift($paths, $confDir); $scriptDir = yyza_script_dir(); if (!in_array($scriptDir, $paths, true)) $paths[] = $scriptDir; return $paths; } function yyza_default_flat_dir() { $paths = yyza_flat_path_list(); return $paths ? $paths[0] : yyza_script_dir(); } function yyza_flat_base_dir_from_request() { $dir = isset($_REQUEST['flat_base_dir']) ? trim((string)$_REQUEST['flat_base_dir']) : ''; if ($dir === '') $dir = yyza_default_flat_dir(); return yyza_clean_base_dir($dir); } function yyza_write_flat_path_list($paths) { $clean = array(); foreach ((array)$paths as $path) { $path = yyza_clean_base_dir($path); if ($path === '') continue; if (!in_array($path, $clean, true)) $clean[] = $path; } if (!$clean) $clean[] = yyza_script_dir(); return @file_put_contents(yyza_flat_conf_path(), implode("\n", $clean) . "\n", LOCK_EX) !== false; } function yyza_remember_flat_base_dir($dir) { $dir = yyza_clean_base_dir($dir); if ($dir === '') return false; $paths = yyza_flat_path_list(); $new = array($dir); foreach ($paths as $p) { if ($p !== $dir) $new[] = $p; } return yyza_write_flat_path_list($new); } function yyza_save_paths_text($text, $selected = '') { $lines = preg_split('/\r\n|\r|\n/', (string)$text); $selected = yyza_clean_base_dir($selected); if ($selected !== '') array_unshift($lines, $selected); return yyza_write_flat_path_list($lines); } function yyza_debug_enabled() { return (isset($_GET['debug']) && (string)$_GET['debug'] === '1'); } function yyza_is_flat_zip_rel($name) { $name = yyza_norm_slash($name); $name = ltrim($name, '/'); return ($name !== '' && strpos($name, '/') === false); } function yyza_zip_first_folder_matches_root($name) { $name = yyza_norm_slash($name); $name = ltrim($name, '/'); if ($name === '' || yyza_is_bad_zip_name($name)) return false; $pos = strpos($name, '/'); if ($pos === false) return false; $first = substr($name, 0, $pos); if ($first === '' || $first === '.' || $first === '..') return false; $rootFirst = '/' . $first; return (is_dir($rootFirst) || file_exists($rootFirst)); } function yyza_zip_name_to_target($name, $flatBaseDir = '') { $name = yyza_norm_slash($name); $name = ltrim($name, '/'); if ($name === '') return '/'; $rootTarget = '/' . $name; // ํต์ฌ ๊ท์น: // ZIP ์ฒซ ๋ฒ์งธ ํด๋๋ช ์ด ์๋ฒ ๋ฃจํธ์ ์ค์ ํด๋์ ๊ฐ์ผ๋ฉด ๊ทธ ์๋๋ ์ ํด๋์ฌ๋ ๋ฌด์กฐ๊ฑด / ๊ธฐ์ค์ด๋ค. // ์: data/_py/a.php + /data ์กด์ฌ => /data/_py/a.php // ์: var/www/a.php + /var ์กด์ฌ => /var/www/a.php // ์: ddd/a.php + /ddd ์์ => ์ ํํ ZIP ํ์ผ ์ ์ฉ ๊ธฐ์ค ํด๋ ์๋ if (yyza_zip_first_folder_matches_root($name)) { return $rootTarget; } if ($flatBaseDir === '') $flatBaseDir = yyza_default_flat_dir(); $flatBaseDir = yyza_clean_base_dir($flatBaseDir); return ($flatBaseDir === '/' ? '' : $flatBaseDir) . '/' . $name; } function yyza_zip_rel_matches_root($name) { return yyza_zip_first_folder_matches_root($name); } function yyza_session_root_matched($sid) { $rows = yyza_scan_unzip($sid, '/'); if (!$rows) return false; $total = 0; $matched = 0; foreach ($rows as $row) { $zipRel = isset($row['zip_rel']) ? $row['zip_rel'] : (isset($row['rel']) ? $row['rel'] : ''); if ($zipRel === '' || yyza_is_bad_zip_name($zipRel)) continue; $total++; if (yyza_zip_rel_matches_root($zipRel)) $matched++; } return ($total > 0 && $total === $matched); } function yyza_safe_session_id($sid) { $sid = strtolower(trim((string)$sid)); if ($sid === '') return ''; if (preg_match('/^[a-f0-9]{4}$/', $sid)) return $sid; return yyza_safe_keep_session_id($sid); } function yyza_make_session_id() { return substr(md5(date('YmdHis') . microtime(true) . mt_rand()), 0, 4); } function yyza_is_short_session_id($sid) { return preg_match('/^[a-f0-9]{4}$/', (string)$sid) ? true : false; } function yyza_zip_base_without_version($name) { $name = basename(yyza_norm_slash((string)$name)); $name = preg_replace('/\.zip$/i', '', $name); $name = preg_replace('/[_-]v[0-9]+(?:[._-][0-9]+)*[a-z]?(?:[_-].*)?$/i', '', $name); $name = preg_replace('/[^A-Za-z0-9._-]+/', '_', $name); $name = trim($name, '._- '); if ($name === '') $name = 'zip_upload'; return strtolower($name); } function yyza_safe_keep_session_id($sid) { $sid = strtolower(trim((string)$sid)); $sid = preg_replace('/[^a-z0-9._-]+/', '_', $sid); $sid = trim($sid, '._- '); if ($sid === '' || $sid === '.' || $sid === '..') return ''; if (strlen($sid) > 80) $sid = substr($sid, 0, 80); if (!preg_match('/^[a-z0-9][a-z0-9._-]*$/', $sid)) return ''; return $sid; } function yyza_session_is_keep($sid) { $sid = yyza_safe_session_id($sid); return ($sid !== '' && !yyza_is_short_session_id($sid)); } function yyza_keep_original_zip_from_request() { return (isset($_REQUEST['keep_original_zip']) && (string)$_REQUEST['keep_original_zip'] === '1'); } function yyza_safe_upload_zip_filename($name) { $name = yyza_upload_label($name); $name = str_replace(array("\0", "/", "\\", ":", "*", "?", "\"", "<", ">", "|"), "_", $name); $name = preg_replace('/[[:cntrl:]]+/', '_', $name); $name = trim($name, " .\t\r\n"); if ($name === '' || $name === '.' || $name === '..') $name = 'upload.zip'; if (!preg_match('/\.zip$/i', $name)) $name .= '.zip'; return $name; } function yyza_upload_label($name) { $name = basename(yyza_norm_slash((string)$name)); return $name !== '' ? $name : 'upload.zip'; } function yyza_session_manifest_path($sid) { $paths = yyza_session_paths($sid); if (!$paths) return ''; return $paths['base'] . '/manifest.json'; } function yyza_read_session_manifest($sid) { $paths = yyza_session_paths($sid); $manifest = array('sid' => $sid, 'uploads' => array()); if (!$paths) return $manifest; $file = $paths['base'] . '/manifest.json'; if (is_file($file)) { $json = json_decode((string)@file_get_contents($file), true); if (is_array($json)) $manifest = array_merge($manifest, $json); } if (!isset($manifest['uploads']) || !is_array($manifest['uploads'])) { $manifest['uploads'] = array(); } return $manifest; } function yyza_write_session_manifest($sid, $manifest) { $paths = yyza_session_paths($sid); if (!$paths) return false; $manifest['sid'] = $sid; $manifest['updated_at'] = date('Y-m-d H:i:s'); if (!isset($manifest['uploads']) || !is_array($manifest['uploads'])) $manifest['uploads'] = array(); return @file_put_contents($paths['base'] . '/manifest.json', json_encode($manifest, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)) !== false; } function yyza_next_upload_seq($manifest) { $max = 0; if (isset($manifest['uploads']) && is_array($manifest['uploads'])) { foreach ($manifest['uploads'] as $u) { $seq = isset($u['seq']) ? (int)$u['seq'] : 0; if ($seq > $max) $max = $seq; } } return $max + 1; } function yyza_make_upload_key($seq, $origName) { $hash = substr(sha1((string)$origName . uniqid('', true) . mt_rand()), 0, 8); return sprintf('%03d_%s_%s', (int)$seq, date('His'), $hash); } function yyza_add_session_upload($sid, $upload) { $manifest = yyza_read_session_manifest($sid); if (!isset($manifest['uploads']) || !is_array($manifest['uploads'])) $manifest['uploads'] = array(); $manifest['uploads'][] = $upload; return yyza_write_session_manifest($sid, $manifest); } function yyza_session_upload_groups($sid) { $paths = yyza_session_paths($sid); if (!$paths) return array(); $manifest = yyza_read_session_manifest($sid); $groups = array(); $seen = array(); if (isset($manifest['uploads']) && is_array($manifest['uploads'])) { foreach ($manifest['uploads'] as $u) { $key = isset($u['key']) ? (string)$u['key'] : ''; if ($key === '') continue; $root = $paths['unzip'] . '/' . $key; if (!is_dir($root)) continue; $seen[$key] = true; $groups[] = array( 'key' => $key, 'root' => yyza_norm_slash($root), 'name' => isset($u['orig']) ? (string)$u['orig'] : $key, 'seq' => isset($u['seq']) ? (int)$u['seq'] : count($groups) + 1, 'uploaded_at' => isset($u['uploaded_at']) ? (string)$u['uploaded_at'] : '', 'file_count' => isset($u['file_count']) ? (int)$u['file_count'] : 0, ); } } if ($groups) { $dirs = @scandir($paths['unzip']); if (is_array($dirs)) { foreach ($dirs as $d) { if ($d === '.' || $d === '..') continue; if (isset($seen[$d])) continue; $root = $paths['unzip'] . '/' . $d; if (!is_dir($root)) continue; $groups[] = array( 'key' => $d, 'root' => yyza_norm_slash($root), 'name' => $d, 'seq' => count($groups) + 1, 'uploaded_at' => '', 'file_count' => 0, ); } } usort($groups, function($a, $b) { $sa = isset($a['seq']) ? (int)$a['seq'] : 0; $sb = isset($b['seq']) ? (int)$b['seq'] : 0; if ($sa !== $sb) return $sa <=> $sb; return strcmp((string)$a['key'], (string)$b['key']); }); return $groups; } // v031 ์ด์ ์ฒ๋ผ unzip ๋ฃจํธ์ ๋ฐ๋ก ํ๋ฆฐ ์ธ์ ์ ํ ๋ฌถ์์ผ๋ก ์ฝ๋๋ค. return array(array( 'key' => '', 'root' => yyza_norm_slash($paths['unzip']), 'name' => '๊ธฐ์กด ์ธ์ ', 'seq' => 1, 'uploaded_at' => '', 'file_count' => 0, )); } function yyza_group_rows_by_upload($rows) { $groups = array(); foreach ((array)$rows as $row) { $key = isset($row['upload_key']) ? (string)$row['upload_key'] : '_legacy'; if ($key === '') $key = '_legacy'; if (!isset($groups[$key])) { $groups[$key] = array( 'key' => $key, 'name' => isset($row['zip_name']) ? (string)$row['zip_name'] : '๊ธฐ์กด ์ธ์ ', 'seq' => isset($row['upload_seq']) ? (int)$row['upload_seq'] : count($groups) + 1, 'uploaded_at' => isset($row['uploaded_at']) ? (string)$row['uploaded_at'] : '', 'rows' => array(), ); } $groups[$key]['rows'][] = $row; } uasort($groups, function($a, $b) { $sa = isset($a['seq']) ? (int)$a['seq'] : 0; $sb = isset($b['seq']) ? (int)$b['seq'] : 0; if ($sa !== $sb) return $sa <=> $sb; return strcmp((string)$a['key'], (string)$b['key']); }); return array_values($groups); } function yyza_mkdir($dir) { if (is_dir($dir)) return true; return @mkdir($dir, 0775, true); } function yyza_allowed_target($path) { global $ALLOWED_ROOTS; $path = yyza_norm_slash($path); if ($path === '' || $path[0] !== '/') return false; if (strpos($path, "\0") !== false) return false; if (function_exists('is_allowed_path') && isset($ALLOWED_ROOTS) && is_array($ALLOWED_ROOTS)) { if (file_exists($path) || file_exists(dirname($path))) { return is_allowed_path($path, $ALLOWED_ROOTS); } foreach ($ALLOWED_ROOTS as $root) { $rootReal = realpath($root); if ($rootReal === false) continue; $rootReal = rtrim(yyza_norm_slash($rootReal), '/'); if ($path === $rootReal || strpos($path, $rootReal . '/') === 0) return true; } foreach (yyza_flat_path_list() as $root) { $rootReal = realpath($root); if ($rootReal === false) continue; $rootReal = rtrim(yyza_norm_slash($rootReal), '/'); if ($path === $rootReal || strpos($path, $rootReal . '/') === 0) return true; } return false; } $fallbackRoots = array_merge(yyza_flat_path_list(), array('/var/www/html', '/data/php56-web/htdocs', '/data/_py')); foreach ($fallbackRoots as $root) { $rootReal = realpath($root); if ($rootReal === false) continue; $rootReal = rtrim(yyza_norm_slash($rootReal), '/'); if ($path === $rootReal || strpos($path, $rootReal . '/') === 0) return true; } return false; } function yyza_format_bytes($bytes) { $bytes = (float)$bytes; if ($bytes >= 1048576) return number_format($bytes / 1048576, 2) . ' MB'; if ($bytes >= 1024) return number_format($bytes / 1024, 2) . ' KB'; return number_format($bytes, 0) . ' B'; } function yyza_load_pct($value, $max) { $value = (float)$value; $max = (float)$max; if ($max <= 0) return 0; $pct = ($value / $max) * 100; if ($pct < 0) $pct = 0; if ($pct > 100) $pct = 100; return $pct; } function yyza_load_level($value, $good, $warn) { $value = (float)$value; if ($value <= (float)$good) return 'ok'; if ($value <= (float)$warn) return 'warn'; return 'danger'; } function yyza_load_bs_class($value, $good, $warn) { $level = yyza_load_level($value, $good, $warn); if ($level === 'ok') return 'bg-success'; if ($level === 'warn') return 'bg-warning'; return 'bg-danger'; } function yyza_load_level_text($value, $good, $warn) { $level = yyza_load_level($value, $good, $warn); if ($level === 'ok') return '์ํธ'; if ($level === 'warn') return '์ฃผ์'; return '๊ฒฝ๊ณ '; } function yyza_load_meter($label, $currentText, $maxText, $value, $good, $warn, $max, $tip = '') { $pct = yyza_load_pct($value, $max); $bs = yyza_load_bs_class($value, $good, $warn); $levelText = yyza_load_level_text($value, $good, $warn); $title = $tip !== '' ? $tip : ('ํ์ฌ ' . $currentText . ' / ์ต๋ ' . $maxText); return '<div class="yyza-load-meter" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="' . yyza_h($title) . '">' . '<div class="yyza-load-head"><span>' . yyza_h($label) . '</span><b>' . yyza_h($currentText . ' / ' . $maxText . ' ยท ' . $levelText) . '</b></div>' . '<div class="progress yyza-load-progress" role="progressbar" aria-valuenow="' . yyza_h(number_format($pct, 1, '.', '')) . '" aria-valuemin="0" aria-valuemax="100">' . '<div class="progress-bar ' . yyza_h($bs) . '" style="width:' . yyza_h(number_format($pct, 2, '.', '')) . '%"></div>' . '</div>' . '</div>'; } function yyza_file_size_bar($bytes) { $bytes = (int)$bytes; if ($bytes < 0) $bytes = 0; return '<div class="yyza-file-size-bar">' . yyza_load_meter('๊ฐ๋ณ์ฉ๋', yyza_format_bytes($bytes), '1 MB', $bytes, 262144, 1048576, 5242880, '๊ฐ๋ณ ํ์ผ ๊ธฐ์ค: ์ถ์ฒโค256KB / ์ฃผ์โค1MB / ๊ฒฝ๊ณ >1MB') . '</div>'; } function yyza_file_time($path) { if (!file_exists($path)) return '-'; $t = @filemtime($path); return $t ? date('Y-m-d H:i:s', $t) : '-'; } function yyza_file_size_text($path) { if (!file_exists($path)) return '-'; $s = @filesize($path); if ($s === false) return '-'; return yyza_format_bytes($s); } function yyza_file_size_raw($path) { if (!file_exists($path)) return false; $s = @filesize($path); return ($s === false) ? false : (int)$s; } function yyza_md5_match_if_same_size($oldPath, $newPath) { $oldSize = yyza_file_size_raw($oldPath); $newSize = yyza_file_size_raw($newPath); if ($oldSize === false || $newSize === false || $oldSize !== $newSize) return false; $oldMd5 = @md5_file($oldPath); $newMd5 = @md5_file($newPath); return ($oldMd5 !== false && $newMd5 !== false && $oldMd5 === $newMd5); } function yyza_ext($path, $isDir = false) { global $CMD_EXT_STYLES; if ($isDir) return 'dir'; $ext = strtolower(pathinfo((string)$path, PATHINFO_EXTENSION)); if ($ext === '') $ext = 'default'; if (!isset($CMD_EXT_STYLES[$ext])) $ext = 'default'; return $ext; } function yyza_target_link($path, $text = '') { $ext = yyza_ext($path, is_dir($path)); $label = ($text !== '') ? $text : $path; // yy="yyy-used-files-exclude:start" return '<a class="sty-' . yyza_h($ext) . '" href="yyy_view.php?view=' . yyza_h(yyza_url($path)) . '" target="_blank">' . yyza_h($label) . '</a>'; // yy="yyy-used-files-exclude:end" } function yyza_cmp_checkbox($path) { return '<input type="checkbox" class="cmp-file" value="' . yyza_h($path) . '">'; } function yyza_apply_checkbox($rel, $enabled = true, $checked = true) { return '<input type="checkbox" class="yyza-apply-file" name="apply_files[]" value="' . yyza_h($rel) . '" ' . (($enabled && $checked) ? 'checked' : '') . ($enabled ? '' : ' disabled') . '>'; } function yyza_backup_path($target, $ts) { $target = yyza_norm_slash($target); $backupDir = yyza_backup_root() . '/' . ltrim(dirname($target), '/'); $backupName = basename($target) . '_' . $ts . '.bak'; return rtrim($backupDir, '/') . '/' . $backupName; } function yyza_latest_backup_info($target) { $base = yyza_backup_root() . '/' . ltrim(yyza_norm_slash($target), '/'); $files = glob($base . '_*.bak'); if (!is_array($files)) $files = array(); /* v006 ์ด์ ์์ํ: file.php.20260520_151635.bak ๋ ํน์ ์์ผ๋ฉด ๊ฐ์ด ์ธ์ */ $oldFiles = glob($base . '.*.bak'); if (is_array($oldFiles) && $oldFiles) $files = array_merge($files, $oldFiles); $latest = ''; $latestTime = 0; if (is_array($files)) { foreach ($files as $f) { if (!is_file($f)) continue; $t = @filemtime($f); if (!$t) $t = 0; if ($t >= $latestTime) { $latestTime = $t; $latest = yyza_norm_slash($f); } } } return array( 'exists' => ($latest !== ''), 'path' => $latest, 'time' => $latestTime, 'age' => $latestTime ? yyza_time_ago($latestTime) : '๋ฐฑ์ ๋ณธ ์์', ); } function yyza_time_ago($ts) { $diff = time() - (int)$ts; if ($diff < 0) $diff = 0; if ($diff < 60) return $diff . '์ด ์ '; if ($diff < 3600) return floor($diff / 60) . '๋ถ ์ '; if ($diff < 86400) return floor($diff / 3600) . '์๊ฐ ์ '; if ($diff < 2592000) return floor($diff / 86400) . '์ผ ์ '; return date('Y-m-d H:i:s', (int)$ts); } function yyza_upload_files_from_request() { if (!isset($_FILES['zip_file'])) return array(); $f = $_FILES['zip_file']; $out = array(); if (isset($f['name']) && is_array($f['name'])) { $cnt = count($f['name']); for ($i = 0; $i < $cnt; $i++) { $out[] = array( 'name' => isset($f['name'][$i]) ? $f['name'][$i] : '', 'type' => isset($f['type'][$i]) ? $f['type'][$i] : '', 'tmp_name' => isset($f['tmp_name'][$i]) ? $f['tmp_name'][$i] : '', 'error' => isset($f['error'][$i]) ? $f['error'][$i] : UPLOAD_ERR_NO_FILE, 'size' => isset($f['size'][$i]) ? $f['size'][$i] : 0, ); } } else { $out[] = $f; } return $out; } function yyza_copy_upload_zip($upload, $reuseSid = '', $keepOriginalZip = false, $zipKeepRoot = '') { if (!isset($upload['error']) || $upload['error'] !== UPLOAD_ERR_OK) { return array(false, 'ZIP ์ ๋ก๋ ์คํจ: error=' . (isset($upload['error']) ? $upload['error'] : 'unknown')); } $origName = isset($upload['name']) ? yyza_upload_label((string)$upload['name']) : 'upload.zip'; if (!preg_match('/\.zip$/i', $origName)) { return array(false, 'zip ํ์ผ๋ง ์ ๋ก๋ ๊ฐ๋ฅํฉ๋๋ค: ' . $origName); } $zipKeepRoot = yyza_clean_base_dir($zipKeepRoot !== '' ? $zipKeepRoot : yyza_default_zip_keep_root()); if ($zipKeepRoot === '') { return array(false, 'ZIP ์๋ณธ์ ์ฅ ๊ธฐ์คํด๋๋ฅผ ๋จผ์ ์ค์ ํ์ธ์.'); } // v043 ํต์ฌ ๊ท์น: // /data/_bak/yyy_zip/{sid} 4์๋ฆฌ ์์ ์ธ์ ์ ๋ ์ด์ ์ฌ์ฉํ์ง ์๋๋ค. // ํญ์ ZIP ์๋ณธ์ ์ฅ ๊ธฐ์คํด๋/๋ฒ์ ์ ์ธํ์ผ๋ช / ์๋์์ uploads/unzip ์์ ์ ์ํํ๋ค. $reuseSid = yyza_safe_session_id($reuseSid); if ($reuseSid !== '' && yyza_is_short_session_id($reuseSid)) { $reuseSid = ''; } $projectSid = yyza_zip_base_without_version($origName); $sid = $reuseSid !== '' ? $reuseSid : $projectSid; $reusePaths = $sid !== '' ? yyza_session_paths($sid) : false; if ($reusePaths) { $sid = $reusePaths['sid']; $base = $reusePaths['base']; $unzipRoot = $reusePaths['unzip']; } else { $base = rtrim($zipKeepRoot, '/') . '/' . $sid; $unzipRoot = $base . '/unzip'; } $uploadsDir = $base . '/uploads'; if (!yyza_mkdir($unzipRoot) || !yyza_mkdir($uploadsDir)) { return array(false, 'ZIP ๋ณด๊ด/์์ถํด์ ํด๋ ์์ฑ ์คํจ: ' . $base); } $manifest = yyza_read_session_manifest($sid); $seq = yyza_next_upload_seq($manifest); $uploadKey = yyza_make_upload_key($seq, $origName); $unzip = $unzipRoot . '/' . $uploadKey; if (!yyza_mkdir($unzip)) { return array(false, '์ ๋ก๋๋ณ ์์ถํด์ ํด๋ ์์ฑ ์คํจ: ' . $unzip); } $safeOrigZipName = yyza_safe_upload_zip_filename($origName); // v045: uploads/ ์๋ ZIP ์๋ณธ ํ์ผ๋ช ์๋ uploadKey ์ ๋์ด๋ฅผ ์ ๋ ๋ถ์ด์ง ์๋๋ค. // uploadKey๋ unzip/manifest ๋ฌถ์ ๊ตฌ๋ถ์ฉ์ผ๋ก๋ง ์ฌ์ฉํ๋ค. $zipStoreName = $safeOrigZipName; $zipPath = $uploadsDir . '/' . $zipStoreName; if (is_file($zipPath) && !@unlink($zipPath)) { return array(false, '๊ธฐ์กด ์๋ณธ ZIP ๋ฎ์ด์ฐ๊ธฐ ์ค๋น ์คํจ: ' . $zipPath); } if (!@move_uploaded_file($upload['tmp_name'], $zipPath)) { return array(false, '์ ๋ก๋ ZIP ์๋ณธ ์ ์ฅ ์คํจ: ' . $zipPath); } return array(true, array( 'sid' => $sid, 'base' => $base, 'unzip' => $unzip, 'zip' => $zipPath, 'zip_store_name' => $zipStoreName, 'orig' => $origName, 'key' => $uploadKey, 'seq' => $seq, 'keep_original_zip' => $keepOriginalZip ? 1 : 0, 'zip_project' => $projectSid, 'reused' => $reusePaths ? 1 : 0, )); } function yyza_extract_zip($zipPath, $unzipRoot) { if (!class_exists('ZipArchive')) { return array(false, 'PHP ZipArchive ํ์ฅ์ด ์์ต๋๋ค.'); } $zip = new ZipArchive(); if ($zip->open($zipPath) !== true) { return array(false, 'ZIP ์ด๊ธฐ ์คํจ: ' . $zipPath); } $items = array(); for ($i = 0; $i < $zip->numFiles; $i++) { $st = $zip->statIndex($i); if (!$st || !isset($st['name'])) continue; $name = yyza_norm_slash($st['name']); $isDir = (substr($name, -1) === '/'); if ($isDir) continue; if (yyza_is_bad_zip_name($name)) { $items[] = array('name' => $name, 'error' => 'BAD_PATH'); continue; } $rel = ltrim($name, '/'); $dest = rtrim($unzipRoot, '/') . '/' . $rel; $destDir = dirname($dest); if (!yyza_mkdir($destDir)) { $items[] = array('name' => $name, 'error' => 'UNZIP_DIR_FAIL'); continue; } $in = $zip->getStream($st['name']); if (!$in) { $items[] = array('name' => $name, 'error' => 'ZIP_STREAM_FAIL'); continue; } $out = @fopen($dest, 'wb'); if (!$out) { fclose($in); $items[] = array('name' => $name, 'error' => 'UNZIP_WRITE_FAIL'); continue; } while (!feof($in)) { fwrite($out, fread($in, 1048576)); } fclose($in); fclose($out); if (isset($st['mtime']) && $st['mtime']) { @touch($dest, (int)$st['mtime']); } $items[] = array('name' => $name, 'rel' => $rel, 'local' => $dest, 'target' => yyza_zip_name_to_target($name, yyza_default_flat_dir()), 'error' => ''); } $zip->close(); return array(true, $items); } function yyza_session_paths($sid) { $sid = yyza_safe_session_id($sid); if ($sid === '' || yyza_is_short_session_id($sid)) return false; // v043: 4์๋ฆฌ /data/_bak/yyy_zip/{sid} ์์ ์ธ์ ์ ์ฌ์ฉํ์ง ์๋๋ค. // ๋ฑ๋ก๋ ZIP ์๋ณธ์ ์ฅ ๊ธฐ์คํด๋ ์๋์ ํ๋ก์ ํธ ํด๋๋ง ์ธ์ ์ผ๋ก ์ธ์ ํ๋ค. foreach (yyza_zip_keep_path_list() as $root) { $root = yyza_clean_base_dir($root); if ($root === '') continue; $base = rtrim($root, '/') . '/' . $sid; $unzip = $base . '/unzip'; if (is_dir($unzip)) return array('sid' => $sid, 'base' => yyza_norm_slash($base), 'unzip' => yyza_norm_slash($unzip)); } return false; } function yyza_scan_unzip($sid, $flatBaseDir = '') { $groups = yyza_session_upload_groups($sid); if (!$groups) return array(); $out = array(); foreach ($groups as $group) { $root = rtrim($group['root'], '/'); if (!is_dir($root)) continue; $it = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($it as $fi) { if (!$fi->isFile()) continue; $local = yyza_norm_slash($fi->getPathname()); $zipRel = ltrim(substr($local, strlen($root)), '/'); if ($zipRel === '' || yyza_is_bad_zip_name($zipRel)) continue; $uploadKey = isset($group['key']) ? (string)$group['key'] : ''; $applyRel = ($uploadKey !== '' ? $uploadKey . '/' : '') . $zipRel; $target = yyza_zip_name_to_target($zipRel, $flatBaseDir); $out[] = array( 'rel' => $applyRel, 'zip_rel' => $zipRel, 'local' => $local, 'target' => $target, 'upload_key' => $uploadKey, 'zip_name' => isset($group['name']) ? (string)$group['name'] : '๊ธฐ์กด ์ธ์ ', 'upload_seq' => isset($group['seq']) ? (int)$group['seq'] : 0, 'uploaded_at' => isset($group['uploaded_at']) ? (string)$group['uploaded_at'] : '', ); } } usort($out, function($a, $b) { $sa = isset($a['upload_seq']) ? (int)$a['upload_seq'] : 0; $sb = isset($b['upload_seq']) ? (int)$b['upload_seq'] : 0; if ($sa !== $sb) return $sa <=> $sb; return strcmp($a['target'], $b['target']); }); return $out; } function yyza_dir_create_info($target) { $target = yyza_norm_slash($target); $dir = dirname($target); $parent = $dir; while ($parent !== '/' && $parent !== '.' && !is_dir($parent)) { $parent = dirname($parent); } $parentExists = is_dir($parent); $parentWritable = ($parentExists && is_writable($parent)); $allowed = yyza_allowed_target($target); $canCreate = ($allowed && $parentExists && $parentWritable); return array( 'target_dir' => $dir, 'parent' => $parent, 'parent_exists' => $parentExists, 'parent_writable' => $parentWritable, 'allowed' => $allowed, 'can_create' => $canCreate, ); } function yyza_dir_create_tip($target) { $info = yyza_dir_create_info($target); return '์์ฑํ ํด๋: ' . $info['target_dir'] . " " . '๊ธฐ์กด ์์ํด๋: ' . $info['parent'] . " " . 'ํ์ฉ๊ฒฝ๋ก: ' . ($info['allowed'] ? 'Y' : 'N') . " " . '์์ํด๋ ์กด์ฌ: ' . ($info['parent_exists'] ? 'Y' : 'N') . " " . '์์ํด๋ ์ฐ๊ธฐ๊ถํ: ' . ($info['parent_writable'] ? 'Y' : 'N'); } function yyza_status_for($target) { $dir = dirname($target); if (!yyza_allowed_target($target)) return array('BLOCKED', 'ํ์ฉ๊ฒฝ๋ก๋ฐ', 'table-secondary'); if (!is_dir($dir)) { $info = yyza_dir_create_info($target); if (!empty($info['can_create'])) return array('DIR_CREATABLE', '๋ง๋ค์ด์ผํจ', 'table-primary'); return array('DIR_BLOCKED', '๊ถํ์กฐ์ ํ์', 'table-danger yyza-dir-blocked'); } if (file_exists($target)) return array('EXISTS', '๊ธฐ์กด์์', 'table-danger'); return array('NEW', '์ ๊ท', 'table-success'); } function yyza_handle_upload($reuseSid = '', $keepOriginalZip = false, $zipKeepRoot = '') { $uploads = yyza_upload_files_from_request(); if (!$uploads) { return array('', 'zip_file ์์'); } $sid = yyza_safe_session_id($reuseSid); $okCount = 0; $names = array(); $errors = array(); foreach ($uploads as $upload) { if (!isset($upload['error']) || $upload['error'] === UPLOAD_ERR_NO_FILE) continue; list($ok, $saved) = yyza_copy_upload_zip($upload, $sid, $keepOriginalZip, $zipKeepRoot); if (!$ok) { $errors[] = $saved; continue; } $sid = $saved['sid']; list($ok2, $items) = yyza_extract_zip($saved['zip'], $saved['unzip']); if (!$ok2) { $errors[] = $saved['orig'] . ': ' . $items; continue; } $zipDeleted = 0; if (!$keepOriginalZip && is_file($saved['zip'])) { $zipDeleted = @unlink($saved['zip']) ? 1 : 0; if (!$zipDeleted) { $errors[] = $saved['orig'] . ': ์๋ณธ ZIP ์ญ์ ์คํจ: ' . $saved['zip']; } } $fileCount = 0; foreach ($items as $it) { if (isset($it['rel']) && $it['rel'] !== '' && empty($it['error'])) $fileCount++; } yyza_add_session_upload($sid, array( 'key' => $saved['key'], 'seq' => $saved['seq'], 'orig' => $saved['orig'], 'zip' => $saved['zip'], 'zip_store_name' => isset($saved['zip_store_name']) ? $saved['zip_store_name'] : basename($saved['zip']), 'unzip' => $saved['unzip'], 'uploaded_at' => date('Y-m-d H:i:s'), 'file_count' => $fileCount, 'keep_original_zip' => isset($saved['keep_original_zip']) ? (int)$saved['keep_original_zip'] : 0, 'zip_deleted' => isset($zipDeleted) ? (int)$zipDeleted : 0, 'zip_project' => isset($saved['zip_project']) ? (string)$saved['zip_project'] : '', )); $okCount++; $names[] = $saved['orig']; } if ($okCount < 1) { return array('', $errors ? implode(' / ', $errors) : '์ ๋ก๋๋ ZIP ์์'); } $msg = $okCount . '๊ฐ ZIP ์ ๋ก๋/์์ถํด์ ์๋ฃ'; if ($names) $msg .= ': ' . implode(', ', $names); if ($errors) $msg .= ' / ์ผ๋ถ ์ค๋ฅ: ' . implode(' / ', $errors); return array($sid, $msg); } function yyza_rm_rf($path) { $path = yyza_norm_slash($path); if ($path === '' || $path === '/' || strpos($path, yyza_zip_root() . '/') !== 0) return false; if (!file_exists($path)) return true; if (is_file($path) || is_link($path)) return @unlink($path); $it = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST ); foreach ($it as $fi) { if ($fi->isDir() && !$fi->isLink()) @rmdir($fi->getPathname()); else @unlink($fi->getPathname()); } return @rmdir($path); } function yyza_rm_rf_under($path, $allowedRoot) { $path = yyza_norm_slash($path); $allowedRoot = rtrim(yyza_norm_slash($allowedRoot), '/'); if ($path === '' || $path === '/' || $allowedRoot === '' || strpos($path, $allowedRoot . '/') !== 0) return false; if (!file_exists($path)) return true; if (is_file($path) || is_link($path)) return @unlink($path); $it = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST ); foreach ($it as $fi) { if ($fi->isDir() && !$fi->isLink()) @rmdir($fi->getPathname()); else @unlink($fi->getPathname()); } return @rmdir($path); } function yyza_empty_dir_under($dir, $allowedRoot) { $dir = yyza_norm_slash($dir); $allowedRoot = rtrim(yyza_norm_slash($allowedRoot), '/'); if ($dir === '' || $dir === '/' || $allowedRoot === '' || strpos($dir, $allowedRoot . '/') !== 0) return false; if (!is_dir($dir)) { return @mkdir($dir, 0775, true) || is_dir($dir); } $items = @scandir($dir); if (!is_array($items)) return false; foreach ($items as $item) { if ($item === '.' || $item === '..') continue; $path = rtrim($dir, '/') . '/' . $item; if (!yyza_rm_rf_under($path, $allowedRoot)) return false; } return true; } function yyza_cleanup_session($sid, $keepOriginalZip = false) { $sid = yyza_safe_session_id($sid); if ($sid === '' || yyza_is_short_session_id($sid)) return array(false, '์ ๋ฆฌํ ํ๋ก์ ํธ ์ธ์ ์์'); $paths = yyza_session_paths($sid); if (!$paths) return array(false, '์ ๋ฆฌํ ํ๋ก์ ํธ ํด๋ ์์: ' . $sid); $zipBase = $paths['base']; $zipUploads = $zipBase . '/uploads'; $zipUnzip = $zipBase . '/unzip'; $viewBase = yyza_view_drop_root() . '/' . $sid; $matchedRoot = ''; foreach (yyza_zip_keep_path_list() as $root) { $root = yyza_clean_base_dir($root); if ($root !== '' && strpos($zipBase, rtrim($root, '/') . '/') === 0) { $matchedRoot = $root; break; } } if ($matchedRoot === '') return array(false, 'ZIP ์๋ณธ์ ์ฅ ๊ธฐ์คํด๋ ๋ฐ์ด๋ผ ์ญ์ /์ ๋ฆฌ ์ฐจ๋จ: ' . $zipBase); if (!$keepOriginalZip) { $zipOk = yyza_rm_rf_under($zipBase, $matchedRoot); $viewOk = yyza_rm_rf_under($viewBase, yyza_view_drop_root()); if ($zipOk && $viewOk) return array(true, '์ด๊ธฐํ ์๋ฃ ยท ์๋ณธZIP ์ ์ง OFF ยท ํ๋ก์ ํธ ํด๋ ์ ์ฒด ์ญ์ : ' . $sid); return array(false, '์ด๊ธฐํ ์คํจ ยท ํ๋ก์ ํธ ํด๋ ์ญ์ ์คํจ: ' . $zipBase . ' / ' . $viewBase); } if (!is_dir($zipBase)) @mkdir($zipBase, 0775, true); if (!is_dir($zipUploads)) @mkdir($zipUploads, 0775, true); if (!is_dir($zipUnzip)) @mkdir($zipUnzip, 0775, true); if (!is_dir($viewBase)) @mkdir($viewBase, 0775, true); $unzipOk = yyza_empty_dir_under($zipUnzip, $matchedRoot); $viewOk = yyza_rm_rf_under($viewBase, yyza_view_drop_root()); $manifest = yyza_read_session_manifest($sid); $manifest['keep_original_zip'] = 1; $manifest['reset_at'] = date('Y-m-d H:i:s'); $manifestOk = yyza_write_session_manifest($sid, $manifest); if ($unzipOk && $viewOk && $manifestOk) { return array(true, '์ด๊ธฐํ ์๋ฃ ยท ์๋ณธ ZIP ์ ์ง ยท unzip/view๋ง ์ ๋ฆฌ: ' . $sid); } $msg = '์ด๊ธฐํ ์ผ๋ถ ์คํจ'; if (!$unzipOk) $msg .= ' ยท ZIP unzip ๋น์ฐ๊ธฐ ์คํจ: ' . $zipUnzip; if (!$viewOk) $msg .= ' ยท VIEW ์ ๋ฆฌ ์คํจ: ' . $viewBase; if (!$manifestOk) $msg .= ' ยท manifest ๊ฐฑ์ ์คํจ: ' . $zipBase . '/manifest.json'; return array(false, $msg); } function yyza_apply_session($sid, $applyFiles, $createDirs, $prebackupFiles, $flatBaseDir = '') { $paths = yyza_session_paths($sid); if (!$paths) { return array(false, array(array('status' => 'ERROR', 'msg' => '์ธ์ ์์'))); } $allow = array(); foreach ((array)$applyFiles as $rel) { $rel = yyza_norm_slash($rel); if ($rel === '' || yyza_is_bad_zip_name($rel)) continue; $allow[$rel] = true; } $prebackup = array(); foreach ((array)$prebackupFiles as $rel) { $rel = yyza_norm_slash($rel); if ($rel === '' || yyza_is_bad_zip_name($rel)) continue; $prebackup[$rel] = true; } if (!$allow) { return array(false, array(array('status' => 'ERROR', 'msg' => '์ ์ฉ ์ ํ ํ์ผ ์์'))); } $rows = yyza_scan_unzip($sid, $flatBaseDir); $results = array(); $ts = date('Ymd__H_i_s'); foreach ($rows as $row) { $rel = $row['rel']; if (!isset($allow[$rel])) continue; $src = $row['local']; $target = $row['target']; $dir = dirname($target); $info = array('rel' => $rel, 'src' => $src, 'target' => $target, 'status' => '', 'msg' => '', 'backup' => ''); if (!yyza_allowed_target($target)) { $info['status'] = 'BLOCKED'; $info['msg'] = 'ํ์ฉ ๊ฒฝ๋ก ๋ฐ'; $results[] = $info; continue; } if (!is_file($src)) { $info['status'] = 'ERROR'; $info['msg'] = '์ ๊ท ํ์ผ ์์'; $results[] = $info; continue; } if (!is_dir($dir)) { if (!$createDirs) { $info['status'] = 'DIR_MISSING'; $info['msg'] = '๋์ ํด๋ ์์. ์์ฑ ํ์ฉ ํ์'; $results[] = $info; continue; } if (!yyza_mkdir($dir)) { $info['status'] = 'ERROR'; $info['msg'] = '๋์ ํด๋ ์์ฑ ์คํจ'; $results[] = $info; continue; } } if (file_exists($target)) { $backup = yyza_backup_path($target, $ts); $backupDir = dirname($backup); if (!yyza_mkdir($backupDir)) { $info['status'] = 'ERROR'; $info['msg'] = '๋ฐฑ์ ํด๋ ์์ฑ ์คํจ'; $results[] = $info; continue; } if (isset($prebackup[$rel])) { if (!@copy($target, $backup)) { $info['status'] = 'ERROR'; $info['msg'] = '๊ธฐ์กด ์ด์๋ณธ ์ ๋ฐฑ์ ์คํจ'; $results[] = $info; continue; } $info['backup_mode'] = 'existing_first'; $info['msg'] = '๊ธฐ์กด ์ด์๋ณธ ์ ๋ฐฑ์ ํ ์ ์ฉ'; } else { if (!@copy($src, $backup)) { $info['status'] = 'ERROR'; $info['msg'] = '์ ๊ท ์ ์ฉ๋ณธ ๋ฐฑ์ ์คํจ'; $results[] = $info; continue; } $info['backup_mode'] = 'new_file'; $info['msg'] = '์ ๊ท ์ ์ฉ๋ณธ ๋ฐฑ์ ํ ์ ์ฉ'; } $info['backup'] = $backup; } if (!@copy($src, $target)) { $info['status'] = 'ERROR'; $info['msg'] = 'ํ์ผ ๋ฎ์ด์ฐ๊ธฐ ์คํจ'; $results[] = $info; continue; } @touch($target, filemtime($src)); if (!file_exists($target) || !is_writable($target)) { @chmod($target, 0664); } $info['status'] = 'OK'; if ($info['msg'] === '') { $info['msg'] = '์ ๊ท ํ์ผ ์ ์ฉ ์๋ฃ'; } else { $info['msg'] .= ' ์๋ฃ'; } $results[] = $info; } return array(true, $results); } if (isset($_GET['action']) && (string)$_GET['action'] === 'zip_keep_download') { $file = yyza_b64url_decode(isset($_GET['file']) ? $_GET['file'] : ''); yyza_download_file_response($file); } if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && (string)$_POST['action'] === 'zip_keep_zip_download') { $filesJson = isset($_POST['files_json']) ? (string)$_POST['files_json'] : '[]'; $files = json_decode($filesJson, true); if (!is_array($files)) $files = array(); $zipKeepRootForDownload = yyza_zip_keep_root_from_request(); yyza_zip_keep_selected_download($files, $zipKeepRootForDownload); } $notice = ''; $error = ''; $sid = isset($_REQUEST['sid']) ? yyza_safe_session_id($_REQUEST['sid']) : ''; $applyResults = array(); $resetDone = false; $flatBaseDir = yyza_flat_base_dir_from_request(); $keepOriginalZip = yyza_keep_original_zip_from_request(); $zipKeepRoot = yyza_zip_keep_root_from_request(); $clientTabIdRaw = isset($_REQUEST['aiwk_tab_id']) ? (string)$_REQUEST['aiwk_tab_id'] : (isset($_REQUEST['s_tab']) ? (string)$_REQUEST['s_tab'] : 'global'); $clientTabIdRaw = preg_replace('/^tab:/', '', trim($clientTabIdRaw)); $clientTabKey = preg_replace('/[^A-Za-z0-9._-]+/', '_', $clientTabIdRaw); if ($clientTabKey === '') $clientTabKey = 'global'; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $action = isset($_POST['action']) ? trim((string)$_POST['action']) : ''; if ($action === 'keep_path_save') { $pathsText = isset($_POST['zip_keep_paths_text']) ? (string)$_POST['zip_keep_paths_text'] : ''; $selectedPath = isset($_POST['zip_keep_root']) ? (string)$_POST['zip_keep_root'] : ''; if (yyza_save_zip_keep_paths_text($pathsText, $selectedPath)) { $notice = 'ZIP ์๋ณธ์ ์ฅ ๊ธฐ์คํด๋ ๋ชฉ๋ก ์ ์ฅ ์๋ฃ: ' . yyza_zip_keep_conf_path(); $zipKeepRoot = yyza_clean_base_dir($selectedPath); if ($zipKeepRoot === '') $zipKeepRoot = yyza_default_zip_keep_root(); } else { $error = 'ZIP ์๋ณธ์ ์ฅ ๊ธฐ์คํด๋ ๋ชฉ๋ก ์ ์ฅ ์คํจ: ' . yyza_zip_keep_conf_path(); } } elseif ($action === 'keep_path_select_save') { $zipKeepRoot = yyza_zip_keep_root_from_request(); if (yyza_remember_zip_keep_root($zipKeepRoot)) { yyza_json(array('ok' => true, 'path' => $zipKeepRoot, 'conf' => yyza_zip_keep_conf_path())); } yyza_json(array('ok' => false, 'path' => $zipKeepRoot, 'conf' => yyza_zip_keep_conf_path(), 'error' => 'write failed'), 500); } elseif ($action === 'path_save') { $pathsText = isset($_POST['paths_text']) ? (string)$_POST['paths_text'] : ''; $selectedPath = isset($_POST['flat_base_dir']) ? (string)$_POST['flat_base_dir'] : ''; if (yyza_save_paths_text($pathsText, $selectedPath)) { $notice = '๊ธฐ์ค ํด๋ ๋ชฉ๋ก ์ ์ฅ ์๋ฃ: ' . yyza_flat_conf_path(); $flatBaseDir = yyza_clean_base_dir($selectedPath); if ($flatBaseDir === '') $flatBaseDir = yyza_default_flat_dir(); } else { $error = '๊ธฐ์ค ํด๋ ๋ชฉ๋ก ์ ์ฅ ์คํจ: ' . yyza_flat_conf_path(); } } elseif ($action === 'path_select_save') { $flatBaseDir = yyza_flat_base_dir_from_request(); if (yyza_remember_flat_base_dir($flatBaseDir)) { yyza_json(array('ok' => true, 'path' => $flatBaseDir, 'conf' => yyza_flat_conf_path())); } yyza_json(array('ok' => false, 'path' => $flatBaseDir, 'conf' => yyza_flat_conf_path(), 'error' => 'write failed'), 500); } elseif ($action === 'upload') { $flatBaseDir = yyza_flat_base_dir_from_request(); $keepOriginalZip = yyza_keep_original_zip_from_request(); $zipKeepRoot = yyza_zip_keep_root_from_request(); if (trim((string)(isset($_POST['flat_base_dir']) ? $_POST['flat_base_dir'] : '')) === '') { $error = 'ZIP ํ์ผ ์ ์ฉ ๊ธฐ์ค ํด๋๋ฅผ ๋จผ์ ์ค์ ํ์ธ์. ์ ๋ก๋๋ฅผ ์ ์ฅํ์ง ์์์ต๋๋ค.'; } elseif (trim((string)(isset($_POST['zip_keep_root']) ? $_POST['zip_keep_root'] : '')) === '') { $error = 'ZIP ์๋ณธ์ ์ฅ ๊ธฐ์คํด๋๋ฅผ ๋จผ์ ์ค์ ํ์ธ์. ์ ๋ก๋๋ฅผ ์ ์ฅํ์ง ์์์ต๋๋ค.'; } else { yyza_remember_flat_base_dir($flatBaseDir); yyza_remember_zip_keep_root($zipKeepRoot); $reuseSid = yyza_safe_session_id(isset($_POST['sid']) ? $_POST['sid'] : ''); if ($reuseSid !== '' && yyza_is_short_session_id($reuseSid)) $reuseSid = ''; list($newSid, $msg) = yyza_handle_upload($reuseSid, $keepOriginalZip, $zipKeepRoot); if ($newSid !== '') { $sid = $newSid; if (yyza_session_root_matched($sid)) { $flatBaseDir = '/'; } $notice = ($reuseSid !== '' && $reuseSid === $sid) ? $msg . ' ยท ๊ธฐ์กด ์ธ์ ์ ํฉ์ณ์ ํ์ํฉ๋๋ค.' : $msg; } else { $error = $msg; } } } elseif ($action === 'apply') { $sid = yyza_safe_session_id(isset($_POST['sid']) ? $_POST['sid'] : ''); $keepOriginalZip = yyza_keep_original_zip_from_request(); $zipKeepRoot = yyza_zip_keep_root_from_request(); $createDirs = isset($_POST['create_dirs']) && $_POST['create_dirs'] === '1'; $applyFiles = isset($_POST['apply_files']) && is_array($_POST['apply_files']) ? $_POST['apply_files'] : array(); $prebackupFiles = isset($_POST['prebackup_files']) && is_array($_POST['prebackup_files']) ? $_POST['prebackup_files'] : array(); $flatBaseDir = yyza_flat_base_dir_from_request(); yyza_remember_flat_base_dir($flatBaseDir); list($okApply, $applyResults) = yyza_apply_session($sid, $applyFiles, $createDirs, $prebackupFiles, $flatBaseDir); $notice = $okApply ? '์ ์ฉ ์ฒ๋ฆฌ ์๋ฃ ยท ํ๋ก์ ํธ ์ธ์ ์ ์ ์ง๋ฉ๋๋ค. ์ด๊ธฐํ ๊ท์น์ ์๋ณธzip ์ ์ง ์ฒดํฌ ์ํ๋ฅผ ๋ฐ๋ฆ ๋๋ค.' : '์ ์ฉ ์ฒ๋ฆฌ ์ค๋จ ยท ํ๋ก์ ํธ ์ธ์ ์ ์ ์ง๋ฉ๋๋ค.'; } elseif ($action === 'reset') { $sid = yyza_safe_session_id(isset($_POST['sid']) ? $_POST['sid'] : ''); $keepOriginalZip = yyza_keep_original_zip_from_request(); if ($sid !== '') { list($cleanOk, $cleanMsg) = yyza_cleanup_session($sid, $keepOriginalZip); if ($cleanOk) $notice = $cleanMsg; else $error = $cleanMsg; } $resetDone = true; } } $invalidSidCleared = false; if ($sid !== '' && !yyza_session_paths($sid)) { $sid = ''; $invalidSidCleared = true; if ($notice === '') $notice = '์ ์ฅ๋ ZIP ํ๋ก์ ํธ ์ธ์ ์ ์ฐพ์ง ๋ชปํด ์ด๊ธฐํํ์ต๋๋ค.'; } $rows = $sid !== '' ? yyza_scan_unzip($sid, $flatBaseDir) : array(); $rowGroups = yyza_group_rows_by_upload($rows); $hasDirMissing = false; $hasDirCreatable = false; $hasDirBlocked = false; foreach ($rows as $__yyza_row) { $__yyza_target = isset($__yyza_row['target']) ? $__yyza_row['target'] : ''; if ($__yyza_target === '') continue; list($__yyza_status) = yyza_status_for($__yyza_target); if ($__yyza_status === 'DIR_CREATABLE') { $hasDirMissing = true; $hasDirCreatable = true; } elseif ($__yyza_status === 'DIR_BLOCKED') { $hasDirMissing = true; $hasDirBlocked = true; } } unset($__yyza_row, $__yyza_target, $__yyza_status); $confFlatBaseDir = yyza_conf_flat_base_dir(); $flatPathConfFile = yyza_flat_conf_path(); $flatPathList = yyza_flat_path_list(); if ($flatBaseDir !== '' && !in_array($flatBaseDir, $flatPathList, true)) array_unshift($flatPathList, $flatBaseDir); $zipKeepPathConfFile = yyza_zip_keep_conf_path(); $zipKeepPathList = yyza_zip_keep_path_list(); if ($zipKeepRoot !== '' && !in_array($zipKeepRoot, $zipKeepPathList, true)) array_unshift($zipKeepPathList, $zipKeepRoot); $zipKeepFiles = yyza_list_zip_keep_files($zipKeepRoot); $theme = isset($_COOKIE['theme']) ? $_COOKIE['theme'] : 'dark'; if (!in_array($theme, array('dark', 'light', 'auto'), true)) $theme = 'dark'; if ($theme === 'auto') $theme = 'dark'; $faviEmoji = yyza_favi_emoji(); ?> <!doctype html> <html lang="ko" data-bs-theme="<?= yyza_h($theme) ?>"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Z<?= yyza_h($faviEmoji) ?><?= yyza_h($flatBaseDir) ?></title> <link rel="icon" href="<?= yyza_h(yyza_favi_href($faviEmoji)) ?>"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> <?php if (is_file(__DIR__ . '/yyy__theme_style.php')) include __DIR__ . '/yyy__theme_style.php'; ?> <style> body{padding:14px;background:var(--bg,var(--bs-body-bg));color:var(--text,var(--bs-body-color));} .yyza-wrap{max-width:1600px;margin:0 auto;} .yyza-card{border:1px solid var(--border,#334155);border-radius:10px;background:var(--panel,var(--bs-body-bg));padding:12px;margin-bottom:12px;} .yyza-drop{border:2px dashed var(--bs-secondary);border-radius:12px;padding:28px;text-align:center;background:rgba(127,127,127,.08);cursor:pointer;} .yyza-drop.yyza-drop-big{padding:200px 28px;} @media (max-width:768px){.yyza-drop.yyza-drop-big{padding:120px 16px;}} .yyza-drop.dragover{border-color:var(--bs-primary);background:rgba(13,110,253,.12);} .yyza-path{font-family:Consolas,monospace;font-size:12px;word-break:break-all;} .yyza-small{font-size:12px;opacity:.85;} .yyza-old{color:#ff8a8a;font-weight:700;} .yyza-new{color:#4ade80;font-weight:700;} .yyza-bak-none{color:#ffb4b4;font-weight:800;} .yyza-bak-exists{color:#93c5fd;font-weight:800;} .yyza-prebackup{border-top:1px dotted rgba(127,127,127,.25);margin-top:2px;padding-top:4px;} .yyza-filebox{display:flex;flex-direction:column;gap:5px;} .yyza-comparebox{display:flex;flex-direction:column;gap:4px;} .yyza-file-title{font-weight:800;} .yyza-file-title a{font-family:Consolas,monospace;word-break:break-all;} .yyza-backup-line{font-size:12px;opacity:.9;border-top:1px dashed rgba(127,127,127,.35);padding-top:4px;} .yyza-cmp-line{display:flex;flex-wrap:wrap;align-items:center;gap:6px;padding:3px 0;} .yyza-cmp-line + .yyza-cmp-line{border-top:1px dotted rgba(127,127,127,.25);} .yyza-open-link{font-weight:800;text-decoration:none;} .yyza-meta{font-family:Consolas,monospace;font-size:12px;opacity:.92;} .yyza-same{display:inline-block;padding:1px 7px;border-radius:999px;background:rgba(25,135,84,.18);color:#22c55e;font-weight:900;} .yyza-md5-match{display:inline-block;padding:1px 7px;border-radius:999px;background:rgba(25,135,84,.20);color:#198754;font-weight:900;} .yyza-md5-row{--bs-table-bg:rgba(25,135,84,.18)!important;} .yyza-actions{position:sticky;top:0;z-index:20;background:var(--bs-body-bg);border:1px solid var(--border,#334155);border-radius:10px;padding:8px;margin-bottom:10px;} .yyza-floating-stack{position:fixed;right:24px;bottom:64px;z-index:1080;display:flex;flex-direction:column;align-items:flex-end;gap:9px;} .yyza-floating-compare{min-width:118px;min-height:38px;border-radius:999px;font-weight:900;box-shadow:0 10px 28px rgba(0,0,0,.32);opacity:1!important;} .yyza-floating-compare:hover{transform:translateY(-1px);box-shadow:0 14px 34px rgba(0,0,0,.42);} .yyza-floating-stack .yyza-floating-reset{margin:0;} .yyza-floating-stack .yyza-reset-big{min-width:124px;min-height:46px;border-radius:999px;font-size:17px;font-weight:900;box-shadow:0 12px 34px rgba(0,0,0,.35);padding:9px 20px;} .yyza-floating-stack .yyza-reset-big:hover{transform:translateY(-1px);box-shadow:0 16px 40px rgba(0,0,0,.45);} @media (max-width:768px){.yyza-floating-stack{right:14px;bottom:58px}.yyza-floating-stack .yyza-reset-big{min-width:110px;min-height:42px;font-size:15px;padding:8px 16px;}} .table td,.table th{vertical-align:middle;font-size:13px;} .table-danger{--bs-table-bg:rgba(220,53,69,.10);} .table-success{--bs-table-bg:rgba(25,135,84,.18);} .table-warning{--bs-table-bg:rgba(255,193,7,.18);} .table-primary{--bs-table-bg:rgba(13,110,253,.18);} .table-secondary{--bs-table-bg:rgba(108,117,125,.18);} .yyza-dir-blocked{--bs-table-bg:rgba(220,53,69,.34)!important;} body,.btn,button,label,.badge,.form-control,.table,th,td{user-select:text!important;-webkit-user-select:text!important;} .yyza-status-badge{cursor:help;} .yyza-conf-check{white-space:nowrap;} .yyza-path-select{max-width:520px;} .yyza-path-edit{font-family:Consolas,monospace;font-size:12px;} .yyza-zip-card{border-width:2px;} .yyza-zip-head{border-bottom:1px dashed rgba(127,127,127,.35);padding-bottom:8px;} .yyza-load-panel{border:1px solid rgba(127,127,127,.28);border-radius:10px;padding:11px 12px;background:rgba(127,127,127,.06);margin:12px 0 18px;} .yyza-load-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center;width:100%;} .yyza-load-meter{width:100%;min-width:0;} .yyza-load-head{display:flex;justify-content:space-between;gap:8px;font-size:12px;line-height:1.35;white-space:nowrap;} .yyza-load-head b{font-weight:900;} .yyza-load-progress{height:10px;background:rgba(127,127,127,.25);} .yyza-file-size-bar{width:100%;max-width:360px;margin-top:7px;} .yyza-file-size-bar .yyza-load-head{font-size:11px;} .yyza-file-size-bar .yyza-load-progress{height:6px;} .yyza-load-note{font-size:12px;opacity:.82;} <?php foreach ($CMD_EXT_STYLES as $ext => $sty): $darkColor = isset($sty['dark_color']) ? $sty['dark_color'] : (isset($sty['color']) ? $sty['color'] : '#9aa0a6'); $darkHover = isset($sty['dark_hover']) ? $sty['dark_hover'] : (isset($sty['hover']) ? $sty['hover'] : $darkColor); $lightColor = isset($sty['light_color']) ? $sty['light_color'] : $darkColor; $lightHover = isset($sty['light_hover']) ? $sty['light_hover'] : $darkHover; $icon = isset($sty['icon']) ? $sty['icon'] : '๐'; ?> .sty-<?= yyza_h($ext) ?>{color:<?= yyza_h($darkColor) ?>!important;text-decoration:none;} .sty-<?= yyza_h($ext) ?>:hover{color:<?= yyza_h($darkHover) ?>!important;text-decoration:underline;} html[data-bs-theme="light"] .sty-<?= yyza_h($ext) ?>{color:<?= yyza_h($lightColor) ?>!important;} html[data-bs-theme="light"] .sty-<?= yyza_h($ext) ?>:hover{color:<?= yyza_h($lightHover) ?>!important;} .sty-<?= yyza_h($ext) ?>::before{content:"<?= yyza_h($icon) ?> ";display:inline-block;width:20px;text-align:center;} <?php endforeach; ?> </style> </head> <body> <div class="yyza-wrap" > <div class="d-flex flex-wrap align-items-center gap-2 mb-2" yy="yyy-used-files-exclude:start" > <a href="yyy_cmd.php" class="btn btn-sm btn-outline-secondary btn-theme-text">๐ปCMD</a> <a href="yyy_view.php?view=/var/www/html/y/yyy_zip_apply.php" class="btn btn-sm btn-outline-secondary btn-theme-text">๐์์ </a> <div onclick="compareFiles()" class="btn btn-sm btn-outline-secondary btn-theme-text" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-title="2๊ฐ ์ฒดํฌ ๋น๊ต">โ๋น๊ต</div> <label class="btn btn-sm btn-outline-secondary btn-theme-text"><input type="checkbox" id="chk-all"> ์ ์ฒด</label> <div id="themeToggle" class="btn btn-sm btn-outline-secondary btn-theme-text" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-title="๋คํฌ๋ชจ๋"></div> <span class="badge bg-info">ZIP ์ ์ฉ</span> <span class="badge bg-secondary">๋ณด๊ด: <?= yyza_h(yyza_zip_root()) ?></span> <span class="badge bg-secondary">๋ฐฑ์ : <?= yyza_h(yyza_backup_root()) ?></span> </div yy="yyy-used-files-exclude:end" > <?php if ($notice !== ''): ?><div class="alert alert-success py-2"><?= yyza_h($notice) ?></div><?php endif; ?> <?php if ($error !== ''): ?><div class="alert alert-danger py-2"><?= yyza_h($error) ?></div><?php endif; ?> <?php if (yyza_debug_enabled()): ?> <div class="alert alert-info py-2 yyza-small"> <b>debug</b> ยท host=<?= yyza_h(yyza_host_key()) ?> ยท path_conf=<?= yyza_h($flatPathConfFile) ?> ยท define YYY_ZIP_APPLY_FLAT_BASE_DIR=<?= yyza_h($confFlatBaseDir !== '' ? $confFlatBaseDir : '์ ์์์') ?> ยท flat_base_dir=<?= yyza_h($flatBaseDir) ?> ยท script_dir=<?= yyza_h(yyza_script_dir()) ?> ยท path_count=<?= count($flatPathList) ?> </div> <?php endif; ?> <div class="yyza-card"> <form id="uploadForm" method="post" enctype="multipart/form-data"> <input type="hidden" name="action" value="upload"> <input type="hidden" name="sid" id="uploadSid" value="<?= yyza_h($sid) ?>"> <input type="hidden" name="keep_original_zip" id="uploadKeepOriginalZip" value="<?= $keepOriginalZip ? '1' : '0' ?>"> <input id="zipInput" type="file" name="zip_file[]" accept=".zip,application/zip" class="d-none" multiple> <div id="dropZone" class="yyza-drop<?= $sid === '' ? ' yyza-drop-big' : '' ?>"> <div style="font-size:28px"><?=yyza_favi_emoji()?> </div> <div class="fw-bold">ZIP ํ์ผ์ ์ฌ๊ธฐ์ ๋์ด๋ค ๋๊ฑฐ๋ ํด๋ฆญํ์ธ์.</div> <div class="yyza-small">์ ๋ก๋ ์ฆ์ <?= yyza_h(yyza_zip_root()) ?>/์ธ์ /unzip ์๋์ ์์ถ์ ํ๊ณ ๋ชฉ๋ก๋ง ํ์ํฉ๋๋ค. ๋ฐ๋ก ์ ์ฉํ์ง ์์ต๋๋ค.</div> </div> <div class="mt-2 d-flex flex-wrap align-items-center gap-2"> <label class="fw-bold">ZIP ํ์ผ ์ ์ฉ ๊ธฐ์ค ํด๋</label> <select class="form-select form-select-sm yyza-path-select yyza-path" id="flatBaseSelect" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="๋๋ฉ์ธ๋ณ ๊ฒฝ๋ก ๋ชฉ๋ก: <?= yyza_h($flatPathConfFile) ?>"> <?php foreach ($flatPathList as $p): ?> <option value="<?= yyza_h($p) ?>" <?= $p === $flatBaseDir ? 'selected' : '' ?>><?= yyza_h($p) ?></option> <?php endforeach; ?> </select> <input type="text" class="form-control form-control-sm yyza-path" name="flat_base_dir" id="flatBaseDir" value="<?= yyza_h($flatBaseDir) ?>" style="max-width:680px" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="์ ๊ฒฝ๋ก๋ฅผ ์ง์ ์ ๋ ฅํ๋ฉด ์ ๋ก๋/์ ์ฉ ์ <?= yyza_h($flatPathConfFile) ?> ๋งจ ์์ ์๋ ์ ์ฅ๋ฉ๋๋ค."> <button type="button" class="btn btn-sm btn-outline-secondary btn-theme-text" data-bs-toggle="collapse" data-bs-target="#flatPathEditor">๊ฒฝ๋ก๋ชฉ๋ก ํธ์ง</button> <span class="yyza-small" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="์ค์ ํ์ผ: <?= yyza_h($flatPathConfFile) ?> / define ๊ธฐ๋ณธ๊ฐ: <?= yyza_h($confFlatBaseDir !== '' ? $confFlatBaseDir : '์ ์์์') ?> / ํ์ฌ ์ ์ฉ๊ฐ: <?= yyza_h($flatBaseDir) ?>">ZIP ๋ด๋ถ ๊ฒฝ๋ก๊ฐ ์๋ฒ ์ค์ ๊ฒฝ๋ก์ ๋ง์ผ๋ฉด <b>/</b> ๊ธฐ์ค, ๋ง์ง ์์ผ๋ฉด ์ ํํ ๊ธฐ์ค ํด๋ ์๋๋ก ์ ์ฉํฉ๋๋ค.</span> </div> <div class="mt-2 d-flex flex-wrap align-items-center gap-2"> <label class="btn btn-sm btn-outline-info btn-theme-text" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="์ฒดํฌON: ์๋ณธ ZIP์ ZIP ์๋ณธ์ ์ฅ ๊ธฐ์คํด๋/๋ฒ์ ์ ์ธํ์ผ๋ช /uploads์ ์๋ณธ ํ์ผ๋ช ๊ทธ๋๋ก ์ ์ฅํ๊ณ , ๊ฐ์ ์ด๋ฆ์ด๋ฉด ๋ฎ์ด์๋๋ค. ์ฒดํฌOFF: ์ด๊ธฐํ ์ ์์ ํด๋๋ฅผ ์์ ์ญ์ ํฉ๋๋ค."><input type="checkbox" id="keepOriginalZip" <?= $keepOriginalZip ? 'checked' : '' ?>> ์๋ณธzip ์ ์ง</label> <label class="fw-bold">ZIP ์๋ณธ์ ์ฅ ๊ธฐ์คํด๋</label> <select class="form-select form-select-sm yyza-path-select yyza-path" id="zipKeepRootSelect" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="๋๋ฉ์ธ๋ณ ์๋ณธ ZIP ๋ณด๊ด ๊ฒฝ๋ก ๋ชฉ๋ก: <?= yyza_h($zipKeepPathConfFile) ?>"> <?php foreach ($zipKeepPathList as $p): ?> <option value="<?= yyza_h($p) ?>" <?= $p === $zipKeepRoot ? 'selected' : '' ?>><?= yyza_h($p) ?></option> <?php endforeach; ?> </select> <input type="text" class="form-control form-control-sm yyza-path" name="zip_keep_root" id="zipKeepRoot" value="<?= yyza_h($zipKeepRoot) ?>" style="max-width:680px" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="์๋ณธ ZIP ๋ณด๊ด ๊ธฐ์ค ํด๋์ ๋๋ค. ๋ฒ์ ์ ์ธ ZIP ๊ธฐ์ค ํด๋๊ฐ ์ด ์๋์ ์์ฑ๋ฉ๋๋ค."> <button type="button" class="btn btn-sm btn-outline-secondary btn-theme-text" data-bs-toggle="collapse" data-bs-target="#zipKeepPathEditor">์๋ณธ๊ฒฝ๋ก๋ชฉ๋ก ํธ์ง</button> <span class="yyza-small">์: abc123_v201.zip โ ๊ธฐ์คํด๋/abc123/uploads/abc123_v201.zip ์ ์๋ณธ ZIP ์ ์ฅ(๋์ผ ํ์ผ๋ช ๋ฎ์ด์ฐ๊ธฐ), ๊ธฐ์คํด๋/abc123/unzip ์ ์์ถํด์ .</span> </div> </form> <div id="flatPathEditor" class="collapse mt-2"> <form method="post" id="flatPathForm" class="d-flex flex-column gap-2"> <input type="hidden" name="action" value="path_save"> <input type="hidden" name="flat_base_dir" id="flatPathBaseDir" value="<?= yyza_h($flatBaseDir) ?>"> <div class="yyza-small">๋๋ฉ์ธ๋ณ ์ค์ ํ์ผ: <span class="yyza-path"><?= yyza_h($flatPathConfFile) ?></span> ยท 1์ค์ 1๊ฐ ํด๋ ๊ฒฝ๋ก</div> <textarea name="paths_text" id="flatPathText" class="form-control yyza-path-edit" rows="5" style="min-height:1.4em; resize:vertical;"><?= yyza_h(implode(" ", $flatPathList)) ?></textarea> <div><button type="submit" class="btn btn-sm btn-primary btn-theme-text">๊ฒฝ๋ก๋ชฉ๋ก ์ ์ฅ</button></div> </form> </div> <div id="zipKeepPathEditor" class="collapse mt-2"> <form method="post" id="zipKeepPathForm" class="d-flex flex-column gap-2"> <input type="hidden" name="action" value="keep_path_save"> <input type="hidden" name="zip_keep_root" id="zipKeepPathBaseDir" value="<?= yyza_h($zipKeepRoot) ?>"> <div class="yyza-small">์๋ณธ ZIP ๋ณด๊ด ์ค์ ํ์ผ: <span class="yyza-path"><?= yyza_h($zipKeepPathConfFile) ?></span> ยท 1์ค์ 1๊ฐ ํด๋ ๊ฒฝ๋ก</div> <textarea name="zip_keep_paths_text" id="zipKeepPathText" class="form-control yyza-path-edit" rows="5" style="min-height:1.4em; resize:vertical;"><?= yyza_h(implode(" ", $zipKeepPathList)) ?></textarea> <div><button type="submit" class="btn btn-sm btn-primary btn-theme-text">์๋ณธ๊ฒฝ๋ก๋ชฉ๋ก ์ ์ฅ</button></div> </form> </div> <div class="yyza-card mt-3" style="border-style:dashed;"> <div class="d-flex flex-wrap align-items-center gap-2 mb-2"> <span class="badge bg-info">์๋ณธ ZIP ํ์ผ ๋ชฉ๋ก</span> <span class="yyza-small yyza-path">๊ธฐ์ค: <?= yyza_h($zipKeepRoot) ?></span> <span class="badge bg-secondary"><?= count($zipKeepFiles) ?>๊ฐ</span> <label class="btn btn-sm btn-outline-secondary btn-theme-text mb-0"><input type="checkbox" id="zipKeepFileAll"> ์ ์ฒด</label> <button type="button" class="btn btn-sm btn-outline-warning btn-theme-text" id="zipKeepSelectedDownload" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="์ฒดํฌํ ์๋ณธ ZIP๋ค์ ๋ค์ ํ๋์ ZIP์ผ๋ก ์์ถ ๋ค์ด๋ก๋">๐ฆ ์ ํ์์ถ๋ค์ด๋ก๋</button> <span class="yyza-small">๊ฐ๋ณ ZIP์ ๊ฐ ํ์ ๋ค์ด๋ก๋ ๋ฒํผ์ผ๋ก ๋ฐ์ ์ ์์ต๋๋ค.</span> </div> <?php if (!$zipKeepFiles): ?> <div class="text-muted yyza-small">์ ํํ ZIP ์๋ณธ์ ์ฅ ๊ธฐ์คํด๋ ์๋์ ๋ณด๊ด๋ .zip ํ์ผ์ด ์์ต๋๋ค.</div> <?php else: ?> <form id="zipKeepDownloadForm" method="post" class="m-0"> <input type="hidden" name="action" value="zip_keep_zip_download"> <input type="hidden" name="zip_keep_root" value="<?= yyza_h($zipKeepRoot) ?>"> <input type="hidden" name="files_json" id="zipKeepFilesJson" value="[]"> <div class="table-responsive"> <table class="table table-sm table-bordered align-middle mb-0"> <thead> <tr> <th style="width:58px">์ ํ</th> <th style="width:100px">๋ค์ด๋ก๋</th> <th>ZIP ํ์ผ</th> <th style="width:110px">ํฌ๊ธฐ</th> <th style="width:160px">์์ ์ผ</th> </tr> </thead> <tbody> <?php foreach ($zipKeepFiles as $kf): ?> <tr> <td class="text-center"><input type="checkbox" class="zip-keep-file" value="<?= yyza_h($kf['path']) ?>"></td> <td><a class="btn btn-sm btn-outline-primary btn-theme-text" href="<?= yyza_h(yyza_zip_keep_download_url($kf['path'], $zipKeepRoot)) ?>" target="_blank">๋ค์ด๋ก๋</a></td> <td> <div class="yyza-file-title">๐ฆ <?= yyza_h($kf['name']) ?></div> <div class="yyza-small yyza-path"><?= yyza_h($kf['rel']) ?></div> </td> <td><?= yyza_h(yyza_format_bytes(isset($kf['size']) ? $kf['size'] : 0)) ?></td> <td><?= yyza_h(!empty($kf['mtime']) ? date('Y-m-d H:i:s', (int)$kf['mtime']) : '-') ?></td> </tr> <?php endforeach; ?> </tbody> </table> </div> </form> <?php endif; ?> </div> </div> <?php if ($sid !== ''): ?> <form method="post" id="applyForm"> <input type="hidden" name="action" value="apply"> <input type="hidden" name="sid" value="<?= yyza_h($sid) ?>"> <input type="hidden" name="flat_base_dir" id="applyFlatBaseDir" value="<?= yyza_h($flatBaseDir) ?>"> <input type="hidden" name="keep_original_zip" id="applyKeepOriginalZip" value="<?= $keepOriginalZip ? '1' : '0' ?>"> <input type="hidden" name="zip_keep_root" id="applyZipKeepRoot" value="<?= yyza_h($zipKeepRoot) ?>"> <div class="yyza-actions d-flex flex-wrap align-items-center gap-2"> <span class="badge bg-primary">์ธ์ <?= yyza_h($sid) ?></span> <span class="badge bg-secondary">ZIP ๊ธฐ์ค: <?= yyza_h($flatBaseDir) ?></span> <span class="badge bg-secondary">์๋ณธZIP ๊ธฐ์ค: <?= yyza_h($zipKeepRoot) ?></span> <label class="btn btn-sm btn-outline-info btn-theme-text" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="์ด๊ธฐํ ๊ธฐ์ค: ์ฒดํฌON์ ์๋ณธ ZIP ์ ์ง, ์ฒดํฌOFF๋ ์ธ์ /๋ณด๊ด ํด๋ ์์ ์ญ์ "><input type="checkbox" id="keepOriginalZipApply" <?= $keepOriginalZip ? 'checked' : '' ?>> ์๋ณธzip ์ ์ง</label> <label class="btn btn-sm btn-outline-success btn-theme-text"><input type="checkbox" id="apply-all" checked> ์ ์ฉ ์ ์ฒด</label> <label class="btn btn-sm btn-outline-warning btn-theme-text"><input type="checkbox" name="create_dirs" value="1" <?= $hasDirCreatable ? 'checked' : '' ?>> ์๋ ํด๋ ์์ฑ ํ์ฉ</label> <span class="badge bg-warning text-dark">์ฒดํฌON=๊ธฐ์กด ์ด์๋ณธ ์ ๋ฐฑ์ ยท ์ฒดํฌOFF=์ ๊ท ์ ์ฉ๋ณธ ๋ฐฑ์ </span> <button type="submit" class="btn btn-sm btn-success" onclick="return confirm('์ ํ ํ์ผ์ ์ค์ ์๋ฒ ๊ฒฝ๋ก์ ์ ์ฉํ ๊น์?');">โ ์ ์ฉํ๊ธฐ</button> <button type="submit" class="btn btn-sm btn-outline-secondary btn-theme-text" name="action" value="reset" formnovalidate onclick="return confirm('ํ์ฌ ์ธ์ ์ค์ ์ ๋ฐ๋ผ ์ด๊ธฐํํ ๊น์? ์๋ณธzip ์ ์ง OFF๋ฉด ์ธ์ /๋ณด๊ด ํด๋๋ฅผ ์์ ์ญ์ ํฉ๋๋ค.');">์ด๊ธฐํ</button> </div> <?php $yyza_manifest_for_list = yyza_read_session_manifest($sid); if (!empty($yyza_manifest_for_list['uploads'])): $yyza_paths_for_list = yyza_session_paths($sid); ?> <div class="yyza-card"> <div class="d-flex flex-wrap align-items-center gap-2 mb-2"> <span class="badge bg-info">์๋ณธ ZIP ๋ณด๊ด ๋ชฉ๋ก</span> <span class="yyza-small yyza-path">base=<?= yyza_h($yyza_paths_for_list ? $yyza_paths_for_list['base'] : '') ?></span> </div> <div class="table-responsive"> <table class="table table-sm table-bordered align-middle"> <thead><tr><th style="width:80px">์๋ฒ</th><th>์๋ณธ ZIP</th><th>์ ์ฅ ZIP</th><th style="width:160px">์ ๋ก๋</th><th style="width:90px">ํ์ผ์</th></tr></thead> <tbody> <?php foreach ($yyza_manifest_for_list['uploads'] as $__u): ?> <tr> <td><?= yyza_h(isset($__u['seq']) ? $__u['seq'] : '') ?></td> <td class="yyza-path"><?= yyza_h(isset($__u['orig']) ? $__u['orig'] : '') ?></td> <td class="yyza-path"><?= yyza_h(isset($__u['zip']) ? $__u['zip'] : '') ?></td> <td><?= yyza_h(isset($__u['uploaded_at']) ? $__u['uploaded_at'] : '') ?></td> <td><?= yyza_h(isset($__u['file_count']) ? $__u['file_count'] : '') ?></td> </tr> <?php endforeach; unset($__u); ?> </tbody> </table> </div> </div> <?php endif; ?> <?php if (!$rows): ?> <div class="table-responsive yyza-card"> <table class="table table-sm table-bordered align-middle"> <tbody><tr><td class="text-center text-muted">์์ถ ํด์ ํ์ผ์ด ์์ต๋๋ค.</td></tr></tbody> </table> </div> <?php endif; ?> <?php $yyzaStatusCounts = array(); $yyzaLoadStats = array('file_count' => 0, 'total_bytes' => 0, 'max_file_bytes' => 0, 'max_file_name' => ''); $yyzaGroupStats = array(); foreach ($rowGroups as $__g) { $__gkey = isset($__g['key']) ? (string)$__g['key'] : ''; if ($__gkey === '') $__gkey = '_legacy'; if (!isset($yyzaGroupStats[$__gkey])) { $yyzaGroupStats[$__gkey] = array('file_count' => 0, 'total_bytes' => 0, 'max_file_bytes' => 0, 'max_file_name' => ''); } foreach ((array)(isset($__g['rows']) ? $__g['rows'] : array()) as $__r) { $__local = isset($__r['local']) ? (string)$__r['local'] : ''; $__bytes = is_file($__local) ? (int)@filesize($__local) : 0; $yyzaLoadStats['file_count']++; $yyzaLoadStats['total_bytes'] += $__bytes; if ($__bytes > $yyzaLoadStats['max_file_bytes']) { $yyzaLoadStats['max_file_bytes'] = $__bytes; $yyzaLoadStats['max_file_name'] = isset($__r['zip_rel']) ? (string)$__r['zip_rel'] : (isset($__r['rel']) ? (string)$__r['rel'] : ''); } $yyzaGroupStats[$__gkey]['file_count']++; $yyzaGroupStats[$__gkey]['total_bytes'] += $__bytes; if ($__bytes > $yyzaGroupStats[$__gkey]['max_file_bytes']) { $yyzaGroupStats[$__gkey]['max_file_bytes'] = $__bytes; $yyzaGroupStats[$__gkey]['max_file_name'] = isset($__r['zip_rel']) ? (string)$__r['zip_rel'] : (isset($__r['rel']) ? (string)$__r['rel'] : ''); } } } unset($__g, $__gkey, $__r, $__local, $__bytes); ?> <?php foreach ($rowGroups as $zipGroup): ?> <div class="yyza-card yyza-zip-card"> <div class="d-flex flex-wrap align-items-center gap-2 mb-2 yyza-zip-head"> <span class="badge bg-info">ZIP #<?= yyza_h(isset($zipGroup['seq']) ? $zipGroup['seq'] : '') ?></span> <span class="fw-bold">๐ฆ <?= yyza_h(isset($zipGroup['name']) ? $zipGroup['name'] : 'upload.zip') ?></span> <span class="badge bg-secondary"><?= count($zipGroup['rows']) ?>๊ฐ ํ์ผ</span> <?php if (!empty($zipGroup['uploaded_at'])): ?><span class="badge bg-secondary"><?= yyza_h($zipGroup['uploaded_at']) ?></span><?php endif; ?> <span class="yyza-small yyza-path">group=<?= yyza_h(isset($zipGroup['key']) ? $zipGroup['key'] : '') ?></span> <?php $__zipGroupKey = isset($zipGroup['key']) ? (string)$zipGroup['key'] : ''; if ($__zipGroupKey === '') $__zipGroupKey = '_legacy'; $__zipStat = isset($yyzaGroupStats[$__zipGroupKey]) ? $yyzaGroupStats[$__zipGroupKey] : array('file_count'=>0,'total_bytes'=>0,'max_file_bytes'=>0,'max_file_name'=>''); ?> <span class="badge bg-secondary">์ด <?= yyza_h(yyza_format_bytes($__zipStat['total_bytes'])) ?></span> <span class="badge bg-secondary">์ต๋ํ์ผ <?= yyza_h(yyza_format_bytes($__zipStat['max_file_bytes'])) ?></span> </div> <div class="yyza-load-panel mb-2"> <div class="yyza-load-grid"> <?= yyza_load_meter('ํ์ผ์', (int)$__zipStat['file_count'] . '๊ฐ', '200๊ฐ', $__zipStat['file_count'], 80, 200, 500, 'ChatGPT ์ฝ๋๊ฒํ ๊ธฐ์ค: ์ถ์ฒโค80๊ฐ / ์ฃผ์โค200๊ฐ / ๊ฒฝ๊ณ >200๊ฐ') ?> <?= yyza_load_meter('๊ฐ๋ณ์ต๋', yyza_format_bytes($__zipStat['max_file_bytes']), '1 MB', $__zipStat['max_file_bytes'], 262144, 1048576, 5242880, 'ChatGPT ์ฝ๋๊ฒํ ๊ธฐ์ค: ์ถ์ฒโค256KB / ์ฃผ์โค1MB / ๊ฒฝ๊ณ >1MB') ?> <?= yyza_load_meter('ZIPํด์ ํฉ๊ณ', yyza_format_bytes($__zipStat['total_bytes']), '15 MB', $__zipStat['total_bytes'], 5242880, 15728640, 31457280, 'ChatGPT ์ฝ๋๊ฒํ ๊ธฐ์ค: ์ถ์ฒโค5MB / ์ฃผ์โค15MB / ๊ฒฝ๊ณ >15MB') ?> </div> <?php if (!empty($__zipStat['max_file_name'])): ?><div class="yyza-load-note yyza-path">์ต๋ ํ์ผ: <?= yyza_h($__zipStat['max_file_name']) ?></div><?php endif; ?> </div> <div class="table-responsive"> <table class="table table-sm table-bordered align-middle"> <thead> <tr> <th style="width:70px">์ ์ฉ</th> <th style="width:90px">์ํ</th> <th style="width:46%">ํ์ผ</th> <th style="width:44%">๊ธฐ์กด / ์์ ๋น๊ต</th> </tr> </thead> <tbody> <?php foreach ($zipGroup['rows'] as $row): $target = $row['target']; $local = $row['local']; $rel = $row['rel']; $zipRel = isset($row['zip_rel']) ? $row['zip_rel'] : $rel; list($status, $label, $trClass) = yyza_status_for($target); $canApply = ($status !== 'BLOCKED' && $status !== 'DIR_BLOCKED'); $backupPreview = file_exists($target) ? yyza_backup_path($target, date('Ymd__H_i_s')) : '-'; $latestBackup = file_exists($target) ? yyza_latest_backup_info($target) : array('exists'=>false,'path'=>'','time'=>0,'age'=>'-'); $prebackupDefault = (file_exists($target) && empty($latestBackup['exists'])); $oldSizeText = file_exists($target) ? yyza_file_size_text($target) : '-'; $oldTimeText = file_exists($target) ? yyza_file_time($target) : '-'; $newSizeText = yyza_file_size_text($local); $newTimeText = yyza_file_time($local); $oldSizeRaw = file_exists($target) ? yyza_file_size_raw($target) : false; $newSizeRaw = yyza_file_size_raw($local); $sameSize = ($oldSizeRaw !== false && $newSizeRaw !== false && $oldSizeRaw === $newSizeRaw); $md5Match = $sameSize ? yyza_md5_match_if_same_size($target, $local) : false; if ($md5Match) { $canApply = false; $trClass = 'table-success yyza-md5-row'; } $newSizeShow = $sameSize ? '<span class="yyza-same">์๋</span>' : yyza_h($newSizeText); $newTimeShow = (file_exists($target) && $oldTimeText === $newTimeText) ? '<span class="yyza-same">์๋</span>' : yyza_h($newTimeText); $md5Show = $md5Match ? ' <span class="yyza-md5-match">md5์ผ์น</span>' : ''; $applyChecked = (!$md5Match && $canApply); ?> <tr class="<?= yyza_h($trClass) ?>"> <td class="text-center"><?= yyza_apply_checkbox($rel, $canApply, $applyChecked) ?></td> <td> <?php $statusTip = '๋ฐฑ์ ์์ : ' . $backupPreview; if ($status === 'DIR_CREATABLE' || $status === 'DIR_BLOCKED') { $statusTip .= "\n" . yyza_dir_create_tip($target); } if (file_exists($target)) { $statusTip .= "\n๊ธฐ์กด๋ฐฑ์ : " . (!empty($latestBackup['exists']) ? ($latestBackup['age'] . ' ๋ฐฑ์ ๋ณธ ์กด์ฌ ' . $latestBackup['path']) : '๋ฐฑ์ ๋ณธ ์์'); } ?> <?php if ($md5Match) { $statusBadgeClass = 'bg-success'; } elseif ($status === 'EXISTS') { $statusBadgeClass = 'bg-danger'; } elseif ($status === 'NEW') { $statusBadgeClass = 'bg-success'; } elseif ($status === 'DIR_CREATABLE') { $statusBadgeClass = 'bg-primary'; } elseif ($status === 'DIR_BLOCKED') { $statusBadgeClass = 'bg-danger'; } elseif ($status === 'DIR_MISSING') { $statusBadgeClass = 'bg-warning text-dark'; } else { $statusBadgeClass = 'bg-secondary'; } $statusLabel = $md5Match ? 'md5์ผ์น' : $label; if (!isset($yyzaStatusCounts[$statusLabel])) $yyzaStatusCounts[$statusLabel] = array('count' => 0, 'bytes' => 0); $yyzaStatusCounts[$statusLabel]['count']++; $yyzaStatusCounts[$statusLabel]['bytes'] += ($newSizeRaw !== false ? (int)$newSizeRaw : 0); ?> <span class="badge yyza-status-badge <?= yyza_h($statusBadgeClass) ?>" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-html="false" data-bs-title="<?= yyza_h($statusTip) ?>"><?= yyza_h($statusLabel) ?></span> </td> <td> <div class="yyza-filebox"> <div class="yyza-file-title"><?= yyza_target_link($target, $target) ?></div> <div class="yyza-small yyza-path">ZIP ๋ด๋ถ: <?= yyza_h($zipRel) ?></div> <?php if (file_exists($target)): ?> <?php if (!empty($latestBackup['exists'])): ?> <div class="yyza-backup-line"><span class="yyza-bak-exists"><?= yyza_h($latestBackup['age']) ?> ๋ฐฑ์ ๋ณธ ์กด์ฌ</span></div> <?php else: ?> <div class="yyza-backup-line"><span class="yyza-bak-none">๋ฐฑ์ ๋ณธ ์์</span></div> <?php endif; ?> <?php endif; ?> </div> </td> <td> <div class="yyza-comparebox"> <div class="yyza-cmp-line yyza-old"> <?php if (file_exists($target)): ?> <?= yyza_cmp_checkbox($target) ?> <?= yyza_target_link($target, "๊ธฐ์กด์ด๊ธฐ") ?> <span class="yyza-meta">๊ธฐ์กด ํฌ๊ธฐ: <?= yyza_h($oldSizeText) ?> ยท ํ์ผ์ผ์: <?= yyza_h($oldTimeText) ?></span> <?php else: ?> <span class="text-muted">๊ธฐ์กด: ์์</span> <?php endif; ?> </div> <div class="yyza-cmp-line yyza-new"> <?= yyza_cmp_checkbox($local) ?> <?= yyza_target_link($local, "์์์ด๊ธฐ") ?> <span class="yyza-meta">์ ๊ท ํฌ๊ธฐ: <?= $newSizeShow ?><?= $md5Show ?> ยท ํ์ผ์ผ์: <?= $newTimeShow ?></span> <?php if ($newSizeRaw !== false): ?><?= yyza_file_size_bar((int)$newSizeRaw) ?><?php endif; ?> </div> <?php if (file_exists($target)): ?> <div class="yyza-cmp-line yyza-prebackup"> <label class="btn btn-sm btn-outline-warning btn-theme-text mb-0"> <input type="checkbox" name="prebackup_files[]" value="<?= yyza_h($rel) ?>" <?= $prebackupDefault ? 'checked' : '' ?>> ๊ธฐ์กด ์ด์๋ณธ ์ ๋ฐฑ์ </label> <span class="yyza-small"><?= $prebackupDefault ? '๋ฐฑ์ ๋ณธ ์์ โ ์๋ ON' : '๋ฐฑ์ ๋ณธ ์์ โ ๊ธฐ๋ณธ OFF, ์ ๊ท ์ ์ฉ๋ณธ ๋ฐฑ์ ' ?></span> </div> <?php endif; ?> </div> </td> </tr> <?php endforeach; ?> </tbody> </table> </div> </div> <?php endforeach; ?> <?php if (!empty($yyzaStatusCounts)): ?> <div class="yyza-card"> <h6>ChatGPT ์์ ๋ถ๋ด / ์ํ๋ณ ์นด์ดํธ</h6> <div class="yyza-load-panel mb-2"> <div class="yyza-load-grid"> <?= yyza_load_meter('์ ์ฒด ํ์ผ์', (int)$yyzaLoadStats['file_count'] . '๊ฐ', '200๊ฐ', $yyzaLoadStats['file_count'], 80, 200, 500, 'ChatGPT ์ฝ๋๊ฒํ ๊ธฐ์ค: ์ถ์ฒโค80๊ฐ / ์ฃผ์โค200๊ฐ / ๊ฒฝ๊ณ >200๊ฐ') ?> <?= yyza_load_meter('๊ฐ๋ณ ์ต๋', yyza_format_bytes($yyzaLoadStats['max_file_bytes']), '1 MB', $yyzaLoadStats['max_file_bytes'], 262144, 1048576, 5242880, 'ChatGPT ์ฝ๋๊ฒํ ๊ธฐ์ค: ์ถ์ฒโค256KB / ์ฃผ์โค1MB / ๊ฒฝ๊ณ >1MB') ?> <?= yyza_load_meter('์ ์ฒด ์ฉ๋', yyza_format_bytes($yyzaLoadStats['total_bytes']), '15 MB', $yyzaLoadStats['total_bytes'], 5242880, 15728640, 31457280, 'ChatGPT ์ฝ๋๊ฒํ ๊ธฐ์ค: ์ถ์ฒโค5MB / ์ฃผ์โค15MB / ๊ฒฝ๊ณ >15MB') ?> </div> <div class="yyza-load-note">๊ธฐ์ค์ ChatGPT ์ฝ๋ ์์ ยท๊ฒํ ํจ์จ ๊ธฐ์ค์ ๋๋ค. ์์คํ ์ ๋ก๋ ํ๊ณ๊ฐ ์๋๋ผ, ๋ต๋ณ ์ ํ๋์ ์ฒ๋ฆฌ ๋ถ๋ด์ ์ค์ด๊ธฐ ์ํ ์ค๋ฌด ๊ถ์ฅ๊ฐ์ ๋๋ค.</div> <?php if (!empty($yyzaLoadStats['max_file_name'])): ?><div class="yyza-load-note yyza-path">์ต๋ ํ์ผ: <?= yyza_h($yyzaLoadStats['max_file_name']) ?></div><?php endif; ?> </div> <div class="d-flex flex-wrap gap-2"> <?php foreach ($yyzaStatusCounts as $__st => $__info): ?> <span class="badge bg-secondary"><?= yyza_h($__st) ?>: <?= (int)$__info['count'] ?>๊ฐ, <?= yyza_h(yyza_format_bytes($__info['bytes'])) ?></span> <?php endforeach; unset($__st, $__info); ?> </div> </div> <?php endif; ?> </form> <?php endif; ?> <?php if ($applyResults): ?> <div class="yyza-card"> <h6>์ ์ฉ ๊ฒฐ๊ณผ</h6> <div class="table-responsive"> <table class="table table-sm table-bordered"> <thead><tr><th>์ํ</th><th>๋์</th><th>๋ฐฑ์ </th><th>๋ฐฑ์ ๋ฐฉ์</th><th>๋ฉ์์ง</th></tr></thead> <tbody> <?php foreach ($applyResults as $r): ?> <tr class="<?= (isset($r['status']) && $r['status'] === 'OK') ? 'table-success' : 'table-danger' ?>"> <td><?= yyza_h(isset($r['status']) ? $r['status'] : '') ?></td> <td class="yyza-path"><?= yyza_h(isset($r['target']) ? $r['target'] : '') ?></td> <td class="yyza-path"><?= yyza_h(isset($r['backup']) ? $r['backup'] : '') ?></td> <td><?= yyza_h(isset($r['backup_mode']) ? $r['backup_mode'] : '') ?></td> <td><?= yyza_h(isset($r['msg']) ? $r['msg'] : '') ?></td> </tr> <?php endforeach; ?> </tbody> </table> </div> </div> <?php endif; ?> </div> <?php if ($sid !== ''): ?> <div class="yyza-floating-stack"> <div id="yyzaFloatingCompare" onclick="compareFiles()" class="btn btn-sm btn-primary yyza-floating-compare btn-theme-text" data-bs-placement="left" data-bs-toggle="tooltip" data-bs-title="2๊ฐ ์ฒดํฌ ๋น๊ต ยท 2๊ฐ ์ด๊ณผ ์ ํ ์ ์ต๊ทผ 2๊ฐ๋ง ๋จ๊ธฐ๊ณ ์คํ">โ๋น๊ต <span id="yyzaCmpCount">0/2</span></div> <button type="submit" form="applyForm" class="btn btn-sm btn-success" onclick="return confirm('์ ํ ํ์ผ์ ์ค์ ์๋ฒ ๊ฒฝ๋ก์ ์ ์ฉํ ๊น์?');">โ ์ ์ฉํ๊ธฐ</button> <form method="post" class="yyza-floating-reset"> <input type="hidden" name="action" value="reset"> <input type="hidden" name="sid" value="<?= yyza_h($sid) ?>"> <input type="hidden" name="keep_original_zip" class="floatingKeepOriginalZip" value="<?= $keepOriginalZip ? '1' : '0' ?>"> <button type="submit" class="btn btn-danger yyza-reset-big btn-theme-text" formnovalidate onclick="return confirm('ํ์ฌ ์ธ์ ์ค์ ์ ๋ฐ๋ผ ์ด๊ธฐํํ ๊น์? ์๋ณธzip ์ ์ง OFF๋ฉด ์ธ์ /๋ณด๊ด ํด๋๋ฅผ ์์ ์ญ์ ํฉ๋๋ค.');" data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="์๋ณธzip ์ ์ง ON์ ์๋ณธ ZIP ์ ์ง, OFF๋ ์ธ์ /๋ณด๊ด ํด๋ ์์ ์ญ์ ">โบ ์ด๊ธฐํ</button> </form> </div> <?php endif; ?> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script> <?php if (is_file(__DIR__ . '/yyy_batch_files.js')): ?> <script> window.YY_BATCH_FILES_CFG = Object.assign({}, window.YY_BATCH_FILES_CFG || {}, { backupRoot: <?= json_encode(yyza_backup_root(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, diffUrl: 'yyy_diff.php', checkAllSelector: '#chk-all', fileCheckboxSelector: '.cmp-file' }); </script> <script src="yyy_batch_files.js?v=1.2"></script> <?php else: ?> <script> function showToast(msg){ alert(msg); } function compareFiles(){ const selected = Array.from(document.querySelectorAll('.cmp-file:checked')).map(cb => cb.value); if(selected.length !== 2){ showToast('๋น๊ตํ ํ์ผ 2๊ฐ๋ฅผ ์ ํํ์ธ์.'); return; } window.open('yyy_diff.php?left=' + selected[0] + '&right=' + selected[1] + '&fmt=html', '_blank'); } </script> <?php endif; ?> <script> // yyy ๊ณ์ด ๊ท์น: ํ์ผ ๊ฒฝ๋ก๋ URL์์ ์ธ์ฝ๋ฉํ์ง ์๊ณ ๊ทธ๋๋ก ๋ณด์ด๊ฒ ํ๋ค. (function(){ const order = []; function cmpChecked(){ return Array.from(document.querySelectorAll('.cmp-file:checked')); } function cmpCompactOrder(){ for (let i = order.length - 1; i >= 0; i--) { const cb = order[i]; if (!cb || !cb.isConnected || !cb.checked) order.splice(i, 1); } cmpChecked().forEach(function(cb){ if (order.indexOf(cb) < 0) order.push(cb); }); return order; } function cmpKeepRecentTwo(){ cmpCompactOrder(); const keep = order.slice(-2); document.querySelectorAll('.cmp-file:checked').forEach(function(cb){ if (keep.indexOf(cb) < 0) cb.checked = false; }); order.splice(0, order.length); keep.forEach(function(cb){ if (cb && cb.isConnected) order.push(cb); }); cmpUpdateCount(); return keep.filter(function(cb){ return cb && cb.checked; }); } function cmpUpdateCount(){ const checked = cmpChecked(); const cnt = document.getElementById('yyzaCmpCount'); const btn = document.getElementById('yyzaFloatingCompare'); if (cnt) cnt.textContent = checked.length + '/2'; if (btn) { btn.classList.toggle('btn-success', checked.length === 2); btn.classList.toggle('btn-warning', checked.length > 2); btn.classList.toggle('btn-primary', checked.length < 2); const title = checked.length > 2 ? '์ ํ ' + checked.length + '๊ฐ ยท ํด๋ฆญํ๋ฉด ์ต๊ทผ 2๊ฐ๋ง ๋จ๊ธฐ๊ณ ๋น๊ต' : (checked.length === 2 ? '์ ํ 2๊ฐ ๋น๊ต ์คํ' : '๋น๊ตํ ํ์ผ 2๊ฐ๋ฅผ ์ ํํ์ธ์. ํ์ฌ ' + checked.length + '๊ฐ'); btn.setAttribute('data-bs-title', title); btn.setAttribute('title', title); } } window.compareFiles = function(){ let selectedCbs = cmpChecked(); if (selectedCbs.length > 2) { selectedCbs = cmpKeepRecentTwo(); if (typeof showToast === 'function') showToast('๋น๊ต ์ ํ์ด 2๊ฐ๋ฅผ ์ด๊ณผํ์ฌ ์ต๊ทผ ์ ํ 2๊ฐ๋ง ๋จ๊ฒผ์ต๋๋ค.', 'info'); } const selected = selectedCbs.map(function(cb){ return cb.value; }); if(selected.length !== 2){ if(typeof showToast === 'function') showToast('๋น๊ตํ ํ์ผ 2๊ฐ๋ฅผ ์ ํํ์ธ์. ํ์ฌ ' + selected.length + '๊ฐ ์ ํ๋จ'); else alert('๋น๊ตํ ํ์ผ 2๊ฐ๋ฅผ ์ ํํ์ธ์. ํ์ฌ ' + selected.length + '๊ฐ ์ ํ๋จ'); cmpUpdateCount(); return; } cmpUpdateCount(); window.open('yyy_diff.php?left=' + selected[0] + '&right=' + selected[1] + '&fmt=html', '_blank'); }; document.addEventListener('change', function(e){ const cb = e.target; if (!cb || !cb.classList || !cb.classList.contains('cmp-file')) return; const idx = order.indexOf(cb); if (idx >= 0) order.splice(idx, 1); if (cb.checked) order.push(cb); cmpUpdateCount(); }, true); document.addEventListener('DOMContentLoaded', cmpUpdateCount); setTimeout(cmpUpdateCount, 250); })();</script> <script> window.YY_ZIP_DROP_TO_APPLY_CFG = Object.assign({}, window.YY_ZIP_DROP_TO_APPLY_CFG || {}, { sid: <?= json_encode($sid, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, storageKey: <?= json_encode('yy_zip_apply_sid_' . $clientTabKey, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, flatBaseDir: <?= json_encode($flatBaseDir, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, keepOriginalZip: <?= $keepOriginalZip ? 'true' : 'false' ?>, zipKeepRoot: <?= json_encode($zipKeepRoot, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, targetUrl: <?= json_encode('/y/yyy_zip_apply.php', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?> }); window.YY_ZIP_APPLY_RESET_DONE = <?= $resetDone ? 'true' : 'false' ?>; window.YY_ZIP_APPLY_INVALID_SID = <?= $invalidSidCleared ? 'true' : 'false' ?>; </script> <script src="yy_zip_drop_to_apply.js?v=1.10"></script> <script src="yy__theme.js?v=1.3"></script> <script> (function(){ const dz = document.getElementById('dropZone'); const input = document.getElementById('zipInput'); const form = document.getElementById('uploadForm'); const uploadSid = document.getElementById('uploadSid'); const sidStorageKey = <?= json_encode('yy_zip_apply_sid_' . $clientTabKey, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>; const pageFaviEmoji = <?= json_encode($faviEmoji, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>; const flatBaseStorageKey = 'yy_zip_apply_flat_base_dir_' + <?= json_encode(yyza_host_key(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>; const flatPathTextStorageKey = 'yy_zip_apply_paths_text_' + <?= json_encode(yyza_host_key(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>; const aiwkTabId = String(new URL(location.href).searchParams.get('aiwk_tab_id') || new URL(location.href).searchParams.get('s_tab') || 'global').replace(/^tab:/, ''); const keepStorageKey = 'yy_zip_apply_keep_original_zip_' + aiwkTabId; const zipKeepRootStorageKey = 'yy_zip_apply_zip_keep_root_' + aiwkTabId + '_' + <?= json_encode(yyza_host_key(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>; const zipKeepPathTextStorageKey = 'yy_zip_apply_zip_keep_paths_text_' + <?= json_encode(yyza_host_key(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>; const currentSid = <?= json_encode($sid, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>; const dropCfgStorageKey = 'yy_zip_drop_to_apply_cfg'; function yyzaReadDropCfg(){ try { const obj = JSON.parse(localStorage.getItem(dropCfgStorageKey) || '{}'); return obj && typeof obj === 'object' && !Array.isArray(obj) ? obj : {}; } catch(e) { return {}; } } function yyzaWriteDropCfg(extra){ try { const old = yyzaReadDropCfg(); const fbd = document.getElementById('flatBaseDir'); const zkr = document.getElementById('zipKeepRoot'); const keepCb = document.getElementById('keepOriginalZip') || document.getElementById('keepOriginalZipApply'); const merged = Object.assign({}, old, { sid: currentSid || old.sid || '', flatBaseDir: fbd ? yyzaNormClientPath(fbd.value) : (old.flatBaseDir || ''), zipKeepRoot: zkr ? yyzaNormClientPath(zkr.value) : (old.zipKeepRoot || ''), keepOriginalZip: keepCb ? !!keepCb.checked : !!old.keepOriginalZip, targetUrl: '/y/yyy_zip_apply.php' }, extra || {}); localStorage.setItem(dropCfgStorageKey, JSON.stringify(merged)); window.YY_ZIP_DROP_TO_APPLY_CFG = Object.assign({}, window.YY_ZIP_DROP_TO_APPLY_CFG || {}, merged); return merged; } catch(e) { return extra || {}; } } if (window.YY_ZIP_APPLY_RESET_DONE || window.YY_ZIP_APPLY_INVALID_SID) { try { localStorage.removeItem(sidStorageKey); } catch(e) {} yyzaWriteDropCfg({ sid: '' }); } function yyzaValidShortSid(v){ return /^[a-f0-9]{4}$/.test(String(v || '').trim().toLowerCase()); } function yyzaValidClientSid(v){ v = String(v || '').trim().toLowerCase(); return !/^[a-f0-9]{4}$/.test(v) && /^[a-z0-9][a-z0-9._-]{0,79}$/.test(v); } function yyzaGetSavedSid(){ try { const v = String(localStorage.getItem(sidStorageKey) || '').trim().toLowerCase(); if (v && !yyzaValidClientSid(v)) { localStorage.removeItem(sidStorageKey); return ''; } return v; } catch(e) { return ''; } } function yyzaNormClientPath(v){ const raw = String(v || '').trim(); const hadTailSlash = raw.length > 1 && /[\\\/]$/.test(raw); v = raw.replace(/\\/g, '/').replace(/\/+/g, '/'); if (v.length > 1) v = v.replace(/\/+$/g, ''); if (hadTailSlash) v += '/'; return v; } function yyzaGetTitleFlatBaseDir(){ const el = document.getElementById('flatBaseDir'); const saved = yyzaGetSavedFlatBaseDir(); const cur = el ? yyzaNormClientPath(el.value) : ''; return saved || cur || yyzaNormClientPath(<?= json_encode($flatBaseDir, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>); } function yyzaUpdateDocumentTitle(){ const baseDir = yyzaGetTitleFlatBaseDir(); document.title = baseDir ? ('Z'+pageFaviEmoji + '' + baseDir) : ('Z'+pageFaviEmoji + ''); } yyzaWriteDropCfg({}); function yyzaGetSavedFlatBaseDir(){ try { return yyzaNormClientPath(localStorage.getItem(flatBaseStorageKey) || ''); } catch(e) { return ''; } } function yyzaSetSavedFlatBaseDir(v){ v = yyzaNormClientPath(v); try { if (v) localStorage.setItem(flatBaseStorageKey, v); else localStorage.removeItem(flatBaseStorageKey); yyzaWriteDropCfg({ flatBaseDir: v }); yyzaUpdateDocumentTitle(); } catch(e) { yyzaUpdateDocumentTitle(); } return v; } yyzaUpdateDocumentTitle(); function yyzaSetSavedPathsText(v){ try { localStorage.setItem(flatPathTextStorageKey, String(v || '')); } catch(e) {} } function yyzaGetSavedPathsText(){ try { return String(localStorage.getItem(flatPathTextStorageKey) || ''); } catch(e) { return ''; } } function yyzaGetSavedZipKeepRoot(){ try { return yyzaNormClientPath(localStorage.getItem(zipKeepRootStorageKey) || ''); } catch(e) { return ''; } } function yyzaSetSavedZipKeepRoot(v){ v = yyzaNormClientPath(v); try { if (v) localStorage.setItem(zipKeepRootStorageKey, v); else localStorage.removeItem(zipKeepRootStorageKey); yyzaWriteDropCfg({ zipKeepRoot: v }); } catch(e) {} return v; } function yyzaSetSavedZipKeepPathsText(v){ try { localStorage.setItem(zipKeepPathTextStorageKey, String(v || '')); } catch(e) {} } function yyzaGetSavedZipKeepPathsText(){ try { return String(localStorage.getItem(zipKeepPathTextStorageKey) || ''); } catch(e) { return ''; } } function yyzaServerRememberZipKeepRoot(v){ v = yyzaNormClientPath(v); if (!v) return; try { const fd = new FormData(); fd.append('action', 'keep_path_select_save'); fd.append('zip_keep_root', v); fetch(location.pathname + location.search, { method: 'POST', body: fd, credentials: 'same-origin' }).catch(function(){}); } catch(e) {} } function yyzaServerRememberFlatBaseDir(v){ v = yyzaNormClientPath(v); if (!v) return; try { const fd = new FormData(); fd.append('action', 'path_select_save'); fd.append('flat_base_dir', v); fetch(location.pathname + location.search, { method: 'POST', body: fd, credentials: 'same-origin' }).catch(function(){}); } catch(e) {} } if (currentSid) { try { localStorage.setItem(sidStorageKey, currentSid); } catch(e) {} yyzaWriteDropCfg({ sid: currentSid }); } else { try { const savedSidForRestore = yyzaGetSavedSid(); const hasSidParam = new URL(location.href).searchParams.has('sid'); if (savedSidForRestore && !hasSidParam) { const u = new URL(location.href); u.searchParams.set('sid', savedSidForRestore); const fbd = document.getElementById('flatBaseDir'); if (fbd && fbd.value) u.searchParams.set('flat_base_dir', fbd.value); location.replace(u.toString()); return; } } catch(e) {} } try { const savedFlatBaseDir = yyzaGetSavedFlatBaseDir(); const hasFlatBaseParam = new URL(location.href).searchParams.has('flat_base_dir'); const currentFlatBaseDir = yyzaNormClientPath(<?= json_encode($flatBaseDir, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>); if (savedFlatBaseDir && !hasFlatBaseParam && savedFlatBaseDir !== currentFlatBaseDir) { const u = new URL(location.href); u.searchParams.set('flat_base_dir', savedFlatBaseDir); if (currentSid) u.searchParams.set('sid', currentSid); location.replace(u.toString()); return; } } catch(e) {} try { const savedZipKeepRoot = yyzaGetSavedZipKeepRoot(); const hasZipKeepParam = new URL(location.href).searchParams.has('zip_keep_root'); const currentZipKeepRoot = yyzaNormClientPath(<?= json_encode($zipKeepRoot, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>); if (savedZipKeepRoot && !hasZipKeepParam && savedZipKeepRoot !== currentZipKeepRoot) { const u = new URL(location.href); u.searchParams.set('zip_keep_root', savedZipKeepRoot); if (currentSid) u.searchParams.set('sid', currentSid); const fbd = document.getElementById('flatBaseDir'); if (fbd && fbd.value) u.searchParams.set('flat_base_dir', fbd.value); location.replace(u.toString()); return; } } catch(e) {} function syncUploadSid(){ if (!uploadSid) return; if (currentSid) { uploadSid.value = currentSid; return; } try { const savedSid = yyzaGetSavedSid(); if (savedSid) uploadSid.value = savedSid; } catch(e) {} } function yyzaShowClientError(msg){ if (typeof window.showToast === 'function') window.showToast(msg, 'error'); else if (typeof showToast === 'function') showToast(msg, 'error'); else alert(msg); } function yyzaBeforeUploadSubmit(){ const fbd = document.getElementById('flatBaseDir'); const keepCb = document.getElementById('keepOriginalZip'); const zkr = document.getElementById('zipKeepRoot'); if (!fbd || !yyzaNormClientPath(fbd.value)) { yyzaShowClientError('ZIP ํ์ผ ์ ์ฉ ๊ธฐ์ค ํด๋๋ฅผ ๋จผ์ ์ค์ ํ์ธ์. ์ ๋ก๋๋ฅผ ์ ์ฅํ์ง ์์ต๋๋ค.'); if (input) input.value = ''; return false; } if (!zkr || !yyzaNormClientPath(zkr.value)) { yyzaShowClientError('ZIP ์๋ณธ์ ์ฅ ๊ธฐ์คํด๋๋ฅผ ๋จผ์ ์ค์ ํ์ธ์. ์ ๋ก๋๋ฅผ ์ ์ฅํ์ง ์์ต๋๋ค.'); if (input) input.value = ''; return false; } syncUploadSid(); if (keepCb && uploadKeepOriginalZip) uploadKeepOriginalZip.value = keepCb.checked ? '1' : '0'; if (fbd) yyzaSetSavedFlatBaseDir(fbd.value); if (zkr) yyzaSetSavedZipKeepRoot(zkr.value); yyzaWriteDropCfg({ sid: uploadSid ? uploadSid.value : currentSid }); return true; } if(dz && input && form){ dz.addEventListener('click', function(){ syncUploadSid(); input.click(); }); input.addEventListener('change', function(){ if(input.files && input.files.length && yyzaBeforeUploadSubmit()){ form.submit(); } }); ['dragenter','dragover'].forEach(function(ev){ dz.addEventListener(ev, function(e){ e.preventDefault(); dz.classList.add('dragover'); }); }); ['dragleave','drop'].forEach(function(ev){ dz.addEventListener(ev, function(e){ e.preventDefault(); dz.classList.remove('dragover'); }); }); dz.addEventListener('drop', function(e){ if(!e.dataTransfer || !e.dataTransfer.files || !e.dataTransfer.files.length) return; input.files = e.dataTransfer.files; if (yyzaBeforeUploadSubmit()) form.submit(); }); } const flatBaseSelect = document.getElementById('flatBaseSelect'); const flatBaseDir = document.getElementById('flatBaseDir'); const applyFlatBaseDir = document.getElementById('applyFlatBaseDir'); const flatPathBaseDir = document.getElementById('flatPathBaseDir'); function syncFlatBaseDir(){ if(applyFlatBaseDir && flatBaseDir) applyFlatBaseDir.value = flatBaseDir.value; if(flatPathBaseDir && flatBaseDir) flatPathBaseDir.value = flatBaseDir.value; } function rememberFlatBaseDirClient(sendServer){ if (!flatBaseDir) return; const v = yyzaSetSavedFlatBaseDir(flatBaseDir.value); if (flatBaseDir.value !== v) flatBaseDir.value = v; syncFlatBaseDir(); if (sendServer) yyzaServerRememberFlatBaseDir(v); } if(flatBaseSelect && flatBaseDir){ flatBaseSelect.addEventListener('change', function(){ flatBaseDir.value = flatBaseSelect.value; rememberFlatBaseDirClient(true); }); flatBaseDir.addEventListener('input', function(){ rememberFlatBaseDirClient(false); }); flatBaseDir.addEventListener('change', function(){ rememberFlatBaseDirClient(true); }); flatBaseDir.addEventListener('blur', function(){ rememberFlatBaseDirClient(true); }); syncFlatBaseDir(); } const flatPathForm = document.getElementById('flatPathForm'); const flatPathText = document.getElementById('flatPathText'); if (flatPathText) { try { const savedPathsText = yyzaGetSavedPathsText(); if (savedPathsText && String(flatPathText.value || '').trim() === '') flatPathText.value = savedPathsText; } catch(e) {} flatPathText.addEventListener('input', function(){ yyzaSetSavedPathsText(flatPathText.value); }); } if (flatPathForm && flatPathText) { flatPathForm.addEventListener('submit', function(){ if (flatBaseDir && flatBaseDir.value) { const cur = yyzaNormClientPath(flatBaseDir.value); const lines = String(flatPathText.value || '').split(/\r\n|\r|\n/).map(function(v){ return yyzaNormClientPath(v); }).filter(Boolean); if (cur && lines.indexOf(cur) < 0) lines.unshift(cur); flatPathText.value = lines.join('\n'); yyzaSetSavedFlatBaseDir(cur); if (flatPathBaseDir) flatPathBaseDir.value = cur; yyzaSetSavedPathsText(flatPathText.value); } }); } const keepOriginalZip = document.getElementById('keepOriginalZip'); const keepOriginalZipApply = document.getElementById('keepOriginalZipApply'); const uploadKeepOriginalZip = document.getElementById('uploadKeepOriginalZip'); const applyKeepOriginalZip = document.getElementById('applyKeepOriginalZip'); const floatingKeepOriginalZip = document.querySelectorAll('.floatingKeepOriginalZip'); const zipKeepRootSelect = document.getElementById('zipKeepRootSelect'); const zipKeepRoot = document.getElementById('zipKeepRoot'); const applyZipKeepRoot = document.getElementById('applyZipKeepRoot'); const zipKeepPathBaseDir = document.getElementById('zipKeepPathBaseDir'); function syncZipKeepRoot(){ if (applyZipKeepRoot && zipKeepRoot) applyZipKeepRoot.value = zipKeepRoot.value; if (zipKeepPathBaseDir && zipKeepRoot) zipKeepPathBaseDir.value = zipKeepRoot.value; } function rememberZipKeepRootClient(sendServer){ if (!zipKeepRoot) return; const v = yyzaSetSavedZipKeepRoot(zipKeepRoot.value); if (zipKeepRoot.value !== v) zipKeepRoot.value = v; syncZipKeepRoot(); if (sendServer) yyzaServerRememberZipKeepRoot(v); } function yyzaSyncKeepOriginalZip(on){ on = !!on; if (keepOriginalZip) keepOriginalZip.checked = on; if (keepOriginalZipApply) keepOriginalZipApply.checked = on; if (uploadKeepOriginalZip) uploadKeepOriginalZip.value = on ? '1' : '0'; if (applyKeepOriginalZip) applyKeepOriginalZip.value = on ? '1' : '0'; floatingKeepOriginalZip.forEach(function(el){ el.value = on ? '1' : '0'; }); try { localStorage.setItem(keepStorageKey, on ? '1' : '0'); } catch(e) {} yyzaWriteDropCfg({ keepOriginalZip: on }); } try { const savedKeep = localStorage.getItem(keepStorageKey); if (savedKeep === '1' || savedKeep === '0') yyzaSyncKeepOriginalZip(savedKeep === '1'); else yyzaSyncKeepOriginalZip(<?= $keepOriginalZip ? 'true' : 'false' ?>); } catch(e) { yyzaSyncKeepOriginalZip(<?= $keepOriginalZip ? 'true' : 'false' ?>); } if (keepOriginalZip) keepOriginalZip.addEventListener('change', function(){ yyzaSyncKeepOriginalZip(keepOriginalZip.checked); }); if (keepOriginalZipApply) keepOriginalZipApply.addEventListener('change', function(){ yyzaSyncKeepOriginalZip(keepOriginalZipApply.checked); }); if (zipKeepRootSelect && zipKeepRoot) { zipKeepRootSelect.addEventListener('change', function(){ zipKeepRoot.value = zipKeepRootSelect.value; rememberZipKeepRootClient(true); }); zipKeepRoot.addEventListener('input', function(){ rememberZipKeepRootClient(false); }); zipKeepRoot.addEventListener('change', function(){ rememberZipKeepRootClient(true); }); zipKeepRoot.addEventListener('blur', function(){ rememberZipKeepRootClient(true); }); syncZipKeepRoot(); } const zipKeepPathForm = document.getElementById('zipKeepPathForm'); const zipKeepPathText = document.getElementById('zipKeepPathText'); if (zipKeepPathText) { try { const savedZipKeepPathsText = yyzaGetSavedZipKeepPathsText(); if (savedZipKeepPathsText && String(zipKeepPathText.value || '').trim() === '') zipKeepPathText.value = savedZipKeepPathsText; } catch(e) {} zipKeepPathText.addEventListener('input', function(){ yyzaSetSavedZipKeepPathsText(zipKeepPathText.value); }); } if (zipKeepPathForm && zipKeepPathText) { zipKeepPathForm.addEventListener('submit', function(){ if (zipKeepRoot && zipKeepRoot.value) { const cur = yyzaNormClientPath(zipKeepRoot.value); const lines = String(zipKeepPathText.value || '').split(/\r\n|\r|\n/).map(function(v){ return yyzaNormClientPath(v); }).filter(Boolean); if (cur && lines.indexOf(cur) < 0) lines.unshift(cur); zipKeepPathText.value = lines.join('\n'); yyzaSetSavedZipKeepRoot(cur); if (zipKeepPathBaseDir) zipKeepPathBaseDir.value = cur; yyzaSetSavedZipKeepPathsText(zipKeepPathText.value); } }); } const zipKeepFileAll = document.getElementById('zipKeepFileAll'); const zipKeepSelectedDownload = document.getElementById('zipKeepSelectedDownload'); const zipKeepDownloadForm = document.getElementById('zipKeepDownloadForm'); const zipKeepFilesJson = document.getElementById('zipKeepFilesJson'); if (zipKeepFileAll) { zipKeepFileAll.addEventListener('change', function(){ document.querySelectorAll('.zip-keep-file').forEach(function(cb){ cb.checked = zipKeepFileAll.checked; }); }); } if (zipKeepSelectedDownload && zipKeepDownloadForm && zipKeepFilesJson) { zipKeepSelectedDownload.addEventListener('click', function(){ const selected = Array.from(document.querySelectorAll('.zip-keep-file:checked')).map(function(cb){ return cb.value; }); if (!selected.length) { if (typeof window.showToast === 'function') window.showToast('์์ถ ๋ค์ด๋ก๋ํ ์๋ณธ ZIP์ ์ ํํ์ธ์.', 'warning'); else if (typeof showToast === 'function') showToast('์์ถ ๋ค์ด๋ก๋ํ ์๋ณธ ZIP์ ์ ํํ์ธ์.', 'warning'); else alert('์์ถ ๋ค์ด๋ก๋ํ ์๋ณธ ZIP์ ์ ํํ์ธ์.'); return; } zipKeepFilesJson.value = JSON.stringify(selected); zipKeepDownloadForm.submit(); }); } const applyAll = document.getElementById('apply-all'); if(applyAll){ applyAll.addEventListener('change', function(){ document.querySelectorAll('.yyza-apply-file').forEach(function(cb){ if(!cb.disabled) cb.checked = applyAll.checked; }); }); } <?php if ($hasDirCreatable || $hasDirBlocked): ?> setTimeout(function(){ var msg = <?= json_encode($hasDirBlocked ? '๊ถํ์กฐ์ ํ์ ํญ๋ชฉ์ด ์์ต๋๋ค. ๋นจ๊ฐ ํ์ ํดํ์์ ์์ํด๋ ์ฐ๊ธฐ๊ถํ์ ํ์ธํ์ธ์.' : '๋ง๋ค์ด์ผํจ ํญ๋ชฉ์ด ์์ด [์๋ ํด๋ ์์ฑ ํ์ฉ]์ ์๋ ์ฒดํฌํ์ต๋๋ค.', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>; if (typeof window.showToast === 'function') window.showToast(msg, <?= json_encode($hasDirBlocked ? 'error' : 'warning') ?>); else if (typeof showToast === 'function') showToast(msg, <?= json_encode($hasDirBlocked ? 'error' : 'warning') ?>); else console.warn(msg); }, 300); <?php endif; ?> try { document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function(el){ new bootstrap.Tooltip(el); }); } catch(e) {} })(); </script> </body> </html>
โจ/var/www/html/y/yyy_zip_apply.php
127,894 bytes
::
php
Y:0px 0%
X:0px 0%
Split:--
Line:1
A0
Aโ
A+
Lh0
Lhโ
Lh+
pre+
ฦ ํจ์ / ํด๋์ค ๋ชฉ๋ก
โ
๐พ
Aa
.*
bF
1
๐
๐
๊ฒ์์ด๋ฅผ ๋จผ์ ์ ๋ ฅํ์ธ์.
1
At
์ ์ฒด์ ์ฉ
๋ค์์ฐพ๊ธฐ
1๊ฐ์ ์ฉ
-----------