Hexo Icarus 関連する記事を表示したい

Hexo Icarus 関連する記事を表示したい

hexo-related-popular-posts

ブログによくある「この記事に関連する記事」みたいなやつを出したい。
このブログはHexoにより生成されており、Hexoには良い感じのプラグインが多数存在する。

ここでhexo-related-popular-postsというHexo用の素晴らしいプラグインを使うことで関連記事を生成する。

hexo-related-popular-postsのGitHubリポジトリで実験する。

公式ドキュメント通りにやればおおよそうまくいくが環境の問題か一部詰まった所がある。

  • 環境
パッケージバージョン
hexo4.2.1
node12.14.1
icarus2.6.0

実装方法を以下に記す。

1. インストール

下記のコマンドでhexo-related-popular-postsをインストール。

1
npm install hexo-related-popular-posts@4.0.0. --save

インストール後、hexo sでプレビュー表示しても特にエラーが起きなければOK。

1.1. 失敗例

以下のようにバージョン指定せずにインストールしたら、

1
npm install hexo-related-popular-posts --save

hexo sでプレビュー表示したときに下記のエラーが起きた。

1
TypeError: hexo.locals.cache.cache.get is not a function

その場合は以下のようにアンインストールしてからやり直す。

1
npm uninstall hexo-related-popular-posts --save

2. シンプルな実装

icarus\layout\widgetrelated_posts.ejsという名前のファイルを作る。
中身は以下のようにする。

公式サンプルではpostだがIcarusではpageにする必要がある。

related_posts.ejs

1
2
3
4
5
6
7
8
9
10
<div class="card widget">
<div class="card-content">
<h3 class="menu-label">
<%= __('widget.related_posts') %>
</h3>
<%-
popular_posts( { maxCount: 5 , ulClass: 'popular-posts' , PPMixingRate: 0.0 , isDate: false , isImage: true , isExcerpt: false} , page )
%>
</div>
</div>

icarus\_config.ymlにWidgetの設定を追加。
widgets:のところに以下のように追記する。

1
2
3
4
5
-
# Widget name
type: related_posts
# Where should the widget be placed, left or right
position: left

hexo sでプレビュー表示すると関連記事が表示されている。
トップページには関連記事が出ないので適当な記事を見る。



リンクは正しく表示されているがサムネイル画像が表示されていない記事がある。
表示されているサムネイル画像も記事に設定されているものとは違う。

これを修正する。

3. サムネイル画像の取得

hexo-related-popular-postsのサムネイル取得部分を修正する。

node_modules\hexo-related-popular-posts\lib\collector.jsのコメントで

1
// get eyecatch image

と書いてあるあたりを以下のように変更。

変更前

1
2
3
// get eyecatch image
if (post.eyeCatchImage || post.postImage || (post.ampSettings && post.ampSettings.titleImage.path) ) {
eyeCatchImage = post.eyeCatchImage || post.postImage || ( (post.ampSettings && post.ampSettings.titleImage.path) ? post.ampSettings.titleImage.path : null )

変更後

1
2
3
// get eyecatch image // 修正 "post.thumbnail || " 追加 
if (post.thumbnail || post.eyeCatchImage || post.postImage || (post.ampSettings && post.ampSettings.titleImage.path) ) {
eyeCatchImage = post.thumbnail || post.eyeCatchImage || post.postImage || ( (post.ampSettings && post.ampSettings.titleImage.path) ? post.ampSettings.titleImage.path : null )

保存した後、一度hexo cleanでキャッシュを消去。
再度hexo sでプレビュー表示すると関連記事のサムネイル画像が想定通りに変わっている。



4. フォーマットの修正

このブログで使われているHexoテーマのIcarusには最新の記事を表示する機能がある。



これは先のhexo-related-popular-postsにより作られた関連する記事とフォーマットが異なる。
なのでhexo-related-popular-postsのフォーマットを修正することで統一する。

icarus\scriptsrelatedPostsHelper.jsという名前のファイルを作る。

relatedPostsHelper.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// hexo-related-popular-posts のフォーマットを変換する
hexo.extend.helper.register('relatedPostsConvert', function(args){
// 中身が無かったら空白を返す
if(!args) return "";

// cheerio で 画像path,記事path,記事タイトルのリストを取得
const cheerio = require('cheerio');
const $ = cheerio.load(args);
const imgsrclist = $('div[class="popular-posts-img"]').find('img').toArray().map(element => $(element).attr('src'));
const pathlist = $('div[class="popular-posts-title"]').find('a').toArray().map(element => $(element).attr('href'));
const titlelist = $('div[class="popular-posts-title"]').find('a').toArray().map(element => $(element).attr('title'));

// 返すHTMLを作る
var returnHTML = "";
for(var i=0; i<imgsrclist.length; i++){
returnHTML += `<article class="media">`
returnHTML += `<a href="` + pathlist[i] + `" class="media-left"><p class="image is-64x64">`
returnHTML += `<img class="thumbnail" src="` + imgsrclist[i] + `" alt="` + titlelist[i] + `"></p></a>`
returnHTML += `<div class="media-content">`
returnHTML += `<a href="` + pathlist[i] + `" class="title has-link-black-ter is-size-6 has-text-weight-normal">`
returnHTML += titlelist[i] + `</a></div></article>`
}
return returnHTML;
});

これにより作られた関数relatedPostsConvertは引数として与えたHTMLを良い感じに変換する。

さっきのrelated_posts.ejsの中から呼んで使う。

related_posts.ejs

1
2
3
4
5
6
7
8
9
10
11
12
<div class="card widget">
<div class="card-content">
<h3 class="menu-label">
<%= __('widget.related_posts') %>
</h3>
<%-
  relatedPostsConvert(
  popular_posts( { maxCount: 5 , ulClass: 'popular-posts' , PPMixingRate: 0.0 , isDate: false , isImage: true , isExcerpt: false} , page )
)
%>
</div>
</div>

するとこうなる。



おおよそフォーマットの統一ができた。
多少の違いはあるが気にならない程度なので良し。

5. 関連する記事が存在しないページの対応

仕組み上、トップページなど関連する記事が存在しないページの場合「無」が生成される。



なので関連する記事がなかった場合は関連する記事を作らないようにする。

さっきのrelated_posts.ejsを以下のように修正する。

related_posts.ejs

1
2
3
4
5
6
7
8
9
10
11
12
<% var relatedPostsHTML = popular_posts( { maxCount: 5 , ulClass: 'popular-posts' , PPMixingRate: 0.0 , isDate: false , isImage: true , isExcerpt: false} , page ) %>
<% if (relatedPostsHTML == '') { %>
<% } else { %>
<div class="card widget">
<div class="card-content">
<h3 class="menu-label">
<%= __('widget.related_posts') %>
</h3>
<%- relatedPostsConvert( relatedPostsHTML ) %>
</div>
</div>
<% } %>

これによりトップページなどを開いたときに関連する記事が表示されなくなった。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×