Themes
To simplify version control, fork the theme first, add the forked repo as a submodue, then start a custom branch.
m10c
https://github.com/vaga/hugo-theme-m10c
To use avatar: put the photo into static/avatar.jpg
(needs +x permission)
PaperMod
For a list of configuration variables, see wiki.
The iOS font size problem is also not fixed, needs to modify the .post-content pre code
part of assets/css/common/post-single.css
. See this comment
To use Chroma code highlighting:
[params.assets]
disableHLJS = true
pygmentsUseClasses = true
[markup.highlight]
codeFences = true
guessSyntax = true
lineNos = false
style = "solarized-light"
About Page
You can add an about page by putting the corresponding markdown file into the content
folder.
cd content
vim about.md
Front Matter
You can add tags to the post in the front matter section of the post.
---
title: "My Second Post"
date: 2020-05-31T22:30:00Z
draft: false
comments: true
tags: ["Test","Blogging"]
---
noindex
in baseof.html
, add the following code to the header.
{{ if .Params.noindex }}
<meta name="robots" content="noindex, nofollow" />
{{ end }}
Then if you put noindex: true
in the front matter, the page will not be indexed by the search engine.
for PaperMod, it’s already implemented, add the following to front matter.
robotsNoIndex: true
shortcodes
Collapsed Text
Add /layouts/shortcodes/details.html
:
<details>
<summary>{{ (.Get 0) | markdownify }}</summary>
{{ .Inner | markdownify }}
</details>
Use the following:
{{< details "This is the summary" >}}
Collapsed text
{{< /details >}}
Rendition
Open links in new tab
Edit layouts/_default/_markup/render-link.html
in the main directory
<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if or (strings.HasPrefix .Destination "http") (strings.HasPrefix .Destination "https") }} target="_blank"{{ end }} >{{ .Text | safeHTML }}</a>
MathJax
vim layouts/partials/site-scripts.html
Copy the following:
{{ $script := .Site.Data.webpack_assets.app }}
{{ with $script.js }}
<script src="{{ relURL (printf " %s%s" "dist/" .) }}"></script>
{{ end }}
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.9/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({ tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]} })
</script>
If the default template does not include site-scripts.html
, you can add it manually.
For line breaks, you need to use \\\
instead of \\
.
Embed
video
The built-in youtube embedding shortcode is
#replace with the youtube video id
{{< youtube F3r-tOc3N9E >}}
external image
https://gohugo.io/content-management/shortcodes/#figure
{{< figure src="https://i.imgur.com/znjCzIE.png" width="500" caption="test img" >}}
Deployment
remote host + github action
remote host + rsync
https://gohugo.io/hosting-and-deployment/deployment-with-rsync/
Comment System
https://cloudcannon.com/jamstack-ecosystem/commenting/
I use docker for its simplicity.
install and config
#clone the repo
git clone https://github.com/posativ/isso.git
#build the docker image
cd isso
docker build . -t isso
#setup config and db folders
cd ..
mkdir -p comments_isso/config comments_isso/db
#edit the config file
vim comments_isso/config/isso.cfg
don’t forget to add comments.blog.konomama.dev
to DNS record
contents of isso.cfg
:
[general]
; database location, check permissions,
; automatically created if it does not exist
dbpath = /db/isso_comments.db
; your website or blog (not the location of Isso!)
host = https://blog.konomama.dev/
reply-notifications = true
notify = smtp
gravatar = true
gravatar-url = https://www.gravatar.com/avatar/{}?d=identicon&s=55
[admin]
; admin interface in /admin
enabled = true
password = yourpassword
[moderation]
; Enable comment moderation
enabled = true
approve-if-email-previously-approved = true
purge-after = 30d
[guard]
; basic spam protection
enabled = true
; limit to N new comments per minute.
ratelimit = 6
; how many comments directly to the thread
direct-reply = 25
; allow commenters to reply to their own comments when they could still edit the comment.
reply-to-self = true
require-author = true
require-email = true
[smtp]
username = xxxxxx@gmail.com
password = AppSpecificPassword
host = smtp.gmail.com
port = 465
security = ssl
to = yyy@gmail.com
from = "ISSO Comment System"<xxxxxx@gmail.com>
timeout = 10
for more config options, see https://isso-comments.de/docs/reference/server-config/
put the comment section into template
vim layouts/partials/comments.html
{{ if and .Site.DisqusShortname (index .Params "comments" | default "true") (not .Site.IsServer) }}
<section class="comments">
{{ template "_internal/disqus.html" . }}
</section>
{{/* Add support for ISSO comment system */}}
{{ else if .Site.Params.isso.enabled }}
<script
data-isso="{{ .Site.Params.isso.data }}"
data-isso-id="{{ .Site.Params.isso.id}}"
data-isso-css="{{ .Site.Params.isso.css }}"
data-isso-lang="{{ .Site.Params.isso.lang }}"
data-isso-avatar="{{ .Site.Params.isso.avatar }}"
data-isso-avatar-bg="{{ .Site.Params.isso.avatarBg }}"
data-isso-reply-notifications-default-enabled="true"
src="{{ .Site.Params.isso.jsLocation }}">
</script>
<noscript>Please enable JavaScript to view the comments powered by <a href="https://posativ.org/isso/">Isso</a>.</noscript>
<div>
<section id="isso-thread"></section>
</div>
{{ end }}
For more client side config, see https://isso-comments.de/docs/reference/client-config/
Then include the partial file into the main file footer.
You can also enable or disable the comments section using the (user-defined) comments
boolean in the post front matter.
vim themes/m10c/layouts/_default/single.html
<div class="post-footer">
{{ if eq .Params.comments true }}
{{partial "comments.html" .}}
{{ end }}
</div>
smtp setup
If you are using gmail, then you need to enable 2FA for your account and set up app specific password (not your regular google account password) to use in the smtp setting.
Go to https://myaccount.google.com/security
and enter the 2FA tab. At the bottom you can set up app passwords.
setup nginx for isso
Generate nginx config for comments
sudo vim /etc/nginx/sites-enabled/isso.conf
Paste and save
server {
server_name comments.blog.konomama.dev;
location / {
proxy_pass http://localhost:8080;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Get a SSL certificate for comments.blog.konomama.dev
sudo certbot --nginx -d comments.blog.konomama.dev --email yourname@youremaildomain.tld
sudo systemctl restart nginx
Run the docker and fix SELinux problems.
#in the ~ path
sudo systemctl restart docker
docker run -d --rm --name isso -p 127.0.0.1:8080:8080 -v `pwd`/comments_isso/config:/config -v `pwd`/comments_isso/db:/db isso
#test
curl -I https://comments.blog.konomama.dev/admin/
#do not start or restart iptables after running this, always start iptables first because docker will create rules in iptables for it to work properly
#if you want auto-restart
docker run -d --name isso -p 127.0.0.1:8080:8080 -v `pwd`/comments_isso/config:/config -v `pwd`/comments_isso/db:/db --restart always isso
#fix SELinux quirks
sudo cat /var/log/audit/audit.log | grep nginx | grep denied | audit2allow -M mynginx
sudo semodule -i mynginx.pp
Run and test again.
#restart and retry
sudo systemctl restart nginx
#test: should return 200
curl -I https://comments.blog.konomama.dev/admin/
API
getting the number of comments
curl -X POST 'https://comments.blog.konomama.dev/count' -d '["/posts/pandas-are-back/"]'
instant click
http://instantclick.io/scripts
isso
the isso script needs the isso threads section to work.
If you put instantclick in the footer and hide the footer, and the comment section is not in the footer, then you need to manage the loading of isso in your html file directly.
Modify layouts/partials/comments.html
:
<script>
(function () {
const isso = window.Isso
if (isso) {
console.log("isso already exists");
window.Isso.init();
window.Isso.fetchComments();
} else {
console.log("isso not ready");
var d = document, s = d.createElement('script');
s.src = "{{ .Site.Params.isso.jsLocation }}";
s.defer = true;
// prevent the <script> from loading mutiple times by InstantClick
s.setAttribute('data-isso',"{{ .Site.Params.isso.data }}");
s.setAttribute('data-isso-id',"{{ .Site.Params.isso.id}}");
s.setAttribute('data-isso-css',"{{ .Site.Params.isso.css }}");
s.setAttribute('data-isso-lang',"{{ .Site.Params.isso.lang }}");
s.setAttribute('data-isso-avatar',"{{ .Site.Params.isso.avatar }}");
s.setAttribute('data-isso-avatar-bg',"{{ .Site.Params.isso.avatarBg }}");
s.setAttribute('data-isso-reply-notifications-default-enabled','true');
s.setAttribute('data-no-instant', '');
d.head.appendChild(s);
console.log("loaded");
}
})();
</script>
mathjax
Add data-no-instant
tag to the script and the config in layouts/partials/site-scripts.html
, then modify the code that initializes InstantClick in your template:
<script data-no-instant>
InstantClick.on('change', function(){
//mathjax
if (window.MathJax){
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);}
});
InstantClick.init();
</script>
statecounter
Put the following code into layouts/partials/site-scripts.html
<script>
(function () {
const sc = window._statcounter
if (sc) {
console.log("sc already exists");
sc.record_pageview();
} else {
console.log("sc not ready");
window.sc_project=11111111;
window.sc_invisible=1;
window.sc_security="aaaaaaaa";
var d = document, s = d.createElement('script');
s.src = "https://www.statcounter.com/counter/counter.js";
s.async = true;
// prevent the <script> from loading mutiple times by InstantClick
s.setAttribute('data-no-instant', '');
d.head.appendChild(s);
console.log("sc loaded");
}
})();
</script>
mermaid
Edit layouts/_default/_markup/render-codeblock-mermaid.html
, paste
<div class="mermaid">
{{- .Inner | safeHTML }}
</div>
{{ .Page.Store.Set "hasMermaid" true }}
This way, the code block marked as mermaid will be rendered as a div section of mermaid class.
Put the following code into layouts/partials/site-scripts.html
. do not bother with the idea of conditionally loading the script using .Page.Store.Get
, it does not work with InstantClick.
<script>
var config = {
startOnLoad: true,
theme:'{{ if site.Params.mermaid.theme }}{{ site.Params.mermaid.theme }}{{ else }}forest{{ end }}',
align:'{{ if site.Params.mermaid.align }}{{ site.Params.mermaid.align }}{{ else }}center{{ end }}',
};
(function () {
const mm = window.mermaid;
if (mm) {
console.log("mermaid already exists");
//mermaid.initialize(config);
mermaid.run({querySelector: '.mermaid',});
} else {
console.log("mermaid not ready");
var d = document, s = d.createElement('script');
s.src = "https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js";
s.defer = true;
// prevent the <script> from loading mutiple times by InstantClick
s.setAttribute('data-no-instant', '');
window.onload = () => {
window.mermaid.initialize(config);
}
d.head.appendChild(s);
console.log("mermaid loaded");
}
})();
</script>
Sample hugo.toml
baseURL = 'https://blog.konomama.dev'
languageCode = 'en-us'
title = 'このままでいいから'
theme = 'PaperMod'
paginate = 8
[[menu.main]]
name = "Blog"
weight = 1
url = "/posts"
[[menu.main]]
name = "About"
weight = 2
url = "/about"
[[menu.main]]
identifier = "tags"
name = "Tags"
url = "/tags"
weight = 3
[params]
author = "konomama"
description = "哀しみとよろこびくり返して"
menu_item_separator = " - "
disableSpecial1stPost = true
disableAnchoredHeadings = true
hideSummary = true
#showtoc = true
tocopen = true
TocSide = "left"
ShowPostNavLinks = true
ShowBreadCrumbs = true
hideFooter = true
disableThemeToggle = true
EnableInstantClick = true
dateFormat = "2006-01-02"
[params.isso]
enabled = true
data = "https://comments.blog.konomama.dev/"
id = "thread-id"
css = true
lang = "en"
avatar = true
avatar-bg = "#f0f0f0"
jsLocation = "https://comments.blog.konomama.dev/js/embed.min.js"