Commit 5cbe7c5b authored by phil's avatar phil

Merge branch 'master' of

parents 6c997f9a c44947be
Pipeline #5675 passed with stage
in 3 minutes and 58 seconds
function new-post() {
NAME=$(echo "$*" | sed -e "s/ /_/g")
# Snake Case, Lower Case and remove non alphanumeric characters
# (except for `.`, `-`, and `_`)
NAME=$(echo "$*" | sed -e "s/ /_/g" | sed -e 's/\(.*\)/\L\1/' | sed "s/[^[:alpha:].-_]//g")
TARGET=resources/posts/$(date -I)_$
TITLE="$*" sh ./.templates/ > $TARGET
echo "Created $TARGET"
......@@ -156,15 +156,24 @@
***** TODO The image is not optimal. It doesn't show for FB and Twitter seems to need a 1:1 crop.
*** PROJECT Feed
**** TODO The atom feed has things in random order
- This is a serious issue, because our newest blog posts _do not_ show
up in the Atom feed.
- This [[][has lately been fixed for the RSS feed in Perun]], but not for
- Even if we wanted to switch to RSS, I do not know how to make use
of Perun =master= which isn't published to clojars. I asked about
that in #perun:
**** TODO Fix categories in feed
*** PROJECT Sitemap
**** TODO [#A] fix the sitemap
**** TODO fix categories in feed
**** TODO [#C] weirdly, adding a rss-feed seems to have fixed the order of entries in the atom-feed, albeit the order in the rss-feed seems to be wrong. wtf
**** TODO Add language field to Post
- Schema: BlogPosting =inLanugage= or similar
*** TODO Sharing Improvement: If there's a picture in the post, don't share the 200ok logo
*** PROJECT Improve Technologies Page
**** TODO Some technologies in =technologies.yml= are only stubs
......@@ -180,10 +189,6 @@
*** TODO Add favicon through realfavicongenerator
*** TODO Style Opensource page and source in some logic from =project.clj=
*** DONE Eval how to do a 404 Page
- [X] Is it even possible without a back-end?
*** TODO When rendering the blog preview, properly close the missing tags
- [ ] Maybe use an HTML parser and close the tags
- [ ] Maybe use a completely different option for previews
......@@ -198,3 +203,8 @@
*** TODO on project page the technologies should not overflow the center area
*** TODO fix categories in feed
*** TODO [#C] weirdly, adding a rss-feed seems to have fixed the order of entries in the atom-feed, albeit the order in the rss-feed seems to be wrong. wtf
*** Archive
**** DONE Sharing Improvement: If there's a picture in the post, don't share the 200ok logo
**** DONE Eval how to do a 404 Page
- [X] Is it even possible without a back-end?
......@@ -753,7 +753,7 @@ footer {
color: $gray;
display: flex;
flex-flow: column nowrap;
justify-content: center;
text-align: center;
align-items: center;
padding: 2rem;
This diff is collapsed.
User-agent: *
Disallow: /debug.html
......@@ -59,7 +59,7 @@
copy {:output-dir "target/public"
;; TODO: Make this regexp more readable. It has three parts:
;; google search console + favicon stuff + other assets.
:matching #{#"(google.*\.html|safari-pinned-tab\.svg|favicon\.ico|browserconfig\.xml|manifest\.json|\.htaccess)|\.(css|js|png|jpg|svg|gif|pdf|woff|woff2)$" #".well-known"}}
:matching #{#"(google.*\.html|safari-pinned-tab\.svg|favicon\.ico|browserconfig\.xml|manifest\.json|robots\.txt|\.htaccess)|\.(css|js|png|jpg|svg|gif|pdf|woff|woff2)$" #".well-known"}}
slack {:url ""})
(defn slug-fn
......@@ -3,8 +3,11 @@ title: Hack4Glarus Hackathon in Linthal
authors: Alain M. Lafon
category: 200ok
date-created: 2017-11-18
tags: 200ok, ungleich, ungleich glarus ag,
tags: 200ok, ungleich, ungleich glarus ag
uuid: 65b2ebe34ef2b65bdcbfee845a109811
description: >-
Hack4Glarus Hackathon in Linthal.
featured-image: /img/2017-11-18/hack4glarus.jpg
Hack4Glarus( is the first hackathon of Glarus. It is your chance to meet & hack with geniuses of Switzerland. It is all about technology and fun.
title: Disable MU4E 'HTML over plain text' heuristic
authors: Alain M. Lafon
category: Emacs
date-created: 2018-10-25
tags: mu4e
uuid: 453dc43b-b160-4c77-9ce7-d0c6a3ec746a
description: >-
If you're a mu4e user and you're seeing way too many HTML emails,
consider changing a built in preset.
By default, the great Mail User Agent
[MU4E]( prefers Plain Text mails over
HTML. This configuration can be overridden via
`mu4e-view-prefer-html`, but there's probably few of us who would do
However, you might still see a whole lot of HTML emails. And when you
check if they have a plain text version, they might have one! There's
a reason for that. MU4E has a 'HTML over plain text' heuristic with
this official rationale:
Ratio between the length of the html and the plain text part below
which mu4e will consider the plain text part to be 'This messages
requires html' text bodies. You can neutralize it (always show the
text version) by using `most-positive-fixnum'.
This heuristic overwrites the default setting (and configuration) that
Plain text should be preferred over HTML!
In my experience, HTML Emails are _WAY_ longer than only 5x the Plain
text (Doodle, Airbnb, Meetup, etc), so this will yield me a lot of
false positives whereas I have never seen a "This message requires
HTML" body. Since I realized that MU4E has this heuristic, I overrode
it just like the doc string told me to and am an even happier MU4E
(setq mu4e-view-html-plaintext-ratio-heuristic most-positive-fixnum)
NB, if you want to be able to read HTML emails, that's totally and
100% supported within MU4E! You can render them as:
- Text within a regular Emacs buffer
- PDF within a regular Emacs buffer - complete with styles and all
- Open the email in a browser through a shortcut
For these and other goodies in MU4E, please have a look at my
title: Automated time tracking to letsfreckle
authors: Alain M. Lafon
category: Tooling
date-created: 2018-11-08
tags: 200ok
uuid: 6e3becb2-9ea8-41ea-8ada-fe86c2c2d985
At 200ok, we automate all of the repetitive Admin tasks like
invoicing, bookkeeping, payroll and tracking time to customer systems.
Those specific four tasks are all based on the time that we spend
working. Everyone at 200ok tracks time with their favorite tool, but
then exports it in a standardized format - a CSV file.
Today, we open sourced our tool to synchronize a time CSV sheet to
We hope it can make your life a little less repetitive and give you
more time to work on your own projects!
title: Hack4Glarus Hackathon in Linthal
authors: Alain M. Lafon
category: 200ok
date-created: 2018-11-16
tags: 200ok, ungleich, ungleich glarus ag
uuid: 9fdc1bba-80d7-4bad-9305-57db23d60aa5
description: >-
Third incarnation of the Glarner Hackathon from 2018-11-30 until 2018-12-02.
featured-image: /img/2018-11/hack4glarus-2018-winter.jpg
[Hack4Glarus]( is the first hackathon of
Glarus. Hack4Glarus is hosted by our partner company
[ungleich glarus ag]( This will be the third
incarnation and it will be on the weekend from 2018-11-30 until
We from 200ok will be there as well - as are our friends from the
[Insopor Zen Academy]( Join us for three great
days of hacking, fun and learning!
Last time we built and released our open source Crowdfunding and and Equity
funding platform [Swiss Crowdfunder](project/swiss-crowdfunder.html)
which was immediately successfully used to raise over a quarter million
Swiss franks for the [Data Center Light](
The event itself was really successful and we got a
[great article in the newspaper Südostschweiz about 200ok and ungleich](posts/200ok-in-the-media.html).
Join the event now on the [dedicated page]( or
on [ Meetup ](
## More information
**Where does Hack4Glarus take place?**
It will happen at [Spinnerei Linthal](, a very cool old factory hall at Linthal.
What will be provided at Hack4Glarus?
- food and drinks
- place to sleep
- very cool environment
What do I need to bring to Hack4Glarus?
- great ideas
- a sleeping bag
- your awesome self
And there’s an extra: Fridolinpass.
If you have an extraordinarily good idea, apply for Fridolinpass!
Write us why you should participate Hack4Glarus, and how will the
community benefit from your hacking. For the ones who win
Fridonlinpass we will cover her/his travel cost within Switzerland to
**How can I apply?**
[Apply here]( and submit your ideas!
**Attention: only limited number of seats are available. Apply now!**
title: Python’s binascii – hexlify() and unhexlify()
authors: Alain M. Lafon
category: python
date-created: 2009-12-09
tags: 200ok
uuid: e219ff9f-81b9-4728-ac2a-b94e018b1b55
Some time ago, a dear friend of mine came up to me and asked about the
Python module binascii – particularly about the methods `hexlify()`
and `unhexlify()`. Since he asked for it, I’m going to share my answer
publicly with you.
First of all, I’m defining the used nomenclature:
- ASCII characters are being written in single quotes
- decimal numbers are of the type Long with a L suffix
- hex values have a x prefix
First, let me quote the documentation:
> binascii.b2a_hex(data)
> binascii.hexlify(data)
> Return the hexadecimal representation of the binary data. Every
> byte of data is converted into the corresponding 2-digit hex
> representation. The resulting string is therefore twice as long
> as the length of data.
> binascii.a2b_hex(hexstr)
> binascii.unhexlify(hexstr)
> Return the binary data represented by the hexadecimal string
> hexstr. This function is the inverse of b2a_hex(). hexstr must
> contain an even number of hexadecimal digits (which can be upper
> or lower case), otherwise a TypeError is raised.
I’ll begin with `hexlify()`. As the documentation states, this method
splits a string which consists of hex-tuples into distinct bytes.
The ASCII character ‘A’ has 65L as numerical representation. To verify
this in Python:
You might ask “Why is this even relevant to understand binascii?”
Well, we don’t know anything about how ord() does its job. But with
binascii we can re-calculate manually and verify.
Now we know that an ‘A’ – interpreted as binary data and shown in hex
– resembles ’41’. But wait, ’41’ is a string and no hex value! That’s
no biggy, `hexlify()` represents its result as string.
To stay with the example, let’s convert 41 into a decimal number and
check if it equals 65L.
long('41', 16)
Tada! It seems that ‘A’ = 41 = 65L.
You might have known that already, but please, stay with me a minute
To make it look a little more complex:
binascii.hexlify('A') == '%x' % long('41', 16)
Be aware that `'%x' % n` converts a decimal number `n` into its hex
`binascii.unhexlify()` naturally does the same thing as `hexlify()`,
but in reverse. It takes binary data and displays it in tuples of
I’ll start off with an example:
binascii.unhexlify('%x' % ord('A'))
Here, `unhexlify()` takes the numerical representation 65L from the
ASCII character ‘A’
converts it into hex 41
'%x' % ord('A')
and represents it as a 1-tuple (meaning dimension of one) of hex
And now the conclusio – why might all of this be useful? Right now, I
can think of at least four use cases:
- cryptography
- data-transformation (i.e. Base64 for MIME/E-Mail attachments)
- security (deciphering binary readings off a network, pattern
matching, …)
- textual representation of escape sequences
Taking up the last example, I’ll show you how to visualize the Bell
escape sequence (you know, that thing that keeps beeping in your
terminal). Taken from the ASCII table, the numerical representation of
the Bell is 7. Programmers might know it better as `\a`.
ord('\a') == 7
Presuming you read such a character in some kind of binary data – for
example from a socket and you want to visualize this data with
`print`, you will not get any results – at least none visible. You
might hear the Bell sound if you’re not on a silent terminal.
Now, finally – binascii to the rescue:
Voilà, the dubious string is decrypted.
title: Making Bluetooth work on Lenovo X1 Carbon 6th gen with Linux
authors: Alain M. Lafon
category: Debian
date-created: 2018-12-17
tags: 200ok
uuid: 3f14aceb-84de-415d-8fc7-f9ef44415e76
description: >-
If your bluetooth adapter doesn't work for your brand new Lenovo X1
Carbon 6th Gen under Linux, read this article.
The Lenovo X1 Carbon 6th gen is quite a nice laptop overall. It used
to have some quirks under Linux, with regards to ACPI sleep, CPU
frequency and such. They have all been taken care of by BIOS upgrades,
already, so just upgrade if you any issues there.
There was one thing that I never got properly to work: Bluetooth. The
new 6th gen uses a different chipset than the older models which I saw
working just fine on my colleagues machines. It turns out, that the
new chipset also did work fine under old Kernels (I tested 4.9) while
on newer ones (I tested 4.16 and 4.18) it's just behaving weirdly.
Sometimes it might find devices, sometimes it might pair, sometimes it
might work for a couple of seconds, but mostly it will not work.
After some time researching the issue, because I didn't want to run a
2 year old kernel if I didn't have to, I found this discussion on
Turns out, the Intel developers know about the problem and it's
already fixed in some downstreams of the kernel. If it isn't working
for your Distro of choice, you can just change one setting in the
kernel module. Add this one line to your
options iwlwifi bt_coex_active=0
Create the file if it isn't already there (it wasn't for me on Debian
Testing). Reboot and check if the `bt_coex` option is disabled:
cat /sys/module/iwlwifi/parameters/bt_coex_active
If you're curious what the flag is about, you can read a good
explanation on [](
You're good to go now. Enjoy all the BT goodness of Audio, input
devices and so forth.
title: Livingdocs is on the shortlist for the "Best of Swiss Web Awards"
authors: "Alain M. Lafon"
category: 200ok
date-created: 2019-03-01
tags: livingdocs
uuid: 3ae1e748-42c4-4d3a-9c20-da2505f764b2
description: >-
Get on the bandwagon and sign up for the great CMS SaaS offering of
featured-image: /img/2019/03/livingdocs_swisscom-tv_whitepaper.png
Excited to see that our good friends from
[Livingdocs]( are on the [shortlist]( for the
[Best of Swiss Web Awards]( with their project for Swisscom TV
where 200ok has been involved with since the end of 2017. You can read
the whitepaper here:
Livingdocs is a growing Startup based in Zurich. Their product is a
modern Web Content Creation and Publishing System, in use at large
corporations. However, if you have any use cases for CMSs (and who
doesn't?^^): Their product is also great for smaller and bigger
publishers alike - you can [sign up to their SaaS product for free]( and try it out yourself!
title: Autoformatting source code in Emacs
authors: Alain M. Lafon
category: Emacs
date-created: 2019-03-02
tags: programming, javascript, elisp, eslint, prettier
uuid: d6511129-46d6-4f2f-a5bc-e076b78811d6
description: >-
Autoformatting source code with prettier, eslint and Emacs.
Recently at [ClojureD](,
[Josef]( introduced me to a tool which quickly
rose to fame (in the JavaScript world):
[prettier]( It is an opinionated code formatter
for various languages - this paradigm probably got popular ever since
Go started to ship such a formatter in the core tooling. I knew the
paradigm and prettier itself from blog posts, but didn't really try to
use it so far.
Generally speaking I liked the idea of having my code automatically
formatted in Emacs whilst adhering to configured linters such as
[eslint]( However, from the docs it was much too
complicated to use. More importantly, though, the documentation asks
to configure it through files which are usually checked in (like
`package.json` or `.eslintrc.json`). For new or our own projects, this
is fine. However, for customer projects, I don't want to impose new
tools. Additionally, it is a whole lot of repetitive work to set up
such tooling for every single project which kind of defeats it's
ulterior motive. In these days, who doesn't work on a couple dozen
code bases in parallel?(;
Therefore, I did what every self-respecting engineer would do in this
scenario: I wrote a little Elisp wrapper. This wrapper implements an
interactive function `autoformat` which is a thin wrapper around
command-line based code autoformatters which it utilizes through a
strategy pattern. At this moment, the tools `prettier` and
`prettier-eslint-cli` are implemented. With those, autoformatting a
wide variety of languages/formats like JS, CSS,Sass,HTML, JSON and
many more is possible. To add a new language/framework, just add a new
strategy function which yields the a command-line tool that adheres to
this workflow: Reads source code from `stdin`, formats it and passes
it to `stdout`.
You can find the documented code in my Emacs repository:
If you like it, feel free to fork the repository, make pull requests
or just give it a star^^
Happy hacking!
![Autoformat demo](/img/2019/03/demo-ok-autoformat.gif)
name: Ursula Ledergerber
title: Head of Communications
image: /img/ursula.jpg
position: 55
uuid: f361e54b-5698-442c-a339-f1356efbb531
Sales, Public Relations, Marketing.
......@@ -15,5 +15,7 @@
(defn fully-qualifying-url
"Given a `resource-path`, it appends the canonical URL for the domain"
(str "" resource-path))
(if resource-path
(if (re-find #"^http" resource-path)
(str "" resource-path))))
......@@ -147,9 +147,7 @@
[:meta {:itemprop "width"
:content "349"}]
[:meta {:itemprop "url"
:content (if (:featured-image post)
(:featured-image post)
:content (helper/logo-url)}]])
(defn render-post
"Renders a post as :article"
......@@ -199,22 +197,23 @@
;; `:last-page` isn't set. For all actually
;; rendered pages is is, though.
(if-not (= (:page page) 1)
[:a.pager {:href (:first-page page)}
(if (:prev-page page)
[:a.pager {:href (:prev-page page)}
[:span#current-page.pager (str (:page page)
" / "
(if (:next-page page)
[:a.pager {:href (:next-page page)}
(if (not (= (:page page) pages-count))
[:a.pager {:href (:last-page page)}
(if (> pages-count 0)
(if-not (= (:page page) 1)
[:a.pager {:href (:first-page page)}
(if (:prev-page page)
[:a.pager {:href (:prev-page page)}
[:span#current-page.pager (str (:page page)
" / "
(if (:next-page page)
[:a.pager {:href (:next-page page)}
(if (not (= (:page page) pages-count))
[:a.pager {:href (:last-page page)}
(defn render [{global-meta :meta posts :entries page :entry}]
(layout/blog global-meta
......@@ -37,7 +37,9 @@
[:span {:itemprop "addressLocality"} "Zürich"]]]]
[:div {:itemprop "telephone"} "+41 76 405 05 67"]
[:div {:itemprop "email"}
[:a {:href ""} ""]]]])
[:a {:href ""} ""]]
[:img {:itemprop "logo"
:src (helper/fully-qualifying-url "/img/200ok.svg")}]]])
(defn- scripts [global-meta]
[:div.scripts {:style {:display "none"}}
(:require [ok.layout :as layout]))
(:require [ok.layout :as layout]
[ok.index :as index]))
(defn render [{global-meta :meta posts :entries post :entry}]
(layout/blog global-meta
(ok.index/render-post post)]]))
(index/render-post post)]]))
......@@ -5,9 +5,9 @@
(:description meta-data))
(defn image-url [post-meta]
;; TODO: This should be optimized by sourcing an image from the
;; post if there is one.
(if-let [featured-image-url (:featured-image post-meta)]
(helper/fully-qualifying-url featured-image-url)
(defn meta-description-tag
"If the `meta-data` includes a `:description`, add the relevant <meta>
......@@ -18,12 +18,14 @@
:content (meta-description meta-data)}]))
(defn title [global-meta post-meta]
(or (str (:title post-meta) " - 200ok")
(:site-title global-meta)))
(if-let [post-title (:title post-meta)]
(str post-title " - 200ok")
(:site-title global-meta)))
(defn opengraph-meta [global-meta post-meta]
[[:meta {:property "og:title" :content (title global-meta post-meta)}]
[:meta {:property "og:type" :content "article"}]
[:meta {:property "og:description" :content (meta-description post-meta)}]
[:meta {:property "og:url" :content (helper/fully-qualifying-url (:permalink post-meta))}]
[:meta {:property "og:image" :content (image-url post-meta)}]])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment