스킨 댓글창 코어파일 수정없이 댓글 삭제 표시 및 기타 기능 추가
코어파일 수정이나 db불러오는 거 없이,,
순전히 댓글의 들여쓰기 상태로 파악하도록 했습니다.
대댓글에는 원댓글 작성자명이 '@원댓글작성자명' 이렇게 표시됩니다.
게시판 본문글을 작성한 사람의 댓글은 배경색이 나오게 했습니다.
삭제된 댓글에 달린 대댓글이 없는 경우는 삭제 표시없이 그냥 없어집니다.
(이 부분이 제 용도에는 맞는데, 다른 분은 어떨지 모르겠네요.)
테스트를 많이 거친게 아니라 오류가 있을수 있으니 원본은 꼭 백업해 두시고,
오류가 있으면 말씀해주시고, 감당이 안되는 오류가 있으면 이 글을 삭제 하겠습니다.
그리고 제 홈페이지 용도에 맞춘거라 디자인은 다소 이상할 거 같습니다.
view_comment.skin.php 파일 내용 ---------------------------
<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
?>
<style>
/* ── 댓글 토글 버튼 ── */
.cmt_btn {
display: inline-flex;
align-items: center;
gap: 5px;
background: none;
border: none;
padding: 4px 0;
font-size: 0.82em;
cursor: pointer;
color: inherit;
}
.cmt_btn .cmt_more::before {
content: '▾';
display: inline-block;
transition: transform 0.2s;
}
.cmt_btn.cmt_btn_op .cmt_more::before {
transform: rotate(180deg);
}
/* ── 댓글 목록 섹션 ── */
#bo_vc {
border-top: 1px solid rgba(128,128,128,0.25);
opacity: 1;
padding-top: 10px;
margin-top: 6px;
}
#bo_vc > h2 { display: none; }
/* ── 댓글 없을 때 ── */
#bo_vc_empty {
padding: 14px 0;
text-align: center;
font-size: 0.82em;
opacity: 0.4;
}
/* ── 개별 댓글 article (공통 경계선) ── */
#bo_vc article {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 12px 10px;
border-bottom: 1px solid rgba(128,128,128,0.18);
position: relative;
width: 100%;
box-sizing: border-box;
}
#bo_vc article:last-of-type { border-bottom: none; }
/* [수정] 원글 작성자 강조 스타일 - 흰색 배경에서도 잘 보이도록 보완 */
#bo_vc article.is_author {
background-color: rgba(0, 0, 0, 0.03); /* 아주 연한 회색 배경 */
border: 1px solid rgba(0, 0, 0, 0.05); /* 은은한 테두리 추가 */
border-radius: 8px;
margin-top: 4px;
margin-bottom: 4px;
}
/* ── 삭제된 댓글 가상 표시 스타일 ── */
#bo_vc article.is_deleted_placeholder {
padding-top: 10px;
padding-bottom: 10px;
background: none;
}
.deleted_text {
display: flex;
align-items: center;
gap: 6px;
color: #e74c3c;
font-size: 0.82em;
opacity: 0.8;
}
/* 프로필 이미지 */
#bo_vc .pf_img {
flex-shrink: 0;
width: 26px;
height: 26px;
}
#bo_vc .pf_img img {
width: 26px;
height: 26px;
border-radius: 50%;
object-fit: cover;
display: block;
}
/* cm_wrap */
#bo_vc .cm_wrap {
flex: 1;
min-width: 0;
}
/* ── 댓글 헤더 ── */
#bo_vc header {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 3px;
background: none !important;
border: none !important;
padding: 0 !important;
position: static !important;
}
#bo_vc header > h2 { display: none; }
#bo_vc .bo_vc_hdinfo {
font-size: 0.75em;
opacity: 0.42;
margin-left: auto;
}
/* ── 댓글 본문 ── */
#bo_vc .cmt_contents {
font-size: 0.88em;
line-height: 1.6;
word-break: break-all;
}
.reply_mention {
color: #3498db;
font-weight: 600;
margin-right: 4px;
font-size: 0.95em;
}
#bo_vc .cmt_contents p { margin: 0; }
#bo_vc .cmt_contents img[alt="비밀글"] {
width: 11px;
vertical-align: middle;
opacity: 0.35;
margin-right: 2px;
}
/* ── 답변/수정/삭제 옵션 영역 ── */
#bo_vc .bo_vl_opt {
width: 100%;
padding-left: 34px;
box-sizing: border-box;
}
#bo_vc .bo_vl_opt .btn_cm_opt { display: none !important; }
#bo_vc .bo_vl_opt .bo_vc_act {
display: flex !important;
flex-direction: row;
gap: 10px;
list-style: none;
margin: 0;
padding: 0;
}
#bo_vc .bo_vl_opt .bo_vc_act li { margin: 0; padding: 0; }
#bo_vc .bo_vl_opt .bo_vc_act li a {
font-size: 0.75em;
opacity: 0.35;
text-decoration: none;
color: inherit;
}
#bo_vc .bo_vl_opt .bo_vc_act li a:hover {
opacity: 0.8;
text-decoration: underline;
}
/* ── 댓글 쓰기 aside ── */
aside#bo_vc_w {
margin-top: 10px;
border-top: 1px solid rgba(128,128,128,0.25);
padding-top: 10px;
}
aside#bo_vc_w > h2 { display: none; }
#fviewcomment textarea#wr_content {
display: block !important;
width: 100% !important;
min-height: 68px;
background: transparent;
color: inherit;
border: 1px solid rgba(128,128,128,0.35);
border-radius: 5px;
padding: 7px 9px;
font-size: 0.84em;
font-family: inherit;
line-height: 1.55;
resize: vertical;
box-sizing: border-box;
outline: none;
}
#fviewcomment textarea#wr_content:focus {
border-color: rgba(128,128,128,0.7);
}
#char_cnt {
float: right;
font-size: 0.74em;
font-weight: normal;
opacity: 0.4;
margin-bottom: 2px;
}
.bo_vc_w_wr {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 5px;
margin-top: 5px;
}
.bo_vc_w_info {
display: flex;
align-items: center;
gap: 5px;
flex-wrap: wrap;
}
.bo_vc_w_info .frm_input {
background: transparent;
color: inherit;
border: 1px solid rgba(128,128,128,0.35);
border-radius: 4px;
padding: 4px 7px;
font-size: 0.8em;
outline: none;
width: 88px;
font-family: inherit;
}
.bo_vc_w_info .frm_input:focus {
border-color: rgba(128,128,128,0.7);
}
.btn_confirm {
display: flex;
align-items: center;
gap: 8px;
margin-left: auto;
}
.secret_cm label {
font-size: 0.76em;
opacity: 0.5;
cursor: pointer;
display: flex;
align-items: center;
gap: 3px;
color: inherit;
}
.secret_cm input[type="checkbox"] { width: 12px; height: 12px; }
.secret_cm label span { display: none; }
#btn_submit {
border: 1px solid rgba(128,128,128,0.5);
border-radius: 4px;
padding: 4px 13px;
font-size: 0.8em;
cursor: pointer;
background: transparent;
color: inherit;
font-family: inherit;
transition: opacity 0.15s;
}
#btn_submit:hover { opacity: 0.55; }
#btn_submit:disabled { opacity: 0.25; cursor: default; }
.sound_only {
position: absolute;
width: 1px; height: 1px;
overflow: hidden;
clip: rect(0,0,0,0);
}
</style>
<script>
var char_min = parseInt(<?php echo $comment_min ?>);
var char_max = parseInt(<?php echo $comment_max ?>);
</script>
<button type="button" class="cmt_btn"><span class="total"><b>댓글</b> <?php echo $view['wr_comment']; ?></span><span class="cmt_more"></span></button>
<section id="bo_vc">
<h2>댓글목록</h2>
<?php
$cmt_amt = count($list);
$printed_replies = array();
for ($i=0; $i<$cmt_amt; $i++) {
$comment_id = $list[$i]['wr_id'];
$reply_str = $list[$i]['wr_comment_reply'];
$group_id = $list[$i]['wr_comment']; // 같은 댓글 묶음 번호
// ── 원댓글 및 단계별 삭제 대응 로직 ──
$root_exists = false;
for ($j=0; $j<$cmt_amt; $j++) {
if ($list[$j]['wr_comment'] == $group_id && !$list[$j]['wr_comment_reply']) {
$root_exists = true;
break;
}
}
if (!$root_exists && !in_array($group_id.'_root', $printed_replies)) {
?>
<article class="is_deleted_placeholder" style="padding-left:10px;">
<div class="deleted_text"><i class="fa fa-times-circle"></i> 삭제된 원본 댓글입니다.</div>
</article>
<?php
$printed_replies[] = $group_id.'_root';
}
if ($reply_str) {
for ($k=1; $k<=strlen($reply_str); $k++) {
$check_path = substr($reply_str, 0, $k);
$path_exists = false;
for ($j=0; $j<$cmt_amt; $j++) {
if ($list[$j]['wr_comment_reply'] == $check_path && $list[$j]['wr_comment'] == $group_id) {
$path_exists = true;
break;
}
}
if (!$path_exists && !in_array($group_id.'_'.$check_path, $printed_replies)) {
$p_depth = (strlen($check_path)) * 20;
?>
<article class="is_deleted_placeholder" style="padding-left:<?php echo $p_depth + 10 ?>px;">
<div class="deleted_text"><i class="fa fa-times-circle"></i> 삭제된 대댓글입니다.</div>
</article>
<?php
$printed_replies[] = $group_id.'_'.$check_path;
}
}
}
$printed_replies[] = $group_id.'_'.$reply_str;
// ── 본 댓글 출력 ──
$cmt_depth = strlen($reply_str) * 20;
// 게시글 작성자와 댓글 작성자의 mb_id가 일치하는지 확인
$is_author_class = ($list[$i]['mb_id'] && $list[$i]['mb_id'] === $view['mb_id']) ? 'is_author' : '';
$reply_name = '';
if ($reply_str) {
$parent_reply_path = substr($reply_str, 0, -1);
for ($j=$i-1; $j>=0; $j--) {
if ($list[$j]['wr_comment_reply'] == $parent_reply_path && $list[$j]['wr_comment'] == $list[$i]['wr_comment']) {
$reply_name = get_text($list[$j]['wr_name']);
break;
}
}
}
$comment = $list[$i]['content'];
$comment = preg_replace("/\[\<a\s.*href\=\"(http|https|ftp|mms)\:\/\/([^[:space:]]+)\.(mp3|wma|wmv|asf|asx|mpg|mpeg)\".*\<\/a\>\]/i", "<script>doc_write(obj_movie('$1://$2.$3'));<\/script>", $comment);
$cmt_sv = $cmt_amt - $i + 1;
$c_reply_href = $comment_common_url.'&c_id='.$comment_id.'&w=c#bo_vc_w';
$c_edit_href = $comment_common_url.'&c_id='.$comment_id.'&w=cu#bo_vc_w';
$is_comment_reply_edit = ($list[$i]['is_reply'] || $list[$i]['is_edit'] || $list[$i]['is_del']) ? 1 : 0;
?>
<article id="c_<?php echo $comment_id ?>" class="<?php echo $is_author_class; ?>" style="padding-left:<?php echo $cmt_depth + 10 ?>px;">
<div class="pf_img"><?php echo get_member_profile_img($list[$i]['mb_id']); ?></div>
<div class="cm_wrap">
<header style="z-index:<?php echo $cmt_sv; ?>">
<h2><?php echo get_text($list[$i]['wr_name']); ?>님의 댓글</h2>
<?php echo $list[$i]['name'] ?>
<?php if ($is_ip_view) { ?>
<span>(<?php echo $list[$i]['ip']; ?>)</span>
<?php } ?>
<span class="bo_vc_hdinfo"><i class="fa fa-clock-o"></i> <time datetime="<?php echo date('Y-m-d\TH:i:s+09:00', strtotime($list[$i]['datetime'])) ?>"><?php echo $list[$i]['datetime'] ?></time></span>
</header>
<div class="cmt_contents">
<p>
<?php if (strstr($list[$i]['wr_option'], "secret")) { ?><img src="<?php echo $board_skin_url; ?>/img/icon_secret.gif" alt="비밀글"><?php } ?>
<?php if ($reply_name) { ?>
<span class="reply_mention">@<?php echo $reply_name; ?></span>
<?php } ?>
<?php echo $comment ?>
</p>
</div>
<span id="edit_<?php echo $comment_id ?>" class="bo_vc_w"></span><span id="reply_<?php echo $comment_id ?>" class="bo_vc_w"></span>
<input type="hidden" value="<?php echo strstr($list[$i]['wr_option'],"secret") ?>" id="secret_comment_<?php echo $comment_id ?>">
<textarea id="save_comment_<?php echo $comment_id ?>" style="display:none"><?php echo get_text($list[$i]['content1'], 0) ?></textarea>
</div>
<?php if($is_comment_reply_edit) { ?>
<div class="bo_vl_opt">
<ul class="bo_vc_act">
<?php if ($list[$i]['is_reply']) { ?><li><a href="<?php echo $c_reply_href; ?>" onclick="comment_box('<?php echo $comment_id ?>', 'c'); return false;">답변</a></li><?php } ?>
<?php if ($list[$i]['is_edit']) { ?><li><a href="<?php echo $c_edit_href; ?>" onclick="comment_box('<?php echo $comment_id ?>', 'cu'); return false;">수정</a></li><?php } ?>
<?php if ($list[$i]['is_del']) { ?><li><a href="<?php echo $list[$i]['del_link']; ?>" onclick="return comment_delete();">삭제</a></li><?php } ?>
</ul>
</div>
<?php } ?>
</article>
<?php } ?>
<?php if ($i == 0) { ?><p id="bo_vc_empty">등록된 댓글이 없습니다.</p><?php } ?>
</section>
<?php if ($is_comment_write) {
if($w == '') $w = 'c';
?>
<aside id="bo_vc_w" class="bo_vc_w">
<h2>댓글쓰기</h2>
<form name="fviewcomment" id="fviewcomment" action="<?php echo $comment_action_url; ?>" onsubmit="return fviewcomment_submit(this);" method="post" autocomplete="off">
<input type="hidden" name="w" value="<?php echo $w ?>" id="w">
<input type="hidden" name="bo_table" value="<?php echo $bo_table ?>">
<input type="hidden" name="wr_id" value="<?php echo $wr_id ?>">
<input type="hidden" name="comment_id" value="<?php echo $c_id ?>" id="comment_id">
<input type="hidden" name="sca" value="<?php echo $sca ?>">
<input type="hidden" name="sfl" value="<?php echo $sfl ?>">
<input type="hidden" name="stx" value="<?php echo $stx ?>">
<input type="hidden" name="spt" value="<?php echo $spt ?>">
<input type="hidden" name="page" value="<?php echo $page ?>">
<input type="hidden" name="is_good" value="">
<?php if ($comment_min || $comment_max) { ?><strong id="char_cnt"><span id="char_count"></span>글자</strong><?php } ?>
<textarea id="wr_content" name="wr_content" maxlength="10000" required class="required" title="내용" placeholder="댓글내용을 입력해주세요"
<?php if ($comment_min || $comment_max) { ?>onkeyup="check_byte('wr_content', 'char_count');"<?php } ?>><?php echo $c_wr_content; ?></textarea>
<div class="bo_vc_w_wr">
<div class="bo_vc_w_info">
<?php if ($is_guest) { ?>
<input type="text" name="wr_name" value="<?php echo get_cookie("ck_sns_name"); ?>" id="wr_name" required class="frm_input required" size="25" placeholder="이름">
<input type="password" name="wr_password" id="wr_password" required class="frm_input required" size="25" placeholder="비밀번호">
<?php } ?>
</div>
<div class="btn_confirm">
<span class="secret_cm chk_box">
<input type="checkbox" name="wr_secret" value="secret" id="wr_secret" class="selec_chk">
<label for="wr_secret"><span></span>비밀글</label>
</span>
<button type="submit" id="btn_submit" class="btn_submit">댓글등록</button>
</div>
</div>
</form>
</aside>
<script>
var save_before = '';
var save_html = document.getElementById('bo_vc_w').innerHTML;
function fviewcomment_submit(f)
{
var pattern = /(^\s*)|(\s*$)/g;
f.is_good.value = 0;
var content = "";
$.ajax({
url: g5_bbs_url+"/ajax.filter.php",
type: "POST",
data: { "subject": "", "content": f.wr_content.value },
dataType: "json",
async: false,
cache: false,
success: function(data) { content = data.content; }
});
if (content) {
alert("내용에 금지단어('"+content+"')가 포함되어있습니다");
f.wr_content.focus();
return false;
}
f.wr_content.value = f.wr_content.value.replace(pattern, "");
if (!f.wr_content.value) { alert("댓글을 입력하여 주십시오."); return false; }
<?php if($is_guest) echo chk_captcha_js(); ?>
set_comment_token(f);
document.getElementById("btn_submit").disabled = "disabled";
return true;
}
function comment_box(comment_id, work)
{
var el_id, form_el = 'fviewcomment', respond = document.getElementById(form_el);
if (comment_id) {
if (work == 'c') el_id = 'reply_' + comment_id;
else el_id = 'edit_' + comment_id;
} else el_id = 'bo_vc_w';
if (save_before != el_id) {
if (save_before) document.getElementById(save_before).style.display = 'none';
document.getElementById(el_id).style.display = '';
document.getElementById(el_id).appendChild(respond);
document.getElementById('wr_content').value = '';
if (work == 'cu') {
document.getElementById('wr_content').value = document.getElementById('save_comment_' + comment_id).value;
if (document.getElementById('secret_comment_'+comment_id).value) document.getElementById('wr_secret').checked = true;
else document.getElementById('wr_secret').checked = false;
}
document.getElementById('comment_id').value = comment_id;
document.getElementById('w').value = work;
save_before = el_id;
}
}
function comment_delete() { return confirm("이 댓글을 삭제하시겠습니까?"); }
comment_box('', 'c');
jQuery(function($) {
$(".cmt_btn").click(function(e){
e.preventDefault();
$(this).toggleClass("cmt_btn_op");
$("#bo_vc").toggle();
});
});
</script>
<?php } ?>
댓글 3개