Files
firstgarden-web-gnu/plugin/editor/rb.editor/editor.lib.php
2025-07-02 14:13:16 +09:00

245 lines
9.0 KiB
PHP

<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
function editor_html($id, $content, $is_dhtml_editor = true)
{
global $g5, $config;
static $js = true;
$editor_url = G5_EDITOR_URL.'/'.$config['cf_editor'];
// ✅ Nonce 생성 및 세션에 저장
if (!isset($_SESSION['token_' . FT_NONCE_SESSION_KEY])) {
$_SESSION['token_' . FT_NONCE_SESSION_KEY] = ft_nonce_create('rbeditor');
}
$nonce = $_SESSION['token_' . FT_NONCE_SESSION_KEY];
$html = "<span class=\"sound_only\">웹에디터 시작</span>";
if ($is_dhtml_editor) {
if ($js) {
$js = false;
}
$html .= "<script>
var g5_editor_url = '{$editor_url}';
var ed_nonce = '{$nonce}';
window.addEventListener('message', function(event) {
if (event.data.type === 'request-nonce') {
event.source.postMessage({ type: 'rbeditor-nonce', nonce: ed_nonce }, '*');
}
if (event.data.type === 'rbeditor-ready') {
const editorId = event.data.editorId;
const iframe = document.getElementById('rb-editor-frame-' + editorId);
const hiddenInput = document.getElementById('rb-' + editorId + '-hidden');
if (iframe && hiddenInput) {
setTimeout(() => {
iframe.contentWindow.postMessage({
type: 'rbeditor-set-content',
content: hiddenInput.value,
editorId: editorId
}, '*');
}, 100);
}
}
});
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.rb-editor-frames').forEach((iframe) => {
iframe.addEventListener('load', function() {
var messageData = {
type: 'rbeditor-config',
g5_editor_url: g5_editor_url || '',
g5_bo_table: typeof g5_bo_table !== 'undefined' ? g5_bo_table : '',
g5_is_member: typeof g5_is_member !== 'undefined' ? g5_is_member : '',
g5_is_mobile: typeof g5_is_mobile !== 'undefined' ? g5_is_mobile : '',
g5_url: typeof g5_url !== 'undefined' ? g5_url : '',
g5_bbs_url: typeof g5_bbs_url !== 'undefined' ? g5_bbs_url : '',
g5_editor: typeof g5_editor !== 'undefined' ? g5_editor : '',
g5_is_admin: typeof g5_is_admin !== 'undefined' ? g5_is_admin : ''
};
if (typeof g5_admin_url !== 'undefined') {
messageData.g5_admin_url = g5_admin_url;
}
iframe.contentWindow.postMessage(messageData, '*');
});
});
});
</script>";
$html .= "
<iframe id='rb-editor-frame-{$id}' src='{$editor_url}/rb.editor.html?editorId={$id}' data-editor-id='{$id}' class='rb-editor-frames' width='100%' height='670px' frameborder='0'></iframe>
<input type='hidden' name='{$id}' id='rb-{$id}-hidden' value='".htmlspecialchars($content, ENT_QUOTES, 'UTF-8')."'>";
$html .= "<script>
window.addEventListener('message', function(event) {
if (event.data.type === 'rbeditor-content') {
const editorId = event.data.editorId;
const hiddenInput = document.getElementById('rb-' + editorId + '-hidden');
if (hiddenInput) {
let content = event.data.content;
const iframe = document.getElementById('rb-editor-frame-' + editorId);
if (!iframe) {
hiddenInput.value = ensureEditorDataWrapper(content);
return;
}
try {
const editorElem = iframe.contentWindow.document.querySelector('#editor');
let editorStyle = editorElem ? editorElem.style.cssText.trim() : '';
content = ensureEditorDataWrapper(content, editorStyle);
hiddenInput.value = content;
} catch (e) {
hiddenInput.value = ensureEditorDataWrapper(content);
}
}
}
});
function ensureEditorDataWrapper(content, editorStyle = '') {
let parser = new DOMParser();
let doc = parser.parseFromString(content, 'text/html');
// 내용이 텍스트도 없이 완전히 비어있거나 빈 div만 존재하면 빈 값 리턴
if (!doc.body.textContent.trim() && !doc.body.querySelector('img, video, iframe')) {
return '';
}
let existingEditorData = doc.querySelector('.rb_editor_data');
if (!existingEditorData) {
let wrapper = doc.createElement('div');
wrapper.className = 'rb_editor_data';
if (editorStyle) wrapper.style.cssText = editorStyle;
wrapper.innerHTML = doc.body.innerHTML.trim();
doc.body.innerHTML = '';
doc.body.appendChild(wrapper);
} else {
let allElements = [...doc.body.children];
allElements.forEach(el => {
if (!el.classList.contains('rb_editor_data')) {
existingEditorData.appendChild(el);
}
});
if (editorStyle) {
existingEditorData.style.cssText = editorStyle;
}
}
return doc.body.innerHTML;
}
</script>";
} else {
$html .= "<textarea id='$id' name='$id' style='width:100%;height:300px;'>$content</textarea>\n";
}
$html .= "<span class=\"sound_only\">웹 에디터 끝</span>";
return $html;
}
// 에디터 내용 저장 (TEXTAREA → DIV 대응)
function get_editor_js($id, $is_dhtml_editor = true)
{
if ($is_dhtml_editor) {
return "
const iframe_{$id} = document.getElementById('rb-editor-frame-{$id}').querySelector('iframe');
if (iframe_{$id} && iframe_{$id}.contentWindow) {
iframe_{$id}.contentWindow.postMessage({
type: 'rbeditor-get-content',
editorId: '{$id}'
}, '*');
}
";
} else {
return "var {$id}_editor = document.getElementById('{$id}');\n";
}
}
// 에디터 값이 비어 있는지 검사 (TEXTAREA → DIV 대응)
function chk_editor_js($id, $is_dhtml_editor = true)
{
if ($is_dhtml_editor) {
return "
var content = document.getElementById('rb-{$id}-hidden').value;
if (!content || content.trim() === '') {
alert('내용을 입력해 주십시오.');
return false;
}
";
} else {
return "if (!{$id}_editor.value) { alert(\"내용을 입력해 주십시오.\"); {$id}_editor.focus(); return false; }\n";
}
}
// Nonce 관련 상수 및 함수 정의
if (!defined('FT_NONCE_UNIQUE_KEY'))
define('FT_NONCE_UNIQUE_KEY', sha1($_SERVER['SERVER_SOFTWARE'] . G5_MYSQL_USER . session_id() . G5_TABLE_PREFIX));
if (!defined('FT_NONCE_SESSION_KEY'))
define('FT_NONCE_SESSION_KEY', substr(md5(FT_NONCE_UNIQUE_KEY), 5));
if (!defined('FT_NONCE_DURATION'))
define('FT_NONCE_DURATION', 60 * 60);
if (!defined('FT_NONCE_KEY'))
define('FT_NONCE_KEY', '_nonce');
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
function ft_nonce_create($action = '', $user = '', $timeoutSeconds = FT_NONCE_DURATION)
{
$secret = ft_get_secret_key($action . $user);
set_session('token_' . FT_NONCE_SESSION_KEY, $secret);
$salt = ft_nonce_generate_hash();
$time = time();
$maxTime = $time + $timeoutSeconds;
$nonce = $salt . '|' . $maxTime . '|' . sha1($salt . $secret . $maxTime);
return $nonce;
}
function ft_nonce_is_valid($nonce, $action = '', $user = '')
{
$secret = ft_get_secret_key($action.$user);
$token = get_session('token_'.FT_NONCE_SESSION_KEY);
if ($secret != $token) return false;
if (!is_string($nonce)) return false;
$a = explode('|', $nonce);
if (count($a) != 3) return false;
$salt = $a[0];
$maxTime = intval($a[1]);
$hash = $a[2];
$back = sha1($salt . $secret . $maxTime);
if ($back != $hash || time() > $maxTime) return false;
return true;
}
function ft_get_secret_key($secret)
{
return md5(FT_NONCE_UNIQUE_KEY . $secret);
}
function ft_nonce_generate_hash()
{
$length = 10;
$chars = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM';
$ll = strlen($chars) - 1;
$o = '';
while (strlen($o) < $length) {
$o .= $chars[rand(0, $ll)];
}
return $o;
}