hexo-related-popular-posts
Version:
A hexo plugin that generates a list of links to related posts and popular posts. Also , this plugin can get Visitor Counts (PageView) on posts.
403 lines (354 loc) • 17.4 kB
JavaScript
module.exports.getList = (inOptions, inPost, inHexo) => {
const moment = require('moment')
const pathFn = require('path')
const lg = require('./log.js')
const config = inHexo.config
const postData = inPost
let retrunJson = []
let relatedPosts = []
let popularPosts = []
let relatedCnt = 0
let popularCnt = 0
let postsCat = ''
let postsTag = ''
let weight_of_tag_relevancy
let weight_of_contents_relevancy
let i = 0
let k = 0
let n = 0
// load hexo@3.2's cache
// --------------------------------------------
if (!config.popularPosts.tmp.cache_path && inHexo && inHexo.locals && inHexo.locals.cache.cache && inHexo.locals.cache.cache.get('posts').length && inHexo.locals.cache.cache.get('posts').data[0].popularPost_tmp_gaData) {
let tmp_gaData = config.popularPosts.tmp.gaData
config.popularPosts.tmp.postPath = null
config.popularPosts.tmp.postPath = []
config.popularPosts.tmp.gaData = null
config.popularPosts.tmp.gaData = []
for (let i = 0; i < inHexo.locals.cache.cache.get('posts').length; i++) {
// PV update
if (config.popularPosts.tmp.isGaUpdate) {
for (k=0; k<tmp_gaData.length; k++) {
if (inHexo.locals.cache.cache.get('posts').data[i].popularPost_tmp_gaData.path == tmp_gaData[k].path) {
inHexo.locals.cache.cache.get('posts').data[i].popularPost_tmp_gaData.pv = tmp_gaData[k].pv
inHexo.locals.cache.cache.get('posts').data[i].popularPost_tmp_gaData.totalPV = tmp_gaData[k].totalPV
break
}
}
}
config.popularPosts.tmp.gaData.push(inHexo.locals.cache.cache.get('posts').data[i].popularPost_tmp_gaData)
if (inHexo.locals.cache.cache.get('posts').data[i].popularPost_tmp_postPath)config.popularPosts.tmp.postPath.push(inHexo.locals.cache.cache.get('posts').data[i].path)
}
tmp_gaData = null
}
// --------------------------------------------
lg.setConfig(config)
if (!inOptions)inOptions = {}
inOptions = Object.assign({
maxCount: 5,
ulClass: 'popular-posts',
PPMixingRate: 0.0,
isDate: false,
isImage: false,
isExcerpt: false,
PPCategoryFilter: '',
PPTagFilter: '',
}, inOptions)
if (Number(inOptions.PPMixingRate) <= 1.0 && Number(inOptions.maxCount) > 0) {
relatedCnt = Math.floor( Number(inOptions.maxCount) * ( 1 - Number(inOptions.PPMixingRate) ) )
} else {
lg.log('error', '(Option Error) Please correctly set the option value of helper tag. ', null, true)
relatedCnt = 0
return ''
}
if (inOptions.PPCategoryFilter && inOptions.PPCategoryFilter != '') {
postsCat = inOptions.PPCategoryFilter
}
if (inOptions.PPTagFilter && inOptions.PPTagFilter != '') {
postsTag = inOptions.PPTagFilter
}
weight_of_tag_relevancy = config.popularPosts.tmp.weight_of_tag_relevancy
weight_of_contents_relevancy = config.popularPosts.tmp.weight_of_contents_relevancy
// -------------------------------------------
// popular posts
// -------------------------------------------
if ( Number(inOptions.PPMixingRate) != 0 ) {
let gaData_opt = []
if (postData != undefined && postsCat == '') {
for (i = 0; i < config.popularPosts.tmp.gaData.length; i++) {
if (config.popularPosts.tmp.gaData[i].path == postData.path) {
postsCat = config.popularPosts.tmp.gaData[i].categories
break
}
}
}
// filtering items tag
if (postsTag != undefined && postsTag != '') {
for ( i = 0; i < config.popularPosts.tmp.gaData.length; i++) {
if (config.popularPosts.tmp.gaData[i].date != '') {
let p_tags = config.popularPosts.tmp.gaData[i].tags
if (p_tags && p_tags.length > 0) {
for (k = 0; k < p_tags.length; k++) {
if (p_tags[k] == postsTag) {
gaData_opt.push(config.popularPosts.tmp.gaData[i])
}
}
}
}
}
// filtering items category
} else {
for ( i = 0; i < config.popularPosts.tmp.gaData.length; i++) {
if (config.popularPosts.tmp.gaData[i].date != '') {
let cat_spl
if ( config.popularPosts.tmp.gaData[i].categories.match(/\s\>\s/) ) {
cat_spl = config.popularPosts.tmp.gaData[i].categories.split(' > ')
cat_spl = cat_spl[cat_spl.length -1]
} else {
cat_spl = config.popularPosts.tmp.gaData[i].categories
}
let postsCat_spl
if ( postsCat.match(/\s\>\s/) ) {
postsCat_spl = postsCat.split(' > ')
postsCat_spl = postsCat_spl[postsCat_spl.length -1]
} else {
postsCat_spl = postsCat
}
if (postsCat_spl == '' || cat_spl == postsCat_spl ) {
gaData_opt.push(config.popularPosts.tmp.gaData[i])
}
}
}
}
gaData_opt.sort((a, b) => {
return (b.pv - a.pv)
})
// Remove self page
if ( postData ) {
let tmpList = []
for ( i = 0; i < gaData_opt.length; i++ ) {
if ( gaData_opt[i] && gaData_opt[i].path && gaData_opt[i].path != postData.path )tmpList.push(gaData_opt[i])
}
gaData_opt = tmpList
}
popularPosts = gaData_opt
}
// // -------------------------------------------
// // (debug) popular posts
// // -------------------------------------------
// console.log('\n\n--- popularPosts ('+ popularPosts.length +') ---')
// // console.log(popularPosts)
// for(i=0; i<popularPosts.length; i++){
// console.log(' -> '+i +' : '+ popularPosts[i].title + ' (' + popularPosts[i].pv + ' pv)')
// }
if ( Number(inOptions.PPMixingRate) != 1.0 ) {
// -------------------------------------------
// Related posts (tag)
// -------------------------------------------
if (postData) {
if (postData.tags && postData.tags.length > 0) {
postData.tags.each((tag) => {
tag.posts.each((post) => {
let description = null
let eyeCatchImage = null
let post_title = null
let post_date = null
let post_path = null
for (i = 0; i < config.popularPosts.tmp.gaData.length; i++) {
if (config.popularPosts.tmp.gaData[i] && config.popularPosts.tmp.gaData[i].path == post.path) {
description = config.popularPosts.tmp.gaData[i].excerpt
eyeCatchImage = config.popularPosts.tmp.gaData[i].eyeCatchImage
post_path = config.popularPosts.tmp.gaData[i].path
post_date = config.popularPosts.tmp.gaData[i].date
post_title = config.popularPosts.tmp.gaData[i].title
break
}
}
if (post.published) {
let isExistPost = false
for ( i = 0; i < relatedPosts.length; i++) {
if (relatedPosts[i].path == post.path) {
relatedPosts[i].relatedDegree++
isExistPost = true
break
}
}
if (!isExistPost && postData.path != post.path) {
let pv = 0
if (popularPosts.length > 0) {
for ( i = 0; i < popularPosts.length; i++) {
if (popularPosts[i].path == post.path) {
pv = popularPosts[i].pv
break
}
}
}
relatedPosts.push({
'title': post_title,
'path': post_path,
'eyeCatchImage': eyeCatchImage,
'excerpt': description,
'date': post_date,
'pv': pv,
'relatedDegree': 1,
'relatedDegree_ma': 0,
'matchKeyWord': [],
})
}
}
})
})
}
} else {
lg.log('error', '(list-json.js)The theme you are using doesn\'t support \"post\" data. Please set the data manually. (e.g. <%- popular_posts( {} , post ) %> )\nFor detail , please see this url.\nhttps://github.com/tea3/hexo-related-popular-posts/issues?utf8=%E2%9C%93&q=Cannot%20read%20property%20%27tags%27%20of%20undefined', null, false)
}
// -------------------------------------------
// Related posts ( Contents morphological analysis )
// -------------------------------------------
if (config.popularPosts.morphologicalAnalysis && config.popularPosts.tmp.gaData) {
let postsGaData
for ( i = 0; i < config.popularPosts.tmp.gaData.length; i++) {
if (config.popularPosts.tmp.gaData[i].path == postData.path) {
postsGaData = config.popularPosts.tmp.gaData[i]
break
}
}
if (postsGaData) {
for ( i = 0; i < config.popularPosts.tmp.gaData.length; i++) {
let matchScore = 0
let matchKeyWord = []
if (config.popularPosts.tmp.gaData[i].path != postData.path) {
// Relevance of content's keywords
for (k = 0; k < postsGaData.keywords.length; k++) {
for (n = 0; n < config.popularPosts.tmp.gaData[i].keywords.length; n++) {
if (postsGaData.keywords[k].w == config.popularPosts.tmp.gaData[i].keywords[n].w && postsGaData.categories == config.popularPosts.tmp.gaData[i].categories ) {
matchScore += Number(postsGaData.keywords[k].f) / (Number(postsGaData.keywordsLength) || 500 ) + Number(config.popularPosts.tmp.gaData[i].keywords[n].f) / (Number(config.popularPosts.tmp.gaData[i].keywordsLength) || 500)
matchKeyWord.push({'w': config.popularPosts.tmp.gaData[i].keywords[n].w, 'f': config.popularPosts.tmp.gaData[i].keywords[n].f})
}
}
}
// Relevance of internal link
for (k = 0; k < postsGaData.internalLinks.length; k++) {
if (pathFn.normalize(postsGaData.internalLinks[k]) == pathFn.normalize(config.popularPosts.tmp.gaData[i].path)) {
matchScore += 5.0
}
}
}
if (matchScore > 0) {
let isAleadyCreatedList = false
for ( k = 0; k < relatedPosts.length; k++) {
if (relatedPosts[k].path == config.popularPosts.tmp.gaData[i].path) {
relatedPosts[k].relatedDegree_ma = matchScore
relatedPosts[k].matchKeyWord = matchKeyWord
isAleadyCreatedList = true
break
}
}
if ( !isAleadyCreatedList ) {
relatedPosts.push({
'title': config.popularPosts.tmp.gaData[i].title,
'path': config.popularPosts.tmp.gaData[i].path,
'eyeCatchImage': config.popularPosts.tmp.gaData[i].eyeCatchImage,
'excerpt': config.popularPosts.tmp.gaData[i].excerpt,
'date': config.popularPosts.tmp.gaData[i].date,
'pv': config.popularPosts.tmp.gaData[i].pv,
'relatedDegree': 0,
'relatedDegree_ma': matchScore,
'matchKeyWord': matchKeyWord,
})
}
}
}
}
}
// sort by relatedDegree & pv
relatedPosts.sort((a, b) => {
let degreeA = a.relatedDegree * weight_of_tag_relevancy + a.relatedDegree_ma * weight_of_contents_relevancy
let degreeB = b.relatedDegree * weight_of_tag_relevancy + b.relatedDegree_ma * weight_of_contents_relevancy
if (degreeB == degreeA) {
return (b.pv - a.pv)
} else {
return (degreeB - degreeA)
}
});
}
// -------------------------------------------
// (debug) Related posts
// -------------------------------------------
// console.log('\n\n--- relatedPosts ('+ relatedPosts.length +') ---')
// if(relatedPosts && relatedPosts.length > 0){
// for(i=0; i<relatedPosts.length; i++){
// console.log('\n-> '+i)
// console.log(' title: ' + relatedPosts[i].title)
// console.log(' path: ' + relatedPosts[i].path)
// console.log(' score: ' + relatedPosts[i].relatedDegree + ' pt & ' + relatedPosts[i].relatedDegree_ma + ' pt & ' + relatedPosts[i].pv +' pv')
// // if(relatedPosts[i].matchKeyWord)console.log(' keywords: ')
// // if(relatedPosts[i].matchKeyWord)console.log(relatedPosts[i].matchKeyWord)
// }
// }
// -------------------------------------------
// checked lists
// -------------------------------------------
let isWarning = false
for ( i = 0; i < popularPosts.length; i++ ) {
if (popularPosts[i] == null || popularPosts[i] == undefined) {
isWarning = true
break
}
}
for ( i = 0; i < relatedPosts.length; i++ ) {
if (relatedPosts[i] == null || relatedPosts[i] == undefined) {
isWarning = true
break
}
}
if (isWarning)lg.log('error', '(list-json.js)Because the post\'s path has been changed, the link can not be created successfully. Please remove the cache with the following command.\r\n$ hexo clean' + config.popularPosts.tmp.cache_path ? ('\r\n$ rm -f '+config.popularPosts.tmp.cache_path) : '', null, false)
// -------------------------------------------
// Generated posts
// -------------------------------------------
// retrunJson
let addedPath = []
let getElm = (list) => {
if ( addedPath.indexOf( list.path ) != -1 ) return null
addedPath.push( list.path )
let ret = {
'date': '',
'img': '',
'title': '',
'path': '',
'excerpt': '',
}
if (inOptions.isDate && list.date != '') {
ret.date = moment(list.date).format(config.date_format || 'YYYY-MM-DD')
}
if (inOptions.isImage && list.eyeCatchImage != '') {
ret.img = list.eyeCatchImage
}
ret.title = list.title
ret.path = pathFn.join(config.root , list.path)
if (inOptions.isExcerpt && list.excerpt != '') {
ret.excerpt = list.excerpt
}
return ret
}
// mixing related posts
for ( i = 0; i < relatedCnt; i++) {
if ( i < relatedPosts.length) {
let elm = getElm(relatedPosts[i])
if ( elm ) retrunJson.push( elm )
} else {
break
}
}
// mixing popular posts
popularCnt = Number(inOptions.maxCount) - relatedCnt
i = 0
while ( addedPath.length < Number(inOptions.maxCount) && popularCnt > 0 && i < popularPosts.length ) {
let elm = getElm( popularPosts[i] )
if ( elm ) {
retrunJson.push( elm )
popularCnt--
}
i++
}
return {'json': retrunJson, 'class': inOptions.ulClass}
}