Mặc định NukeViet chưa có tính năng tạo mục lục tự động. Bài viết này sẽ hướng dẫn các bạn thêm Mục lục Tự động cho NukeViet CMS.
Lưu ý: Để có thể hiển thị mục lục đúng và đẹp, chúng ta nên tạo các bài viết có cấu trúc chuẩn các thẻ heading (h1 - h6)
1. Thêm các hàm hỗ trợ
Thêm đoạn sau vào cuối file: modules/news/global.functions.php
function mb_find_replace(&$find = false, &$replace = false, &$string = '')
{
if (is_array($find) && is_array($replace) && $string) {
// check if multibyte strings are supported
if (function_exists('mb_strpos')) {
for ($i = 0; $i < count($find); $i++) {
$string =
mb_substr($string, 0, mb_strpos($string, $find[$i])) . // everything befor $find
$replace[$i] . // its replacement
mb_substr($string, mb_strpos($string, $find[$i]) + mb_strlen($find[$i])) // everything after $find
;
}
} else {
for ($i = 0; $i < count($find); $i++) {
$string = substr_replace(
$string,
$replace[$i],
strpos($string, $find[$i]),
strlen($find[$i])
);
}
}
}
return $string;
}
function url_anchor_target($title, &$array_anchors = [])
{
$title = strip_tags($title);
$title = str_replace(array("\r", "\n", "\n\r", "\r\n"), ' ', $title);
$title = str_replace('&', '', $title);
// remove non alphanumeric chars
$title = preg_replace('/[^a-zA-Z0-9 \-_]*/', '', $title);
// convert spaces to _
$title = str_replace(
array(' ', ' ', 'nbsp'),
'_',
$title
);
// remove trailing - and _
$title = rtrim($title, '-_');
$title = strtolower($title);
$title = str_replace('_', '-', $title);
$title = str_replace('--', '-', $title);
if (array_key_exists($title, $array_anchors)) {
$array_anchors[$title]++;
$title .= '-' . $array_anchors[$title];
} else
$array_anchors[$title] = 1;
return $title;
}
function get_title_target($title)
{
$title = strip_tags($title);
$title = str_replace(array("\r", "\n", "\n\r", "\r\n"), ' ', $title);
$title = str_replace('&', '', $title);
return $title;
}
function build_hierarchy( &$matches )
{
$current_depth = 100; // headings can't be larger than h6 but 100 as a default to be sure
$html = '';
$numbered_items = array();
$numbered_items_min = null;
// reset the internal collision collection
$array_anchors = array();
// find the minimum heading to establish our baseline
for ($i = 0; $i < count($matches); $i++) {
if ( $current_depth > $matches[$i][2] )
$current_depth = (int)$matches[$i][2];
}
$numbered_items[$current_depth] = 0;
$numbered_items_min = $current_depth;
for ($i = 0; $i < count($matches); $i++) {
if ( $current_depth == (int)$matches[$i][2] )
$html .= '<li>';
// start lists
if ( $current_depth != (int)$matches[$i][2] ) {
for ($current_depth; $current_depth < (int)$matches[$i][2]; $current_depth++) {
$numbered_items[$current_depth + 1] = 0;
$html .= '<ul><li>';
}
}
// list item
$html .= '<a href="#' . url_anchor_target( $matches[$i][0] ) . '">';
$html .= strip_tags($matches[$i][0]) . '</a>';
// end lists
if ( $i != count($matches) - 1 ) {
if ( $current_depth > (int)$matches[$i + 1][2] ) {
for ($current_depth; $current_depth > (int)$matches[$i + 1][2]; $current_depth--) {
$html .= '</li></ul>';
$numbered_items[$current_depth] = 0;
}
}
if ( $current_depth == (int)@$matches[$i + 1][2] )
$html .= '</li>';
}
else {
// this is the last item, make sure we close off all tags
for ($current_depth; $current_depth >= $numbered_items_min; $current_depth--) {
$html .= '</li>';
if ( $current_depth != $numbered_items_min ) $html .= '</ul>';
}
}
}
return $html;
}
2. Thêm đoạn xử lý detail
Mở file: modules/news/funcs/detail.phpTìm đoạn (Line: 228)
$news_contents['newscheckss'] = md5($news_contents['id'] . NV_CHECK_SESSION);
Thêm vào bên dưới đoạn sauif (preg_match_all('/(<h([1-6]{1})[^>]*>).*<\/h\2>/msuU', $news_contents['bodyhtml'], $matches, PREG_SET_ORDER)) {
$array_anchors = [];
for ($i = 0; $i < count($matches); $i++) {
$anchor = url_anchor_target($matches[$i][0], $array_anchors);
$find[] = $matches[$i][0];
$replace[] = str_replace(
array(
$matches[$i][1], // start of heading
'</h' . $matches[$i][2] . '>' // end of heading
),
array(
$matches[$i][1] . '<span id="' . $anchor . '">',
'</span></h' . $matches[$i][2] . '>'
),
$matches[$i][0]
);
}
}
$news_contents['bodyhtml'] = mb_find_replace($find, $replace, $news_contents['bodyhtml']);
$news_contents['toc'] = build_hierarchy($matches);
3. Hiển thị lên giao diện
Mở file: themes/<default>/modules/news/detail.tpl- Tìm đoạn nội dung chi tiết bài viết, thường là:
<div id="news-bodyhtml" class="bodytext margin-bottom-lg">
{DETAIL.bodyhtml}
</div>
- Thêm vào phía bên trên phần chi tiết đó code sau đây:
<!-- BEGIN: show_toc -->
<div id="toc_container">
<p class="toc_title">Mục lục</p>
<ul class="toc_list">
{DETAIL.toc}
</ul>
</div>
<!-- END: show_toc -->
Thêm vào chỗ trước khi đóng <!-- END: main --> đoạn code script sau:
<script>
$(document).ready(function() {
if ($("#toc_container .toc_list").length > 0) {
var height = ($('#toc_container .toc_list').height());
if (height > 300) {
$("#toc_container .toc_list").css("height", 300 + "px");
$("#toc_container .toc_list").parent().append('<div class="vuta_toc_readmore"><a title="Mở rộng" href="javascript:void(0);">Mở rộng</a></div>');
$("body").on("click", ".vuta_toc_readmore", function() {
$("#toc_container .toc_list").removeAttr("style");
$("body .vuta_toc_readmore").remove();
$("#toc_container .toc_list").parent().append('<div class="vuta_toc_expand"><a title="Thu gọn" href="javascript:void(0);">Thu gọn</a></div>');
});
$("body").on("click", ".vuta_toc_expand a", function() {
$("#toc_container .toc_list").css("height", 300 + "px");
$("#toc_container .toc_list").parent().append('<div class="vuta_toc_readmore"><a title="Mở rộng" href="javascript:void(0);">Mở rộng</a></div>');
$("body .vuta_toc_expand").remove();
});
}
}
// Add smooth scrolling to all links
$("#toc_container a").on('click', function(event) {
// Make sure this.hash has a value before overriding default behavior
if (this.hash !== "") {
// Prevent default anchor click behavior
event.preventDefault();
// Store hash
var hash = this.hash;
window.location.hash = hash;
// Using jQuery's animate() method to add smooth page scroll
// The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
var to_offset = $(hash).offset().top - 100;
$('html, body').animate({
scrollTop: to_offset
}, 500);
} // End if
});
});
</script>
4. Check hiển thị
Mở file: modules/news/theme.phpTìm đoạn này (Line: 788)
if (! empty($news_contents['post_name'])) {
$xtpl->parse('main.post_name');
}
Thêm vào bên dưới:
if (!empty($news_contents['toc'])) {
$xtpl->parse('main.show_toc');
}
5. Thêm CSS
Cuối cùng thêm đoạn CSS vào style.css giao diện của bạn, hoặc tại file detail.tpl mở thêm thẻ <style> rồi dán vào:
#toc_container li,
#toc_container ul {
margin: 0;
padding: 0
}
#toc_container.no_bullets li,
#toc_container.no_bullets ul,
#toc_container.no_bullets ul li,
.toc_widget_list.no_bullets,
.toc_widget_list.no_bullets li {
background: 0 0;
list-style-type: none;
list-style: none
}
#toc_container.have_bullets li {
padding-left: 12px
}
#toc_container ul ul {
margin-left: 1.5em
}
#toc_container {
background: #dff4ff;
border-radius: 5px;
margin-bottom: 1em;
width: auto;
display: table;
position: relative;
}
#toc_container p.toc_title {
text-align: center;
font-weight: 900;
margin: 0;
padding: 10px 0 8px;
font-family: "Roboto Slab", sans-serif;
text-transform: uppercase;
background: #f4f5f8;
border-radius: 5px 5px 0 0;
}
#toc_container.toc_black p.toc_title {
color: #aaa
}
#toc_container p.toc_title+ul.toc_list {
padding: 10px 30px;
overflow: hidden;
}
#toc_container a {
text-decoration: none;
text-shadow: none;
color: #009ded;
font-size: 15px;
line-height: 15px;
}
#toc_container a:hover {
color: #007bff;
}
.vuta_toc_readmore {
text-align: center;
cursor: pointer;
position: absolute;
z-index: 9999;
bottom: 0;
width: 100%;
background: #fff;
}
.vuta_toc_readmore:before {
height: 55px;
margin-top: -45px;
content: "";
background: -moz-linear-gradient( top, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
background: -webkit-linear-gradient( top, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
background: linear-gradient( to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#ffffff00', endColorstr='#ffffff', GradientType=0);
display: block;
}
.vuta_toc_readmore a {
color: #222 !important;
display: block;
font-size: 13px !important;
font-weight: 700;
}
.vuta_toc_readmore a:after {
content: "";
width: 0;
right: 0;
border-top: 4px solid #222;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
display: inline-block;
vertical-align: middle;
margin: -2px 0 0 5px;
}
.vuta_toc_expand {
text-align: center;
}
.vuta_toc_expand a {
position: relative;
color: #222 !important;
font-size: 13px !important;
font-weight: 700;
}
.vuta_toc_expand a:after {
content: "";
width: 0;
right: 0;
border-bottom: 4px solid #222;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
display: inline-block;
vertical-align: middle;
margin: -2px 0 0 5px;
}
0 Comments