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

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

https://chringel.dev/2022/05/automatically-deploy-a-hugo-website-to-a-remote-host-using-github-actions/

remote host + rsync

https://www.digitalocean.com/community/tutorials/how-to-use-rsync-to-sync-local-and-remote-directories

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"