diff --git a/config.toml b/config.toml index 8698586..9ec5548 100644 --- a/config.toml +++ b/config.toml @@ -16,6 +16,7 @@ dot = "set -e; echo -n '

'; dot -Tsvg | tail -n +4; echo '< nav_links = [ { url = "/", text = "home" }, { url = "/pages/about", text = "about" }, + { url = "/pages/projects", text = "projects" }, + { url = "/setup", text = "setup" }, { url = "/pages", text = "all pages" }, - { url = "https://git.iptq.io", text = "projects »" }, ] diff --git a/content/2018-02-01-my-new-life-stack.md b/content/2018-02-01-my-new-life-stack.md index f1f1304..fbf8774 100644 --- a/content/2018-02-01-my-new-life-stack.md +++ b/content/2018-02-01-my-new-life-stack.md @@ -3,7 +3,7 @@ title = "my new life stack" date = 2018-02-01 [taxonomies] -tags = ["arch", "linux", "life"] +tags = ["arch", "linux", "setup", "computers"] +++ This is my first post on my new blog! I used to put a CTF challenge writeup here but decided to change it up a bit. Recently, I've been changing a lot of the technology that I use day to day. Here's some of the changes that I've made! diff --git a/content/2018-02-25-cleaning-up-your-shell.md b/content/2018-02-25-cleaning-up-your-shell.md index 5c759ee..d3bfd1b 100644 --- a/content/2018-02-25-cleaning-up-your-shell.md +++ b/content/2018-02-25-cleaning-up-your-shell.md @@ -3,7 +3,7 @@ title = "cleaning up your shell" date = 2018-02-25 [taxonomies] -tags = ["bash", "zsh", "terminal"] +tags = ["computers", "linux", "bash", "terminal"] +++ Is your shell loading slower than it used to? Maybe you've been sticking a bit more into your `.bashrc`/`.zshrc` than you thought. diff --git a/content/2018-04-23-fixing-tmux-colors.md b/content/2018-04-23-fixing-tmux-colors.md index 96e325e..d6f2089 100644 --- a/content/2018-04-23-fixing-tmux-colors.md +++ b/content/2018-04-23-fixing-tmux-colors.md @@ -3,7 +3,7 @@ title = "fixing tmux colors" date = 2018-04-23 [taxonomies] -tags = ["tmux", "terminal"] +tags = ["computers", "terminal"] +++ Put this in your `~/.tmux.conf`. diff --git a/content/2018-05-28-web-apps.md b/content/2018-05-28-web-apps.md index c282c6b..79a3dde 100644 --- a/content/2018-05-28-web-apps.md +++ b/content/2018-05-28-web-apps.md @@ -3,7 +3,7 @@ title = "web apps" date = 2018-05-28 [taxonomies] -tags = ["javascript", "web", "rant", "things-that-are-bad"] +tags = ["computers", "javascript", "web-dev", "rant", "things-that-are-bad"] +++ The other day, I just turned off JavaScript from my browser. "fucking neckbeard", "you'll turn it back in 2 weeks", "living without JavaScript is like living without electricity" were some of the responses I got. And they might be right. But let's see why things are the way they are and what we can do about it. diff --git a/content/2018-10-26-twenty-years-of-rsa-attacks.md b/content/2018-10-26-twenty-years-of-rsa-attacks.md index 8c9f69e..ef5523f 100644 --- a/content/2018-10-26-twenty-years-of-rsa-attacks.md +++ b/content/2018-10-26-twenty-years-of-rsa-attacks.md @@ -3,7 +3,7 @@ title = "twenty years of attacks on rsa.. with examples!" date = 2018-10-26 [taxonomies] -tags = ["rsa", "math", "crypto", "python"] +tags = ["math", "crypto", "python"] [extra] toc = true diff --git a/content/2019-02-01-magic-forms-with-proc-macros.md b/content/2019-02-01-magic-forms-with-proc-macros.md index f9acce0..6984054 100644 --- a/content/2019-02-01-magic-forms-with-proc-macros.md +++ b/content/2019-02-01-magic-forms-with-proc-macros.md @@ -3,7 +3,7 @@ title = "magic forms with proc macros: ideas" date = 2019-02-01 [taxonomies] -tags = ["rust", "web", "macros"] +tags = ["computers", "rust", "web-dev", "macros"] +++ Procedural macros (proc macros for short) in Rust are incredible because they allow pre-compile source transformation. Many of the greatest abstractions in Rust take advantage of this feature. For example, you can diff --git a/content/2019-03-04-server-analogy.md b/content/2019-03-04-server-analogy.md index e9b080f..0eff3f8 100644 --- a/content/2019-03-04-server-analogy.md +++ b/content/2019-03-04-server-analogy.md @@ -3,7 +3,7 @@ title = "accept server analogy" date = 2019-03-04 [taxonomies] -tags = [] +tags = ["computers"] +++ This is just something stupid I thought of recently, but decided to write about it anyway. diff --git a/content/2020-04-01-password-managers.md b/content/2020-04-01-password-managers.md index a89242a..72572e0 100644 --- a/content/2020-04-01-password-managers.md +++ b/content/2020-04-01-password-managers.md @@ -3,7 +3,7 @@ title = "password managers" date = 2020-04-01 [taxonomies] -tags = ["tech", "things-that-are-good", "privacy"] +tags = ["computers", "things-that-are-good", "privacy"] +++ Password managers are programs that store passwords for you. With the number of accounts you keep on the web, you generally don't want to store all of them in your head. If you want to see articles on why you should use a password manager NOW, search "reasons to use a password manager" online and any of the articles you find should explain it. Here I'll add some more commentary on top of the traditional arguments. diff --git a/content/2020-04-12-drawing-bezier-curves.md b/content/2020-04-12-drawing-bezier-curves.md new file mode 100644 index 0000000..118b6cf --- /dev/null +++ b/content/2020-04-12-drawing-bezier-curves.md @@ -0,0 +1,8 @@ ++++ +title = "drawing bezier curves" +date = 2020-04-12 +draft = true + +[taxonomies] +tags = ["osu", "graphics"] ++++ diff --git a/content/enterprise/2020-02-11-prototype/index.md b/content/enterprise/2020-02-11-prototype/index.md index f5109dd..b7e9b5b 100644 --- a/content/enterprise/2020-02-11-prototype/index.md +++ b/content/enterprise/2020-02-11-prototype/index.md @@ -4,7 +4,7 @@ date = 2020-02-11 template = "post.html" [taxonomies] -tags = ["enterprise", "web", "ui", "rust", "design"] +tags = ["computers", "web-dev"] +++ This past weekend, while on my trip to Minneapolis, I completed a very early prototype of "enterprise", a new UI framework I've been kind of envisioning over the past couple of weeks. While the UI framework is mainly targeted at web apps, the hope is that with a bit more effort, native UIs can be produced with almost no changes to existing applications. Before I begin to describe how it works, I'd like to acknowledge [Nathan Ringo][1] for his massively helpful feedback in both the brainstorming and the implementation process. diff --git a/content/enterprise/2020-02-17-syntax-update.md b/content/enterprise/2020-02-17-syntax-update.md index 1e8c9cb..d8863f6 100644 --- a/content/enterprise/2020-02-17-syntax-update.md +++ b/content/enterprise/2020-02-17-syntax-update.md @@ -4,7 +4,7 @@ date = 2020-02-17 template = "post.html" [taxonomies] -tags = ["enterprise", "web", "ui", "syntax"] +tags = ["computers", "web-dev"] +++ [Enterprise][1]'s frontend DSL just got a syntax! Although the major functionality hasn't really changed, I threw out the ugly verbose AST-construction syntax for a hand-rolled recursive-descent-ish parser. diff --git a/content/pages/about.md b/content/pages/about.md index 146b696..5b06847 100644 --- a/content/pages/about.md +++ b/content/pages/about.md @@ -13,8 +13,4 @@ If you want my resume, contact me through one of these means: - Email: (I sign all my Git commits with this email) - PGP Key: [hosted on Keybase][1] -## setup - -My setup can be found [here](/pages/setup). - [1]: https://keybase.io/michaelz/pgp_keys.asc?fingerprint=925ecc02890d5cdae26180d4bda47a31a3c8ee6b diff --git a/content/pages/setup.md b/content/pages/setup.md deleted file mode 100644 index 7f72aca..0000000 --- a/content/pages/setup.md +++ /dev/null @@ -1,88 +0,0 @@ -+++ -title = "my setup" - -[extra] -toc = true -+++ - -## laptop - -I'm using Arch Linux on my personal machine. Here's a neofetch: - -``` -# michael @ kawa in ~ [12:42:16] -$ neofetch - -` michael@kawa - .o+` ------------ - `ooo/ OS: Arch Linux x86_64 - `+oooo: Host: K501UX 1.0 - `+oooooo: Kernel: 5.1.8-arch1-1-ARCH - -+oooooo+: Uptime: 10 hours, 32 mins - `/:-:++oooo+: Packages: 960 (pacman), 242 (nix) - `/++++/+++++++: Shell: zsh 5.7.1 - `/++++++++++++++: Resolution: 1920x1080 - `/+++ooooooooooooo/` Theme: Adwaita [GTK2/3] - ./ooosssso++osssssso+` Icons: Adwaita [GTK2/3] - .oossssso-````/ossssss+` Terminal: alacritty - -osssssso. :ssssssso. CPU: Intel i7-6500U (4) @ 3.100GHz - :osssssss/ osssso+++. GPU: NVIDIA GeForce GTX 950M - /ossssssss/ +ssssooo/- GPU: Intel Skylake GT2 [HD Graphics 520] - `/ossssso+/:- -:/+osssso+- Memory: 3789MiB / 7867MiB - `+sso+:-` `.-/+oso: - `++:. `-/+/ - .` `/ -``` - -My desktop environment is [i3](https://i3wm.org) on X11. I like it because it's lightweight and doesn't use much battery. Even after many years my laptop can still sustain 5-6 hours of prolonged usage. - -### email - -Currently using [ProtonMail](https://protonmail.com/). - -### coding - -I use the trial version of [Sublime Text 3](http://www.sublimetext.com/) on my personal computer, and [neovim](https://neovim.io/) in the terminal. I use the default theme with the [VSCode Dark](https://github.com/nikeee/visual-studio-dark) theme. - -### passwords - -For passwords, I'm using [pass](https://www.passwordstore.org/), which is a GPG-encrypted password store. The passwords are checked into a git repository in order to maintain consistency between multiple devices (I'm using [Android Password Store](https://github.com/zeapo/Android-Password-Store) on my phone). Then, I bind `$mod+p` to a [rofi script][#] so I can access them easily. - -### music - -On my personal computer, I'm using [mpd](https://www.musicpd.org/), the music player daemon along with [Cantata](https://github.com/CDrummond/cantata), which is a Qt frontend. I like using mpd because this also allows me to display my current playing song in my i3 bar. - -### screenshot - -I'm using a [custom screenshot tool][2], written by myself using Rust. The advantage of this over something like scrot or maim would be the ability to first freeze the screen before selecting a region. - -## my phone - -My phone is running the latest version of LineageOS without Google Apps, in a small effort to liberate myself from Google services. Most of the apps that I need notifications from on my phone can contact servers directly without going through Google's Firebase Cloud Messaging, which is where push notifications traditionally go. - -First, here's a list of free software that I use, available from [F-Droid](https://f-droid.org/en/), a free-software app store: - -- [DAVx5](https://f-droid.org/en/packages/at.bitfire.davdroid/). Great for syncing my calendar, contacts, and todo list between my computer and my phone. With a self-hosted CalDAV server, my data is in my hands. -- [DNSFilter](https://f-droid.org/en/packages/dnsfilter.android). Creates a local VPN and selectively blocks requests based on existing blacklists. This actually filters a lot of advertising and tracking data on the regular. -- [Termux](https://f-droid.org/en/packages/com.termux/). It's a terminal on your phone. Why not? -- [Weechat Android](https://f-droid.org/en/packages/com.ubergeek42.WeechatAndroid/). Weechat is an IRC client that can act like a server. With this app, my phone connects to that server and retrieves messages, including sending me notifications for new highlights and such. - -Other software I use include: - -- [Authy](https://authy.com/). Unfortunately, until I figure out my 2-factor backup plan, I'm going to have to stick with Authy since it handles backups well. The long-term solution here is to use backup codes, but I haven't gotten around to sorting that out yet. -- [Firefox](https://www.mozilla.org/en-US/firefox/mobile/). Yes, Firefox is on Android. -- [Signal](https://signal.org/). Encrypted chat that uses phone numbers for identity so you can basically replace SMS with almost no user-interface changes. - -And a slew of other non-free apps that have pretty specific uses, though I think I've crippled my phone to the point where many of those apps are unusable. One of these days I'll go in and purge them again. - -## this website - -The stack for this website looks like: - -- The [source code][1] is written as a set of Gutenberg config files. -- This is then transpiled into static HTML + resources using [Zola](https://getzola.org/), a static site generator written with Rust. -- Changes are deployed using Git hooks. -- Static files are served from a web root using [nginx](https://nginx.org/en/) through a virtual host. -- And here it is! - -[1]: https://git.iptq.io/michael/blog -[2]: https://github.com/iptq/leanshot diff --git a/content/2018-10-18-weechat-relay.md b/content/setup/2018-10-18-weechat-relay.md similarity index 98% rename from content/2018-10-18-weechat-relay.md rename to content/setup/2018-10-18-weechat-relay.md index eaaeef5..6810e99 100644 --- a/content/2018-10-18-weechat-relay.md +++ b/content/setup/2018-10-18-weechat-relay.md @@ -3,7 +3,7 @@ title = "setting up irc with weechat" date = 2018-10-18 [taxonomies] -tags = ["irc", "tutorial", "life", "things-that-are-good"] +tags = ["computers", "irc", "setup", "things-that-are-good"] +++ I've just recently discovered that weechat has a "relay" mode, which means it can act as a relay server to other clients (for example, my phone). If I leave an instance of weechat running on, say, my server that's always running, it can act as a bouncer and my phone can receive notifications for highlights as well. diff --git a/content/setup/2020-05-04-command-line-email-with-aerc/aerc-mail.jpg b/content/setup/2020-05-04-command-line-email-with-aerc/aerc-mail.jpg new file mode 100644 index 0000000..aa5be0f Binary files /dev/null and b/content/setup/2020-05-04-command-line-email-with-aerc/aerc-mail.jpg differ diff --git a/content/setup/2020-05-04-command-line-email-with-aerc/index.md b/content/setup/2020-05-04-command-line-email-with-aerc/index.md new file mode 100644 index 0000000..ee71ec9 --- /dev/null +++ b/content/setup/2020-05-04-command-line-email-with-aerc/index.md @@ -0,0 +1,193 @@ ++++ +title = "command line email with aerc" +date = 2020-05-04 +insert_anchor_links = "left" + +[taxonomies] +tags = ["setup", "email", "protonmail"] + +[extra] +include_posts = true +toc = true ++++ + +I just set up command line email with my new work account and my [ProtonMail][1] account today! This article will be covering the full setup. + +![aerc screenshot](aerc-mail.jpg) + +## setting up proton-bridge + +ProtonMail is an email service that end-to-end encrypts its users' emails with PGP. As a result, it doesn't speak SMTP or IMAP directly, since email clients wouldn't know how to undo the encryption anyway. That's why they've provided [proton-bridge][2], a ([now][3]) open-source SMTP/IMAP server that translates their own API calls into SMTP/IMAP for your email clients. For security, it's best to run this locally, so that emails aren't exposed over the network after they're decrypted. + +For my setup, I have proton-bridge running as a systemd service. That means we have to deal with any interactive parts of the service so they're not popping up anymore. + +Firstly, we want to build the bridge without support for the GUI. We won't be using it anyway, so this eliminates the Qt dependency. + +Secondly, proton-bridge stores keys in an encrypted keyring, like password-store. My regular password-store is encrypted with my passphrase-protected GPG key, so I didn't want to use it since it'll be asking me for the passphrase again every time the timeout expires. We're going to make a separate GPG and password-store setup that will only be used for proton-bridge. Since it's all running locally anyway, we're _not_ to use a passphrase on this GPG key. + +Authenticating only happens once, and the local SMTP/IMAP password doesn't change very often, so we won't really care about that. We'll bundle this up into a couple of nice scripts and then have it configured to start on startup! + +### building proton-bridge + +To build proton-bridge without the GUI, we'll need to grab a copy of the [source][2]. Clone the repo and then change directory to it: + +```bash +git clone https://github.com/protonmail/proton-bridge +cd proton-bridge +``` + +As of now, there's no tagged releases, so let's just build straight from master. Peeking into the Makefile, there isn't a release build for the nogui option, so let's add it ourselves. Put this somewhere in the Makefile: + +```make +build-nogui: + PROTONMAIL_ENV= go build ${BUILD_FLAGS_NOGUI} ./cmd/Desktop-Bridge +``` + +Then run `make build-nogui` and you should get a binary called `Desktop-Bridge`. Don't authenticate just quite yet; we want to set up the keychain first so it stores it in the right place. + +### isolating the keychain + +So for this section, I created two directories: the directory for the new GPG homedir, and the directory for the new password-store. If you're copy-pasting commands out of this post, I'd recommend you add these variables right now: + +```bash +export PASSWORD_STORE_DIR=/path/to/password/store +export GNUPGHOME=/path/to/gpg/home +``` + +...obviously replacing the paths with paths that you choose. These variables are used by `pass` and `gpg` to overwrite the default directories they use, so it's important to set them up. The next step is to make sure they both exist: + +```bash +mkdir -p $PASSWORD_STORE_DIR $GNUPGHOME +``` + +Now initialize the GPG key first. Run: + +```bash +gpg --full-generate-key +``` + +There should be an interactive prompt. Go through and answer the questions however you see fit. I'd recommend you make the key 4096 bits, never expire, and have no passphrase. This key should never leave your local machine, and you will almost never use it directly, so there should be no problem. + +Then, set up password-store. Run: + +```bash +pass init [gpg-id] +``` + +where `[gpg-id]` is some identifier for the key you just created (name or email works). + +At this point, you should test your configuration by running the `Desktop-Bridge` program with the `-c` option to open the prompt. Make sure you are able to log in and that rerunning the program should automatically run the bridge using the authenticated user without any interactive prompts. + +``` + Welcome to ProtonMail Bridge interactive shell + ___....___ + ^^ __..-:'':__:..:__:'':-..__ + _.-:__:.-:'': : : :'':-.:__:-._ + .':.-: : : : : : : : : :._:'. + _ :.': : : : : : : : : : : :'.: _ + [ ]: : : : : : : : : : : : : :[ ] + [ ]: : : : : : : : : : : : : :[ ] + :::::::::[ ]:__:__:__:__:__:__:__:__:__:__:__:__:__:[ ]::::::::::: + !!!!!!!!![ ]!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!![ ]!!!!!!!!!!! + ^^^^^^^^^[ ]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[ ]^^^^^^^^^^^ + [ ] [ ] + [ ] [ ] + jgs [ ] [ ] + ~~^_~^~/ \~^-~^~ _~^-~_^~-^~_^~~-^~_~^~-~_~-^~_^/ \~^ ~~_ ^ +>>> login +Username: iptq +Password: +Authenticating ... +Adding account ... +``` + +### automating the whole process + +Now that the bridge is able to run independently, let's make a tiny wrapper script called `run-proton-bridge.sh` that calls it using the appropriate environment variables: + +```bash +#!/bin/bash + +# same as before +export PASSWORD_STORE_DIR=/path/to/password/store +export GNUPGHOME=/path/to/gpg/home + +exec /path/to/Desktop-Bridge $@ +``` + +Save this somewhere, give it executable permissions. + +Finally, we can add a systemd service that runs this whole business whenever the network is available. I'd recommend adding this as a user service rather than a system service. You can put this somewhere like `$HOME/.config/systemd/user/proton-bridge.service`: + +```systemd +[Unit] +After=network.target + +[Service] +Restart=always +ExecStart=/path/to/run-proton-bridge.sh + +[Install] +WantedBy=default.target +``` + +Run this service: + +``` +systemctl --user start proton-bridge +``` + +Enable it to have it auto-start: + +``` +systemctl --user enable proton-bridge +``` + +Bridge configuration should be complete at this point, so let's move on to configuring our mail client to work with it. + +## setting up aerc + +[aerc][4] is a new (**work-in-progress**) command-line mail client by Drew Devault. It features a familiar set of default keybinds, calls out to your favorite editor for composition, and DWIM for many other things. I just started using it today and had no problem getting used to the interface. + +The setup for basic SMTP/IMAP email accounts is actually pretty trivial. When you run aerc for the first time, or whenever you run the command `:new-account`, an interactive screen is brought up prompting you for the details about your mailbox. If your mail provider doesn't work immediately, jump into `#aerc` on freenode; the folks there are super helpful with different mail providers. + +### setting up aerc.. _with_ protonmail + +If you've been keeping up, then all the pieces should be in place for aerc to work with ProtonMail. There's just a tiny bit of glue we have to add to put it all together. + +**aerc does not allow untrusted certificates**. Since proton-bridge generates a self-signed cert, we'll need to trust this cert before we can do anything. There's not really an easy way to pull the certificate out, so I'd recommend just firing up the bridge and then connecting to it using the openssl client and then copy-pasting the certificate part: + +``` +$ openssl s_client -starttls imap -connect 127.0.0.1:1143 -showcerts +CONNECTED(00000003) +Can't use SSL_get_servername +depth=0 C = CH, O = Proton Technologies AG, OU = ProtonMail, CN = 127.0.0.1 +verify return:1 +--- +Certificate chain + 0 s:C = CH, O = Proton Technologies AG, OU = ProtonMail, CN = 127.0.0.1 + i:C = CH, O = Proton Technologies AG, OU = ProtonMail, CN = 127.0.0.1 +-----BEGIN CERTIFICATE----- +... +-----END CERTIFICATE----- +--- +Server certificate +subject=C = CH, O = Proton Technologies AG, OU = ProtonMail, CN = 127.0.0.1 +issuer=C = CH, O = Proton Technologies AG, OU = ProtonMail, CN = 127.0.0.1 +... +``` + +Take that huge chunk starting with the line `BEGIN CERTIFICATE` and ending with the line `END CERTIFICATE` and stick it into some file (ex. `protonmail.crt`). This is the self-signed cert we need to trust. + +Now we'll need to tell our system to allow this cert. This varies from system to system; I'm on an Arch machine, so I ran: + +``` +sudo trust anchor --store protonmail.crt +``` + +After this, fire up `aerc` again, and your emails should start showing up. + +[1]: https://protonmail.com/ +[2]: https://github.com/ProtonMail/proton-bridge +[3]: https://protonmail.com/blog/bridge-open-source/ +[4]: https://aerc-mail.org/ diff --git a/content/setup/_index.md b/content/setup/_index.md new file mode 100644 index 0000000..8f199a2 --- /dev/null +++ b/content/setup/_index.md @@ -0,0 +1,11 @@ ++++ +template = "blog.html" +insert_anchor_links = "left" + +[extra] +include_posts = true ++++ + +# setup + +These posts are tutorial-style articles for setting things up. diff --git a/sass/_content.scss b/sass/_content.scss index ef38499..52f20a2 100644 --- a/sass/_content.scss +++ b/sass/_content.scss @@ -1,5 +1,5 @@ body { - max-width: 850px; + max-width: 980px; margin: auto; min-height: 100%; padding-bottom: 20px; @@ -67,6 +67,10 @@ blockquote { #content { font-size: 1.05em; line-height: 1.5em; + + img { + max-width: 100%; + } } pre { diff --git a/templates/blog.html b/templates/blog.html index 09810af..8afb061 100644 --- a/templates/blog.html +++ b/templates/blog.html @@ -8,6 +8,8 @@ {% endblock %} {% block content %} + {{ section.content | safe }} + {{ blog::sectionlisting(section=section) }}

diff --git a/templates/layout.html b/templates/layout.html index 91e6f3e..fefab7a 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -8,6 +8,8 @@ {% block title %}{% endblock %} + + {% block headext %}{% endblock %} diff --git a/templates/post.html b/templates/post.html index a23857f..65c1b43 100644 --- a/templates/post.html +++ b/templates/post.html @@ -19,12 +19,24 @@ {% endif %} - {% if page.extra.toc and toc | length %} + {% if page.extra.toc and page.toc | length %}

table of contents - {{ post::render_toc(toc=toc) }} + {{ post::render_toc(toc=page.toc) }}
{% endif %}
{{ page.content | safe }}
+ +
+ + End. + {% if page.taxonomies.tags %} + This post is tagged with: + {% for tag in page.taxonomies.tags %} + {% if loop.index0 > 0 %}, {% endif %} + {{ tag }} + {% endfor %} + {% endif %} + {% endblock %}