pinning posts in hexo landscape theme

After using Hexo for more than a month, I finally figured out how to pin a post to the top. The catch is that this only works for the default Landscape Theme (Link). And also, I achieved this through trial and error, so I am not sure what I did was even right or wrong or if there is a better way.

When I do a quick google search on how to pin posts in hexo, the only results are to either install some external plugin, or modify the code as seen in this GitHub comment (Link). I decided not to go with the external plugin method, so that only left me with the approach of modifying the code. According to the above link, all I need to do is to add a front-matter to my posts and edit my template file. Ok.. So.. which file is it?

It took me several hours to figured out that the file in question is “archive.ejs” under the path “/themes/landscape(renamed)/layout/_partial/archive.ejs”. For people like me, who uses the newer version of Hexo, it seems that the default landscape theme has been merged together as part of the core Hexo package. So the “hack” I did was to redownload the landscape theme from GitHub (Link), rename it to something else, and treat it like as if I am using a different theme. (To be honest, I am not sure if that “hack” was required, or if there is a better way.)

The original archive.ejs:

1
2
3
4
5
6
7
8
9
10
11
<% if (pagination == 2){ %>
<% page.posts.each(function(post){ %>
<%- partial('article', {post: post, index: true}) %>
<% }) %>
<% } else { %>
<% var last; %>
<% page.posts.each(function(post, i){ %>
<% var year = post.date.year(); %>
.
.
.

Changes so that it will pin a post that has the custom Front-matter: pinned: true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<% if (pagination == 2){ %>
<% if (page.current == 1){ %>
<% site.posts.each(function(post){ %>
<% if (post.pinned == true){ %>
<%- partial('pinned', {post: post, index: true}) %>
<% } %>
<% }) %>
<% } %>
<% page.posts.each(function(post){ %>
<% if (page.current == 1){ %>
<% if (post.pinned == false || post.pinned == null){ %>
<%- partial('article', {post: post, index: true}) %>
<% } %>
<% } else { %>
<%- partial('article', {post: post, index: true}) %>
<% } %>
<% }) %>
<% } else { %>
<% var last; %>
<% page.posts.each(function(post, i){ %>
<% var year = post.date.year(); %>
.
.
.

Note that I only changed the top section of the if/else statement (original file: from line 2 to 4), everything after that stays the same. BTW, I have no idea what exactly does if (pagination == 2) checks for. From my trial and error, all I know is that it has to be there otherwise no post will be displayed.

These changes are coded based on a number of my personal preferences.

First, I prefer to use a custom Front-matter called pinned, and set to true in the posts that are to be pinned (As recommeded by the GitHub comment above). Adding Front-matter to posts should be pretty straightforward. (Link)

Second, it is hardcoded to only show pinned post on page 1.

Note that I used the variable site.posts.each instead of page.posts.each. Because according to the documentation on Variables (Link), page.posts.each only returns the posts that are in that page. Meaning if the pinned post is in another page due to pagination, it will not be returned as a search result.

Also note that I have used the “partial ‘pinned’” instead of the default “partial ‘article’”. It is actually just a duplicate of the partial ‘article’, the only change I did was to replace the date with the word “PINNED”.

Third, if the posts to be pinned also happen to be on page 1, it will not show up as a normal post in chronological order. However it will still shows up normally in other pages. This is to avoid showing duplicates of the same post in the same page.

Well, I guess that’s it. I somehow got it to work without actually knowing what more than half the code actually means :P