Feuerfest

Just the private blog of a Linux sysadmin

Quo vadis Bludit? And a new blog theme

I switched to a new blog theme. While Solen was great, I didn't like the mandatory article images. It just makes no sense to search for the 20th "Code lines displayed in some kind of terminal or IDE" image for a blogpost. Also the overall look & feel was a bit too much "early 2000s". I wanted something that looked a bit more refined. More clean.

Browsing through https://themes.bludit.com/ I found the Keep It Simple-Theme. Only that it was last modified in 2020 for a 2.x Bludit version, while we are now at 3.16.2. 

Luckily only a few modifications were needed and I finally took the time to create a separate Git-Repository for my theme modifications. Have a look at if you want to use it too: https://github.com/ChrLau/keep-it-simple

Although it contains some CSS changes, but I kept the original CSS in via comments so it should be rather easy to switch back. The changes mostly affect colours, blockquotes, fonts. Not the general layout, hence incorporating updates from the original StyleShout template should be easy.

Some minor tweaks are still coming, as I still have to check mobile & widescreen support and I want a different font used in blockquotes. The current one merriweather-italic doesn't look good as the vertical alignment is too uneven for my liking. Especially the letter "e" is too high and gives every word with an "e" a somewhat strange look.

But in general I am happy with the current look & feel.

I mean, the W3 Validator still isn't completely happy, but there are some things I can't fix directly and the only option I have is to open a pull request: Bludit: Fix canoncial links in siteHead plugin. Sadly alt-tags for images are also not possible and the corresponding Issue in the Bludit repository is closed since 2022 as "this will be fixed with Bludit 4.x". Meanwhile we are still at Bludit 3.x, a 4.x branch doesn't even exist and development really slowed down and there isn't much activity from the only developer. I seriously hope these are not bad omens..

Also no activity on my Cookie security issue Enhance cookie security by setting samesite attribute and adding __Secure- prefix to sessionname (Bludit issue 1582) and at least one unpatched Stored XSS does exist: Bludit - Stored XSS Vulnerability (Bludit issue 1579).

Let's just hope the best for now...

Comments

Development of a custom Telegram notification mechanism for new Isso blog comments

Photo by Ehtiram Mammadov: https://www.pexels.com/photo/wooden-door-between-drawers-on-walls-23322329/

After integrating Isso into my Bludit blog, I am slowly starting to receive comments from my dear readers. As Bludit and Isso are separate, there is no easily visible notification of new comments that need to be approved.

As this is a fairly low traffic blog and I'm not constantly checking Isso manually, this has resulted in a comment being stuck in the moderation queue for 7 days. Only approved after the person contacted me directly to point this out.

Problem identified and the solution that immediately came to mind was the following:

  1. Write a script that will be executed every n minutes by a Systemd timer
  2. This script retrieves the number of Isso comments, pending approval, from the sqlite3 database file
  3. If the number is not zero, send a message via Telegram

This should be a feasible solution as long as currently I receive a somewhat low-amount of comments.

Database internals

Isso stores the comments in a sqlite3 database so our first task is to identify the structure (tables, columns) which store the data we need. With .table we get the tables and with .schema comments we get the SQL-Statement which was used to create the corresponding table.

However using PRAGMA table_info(comments); we get a cleaner list of all columns and the associated datatype. https://www.sqlite.org/pragma.html#toc lists all Pragmas in SQLite.

root@admin /opt/isso/db # cp comments.db testcomments.db

root@admin /opt/isso/db # sqlite3 testcomments.db
SQLite version 3.34.1 2021-01-20 14:10:07
Enter ".help" for usage hints.

sqlite> .table
comments     preferences  threads

sqlite> .schema comments
CREATE TABLE comments (     tid REFERENCES threads(id), id INTEGER PRIMARY KEY, parent INTEGER,     created FLOAT NOT NULL, modified FLOAT, mode INTEGER, remote_addr VARCHAR,     text VARCHAR, author VARCHAR, email VARCHAR, website VARCHAR,     likes INTEGER DEFAULT 0, dislikes INTEGER DEFAULT 0, voters BLOB NOT NULL,     notification INTEGER DEFAULT 0);
CREATE TRIGGER remove_stale_threads AFTER DELETE ON comments BEGIN     DELETE FROM threads WHERE id NOT IN (SELECT tid FROM comments); END;

sqlite> PRAGMA table_info(comments);
0|tid||0||0
1|id|INTEGER|0||1
2|parent|INTEGER|0||0
3|created|FLOAT|1||0
4|modified|FLOAT|0||0
5|mode|INTEGER|0||0
6|remote_addr|VARCHAR|0||0
7|text|VARCHAR|0||0
8|author|VARCHAR|0||0
9|email|VARCHAR|0||0
10|website|VARCHAR|0||0
11|likes|INTEGER|0|0|0
12|dislikes|INTEGER|0|0|0
13|voters|BLOB|1||0
14|notification|INTEGER|0|0|0

From the first look the column mode looks like what we want. To test this I created a new comment which is pending approvement.

user@host /opt/isso/db # sqlite3 testcomments.db <<< 'select * from comments where mode == 2;'
5|7||1724288099.62894||2|ip.ip.ip.ip|etertert|test||https://admin.brennt.net|0|0||0

And we got confirmation as only the new comment is listed. After approving the comment's mode changes to 1. Therefore we found a way to identify comments pending approval.

The check-isso-comments.sh script

Adding a bit of fail-safe and output we hack together the following script.

If you want to use it you need to fill in the values for TELEGRAM_CHAT_ID & TELEGRAM_BOT_TOKEN. Where TELEGRAM_CHAT_ID is the ID of the person who shall receive the messages and TELEGRAM_BOT_TOKEN takes your Bot's Token for accessing Telegrams HTTP-API.

Please always check https://github.com/ChrLau/scripts/blob/master/check-isso-comments.sh for the current version. I'm not going to update the script in this blogpost anymore.

#!/bin/bash
# vim: set tabstop=2 smarttab shiftwidth=2 softtabstop=2 expandtab foldmethod=syntax :

# Bash strict mode
#  read: http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
IFS=$'\n\t'

# Generic
VERSION="1.0"
#SOURCE="https://github.com/ChrLau/scripts/blob/master/check-isso-comments.sh"
# Values
TELEGRAM_CHAT_ID=""
TELEGRAM_BOT_TOKEN=""
ISSO_COMMENTS_DB=""
# Needed binaries
SQLITE3="$(command -v sqlite3)"
CURL="$(command -v curl)"
# Colored output
RED="\e[31m"
#GREEN="\e[32m"
ENDCOLOR="\e[0m"

# Check that variables are defined
if [ -z "$TELEGRAM_CHAT_ID" ] || [ -z "$TELEGRAM_BOT_TOKEN" ] || [ -z "$ISSO_COMMENTS_DB" ]; then
  echo "${RED}This script requires the variables TELEGRAM_CHAT_ID, TELEGRAM_BOT_TOKEN and ISSO_COMMENTS_DB to be set. Define them at the top of this script.${ENDCOLOR}"
  exit 1;
fi

# Test if sqlite3 is present and executeable
if [ ! -x "${SQLITE3}" ]; then
  echo "${RED}This script requires sqlite3 to connect to the database. Exiting.${ENDCOLOR}"
  exit 2;
fi

# Test if ssh is present and executeable
if [ ! -x "${CURL}" ]; then
  echo "${RED}This script requires curl to send the message via Telegram API. Exiting.${ENDCOLOR}"
  exit 2;
fi

# Test if the Isso comments DB file is readable
if [ ! -r "${ISSO_COMMENTS_DB}" ]; then
  echo "${RED}The ISSO sqlite3 database ${ISSO_COMMENTS_DB} is not readable. Exiting.${ENDCOLOR}"
  exit 3;
fi

COMMENT_COUNT=$(echo "select count(*) from comments where mode == 2" | sqlite3 "${ISSO_COMMENTS_DB}")

TEMPLATE=$(cat <<TEMPLATE
<strong>ISSO Comment checker</strong>

<pre>${COMMENT_COUNT} comments need approval</pre>
TEMPLATE
)

if [ "${COMMENT_COUNT}" -gt 0 ]; then

  ${CURL} --silent --output /dev/null \
    --data-urlencode "chat_id=${TELEGRAM_CHAT_ID}" \
    --data-urlencode "text=${TEMPLATE}" \
    --data-urlencode "parse_mode=HTML" \
    --data-urlencode "disable_web_page_preview=true" \
    "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage"

fi

Executing this script on the command-line will already sent a notification to me via Telegram. However I do want this to be automated hence I make use of a Systemd timer.

Systemd configuration

I use the following timer to get notified between 10 to 22 o'clock as there is no need to spam me with message when I can't do anything. I create the files as /etc/systemd/system/isso-comments.timer and /etc/systemd/system/isso-comments.service.

user@host ~ # systemctl cat isso-comments.timer
# /etc/systemd/system/isso-comments.timer
[Unit]
Description=Checks for new, unapproved Isso comments

[Timer]
# Documentation: https://www.freedesktop.org/software/systemd/man/latest/systemd.time.html#Calendar%20Events
OnCalendar=*-*-* 10..22:00:00
Unit=isso-comments.service

[Install]
WantedBy=default.target

The actual script is started by the following service unit file.

user@host ~ # systemctl cat isso-comments.service
# /etc/systemd/system/isso-comments.service
[Unit]
Description=Check for new unapproved Isso comments
After=network-online.target
Wants=network-online.target

[Service]
# Allows the execution of multiple ExecStart parameters in sequential order
Type=oneshot
# Show status "dead" after commands are executed (this is just commands being run)
RemainAfterExit=no
ExecStart=/usr/local/bin/check-isso-comments.sh

[Install]
WantedBy=default.target

After that it's the usual way of activating & starting a new Systemd unit and timer:

user@host ~ # systemctl daemon-reload

user@host ~ # systemctl enable isso-comments.service
Created symlink /etc/systemd/system/default.target.wants/isso-comments.service → /etc/systemd/system/isso-comments.service.
user@host ~ # systemctl enable isso-comments.timer
Created symlink /etc/systemd/system/default.target.wants/isso-comments.timer → /etc/systemd/system/isso-comments.timer.

user@host ~ # systemctl start isso-comments.timer
user@host ~ # systemctl status isso-comments.timer
● isso-comments.timer - Checks for new, unapproved Isso comments
     Loaded: loaded (/etc/systemd/system/isso-comments.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Mon 2024-09-09 19:33:45 CEST; 2s ago
    Trigger: Mon 2024-09-09 20:00:00 CEST; 26min left
   Triggers: ● isso-comments.service

Sep 09 19:33:45 admin systemd[1]: Started Checks for new, unapproved Isso comments.
user@host ~ # systemctl start isso-comments.service
user@host ~ # systemctl status isso-comments.service
● isso-comments.service - Check for new unapproved Isso comments
     Loaded: loaded (/etc/systemd/system/isso-comments.service; enabled; vendor preset: enabled)
     Active: inactive (dead) since Mon 2024-09-09 19:33:58 CEST; 14s ago
TriggeredBy: ● isso-comments.timer
    Process: 421812 ExecStart=/usr/local/bin/check-isso-comments.sh (code=exited, status=0/SUCCESS)
   Main PID: 421812 (code=exited, status=0/SUCCESS)
        CPU: 23ms

Sep 09 19:33:58 admin systemd[1]: Starting Check for new unapproved Isso comments...
Sep 09 19:33:58 admin systemd[1]: isso-comments.service: Succeeded.
Sep 09 19:33:58 admin systemd[1]: Finished Check for new unapproved Isso comments.

user@host ~ # systemctl list-timers
NEXT                         LEFT          LAST                         PASSED        UNIT                         ACTIVATES
Mon 2024-09-09 20:00:00 CEST 22min left    n/a                          n/a           isso-comments.timer          isso-comments.service
Tue 2024-09-10 00:00:00 CEST 4h 22min left Mon 2024-09-09 00:00:00 CEST 19h ago       logrotate.timer              logrotate.service
Tue 2024-09-10 00:00:00 CEST 4h 22min left Mon 2024-09-09 00:00:00 CEST 19h ago       man-db.timer                 man-db.service
Tue 2024-09-10 06:10:34 CEST 10h left      Mon 2024-09-09 06:06:10 CEST 13h ago       apt-daily-upgrade.timer      apt-daily-upgrade.service
Tue 2024-09-10 11:42:59 CEST 16h left      Mon 2024-09-09 19:33:17 CEST 4min 28s ago  apt-daily.timer              apt-daily.service
Tue 2024-09-10 14:22:35 CEST 18h left      Mon 2024-09-09 14:22:35 CEST 5h 15min ago  systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Sun 2024-09-15 03:10:04 CEST 5 days left   Sun 2024-09-08 03:10:27 CEST 1 day 16h ago e2scrub_all.timer            e2scrub_all.service
Mon 2024-09-16 01:21:39 CEST 6 days left   Mon 2024-09-09 00:45:54 CEST 18h ago       fstrim.timer                 fstrim.service

9 timers listed.
Pass --all to see loaded but inactive timers, too.

And now I'm getting informed in time of new comments. Yai!

Comments

Integrating Isso into Bludit via Apache2 and WSGI

Photo by Sébastien BONNEVAL: https://www.pexels.com/photo/women-posting-notes-on-brown-board-7393948/

Isso is a comment system designed to be included in static sites. As Bludit is a static site generator and has no comment system on its own this is a perfect fit. 😀 Also the comments are stored in a local SQLite DB which means all data stays locally on this server and no external account registration is required.

Comment moderation is of course possible via a small admin backend.

Introduction

I actually tried to separate methods of setting up and integrating Isso in my Bludit instance.

The start was always a Python virtualenv as I didn't want to install all packages and libraries system-wide. After that, I tried two approaches as I wasn't sure which one I liked better.

First approach:

  • Python virtualenv
  • Isso started via Systemd Unit on localhost:8080
  • ProxyPass & ProxyPassReverse directives in the Apache vHost configuration forwarding the relevant URIs to Isso on http://localhost:8080

Second approach:

  • Python virtualenv
  • Isso integrated via mod_wsgi into Apache

In the end, I went with the second approach as it spared me a Systemd Unit and activating the mod_proxy & mod_proxy_http in Apache. Also the whole integration was done with one WSGI file and 8 lines in the vHost.

But as I got both to a working condition I document them here nonetheless.

Creating the Python virtualenv

Creating the virtualenv is a necessary step for both paths. Generally, you can omit it if you want to install the Python packages system-wide, but as I'm still testing the software I didn't want this.

Here I followed the Install from PyPi documentation from Isso.

root@admin ~# mkdir /opt/isso
root@admin ~# apt-get install python3-dev python3-virtualenv sqlite3 build-essential
# Switching back to our non-root user
root@admin ~# exit
user@admin:~$ cd /opt/isso
user@admin:/opt/isso$ virtualenv --download /opt/isso
user@admin:/opt/isso$ source /opt/isso/bin/activate
(isso) user@admin:/opt/isso$ pip install isso

# After that I ran an upgrade for isso to get the latest package
(isso) user@admin:/opt/isso$ pip install --upgrade isso

# Also we can already create an directory for the SQLite DB where the comments get stored
# Note: This MUST be writeable by the user running isso. So either the user the Systemd unit runs under, or, in my case: the Apache2 user
(isso) user@admin:/opt/isso$ mkdir /opt/isso/db

# To change the ownership I need to become root again
# I choose www-data:adm to allow myself write-access as the used non-root user is part of that group
root@admin ~# chown www-data:adm/opt/isso/db
root@admin ~# chmod 775 /opt/isso/db

With pip list we can get an overview of all installed packages and their versions.

(isso) user@admin:/opt/isso$ pip list
Package       Version
------------- -------
bleach        6.1.0
cffi          1.16.0
html5lib      1.1
isso          0.13.0
itsdangerous  2.2.0
jinja2        3.1.4
MarkupSafe    2.1.5
misaka        2.1.1
pip           20.3.4
pkg-resources 0.0.0
pycparser     2.22
setuptools    44.1.1
six           1.16.0
webencodings  0.5.1
werkzeug      3.0.3
wheel         0.34.2

Isso configuration file

My isso.cfg looked like the following. The documentation is here: https://isso-comments.de/docs/reference/server-config/

(isso) user@admin:/opt/isso$ cat isso.cfg
[general]
dbpath = /opt/isso/db/comments.db
host = https://admin.brennt.net/
max-age = 15m
log-file = /var/log/isso/isso.log
[moderation]
enabled = true
approve-if-email-previously-approved = false
purge-after = 30d
[server]
listen = http://localhost:8080/
public-endpoint = https://admin.brennt.net/isso
[guard]
enabled = true
ratelimit = 2
direct-reply = 3
reply-to-self = false
require-author = true
require-email = false
[markup]
options = strikethrough, superscript, autolink, fenced-code
[hash]
salt = Eech7co8Ohloopo9Ol6baimi
algorithm = pbkdf2
[admin]
enabled = true
password = YOUR-PASSWORD-HERE

This meant I needed to create the /var/log/isso directory and set the appropriate rights.

But I want to raise your attention towards the following line public-endpoint = https://admin.brennt.net/isso here I added /isso to the host as else we will run in problems as both Isso and Bludit are using the /admin URI for backend access.

While this can be changed in Isso when you hack the code and, as far as it could tell, also in Bludit. I didn't want to do this, as these are modifications which will always cause additional work when an update of either Isso or Bludit is done. Therefore I added the /isso URI and adapted the ReverseProxy/WSGI configuration accordingly. Then everything works out of the box.

Decide how to proceed

Now you need to decide if you want to go with the first or second approach. Depending on this either start with point 1a) Creating the Systemd unit file or 2) Apache2 + WSGI configuration

1a) Creating the Systemd unit file

The following unit file will start Isso. Store it under /etc/systemd/system/isso.service.

[Unit]
Description=Isso Comment Server

[Service]
Type=simple
User=user
WorkingDirectory=/opt/isso
ExecStart=/opt/isso/bin/isso -c /opt/isso/isso.cfg
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Then activate it and start the service.

root@admin /opt/isso # vi /etc/systemd/system/isso.service
root@admin /opt/isso # systemctl daemon-reload
root@admin /opt/isso # systemctl start isso.service
root@admin /opt/isso # systemctl status isso.service
● isso.service - Isso Comment Server
     Loaded: loaded (/etc/systemd/system/isso.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2024-07-24 23:19:48 CEST; 1s ago
   Main PID: 1272753 (isso)
      Tasks: 2 (limit: 9489)
     Memory: 24.1M
        CPU: 191ms
     CGroup: /system.slice/isso.service
             └─1272753 /opt/isso/bin/python /opt/isso/bin/isso -c /opt/isso/isso.cfg

Jul 24 23:19:48 admin systemd[1]: Started Isso Comment Server.
Jul 24 23:19:48 admin isso[1272753]: 2024-07-24 23:19:48,475 INFO: Using configuration file '/opt/isso/isso.cfg'

The logfile should now also have an entry that the service is started:

root@admin /opt/isso # cat /var/log/isso/isso.log
Using database at '/opt/isso/db/comments.db'
connected to https://admin.brennt.net/

1b) Writing the Apache2 ReverseProxy configuration

This is the relevant part of the Apache ReverseProxy configuration.

Note: I used the ReverseProxy configuration before adding the /isso URI to the public-endpoint parameter in the isso.cfg. I adapted the ProxyPass and ProxyPassReverse rules accordingly, but these may not worked as displayed here and you have to troubleshoot it a bit yourself.

# Isso comments directory
<Directory /opt/isso/>
        Options -Indexes +FollowSymLinks -MultiViews
        Require all granted
</Directory>

<Proxy *>
        Require all granted
</Proxy>

ProxyPass "/isso/" "http://localhost:8080/isso/"
ProxyPassReverse "/isso/" "http://localhost:8080/isso/"
ProxyPass "/isso/isso-admin/" "http://localhost:8080/isso/admin/"
ProxyPassReverse "/isso/isso-admin/" "http://localhost:8080/isso/admin/"
ProxyPass "/isso/login" "http://localhost:8080/isso/login"
ProxyPassReverse "/isso/login" "http://localhost:8080/isso/login"

After a configtest we restart apache2.

root@admin ~# apache2ctl configtest
Syntax OK
root@admin ~# systemctl restart apache2.service

Now that this part is complete, continue with point 3) Integrating Isso into Bludit.

2) Apache2 + WSGI configuration

WSGI has the benefit that we can offer our Python application via Apache with no changes to the application and have it easily accessible via HTTP(S). As this is the first WSGI application on this server I need to install and activate mod_wsgi for Apache2 and create the isso.wsgi file which is used by mod_wsgi to load the application.

This is my changed /opt/isso/isso.wsgi file.

import os
import site
import sys

# Remember original sys.path.
prev_sys_path = list(sys.path)

# Add the new site-packages directory.
site.addsitedir("/opt/isso/lib/python3.9/site-packages")

# Reorder sys.path so new directories at the front.
new_sys_path = []
for item in list(sys.path):
    if item not in prev_sys_path:
        new_sys_path.append(item)
        sys.path.remove(item)
sys.path[:0] = new_sys_path

from isso import make_app
from isso import dist, config

application = make_app(
config.load(
    config.default_file(),
    "/opt/isso/isso.cfg"))

Installing mod_wsgi, as there is an apache2_invoke in Debian I don't need to activate the module via a2enmod wsgi.

root@admin ~# apt-get install libapache2-mod-wsgi-py3
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed:
  libapache2-mod-wsgi-py3
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 99.5 kB of archives.
After this operation, 292 kB of additional disk space will be used.
Get:1 http://deb.debian.org/debian bullseye/main amd64 libapache2-mod-wsgi-py3 amd64 4.7.1-3+deb11u1 [99.5 kB]                                                                                                       Fetched 99.5 kB in 0s (3,761 kB/s)
Selecting previously unselected package libapache2-mod-wsgi-py3.                                                                                                                                                     (Reading database ... 57542 files and directories currently installed.)
Preparing to unpack .../libapache2-mod-wsgi-py3_4.7.1-3+deb11u1_amd64.deb ...
Unpacking libapache2-mod-wsgi-py3 (4.7.1-3+deb11u1) ...
Setting up libapache2-mod-wsgi-py3 (4.7.1-3+deb11u1) ...
apache2_invoke: Enable module wsgi

Then we add the necessary Directory & WSGI directives to the vHost:

# Isso comments directory
<Directory /opt/isso/>
        Options -Indexes +FollowSymLinks -MultiViews
        Require all granted
</Directory>

# For isso comments
WSGIDaemonProcess isso user=www-data group=www-data threads=5
WSGIScriptAlias /isso /opt/isso/isso.wsgi

After an configtest we restart apache2.

root@admin ~# apache2ctl configtest
Syntax OK
root@admin ~# systemctl restart apache2.service

Now that this part is complete, continue with point 3) Integrating Isso into Bludit.

3) Integrating Isso into Bludit

Now we need to integrate Isso into Bludit in two places:

  1. The <script>-Tag containing the configuration and necessary Javascript code
  2. The <section>-Tag which will actually display the comments and comment form.

For 1. we need to find a place which is always loaded. No matter if we are on the main side or view a single article. For point 2 we must identify the part which renders single page articles and add it after the article.

These places are of course depending on the theme you use. As I use the Solen-Theme for Bludit I can use the following places:

  1. File bl-themes/solen-1.2/php/header-library.php, after line 28 add the <script>-Tag.
    • [...]
      <link rel="preload" href="https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'"><noscript><link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap" rel="stylesheet"></noscript>
      
      <script data-isso="/isso/"
              data-isso-css="true"
              data-isso-css-url="null"
              data-isso-lang="en"
              data-isso-max-comments-top="10"
              data-isso-max-comments-nested="5"
              data-isso-reveal-on-click="5"
              data-isso-sorting="newest"
              data-isso-avatar="true"
              data-isso-avatar-bg="#f0f0f0"
              data-isso-avatar-fg="#9abf88 #5698c4 #e279a3 #9163b6 ..."
              data-isso-vote="true"
              data-isso-vote-levels=""
              data-isso-page-author-hashes="f124cf6b2f01,7831fe17a8cd"
              data-isso-reply-notifications-default-enabled="false"
              src="/isso/js/embed.min.js"></script>
      
      <!-- Load Bludit Plugins: Site head -->
      <?php Theme::plugins('siteHead'); ?>
  2. File bl-themes/solen-1.2/php/page.php, line 12. This file takes care of displaying the content for single page articles. To add the comments and comment form, we need to place the <section>-Tag after the <div class="article_spacer"></div>-Tag. By doing that we have a the Tag-List and a small horizontal line as optical separator between content and comments.
    • Change:

                      </article>
                      <div class="article_spacer"></div>
                  </div>
                  <div class="section_wrapper">
    • To:
                      </article>
                      <div class="article_spacer"></div>
                      <section id="isso-thread">
                          <noscript>Javascript needs to be activated to view comments.</noscript>
                      </section>
                  </div>
                  <div class="section_wrapper">

After a reload you should see the comment form below your article content, but above the article tags.

Like in this example screenshot:

Admin backend

A picture says more than a dozen sentences, so here is a screenshot of the Admin backend with a comment awaiting moderation. Using my configuration you can access it via http://host.domain.tld/isso/admin/.

Screenshot from the Isso Admin backend

Changing the /admin URI for Isso

Issos admin backend is reachable via /admin per-default. If, for whatever reason, you can't set public-endpoint = https://host.domain.tld/prefix with a prefix of your choice, but have to use host.domain.tld, this can still be changed in the source code.

Note: The following line numbers are for version 0.13.0 and change can at anytime. Also I haven't tested this thoroughly, so go with this at your own risk.

Grep'ing for /admin we learn that this is defined in the site-packages/isso/views/comments.py (NOT site-packages/isso/db/comments.py) file.

# Finding the right comments.py
(isso) user@admin:/opt/isso$ find . -type f -wholename *views/comments.py
./lib/python3.9/site-packages/isso/views/comments.py

Now open ./lib/python3.9/site-packages/isso/views/comments.py with your texteditor of choice and navigate to line 113 and 1344.

Line 113:
Change: ('admin', ('GET', '/admin/'))
    To: ('admin', ('GET', '/isso-admin/'))
Line 1344:
Change: '/admin/',
    To: '/isso-admin/',

Of course you can change /isso-admin/ to anything you like. But keep in mind this will be overwritten once you update Isso.

Troubleshooting

AH01630: client denied by server configuration

[authz_core:error] [pid 1257781:tid 1257781] AH01630: client denied by server configuration: /opt/isso/isso.wsgi

This error was easy to fix. I simply forgot the needed Directory directive in my Apaches vHost configuration.

Adding the following block to the vHost fixed that:

# Isso comments directory
<Directory /opt/isso/>
        Options -Indexes +FollowSymLinks -MultiViews
        Require all granted
</Directory>

ModuleNotFoundError: No module named 'isso'

This one is easy to fix and was just an oversight on my end. I forgot to change the example path to the site-packages directory from my virtualenv. Hence the install isso package couldn't be located.

[wsgi:error] [pid 1258908:tid 1258908] mod_wsgi (pid=1258908): Failed to exec Python script file '/opt/isso/isso.wsgi'.
[wsgi:error] [pid 1258908:tid 1258908] mod_wsgi (pid=1258908): Exception occurred processing WSGI script '/opt/isso/isso.wsgi'.
[wsgi:error] [pid 1258908:tid 1258908] Traceback (most recent call last):
[wsgi:error] [pid 1258908:tid 1258908]   File "/opt/isso/isso.wsgi", line 3, in <module>
[wsgi:error] [pid 1258908:tid 1258908]     from isso import make_app
[wsgi:error] [pid 1258908:tid 1258908] ModuleNotFoundError: No module named 'isso'

I changed the following:

# Add the new site-packages directory.
site.addsitedir("/path/to/isso_virtualenv")

To the correct path:

# Add the new site-packages directory.
site.addsitedir("/opt/isso/lib/python3.9/site-packages")

Your path can of course be different. Just execute an find . -type d -name site-packages inside your virtualenv and you should get the correct path as result.

SyntaxError: unexpected EOF while parsing

On https://isso-comments.de/docs/reference/deployment/#mod-wsgi Isso lists some WSGI example config files to work with. However all but one have a crucial syntax error: A missing closing bracket at the end.

If you get an error like this:

[wsgi:error] [pid 1259079:tid 1259079] mod_wsgi (pid=1259079, process='', application='admin.brennt.net|/isso'): Failed to parse Python script file '/opt/isso/isso.wsgi'.
[wsgi:error] [pid 1259079:tid 1259079] mod_wsgi (pid=1259079): Exception occurred processing WSGI script '/opt/isso/isso.wsgi'.
[wsgi:error] [pid 1259079:tid 1259079]    File "/opt/isso/isso.wsgi", line 14
[wsgi:error] [pid 1259079:tid 1259079] 
[wsgi:error] [pid 1259079:tid 1259079]     ^
[wsgi:error] [pid 1259079:tid 1259079] SyntaxError: unexpected EOF while parsing

Be sure to check if every opening bracket has a closing one. Usually you just need to add one ) at the end of the last line and that's it.

SystemError: ffi_prep_closure(): bad user_data (it seems that the version of the libffi library seen at runtime is different from the 'ffi.h' file seen at compile-time)

This one baffled me at first and I had to search for an answer. Luckily StackOverflow came to the rescue: https://stackoverflow.com/a/70694565

The solution was rather easy. First install the libffi-dev package, than recompile the cffi with the command in the answer inside the virtualenv. After that the error was gone.

root@admin /opt/isso # apt-get install libffi-dev
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed:
  libffi-dev
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 56.5 kB of archives.
After this operation, 308 kB of additional disk space will be used.
Get:1 http://deb.debian.org/debian bullseye/main amd64 libffi-dev amd64 3.3-6 [56.5 kB]
Fetched 56.5 kB in 0s (2,086 kB/s)
Selecting previously unselected package libffi-dev:amd64.
(Reading database ... 57679 files and directories currently installed.)
Preparing to unpack .../libffi-dev_3.3-6_amd64.deb ...
Unpacking libffi-dev:amd64 (3.3-6) ...
Setting up libffi-dev:amd64 (3.3-6) ...
Processing triggers for man-db (2.9.4-2) ...

Then switch back to the non-root user for the virtualenv and recompile the package.

(isso) user@admin:/opt/isso$ pip install --force-reinstall --no-binary :all: cffi
Collecting cffi
  Using cached cffi-1.16.0.tar.gz (512 kB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
    Preparing wheel metadata ... done
Collecting pycparser
  Using cached pycparser-2.22.tar.gz (172 kB)
Skipping wheel build for pycparser, due to binaries being disabled for it.
Building wheels for collected packages: cffi
Building wheel for cffi (PEP 517) ... done
  Created wheel for cffi: filename=cffi-1.16.0-cp39-cp39-linux_x86_64.whl size=379496 sha256=cd91c1e57d0f3b6e5d23393c0aa436da657cd738262ab29d13ab665b2ad2a66c
Stored in directory: /home/clauf/.cache/pip/wheels/49/dd/bc/ce13506ca37e8052ad8f14cdfc175db1b3943e5d7bce7718e3
Successfully built cffi
Installing collected packages: pycparser, cffi
  Attempting uninstall: pycparser
    Found existing installation: pycparser 2.22
    Uninstalling pycparser-2.22:
      Successfully uninstalled pycparser-2.22
    Running setup.py install for pycparser ... done
  Attempting uninstall: cffi
    Found existing installation: cffi 1.16.0
    Uninstalling cffi-1.16.0:
      Successfully uninstalled cffi-1.16.0
Successfully installed cffi-1.16.0 pycparser-2.22

TypeError: load() got an unexpected keyword argument 'multiprocessing'

On https://isso-comments.de/docs/reference/deployment/#mod-wsgi Isso lists some WSGI example config files to work with. Using them brought up this error message.

To my shame I couldn't fix this one. From my understanding the syntax, etc. was correct but I couldn't find any really helpful answer. Therefore I remove that from the provided WSGI files and the error vanished.

Note: The missing closing bracket at the end has already been added here.

import os
import site
import sys

# Remember original sys.path.
prev_sys_path = list(sys.path)

# Add the new site-packages directory.
site.addsitedir("/path/to/isso_virtualenv")

# Reorder sys.path so new directories at the front.
new_sys_path = []
for item in list(sys.path):
    if item not in prev_sys_path:
        new_sys_path.append(item)
        sys.path.remove(item)
sys.path[:0] = new_sys_path

from isso import make_app
from isso import dist, config

application = make_app(
config.load(
    config.default_file(),
    "/path/to/isso.cfg"))

Other articles

Alternatives

I also found Remark42 (Official Website, GitHub) and might try that at a later time. So if Isso isn't after your liking, maybe have a look at that one.

Comments

This blog now has comments! Yai!

Photo by Teddy Yang: https://www.pexels.com/photo/people-inside-stadium-2263410/

I used Isso (Official Website, GitHub) to integrate comments into this block. This was a feature which I missed dearly as I value the input from others, but wouldn't go all the way in using a Wordpress installation or similar blogging software.

Isso integrates nicely, just two small changes to my blogs templates and done. All comments are stored in a SQLite DB on this host. This means no data sharing with services like Discourse, Google or the like. Additionally this has the benefit that no registration is required. As I hate it when I have to create yet another account to just write one or two sentences of feedback. More often than not I refrained from commenting because of this.

Comments have to pass a moderation for spam reasons though.

Comments

Things to do when updating Bludit

Photo by Markus Spiske: https://www.pexels.com/photo/green-and-yellow-printed-textile-330771/

I finally got around to update to the recent version of Bludit. And as I made two changes to files which will be overwritten, I made myself a small documentation.

Changing the default link target

Post with code example, here: https://admin.brennt.net/changing-the-default-link-target-in-bludits-tinymce

  1. Open file bludit-folder/bl-plugins/tinymce/plugin.php file
  2. Search for tinymce.init
  3. Then we add the default_link_target: '_blank' parameter at the end of the list
  4. Don't forget to add a semicolon behind the formerly last parameter

Keep the syntax highlighting

Original post: https://admin.brennt.net/bludit-and-syntax-highlighting

  1. Open: bludit-folder/bl-plugins/tinymce/tinymce/plugins/codesample/plugin.min.js
  2. Search for <pre and in the class property add line-numbers. It should now look like this: t.insertContent('<pre id="__new" class="language-'+a+' line-numbers">'+r+"</pre>")
  3. A little after that pre you will also find t.dom.setAttrib(e,"class","language-"+a), add the line-numbers class in there too, it should look like this: t.dom.setAttrib(e,"class","line-numbers language-"+a)
  4. Edit a random blogpost with code in it to verify that newly rendered pages get the line-numbers and syntax highlighting.

Enhancing Cookie security

Mozillas Observatory states that the Bludit Session Cookie is missing the samesite attribute and the Cookie name isn't prefixed with __Secure- or __Host-. I opened an issue for this on GitHub (Bludit issue #1582 Enhance cookie security by setting samesite attribute and adding __Secure- prefix to sessionname) but until this is integrated we can fix it in the following way:

  1. Open bludit-folder/bl-kernel/helpers/session.class.php
  2. Comment out the line containing: private static $sessionName = 'BLUDIT-KEY';
  3. Copy & paste the following to change the Cookie name:
    • // Set the __Secure- prefix if site is called via HTTPS, preventing overwrites from insecure origins
      //   see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#cookie_prefixes
      //private static $sessionName = 'BLUDIT-KEY';
      private static $sessionName = '__Secure-BLUDIT-KEY';
  4. Search for the function session_set_cookie_params
  5. Add the following line to add the samesite attribute to the Cookie by adding the following line. Also add a comma at the end of the formerly last line.
    • 'samesite' => 'strict'
  6. it should looks like this:
    • session_set_cookie_params([
          'lifetime' => $cookieParams["lifetime"],
          'path' => $path,
          'domain' => $cookieParams["domain"],
          'secure' => $secure,
          'httponly' => true,
          'samesite' => 'strict'
      ]);

Check is the RSS-Feed works

  1. Access https://admin.brennt.net/rss.xml and verify there is content displayed.
    • If not: Check if the RSS-Plugin works and is activated

Apply / check CSS changes

I made some small changes to the Solen Theme CSS. These must be re-done when the theme is updated.

  1. Open bl-themes/solen-1.0/css/style.css
  2. Change link color:
    • Element: .plugin a, ul li a, .footer_entry a, .judul_artikel a, line 5
    • Change: color: #DE004A;
    • To: color: #F06525;
  3. Change link color when hovering:
    • Element: .plugin a:hover, ul li a:hover, .footer_entry a:hover, .judul_artikel a:hover, line 10
    • Change: color: #F06525;
    • To: color: #C68449;
  4. Fix position of the blockquote bar:
    • Element: blockquote::box-shadow, line 24
    • Change:  box-shadow: inset 5px 0 rgb(255, 165, 0);
    • To:  box-shadow: -5px 0px 0px 0px rgb(255, 165, 0);
  5. Format code in posts:
    • Add the following element after line 37:
    • /* My custom stuff */
      code {
          font-size: 87.5%;
          color: #e83e8c;
          word-wrap: break-word;
          font-family: 'Roboto Mono', monospace;
      }
  6. Same padding-bottom as padding-top for header:
    • Element .section_title, line 136
    • Change: padding-bottom: 0px;
    • To: padding-bottom: 0.6em;
  7. Disable the white blur for the introduction texts:
    • Element: .pratinjau_artikel p:after, line 277
    • Change background: linear-gradient(to right, transparent, #ffffff 80%);
    • To: background: 0% 0%;

Solen-Theme changes

  1. Make header smaller:
    • Open solen-1.2/php/mini-hero.php
    • Remove line 4: <h2 class="hero_welcome"><?php echo $L->get('welcome'); ?></h2>
Comments

Changing the default link target in Bludits TinyMCE

Photo by Monstera: https://www.pexels.com/photo/woman-holding-book-with-blank-pages-6373293/

When creating links in TinyMCE the link-plugin is configured to open links in the current window. As this is the default setting. However I don't like this and changed it every time. And when you change something every single time, you should just make it your new default.

Luckily TinyMCE has good documentation about every plugin. So we learn about the default_link_target parameter we can utilize to achieve exactly this.

We open the bludit-folder/bl-plugins/tinymce/plugin.php file and search for tinymce.init. Then we add the default_link_target: '_blank' parameter at the end of the list. Don't forget to add a semicolon behind the formerly last parameter.

In the end it looks like this:

        tinymce.init({
                selector: "#jseditor",
                auto_focus: "jseditor",
                element_format : "html",
                entity_encoding : "raw",
                skin: "oxide",
                schema: "html5",
                statusbar: false,
                menubar:false,
                branding: false,
                browser_spellcheck: true,
                pagebreak_separator: PAGE_BREAK,
                paste_as_text: true,
                remove_script_host: false,
                convert_urls: true,
                relative_urls: false,
                valid_elements: "*[*]",
                cache_suffix: "?version=$version",
                $document_base_url
                plugins: ["$plugins"],
                toolbar1: "$toolbar1",
                toolbar2: "$toolbar2",
                language: "$lang",
                content_css: "$content_css",
                codesample_languages: [$codesampleConfig],
                default_link_target: '_blank'
        });

And now generated links will per-default open in a new window.

Comments