- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경 - DB 연결 하드코딩 → .env 기반으로 변경 - MySQL strict mode DATE 오류 수정
523 lines
17 KiB
PHP
523 lines
17 KiB
PHP
<?php
|
|
// 인코딩 설정
|
|
header('Content-Type: text/html; charset=UTF-8');
|
|
mb_internal_encoding('UTF-8');
|
|
mb_http_output('UTF-8');
|
|
|
|
// 문서 파일을 웹에서 보기 좋게 표시하는 페이지
|
|
$requestedFile = isset($_GET['file']) ? $_GET['file'] : 'README.md';
|
|
$allowedFiles = ['README.md', 'common_addrowJS_developer_guide.md', 'common_screen_developer_guide.md', 'common_slat_developer_guide.md', 'compare_lastJS_developer_guide.md', 'compare_price_edit_table_developer_guide.md', 'estimate_compare_head_developer_guide.md', 'lastJS_developer_guide.md', 'output_head_developer_guide.md', 'output_lastJS_developer_guide.md'];
|
|
|
|
// 보안: 허용된 파일만 접근 가능
|
|
if (!in_array($requestedFile, $allowedFiles)) {
|
|
$requestedFile = 'README.md';
|
|
}
|
|
|
|
$readmeFile = __DIR__ . '/' . $requestedFile;
|
|
|
|
// 대안 경로들 시도
|
|
if (!file_exists($readmeFile)) {
|
|
$alternativePaths = [
|
|
$_SERVER['DOCUMENT_ROOT'] . '/5130/0readme/estimate/README.md',
|
|
$_SERVER['DOCUMENT_ROOT'] . '/0readme/estimate/README.md',
|
|
dirname(__DIR__) . '/estimate/README.md',
|
|
dirname(dirname(__DIR__)) . '/estimate/README.md'
|
|
];
|
|
|
|
foreach ($alternativePaths as $path) {
|
|
if (file_exists($path)) {
|
|
$readmeFile = $path;
|
|
// echo "대안 경로에서 파일을 찾았습니다: " . $path . "<br>";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 디버깅 정보 출력
|
|
// echo "현재 디렉토리: " . __DIR__ . "<br>";
|
|
// echo "찾는 파일 경로: " . $readmeFile . "<br>";
|
|
// echo "파일 존재 여부: " . (file_exists($readmeFile) ? '존재함' : '존재하지 않음') . "<br>";
|
|
|
|
// README.md 파일이 존재하는지 확인
|
|
if (!file_exists($readmeFile)) {
|
|
die('README.md 파일을 찾을 수 없습니다. 경로: ' . $readmeFile);
|
|
}
|
|
|
|
// README.md 파일 내용 읽기 (UTF-8 인코딩 명시)
|
|
$content = file_get_contents($readmeFile, false, null, 0, filesize($readmeFile));
|
|
|
|
// BOM 제거 (UTF-8 BOM이 있는 경우)
|
|
if (substr($content, 0, 3) === "\xEF\xBB\xBF") {
|
|
$content = substr($content, 3);
|
|
}
|
|
|
|
// 강제 UTF-8 변환 (모든 경우에 대해)
|
|
$content = mb_convert_encoding($content, 'UTF-8', 'UTF-8, EUC-KR, CP949, ISO-8859-1, ASCII');
|
|
|
|
// 인코딩 확인 및 추가 변환
|
|
if (!mb_check_encoding($content, 'UTF-8')) {
|
|
// UTF-8이 아닌 경우 다시 변환 시도
|
|
$content = mb_convert_encoding($content, 'UTF-8', 'EUC-KR, CP949, ISO-8859-1');
|
|
}
|
|
|
|
// 디버깅: 인코딩 정보 출력 (개발 중에만 사용)
|
|
if (isset($_GET['debug']) && $_GET['debug'] === 'encoding') {
|
|
echo "<!-- 파일 인코딩 정보: " . mb_detect_encoding($content) . " -->\n";
|
|
echo "<!-- 파일 크기: " . strlen($content) . " bytes -->\n";
|
|
echo "<!-- 첫 100자: " . htmlspecialchars(substr($content, 0, 100)) . " -->\n";
|
|
echo "<!-- 변환 후 인코딩: " . mb_detect_encoding($safeContent) . " -->\n";
|
|
}
|
|
|
|
// Markdown을 HTML로 변환하는 간단한 함수
|
|
function markdownToHtml($markdown) {
|
|
// UTF-8 인코딩 확인
|
|
if (!mb_check_encoding($markdown, 'UTF-8')) {
|
|
$markdown = mb_convert_encoding($markdown, 'UTF-8', 'EUC-KR, CP949, ISO-8859-1');
|
|
}
|
|
|
|
// 제목 변환 (한글 지원 개선)
|
|
$markdown = preg_replace('/^# (.*$)/mu', '<h1>$1</h1>', $markdown);
|
|
$markdown = preg_replace('/^## (.*$)/mu', '<h2>$1</h2>', $markdown);
|
|
$markdown = preg_replace('/^### (.*$)/mu', '<h3>$1</h3>', $markdown);
|
|
$markdown = preg_replace('/^#### (.*$)/mu', '<h4>$1</h4>', $markdown);
|
|
$markdown = preg_replace('/^##### (.*$)/mu', '<h5>$1</h5>', $markdown);
|
|
$markdown = preg_replace('/^###### (.*$)/mu', '<h6>$1</h6>', $markdown);
|
|
|
|
// 강조 표시 (한글 지원)
|
|
$markdown = preg_replace('/\*\*(.*?)\*\*/su', '<strong>$1</strong>', $markdown);
|
|
$markdown = preg_replace('/\*(.*?)\*/su', '<em>$1</em>', $markdown);
|
|
$markdown = preg_replace('/`(.*?)`/su', '<code>$1</code>', $markdown);
|
|
|
|
// 링크 (한글 지원)
|
|
$markdown = preg_replace('/\[([^\]]+)\]\(([^)]+)\)/u', '<a href="$2" target="_blank">$1</a>', $markdown);
|
|
|
|
// 코드 블록 (한글 지원)
|
|
$markdown = preg_replace('/```([\s\S]*?)```/su', '<pre><code>$1</code></pre>', $markdown);
|
|
|
|
// 인라인 코드 (한글 지원)
|
|
$markdown = preg_replace('/`([^`]+)`/u', '<code>$1</code>', $markdown);
|
|
|
|
// 목록 (한글 지원)
|
|
$markdown = preg_replace('/^\* (.*$)/mu', '<li>$1</li>', $markdown);
|
|
$markdown = preg_replace('/^- (.*$)/mu', '<li>$1</li>', $markdown);
|
|
$markdown = preg_replace('/^\d+\. (.*$)/mu', '<li>$1</li>', $markdown);
|
|
|
|
// 단락
|
|
$markdown = preg_replace('/\n\n/', '</p><p>', $markdown);
|
|
$markdown = '<p>' . $markdown . '</p>';
|
|
|
|
// 줄바꿈
|
|
$markdown = str_replace("\n", '<br>', $markdown);
|
|
|
|
return $markdown;
|
|
}
|
|
|
|
// HTML 변환
|
|
$htmlContent = markdownToHtml($content);
|
|
?>
|
|
|
|
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>방화셔터 견적 시스템 문서</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
line-height: 1.6;
|
|
color: #333;
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
background-color: white;
|
|
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.header {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
padding: 30px;
|
|
margin: -20px -20px 30px -20px;
|
|
border-radius: 0 0 10px 10px;
|
|
text-align: center;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 2.5em;
|
|
margin-bottom: 10px;
|
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
|
}
|
|
|
|
.nav-tabs {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.nav-tabs .nav-link {
|
|
color: #495057;
|
|
border: none;
|
|
border-bottom: 2px solid transparent;
|
|
padding: 10px 20px;
|
|
font-weight: 500;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.nav-tabs .nav-link:hover {
|
|
color: #667eea;
|
|
border-bottom-color: #667eea;
|
|
}
|
|
|
|
.nav-tabs .nav-link.active {
|
|
color: #667eea;
|
|
border-bottom-color: #667eea;
|
|
background-color: transparent;
|
|
}
|
|
|
|
.header p {
|
|
font-size: 1.2em;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.content {
|
|
font-size: 16px;
|
|
line-height: 1.8;
|
|
}
|
|
|
|
h1, h2, h3, h4, h5, h6 {
|
|
color: #2c3e50;
|
|
margin: 30px 0 15px 0;
|
|
padding-bottom: 10px;
|
|
border-bottom: 2px solid #ecf0f1;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 2.2em;
|
|
color: #34495e;
|
|
}
|
|
|
|
h2 {
|
|
font-size: 1.8em;
|
|
color: #3498db;
|
|
}
|
|
|
|
h3 {
|
|
font-size: 1.5em;
|
|
color: #2980b9;
|
|
}
|
|
|
|
h4 {
|
|
font-size: 1.3em;
|
|
color: #1f618d;
|
|
}
|
|
|
|
h5, h6 {
|
|
font-size: 1.1em;
|
|
color: #154360;
|
|
}
|
|
|
|
p {
|
|
margin-bottom: 15px;
|
|
text-align: justify;
|
|
}
|
|
|
|
strong {
|
|
color: #e74c3c;
|
|
font-weight: 600;
|
|
}
|
|
|
|
em {
|
|
color: #f39c12;
|
|
font-style: italic;
|
|
}
|
|
|
|
code {
|
|
background-color: #f8f9fa;
|
|
color: #e83e8c;
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
pre {
|
|
background-color: #2c3e50;
|
|
color: #ecf0f1;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
overflow-x: auto;
|
|
margin: 20px 0;
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
pre code {
|
|
background: none;
|
|
color: inherit;
|
|
padding: 0;
|
|
}
|
|
|
|
a {
|
|
color: #3498db;
|
|
text-decoration: none;
|
|
border-bottom: 1px solid transparent;
|
|
transition: border-bottom 0.3s ease;
|
|
}
|
|
|
|
a:hover {
|
|
border-bottom: 1px solid #3498db;
|
|
color: #2980b9;
|
|
}
|
|
|
|
ul, ol {
|
|
margin: 15px 0;
|
|
padding-left: 30px;
|
|
}
|
|
|
|
li {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.file-info {
|
|
background-color: #ecf0f1;
|
|
border-left: 4px solid #3498db;
|
|
padding: 15px;
|
|
margin: 15px 0;
|
|
border-radius: 0 5px 5px 0;
|
|
}
|
|
|
|
.file-info strong {
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.folder-structure {
|
|
background-color: #f8f9fa;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
margin: 20px 0;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.emoji {
|
|
font-size: 1.2em;
|
|
margin-right: 8px;
|
|
}
|
|
|
|
.section {
|
|
margin: 30px 0;
|
|
padding: 20px;
|
|
background-color: #f8f9fa;
|
|
border-radius: 8px;
|
|
border-left: 4px solid #3498db;
|
|
}
|
|
|
|
.usage-guide {
|
|
background-color: #e8f5e8;
|
|
border-left: 4px solid #27ae60;
|
|
padding: 20px;
|
|
margin: 20px 0;
|
|
border-radius: 0 8px 8px 0;
|
|
}
|
|
|
|
.usage-guide h3 {
|
|
color: #27ae60;
|
|
border-bottom: 2px solid #27ae60;
|
|
}
|
|
|
|
.security-info {
|
|
background-color: #fff3cd;
|
|
border-left: 4px solid #ffc107;
|
|
padding: 20px;
|
|
margin: 20px 0;
|
|
border-radius: 0 8px 8px 0;
|
|
}
|
|
|
|
.security-info h3 {
|
|
color: #856404;
|
|
border-bottom: 2px solid #ffc107;
|
|
}
|
|
|
|
.footer {
|
|
margin-top: 50px;
|
|
padding: 20px;
|
|
background-color: #34495e;
|
|
color: white;
|
|
text-align: center;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.container {
|
|
padding: 10px;
|
|
}
|
|
|
|
.header {
|
|
padding: 20px;
|
|
margin: -10px -10px 20px -10px;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 2em;
|
|
}
|
|
|
|
h1 { font-size: 1.8em; }
|
|
h2 { font-size: 1.5em; }
|
|
h3 { font-size: 1.3em; }
|
|
h4 { font-size: 1.1em; }
|
|
}
|
|
|
|
/* 스크롤바 스타일링 */
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: #f1f1f1;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: #888;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: #555;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>🏢 방화셔터 견적 시스템</h1>
|
|
<p>Estimate System Documentation</p>
|
|
</div>
|
|
|
|
<!-- 네비게이션 탭 -->
|
|
<ul class="nav nav-tabs" id="documentTabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link <?= $requestedFile === 'README.md' ? 'active' : '' ?>"
|
|
href="?file=README.md" role="tab">
|
|
📋 시스템 개요
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link <?= $requestedFile === 'common_addrowJS_developer_guide.md' ? 'active' : '' ?>"
|
|
href="?file=common_addrowJS_developer_guide.md" role="tab">
|
|
🔧 JavaScript 가이드
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link <?= $requestedFile === 'common_screen_developer_guide.md' ? 'active' : '' ?>"
|
|
href="?file=common_screen_developer_guide.md" role="tab">
|
|
📊 스크린 가이드
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link <?= $requestedFile === 'common_slat_developer_guide.md' ? 'active' : '' ?>"
|
|
href="?file=common_slat_developer_guide.md" role="tab">
|
|
🔧 철재 가이드
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link <?= $requestedFile === 'compare_lastJS_developer_guide.md' ? 'active' : '' ?>"
|
|
href="?file=compare_lastJS_developer_guide.md" role="tab">
|
|
📊 비교 가이드
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link <?= $requestedFile === 'compare_price_edit_table_developer_guide.md' ? 'active' : '' ?>"
|
|
href="?file=compare_price_edit_table_developer_guide.md" role="tab">
|
|
💰 단가 가이드
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link <?= $requestedFile === 'estimate_compare_head_developer_guide.md' ? 'active' : '' ?>"
|
|
href="?file=estimate_compare_head_developer_guide.md" role="tab">
|
|
📋 헤더 가이드
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link <?= $requestedFile === 'lastJS_developer_guide.md' ? 'active' : '' ?>"
|
|
href="?file=lastJS_developer_guide.md" role="tab">
|
|
🧮 계산 가이드
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link <?= $requestedFile === 'output_head_developer_guide.md' ? 'active' : '' ?>"
|
|
href="?file=output_head_developer_guide.md" role="tab">
|
|
📄 거래명세표 가이드
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link <?= $requestedFile === 'output_lastJS_developer_guide.md' ? 'active' : '' ?>"
|
|
href="?file=output_lastJS_developer_guide.md" role="tab">
|
|
🧮 거래명세표 계산 가이드
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="content">
|
|
<?php
|
|
// 안전한 HTML 출력
|
|
$safeContent = mb_convert_encoding($htmlContent, 'UTF-8', 'UTF-8');
|
|
echo $safeContent;
|
|
?>
|
|
</div>
|
|
|
|
<div class="footer">
|
|
<p>📁 파일 경로: <?php echo realpath($readmeFile); ?></p>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// 코드 블록에 구문 강조 적용
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// 모든 pre 태그에 클래스 추가
|
|
const preElements = document.querySelectorAll('pre');
|
|
preElements.forEach(function(pre) {
|
|
pre.classList.add('language-php');
|
|
});
|
|
|
|
// 제목에 앵커 링크 추가
|
|
const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
|
headings.forEach(function(heading) {
|
|
const id = heading.textContent.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
heading.id = id;
|
|
|
|
const link = document.createElement('a');
|
|
link.href = '#' + id;
|
|
link.innerHTML = '🔗';
|
|
link.style.cssText = 'float: right; text-decoration: none; opacity: 0.5; font-size: 0.8em;';
|
|
link.title = '이 섹션으로 링크';
|
|
|
|
heading.appendChild(link);
|
|
});
|
|
|
|
// 파일 정보 박스 스타일링
|
|
const fileInfos = document.querySelectorAll('strong');
|
|
fileInfos.forEach(function(strong) {
|
|
if (strong.textContent.includes('.php') || strong.textContent.includes('.css') || strong.textContent.includes('.js')) {
|
|
const parent = strong.parentElement;
|
|
if (parent.tagName === 'P') {
|
|
parent.classList.add('file-info');
|
|
}
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|