카테고리 관련
MORE// 1️⃣ 말머리 리스트와 게시글 개수 가져오기
$sql = "SELECT
SUBSTRING_INDEX(wr_subject, ')', 1) AS genre,
COUNT(*) AS count
FROM avo_write_$bo_table
WHERE wr_subject LIKE '%)%'
GROUP BY genre
ORDER BY genre ASC";
$result = sql_query($sql);
// 2️⃣ 결과를 배열($genre)에 저장
$genre = [];
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$genre[$row['genre']] = $row['count']; // 카테고리와 개수를 배열에 저장
}
}
$genre_option = "";
// 3️⃣ 말머리가 존재할 경우 출력
if (!empty($genre)) {
$genre_option = '<nav id="navi_category">';
$genre_option .= ' <ul>';
$genre_option .= ' <li><a href="?bo_table=' . $bo_table . '">전체</a></li>';
foreach ($genre as $genre_name => $count) {
$genre_option .= ' <li><a href="?bo_table=' . $bo_table . '&stx=' . urlencode($genre_name) . '">'
. htmlspecialchars($genre_name) . ' <span class="cnt_tit">' . $count . '</span></a></li>';
}
$genre_option .= ' </ul>';
$genre_option .= '</nav>';
}
return $genre_option;
// 말머리에 class 부여하기
function replace_subject($str)
{
// $pattern = "/^(\\S+?)\\)\\s(.*)/";
// $pattern = "/^(\\S+?)\\)\\s?(.*)/";
$pattern = "/^([^)]+)\\)\\s?(.*)/";
$replacement = '<span class="genre">$1</span>$2';
return preg_replace($pattern, $replacement, $str);
}
<? include($board_skin_path . "/bbs_str.lib.php") ?>
<? echo replace_subject($list[$i]['subject']) ?>
치환자 관련
MORE<?
if (!defined("_GNUBOARD_")) exit; // 개별 페이지 접근 불가
// 변환 패턴 및 치환 정의
function replace_contents($str)
{
$str = markup_text($str);
//링크에 라벨붙게 만들기
$str = preg_replace_callback(
'`<a href="([^"])"[^>]>(.*?)<\/a>`i',
function ($match) {
$url_label = explode(",", $match[2]);
$url = $match[1];
// 일반 링크 설정
$url = preg_replace_callback(
'`^(?!http)[^@](@.)?$`i',
function ($match) {
return 'http://' . $match[0];
},
$url
);
// 라벨 값이 없을 경우 LINK로 설정
$label = isset($url_label[1]) ? $url_label[1] : 'LINK';
// 주소와 라벨을 구분하여 설정
if (isset($url_label[0])) {
$url = trim($url_label[0]);
}
return '<a href="' . $url . '" target="_blank" class="other-site-link textggu--etc4">' . $label . '</a>';
},
$str
);
// 이미지
$str = preg_replace("/\[\<a\shref\=\"(http|https|ftp)\:\/\/([^[:space:]]+)\.(gif|png|jpg|jpeg|bmp)\"\s[^\>]\>[^\s]\<\/a\>\]/i", "<img src='$1://$2.$3' id='target_resize_image[]' onclick='image_window(this);' border='0'>", $str);
// 유튜브 임베드
$str = preg_replace('/\[\s<a\shref="https:\/\/(?:www\.)?(?:youtube\.com\/(?:watch\?v=|shorts\/|live\/)|youtu\.be\/)([a-zA-Z0-9_-]+)(?:\?si=[a-zA-Z0-9_-]+)?[^"]"[^>]>[^<]<\/a>\s\]/i', '<iframe width="100%" height="315" src="https://www.youtube.com/embed/$1" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>', $str);
// 트위터 임베드
$str = preg_replace('/\[\s<a\shref="https:\/\/(?:x\.com|twitter\.com)\/([a-zA-Z0-9_]+)\/status\/([0-9]+)"[^>]>[^<]<\/a>\s*\]/i', '<div><blockquote class="twitter-tweet"><a href="https://twitter.com/$1/status/$2"></a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>', $str);
// 요약 변환
$str = preg_replace('`요1\[(?![\s])(.?)(?<![\s])\]-(?![\s])(.?)(?<![\s])-`', '<span class="details1"><details><summary class="textggu--etc6">$2</summary>$1</details></span>', $str);
// 해시태그 처리
// $str = preg_replace(
// "/(?<!&\#)(?<=\s)#([0-9a-zA-Z가-힣_]+)/", // #단어 형태의 해시태그 처리, ' 같은 엔티티는 제외
// '<a href="?bo_table=' . $bo_table . '&hash=%23$1" class="link_hash_tag textggu--etc7">#$1</a>',
// $str
// );
// 스타일 설정
$str = preg_replace('`제목1\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--title1">$1</span>', $str);
$str = preg_replace('`제목2\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--title2">$1</span>', $str);
$str = preg_replace('`제목3\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--title3">$1</span>', $str);
$str = preg_replace('`제목4\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--title4">$1</span>', $str);
$str = preg_replace('`제목5\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--title5">$1</span>', $str);
$str = preg_replace('`제목6\[(?![\s])(.?)(?<![\s])\]-(?![\s])(.?)(?<![\s])-`', '<span class="textggu--title6" data-text="$2">$1</span>', $str);
$str = preg_replace('`제목7\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--title7">$1</span>', $str);
$str = preg_replace('`소제1\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--sub1">$1</span>', $str);
$str = preg_replace('`소제2\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--sub2">$1</span>', $str);
$str = preg_replace('`소제3\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--sub3">$1</span>', $str);
$str = preg_replace('`소제4\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--sub4">$1</span>', $str);
$str = preg_replace('`소제5\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--sub5">$1</span>', $str);
$str = preg_replace('`소제6\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--sub6">$1</span>', $str);
$str = preg_replace('`소제7\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--sub7">$1</span>', $str);
$str = preg_replace('`기타1\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--etc1">$1</span>', $str);
$str = preg_replace('`기타2\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--etc2">$1</span>', $str);
$str = preg_replace('`기타3\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--etc3">$1</span>', $str);
$str = preg_replace('`기타4\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--etc4">$1</span>', $str);
$str = preg_replace('`기타5\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--etc5">$1</span>', $str);
$str = preg_replace('`기타6\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--etc6">$1</span>', $str);
$str = preg_replace('`기타7\[(?![\s])(.?)(?<![\s*])\]`', '<span class="textggu--etc7">$1</span>', $str);
echo $str;
};
?>
<? include($board_skin_path . "/replacement_str.php") ?>
<? replace_contents(get_view_thumbnail($view['content'])) ?>
<? replace_contents($comment) ?>
접기 시 내용에 개행이 있으면 변환 안 되는 오류 수정
MORE$str = preg_replace_callback(
'`요1\[(.?)\]-(.?)-`s',
function ($match) {
return '<span class="details1"><details><summary class="textggu--etc1">▶' . $match[2] . '</summary>' . replace_contents($match[1]) . '</details></span>';
},
$str
);
하단의 스타일 설정 공통 처리 가능하도록 수정
MORE $style_patterns = [
'제목' => 'textggu--title',
'소제' => 'textggu--sub',
'기타' => 'textggu--etc',
];
foreach ($style_patterns as $key => $class_prefix) {
for ($i = 1; $i <= 7; $i++) {
$str = preg_replace(
"`{$key}{$i}\[(.*?)\]`",
"<span class='{$class_prefix}{$i}'>$1</span>",
$str
);
}
}
// 제목6 추가 변환 (data-text 속성 포함)
$str = preg_replace(
'`제목6\[(.?)\]-(.?)-`',
'<span class="textggu--title6" data-text="$2">$1</span>',
$str
);
요약 시 내부의 트위터 임베딩이 안 되는 오류 수정
MORE// 요약 변환 (재귀 적용)
$str = preg_replace_callback(
'`요1\[(.?)\]-(.?)-`s',
function ($match) {
return '<span class="details1"><details><summary class="textggu--etc1">▶' . $match[2] . '</summary>' . $match[1] . '</details></span>';
// return '<span class="details1"><details><summary class="textggu--etc1">▶' . $match[2] . '</summary>' . replace_contents($match[1]) . '</details></span>';
},
$str
);
DB 내에서 일괄적으로 말머리 붙일 때 sql문
MOREUPDATE avo_write_[테이블명]
SET wr_subject = CONCAT('[말머리A]) ', wr_subject)
WHERE wr_is_comment = 0 AND wr_subject NOT LIKE '[말머리A])%';
UPDATE avo_write_[테이블명]
SET wr_subject = CONCAT('[말머리A]) ', wr_subject)
WHERE wr_is_comment = 0
AND ca_name = '[카테고리명]'
AND wr_subject NOT LIKE '[말머리A])%';
보호글 작성 기능 추가
MORE<dl>
<dt>OPTION</dt>
<dd>
<? if ($is_secret != 2 || $is_admin) { ?>
<select name="set_secret" id="set_secret">
<option value="">전체공개</option>
<?= $sec ?>
</select>
<? } ?>
<?php echo $option ?>
</dd>
</dl>
<dl id="set_protect" style="display:<?= $w == 'u' && $pro_select ? 'block' : 'none' ?>;">
<dt><label for="wr_protect">PW</label></dt>
<dd><input type="text" name="wr_protect" id="wr_protect" value="<?= $write['wr_protect'] ?>" maxlength="20"></dd>
</dl>
$('#set_secret').on('change', function() {
var selection = $(this).val();
if(selection=='protect') $('#set_protect').css('display','block');
else {$('#set_protect').css('display','none'); $('#wr_protect').val('');}
});
각 카테고리 별 게시글이 검색 구간 나뉘어 보기 불편한 문제 일단 수정
MORE /*
// 가장 작은 번호를 얻어서 변수에 저장 (하단의 페이징에서 사용)
$sql = " select MIN(wr_num) as min_wr_num from {$write_table} ";
$row = sql_fetch($sql);
$min_spt = (int)$row['min_wr_num'];
if (!$spt) $spt = $min_spt;
$sql_search .= " and (wr_num between {$spt} and ({$spt} + {$config['cf_search_part']})) ";
*/
치환자는 (코드)내용(코드)
<!-- highlight.js CSS -->
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css"> -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/a11y-dark.min.css">
<!-- highlight.js JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', (event) => {
// 모든 코드 블록에 대해 하이라이팅 적용
hljs.highlightAll();
});
</script>
function markup_text($str)
{
// 1. 감싸진 부분을 우선 템포러리한 구분자로 변환
$str = preg_replace_callback('/\(코드\)(.*?)\(코드\)/s', function ($matches) {
// 감싸진 텍스트를 임시 변수에 저장
$temp_code = $matches[1];
// 2. 백틱 처리 전에 임시 변수에 저장된 텍스트를 수정 (백틱 처리)
// 백틱을 임시 마커로 변환
$temp_code = preg_replace('/`(.*?)`/s', '`$1`', $temp_code);
// 감싸진 텍스트를 임시 태그로 감싸기
return '<!--START_CODE-->' . $temp_code . '<!--END_CODE-->';
}, $str);
// 2. 기존 마크업 변환 처리 (중략)
// 3. 감쌌던 부분을 다시 <pre><code>로 복원하고, 'language-php' 클래스를 추가
$str = preg_replace_callback('/<!--START_CODE-->(.*?)<!--END_CODE-->/s', function ($matches) {
// 백틱을 임시 마커에서 원래의 백틱으로 복원
$code = str_replace('`', '`', $matches[1]);
// return '<pre><code class="language-php">' . htmlspecialchars($code, ENT_NOQUOTES) . '</code></pre>';
return '<pre><code class="language-php">' . $code . '</code></pre>';
}, $str);
return $str;
}
단백님의 티키타카 스킨에서 해당 문제 발생
define('G5_DB_URL', 'MySQL 관리자 링크');
blockquote { margin: 30px 5px 5px 5px; }
[아보카도 에디션] 트위터, 유튜브, 이미지 링크로 임베딩하기 + 접기 기능 넣기 + 링크에 라벨 붙여 단축하기
해시태그 처리 시 '와 같은 엔티티는 제외하도록 정규식 수정