link shortcode

This commit is contained in:
Erik Winter 2025-01-18 15:24:45 +01:00
parent f47c5075fe
commit cd9bcf8413
12 changed files with 140 additions and 120 deletions

View File

@ -25,6 +25,8 @@ server {
{% end %} {% end %}
Breakdown:
- `off` means no caching headers. - `off` means no caching headers.
- `epoch` is no caching, ask the website itself. - `epoch` is no caching, ask the website itself.
- `30d` cache for 30 days. - `30d` cache for 30 days.
@ -52,7 +54,7 @@ In `/etc/nginx/nginx.conf` (on Debian).
## Sources ## Sources
- [www.digitalocean.com](https://www.digitalocean.com/community/tutorials/how-to-implement-browser-caching-with-nginx-s-header-module-on-centos-7) - {{ link(title="How to Implement Browser Caching with Nginx's header Module on CentOS7", domain="www.digitalocean.com", url="https://www.digitalocean.com/community/tutorials/how-to-implement-browser-caching-with-nginx-s-header-module-on-centos-7") }}
- [drawingablank.me](http://drawingablank.me/blog/font-mime-types-in-nginx.html) - {{ link(title="Font mime types in Nginx", domain="drawingablank.me", url="https://web.archive.org/web/20160430140318/http://drawingablank.me/blog/font-mime-types-in-nginx.html") }}

View File

@ -20,7 +20,10 @@ wmctrl -b add,demands_attention -r "Pomodoro"
This runs a terminal timer that counts down to zero from a specified amount of minutes and then lets the window manager draw attention to it in it's native way. In default i3 this means the border turns red, as well as the workspace indicator in the status bar. This runs a terminal timer that counts down to zero from a specified amount of minutes and then lets the window manager draw attention to it in it's native way. In default i3 this means the border turns red, as well as the workspace indicator in the status bar.
- `wmctrl` handles the part of drawing the attention. The trick here is to make sure that attention is drawn to the right window. This is done setting the window title of the active window, the one we run the script in, to something known beforehand. This way, we can find it back later at the end of the countdown: Breakdown of programs used:
`wmctrl` handles the part of drawing the attention. The trick here is to make sure that attention is drawn to the right window. This is done setting the window title of the active window, the one we run the script in, to something known beforehand. This way, we can find it back later at the end of the countdown:
- `-N "Pomodoro"` sets the title of the window to "Pomodoro". - `-N "Pomodoro"` sets the title of the window to "Pomodoro".
- `-r :ACTIVE:` selects the currently active window. - `-r :ACTIVE:` selects the currently active window.
@ -37,11 +40,11 @@ After the countdown, we let wmctrl draw the attention:
## Sources ## Sources
- [en.wikipedia.org](https://en.wikipedia.org/wiki/Pomodoro_Technique) - {{ link(title="Pomodoro Technique", domain="en.wikipedia.org", url="https://en.wikipedia.org/wiki/Pomodoro_Technique") }}
- [gitlab.com](https://gitlab.com/moritz-weber/i3-pomodoro) - {{ link(title="i3-pomodoro", domain="gitlab.com", url="https://gitlab.com/moritz-weber/i3-pomodoro") }}
- [github.com](https://github.com/i3/i3status) - {{ link(title="i3status", domain="github.com", url="https://github.com/i3/i3status") }}
- [github.com](https://github.com/vivien/i3blocks) - {{ link(title="i3blocks", domain="github.com", url="https://github.com/vivien/i3blocks") }}
- [linux.die.net](https://linux.die.net/man/1/wmctrl) - {{ link(title="wmctrl(1) - Linux man page", domain="linux.die.net", url="https://linux.die.net/man/1/wmctrl") }}
- [github.com](https://github.com/trehn/termdown) - {{ link(title="termdown", domain="github.com", url="https://github.com/trehn/termdown") }}
- [askubuntu.com](https://askubuntu.com/questions/40463/is-there-any-way-to-initiate-urgent-animation-of-an-icon-on-the-unity-launcher) - {{ link(title='Is there any way to initiate "urgent animation" of an icon on the Unity launcher from command line?', domain="askubuntu.com", url="https://askubuntu.com/questions/40463/is-there-any-way-to-initiate-urgent-animation-of-an-icon-on-the-unity-launcher") }}

View File

@ -5,7 +5,7 @@ date = 2020-04-17
`iota` is a helpful way to enumerate constants in Go: `iota` is a helpful way to enumerate constants in Go:
```bash {% code() %}
const ( const (
c0 = iota c0 = iota
c1 c1
@ -15,7 +15,7 @@ const (
func main() { func main() {
fmt.Println(c0, c1, c2) // "0 1 2" fmt.Println(c0, c1, c2) // "0 1 2"
} }
``` {% end %}
But it is more flexible than just defining a constant to be an integer. According to the specification: But it is more flexible than just defining a constant to be an integer. According to the specification:
@ -25,7 +25,7 @@ So we are allowed to use expressions and conversions on the right hand side of t
However, although less seen, conversions can be pretty useful as well: However, although less seen, conversions can be pretty useful as well:
```bash {% code() %}
type Priority string type Priority string
const ( const (
@ -35,7 +35,7 @@ const (
P3 // "P3" P3 // "P3"
... ...
) )
``` {% end %}
It is not possible to use `strconv` functions in the `const` block because, well, they're functions. According to the specification: It is not possible to use `strconv` functions in the `const` block because, well, they're functions. According to the specification:
@ -47,7 +47,7 @@ And that rule is:
The first 128 characters of UTF-8 are the same as the ASCII characters, prefixed with a `0`. In ASCII the characters 0 to 9 are encoded sequentially from 48 until 57, so `string(49)` converts to "1". The letters of the alphabet are encoded sequentially as well, so a range of constants representing grades from A to F could be declared in the same fashion: The first 128 characters of UTF-8 are the same as the ASCII characters, prefixed with a `0`. In ASCII the characters 0 to 9 are encoded sequentially from 48 until 57, so `string(49)` converts to "1". The letters of the alphabet are encoded sequentially as well, so a range of constants representing grades from A to F could be declared in the same fashion:
```go {% code() %}
type Grade string type Grade string
const ( const (
@ -55,14 +55,14 @@ const (
B B
C C
) )
``` {% end %}
## Sources ## Sources
- [golang.org](https://golang.org/ref/spec#Constants) - {{ link(title="The Go Programming Language Specification - Constants", domain="go.dev", url="https://go.dev/ref/spec#Constants") }}
- [golang.org](https://golang.org/doc/effective_go.html#constants) - {{ link(title="Effective Go - Constants", domain="go.dev", url="https://go.dev/doc/effective_go#constants")}}
- [golang.org](https://golang.org/ref/spec#Conversions) - {{ link(title="The Go Programming Language Specification - Conversions", domain="go.dev", url="https://go.dev/ref/spec#Conversions") }}
- [golang.org](https://golang.org/ref/spec#Conversions_to_and_from_a_string_type) - {{ link(title="The Go Programming Language Specification - Conversions to and from a string type", domain="go.dev", url="https://go.dev/ref/spec#Conversions_to_and_from_a_string_type") }}
- [en.wikipedia.org](https://en.wikipedia.org/wiki/UTF-8) - {{ link(title="UTF-8", domain="en.wikipedia.org", url="https://en.wikipedia.org/wiki/UTF-8") }}
- [en.wikipedia.org](https://en.wikipedia.org/wiki/ASCII#Printable_characters) - {{ link(title="ASCII", domain="en.wikipedia.org", url="https://en.wikipedia.org/wiki/ASCII#Printable_characters") }}

View File

@ -35,6 +35,6 @@ fg
## Sources ## Sources
- [stackoverflow.com](https://stackoverflow.com/questions/690266/why-cant-i-use-job-control-in-a-bash-script) - {{ link(title="Why can't I use job control in a bash script?", domain="stackoverflow.com", url="https://stackoverflow.com/questions/690266/why-cant-i-use-job-control-in-a-bash-script") }}
- [stackoverflow.com](https://stackoverflow.com/questions/16542372/shell-script-check-mongod-server-is-running#16546193) - {{ link(title="shell script - check mongod server is running", domain="stackoverflow.com", url="https://stackoverflow.com/questions/16542372/shell-script-check-mongod-server-is-running#16546193") }}

View File

@ -1,13 +1,15 @@
+++ +++
title = "LDAP basics for go developers" title = "LDAP basics for go developers"
date = 2020-04-05 date = 2020-04-05
[extra]
add_toc = true
+++ +++
Recently I needed to prepare a Go application to accept LDAP as a form of authentication. Knowing absolutely nothing about LDAP, I researched the topic a bit first and I thought it would be helpful to summarize my findings so far. Recently I needed to prepare a Go application to accept [LDAP](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol) as a form of authentication. Knowing absolutely nothing about LDAP, I researched the topic a bit first and I thought it would be helpful to summarize my findings so far.
## Minimal LDAP background ## Minimal LDAP background
If you are like me, then you know that LDAP is used for authentication and authorization but not much more. As it turns out, you can do a lot more with it than just store users and permissions. One can put the whole company inventory in it. In fact, I think it is best to view an LDAP server as a kind of weird database. The weird part is dat the data is not stored in tables, like in an SQL database, but in a tree. If you are like me, then you know that LDAP is used for authentication and authorization but not much more. As it turns out, you can do a lot more with it than just store users and permissions. One can catalog the whole company inventory with it. In fact, I think it is best to view an LDAP server as a kind of weird database. The weird part is dat the data is not stored in tables, like in an SQL database, but in a tree.
Like a database, you cannot go out and just fire some queries at it. You have to know the schema first. Like a database, you cannot go out and just fire some queries at it. You have to know the schema first.
@ -21,9 +23,9 @@ Every entry in the tree has at least three components:
The distinguished name is the unique name and the location of the entry in the tree. For instance: The distinguished name is the unique name and the location of the entry in the tree. For instance:
``` {% code() %}
cn=admin,dc=example,dc=org cn=admin,dc=example,dc=org
``` {% end %}
could be the DN of the administrator of the example.org company. It is a list of comma separated key-value pairs with the most specific pair (`cn=admin`) on the left and the most common one, the top of the tree, on the right (`dc=org`). could be the DN of the administrator of the example.org company. It is a list of comma separated key-value pairs with the most specific pair (`cn=admin`) on the left and the most common one, the top of the tree, on the right (`dc=org`).
@ -31,33 +33,38 @@ The `cn` and `dn` describe the type of the value. `cn` means 'common name', `dc`
The complete entry for this administrator coulde be: The complete entry for this administrator coulde be:
``` {% code() %}
dn: cn=admin,dc=example,dc=org dn: cn=admin,dc=example,dc=org
objectClass: simpleSecurityObject objectClass: simpleSecurityObject
objectClass: organizationalRole objectClass: organizationalRole
cn: admin cn: admin
description: LDAP administrator description: LDAP administrator
userPassword:: e1NTSEF9ZGdKR1g1YTBKQ2twZkZLY1J5cHB0LzYwZmMwVWNReW4= userPassword:: e1NTSEF9ZGdKR1g1YTBKQ2twZkZLY1J5cHB0LzYwZmMwVWNReW4=
``` {% end %}
## Binding ## Binding
LDAP does not really use the term 'authentication'. Instead one speaks of 'binding' a user. This binding is done to a BindDN, the distinguished name of a branch in the tree. Subsequent requests will be performed in the scope of that branch. That is, this user will only be able to 'see' the subbranches and leave nodes of this BindDN. LDAP does not really use the term 'authentication'. Instead one speaks of 'binding' a user. This binding is done to a BindDN, the distinguished name of a branch in the tree. Subsequent requests will be performed in the scope of that branch. That is, this user will only be able to 'see' the subbranches and leave nodes of this BindDN.
Trying it out on the command line
## Trying it out on the command line
LDAP servers require some work to setup. For the purposes of just testing things out, there are free online servers that kind people have set up and maintain. But a better solution is to find a good Docker image and run things on your local machine. The public servers will not let you modify data, for obvious reasons. The `osixia/openldap` worked for me: LDAP servers require some work to setup. For the purposes of just testing things out, there are free online servers that kind people have set up and maintain. But a better solution is to find a good Docker image and run things on your local machine. The public servers will not let you modify data, for obvious reasons. The `osixia/openldap` worked for me:
```bash {% code() %}
docker run -p 389:389 -p 636:636 osixia/openldap $ docker run -p 389:389 -p 636:636 osixia/openldap
``` {% end %}
Port `389` is a plain `ldap://` connection, port `636` is used for a secure `ldaps://` one. Port `389` is a plain `ldap://` connection, port `636` is used for a secure `ldaps://` one.
This image has a minimal set of data in it. Let's see what it contains by running a search: This image has a minimal set of data in it. Let's see what it contains by running a search:
```bash {% code() %}
$ ldapsearch -x -H ldap://localhost -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w admin $ ldapsearch -x -H ldap://localhost \
``` -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" \
-w admin
{% end %}
Breakdown:
- `-x` to use simple authentication and no SASL - `-x` to use simple authentication and no SASL
- `-H ldap://localhost` to point the program to the uri of our server - `-H ldap://localhost` to point the program to the uri of our server
@ -67,7 +74,7 @@ $ ldapsearch -x -H ldap://localhost -b dc=example,dc=org -D "cn=admin,dc=example
The result should be something like this: The result should be something like this:
``` {% code() %}
# extended LDIF # extended LDIF
# #
# LDAPv3 # LDAPv3
@ -98,39 +105,43 @@ result: 0 Success
# numResponses: 3 # numResponses: 3
# numEntries: 2 # numEntries: 2
``` {% end %}
We see that we have one organization and one admin user in that organization. We see that we have one organization and one admin user in that organization.
Let's try to add a user. First create a file with the specifics of this new user: Let's try to add a user. First create a file with the specifics of this new user:
``` {% code() %}
# ldapentry # ldapentry
dn: cn=john.doe,dc=example,dc=org dn: cn=john.doe,dc=example,dc=org
objectClass: person objectClass: person
cn: John Doe cn: John Doe
sn: john.doe sn: john.doe
description: just some guy description: just some guy
``` {% end %}
Then add it: Then add it:
```bas {% code() %}
$ ldapadd -x -H ldap://localhost -D "cn=admin,dc=example,dc=org" -w admin -f ldapentry $ ldapadd -x -H ldap://localhost \
-D "cn=admin,dc=example,dc=org" \
-w admin -f ldapentry
adding new entry "cn=john.doe,dc=example,dc=org" adding new entry "cn=john.doe,dc=example,dc=org"
``` {% end %}
Now we must set the password: Now we must set the password:
```bash {% code() %}
ldappasswd -s welcome123 -w admin -D "cn=admin,dc=example,dc=org" -x "cn=john.doe,dc=example,dc=org" $ ldappasswd -s welcome123 -w admin \
``` -D "cn=admin,dc=example,dc=org" \
-x "cn=john.doe,dc=example,dc=org"
{% end %}
`-s welcome123` sets the password to welcome123. `-s welcome123` sets the password to welcome123.
Check that it was added: Check that it was added:
``` {% code() %}
$ ldapsearch -x -H ldap://localhost -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w admin $ ldapsearch -x -H ldap://localhost -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w admin
# extended LDIF # extended LDIF
# #
@ -171,13 +182,15 @@ result: 0 Success
# numResponses: 4 # numResponses: 4
# numEntries: 3 # numEntries: 3
``` {% end %}
Now, let's see if we can authenticate as this new user and see ourselves: Now, let's see if we can authenticate as this new user and see ourselves:
``` {% code() %}
$ ldapsearch -x -H ldap://localhost -b "cn=john.doe,dc=example,dc=org" -D "cn=john.doe,dc=example $ ldapsearch -x -H ldap://localhost \
,dc=org" -w welcome123 -b "cn=john.doe,dc=example,dc=org" \
-D "cn=john.doe,dc=example,dc=org" \
-w welcome123
# extended LDIF # extended LDIF
# #
# LDAPv3 # LDAPv3
@ -201,7 +214,7 @@ result: 0 Success
# numResponses: 2 # numResponses: 2
# numEntries: 1 # numEntries: 1
``` {% end %}
Succes! Succes!
@ -217,7 +230,7 @@ For Go, there a package that can do the heavy lifting for us, called `go-ldap`.
The package has example code in the `README.md` that follows exactly these steps. Adjusting for the values we used above, we get: The package has example code in the `README.md` that follows exactly these steps. Adjusting for the values we used above, we get:
```go {% codedetails(summary="Click to expand") %}
# main.go # main.go
package main package main
@ -229,7 +242,6 @@ import (
) )
func main() { func main() {
username := "cn=admin,dc=example,dc=org" username := "cn=admin,dc=example,dc=org"
password := "admin" password := "admin"
@ -251,11 +263,9 @@ func main() {
fmt.Println("searching user...") fmt.Println("searching user...")
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
bindusername, bindusername, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, 0, 0, false, fmt.Sprintf("(&(objectClass=person))"),
fmt.Sprintf("(&(objectClass=person))"), []string{"dn"}, nil,
[]string{"dn"},
nil,
) )
sr, err := l.Search(searchRequest) sr, err := l.Search(searchRequest)
if err != nil { if err != nil {
@ -279,11 +289,11 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
} }
``` {% end %}
Running it: Running it:
```bash {% code() %}
$ go run main.go $ go run main.go
connect.. connect..
binding binduser.. binding binduser..
@ -291,18 +301,17 @@ searching user...
&{Entries:[0xc0001086c0] Referrals:[] Controls:[]} &{Entries:[0xc0001086c0] Referrals:[] Controls:[]}
binding user... binding user...
switching back.. switching back..
``` {% end %}
Success again! Success again!
## Sources ## Sources
- [ldap.com](https://ldap.com/basic-ldap-concepts/) - {{ link(title="Basic LDAP Concepts", domain="ldap.com", domain="ldap.com", url="https://ldap.com/basic-ldap-concepts/") }}
- [stackoverflow.com](https://stackoverflow.com/questions/18756688/what-are-cn-ou-dc-in-an-ldap-search) - {{ link(title="What are CN, OU, DC in an LDAP search?", domain="stackoverflow.com", url="https://stackoverflow.com/questions/18756688/what-are-cn-ou-dc-in-an-ldap-search") }}
- `man ldapsearch`, `man ldapadd` and `man ldappasswd` - `man ldapsearch`, `man ldapadd` and `man ldappasswd`
- [www.forumsys.com](http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/) - {{ link(title="Online LDAP Test Server", domain="www.forumsys.com", url="http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/") }}
- [serverfault.com](https://serverfault.com/questions/514870/how-do-i-authenticate-with-ldap-via-the-command-line) - {{ link(title="How do I authenticate with LDAP via the command line?", domain="serverfault.com", url="https://serverfault.com/questions/514870/how-do-i-authenticate-with-ldap-via-the-command-line") }}
- [github.com](https://github.com/osixia/docker-openldap) - {{ link(title="osixia/openldap", domain="github.com", url="https://github.com/osixia/docker-openldap") }}
- www.thegeekstuff.com - {{ link(title="Basic LDAP v3 functionality for the GO programming language", domain="pkg.go.dev", url="https://pkg.go.dev/github.com/go-ldap/ldap?tab=doc") }}
- [pkg.go.dev](https://pkg.go.dev/github.com/go-ldap/ldap?tab=doc)

View File

@ -10,6 +10,8 @@ $ reflex -r '\.go$' -- \
sh -c 'clear && go test -v ./web/model --run TestEvent' sh -c 'clear && go test -v ./web/model --run TestEvent'
{% end %} {% end %}
Breakdown:
- **reflex** is a small utilty that watches for changes on the file system. - **reflex** is a small utilty that watches for changes on the file system.
- **-r** indiates that it should only watch changes in files that satisfy the following regex pattern. - **-r** indiates that it should only watch changes in files that satisfy the following regex pattern.
- **'\.go$'** the regex tells to only watch changes in go files. - **'\.go$'** the regex tells to only watch changes in go files.
@ -26,9 +28,9 @@ Remember, **reflex** is only triggered by changes on the filesystem. After enter
## Sources ## Sources
- [github.com](https://github.com/cespare/reflex) - {{ link(title="Reflex", domain="github.com", url="https://github.com/cespare/reflex") }}
- [unix.stackexchange.com](https://unix.stackexchange.com/questions/11376/what-does-double-dash-mean) - {{ link(title="What does '--' (double dash / double hyphen) mean?", domain="unix.stackexchange.com", url="https://unix.stackexchange.com/questions/11376/what-does-double-dash-mean") }}
- **man sh** - **man sh**
- [blog.alexellis.io](https://blog.alexellis.io/golang-writing-unit-tests/) - {{ link(title="Golang basics - writing unit tests", domain="blog.alexellis.io", url="https://blog.alexellis.io/golang-writing-unit-tests/") }}
- [golang.org](https://golang.org/cmd/go/#hdr-Testing_flags) - {{ link(title="go command - Testing flags", domain="pkg.go.dev", url="https://pkg.go.dev/cmd/go#hdr-Testing_flags") }}

View File

@ -3,13 +3,13 @@ title = "Shared environment variables for make, bash and docker"
date = 2020-01-07 date = 2020-01-07
+++ +++
It is possible to define a set of variables and share them in Make, Bash and the Docker containers that are orchestrated by `docker-compose`. It is possible to define a set of variables and share them in `make`, `bash` and the Docker containers that are orchestrated by `docker-compose`.
Docker-compose can use an `.env` file to substitute variables in a `docker-compose.yml` file in the same directory. In this docker-compose.yml they can be exported to the containers. Docker-compose can use an `.env` file to substitute variables in a `docker-compose.yml` file in the same directory. In this docker-compose.yml they can be exported to the containers.
Incuding this `.env` file in your `Makefile` makes hem available there as well, but they are not automatically exported to the Bash shells that are spawned by `make` to execute the targets. This can be changed by adding the `.EXPORTALLVARIABLES:` target to your `Makefile`. Incuding this `.env` file in your `Makefile` makes hem available there as well, but they are not automatically exported to the `bash` shells that are spawned by `make` to execute the targets. This can be changed by adding the `.EXPORTALLVARIABLES:` target to your `Makefile`.
`.env`: An example that covers all areas is this `.env` file:
{% code() %} {% code() %}
VAR1=this VAR1=this
@ -17,7 +17,7 @@ VAR2=that
VAR3=those VAR3=those
{% end %} {% end %}
`Makefile`: With a `Makefile`:
{% code() %} {% code() %}
include .env include .env
@ -30,7 +30,7 @@ task:
@docker-compose up @docker-compose up
{% end %} {% end %}
`docker-compose.yml`: Together with this `docker-compose.yml`:
{% code() %} {% code() %}
... ...
@ -42,6 +42,6 @@ app:
## Sources ## Sources
- [vsupalov.com](https://vsupalov.com/docker-arg-env-variable-guide/#the-dot-env-file-env) - {{ link(title="Docker ARG, ENV and .env - a Complete Guide", domain="vsupalov.com", url="https://vsupalov.com/docker-arg-env-variable-guide/#the-dot-env-file-env") }}
- [www.gnu.org](https://www.gnu.org/software/make/manual/html_node/Special-Targets.html#Special-Targets) - {{ link(title="4.9 Special Built-in Target Names", domain="www.gnu.org", url="https://www.gnu.org/software/make/manual/html_node/Special-Targets.html#Special-Targets") }}

View File

@ -6,7 +6,7 @@ title = "About"
My name is Erik, and I am a Dutch software developer. Go is my language of choice, as one can easily gather from the posts on this site. At work I like to program in Go, outside of work I like to program in Go. My name is Erik, and I am a Dutch software developer. Go is my language of choice, as one can easily gather from the posts on this site. At work I like to program in Go, outside of work I like to program in Go.
When I am not programming... I dream of programming in Go. Nah, just kidding, I mostly do the same things as everyone else. Listen to music, watch movies, etc. I also have written some short stories and other fiction. If you can understand Dutch, you can read them at [vrijkorteverhalen.nl](https://vrijkorteverhalen.nl) 🇳🇱. When I am not programming... I dream of programming in Go. Nah, just kidding, I mostly do the same things as everyone else. Listen to music, watch movies, etc. I also have written some short stories and other fiction. If you understand Dutch, you can read them at [vrijkorteverhalen.nl](https://vrijkorteverhalen.nl) 🇳🇱.
## Find me elsewhere ## Find me elsewhere

View File

@ -85,8 +85,9 @@ pre code {
blockquote { blockquote {
font-style: italic; font-style: italic;
border: thin solid black; //border: thin solid black;
padding: 1rem; padding: 1rem;
margin: 1rem 0;
p { margin: 0; } p { margin: 0; }
} }

View File

@ -7,6 +7,7 @@
{% set subsections = section.subsections | sort | reverse %} {% set subsections = section.subsections | sort | reverse %}
{% for s in subsections %} {% for s in subsections %}
{% set ss = get_section(path=s) %} {% set ss = get_section(path=s) %}
{% if ss.title != "About" %}
<h2>{{ ss.title }}</h2> <h2>{{ ss.title }}</h2>
{% set ps = ss.pages | sort(attribute="date") | reverse %} {% set ps = ss.pages | sort(attribute="date") | reverse %}
<ul> <ul>
@ -18,5 +19,6 @@
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %}
{% endfor %} {% endfor %}
{% endblock content %} {% endblock content %}

View File

@ -0,0 +1 @@
<a href={{ url }}>{{ title }}</a> - <em>{{ domain }}</em>