commit 75decb47a3c22a2e2a5514e6f97c1b170a050434 parent 2b9715eb0aac43040576d8b71f7ea90da3382266 Author: Oscar Benedito <oscar@oscarbenedito.com> Date: Mon, 7 Sep 2020 23:55:17 +0200 Changed SSG to gensite.py Diffstat:
90 files changed, 901 insertions(+), 915 deletions(-)
diff --git a/.gitignore b/.gitignore @@ -1,3 +1,2 @@ -/resources/ -/public/ -/blogroll.ompl +/_site +/misc/blogroll.ompl diff --git a/Makefile b/Makefile @@ -1,23 +1,25 @@ -DEST = /srv/oscarbenedito.com +DST = /srv/oscarbenedito.com -.PHONY: default deploy blogroll clean +.PHONY: site server blogroll deploy loc clean -default: - @echo "No default target, please choose one: deploy, blogroll, clean" - @exit 2 +site: + python3 gensite.py -deploy: clean +server: site + python3 -m http.server --bind localhost --directory _site + +blogroll: + python3 misc/update-blogroll.py + +deploy: git fetch origin master git reset --hard origin/master git verify-commit master - hugo - rm -f public/index.xml - rsync --perms --recursive --checksum --delete public/ $(DEST) - -blogroll: data/blogroll.json + python3 gensite.py + rsync --perms --recursive --checksum --delete _site/ $(DST) -data/blogroll.json: blogroll.ompl - ./create-blogroll.py blogroll.ompl > data/blogroll.json +loc: + grep -vE '^[[:space:]]*#|^[[:space:]]*$$|^[[:space:]]*"""' gensite.py | wc -l clean: - rm -rf public resources + rm -rf _site diff --git a/README.md b/README.md @@ -1,8 +1,15 @@ # Personal website -This repository is the source code for my [personal website][pw]. +This repository is the source code for my [personal website][pw]. The site is +generated by `gensite.py`, a program based on [makesite.py][ms] (which is a very +bare-bones static site generator). -## Personal notes +## Dependencies + +To generate the site all you need is Python 3 and the markdown library (install +with `pip3 install markdown`). + +## Other notes The font used for the logo is "Libre Baskerville" in bold. @@ -16,5 +23,6 @@ licensed under the [Creative Commons Attribution 4.0 International License][cc-by]. [pw]: <https://oscarbenedito.com> "Oscar Benedito's personal website" +[ms]: <https://github.com/sunainapai/makesite> "makesite by Sunaina Pai — GitHub" [agpl]: <https://www.gnu.org/licenses/agpl-3.0.html> "GNU Affero General Public License version 3" [cc-by]: <https://creativecommons.org/licenses/by/4.0/> "Creative Commons Attribution 4.0 International License" diff --git a/assets/css/landing.css b/assets/css/landing.css @@ -1,71 +0,0 @@ -/* -Copyright (C) 2020 Oscar Benedito - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <https://www.gnu.org/licenses/>. -*/ -html { - background-color: hsl(0, 0%, 94%); - color: hsl(0, 0%, 47%); - font-family: sans-serif; -} -h1 { - font-size: 3em; -} -p { - font-size: 1.25em; -} -div.imgs { - font-size: 0; -} -a.img { - font-size: 0; -} -a.icon { - font-size: 0; - margin-left: 15px; - margin-right: 15px; -} -.content { - margin: 0; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - text-align: center; -} -.icon { - fill: hsl(0, 0%, 47%); - width: 30px; -} -.icon:hover { - fill: hsl(0, 0%, 67%); -} -.big-icon { - width:100px; - height:100px; -} -.title { - font-size: 3em; - font-weight: bold; - margin-bottom: 0.25em; -} -.subtitle { - font-size: 1.5em; -} -.first { - margin-top: 0; -} -.last { - margin-bottom: 0; -} diff --git a/assets/css/style.min.css b/assets/css/style.min.css @@ -0,0 +1 @@ +:root{--gap: 1.5rem;--content-gap: 1.5rem;--main-width: 800px;--text-color: hsl(0, 0%, 12%);--text-strong: hsl(0, 0%, 10%);--secondary-text: hsl(0, 0%, 44%);--primary-bg: #7ec27b;--primary-logo: #6dba6a;--primary-active: #6dba6a;--primary-dark: #50914a;--primary-invert: #fff;--hr-color: hsl(0, 0%, 94%);--hr-dark: hsl(0, 0%, 88%);--button-color: hsl(0, 0%, 74%);--background: #fff;--code-bg: hsl(0, 0%, 96%)}@media(prefers-color-scheme:dark){:root{--text-color: hsl(0, 0%, 80%);--text-strong: hsl(0, 0%, 80%);--secondary-text: hsl(0, 0%, 55%);--primary-bg: #233951;--primary-logo: #385c81;--primary-active: #1b2c3f;--primary-dark: #4d80b3;--primary-invert: hsl(0, 0%, 80%);--hr-color: hsl(0, 0%, 20%);--hr-dark: hsl(0, 0%, 20%);--button-color: hsl(0, 0%, 70%);--background: hsl(0, 0%, 11%);--code-bg: hsl(0, 0%, 11%)}}*,*::before,*::after{box-sizing:border-box}html{scrollbar-color:var(--primary-bg)transparent}body{margin:0;color:var(--text-color);font-family:sans-serif;line-height:1.7;word-break:break-word;background:var(--background)}article,aside,div,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:0;line-height:1.2;color:var(--text-strong)}h1{margin-bottom:2px;font-size:2em}p,li{text-align:justify}strong,b{font-weight:700;color:var(--text-strong)}ul{margin:0;padding:0}a{color:var(--primary-dark);text-decoration:none}a:hover{text-decoration:underline}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:2rem}table th,table td{min-width:80px;padding:.75rem .5rem;line-height:1.5;border-bottom:1px solid var(--hr-dark)}table th{font-size:.9em;text-align:left}svg{fill:currentColor;stroke:currentColor;max-width:400px}header#page-header{border-bottom:2px solid var(--hr-color);padding:calc(1.5 * var(--gap)) var(--gap)}header#page-header .content{max-width:var(--main-width);margin-right:auto;margin-left:auto}header#page-header .content .logo{height:1.6em;fill:var(--primary-logo);stroke:none}header#page-header .content .logo:hover{fill:var(--primary-dark)}header#page-header .content nav{color:var(--secondary-text)}header#page-header .content nav a{color:var(--secondary-text)}footer#page-footer{border-top:2px solid var(--hr-color);padding:calc(1.5 * var(--gap)) var(--gap)}footer#page-footer .content{max-width:var(--main-width);margin-right:auto;margin-left:auto;text-align:center}footer#page-footer .content nav{color:var(--secondary-text)}footer#page-footer .content nav a{color:var(--secondary-text)}main{position:relative;max-width:calc(var(--main-width) + var(--gap) * 2);padding:0 var(--gap);margin:var(--gap) auto calc(var(--gap) * 2) auto}.pagination{display:flex}.button{color:var(--secondary-text);font-size:.8em;line-height:2.8em;background:var(--background);border-radius:calc(2.8em/2);padding-left:1.2em;padding-right:1.2em;border:1px solid transparent;border-color:var(--button-color);border-width:1px}.button:hover{color:var(--background);border-color:transparent;background:var(--button-color);text-decoration:none}.button.right{margin-left:auto}main.list>header{margin-bottom:var(--gap)}article.summary{padding-top:1rem;padding-bottom:1rem}article.summary h2 a{color:var(--text-color)}article.summary p{margin:0!important}article.summary header{margin-bottom:1rem}article.summary .metadata{color:var(--secondary-text)}article.summary .metadata a{color:var(--secondary-text)}article.summary:not(:first-of-type){border-top:2px solid var(--hr-color)}article header{margin-bottom:var(--content-gap)}article .metadata{color:var(--secondary-text)}article .metadata a{color:var(--secondary-text)}article .content h1{margin-top:2.5rem;margin-bottom:2rem}article .content h2{margin-top:2rem;margin-bottom:1.5rem}article .content h3{margin-top:1.5rem;margin-bottom:1rem}article .content h4,article .content h5,article .content h6{margin-top:1.5rem;margin-bottom:.5rem}article .content p,article .content :not(li)>ul,article .content ol{margin-bottom:var(--content-gap)}article .content ul,article .content ol{padding-left:1.25rem}article .content li{margin-top:.3rem}article .content li p{margin-bottom:0}article .content pre{margin-bottom:var(--gap);border-radius:4px;overflow-x:auto;margin-left:0;margin-right:0;border:1px solid var(--hr-dark);background-color:var(--code-bg)!important}article .content code{border:1px solid var(--hr-dark);background-color:var(--code-bg);padding:4px 6px;font-family:monospace;line-height:1.5;border-radius:2px}article .content pre code{display:block;margin-left:0;margin-right:0;padding:calc(var(--gap)/2);border:none;background:0 0}article .content blockquote{padding:0 0 0 1.2rem;border-left:3px solid var(--secondary-text);color:var(--secondary-text)}article .content hr{height:2px;margin-top:var(--content-gap);margin-bottom:var(--content-gap);background:var(--hr-color);border-top:0;border-bottom:0;border:0}.table-container{overflow-x:auto}.mathjax-container{overflow-x:auto;margin-top:-21.5px} diff --git a/config.toml b/config.toml @@ -1,29 +0,0 @@ -baseURL = "https://oscarbenedito.com/" -languageCode = "en-us" -title = "Oscar Benedito" -paginate = 5 -pygmentsStyle = "solarized-dark256" -disableKinds = "taxonomy" -ignoreErrors = ["error-disable-taxonomy"] - -[Author] - name = "Oscar Benedito" - email = "oscar@oscarbenedito.com" - -[params] - domain = "oscarbenedito.com" - active_years = ["2020", "2019"] - -[taxonomies] - category = "categories" - -[permalinks] - blog = "/blog/:year/:month/:slug/" - categories = "/blog/categories/:slug/" - -[frontmatter] - date = ["date", ":filename", ":default"] - lastmod = [":default", ":filename"] - -[markup.goldmark.renderer] - unsafe = true # This just allows HTML on Markdown, unsafe here just means that if everyone could edit your Markdown, someone could put malicious JS on it. diff --git a/content/_index.md b/content/_index.md @@ -1,6 +1,5 @@ ---- -title: Oscar Benedito ---- +<!-- priority: 1.0 --> +<!-- extraheader: <link rel="me" href="https://fosstodon.org/@ob"><link rel="me" href="https://github.com/oscarbenedito"><link rel="me" href="https://gitlab.com/oscarbenedito"><link rel="me" href="mailto:oscar@oscarbenedito.com"> --> Hello and welcome to my website! My name is Oscar Benedito and I am currently pursuing a double degree in Mathematics and Computer Engineering at the diff --git a/content/about.md b/content/about.md @@ -1,7 +1,4 @@ ---- -title: About this site -type: page ---- +<!-- title: About this site --> ## Privacy @@ -32,7 +29,7 @@ source code in the [JavaScript Web Labels page][jswl]. [hetzner]: <https://www.hetzner.com> "Hetzner" [gpl]: <https://www.gnu.org/licenses/gpl-3.0.html> "GNU General Public License version 3" [cc-by]: <https://creativecommons.org/licenses/by/4.0/> "Creative Commons Attribution 4.0 International License" -[gpl-local]: </licenses/gpl-3.0.txt/> "GNU General Public License version 3" -[cc-by-local]: </licenses/cc-by-4.0.txt/> "Creative Commons Attribution 4.0 International License" +[gpl-local]: </licenses/gpl-3.0.txt> "GNU General Public License version 3" +[cc-by-local]: </licenses/cc-by-4.0.txt> "Creative Commons Attribution 4.0 International License" [repo]: <https://git.oscarbenedito.com/oscarbenedito.com/> "Source code" [jswl]: </jsweblabels/> "JavaScript Web Labels" diff --git a/content/blog/2019-09-09-getting-a-domain.md b/content/blog/2019-09-09-getting-a-domain.md @@ -1,13 +1,8 @@ ---- -title: "Getting my own domain name" -slug: "getting-a-domain" -categories: [ - "Decentralization", - "Personal domain" -] -date: 2019-09-09 -lastmod: 2020-03-01 ---- +<!-- title: Getting my own domain name --> +<!-- slug: getting-a-domain --> +<!-- categories: Decentralization, Personal domain --> +<!-- date: 2019-09-09T00:00:00Z --> +<!-- lastmod: 2020-03-01T00:00:00Z --> After thinking about getting my own domain name for a while and letting the thought rest for a couple of months, I finally bought one. It is a very easy and diff --git a/content/blog/2019-09-11-joplin.md b/content/blog/2019-09-11-joplin.md @@ -1,14 +1,8 @@ ---- -title: "My note taking app: Joplin" -slug: "joplin" -categories: [ - "Decentralization", - "FOSS", - "Privacy" -] -date: 2019-09-11 -lastmod: 2019-09-24 ---- +<!-- title: My note taking app: Joplin --> +<!-- slug: joplin --> +<!-- categories: Decentralization, FOSS, Privacy --> +<!-- date: 2019-09-11T00:00:00Z --> +<!-- lastmod: 2019-09-24T00:00:00Z --> Two years ago I had an iPhone and, back then, the native "Notes" app worked pretty well for me. However, when I changed to Android I had some trouble diff --git a/content/blog/2019-09-23-upgrading-providers.md b/content/blog/2019-09-23-upgrading-providers.md @@ -1,14 +1,8 @@ ---- -title: "Upgrading to privacy-conscious providers" -slug: "upgrading-providers" -categories: [ - "Cryptography", - "Decentralization", - "Privacy" -] -date: 2019-09-23 -lastmod: 2019-09-24 ---- +<!-- title: Upgrading to privacy-conscious providers --> +<!-- slug: upgrading-providers --> +<!-- categories: Cryptography, Decentralization, Privacy --> +<!-- date: 2019-09-23T00:00:00Z --> +<!-- lastmod: 2019-09-24T00:00:00Z --> I have been reading a lot about decentralization and not depending too much in one company in the past six months and I realized how much I relied on Google: diff --git a/content/blog/2019-10-06-dark-theme.md b/content/blog/2019-10-06-dark-theme.md @@ -1,12 +1,7 @@ ---- -title: "Creating a dark theme" -slug: "dark-theme" -categories: [ - "Personal domain", - "Projects" -] -date: 2019-10-06 ---- +<!-- title: Creating a dark theme --> +<!-- slug: dark-theme --> +<!-- categories: Personal domain, Projects --> +<!-- date: 2019-10-06T00:00:00Z --> The first contact I had with HTML and CSS was about two years ago, when I created my first website along with a friend who already had some experience diff --git a/content/blog/2019-10-19-password-manager.md b/content/blog/2019-10-19-password-manager.md @@ -1,11 +1,7 @@ ---- -title: "Switching to a password manager" -slug: "password-manager" -categories: [ - "Cryptography" -] -date: 2019-10-19 ---- +<!-- title: Switching to a password manager --> +<!-- slug: password-manager --> +<!-- categories: Cryptography --> +<!-- date: 2019-10-19T00:00:00Z --> Before I learned about password managers, less than a year ago, having all my passwords on the same place sounded like a really bad idea—if someone managed to diff --git a/content/blog/2019-10-27-ship-scrappy.md b/content/blog/2019-10-27-ship-scrappy.md @@ -1,25 +1,20 @@ ---- -title: "Ship scrappy" -slug: "ship-scrappy" -subtitle: "I just wanted to post this interesting reflexion, as I tend to make sure everything is 100% perfect before “shipping it”. In some scenarios, this can be good, since you don't want what you are …" -categories: [ - "Miscellany" -] -date: 2019-10-27 ---- +<!-- title: Ship scrappy --> +<!-- slug: ship-scrappy --> +<!-- categories: Miscellany --> +<!-- date: 2019-10-27T00:00:00Z --> -> The only choice is to launch before you’re ready.\ -> Before it’s perfect.\ -> Before it’s 100% proven to be no risk to you.\ -> At that moment, your resistance says, “don’t ship it, it’s crappy stuff. We don’t ship crap.”\ -> And it’s true that you shouldn’t ship work that’s hurried, sloppy or ungenerous.\ -> But what’s actually on offer is something scrappy.\ -> Scrappy means that while it’s unpolished, it’s better than good enough.\ -> Scrappy doesn’t care about cosmetics as much as it cares about impact.\ -> Scrappy is flexible and resilient and ready to learn.\ +> The only choice is to launch before you’re ready.<br/> +> Before it’s perfect.<br/> +> Before it’s 100% proven to be no risk to you.<br/> +> At that moment, your resistance says, “don’t ship it, it’s crappy stuff. We don’t ship crap.”<br/> +> And it’s true that you shouldn’t ship work that’s hurried, sloppy or ungenerous.<br/> +> But what’s actually on offer is something scrappy.<br/> +> Scrappy means that while it’s unpolished, it’s better than good enough.<br/> +> Scrappy doesn’t care about cosmetics as much as it cares about impact.<br/> +> Scrappy is flexible and resilient and ready to learn.<br/> > Ship scrappy. > -> --- *[Seth's Blog][ss]* +> — *[Seth's Blog][ss]* I just wanted to post this interesting reflexion, as I tend to make sure everything is 100% perfect before "shipping it". In some scenarios, this can be diff --git a/content/blog/2019-11-04-new-host.md b/content/blog/2019-11-04-new-host.md @@ -1,12 +1,7 @@ ---- -title: "New website hosting servers" -slug: "new-host" -categories: [ - "Decentralization", - "Personal domain" -] -date: 2019-11-04 ---- +<!-- title: New website hosting servers --> +<!-- slug: new-host --> +<!-- categories: Decentralization, Personal domain --> +<!-- date: 2019-11-04T00:00:00Z --> Until yesterday, this website had been hosted by [GitLab][gl] (using GitLab Pages). For some time now the fact that they used servers owned by Google diff --git a/content/blog/2019-11-10-deploying-website.md b/content/blog/2019-11-10-deploying-website.md @@ -1,12 +1,7 @@ ---- -title: "Deploying a website using the WebDAV protocol" -slug: "deploying-website" -categories: [ - "FOSS", - "Personal domain" -] -date: 2019-11-10 ---- +<!-- title: Deploying a website using the WebDAV protocol --> +<!-- slug: deploying-website --> +<!-- categories: FOSS, Personal domain --> +<!-- date: 2019-11-10T00:00:00Z --> Now that my website is [hosted by Autistici/Inventati][hb], I can no longer deploy it by just pushing my git repository's changes to GitLab, as I used to. @@ -42,7 +37,7 @@ implementing it should have been a breeze. I encountered two problems: 1. By default, `rsync` makes use of modification times to check whether a file should be transferred, but every time I build my site, all files are created again, so the modification times are always newer than the ones in the - server.\ + server.<br/> There is a quick fix for this: the program has an option (`-c` or `--checksum`) that makes the program use the checksum of a file (instead of the modification time) along with the file size to determine whether it has @@ -52,7 +47,7 @@ implementing it should have been a breeze. I encountered two problems: reason (that I still don't know, my guess is something to do with permissions), when those auxiliary files are finally renamed to the definitive filename, it fails, giving out an error and exiting without any file - transferred.\ + transferred.<br/> To fix this issue, I used the `--temp-dir` option to specify a local directory as the one that should be used for the temporary files. With that set up, it doesn't give any more errors. @@ -86,7 +81,7 @@ to reduce file sizes), it mounts the WebDAV resource, transfers the files and then unmounts the resource again. -[hb]: <{{< ref "/blog/2019-11-04-new-host.md" >}}> "New website hosting servers — Oscar Benedito" +[hb]: </blog/2019/11/new-host/> "New website hosting servers — Oscar Benedito" [d]: <https://savannah.nongnu.org/projects/davfs2> "davfs2 — NonGNU Savannah" [hugo]: <https://gohugo.io> "Hugo" [r]: <https://rsync.samba.org> "rsync" diff --git a/content/blog/2019-11-17-lineageos-with-microg.md b/content/blog/2019-11-17-lineageos-with-microg.md @@ -1,13 +1,8 @@ ---- -title: "Switching to LineageOS with microG" -slug: "lineageos-with-microg" -categories: [ - "FOSS", - "Privacy" -] -date: 2019-11-17 -lastmod: 2019-11-22 ---- +<!-- title: Switching to LineageOS with microG --> +<!-- slug: lineageos-with-microg --> +<!-- categories: FOSS, Privacy --> +<!-- date: 2019-11-17T00:00:00Z --> +<!-- lastmod: 2019-11-22T00:00:00Z --> One of the things I wanted to do when switching to more privacy-respecting providers was getting rid of Google Services on my phone. According to multiple diff --git a/content/blog/2019-11-24-backups.md b/content/blog/2019-11-24-backups.md @@ -1,12 +1,7 @@ ---- -title: "Backing up my computer" -slug: "backups" -categories: [ - "FOSS", - "Privacy" -] -date: 2019-11-24 ---- +<!-- title: Backing up my computer --> +<!-- slug: backups --> +<!-- categories: FOSS, Privacy --> +<!-- date: 2019-11-24T00:00:00Z --> If you have important information on your computer, you probably back it up somehow. I used to save all my important files on Google Drive, which was diff --git a/content/blog/2019-12-06-composer.md b/content/blog/2019-12-06-composer.md @@ -1,13 +1,8 @@ ---- -title: "Designing a composing interface" -slug: "composer" -categories: [ - "FOSS", - "Projects" -] -lastmod: 2019-12-06T01:00:00+00:00 -date: 2019-12-06 ---- +<!-- title: Designing a composing interface --> +<!-- slug: composer --> +<!-- categories: FOSS, Projects --> +<!-- date: 2019-12-06T00:00:00Z --> +<!-- lastmod: 2019-12-06T01:00:00Z --> To write my blog posts, I use Markdown, a useful language to write simple fragments of text. The text is then "compiled" into HTML, which is then served diff --git a/content/blog/2019-12-15-your-corner-of-the-internet.md b/content/blog/2019-12-15-your-corner-of-the-internet.md @@ -1,13 +1,8 @@ ---- -title: "Your corner of the Internet" -slug: "your-corner-of-the-internet" -categories: [ - "Decentralization", - "Personal domain" -] -lastmod: 2019-12-06 -date: 2019-12-15 ---- +<!-- title: Your corner of the Internet --> +<!-- slug: your-corner-of-the-internet --> +<!-- categories: Decentralization, Personal domain --> +<!-- lastmod: 2019-12-06T00:00:00Z --> +<!-- date: 2019-12-15T00:00:00Z --> We tend to have online accounts across different social networks and services. We upload our projects in some sites, we post on different ones and we follow @@ -76,7 +71,7 @@ dynamic ones. - **More secure**: since there isn't an app server, potential vulnerabilities are reduced drastically. - **Faster**: because the server doesn't need to do operations, it can respond - to requests faster, hence accelerating the loading time.\ + to requests faster, hence accelerating the loading time.<br/> *That is a general claim, by using proper caching and using content delivery networks, speeds can change considerably. It also depends on the number of plugins installed (or other operations made by the server).* diff --git a/content/blog/2019-12-24-new-world-of-software.md b/content/blog/2019-12-24-new-world-of-software.md @@ -1,12 +1,7 @@ ---- -title: "A new world of software" -slug: "new-world-of-software" -categories: [ - "FOSS", - "Miscellany" -] -date: 2019-12-24 ---- +<!-- title: A new world of software --> +<!-- slug: new-world-of-software --> +<!-- categories: FOSS, Miscellany --> +<!-- date: 2019-12-24T00:00:00Z --> As I have said before, I was a big user of big tech companies' services. I also used macOS (and Windows before that) and proprietary software for mostly diff --git a/content/blog/2020-01-12-securing-communications.md b/content/blog/2020-01-12-securing-communications.md @@ -1,12 +1,8 @@ ---- -title: "Securing communications" -slug: "securing-communications" -categories: [ - "Cryptography" -] -date: 2020-01-12 -lastmod: 2020-08-10 ---- +<!-- title: Securing communications --> +<!-- slug: securing-communications --> +<!-- categories: Cryptography --> +<!-- date: 2020-01-12T00:00:00Z --> +<!-- lastmod: 2020-08-10T00:00:00Z --> We use cryptographic techniques daily without really knowing how they work, so I'm going to try and explain some basic concepts. Let's start with Wikipedia's @@ -15,7 +11,7 @@ current definition: > Cryptography or cryptology is the practice and study of techniques for secure > communication in the presence of third parties called adversaries. > -> --- *[Wikipedia's cryptography entry][cry]* +> — *[Wikipedia's cryptography entry][cry]* One cryptographic process we are all familiar with is encryption, that allows us to change the contents of a message so only certain people with a "key" can diff --git a/content/blog/2020-01-17-documenting-server.md b/content/blog/2020-01-17-documenting-server.md @@ -1,12 +1,8 @@ ---- -title: "Documenting my server" -slug: "documenting-server" -categories: [ - "Self-hosting" -] -date: 2020-01-17 -lastmod: 2020-03-01 ---- +<!-- title: Documenting my server --> +<!-- slug: documenting-server --> +<!-- categories: Self-hosting --> +<!-- date: 2020-01-17T00:00:00Z --> +<!-- lastmod: 2020-03-01T01:00:00Z --> Not long ago I realized that I could get $50 of credit on Digital Ocean with my GitHub Student account, so I decided to try it. I transferred my website there, diff --git a/content/blog/2020-01-25-syncthing.md b/content/blog/2020-01-25-syncthing.md @@ -1,13 +1,7 @@ ---- -title: "File synchronization software: Syncthing" -slug: "syncthing" -categories: [ - "Decentralization", - "FOSS", - "Privacy" -] -date: 2020-01-25 ---- +<!-- title: File synchronization software: Syncthing --> +<!-- slug: syncthing --> +<!-- categories: Decentralization, FOSS, Privacy --> +<!-- date: 2020-01-25T00:00:00Z --> [Syncthing][s] is a file synchronization program. It allows you to sync files between computers over LAN or the Internet. It is a very simple program that diff --git a/content/blog/2020-02-12-deploying-hugo-site.md b/content/blog/2020-02-12-deploying-hugo-site.md @@ -1,12 +1,7 @@ ---- -title: "Deploying a website built with Hugo" -slug: "deploying-hugo-site" -categories: [ - "Personal domain", - "Self-hosting" -] -date: 2020-02-12 ---- +<!-- title: Deploying a website built with Hugo --> +<!-- slug: deploying-hugo-site --> +<!-- categories: Personal domain, Self-hosting --> +<!-- date: 2020-02-12T00:00:00Z --> I have [previously talked][post] about creating a personal website, in this post I will talk about hosting it. More specifically, I'm going to explain how to @@ -129,7 +124,7 @@ on port 80 to redirect to the encrypted site. Your site is ready! -[post]: <{{< ref "/blog/2019-12-15-your-corner-of-the-internet.md" >}}> "Your corner of the Internet — Oscar Benedito" +[post]: </blog/2019/12/your-corner-of-the-internet/> "Your corner of the Internet — Oscar Benedito" [ex]: <https://gitlab.com/pages/hugo> "GitLab Pages Examples: Hugo — GitLab" [dmsnh]: <https://doesmysiteneedhttps.com/> "Does my site need HTTPS?" [le]: <https://letsencrypt.org> "Let's Encrypt" diff --git a/content/blog/2020-02-23-sharing-a-secret.md b/content/blog/2020-02-23-sharing-a-secret.md @@ -0,0 +1,96 @@ +<!-- title: Sharing a secret --> +<!-- slug: sharing-a-secret --> +<!-- categories: Cryptography --> +<!-- date: 2020-02-23T00:00:00Z --> +<!-- extrafooter: <script id="MathJax-script" async src="/js/mathjax-3.0.1/tex-chtml.js"></script> --> + +Making a backup of a secret can be tricky. For instance: I have a lot of +passwords stored in an encrypted file, which I can edit through my password +manager. The data in that file is both very sensitive and crucial. I currently +have some offline backups (which are updated every once in a while) in different +locations and one online backup in case I lose access to my passwords and I am +not able to go to one of the locations where other backups are kept. + +The problem with having an online backup is that such sensitive data must be +kept away from untrusted third parties and, so far, there's no third party I +would trust with all those passwords. My solution is to distribute the trust. +The encrypted file is encrypted again multiple times with very long random +passwords. Those passwords are distributed across different services, and the +file in another one, so no one service has access to the encrypted file. + +This seems like a reasonably secure system (considering the diversity of parties +that should agree to attack me/get hacked). However, a couple of days ago, I was +introduced to a much simpler and convenient method to "distribute" a secret: +[Shamir's Secret Sharing][sss]. + +## Shamir's Secret Sharing + +Shamir's Secret Sharing was created by [Adi Shamir][as], a cryptographer who is +also the co-inventor of the—probably more widely known—[RSA algorithm][rsa] +(yes, that S stands for Shamir!). Here, I'll try to briefly explain how it +works. + +Given a secret \\(S\\) (coded as a number), we want to distribute it among +\\(n\\) parties (giving each party a "share" of the secret) in such a way that +only \\(k \\leq n\\) shares are needed to retrieve the secret, but that +\\(k-1\\) shares don't grant any kind of knowledge on \\(S\\). + +Shamir's method is based on the fact that given \\(n + 1\\) pairs \\((x_i, +y_i)\\) such that \\(i \\neq j \\implies x_i \\neq x_j\\), then there exists a +unique polynomial \\(p\\) of degree \\(n\\) or less that satisfies that +\\(p(x_i) = y_i\\), \\(\\forall i \\in \\{1, \\dots, n\\}\\) (and we have an +efficient method to find \\(p\\) given \\(n\\) points). + +Let's put it into practice. Given a secret \\(S\\), to be shared with \\(n\\) +parties in a way that any \\(k\\) parties can retrieve it, we'll build the +following polynomial: + +\\[p(x) = S + a_1 x + a_2 x^2 + ... + a_{k-1} x^{k-1},\\] + +where \\(a_i\\) are random numbers, \\(\\forall i \\in \\{1, \\dots, k-1\\}\\). +Now we'll evaluate our polynomial on \\(n\\) different points (and different +from 0) to obtain \\(n\\) pairs of the form \\((x_i, p(x_i))\\). This will be +the shares of the secret. Each party will get one share. We know that \\(k\\) +shares define a unique polynomial \\(p\\) of degree \\(k-1\\), (if we find it, +we'll find \\(S\\)). On the other hand, there are an infinite amount of +polynomials of degree \\(k-1\\) that interpolate \\(k-1\\) points, so the secret +cannot be easily obtained by gaining access to \\(k-1\\) shares[^integers]. + +[^integers]: This is not completely true when working with positive integers, + but it can be solved by working with finite fields. + +If we want to recover the secret from \\(k\\) shares, we can interpolate the +\\(k\\) points \\((x_i, p(x_i))\\) using [Lagrange's form for the interpolation +polynomial][int][^proof]: + +[^proof]: Let's quickly prove that the \\(p\\) defined in Lagrange's form + (\\(\\bar{p}\\) from now on) is the same as the previously defined \\(p\\). + \\(\\bar{p}\\) is clearly a polynomial of degree (at most) \\(k-1\\), since it + is the sum of polynomials of degree \\(k-1\\), so we only need to prove that + it interpolates the points given (we'll asume that the fact that there is only + one polynomial of degree at most \\(k-1\\) that interpolates \\(k\\) points is + true). That is easy to prove since \\(i \\neq j \\implies l_i(x_j) = 0\\) and + \\(l_i(x_i) = 1\\), therefore having \\(\\bar{p}(x_i) = p(x_i) l_i(x_i) = + p(x_i)\\). + +\\[p(x) = \\sum_{i=1}^{k} p(x_i) l_i(x),\\] + +where + +\\[l_i(x) = \\prod_{\\begin{smallmatrix}1\\leq m\\leq k\\ m\\neq +i\\end{smallmatrix}} \\frac{x-x_m}{x_i-x_m}.\\] + +Now, evaluating on \\(x = 0\\) we'll find the secret (because \\(p(0) = S\\)). + +## Final notes + +Now we have a method to share our secret between multiple parties and being able +to retrieve it with only some of them. This method is not too hard to code +yourself, however, there are implementations online if you would rather not do +that (make sure you are running trusted code!). + + +[sss]: <https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing> "Shamir's Secret Sharing — Wikipedia" +[as]: <https://en.wikipedia.org/wiki/Adi_Shamir> "Adi Shamir — Wikipedia" +[rsa]: <https://en.wikipedia.org/wiki/RSA_(cryptosystem)> "RSA — Wikipedia" +[int]: <https://en.wikipedia.org/wiki/Lagrange_polynomial> "Lagrange polynomial — Wikipedia" diff --git a/content/blog/2020-02-23-sharing-a-secret.pdc b/content/blog/2020-02-23-sharing-a-secret.pdc @@ -1,103 +0,0 @@ ---- -title: "Sharing a secret" -slug: "sharing-a-secret" -categories: [ - "Cryptography" -] -date: 2020-02-23 ---- - -Making a backup of a secret can be tricky. For instance: I have a lot of -passwords stored in an encrypted file, which I can edit through my password -manager. The data in that file is both very sensitive and crucial. I currently -have some offline backups (which are updated every once in a while) in different -locations and one online backup in case I lose access to my passwords and I am -not able to go to one of the locations where other backups are kept. - -The problem with having an online backup is that such sensitive data must be -kept away from untrusted third parties and, so far, there's no third party I -would trust with all those passwords. My solution is to distribute the trust. -The encrypted file is encrypted again multiple times with very long random -passwords. Those passwords are distributed across different services, and the -file in another one, so no one service has access to the encrypted file. - -This seems like a reasonably secure system (considering the diversity of parties -that should agree to attack me/get hacked). However, a couple of days ago, I was -introduced to a much simpler and convenient method to "distribute" a secret: -[Shamir's Secret Sharing][sss]. - -## Shamir's Secret Sharing - -Shamir's Secret Sharing was created by [Adi Shamir][as], a cryptographer who is -also the co-inventor of the—probably more widely known—[RSA algorithm][rsa] -(yes, that S stands for Shamir!). Here, I'll try to briefly explain how it -works. - -Given a secret $S$ (coded as a number), we want to distribute it among $n$ -parties (giving each party a "share" of the secret) in such a way that only $k -\leq n$ shares are needed to retrieve the secret, but that $k-1$ shares don't -grant any kind of knowledge on $S$. - -Shamir's method is based on the fact that given $n + 1$ pairs $(x_i, y_i)$ such -that $i \neq j \implies x_i \neq x_j$, then there exists a unique polynomial $p$ -of degree $n$ or less that satisfies that $p(x_i) = y_i$, $\forall i \in \{1, -\dots, n\}$ (and we have an efficient method to find $p$ given $n$ points). - -Let's put it into practice. Given a secret $S$, to be shared with $n$ parties in -a way that any $k$ parties can retrieve it, we'll build the following -polynomial: - -<div class="mathjax-container"> -$$p(x) = S + a_1 x + a_2 x^2 + ... + a_{k-1} x^{k-1},$$ -</div> - -where $a_i$ are random numbers, $\forall i \in \{1, \dots, k-1\}$. Now we'll -evaluate our polynomial on $n$ different points (and different from 0) to obtain -$n$ pairs of the form $(x_i, p(x_i))$. This will be the shares of the secret. -Each party will get one share. We know that $k$ shares define a unique -polynomial $p$ of degree $k-1$, (if we find it, we'll find $S$). On the other -hand, there are an infinite amount of polynomials of degree $k-1$ that -interpolate $k-1$ points, so the secret cannot be easily obtained by gaining -access to $k-1$ shares[^integers]. - -[^integers]: This is not completely true when working with positive integers, - but it can be solved by working with finite fields. - -If we want to recover the secret from $k$ shares, we can interpolate the $k$ -points $(x_i, p(x_i))$ using [Lagrange's form for the interpolation -polynomial][int][^proof]: - -[^proof]: Let's quickly prove that the $p$ defined in Lagrange's form ($\bar{p}$ - from now on) is the same as the previously defined $p$. $\bar{p}$ is clearly a - polynomial of degree (at most) $k-1$, since it is the sum of polynomials of - degree $k-1$, so we only need to prove that it interpolates the points given - (we'll asume that the fact that there is only one polynomial of degree at most - $k-1$ that interpolates $k$ points is true). That is easy to prove since $i - \neq j \implies l_i(x_j) = 0$ and $l_i(x_i) = 1$, therefore having - $\bar{p}(x_i) = p(x_i) l_i(x_i) = p(x_i)$. - -<div class="mathjax-container"> -$$p(x) = \sum_{i=1}^{k} p(x_i) l_i(x),$$ -</div> - -where - -<div class="mathjax-container"> -$$l_i(x) = \prod_{\begin{smallmatrix}1\leq m\leq k\\ m\neq i\end{smallmatrix}} -\frac{x-x_m}{x_i-x_m}.$$ -</div> - -Now, evaluating on $x = 0$ we'll find the secret (because $p(0) = S$). - -## Final notes - -Now we have a method to share our secret between multiple parties and being able -to retrieve it with only some of them. This method is not too hard to code -yourself, however, there are implementations online if you would rather not do -that (make sure you are running trusted code!). - - -[sss]: <https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing> "Shamir's Secret Sharing — Wikipedia" -[as]: <https://en.wikipedia.org/wiki/Adi_Shamir> "Adi Shamir — Wikipedia" -[rsa]: <https://en.wikipedia.org/wiki/RSA_(cryptosystem)> "RSA — Wikipedia" -[int]: <https://en.wikipedia.org/wiki/Lagrange_polynomial> "Lagrange polynomial — Wikipedia" diff --git a/content/blog/2020-03-01-new-domain-name.md b/content/blog/2020-03-01-new-domain-name.md @@ -1,12 +1,7 @@ ---- -title: "New domain name: oscarbenedito.com" -slug: "new-domain-name" -categories: [ - "Miscellany", - "Personal domain" -] -date: 2020-03-01 ---- +<!-- title: New domain name: oscarbenedito.com --> +<!-- slug: new-domain-name --> +<!-- categories: Miscellany, Personal domain --> +<!-- date: 2020-03-01T00:00:00Z --> After a lot of thought, I have decided to change my domain to [oscarbenedito.com](https://oscarbenedito.com). My website should be completely diff --git a/content/blog/2020-03-02-types-of-networks.md b/content/blog/2020-03-02-types-of-networks.md @@ -1,11 +1,7 @@ ---- -title: "Centralized, decentralized and distributed networks" -slug: "types-of-networks" -categories: [ - "Miscellany", -] -date: 2020-03-02 ---- +<!-- title: Centralized, decentralized and distributed networks --> +<!-- slug: types-of-networks --> +<!-- categories: Miscellany --> +<!-- date: 2020-03-02T00:00:00Z --> When we are trying to understand a communications network, having an approximate image of how the network operates can be very valuable. Do all communications go diff --git a/content/blog/2020-03-12-lightweight-website.md b/content/blog/2020-03-12-lightweight-website.md @@ -1,11 +1,7 @@ ---- -title: "A lightweight website" -slug: "lightweight-website" -categories: [ - "Personal domain" -] -date: 2020-03-12 ---- +<!-- title: A lightweight website --> +<!-- slug: lightweight-website --> +<!-- categories: Personal domain --> +<!-- date: 2020-03-12T00:00:00Z --> Since the start of this site, having a lightweight website has been one of my priorities. Every file served has been minimized, you won't see any pictures diff --git a/content/blog/2020-03-21-lighter-website.md b/content/blog/2020-03-21-lighter-website.md @@ -1,11 +1,7 @@ ---- -title: "A lighter website" -slug: "lighter-website" -categories: [ - "Personal domain" -] -date: 2020-03-21 ---- +<!-- title: A lighter website --> +<!-- slug: lighter-website --> +<!-- categories: Personal domain --> +<!-- date: 2020-03-21T00:00:00Z --> Following up with the [last post][post], I decided to make my website even faster (which probably doesn't make a difference anymore). @@ -64,5 +60,5 @@ helpful if in the future I have a site with bigger static files (or someone reading this has!). -[post]: <{{< ref "/blog/2020-03-12-lightweight-website.md" >}}> "A lightweight website — Oscar Benedito" +[post]: </blog/2020/03/lightweight-website/> "A lightweight website — Oscar Benedito" [hp]: <https://gohugo.io/hugo-pipes> "Hugo Pipes" diff --git a/content/blog/2020-04-07-on-not-caring-about-your-privacy.md b/content/blog/2020-04-07-on-not-caring-about-your-privacy.md @@ -1,12 +1,7 @@ ---- -title: "On not caring about your privacy" -slug: "on-not-caring-about-your-privacy" -categories: [ - "Miscellany", - "Privacy" -] -date: 2020-04-07T16:17:00+00:00 ---- +<!-- title: On not caring about your privacy --> +<!-- slug: on-not-caring-about-your-privacy --> +<!-- categories: Miscellany, Privacy --> +<!-- date: 2020-04-07T16:17:00Z --> When talking about violations of our privacy, I've found that most people don't care because it is a thing that happens "far away" (*who in that huge enterprise diff --git a/content/blog/2020-04-18-use-web-feeds.md b/content/blog/2020-04-18-use-web-feeds.md @@ -1,11 +1,7 @@ ---- -title: "Use web feeds!" -slug: "use-web-feeds" -categories: [ - "Decentralization" -] -date: 2020-04-18T14:59:00+00:00 ---- +<!-- title: Use web feeds! --> +<!-- slug: use-web-feeds --> +<!-- categories: Decentralization --> +<!-- date: 2020-04-18T14:59:00Z --> Web feeds are data formats used to provide users with updates through web syndication. Websites can use web feeds to post their content in a format that diff --git a/content/blog/2020-05-05-my-journey-through-desktop-environments.md b/content/blog/2020-05-05-my-journey-through-desktop-environments.md @@ -1,13 +1,8 @@ ---- -title: "My journey through desktop environments" -slug: "my-journey-through-desktop-environments" -categories: [ - "FOSS", - "Miscellany" -] -date: 2020-05-05T19:26:00+00:00 -lastmod: 2020-05-06T07:52:00+00:00 ---- +<!-- title: My journey through desktop environments --> +<!-- slug: my-journey-through-desktop-environments --> +<!-- categories: FOSS, Miscellany --> +<!-- date: 2020-05-05T19:26:00Z --> +<!-- lastmod: 2020-05-06T07:52:00Z --> My first experience with GNU/Linux was with KDE. It is the desktop environment used on my college computers, and it was more or less the only experience I had diff --git a/content/blog/2020-05-27-blocking-connections-on-android.md b/content/blog/2020-05-27-blocking-connections-on-android.md @@ -1,11 +1,7 @@ ---- -title: "Blocking connections on Android" -slug: "blocking-connections-on-android" -categories: [ - "Privacy" -] -date: 2020-05-27T19:01:00+00:00 ---- +<!-- title: Blocking connections on Android --> +<!-- slug: blocking-connections-on-android --> +<!-- categories: Privacy --> +<!-- date: 2020-05-27T19:01:00Z --> I have been a user of [NetGuard][ng] for quite some time. It is a great Android app that lets you control which apps get Internet access and which don't. The diff --git a/content/blog/2020-06-23-setting-up-a-personal-git-server.md b/content/blog/2020-06-23-setting-up-a-personal-git-server.md @@ -1,14 +1,8 @@ ---- -title: "Setting up a personal Git server" -slug: "setting-up-a-personal-git-server" -categories: [ - "Decentralization", - "Personal domain", - "Self-hosting" -] -date: 2020-06-23T16:10:00+00:00 -lastmod: 2020-07-24T15:17:00+00:00 ---- +<!-- title: Setting up a personal Git server --> +<!-- slug: setting-up-a-personal-git-server --> +<!-- categories: Decentralization, Personal domain, Self-hosting --> +<!-- date: 2020-06-23T16:10:00Z --> +<!-- lastmod: 2020-07-24T15:17:00Z --> Running a personal Git server is something that has been on my mind for quite a long time. One of the most popular solutions I have seen around is diff --git a/content/blog/2020-08-09-what-is-this-vim-talk-all-about.md b/content/blog/2020-08-09-what-is-this-vim-talk-all-about.md @@ -1,12 +1,7 @@ ---- -title: "What is this vim talk all about?" -slug: "what-is-this-vim-talk-all-about" -categories: [ - "FOSS", - "Miscellany" -] -date: 2020-08-09T15:16:00+00:00 ---- +<!-- title: What is this vim talk all about? --> +<!-- slug: what-is-this-vim-talk-all-about --> +<!-- categories: FOSS, Miscellany --> +<!-- date: 2020-08-09T15:16:00Z --> Oh no! Another [vim][vim] post! Well... yes. I have seen a lot of people criticizing vim before even trying it, so I am going to try and explain my diff --git a/content/blog/2020-08-15-adding-about-pages-to-stagit.md b/content/blog/2020-08-15-adding-about-pages-to-stagit.md @@ -1,12 +1,7 @@ ---- -title: "Adding about pages to stagit" -slug: "adding-about-pages-to-stagit" -categories: [ - "FOSS", - "Projects" -] -date: 2020-08-15T19:59:00+00:00 ---- +<!-- title: Adding about pages to stagit --> +<!-- slug: adding-about-pages-to-stagit --> +<!-- categories: FOSS, Projects --> +<!-- date: 2020-08-15T19:59:00Z --> I use [stagit][sg] to show the public repositories of my Git server on the web. I chose stagit because it is a very simple and lightweight tool, which makes diff --git a/content/blog/2020-08-30-davup.md b/content/blog/2020-08-30-davup.md @@ -1,12 +1,7 @@ ---- -title: "DAVup: back up your contacts and calendars" -slug: "davup" -categories: [ - "FOSS", - "Projects" -] -date: 2020-08-30T19:27:00+00:00 ---- +<!-- title: DAVup: back up your contacts and calendars --> +<!-- slug: davup --> +<!-- categories: FOSS, Projects --> +<!-- date: 2020-08-30T19:27:00Z --> Meet [DAVup][d]! It is a simple script that will back up any contacts, calendars and to-do lists synchronized through CardDAV or CalDAV. diff --git a/content/blog/_index.md b/content/blog/_index.md @@ -1,6 +1,4 @@ ---- -title: Personal blog ---- +<!-- title: Personal blog --> # Personal blog diff --git a/content/blog/categories/cryptography/_index.md b/content/blog/categories/cryptography/_index.md @@ -0,0 +1 @@ +# Category: Cryptography diff --git a/content/blog/categories/decentralization/_index.md b/content/blog/categories/decentralization/_index.md @@ -0,0 +1 @@ +# Category: Decentralization diff --git a/content/blog/categories/foss/_index.md b/content/blog/categories/foss/_index.md @@ -0,0 +1 @@ +# Category: FOSS diff --git a/content/blog/categories/miscellany/_index.md b/content/blog/categories/miscellany/_index.md @@ -0,0 +1 @@ +# Category: Miscellany diff --git a/content/blog/categories/personal-domain/_index.md b/content/blog/categories/personal-domain/_index.md @@ -0,0 +1 @@ +# Category: Personal domain diff --git a/content/blog/categories/privacy/_index.md b/content/blog/categories/privacy/_index.md @@ -0,0 +1 @@ +# Category: Privacy diff --git a/content/blog/categories/projects/_index.md b/content/blog/categories/projects/_index.md @@ -0,0 +1 @@ +# Category: Projects diff --git a/content/blog/categories/self-hosting/_index.md b/content/blog/categories/self-hosting/_index.md @@ -0,0 +1 @@ +# Category: Self-hosting diff --git a/content/blog_archive.md b/content/blog_archive.md @@ -1,5 +0,0 @@ ---- -title: Blog archive -type: archive -url: /blog/archive/ ---- diff --git a/content/blogroll.md b/content/blogroll.md @@ -1,12 +1,23 @@ ---- -title: Blogroll -type: page ---- +<!-- title: Blogroll --> Blogs I have found interesting, alphabetically sorted. You can easily import all the blogs to your feed reader using [this OMPL file][ompl]. -{{< blogroll >}} - +<!-- blogroll --> +- [Brendan Abolivier](https://brendan.abolivier.bzh) — [Feed](https://brendan.abolivier.bzh/index.xml) +- [Codesections](https://www.codesections.com/blog/) — [Feed](https://www.codesections.com/rss.xml) +- [David Prandzioch](https://www.davd.io) — [Feed](https://www.davd.io/index.xml) +- [Desmond Rivet](https://desmondrivet.com/blog/) — [Feed](https://desmondrivet.com/feeds/blog.rss) +- [Emanuel Pina](https://emanuelpina.pt) — [Feed](https://emanuelpina.pt/index.xml) +- [Henrique Dias](https://hacdias.com/articles/) — [Feed](https://hacdias.com/articles/feed.xml) +- [Jake Bauer](https://www.paritybit.ca/blog) — [Feed](https://www.paritybit.ca/feeds/sitewide-feed.xml) +- [Jan-Lukas Else](https://jlelse.blog) — [Feed](https://jlelse.blog/index.xml) +- [Kev Quirk](https://kevq.uk) — [Feed](https://kevq.uk/feed/) +- [mcol](https://mcol.xyz) — [Feed](https://mcol.xyz/rss.xml) +- [Mike Babb](https://mikebabb.com/blog/) — [Feed](https://mikebabb.com/feed.xml) +- [Mike Stone](https://mikestone.me) — [Feed](https://mikestone.me/feed/) +- [PapaTutuWawa](https://blog.polynom.me) — [Feed](https://blog.polynom.me/atom.xml) +- [Sheogorath](https://shivering-isles.com/#blog) — [Feed](https://shivering-isles.com/feed.xml) +<!-- /blogroll --> [ompl]: </blogroll/blogroll.ompl> "Blogroll's OMPL file" diff --git a/content/blogroll_ompl.md b/content/blogroll_ompl.md @@ -1,5 +0,0 @@ ---- -title: Blogroll OMPL -type: blogroll_ompl -url: /blogroll/blogroll.ompl ---- diff --git a/content/contact.md b/content/contact.md @@ -1,7 +1,4 @@ ---- -title: Contact me -type: page ---- +<!-- title: Contact me --> You can contact me sending an email to [oscar@oscarbenedito.com][email]. diff --git a/content/jsweblabels.html b/content/jsweblabels.html @@ -1,7 +1,4 @@ ---- -title: JavaScript Web Labels -type: page ---- +<!-- title: JavaScript Web Labels --> <p>This page is basically a list of all the JavaScript files I use in my website, their license and their source code. I wanted to make a completely free/libre website and although it would be free/libre even if this page didn't exist, it allows LibreJS users to run the JavaScript without having to whitelist it manually and also offers an easy way for everybody to find the source code files.</p> diff --git a/create-blogroll.py b/create-blogroll.py @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -# -# Create Blogroll: creates the JSON for a blogroll's page from an OMPL file. -# -# Copyright (C) 2020 Oscar Benedito -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <https://www.gnu.org/licenses/>. - -import sys -import json -import xml.etree.ElementTree as ET - -tree = ET.parse(sys.argv[1]) -root = tree.getroot() - -out = [] -for category in root[0]: - if category.attrib['text'] == "Blogroll": - for entry in category: - out.append({ - "name": entry.attrib['text'], - "url": entry.attrib['htmlUrl'], - "feed": entry.attrib['xmlUrl'] - }) - break - -print(json.dumps(out, indent=2, sort_keys=True, ensure_ascii=False)) diff --git a/data/blogroll.json b/data/blogroll.json @@ -1,72 +0,0 @@ -[ - { - "feed": "https://brendan.abolivier.bzh/index.xml", - "name": "Brendan Abolivier", - "url": "https://brendan.abolivier.bzh" - }, - { - "feed": "https://www.codesections.com/rss.xml", - "name": "Codesections", - "url": "https://www.codesections.com/blog/" - }, - { - "feed": "https://www.davd.io/index.xml", - "name": "David Prandzioch", - "url": "https://www.davd.io" - }, - { - "feed": "https://desmondrivet.com/feeds/blog.rss", - "name": "Desmond Rivet", - "url": "https://desmondrivet.com/blog/" - }, - { - "feed": "https://emanuelpina.pt/index.xml", - "name": "Emanuel Pina", - "url": "https://emanuelpina.pt" - }, - { - "feed": "https://hacdias.com/articles/feed.xml", - "name": "Henrique Dias", - "url": "https://hacdias.com/articles/" - }, - { - "feed": "https://www.paritybit.ca/feeds/sitewide-feed.xml", - "name": "Jake Bauer", - "url": "https://www.paritybit.ca/blog" - }, - { - "feed": "https://jlelse.blog/index.xml", - "name": "Jan-Lukas Else", - "url": "https://jlelse.blog" - }, - { - "feed": "https://kevq.uk/feed/", - "name": "Kev Quirk", - "url": "https://kevq.uk" - }, - { - "feed": "https://mcol.xyz/rss.xml", - "name": "mcol", - "url": "https://mcol.xyz" - }, - { - "feed": "https://mikebabb.com/feed.xml", - "name": "Mike Babb", - "url": "https://mikebabb.com/blog/" - }, - { - "feed": "https://mikestone.me/feed/", - "name": "Mike Stone", - "url": "https://mikestone.me" - }, - { - "feed": "https://blog.polynom.me/atom.xml", - "name": "PapaTutuWawa", - "url": "https://blog.polynom.me" - }, - { - "feed": "https://shivering-isles.com/feed.xml", - "name": "Sheogorath", - "url": "https://shivering-isles.com/#blog" - } -] diff --git a/gensite.py b/gensite.py @@ -0,0 +1,406 @@ +#!/usr/bin/env python3 +# gensite.py: Static site generator based on makesite.py. +# Copyright (C) 2020 Oscar Benedito <oscar@oscarbenedito.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# This file incorporates work covered by the following copyright and +# permission notice: +# +# Copyright (c) 2018 Sunaina Pai +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +"""Static site generator based on makesite.py.""" + + +import os +import shutil +import re +import glob +import sys +import datetime +import hashlib +import markdown + + +def fread(filename): + """Read file and close the file.""" + with open(filename, 'r') as f: + return f.read() + + +def fwrite(filename, text): + """Write content to file and close the file.""" + filename = filename + 'index.html' if filename.endswith('/') or filename == '' else filename + filename = os.path.join('_site', filename) + if os.path.exists(filename): + log('W', 'Warning: Overwritting file: {}', filename) + + basedir = os.path.dirname(filename) + if not os.path.isdir(basedir): + os.makedirs(basedir) + + with open(filename, 'w') as f: + f.write(text) + + +def log(type, msg, *args): + """Log message with specified arguments.""" + if type == 'E' or type == 'W': # or type == 'I': + sys.stderr.write(msg.format(*args) + '\n') + + +def truncate(text, words=50): + """Remove tags and truncate text to the specified number of words.""" + return ' '.join(re.sub('(?s)<.*?>', '', text).split()[:words]) + '...' + + +def urlize(name): + """Convert string tu URL.""" + return name.lower().replace(' ', '-') + + +def add_to_sitemap(path, lastmod=None, freq=None, priority=None): + """Add URL to sitemap.""" + global sitemap + path = '<loc>https://oscarbenedito.com/' + path + '</loc>' + if lastmod == '1970-01-01T00:00:00Z': + lastmod = None + lastmod = '<lastmod>' + lastmod + '</lastmod>' if lastmod else '' + freq = '<changefreq>' + freq + '</changefreq>' if freq else '' + priority = '<priority>' + priority + '</priority>' if priority else '' + sitemap += '<url>' + path + lastmod + freq + priority + '</url>' + + +def set_redirect(src, dst): + """Create HTML redirect.""" + fwrite(src, '<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0; url=\'/' + dst + '\'"/><link rel="canonical" href="/' + dst + '"/><meta name="robots" content="noindex"></head><body><p>This page has been moved to <a href="/' + dst + '">https://oscarbenedito.com/' + dst + '</a>.</p></body></html>') + log('I', 'Info: redirect /{} => /{}', src, dst) + + +def read_headers(text): + """Parse headers in text and yield (key, value, end-index) tuples.""" + for match in re.finditer(r'\s*<!--\s*(.+?)\s*:\s*(.+?)\s*-->\s*|.+', text): + if not match.group(1): + break + yield match.group(1), match.group(2), match.end() + + +def prettify_date(date_str): + """Convert ISO 8601 date string to human friendly date string.""" + d = datetime.datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ') + return d.strftime('%B %-d, %Y') + + +def render(template, pre=False, **params): + """Replace placeholders in template with values from params.""" + if not pre: + template = re.sub(r'{{\s*_if\s+([^}\s]+)\s*}}(.*?){{\s*_fi\s*}}', + lambda m: m.group(2) if m.group(1) in params else '', + template, flags=re.DOTALL) + return re.sub(r'{{\s*([^}\s]+)\s*}}', + lambda m: str(params.get(m.group(1), m.group(0))), + template) + + +def read_content(filename): + """Read content and metadata from file into a dictionary.""" + # read file content + text = fread(filename) + + # read metadata and save it in a dictionary + date_slug = os.path.basename(filename).split('.')[0] + match = re.search(r'^(?:(\d\d\d\d-\d\d-\d\d)-)?(.+)$', date_slug) + content = { + 'date': (match.group(1) or '1970-01-01') + 'T00:00:00Z', + 'slug': match.group(2) + } + + # read headers + end = 0 + for key, val, end in read_headers(text): + content[key] = val + + if 'lastmod' in content: + content['modified'] = '1' + else: + content['lastmod'] = content['date'] + + # separate content from headers + text = text[end:] + + # convert Markdown content to HTML + if filename.endswith('.md'): + text = markdown.markdown(text, extensions=['footnotes', 'fenced_code']) + + content.update({ + 'content': text, + 'year': content['date'][:4], + 'month': content['date'][5:7], + 'day': content['date'][8:10], + 'date_nice': prettify_date(content['date']), + 'lastmod_nice': prettify_date(content['lastmod']) + }) + + if 'categories' in content: + # convert the categories string to array of categories + categories = [c.strip() for c in content['categories'].split(',')] + categories_html = ', '.join(['<a class="p-category" href="/blog/categories/' + urlize(c) + '/">' + c + '</a>' for c in categories]) + content.update({ + 'categories': categories, + 'categories_html': categories_html + }) + + return content + + +def make_pages(src, dst, layout, blog=False, **params): + """Generate pages from page content.""" + items = [] + categories = {} + + for src_path in glob.glob(src): + content = read_content(src_path) + + page_params = dict(params, **content) + + # populate placeholders in content if content-rendering is enabled + if page_params.get('render') == 'yes': + rendered_content = render(page_params['content'], **page_params) + page_params['content'] = rendered_content + + page_dst = render(dst, **page_params) + + if 'url' in page_params: + page_dst = page_params['url'] + else: + page_params.update({'url': page_dst}) + + if blog: + w = int(len(re.sub('(?s)<.*?>', ' ', page_params['content']).split())/140) + page_params.update({ + 'read_time': str(w) + ' minutes' if w > 1 else '1 minute', + 'src_path': src_path, + }) + items.append(page_params) + else: + fwrite(page_dst, render(layout, **page_params)) + pri = page_params['priority'] if 'priority' in page_params else None + add_to_sitemap(page_dst, lastmod=page_params['lastmod'], priority=pri) + log('I', 'Info: page {} => /{}', src_path, page_dst) + + items.sort(key=lambda x: x['date'], reverse=True) + for i, item in enumerate(items): + if i != 0: + item['next_url'] = items[i-1]['url'] + item['next_title'] = items[i-1]['title'] + item['more_pages'] = '1' + if i < len(items)-1: + item['prev_url'] = items[i+1]['url'] + item['prev_title'] = items[i+1]['title'] + item['more_pages'] = '1' + + for category in item['categories']: + if category not in categories: + categories[category] = [item] + else: + categories[category].append(item) + + fwrite(item['url'], render(layout, **item)) + pri = item['priority'] if 'priority' in item else None + add_to_sitemap(item['url'], lastmod=item['lastmod'], priority=pri) + log('I', 'Info: post {} => /{}', item['src_path'], item['url']) + + return items, categories + + +def make_lists(posts, dst, list_layout, item_layout, **params): + """Generate HTML lists for a blog.""" + item_per_page = 5 + items = [] + count = 1 + page_dst = dst + text = fread('content/' + dst + '_index.md') + params['intro'] = markdown.markdown(text, extensions=['footnotes', 'fenced_code']) + for i, post in enumerate(posts): + item_params = dict(params, **post) + item_params['summary'] = truncate(post['content']) + items.append(render(item_layout, **item_params)) + if i % item_per_page == item_per_page-1 and len(posts)-1 > i: + params['more_pages'] = '1' + params['content'] = ''.join(items) + params['next_url'] = dst + 'page/' + str(count+1) + '/' + if count != 1: + params['prev_url'] = dst + ('page/' + str(count-1) + '/' if count != 2 else '') + fwrite(page_dst, render(list_layout, **params)) + log('I', 'Info: list => /{}', page_dst) + count = count+1 + page_dst = dst + 'page/' + str(count) + '/' + items = [] + + if count != 1: + del params['next_url'] + params['prev_url'] = dst + ('page/' + str(count-1) + '/' if count != 2 else '') + params['content'] = ''.join(items) + fwrite(page_dst, render(list_layout, **params)) + log('I', 'Info: list => /{}', page_dst) + + set_redirect(dst + 'page/1/', dst) + + +def make_feed(posts, dst, list_layout, item_layout, **params): + """Generate feed for a blog.""" + max = 15 + params['url'] = dst + page_dst = dst + 'index.xml' + items = [] + for i, post in enumerate(posts): + if (i == max): + break + item_params = dict(params, **post) + item_params['c_escaped'] = post['content'].replace('>', '>').replace('<', '<') + item = render(item_layout, **item_params) + items.append(item) + + params['content'] = ''.join(items) + params['updated'] = posts[0]['lastmod'] + fwrite(page_dst, render(list_layout, **params)) + log('I', 'Info: feed => /{}', page_dst) + + +def make_archive(posts, categories, dst, layout, **params): + year = 0 + params['content'] = '<h2>Posts (' + str(len(posts)) + ')</h2>\n' + for post in posts: + if post['year'] != year: + params['content'] += ('</ul>\n' if year != 0 else '') + '<h3>' + post['year'] + '</h3>\n<ul>\n' + year = post['year'] + params['content'] += '<li><a href="/' + post['url'] + '">' + post['title'] + '</a> (' + post['date_nice'][:-6] + ')</li>\n' + params['content'] += '</ul>\n' + + params['content'] += '<h2>Categories (' + str(len(categories)) + ')</h2>\n<ul>\n' + for key in sorted(categories): + val = categories[key] + params['content'] += '<li><a href="/' + dst + 'categories/' + urlize(key) + '/">' + key + '</a> (' + str(len(val)) + (' entry' if len(val) == 1 else ' entries') + ')</li>\n' + params['content'] += '</ul>\n' + + page_dst = dst + 'archive/' + fwrite(page_dst, render(layout, **params)) + add_to_sitemap(page_dst, lastmod=posts[0]['lastmod'], priority='0.4') + log('I', 'Info: page => /{}', page_dst) + + +def main(): + # create a new _site directory from scratch + if os.path.isdir('_site'): + shutil.rmtree('_site') + shutil.copytree('static', '_site') + + # initialize parameters + params = {} + + # initialize sitemap + global sitemap + sitemap = '<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' + + # copy assets adding part of their sha256 value to the filename + for path, _, files in os.walk('assets'): + for name in files: + file = os.path.join(path, name) + rfile = os.path.relpath(file, 'assets') + h = hashlib.sha256() + with open(file, 'rb') as c: + h.update(c.read()) + name, ext = os.path.splitext(rfile) + dst = '{n}.{h}{e}'.format(n=name, h=h.hexdigest()[:8], e=ext) + params['_asset_' + rfile] = dst + basedir = os.path.dirname(os.path.join('_site', dst)) + if not os.path.isdir(basedir): + os.makedirs(basedir) + shutil.copy(file, os.path.join('_site', dst)) + + # load layouts + base_layout = fread('layouts/base.html') + page_layout = fread('layouts/page.html') + post_layout = fread('layouts/post.html') + list_html = fread('layouts/list.html') + item_html = fread('layouts/item.html') + feed_xml = fread('layouts/feed.xml') + item_xml = fread('layouts/item.xml') + layout_404 = fread('layouts/404.html') + + # combine layouts to form final layouts + page_layout = render(base_layout, pre=True, content=page_layout) + post_layout = render(base_layout, pre=True, content=post_layout) + list_html = render(base_layout, pre=True, content=list_html) + + # create site pages + make_pages('content/_index.md', '', page_layout, **params) + make_pages('content/[!_]*.*', '{{ slug }}/', page_layout, **params) + fwrite('404.html', render(layout_404, **params)) + + # create blog post pages + blog_posts, categories = make_pages('content/blog/[!_]*.*', + 'blog/{{ year }}/{{ month }}/{{ slug }}/', + post_layout, True, **params) + # create HTML list pages + make_lists(blog_posts, 'blog/', list_html, item_html, title='Personal blog', + **params) + add_to_sitemap('blog/', lastmod=blog_posts[0]['lastmod'], priority='1.0') + # create Atom feeds + make_feed(blog_posts, 'blog/', feed_xml, item_xml, title='Personal blog', + long_title='Oscar\'s Blog', **params) + # create blog archive + make_archive(blog_posts, categories, 'blog/', page_layout, + title='Blog archive', **params) + # create blog categories + for name, posts in categories.items(): + dst = 'blog/categories/' + urlize(name) + '/' + lt = name + ' on Oscar\'s Blog' + eh = '<link rel="alternate" type="application/atom+xml" title="' + lt + '" href="/' + dst + 'index.xml"/>' + make_lists(posts, dst, list_html, item_html, title=name, extraheader=eh, + **params) + make_feed(posts, dst, feed_xml, item_xml, title=name, long_title=lt, + **params) + + # set redirections + set_redirect('licenses/agpl-v3/', 'licenses/agpl-3.0.txt') + set_redirect('licenses/gpl-v3/', 'licenses/gpl-3.0.txt') + set_redirect('licenses/cc-by-4.0/', 'licenses/cc-by-4.0.txt') + set_redirect('composer/', 'projects/composer/composer.html') + + fwrite('sitemap.xml', sitemap + '</urlset>') + + +if __name__ == '__main__': + main() diff --git a/layouts/404.html b/layouts/404.html @@ -3,20 +3,16 @@ <head> <meta charset="utf-8"> <meta name="description" content="Oscar Benedito's website"> - <meta name="author" content="{{ .Site.Author.name }}"> - <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"> + <meta name="author" content="Oscar Benedito"> + <meta name="viewport" content="width=device-width,initial-scale=1"> <title>404 Error | Oscar Benedito</title> - {{- $favicon := resources.Get "img/favicon.min.svg" | fingerprint }} - <link rel="icon" href="{{ $favicon.RelPermalink }}"> - <style> -{{ (resources.Get "css/landing.css" | minify).Content | safeCSS }} - </style> + <link rel="icon" href="/{{ _asset_img/favicon.min.svg }}"> </head> - <body> - <div class="content"> - <a href="/" class="img first"><img class="big-icon" src="{{ $favicon.RelPermalink }}"></a> - <p class="title">404 Error</p> - <p class="subtitle last">The file or page you requested was not found.</p> + <body style="background-color: hsl(0, 0%, 94%); color: hsl(0, 0%, 47%); font-family: sans-serif;"> + <div style="margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center;"> + <a href="/" style="font-size:0; margin-top: 0;"><img style="width:100px; height:100px;" src="/{{ _asset_img/favicon.min.svg }}"></a> + <p style="font-size: 3em; font-weight: bold; margin-bottom: 0.25em;">404 Error</p> + <p style="font-size: 1.5em; margin-bottom: 0;">The file or page you requested was not found.</p> </div> </body> </html> diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html @@ -1,19 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - {{ partial "head-base.html" . -}} - {{- $style := resources.Get "css/style.scss" | toCSS | minify | fingerprint }} - <link rel="stylesheet" type="text/css" href="{{ $style.RelPermalink }}"> - {{- block "add-to-header" . }}{{ end }} - </head> - <body> - {{ partial "header.html" . }} - {{- block "main" . }} - <main> - {{ .Content }} - </main> - {{- end }} - {{ partial "footer.html" . -}} - {{ partial "scripts.html" . -}} - </body> -</html> diff --git a/layouts/_default/list.html b/layouts/_default/list.html @@ -1,51 +0,0 @@ -{{ define "add-to-header" }} -{{- if eq .Kind "term" }} -<link rel="alternate" type="application/atom+xml" title="{{ .Title }} on Oscar's Blog" href="{{ .RelPermalink }}index.xml"> -{{ end }} -{{ end }} - -{{ define "main" }} -<main class="list h-feed"> - {{- if .Content }} - <header> - {{ .Content }} - </header> - {{- else if eq .Kind "term" }} - <header> - <h1>{{ .Data.Singular | title }}: {{ .Title }}</h1> - </header> - {{- end }} - - {{- $paginator := .Paginator }} - {{- range $paginator.Pages }} - <article class="summary h-entry"> - <header> - <h2><a class="u-url p-name" href="{{ .RelPermalink }}">{{ .Title }}</a></h2> - <div class="metadata"> - {{ partial "categories.html" . }} · <a href="{{ .RelPermalink }}"><time class="dt-published" datetime="{{ .Date.Format "2006-01-02T15:04:05-07:00" }}">{{ .Date.Format "January 2, 2006" }}</time></a> · {{ partial "read-time.html" . }} - <div> - </header> - <section class="content"> - {{- if .Params.subtitle }} - <p class="p-summary">{{ .Params.subtitle }}</p> - {{- else }} - <p class="p-summary">{{ .Plain | htmlUnescape | truncate 200 }}</p> - {{- end }} - </section> - </article> - {{- end }} - - {{- $paginator := .Paginator }} - {{- if or $paginator.HasPrev $paginator.HasNext }} - <div class="pagination"> - {{- if $paginator.HasPrev }} - <a class="button" href="{{ $paginator.Prev.URL }}">← Newer Posts</a> - {{- end }} - {{- if $paginator.HasNext }} - <a class="button right" href="{{ $paginator.Next.URL }}">Older Posts →</a> - {{- end }} - </div> - {{- end }} - {{ partial "h-card.html" . -}} -</main> -{{ end }} diff --git a/layouts/_default/rss.xml b/layouts/_default/rss.xml @@ -1,37 +0,0 @@ -{{- $pctx := . -}} -{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}} -{{- $pages := slice -}} -{{- if or $.IsHome $.IsSection -}} -{{- $pages = $pctx.RegularPages -}} -{{- else -}} -{{- $pages = $pctx.Pages -}} -{{- end -}} -{{- $limit := .Site.Config.Services.RSS.Limit -}} -{{- if ge $limit 1 -}} -{{- $pages = $pages | first $limit -}} -{{- end -}} -{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\"?>" | safeHTML }} -<feed xmlns="http://www.w3.org/2005/Atom"> - <title>{{ if eq .Title "Personal blog" }}Oscar's Blog{{ else }}{{ with .Title }}{{ . }} on {{ end }}Oscar's Blog{{ end }}</title> - <link href="{{ .Permalink }}index.xml" rel="self"/> - <link href="{{ .Permalink }}" rel="alternate"/>{{ if not .Date.IsZero }} - <updated>{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHTML }}</updated>{{ end }} - <id>tag:{{ $.Site.Params.domain }},2020-04-13:{{ .RelPermalink }}</id>{{ with $.Site.Author.name }} - <author> - <name>{{ . }}</name>{{ with $.Site.Author.email }} - <email>{{ . }}</email>{{ end }} - </author>{{ end }} - <generator>Hugo -- gohugo.io</generator>{{ range $pages }} - <entry> - <title>{{ .Title }}</title> - <link href="{{ .Permalink }}"/> - <id>tag:{{ $.Site.Params.domain }},{{ .Date.Format "2006-01-02" | safeHTML }}:{{ .RelPermalink }}</id>{{ with $.Site.Author.name }} - <author> - <name>{{ . }}</name>{{ with $.Site.Author.email }} - <email>{{ . }}</email>{{ end }} - </author>{{ end }} - <published>{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHTML }}</published> - <updated>{{ .Lastmod.Format "2006-01-02T15:04:05-07:00" | safeHTML }}</updated> - <content type="html">{{ .Content | html }}</content> - </entry>{{ end }} -</feed> diff --git a/layouts/_default/single.html b/layouts/_default/single.html @@ -1,24 +0,0 @@ -{{ define "main" }} -<main> - <article class="h-entry"> - <header> - <h1 class="p-name">{{ .Title }}</h1> - <div class="metadata">{{ partial "categories.html" . }} · <a class="u-url" href="{{ .RelPermalink }}"><time class="dt-published" datetime="{{ .Date.Format "2006-01-02T15:04:05-07:00" }}">{{ .Date.Format "January 2, 2006" }}</time></a>{{ if ne .Lastmod .Date }} (last modified on <time class="dt-updated" datetime="{{ .Lastmod.Format "2006-01-02T15:04:05-07:00" }}">{{ .Lastmod.Format "January 2, 2006" }}</time>){{ end }} - </div> - </header> - <div class="content e-content">{{ .Content }}</div> - {{ partial "h-card.html" . -}} - </article> - - {{- if or .NextInSection .PrevInSection }} - <nav class="pagination"> - {{- if .NextInSection }} - <a class="button" href="{{ .NextInSection.RelPermalink }}" title="{{ .NextInSection.Title }}">← Newer Post</a> - {{- end }} - {{- if .PrevInSection }} - <a class="button right" href="{{ .PrevInSection.RelPermalink }}" title="{{ .PrevInSection.Title }}">Previous Post →</a> - {{- end }} - </nav> - {{- end }} -</main> -{{- end }} diff --git a/layouts/archive/single.html b/layouts/archive/single.html @@ -1,29 +0,0 @@ -{{ define "main" }} -<main> - <article> - <header> - <h1>{{ .Title }}</h1> - </header> - <div class="content"> - <h2>Posts ({{ len (where (where .Site.Pages "File.Dir" "==" "blog/") "IsPage" true) }})</h2> - {{- range .Site.Params.active_years }} - {{- $y := . }} - <h3>{{ . }}</h3> - <ul> - {{- range $.Site.Pages }} - {{- if and (eq .File.Dir "blog/") (.IsPage) (eq $y (.Date.Format "2006")) }} - <li><a href="{{ .RelPermalink }}">{{ .Title }}</a> ({{ .Date.Format "January 2" }})</li> - {{- end }} - {{- end }} - </ul> - {{- end }} - <h2>Categories ({{ len .Site.Taxonomies.categories }})</h2> - <ul> - {{- range .Site.Taxonomies.categories }} - <li><a href="{{ .Page.RelPermalink }}">{{ .Page.Title }}</a> ({{ .Count }} {{ if eq .Count 1}}entry{{ else }}entries{{ end }})</li> - {{- end }} - </ul> - </div> - </article> -</main> -{{ end }} diff --git a/layouts/base.html b/layouts/base.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1"/>{{ _if description }} + <meta name="description" content="{{ description }}">{{ _fi }} + <meta name="author" content="Oscar Benedito"/> + <link rel="icon" href="/{{ _asset_img/favicon.min.svg }}"/> + <link rel="shortcut icon" href="/favicon.ico"/> + <title>{{ _if title }}{{ title }} | {{ _fi }}Oscar Benedito</title> + <link rel="alternate" type="application/atom+xml" title="Oscar's Blog" href="/blog/index.xml"/> + <link rel="stylesheet" type="text/css" href="/{{ _asset_css/style.min.css }}"/>{{ _if extraheader }} + {{ extraheader }}{{ _fi }} + </head> + <body> + <header id="page-header"> + <div class="content"> + <a href="/" title="Home"><svg class="logo" style="height: 1.6em;" viewBox="0 0 84.665 8.89"><use xlink:href="/{{ _asset_img/logo.svg }}#l"></use></svg></a> + <nav><a href="/blog/">Blog</a> · <a href="/contact/">Contact</a> · <a href="https://git.oscarbenedito.com">Git</a></nav> + </div> + </header> + {{ content }} + <footer id="page-footer"> + <div class="content"> + <nav><a href="https://git.oscarbenedito.com/oscarbenedito.com/">Source code</a> · <a href="/jsweblabels/" rel="jslicense">JavaScript Web Labels</a> · <a href="/about/">About this site</a></nav> + </div> + </footer>{{ _if extrafooter }} + {{ extrafooter }}{{ _fi }} + </body> +</html> diff --git a/layouts/blogroll_ompl/single.html b/layouts/blogroll_ompl/single.html @@ -1,13 +0,0 @@ -{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\"?>" | safeHTML }} -<opml version="1.0"> - <head> - <title>Oscar Benedito's Blogroll</title> - </head> - <body> - <outline text="Blogroll"> - {{- range sort $.Site.Data.blogroll "name" }} - <outline type="rss" text="{{ .name }}" xmlUrl="{{ .feed }}"{{ with .url }} htmlUrl="{{ . }}"{{ end }}/> - {{- end }} - </outline> - </body> -</opml> diff --git a/layouts/feed.xml b/layouts/feed.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<feed xmlns="http://www.w3.org/2005/Atom"> + <title>{{ long_title }}</title> + <link href="https://oscarbenedito.com/{{ url }}index.xml" rel="self"/> + <link href="https://oscarbenedito.com/{{ url }}" rel="alternate"/> + <updated>{{ updated }}</updated> + <id>tag:oscarbenedito.com,2020-04-13:/{{ url }}</id> + <author> + <name>Oscar Benedito</name> + </author> + {{ content }} +</feed> diff --git a/layouts/index.html b/layouts/index.html @@ -1,15 +0,0 @@ -{{ define "add-to-header" }} -<link rel="me" href="https://fosstodon.org/@ob"> -<link rel="me" href="https://github.com/oscarbenedito"> -<link rel="me" href="https://gitlab.com/oscarbenedito"> -<link rel="me" href="mailto:oscar@oscarbenedito.com"> -{{ end }} - -{{ define "main" }} -<main> - <article> - <div class="content">{{ .Content }}</div> - </article> -</main> -{{ partial "h-card.html" . -}} -{{ end }} diff --git a/layouts/item.html b/layouts/item.html @@ -0,0 +1,11 @@ +<article class="summary h-entry"> + <header> + <h2><a class="u-url p-name" href="/{{ url }}">{{ title }}</a></h2> + <div class="metadata"> + {{ categories_html }} · <a href="/{{ url }}"><time class="dt-published" datetime="{{ date }}">{{ date_nice }}</time></a> · {{ read_time }} + <div> + </header> + <section class="content"> + <p class="p-summary">{{ summary }} <a href="/{{ url }}">Read more</a></p> + </section> +</article> diff --git a/layouts/item.xml b/layouts/item.xml @@ -0,0 +1,11 @@ +<entry> + <title>{{ title }}</title> + <link href="https://oscarbenedito.com/{{ url }}"/> + <id>tag:oscarbenedito.com,{{ year }}-{{ month }}-{{ day }}:/{{ url }}</id> + <author> + <name>Oscar Benedito</name> + </author> + <published>{{ date }}</published> + <updated>{{ lastmod }}</updated> + <content type="html">{{ c_escaped }}</content> +</entry> diff --git a/layouts/list.html b/layouts/list.html @@ -0,0 +1,16 @@ +<main class="list h-feed"> + <header> + {{ intro }} + </header> + + {{ content }} + + {{ _if more_pages }}<div class="pagination">{{ _fi }} + {{ _if prev_url }}<a class="button" href="/{{ prev_url }}">← Newer Posts</a>{{ _fi }} + {{ _if next_url }}<a class="button right" href="/{{ next_url }}">Older Posts →</a>{{ _fi }} + {{ _if more_pages }}</div>{{ _fi }} + <div class="p-author h-card" style="display: none;"> + <a rel="me" class="p-name u-url" href="https://oscarbenedito.com">Oscar Benedito</a> + <img class="u-photo" src="/{{ _asset_img/favicon.min.svg }}"/> + </div> +</main> diff --git a/layouts/page.html b/layouts/page.html @@ -0,0 +1,8 @@ +<main> + <article>{{ _if title }} + <header> + <h1>{{ title }}</h1> + </header>{{ _fi }} + <div class="content">{{ content }}</div> + </article> +</main> diff --git a/layouts/page/single.html b/layouts/page/single.html @@ -1,10 +0,0 @@ -{{ define "main" }} -<main> - <article> - <header> - <h1>{{ .Title }}</h1> - </header> - <div class="content">{{ .Content }}</div> - </article> -</main> -{{ end }} diff --git a/layouts/partials/categories.html b/layouts/partials/categories.html @@ -1,4 +0,0 @@ -{{ range $i, $e := ( .Params.categories ) }} - {{- if $i }}, {{ end -}} - <a class="p-category" href="{{ "blog/categories/" | relURL }}{{ $e | urlize }}">{{ $e }}</a> -{{- end -}} diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html @@ -1,5 +0,0 @@ -<footer id="page-footer"> - <div class="content"> - <nav><a href="https://git.oscarbenedito.com/oscarbenedito.com/">Source code</a> · <a href="/jsweblabels/" rel="jslicense">JavaScript Web Labels</a> · <a href="/about/">About this site</a></nav> - </div> -</footer> diff --git a/layouts/partials/h-card.html b/layouts/partials/h-card.html @@ -1,5 +0,0 @@ -<div class="p-author h-card" style="display: none;"> - <a rel="me" class="p-name u-url" href="https://oscarbenedito.com">Oscar Benedito</a> - {{- $favicon := resources.Get "img/favicon.min.svg" | fingerprint }} - <img class="u-photo" src="{{ $favicon.RelPermalink }}"/> -</div> diff --git a/layouts/partials/head-base.html b/layouts/partials/head-base.html @@ -1,9 +0,0 @@ -<meta charset="utf-8"> -<meta name="viewport" content="width=device-width, initial-scale=1"> -{{/* {{ if .Site.Params.description }}<meta name="description" content="{{ site.description }}">{% endif %} */}} -<meta name="author" content="{{ .Site.Author.name }}"> -{{- $favicon := resources.Get "img/favicon.min.svg" | fingerprint }} -<link rel="icon" href="{{ $favicon.RelPermalink }}"> -<link rel="shortcut icon" href="/favicon.ico"> -<title>{{ if .Params.headtitle }}{{ .Params.headtitle }} | {{ else if eq .Kind "term" }}{{ .Title }} | {{ else if not .IsHome }}{{ .Title }} | {{ end }}{{ .Site.Title }}</title> -<link rel="alternate" type="application/atom+xml" title="Oscar's Blog" href="/blog/index.xml"> diff --git a/layouts/partials/header.html b/layouts/partials/header.html @@ -1,7 +0,0 @@ -<header id="page-header"> - <div class="content"> - {{- $logo := resources.Get "img/logo.svg" | fingerprint }} - <a href="/" title="Home"><svg class="logo" style="height: 1.6em;" viewBox="0 0 84.665 8.89"><use xlink:href="{{ $logo.RelPermalink }}#l"></use></svg></a> - <nav><a href="/blog/">Blog</a> · <a href="/contact/">Contact</a> · <a href="https://git.oscarbenedito.com">Git</a></nav> - </div> -</header> diff --git a/layouts/partials/read-time.html b/layouts/partials/read-time.html @@ -1,2 +0,0 @@ -{{ $readTime := math.Round (div (countwords .Content) 140.0) -}} -{{ if le $readTime 1.0 }}1 minute{{ else }}{{ $readTime }} minutes{{ end -}} diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html @@ -1,5 +0,0 @@ -{{- with .File }} -{{- if eq .Ext "pdc" }} -<script id="MathJax-script" async src="/js/mathjax-3.0.1/tex-chtml.js"></script> -{{- end }} -{{- end }} diff --git a/layouts/post.html b/layouts/post.html @@ -0,0 +1,19 @@ +<main> + <article class="h-entry"> + <header> + <h1 class="p-name">{{ title }}</h1> + <div class="metadata">{{ categories_html }} · <a class="u-url" href="/{{ url }}"><time class="dt-published" datetime="{{ date }}">{{ date_nice }}</time></a>{{ _if modified }} (last modified on <time class="dt-updated" datetime="{{ lastmod }}">{{ lastmod_nice }}</time>){{ _fi }} + </div> + </header> + <div class="content e-content">{{ content }}</div> + <div class="p-author h-card" style="display: none;"> + <a rel="me" class="p-name u-url" href="https://oscarbenedito.com">Oscar Benedito</a> + <img class="u-photo" src="/{{ _asset_img/favicon.min.svg }}"/> + </div> + </article> + + {{ _if more_pages }}<nav class="pagination">{{ _fi }} + {{ _if next_url }}<a class="button" href="/{{ next_url }}" title="{{ next_title }}">← Newer Post</a>{{ _fi }} + {{ _if prev_url }}<a class="button right" href="/{{ prev_url }}" title="{{ prev_title }}">Previous Post →</a>{{ _fi }} + {{ _if more_pages }}</nav>{{ _fi }} +</main> diff --git a/layouts/shortcodes/blogroll.html b/layouts/shortcodes/blogroll.html @@ -1,5 +0,0 @@ -<ul> - {{- range sort $.Site.Data.blogroll "name" }} - <li>{{ if .url }}<a href="{{ .url }}">{{ .name }}</a>{{ else }}{{ .name }}{{ end }}{{ with .feed }} — <a href="{{ . }}">Feed</a>{{ end }}</li> - {{- end }} -</ul> diff --git a/layouts/shortcodes/js-label.html b/layouts/shortcodes/js-label.html @@ -1,7 +0,0 @@ -<tr> - {{- $js := resources.Get (.Get "asset") | minify | fingerprint }} - <td><a href="{{ $js.RelPermalink }}">/{{ (.Get "asset") }}</a></td> - <td><a href="{{ .Get "license-url" }}">{{ .Get "license-slug" }}</a></td> - {{- $js := resources.Get (.Get "asset") }} - <td><a href="{{ $js.RelPermalink }}">{{ $js.RelPermalink }}</a></td> -</tr> diff --git a/assets/img/favicon.svg b/misc/favicon-original.svg diff --git a/assets/css/style.scss b/misc/style.scss diff --git a/misc/update-blogroll.py b/misc/update-blogroll.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# Update Blogroll: updates blogroll page and OMPL file. +# Copyright (C) 2020 Oscar Benedito <oscar@oscarbenedito.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +import xml.etree.ElementTree +import re + + +tree = xml.etree.ElementTree.parse('misc/blogroll.ompl') +root = tree.getroot() + +blogs = [] +for category in root[0]: + if category.attrib['text'] == 'Blogroll': + for entry in category: + blogs.append({ + 'text': entry.attrib['text'], + 'html': entry.attrib['htmlUrl'], + 'feed': entry.attrib['xmlUrl'] + }) + break + +ompl = '<?xml version="1.0" encoding="utf-8"?>\n<opml version="1.0">\n <head>\n <title>Oscar Benedito\'s Blogroll</title>\n </head>\n <body>\n <outline text="Oscar Benedito\'s Blogroll">\n' +md = '<!-- blogroll -->\n' + +for blog in sorted(blogs, key=lambda i: i['text'].lower()): + ompl += ' <outline type="rss" text="' + blog['text'] + '" xmlUrl="' + blog['feed'] + '" htmlUrl="' + blog['html'] + '"/>\n' + md += '- [' + blog['text'] + '](' + blog['html'] + ') — [Feed](' + blog['feed'] + ')\n' + +ompl += ' </outline>\n </body>\n</opml>\n' +md += '<!-- /blogroll -->' + +with open('static/blogroll/blogroll.ompl', 'w') as f: + f.write(ompl) + +with open('content/blogroll.md', 'r') as f: + text = f.read() + +re.sub('<!-- blogroll -->.*<!-- /blogroll -->', md, text, flags=re.DOTALL) + +with open('content/blogroll.md', 'w') as f: + f.write(text) diff --git a/static/blogroll/blogroll.ompl b/static/blogroll/blogroll.ompl @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<opml version="1.0"> + <head> + <title>Oscar Benedito's Blogroll</title> + </head> + <body> + <outline text="Oscar Benedito's Blogroll"> + <outline type="rss" text="Brendan Abolivier" xmlUrl="https://brendan.abolivier.bzh/index.xml" htmlUrl="https://brendan.abolivier.bzh"/> + <outline type="rss" text="Codesections" xmlUrl="https://www.codesections.com/rss.xml" htmlUrl="https://www.codesections.com/blog/"/> + <outline type="rss" text="David Prandzioch" xmlUrl="https://www.davd.io/index.xml" htmlUrl="https://www.davd.io"/> + <outline type="rss" text="Desmond Rivet" xmlUrl="https://desmondrivet.com/feeds/blog.rss" htmlUrl="https://desmondrivet.com/blog/"/> + <outline type="rss" text="Emanuel Pina" xmlUrl="https://emanuelpina.pt/index.xml" htmlUrl="https://emanuelpina.pt"/> + <outline type="rss" text="Henrique Dias" xmlUrl="https://hacdias.com/articles/feed.xml" htmlUrl="https://hacdias.com/articles/"/> + <outline type="rss" text="Jake Bauer" xmlUrl="https://www.paritybit.ca/feeds/sitewide-feed.xml" htmlUrl="https://www.paritybit.ca/blog"/> + <outline type="rss" text="Jan-Lukas Else" xmlUrl="https://jlelse.blog/index.xml" htmlUrl="https://jlelse.blog"/> + <outline type="rss" text="Kev Quirk" xmlUrl="https://kevq.uk/feed/" htmlUrl="https://kevq.uk"/> + <outline type="rss" text="mcol" xmlUrl="https://mcol.xyz/rss.xml" htmlUrl="https://mcol.xyz"/> + <outline type="rss" text="Mike Babb" xmlUrl="https://mikebabb.com/feed.xml" htmlUrl="https://mikebabb.com/blog/"/> + <outline type="rss" text="Mike Stone" xmlUrl="https://mikestone.me/feed/" htmlUrl="https://mikestone.me"/> + <outline type="rss" text="PapaTutuWawa" xmlUrl="https://blog.polynom.me/atom.xml" htmlUrl="https://blog.polynom.me"/> + <outline type="rss" text="Sheogorath" xmlUrl="https://shivering-isles.com/feed.xml" htmlUrl="https://shivering-isles.com/#blog"/> + </outline> + </body> +</opml> diff --git a/static/composer/index.html b/static/composer/index.html @@ -1 +0,0 @@ -<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0; url='/projects/composer/composer.html'"/><link rel="canonical" href="/projects/composer/composer.html"/><meta name="robots" content="noindex"></head><body><p>This page has been moved to <a href="/projects/composer/composer.html">https://oscarbenedito.com/projects/composer/composer.html</a>.</p></body></html> diff --git a/static/licenses/agpl-v3/index.html b/static/licenses/agpl-v3/index.html @@ -1 +0,0 @@ -<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0; url='/licenses/agpl-3.0.txt'"/><link rel="canonical" href="/licenses/agpl-3.0.txt"/><meta name="robots" content="noindex"></head><body><p>This page has been moved to <a href="/licenses/agpl-3.0.txt">https://oscarbenedito.com/licenses/agpl-3.0.txt</a>.</p></body></html> diff --git a/static/licenses/cc-by-4.0/index.html b/static/licenses/cc-by-4.0/index.html @@ -1 +0,0 @@ -<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0; url='/licenses/cc-by-4.0.txt'"/><link rel="canonical" href="/licenses/cc-by-4.0.txt"/><meta name="robots" content="noindex"></head><body><p>This page has been moved to <a href="/licenses/cc-by-4.0.txt">https://oscarbenedito.com/licenses/cc-by-4.0.txt</a>.</p></body></html> diff --git a/static/licenses/gpl-v3/index.html b/static/licenses/gpl-v3/index.html @@ -1 +0,0 @@ -<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0; url='/licenses/gpl-3.0.txt'"/><link rel="canonical" href="/licenses/gpl-3.0.txt"/><meta name="robots" content="noindex"></head><body><p>This page has been moved to <a href="/licenses/gpl-3.0.txt">https://oscarbenedito.com/licenses/gpl-3.0.txt</a>.</p></body></html> diff --git a/content/robots.txt b/static/robots.txt