Files
andwati.com/templates/posts.html
T
andwati e992baf2d2 Migrate templates to Zola 0.23 / Tera v2
bin/update-zola always installs the latest release, which as of
v0.23.0 rewrote the template engine to Tera v2. That broke the whole
site: removed filters (slice, concat, escape), test calls now require
named args, shortcodes are gone in favor of Tera components, and
content itself is now Tera-templated before markdown runs.

- slice(end=n) -> native [:n] slicing; concat(with=x) -> spread [...arr, x]
- escape -> escape_html; starting_with/containing -> named `pat` arg
- get_taxonomy_url `name` -> `term` (old name is deprecated)
- %+ date format no longer recognized; replaced with explicit RFC3339 pattern
- shortcodes (note/tip/warning/danger/youtube) rewritten as components;
  content call sites updated to component invocation syntax
- {% include %} no longer inherits for-loop-local variables (only ambient
  page/section context) - post-card.html converted to a component that
  takes page/index explicitly instead of relying on implicit `page`/`loop`
- two content code blocks with literal Jinja-style {{ }} syntax (Netlify
  CMS config, a Flask template snippet) wrapped in {% raw %} so Tera
  doesn't try to render them, since content is now templated too
- fixed a real content corruption bug along the way: the youtube
  shortcode's multi-line <iframe> tag had a conditional that could
  collapse to a whitespace-only line, which prematurely closed the
  HTML block under CommonMark rules and got the rest of the tag
  syntax-highlighted as code instead of rendered

Verified with a clean `zola build` and `zola serve` on v0.23.1, plus a
spot-check of the rendered HTML for both callouts and the video embed.
`zola check` only reports pre-existing external link rot, unrelated to
this change.
2026-08-07 10:46:09 +03:00

189 lines
8.0 KiB
HTML

{% extends "base.html" %} {% block title %}{{ config.extra.posts_page_title | default(value="Posts") }}{% if paginator.current_index > 1 %} — Page {{
paginator.current_index }}{% endif %} | {{ config.title }}{% endblock title %} {% block description
%}{{ config.extra.posts_page_subtitle | default(value="Exploring binary exploitation, reverse engineering, and offensive security.") }}{% endblock description %} {% block canonical %}{% if paginator.current_index == 1 %}{{
config.base_url }}/posts/{% else %}{{ paginator.first }}page/{{ paginator.current_index }}/{% endif
%}{% endblock canonical %} {% block og_title %}{{ config.extra.posts_page_title | default(value="Posts") }}{% if paginator.current_index > 1 %} — Page {{
paginator.current_index }}{% endif %} | {{ config.title }}{% endblock og_title %} {% block
og_description %}{{ config.extra.posts_page_subtitle | default(value="Exploring binary exploitation, reverse engineering, and offensive security.") }}{% endblock og_description %} {% block og_url %}{% if paginator.current_index
== 1 %}{{ config.base_url }}/posts/{% else %}{{ paginator.first }}page/{{ paginator.current_index
}}/{% endif %}{% endblock og_url %} {% block head %}
{% set posts_page_title = config.extra.posts_page_title | default(value="Posts") %}
{% set posts_page_subtitle = config.extra.posts_page_subtitle | default(value="Exploring binary exploitation, reverse engineering, and offensive security.") %}
{% set posts_ld_name = posts_page_title ~ " | " ~ config.title %}
<!-- SEO: CollectionPage JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "CollectionPage",
"@id": "{{ config.base_url | safe }}/posts/#collection",
"name": {{ posts_ld_name | json_encode | safe }},
"description": {{ posts_page_subtitle | json_encode | safe }},
"url": "{{ config.base_url | safe }}/posts/",
"inLanguage": "en-US",
"isPartOf": {
"@type": "WebSite",
"@id": "{{ config.base_url | safe }}/#website"
},
"hasPart": [
{% for page in paginator.pages %}
{
"@type": "BlogPosting",
"@id": "{{ page.permalink | safe }}#article",
"headline": {{ page.title | json_encode | safe }},
"image": "{{ config.base_url | safe }}{% if page.extra.image %}{{ page.extra.image | safe }}{% else %}/images/og-image.png{% endif %}",
"url": "{{ page.permalink | safe }}",
"author": {
"@type": "Person",
"@id": "{{ config.base_url | safe }}/#author"
}
{% if page.date %},"datePublished": "{{ page.date | date(format="%Y-%m-%dT%H:%M:%S%:z") }}"{% endif %}
{% if page.updated %},"dateModified": "{{ page.updated | date(format="%Y-%m-%dT%H:%M:%S%:z") }}"{% elif page.date %},"dateModified": "{{ page.date | date(format="%Y-%m-%dT%H:%M:%S%:z") }}"{% endif %}
}{% if not loop.last %},{% endif %}
{% endfor %}
]
}
</script>
<!-- SEO: BreadcrumbList JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "{{ config.base_url | safe }}/" },
{ "@type": "ListItem", "position": 2, "name": {{ posts_page_title | json_encode | safe }}, "item": "{{ config.base_url | safe }}/posts/" }
]
}
</script>
<!-- Pagination link hints for crawlers -->
{% if paginator.previous %}<link rel="prev" href="{{ paginator.previous }}" />{% endif %} {% if
paginator.next %}<link rel="next" href="{{ paginator.next }}" />{% endif %} {% endblock head %} {%
block content %}
<div class="feed-page">
<div class="hero hero-compact">
<div class="hero-content">
<h1 class="hero-title">{{ config.extra.posts_page_title | default(value="Posts") }}</h1>
<p class="hero-subtitle">
{{ config.extra.posts_page_subtitle | default(value="Exploring binary exploitation, reverse engineering, and offensive security.") }}
</p>
</div>
</div>
<div class="post-feed">
{% for page in paginator.pages %}
<article class="feed-item hover-scale">
<a href="{{ page.permalink }}" class="item-link">
<div class="item-date-row">
<time datetime="{{ page.date }}" class="item-date"
>{{ page.date | date(format="%b %d, %Y") }}</time
>
{% if page.reading_time %}
<span class="item-meta-sep">&middot;</span>
<span class="item-reading-time">&sim;{{ page.reading_time }} min read</span>
{% endif %}
</div>
<h2 class="item-title">{{ page.title }}</h2>
{% if page.description %}
<p class="item-desc">{{ page.description }}</p>
{% endif %}
</a>
</article>
{% endfor %}
</div>
{% if paginator.number_pagers > 1 %}
<nav class="pagination" aria-label="Posts pagination">
{# ← Previous #} {% if paginator.previous %}
<a class="pagination-arrow" href="{{ paginator.previous }}" aria-label="Previous page">
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6"></polyline>
</svg>
</a>
{% else %}
<span class="pagination-arrow is-disabled" aria-disabled="true">
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6"></polyline>
</svg>
</span>
{% endif %} {# Page numbers with smart windowing #}
<div class="pagination-pages">
{% set cur = paginator.current_index %} {% set total = paginator.number_pagers %} {% for i in
range(start=1, end=total+1) %} {% set show = false %} {# Always show first and last #} {% if i
== 1 or i == total %}{% set show = true %}{% endif %} {# Show window of 2 around current #} {%
if i >= cur - 2 and i <= cur + 2 %}{% set show = true %}{% endif %} {% if show %} {# Show
ellipsis before this page if there's a gap #} {% if i > 1 and i > cur - 2 and i != 2 %} {# We
need gap indicator — output ellipsis if prev shown page was not adjacent #} {% if i == cur - 2
and cur - 3 > 1 %}
<span class="pagination-ellipsis">…</span>
{% elif i == total and cur + 2 < total - 1 %}
<span class="pagination-ellipsis">…</span>
{% endif %} {% endif %} {% if i == cur %}
<span class="pagination-num is-current" aria-current="page">{{ i }}</span>
{% else %} {% if i == 1 %}
<a class="pagination-num" href="{{ paginator.first }}" aria-label="Page {{ i }}">{{ i }}</a>
{% elif i == total %}
<a class="pagination-num" href="{{ paginator.last }}" aria-label="Page {{ i }}">{{ i }}</a>
{% else %}
<a class="pagination-num" href="{{ paginator.first }}page/{{ i }}/" aria-label="Page {{ i }}"
>{{ i }}</a
>
{% endif %} {% endif %} {% endif %} {% endfor %}
</div>
{# Next → #} {% if paginator.next %}
<a class="pagination-arrow" href="{{ paginator.next }}" aria-label="Next page">
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</a>
{% else %}
<span class="pagination-arrow is-disabled" aria-disabled="true">
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</span>
{% endif %}
</nav>
<p class="pagination-meta">Page {{ paginator.current_index }} of {{ paginator.number_pagers }}</p>
{% endif %}
</div>
{% endblock content %}