After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 26 KiB |
@ -0,0 +1,424 @@
|
|||||||
|
+++
|
||||||
|
title = "Guia completa sobre sèries"
|
||||||
|
date = 2024-11-08
|
||||||
|
description = "Aprèn a organitzar les teves publicacions en sèries seqüencials, perfectes per a tutorials, cursos i històries de diverses parts."
|
||||||
|
|
||||||
|
[taxonomies]
|
||||||
|
tags = ["funcionalitat", "tutorial", "preguntes freqüents", "sèries"]
|
||||||
|
|
||||||
|
[extra]
|
||||||
|
quick_navigation_buttons = true
|
||||||
|
toc = true
|
||||||
|
mermaid = true
|
||||||
|
social_media_card = "social_cards/ca_blog_series.jpg"
|
||||||
|
+++
|
||||||
|
|
||||||
|
Una sèrie organitza publicacions relacionades en ordre seqüencial, similar als capítols d'un llibre. A diferència de les etiquetes, que simplement agrupen contingut relacionat, les sèries suggereixen un ordre específic de lectura de principi a fi.
|
||||||
|
|
||||||
|
Les publicacions dins d'una sèrie no necessiten publicar-se de forma consecutiva; la funció de sèries reuneix publicacions temàticament vinculades en una seqüència coherent.
|
||||||
|
|
||||||
|
El següent diagrama il·lustra com les publicacions de la sèrie (3, 5 i 8) existeixen dins del flux principal del blog mentre mantenen la seva pròpia seqüència ordenada dins de Sèrie 1.
|
||||||
|
|
||||||
|
{% mermaid(full_width=true) %}
|
||||||
|
flowchart
|
||||||
|
subgraph main[BLOG]
|
||||||
|
P1[Post 1]
|
||||||
|
P2[P2]
|
||||||
|
P3[P3]
|
||||||
|
P4[P4]
|
||||||
|
P5[P5]
|
||||||
|
P6[P6]
|
||||||
|
P7[P7]
|
||||||
|
P8[P8]
|
||||||
|
P9[P9]
|
||||||
|
end
|
||||||
|
subgraph series1[SÈRIE 1]
|
||||||
|
PS1["Post Sèrie 1 (=P3)"]
|
||||||
|
PS2["Post Sèrie 2 (=P5)"]
|
||||||
|
PS3["Post Sèrie 3 (=P8)"]
|
||||||
|
end
|
||||||
|
P3 o-.-o PS1
|
||||||
|
P5 o-.-o PS2
|
||||||
|
P8 o-.-o PS3
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
## Inici ràpid
|
||||||
|
|
||||||
|
1. Crea un directori per a la teva sèrie
|
||||||
|
2. Crea `_index.md` al directori de la sèrie
|
||||||
|
3. Configura el front matter de `_index.md`:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
title = "Aprenent Rust"
|
||||||
|
template = "series.html"
|
||||||
|
sort_by = "slug"
|
||||||
|
transparent = true
|
||||||
|
|
||||||
|
[extra]
|
||||||
|
series = true
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Crea els teus articles de la sèrie en aquest directori
|
||||||
|
|
||||||
|
Vols saber-ne més? Continua llegint!
|
||||||
|
|
||||||
|
## Com funcionen les sèries?
|
||||||
|
|
||||||
|
Una sèrie és simplement una secció que tabi gestiona de manera especial. Per a més detalls sobre seccions, consulta la [documentació de Zola](https://www.getzola.org/documentation/content/section/).
|
||||||
|
|
||||||
|
Prenent l'exemple del diagrama anterior, l'estructura de directoris seria així:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
content/
|
||||||
|
_index.md
|
||||||
|
blog/
|
||||||
|
_index.md
|
||||||
|
post1/
|
||||||
|
index.md
|
||||||
|
post2/
|
||||||
|
index.md
|
||||||
|
post4/
|
||||||
|
index.md
|
||||||
|
post6/
|
||||||
|
index.md
|
||||||
|
post7/
|
||||||
|
index.md
|
||||||
|
post9/
|
||||||
|
index.md
|
||||||
|
serie1/
|
||||||
|
_index.md
|
||||||
|
post3/
|
||||||
|
index.md
|
||||||
|
post5/
|
||||||
|
index.md
|
||||||
|
post8/
|
||||||
|
index.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Per crear una sèrie, necessites:
|
||||||
|
|
||||||
|
1. Utilitzar la plantilla `series.html`
|
||||||
|
2. Establir `series = true` a la configuració `[extra]` de la secció
|
||||||
|
3. Activar `transparent = true` per integrar les publicacions de la sèrie amb la secció del blog principal
|
||||||
|
|
||||||
|
La pàgina principal de la sèrie mostra un resum seguit d'una llista de totes les publicacions a la sèrie:
|
||||||
|
|
||||||
|
{{ dual_theme_image(light_src="blog/series/img/series_light.webp", dark_src="blog/series/img/series_dark.webp" alt="una sèrie", full_width=true) }}
|
||||||
|
|
||||||
|
## Saltar a les publicacions
|
||||||
|
|
||||||
|
Si el contingut d'una sèrie (el Markdown després del frontmatter a `_index.md`) supera els 2000 caràcters, apareix un enllaç "Salta a les publicacions" al costat del títol de la sèrie.
|
||||||
|
|
||||||
|
{{ dual_theme_image(light_src="blog/series/img/jump_to_series_posts_light.webp", dark_src="blog/series/img/jump_to_series_posts_dark.webp" alt="enllaç per saltar a les publicacions de la sèrie", full_width=true) }}
|
||||||
|
|
||||||
|
Per forçar l'activació o desactivació d'aquesta funció, configura `show_jump_to_posts` a la secció `[extra]` de la teva secció de sèries o a `config.toml`. Aquesta configuració segueix [la jerarquia](@/blog/mastering-tabi-settings/index.ca.md#jerarquia-de-configuracio).
|
||||||
|
|
||||||
|
## Pàgines de sèries i ordre
|
||||||
|
|
||||||
|
Totes les pàgines a la secció de sèries seran pàgines de sèrie. Les pàgines s'ordenaran segons el `sort_by` de la secció.
|
||||||
|
|
||||||
|
Tot i que les sèries mantenen el seu propi ordre intern, romanen independents del flux cronològic de la secció principal (per exemple, `blog/`) gràcies a la configuració `transparent`.
|
||||||
|
|
||||||
|
### Opcions d'ordre
|
||||||
|
|
||||||
|
Tria entre aquests mètodes d'ordre, cadascun amb els seus avantatges:
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
`sort_by` | avantatges | desavantatges
|
||||||
|
---------|------------|---------------
|
||||||
|
`slug` | L'ordre de les pàgines és explícit a la ruta (per exemple, `example.com/blog/series1/01-series-post-un`). | Cada pàgina de la sèrie ha de tenir el prefix corresponent.
|
||||||
|
`weight` | L'ordre de les pàgines és fàcil de configurar de forma transparent.<br>La primera publicació té pes `1`, la segona pes `2` i així successivament. | Cada pàgina de la sèrie ha de tenir el seu pes configurat.
|
||||||
|
`date` | L'ordre de les pàgines es pot configurar una sola vegada a la configuració de la secció. No cal fer res a cada pàgina. | L'ordre de les pàgines s'ha d'invertir perquè la primera pàgina sol ser la més antiga. Això només es pot aconseguir paginant la secció (`paginate_by = 9999`) i invertint el seu ordre (`paginate_reversed = true`).
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{{ admonition(type="danger", title="Versió de Zola per ordenar per data", text="Per invertir correctament les dates, es requereix Zola v0.19.3+ (no publicada) perquè la informació de paginació estigui disponible a través de la funció `get_section`. En cas contrari, qualsevol cosa que depengui de l'ordre de les pàgines de la sèrie no serà correcta (per exemple, pàgina anterior/següent, llistes ordenades i no ordenades...) Vegeu [Zola PR #2653](https://github.com/getzola/zola/pull/2653).") }}
|
||||||
|
|
||||||
|
### Indexació de pàgines
|
||||||
|
|
||||||
|
Les pàgines en una sèrie s'indexen començant des d'1, seguint el seu ordre `sort_by`. Per invertir la indexació (fent que la primera pàgina tingui l'índex més alt), afegeix aquesta configuració a `_index.md` o `config.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra]
|
||||||
|
post_listing_index_reversed = true # Per defecte és false si no es configura
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ dual_theme_image(light_src="blog/series/img/series_reversed_light.webp", dark_src="blog/series/img/series_reversed_dark.webp" alt="una sèrie amb índexs invertits", full_width=true) }}
|
||||||
|
|
||||||
|
Aquesta configuració segueix [la jerarquia](@/blog/mastering-tabi-settings/index.ca.md#jerarquia-de-configuracio).
|
||||||
|
|
||||||
|
## Plantilles d'introducció i conclusió
|
||||||
|
|
||||||
|
Els articles d'una sèrie poden tenir seccions automàtiques d'introducció i conclusió. Aquestes es configuren al `_index.md` de la teva sèrie. Un exemple bàsic:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
default = "Aquest article és part de la sèrie $SERIES_HTML_LINK."
|
||||||
|
|
||||||
|
[extra.series_outro_templates]
|
||||||
|
default = "Gràcies per llegir la part $SERIES_PAGE_INDEX de $SERIES_HTML_LINK!"
|
||||||
|
```
|
||||||
|
|
||||||
|
Les seccions d'introducció i conclusió tenen les seves pròpies classes CSS (`series-page-intro` i `series-page-outro`), que et permeten personalitzar la seva aparença mitjançant [CSS personalitzat](@/blog/mastering-tabi-settings/index.ca.md#estils-css-personalitzats).
|
||||||
|
|
||||||
|
### Tipus de plantilles
|
||||||
|
|
||||||
|
El sistema de sèries utilitza diferents plantilles segons la posició de l'article a la sèrie:
|
||||||
|
|
||||||
|
- `next_only` - Utilitzat per al primer article (té article següent però no anterior)
|
||||||
|
- `middle` - Utilitzat per a articles amb articles anterior i següent
|
||||||
|
- `prev_only` - Utilitzat per a l'últim article (té article anterior però no següent)
|
||||||
|
- `default` - Plantilla per defecte utilitzada quan no existeix una plantilla específica per a la posició
|
||||||
|
|
||||||
|
El sistema determina automàticament quina plantilla utilitzar segons la posició de l'article. Les plantilles es defineixen a la configuració de la sèrie (`_index.md`), com `extra.series_intro_templates` i `extra.series_outro_templates`:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
next_only = "Benvingut a la part 1! Següent: $NEXT_HTML_LINK"
|
||||||
|
middle = "Anterior: $PREV_HTML_LINK | Següent: $NEXT_HTML_LINK"
|
||||||
|
prev_only = "El capítol final! Anteriorment: $PREV_HTML_LINK"
|
||||||
|
default = "Part $SERIES_PAGE_INDEX de $SERIES_PAGES_NUMBER"
|
||||||
|
```
|
||||||
|
|
||||||
|
Totes les plantilles són opcionals. La selecció de plantilles segueix un sistema de prioritat:
|
||||||
|
|
||||||
|
1. Si existeix una plantilla específica per a la posició (`next_only`, `middle`, o `prev_only`), s'utilitzarà aquesta
|
||||||
|
2. Si no, s'utilitza la plantilla `default`
|
||||||
|
3. Si no es defineix cap plantilla, no es mostrarà informació de la sèrie
|
||||||
|
|
||||||
|
Mira l'[exemple de plantilla](#exemple-de-plantilla) per veure un exemple més elaborat.
|
||||||
|
|
||||||
|
### Ubicació al contingut
|
||||||
|
|
||||||
|
Per defecte:
|
||||||
|
|
||||||
|
- Les introduccions de sèrie apareixen a l'inici del teu article
|
||||||
|
- La conclusió apareix al final (abans de les notes al peu, si n'hi ha)
|
||||||
|
|
||||||
|
Pots controlar exactament on apareixen utilitzant `<!-- series_intro -->` i `<!-- series_outro -->` al teu Markdown:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Aquest paràgraf apareix abans de la introducció de la sèrie.
|
||||||
|
|
||||||
|
<!-- series_intro -->
|
||||||
|
|
||||||
|
Contingut principal de l'article.
|
||||||
|
|
||||||
|
<!-- series_outro -->
|
||||||
|
|
||||||
|
## Recursos d'aprenentatge
|
||||||
|
|
||||||
|
Contingut addicional...
|
||||||
|
|
||||||
|
[^1]: Les notes al peu sempre apareixeran al final.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
Les plantilles de sèries utilitzen un sistema flexible de variables que et permet:
|
||||||
|
|
||||||
|
1. Fer referència a informació de la sèrie (títol, enllaços)
|
||||||
|
2. Afegir navegació entre articles
|
||||||
|
3. Mostrar indicadors de progrés
|
||||||
|
4. Incloure informació personalitzada utilitzant les teves pròpies variables
|
||||||
|
|
||||||
|
Les variables són marcadors que comencen amb `$` i es reemplacen amb contingut real quan es construeix el teu lloc. Per exemple, `$SERIES_HTML_LINK` es converteix en un enllaç clicable a la pàgina índex de la teva sèrie.
|
||||||
|
|
||||||
|
Hi ha tres tipus de variables:
|
||||||
|
|
||||||
|
- [Variables bàsiques de sèrie](#variables-basiques-de-serie): Informació general sobre la sèrie
|
||||||
|
- [Variables de navegació](#variables-de-navegacio): Enllaços a articles anterior/següent
|
||||||
|
- [Variables personalitzades](#variables-personalitzades): Els teus propis marcadors per a informació addicional
|
||||||
|
|
||||||
|
### Variables bàsiques de sèrie
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
| Variable | Disponibilitat | Retorna | Descripció | Exemple d'ús | Exemple de sortida |
|
||||||
|
|----------|---------------|----------|------------|--------------|-------------------|
|
||||||
|
| `$SERIES_TITLE` | Sempre | Text | Títol de la sèrie en text pla | `Part de $SERIES_TITLE` | Part d'Aprenent Rust |
|
||||||
|
| `$SERIES_PERMALINK` | Sempre | Text | URL a l'índex de la sèrie | `[Veure totes les publicacions]($SERIES_PERMALINK)` | [Veure totes les publicacions](/series/learn-rust) |
|
||||||
|
| `$SERIES_HTML_LINK` | Sempre | HTML | Enllaç llest per usar a la sèrie | `Benvingut a $SERIES_HTML_LINK!` | Benvingut a <a href="/series/learn-rust">Aprenent Rust</a>! |
|
||||||
|
| `$SERIES_PAGES_NUMBER` | Sempre | Nombre | Total d'articles a la sèrie | `Una sèrie de $SERIES_PAGES_NUMBER parts` | Una sèrie de 5 parts |
|
||||||
|
| `$SERIES_PAGE_INDEX` | Sempre | Nombre | Posició de l'article actual | `Part $SERIES_PAGE_INDEX de $SERIES_PAGES_NUMBER` | Part 3 de 5 |
|
||||||
|
| `$SERIES_PAGES_OLIST` | Sempre | HTML | Llista ordenada de tots els articles | `Articles a la sèrie: $SERIES_PAGES_OLIST` | Articles a la sèrie: <ol><li>Article actual</li><li><a href="...">Altres articles</a></li></ol> |
|
||||||
|
| `$SERIES_PAGES_ULIST` | Sempre | HTML | Llista desordenada de tots els articles | `Articles a la sèrie: $SERIES_PAGES_ULIST` | Articles a la sèrie: <ul><li>Article actual</li><li><a href="...">Altres articles</a></li></ul> |
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
### Variables de navegació
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
| Variable | Disponibilitat | Retorna | Descripció | Exemple d'ús | Exemple de sortida |
|
||||||
|
|----------|---------------|----------|------------|--------------|-------------------|
|
||||||
|
| `$PREV_TITLE` | Existeix anterior | Text | Títol de l'article anterior | `Anteriorment: $PREV_TITLE` | Anteriorment: Configurant el teu entorn |
|
||||||
|
| `$PREV_PERMALINK` | Existeix anterior | Text | URL a l'article anterior | `[← Enrere]($PREV_PERMALINK)` | [← Enrere](/series/learn-rust/setup) |
|
||||||
|
| `$PREV_HTML_LINK` | Existeix anterior | HTML | Enllaç llest per usar a l'anterior | `Llegeix primer $PREV_HTML_LINK` | Llegeix primer <a href="/series/learn-rust/setup">Configurant el teu entorn</a> |
|
||||||
|
| `$PREV_DESCRIPTION` | Existeix anterior | Text | Descripció de l'article anterior | `Resum: $PREV_DESCRIPTION` | Resum: Configurant Rust |
|
||||||
|
| `$NEXT_TITLE` | Existeix següent | Text | Títol del següent article | `Següent: $NEXT_TITLE` | Següent: Patrons avançats |
|
||||||
|
| `$NEXT_PERMALINK` | Existeix següent | Text | URL al següent article | `[Continuar →]($NEXT_PERMALINK)` | [Continuar →](/series/learn-rust/patterns) |
|
||||||
|
| `$NEXT_HTML_LINK` | Existeix següent | HTML | Enllaç llest per usar al següent | `Continua amb $NEXT_HTML_LINK` | Continua amb <a href="/series/learn-rust/patterns">Patrons avançats</a> |
|
||||||
|
| `$NEXT_DESCRIPTION` | Existeix següent | Text | Descripció del següent article | `Properament: $NEXT_DESCRIPTION` | Properament: Aprèn sobre les característiques avançades de pattern matching en Rust |
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
### Referència al primer article
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
| Variable | Disponibilitat | Retorna | Descripció | Exemple d'ús | Exemple de sortida |
|
||||||
|
|----------|---------------|----------|------------|--------------|-------------------|
|
||||||
|
| `$FIRST_TITLE` | Sempre | Text | Títol del primer article | `Comença amb $FIRST_TITLE` | Comença amb Introducció a Rust |
|
||||||
|
| `$FIRST_HTML_LINK` | Sempre | HTML | Enllaç llest per usar al primer article | `Comença a $FIRST_HTML_LINK` | Comença a <a href="/series/learn-rust/intro">Introducció a Rust</a> |
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
### Exemple de plantilla
|
||||||
|
|
||||||
|
{{ admonition(type="tip", title="Variables HTML vs text", text="Utilitza variables HTML (que acaben en `_HTML_LINK`) quan vulguis enllaços preparats per usar. Utilitza variables de text (que acaben en `_TITLE` o `_PERMALINK`) quan vulguis més control sobre el format.") }}
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Introducció
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
next_only = """
|
||||||
|
Benvingut a $SERIES_HTML_LINK! Aquesta sèrie de $SERIES_PAGES_NUMBER parts t'ensenyarà Rust des de zero.
|
||||||
|
|
||||||
|
Següent: $NEXT_HTML_LINK - $NEXT_DESCRIPTION
|
||||||
|
"""
|
||||||
|
|
||||||
|
middle = """
|
||||||
|
📚 Part $SERIES_PAGE_INDEX de $SERIES_PAGES_NUMBER a $SERIES_HTML_LINK
|
||||||
|
|
||||||
|
Anterior: $PREV_HTML_LINK
|
||||||
|
Següent: $NEXT_HTML_LINK
|
||||||
|
"""
|
||||||
|
|
||||||
|
prev_only = """
|
||||||
|
Benvingut a l'última part de $SERIES_HTML_LINK!
|
||||||
|
Ets nou? Comença amb $FIRST_HTML_LINK per construir una base sòlida.
|
||||||
|
|
||||||
|
Anterior: $PREV_HTML_LINK
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Plantilla de respatller
|
||||||
|
default = "Aquest article és part de la sèrie $SERIES_HTML_LINK."
|
||||||
|
|
||||||
|
# Conclusió
|
||||||
|
[extra.series_outro_templates]
|
||||||
|
next_only = """
|
||||||
|
Gràcies per llegir! 🙌
|
||||||
|
|
||||||
|
Continua el teu viatge amb $NEXT_HTML_LINK, on $NEXT_DESCRIPTION
|
||||||
|
O revisa l'esquema complet de la sèrie [$SERIES_TITLE]($SERIES_PERMALINK).
|
||||||
|
"""
|
||||||
|
|
||||||
|
middle = """
|
||||||
|
---
|
||||||
|
📝 Navegació de la sèrie
|
||||||
|
|
||||||
|
- Anterior: $PREV_HTML_LINK
|
||||||
|
- Següent: $NEXT_HTML_LINK
|
||||||
|
- [Resum de la sèrie]($SERIES_PERMALINK)
|
||||||
|
"""
|
||||||
|
|
||||||
|
prev_only = """
|
||||||
|
🎉 Felicitats! Has completat $SERIES_HTML_LINK.
|
||||||
|
|
||||||
|
Vols repassar? Aquí vam començar: $FIRST_HTML_LINK
|
||||||
|
O revisa el que acabem de veure a $PREV_HTML_LINK.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Respatller.
|
||||||
|
default = """
|
||||||
|
---
|
||||||
|
Aquest article és la part $SERIES_PAGE_INDEX de $SERIES_PAGES_NUMBER a $SERIES_HTML_LINK.
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variables personalitzades
|
||||||
|
|
||||||
|
Les plantilles de sèries admeten variables personalitzades per incloure informació addicional a tota la teva sèrie. El procés té dos passos:
|
||||||
|
|
||||||
|
1. Primer, defineix els teus **marcadors** a la configuració de la teva sèrie (`_index.md`):
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra]
|
||||||
|
series = true
|
||||||
|
series_template_placeholders = ["$POSITION", "$TOPIC", "$DIFFICULTY"]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Després, a cada article de la sèrie, proporciona els valors per a aquests marcadors a `series_template_variables`:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/article.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_template_variables]
|
||||||
|
position = "primer"
|
||||||
|
topic = "Variables i tipus"
|
||||||
|
difficulty = "Principiant"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ús de variables personalitzades
|
||||||
|
|
||||||
|
Pots usar les teves variables personalitzades a qualsevol plantilla, juntament amb les variables integrades:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
default = """
|
||||||
|
Aquest és l'article $POSITION a $SERIES_HTML_LINK.
|
||||||
|
Tema d'avui: $TOPIC
|
||||||
|
Nivell de dificultat: $DIFFICULTY
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ admonition(type="warning", text="Encara que els marcadors es defineixen en majúscules (`$POSITION`), els noms de variables a `series_template_variables` han d'estar en minúscules (`position`).") }}
|
||||||
|
|
||||||
|
### Exemple amb variables personalitzades
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# A la configuració de la sèrie.
|
||||||
|
[extra]
|
||||||
|
series = true
|
||||||
|
series_template_placeholders = ["$LEARNING_TIME", "$KEY_CONCEPTS"]
|
||||||
|
|
||||||
|
series_intro_templates.default = """
|
||||||
|
📚 Part $SERIES_PAGE_INDEX de $SERIES_PAGES_NUMBER
|
||||||
|
⏱️ Temps estimat: $LEARNING_TIME
|
||||||
|
🔑 Conceptes clau: $KEY_CONCEPTS
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/02-learning-rust/index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# En un article de la sèrie.
|
||||||
|
[extra.series_template_variables]
|
||||||
|
learning_time = "30 minuts"
|
||||||
|
key_concepts = "Funcions, gestió d'errors, coincidència de patrons"
|
||||||
|
```
|
||||||
|
|
||||||
|
Això generarà:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
📚 Part 2 de 5
|
||||||
|
⏱️ Temps estimat: 30 minuts
|
||||||
|
🔑 Conceptes clau: Funcions, gestió d'errors, coincidència de patrons
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ admonition(type="warning", title="Variables que falten", text="Si uses un marcador a les teves plantilles però no proporciones el seu valor a `series_template_variables`, la compilació fallarà amb un error que llista les variables que falten.") }}
|
@ -0,0 +1,424 @@
|
|||||||
|
+++
|
||||||
|
title = "Guía completa sobre series"
|
||||||
|
date = 2024-11-08
|
||||||
|
description = "Aprende a organizar tus publicaciones en series secuenciales, perfectas para tutoriales, cursos e historias de varias partes."
|
||||||
|
|
||||||
|
[taxonomies]
|
||||||
|
tags = ["funcionalidad", "tutorial", "preguntas frecuentes", "series"]
|
||||||
|
|
||||||
|
[extra]
|
||||||
|
quick_navigation_buttons = true
|
||||||
|
toc = true
|
||||||
|
mermaid = true
|
||||||
|
social_media_card = "social_cards/es_blog_series.jpg"
|
||||||
|
+++
|
||||||
|
|
||||||
|
Una serie organiza publicaciones relacionadas en orden secuencial, similar a los capítulos de un libro. A diferencia de las etiquetas, que simplemente agrupan contenido relacionado, las series sugieren un orden específico de lectura de principio a fin.
|
||||||
|
|
||||||
|
Las publicaciones dentro de una serie no necesitan publicarse de forma consecutiva; la función de series reúne publicaciones temáticamente vinculadas en una secuencia coherente.
|
||||||
|
|
||||||
|
El siguiente diagrama ilustra cómo las publicaciones de la serie (3, 5 y 8) existen dentro del flujo principal del blog mientras mantienen su propia secuencia ordenada dentro de Serie 1.
|
||||||
|
|
||||||
|
{% mermaid(full_width=true) %}
|
||||||
|
flowchart
|
||||||
|
subgraph main[BLOG]
|
||||||
|
P1[Post 1]
|
||||||
|
P2[P2]
|
||||||
|
P3[P3]
|
||||||
|
P4[P4]
|
||||||
|
P5[P5]
|
||||||
|
P6[P6]
|
||||||
|
P7[P7]
|
||||||
|
P8[P8]
|
||||||
|
P9[P9]
|
||||||
|
end
|
||||||
|
subgraph series1[SERIE 1]
|
||||||
|
PS1["Post Serie 1 (=P3)"]
|
||||||
|
PS2["Post Serie 2 (=P5)"]
|
||||||
|
PS3["Post Serie 3 (=P8)"]
|
||||||
|
end
|
||||||
|
P3 o-.-o PS1
|
||||||
|
P5 o-.-o PS2
|
||||||
|
P8 o-.-o PS3
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
## Inicio rápido
|
||||||
|
|
||||||
|
1. Crea un directorio para tu serie
|
||||||
|
2. Crea `_index.md` en el directorio de la serie
|
||||||
|
3. Configura el front matter de `_index.md`:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
title = "Aprendiendo Rust"
|
||||||
|
template = "series.html"
|
||||||
|
sort_by = "slug"
|
||||||
|
transparent = true
|
||||||
|
|
||||||
|
[extra]
|
||||||
|
series = true
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Crea tus artículos de la serie en este directorio
|
||||||
|
|
||||||
|
¿Quieres saber más? ¡Sigue leyendo!
|
||||||
|
|
||||||
|
## ¿Cómo funcionan las series?
|
||||||
|
|
||||||
|
Una serie es simplemente una sección que tabi maneja de manera especial. Para más detalles sobre secciones, consulta la [documentación de Zola](https://www.getzola.org/documentation/content/section/).
|
||||||
|
|
||||||
|
Tomando el ejemplo del diagrama anterior, la estructura de directorios sería así:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
content/
|
||||||
|
_index.md
|
||||||
|
blog/
|
||||||
|
_index.md
|
||||||
|
post1/
|
||||||
|
index.md
|
||||||
|
post2/
|
||||||
|
index.md
|
||||||
|
post4/
|
||||||
|
index.md
|
||||||
|
post6/
|
||||||
|
index.md
|
||||||
|
post7/
|
||||||
|
index.md
|
||||||
|
post9/
|
||||||
|
index.md
|
||||||
|
serie1/
|
||||||
|
_index.md
|
||||||
|
post3/
|
||||||
|
index.md
|
||||||
|
post5/
|
||||||
|
index.md
|
||||||
|
post8/
|
||||||
|
index.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Para crear una serie, necesitas:
|
||||||
|
|
||||||
|
1. Usar la plantilla `series.html`
|
||||||
|
2. Establecer `series = true` en la configuración `[extra]` de la sección
|
||||||
|
3. Activar `transparent = true` para integrar las publicaciones de la serie con la sección del blog principal
|
||||||
|
|
||||||
|
La página principal de la serie muestra un resumen seguido de una lista de todas las publicaciones en la serie:
|
||||||
|
|
||||||
|
{{ dual_theme_image(light_src="blog/series/img/series_light.webp", dark_src="blog/series/img/series_dark.webp" alt="una serie", full_width=true) }}
|
||||||
|
|
||||||
|
## Saltar a las publicaciones
|
||||||
|
|
||||||
|
Si el contenido de una serie (el Markdown después del frontmatter en `_index.md`) supera los 2000 caracteres, aparece un enlace "Saltar a publicaciones" junto al título de la serie.
|
||||||
|
|
||||||
|
{{ dual_theme_image(light_src="blog/series/img/jump_to_series_posts_light.webp", dark_src="blog/series/img/jump_to_series_posts_dark.webp" alt="enlace para saltar a las publicaciones de la serie", full_width=true) }}
|
||||||
|
|
||||||
|
Para forzar la activación o desactivación de esta función, configura `show_jump_to_posts` en la sección `[extra]` de tu sección de series o en `config.toml`. Esta configuración sigue [la jerarquía](@/blog/mastering-tabi-settings/index.es.md#jerarquia-de-configuracion).
|
||||||
|
|
||||||
|
## Páginas de series y orden
|
||||||
|
|
||||||
|
Todas las páginas en la sección de series serán páginas de serie. Las páginas se ordenarán según el `sort_by` de la sección.
|
||||||
|
|
||||||
|
Aunque las series mantienen su propio orden interno, permanecen independientes del flujo cronológico de la sección principal (por ejemplo, `blog/`) gracias a la configuración `transparent`.
|
||||||
|
|
||||||
|
### Opciones de orden
|
||||||
|
|
||||||
|
Elige entre estos métodos de orden, cada uno con sus ventajas:
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
`sort_by` | ventajas | desventajas
|
||||||
|
---------|-------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
`slug` | El orden de las páginas es explícito en la ruta (por ejemplo, `example.com/blog/series1/01-series-post-uno`). | Cada página de la serie debe tener el prefijo correspondiente.
|
||||||
|
`weight` | El orden de las páginas es fácil de configurar de forma transparente.<br>La primera publicación tiene peso `1`, la segunda peso `2` y así sucesivamente. | Cada página de la serie debe tener su peso configurado.
|
||||||
|
`date` | El orden de las páginas se puede configurar una sola vez en la configuración de la sección. No hay que hacer nada en cada página. | El orden de las páginas debe invertirse porque la primera página suele ser la más antigua. Esto solo se puede lograr paginando la sección (`paginate_by = 9999`) e invirtiendo su orden (`paginate_reversed = true`).
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{{ admonition(type="danger", title="Versión de Zola para ordenar por fecha", text="Para invertir correctamente las fechas, se requiere Zola v0.19.3+ (no publicada) para que la información de paginación esté disponible a través de la función `get_section`. De lo contrario, cualquier cosa que dependa del orden de las páginas de la serie no será correcta (por ejemplo, página anterior/siguiente, listas ordenadas y no ordenadas...) Ver [Zola PR #2653](https://github.com/getzola/zola/pull/2653).") }}
|
||||||
|
|
||||||
|
### Indexación de páginas
|
||||||
|
|
||||||
|
Las páginas en una serie se indexan empezando desde 1, siguiendo su orden `sort_by`. Para invertir la indexación (haciendo que la primera página tenga el índice más alto), añade esta configuración a `_index.md` o `config.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra]
|
||||||
|
post_listing_index_reversed = true # Por defecto es false si no se configura
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ dual_theme_image(light_src="blog/series/img/series_reversed_light.webp", dark_src="blog/series/img/series_reversed_dark.webp" alt="una serie con índices invertidos", full_width=true) }}
|
||||||
|
|
||||||
|
Esta configuración sigue [la jerarquía](@/blog/mastering-tabi-settings/index.es.md#jerarquia-de-configuracion).
|
||||||
|
|
||||||
|
## Plantillas de introducción y conclusión
|
||||||
|
|
||||||
|
Los artículos de una serie pueden tener secciones automáticas de introducción y conclusión. Estas se configuran en el `_index.md` de tu serie. Un ejemplo básico:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
default = "Este artículo es parte de la serie $SERIES_HTML_LINK."
|
||||||
|
|
||||||
|
[extra.series_outro_templates]
|
||||||
|
default = "¡Gracias por leer la parte $SERIES_PAGE_INDEX de $SERIES_HTML_LINK!"
|
||||||
|
```
|
||||||
|
|
||||||
|
Las secciones de introducción y conclusión tienen sus propias clases CSS (`series-page-intro` y `series-page-outro`), lo que te permite personalizar su apariencia mediante [CSS personalizado](@/blog/mastering-tabi-settings/index.es.md#estilos-css-personalizados).
|
||||||
|
|
||||||
|
### Tipos de plantillas
|
||||||
|
|
||||||
|
El sistema de series usa diferentes plantillas según la posición del artículo en la serie:
|
||||||
|
|
||||||
|
- `next_only` - Usado para el primer artículo (tiene artículo siguiente pero no anterior)
|
||||||
|
- `middle` - Usado para artículos con artículos anterior y siguiente
|
||||||
|
- `prev_only` - Usado para el último artículo (tiene artículo anterior pero no siguiente)
|
||||||
|
- `default` - Plantilla por defecto usada cuando no existe una plantilla específica para la posición
|
||||||
|
|
||||||
|
El sistema determina automáticamente qué plantilla usar según la posición del artículo. Las plantillas se definen en la configuración de la serie (`_index.md`), como `extra.series_intro_templates` y `extra.series_outro_templates`:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
next_only = "¡Bienvenido a la parte 1! Siguiente: $NEXT_HTML_LINK"
|
||||||
|
middle = "Anterior: $PREV_HTML_LINK | Siguiente: $NEXT_HTML_LINK"
|
||||||
|
prev_only = "¡El capítulo final! Anteriormente: $PREV_HTML_LINK"
|
||||||
|
default = "Parte $SERIES_PAGE_INDEX de $SERIES_PAGES_NUMBER"
|
||||||
|
```
|
||||||
|
|
||||||
|
Todas las plantillas son opcionales. La selección de plantillas sigue un sistema de prioridad:
|
||||||
|
|
||||||
|
1. Si existe una plantilla específica para la posición (`next_only`, `middle`, o `prev_only`), se usará esa
|
||||||
|
2. Si no, se usa la plantilla `default`
|
||||||
|
3. Si no se define ninguna plantilla, no se mostrará información de la serie
|
||||||
|
|
||||||
|
Mira el [ejemplo de plantilla](#ejemplo-de-plantilla) para ver un ejemplo más elaborado.
|
||||||
|
|
||||||
|
### Ubicación en el contenido
|
||||||
|
|
||||||
|
Por defecto:
|
||||||
|
|
||||||
|
- Las introducciones de serie aparecen al inicio de tu artículo
|
||||||
|
- La conclusión aparece al final (antes de las notas al pie, si las hay)
|
||||||
|
|
||||||
|
Puedes controlar exactamente dónde aparecen usando `<!-- series_intro -->` y `<!-- series_outro -->` en tu Markdown:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Este párrafo aparece antes de la introducción de la serie.
|
||||||
|
|
||||||
|
<!-- series_intro -->
|
||||||
|
|
||||||
|
Contenido principal del artículo.
|
||||||
|
|
||||||
|
<!-- series_outro -->
|
||||||
|
|
||||||
|
## Recursos de aprendizaje
|
||||||
|
|
||||||
|
Contenido adicional...
|
||||||
|
|
||||||
|
[^1]: Las notas al pie siempre aparecerán al final.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
Las plantillas de series usan un sistema flexible de variables que te permite:
|
||||||
|
|
||||||
|
1. Hacer referencia a información de la serie (título, enlaces)
|
||||||
|
2. Añadir navegación entre artículos
|
||||||
|
3. Mostrar indicadores de progreso
|
||||||
|
4. Incluir información personalizada usando tus propias variables
|
||||||
|
|
||||||
|
Las variables son marcadores que comienzan con `$` y se reemplazan con contenido real cuando se construye tu sitio. Por ejemplo, `$SERIES_HTML_LINK` se convierte en un enlace clicable a la página índice de tu serie.
|
||||||
|
|
||||||
|
Hay tres tipos de variables:
|
||||||
|
|
||||||
|
- [Variables básicas de serie](#variables-basicas-de-serie): Información general sobre la serie
|
||||||
|
- [Variables de navegación](#variables-de-navegacion): Enlaces a artículos anterior/siguiente
|
||||||
|
- [Variables personalizadas](#variables-personalizadas): Tus propios marcadores para información adicional
|
||||||
|
|
||||||
|
### Variables básicas de serie
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
| Variable | Disponibilidad | Devuelve | Descripción | Ejemplo de uso | Ejemplo de salida |
|
||||||
|
|----------|---------------|-----------|-------------|----------------|-------------------|
|
||||||
|
| `$SERIES_TITLE` | Siempre | Texto | Título de la serie en texto plano | `Parte de $SERIES_TITLE` | Parte de Aprendiendo Rust |
|
||||||
|
| `$SERIES_PERMALINK` | Siempre | Texto | URL al índice de la serie | `[Ver todas las publicaciones]($SERIES_PERMALINK)` | [Ver todas las publicaciones](/series/learn-rust) |
|
||||||
|
| `$SERIES_HTML_LINK` | Siempre | HTML | Enlace listo para usar a la serie | `¡Bienvenido a $SERIES_HTML_LINK!` | ¡Bienvenido a <a href="/series/learn-rust">Aprendiendo Rust</a>! |
|
||||||
|
| `$SERIES_PAGES_NUMBER` | Siempre | Número | Total de artículos en la serie | `Una serie de $SERIES_PAGES_NUMBER partes` | Una serie de 5 partes |
|
||||||
|
| `$SERIES_PAGE_INDEX` | Siempre | Número | Posición del artículo actual | `Parte $SERIES_PAGE_INDEX de $SERIES_PAGES_NUMBER` | Parte 3 de 5 |
|
||||||
|
| `$SERIES_PAGES_OLIST` | Siempre | HTML | Lista ordenada de todos los artículos | `Artículos en la serie: $SERIES_PAGES_OLIST` | Artículos en la serie: <ol><li>Artículo actual</li><li><a href="...">Otros artículos</a></li></ol> |
|
||||||
|
| `$SERIES_PAGES_ULIST` | Siempre | HTML | Lista desordenada de todos los artículos | `Artículos en la serie: $SERIES_PAGES_ULIST` | Artículos en la serie: <ul><li>Artículo actual</li><li><a href="...">Otros artículos</a></li></ul> |
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
### Variables de navegación
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
| Variable | Disponibilidad | Devuelve | Descripción | Ejemplo de uso | Ejemplo de salida |
|
||||||
|
|----------|---------------|-----------|-------------|----------------|-------------------|
|
||||||
|
| `$PREV_TITLE` | Existe anterior | Texto | Título del artículo anterior | `Anteriormente: $PREV_TITLE` | Anteriormente: Configurando tu entorno |
|
||||||
|
| `$PREV_PERMALINK` | Existe anterior | Texto | URL al artículo anterior | `[← Atrás]($PREV_PERMALINK)` | [← Atrás](/series/learn-rust/setup) |
|
||||||
|
| `$PREV_HTML_LINK` | Existe anterior | HTML | Enlace listo para usar al anterior | `Lee primero $PREV_HTML_LINK` | Lee primero <a href="/series/learn-rust/setup">Configurando tu entorno</a> |
|
||||||
|
| `$PREV_DESCRIPTION` | Existe anterior | Texto | Descripción del artículo anterior | `Resumen: $PREV_DESCRIPTION` | Resumen: Configurando Rust |
|
||||||
|
| `$NEXT_TITLE` | Existe siguiente | Texto | Título del siguiente artículo | `Siguiente: $NEXT_TITLE` | Siguiente: Patrones avanzados |
|
||||||
|
| `$NEXT_PERMALINK` | Existe siguiente | Texto | URL al siguiente artículo | `[Continuar →]($NEXT_PERMALINK)` | [Continuar →](/series/learn-rust/patterns) |
|
||||||
|
| `$NEXT_HTML_LINK` | Existe siguiente | HTML | Enlace listo para usar al siguiente | `Continúa con $NEXT_HTML_LINK` | Continúa con <a href="/series/learn-rust/patterns">Patrones avanzados</a> |
|
||||||
|
| `$NEXT_DESCRIPTION` | Existe siguiente | Texto | Descripción del siguiente artículo | `Próximamente: $NEXT_DESCRIPTION` | Próximamente: Aprende sobre las características avanzadas de pattern matching en Rust |
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
### Referencia al primer artículo
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
| Variable | Disponibilidad | Devuelve | Descripción | Ejemplo de uso | Ejemplo de salida |
|
||||||
|
|----------|---------------|-----------|-------------|----------------|-------------------|
|
||||||
|
| `$FIRST_TITLE` | Siempre | Texto | Título del primer artículo | `Comienza con $FIRST_TITLE` | Comienza con Introducción a Rust |
|
||||||
|
| `$FIRST_HTML_LINK` | Siempre | HTML | Enlace listo para usar al primer artículo | `Empieza en $FIRST_HTML_LINK` | Empieza en <a href="/series/learn-rust/intro">Introducción a Rust</a> |
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
### Ejemplo de plantilla
|
||||||
|
|
||||||
|
{{ admonition(type="tip", title="Variables HTML vs texto", text="Usa variables HTML (que terminan en `_HTML_LINK`) cuando quieras enlaces listos para usar. Usa variables de texto (que terminan en `_TITLE` o `_PERMALINK`) cuando quieras más control sobre el formato.") }}
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Introducción.
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
next_only = """
|
||||||
|
¡Bienvenido a $SERIES_HTML_LINK! Esta serie de $SERIES_PAGES_NUMBER partes te enseñará Rust desde cero.
|
||||||
|
|
||||||
|
Siguiente: $NEXT_HTML_LINK - $NEXT_DESCRIPTION
|
||||||
|
"""
|
||||||
|
|
||||||
|
middle = """
|
||||||
|
📚 Parte $SERIES_PAGE_INDEX de $SERIES_PAGES_NUMBER en $SERIES_HTML_LINK
|
||||||
|
|
||||||
|
Anterior: $PREV_HTML_LINK
|
||||||
|
Siguiente: $NEXT_HTML_LINK
|
||||||
|
"""
|
||||||
|
|
||||||
|
prev_only = """
|
||||||
|
¡Bienvenido a la última parte de $SERIES_HTML_LINK!
|
||||||
|
¿Eres nuevo? Comienza con $FIRST_HTML_LINK para construir una base sólida.
|
||||||
|
|
||||||
|
Anterior: $PREV_HTML_LINK
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Plantilla de respaldo.
|
||||||
|
default = "Este artículo es parte de la serie $SERIES_HTML_LINK."
|
||||||
|
|
||||||
|
# Conclusión.
|
||||||
|
[extra.series_outro_templates]
|
||||||
|
next_only = """
|
||||||
|
¡Gracias por leer! 🙌
|
||||||
|
|
||||||
|
Continúa tu viaje con $NEXT_HTML_LINK, donde $NEXT_DESCRIPTION
|
||||||
|
O revisa el esquema completo de la serie [$SERIES_TITLE]($SERIES_PERMALINK).
|
||||||
|
"""
|
||||||
|
|
||||||
|
middle = """
|
||||||
|
---
|
||||||
|
📝 Navegación de la serie
|
||||||
|
|
||||||
|
- Anterior: $PREV_HTML_LINK
|
||||||
|
- Siguiente: $NEXT_HTML_LINK
|
||||||
|
- [Resumen de la serie]($SERIES_PERMALINK)
|
||||||
|
"""
|
||||||
|
|
||||||
|
prev_only = """
|
||||||
|
🎉 ¡Felicidades! Has completado $SERIES_HTML_LINK.
|
||||||
|
|
||||||
|
¿Quieres repasar? Aquí comenzamos: $FIRST_HTML_LINK
|
||||||
|
O revisa lo que acabamos de ver en $PREV_HTML_LINK.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Respaldo.
|
||||||
|
default = """
|
||||||
|
---
|
||||||
|
Este artículo es la parte $SERIES_PAGE_INDEX de $SERIES_PAGES_NUMBER en $SERIES_HTML_LINK.
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variables personalizadas
|
||||||
|
|
||||||
|
Las plantillas de series admiten variables personalizadas para incluir información adicional en toda tu serie. El proceso tiene dos pasos:
|
||||||
|
|
||||||
|
1. Primero, define tus **marcadores** en la configuración de tu serie (`_index.md`):
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra]
|
||||||
|
series = true
|
||||||
|
series_template_placeholders = ["$POSITION", "$TOPIC", "$DIFFICULTY"]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Luego, en cada artículo de la serie, proporciona los valores para estos marcadores en `series_template_variables`:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/article.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_template_variables]
|
||||||
|
position = "primero"
|
||||||
|
topic = "Variables y tipos"
|
||||||
|
difficulty = "Principiante"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Uso de variables personalizadas
|
||||||
|
|
||||||
|
Puedes usar tus variables personalizadas en cualquier plantilla, junto con las variables integradas:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
default = """
|
||||||
|
Este es el artículo $POSITION en $SERIES_HTML_LINK.
|
||||||
|
Tema de hoy: $TOPIC
|
||||||
|
Nivel de dificultad: $DIFFICULTY
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ admonition(type="warning", text="Aunque los marcadores se definen en mayúsculas (`$POSITION`), los nombres de variables en `series_template_variables` deben estar en minúsculas (`position`).") }}
|
||||||
|
|
||||||
|
### Ejemplo con variables personalizadas
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# En la configuración de la serie.
|
||||||
|
[extra]
|
||||||
|
series = true
|
||||||
|
series_template_placeholders = ["$LEARNING_TIME", "$KEY_CONCEPTS"]
|
||||||
|
|
||||||
|
series_intro_templates.default = """
|
||||||
|
📚 Parte $SERIES_PAGE_INDEX de $SERIES_PAGES_NUMBER
|
||||||
|
⏱️ Tiempo estimado: $LEARNING_TIME
|
||||||
|
🔑 Conceptos clave: $KEY_CONCEPTS
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/02-learning-rust/index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# En un artículo de la serie.
|
||||||
|
[extra.series_template_variables]
|
||||||
|
learning_time = "30 minutos"
|
||||||
|
key_concepts = "Funciones, manejo de errores, coincidencia de patrones"
|
||||||
|
```
|
||||||
|
|
||||||
|
Esto generará:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
📚 Parte 2 de 5
|
||||||
|
⏱️ Tiempo estimado: 30 minutos
|
||||||
|
🔑 Conceptos clave: Funciones, manejo de errores, coincidencia de patrones
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ admonition(type="warning", title="Variables faltantes", text="Si usas un marcador en tus plantillas pero no proporcionas su valor en `series_template_variables`, la compilación fallará con un error que lista las variables faltantes.") }}
|
@ -0,0 +1,424 @@
|
|||||||
|
+++
|
||||||
|
title = "A Complete Guide to Series"
|
||||||
|
date = 2024-11-08
|
||||||
|
description = "Learn how to organize your posts into sequential series, perfect for tutorials, courses, and multi-part stories."
|
||||||
|
|
||||||
|
[taxonomies]
|
||||||
|
tags = ["showcase", "tutorial", "FAQ", "series"]
|
||||||
|
|
||||||
|
[extra]
|
||||||
|
quick_navigation_buttons = true
|
||||||
|
toc = true
|
||||||
|
mermaid = true
|
||||||
|
social_media_card = "social_cards/es_blog_series.jpg"
|
||||||
|
+++
|
||||||
|
|
||||||
|
A series organizes related posts in a sequential order, similar to chapters in a book. Unlike tags, which simply group related content, series suggest a specific reading order from start to finish.
|
||||||
|
|
||||||
|
Posts within a series do not need to be published consecutively; the series feature brings together thematically linked posts in a coherent sequence.
|
||||||
|
|
||||||
|
The diagram below illustrates how series posts (3, 5, and 8) exist within the main blog flow while maintaining their own ordered sequence within Series 1.
|
||||||
|
|
||||||
|
{% mermaid(full_width=true) %}
|
||||||
|
flowchart
|
||||||
|
subgraph main[BLOG]
|
||||||
|
P1[Post 1]
|
||||||
|
P2[P2]
|
||||||
|
P3[P3]
|
||||||
|
P4[P4]
|
||||||
|
P5[P5]
|
||||||
|
P6[P6]
|
||||||
|
P7[P7]
|
||||||
|
P8[P8]
|
||||||
|
P9[P9]
|
||||||
|
end
|
||||||
|
subgraph series1[SERIES 1]
|
||||||
|
PS1["Series Post 1 (=P3)"]
|
||||||
|
PS2["Series Post 2 (=P5)"]
|
||||||
|
PS3["Series Post 3 (=P8)"]
|
||||||
|
end
|
||||||
|
P3 o-.-o PS1
|
||||||
|
P5 o-.-o PS2
|
||||||
|
P8 o-.-o PS3
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. Create a directory for your series.
|
||||||
|
2. Create `_index.md` in the series directory.
|
||||||
|
3. Set up the `_index.md` front matter:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
title = "Learning Rust"
|
||||||
|
template = "series.html"
|
||||||
|
sort_by = "slug"
|
||||||
|
transparent = true
|
||||||
|
|
||||||
|
[extra]
|
||||||
|
series = true
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Create your series articles in this directory.
|
||||||
|
|
||||||
|
Want more? Keep reading!
|
||||||
|
|
||||||
|
## How Do Series Work?
|
||||||
|
|
||||||
|
A series is just a section which is handled in a special way by tabi. For more details on sections, see the [Zola documentation](https://www.getzola.org/documentation/content/section/).
|
||||||
|
|
||||||
|
Taking the example from the diagram above, the directory structure would be as follow:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
content/
|
||||||
|
_index.md
|
||||||
|
blog/
|
||||||
|
_index.md
|
||||||
|
post1/
|
||||||
|
index.md
|
||||||
|
post2/
|
||||||
|
index.md
|
||||||
|
post4/
|
||||||
|
index.md
|
||||||
|
post6/
|
||||||
|
index.md
|
||||||
|
post7/
|
||||||
|
index.md
|
||||||
|
post9/
|
||||||
|
index.md
|
||||||
|
series1/
|
||||||
|
_index.md
|
||||||
|
post3/
|
||||||
|
index.md
|
||||||
|
post5/
|
||||||
|
index.md
|
||||||
|
post8/
|
||||||
|
index.md
|
||||||
|
```
|
||||||
|
|
||||||
|
To create a series, you need to:
|
||||||
|
|
||||||
|
1. Use the `series.html` template
|
||||||
|
2. Set `series = true` in the section's `[extra]` configuration
|
||||||
|
3. Enable `transparent = true` to integrate series posts with the parent blog section
|
||||||
|
|
||||||
|
The series main page displays an overview followed by a list of all posts in the series:
|
||||||
|
|
||||||
|
{{ dual_theme_image(light_src="blog/series/img/series_light.webp", dark_src="blog/series/img/series_dark.webp" alt="a series", full_width=true) }}
|
||||||
|
|
||||||
|
## Jump to Posts
|
||||||
|
|
||||||
|
If the content of a series (the Markdown after the front matter in `_index.md`) is over 2000 characters, a "Jump to posts" link appears next to the series title.
|
||||||
|
|
||||||
|
{{ dual_theme_image(light_src="blog/series/img/jump_to_series_posts_light.webp", dark_src="blog/series/img/jump_to_series_posts_dark.webp" alt="jump to series posts link", full_width=true) }}
|
||||||
|
|
||||||
|
To force the feature on or off, set `show_jump_to_posts` in the `[extra]` section of your series section or in `config.toml`. This setting follows [the hierarchy](@/blog/mastering-tabi-settings/index.md#settings-hierarchy).
|
||||||
|
|
||||||
|
## Series Pages and Order
|
||||||
|
|
||||||
|
All pages in the series section will be a series page. The series pages will be ordered as per the series section `sort_by`.
|
||||||
|
|
||||||
|
While series maintain their own internal order, they remain independent from the main section's (e.g. `blog/`) chronological flow thanks to the `transparent` setting.
|
||||||
|
|
||||||
|
### Sorting Options
|
||||||
|
|
||||||
|
Choose from these sorting methods, each with its own advantages:
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
`sort_by` | pros | cons
|
||||||
|
---------|-------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
`slug` | The series pages order is made explicit in the path (e.g. `example.com/blog/series1/01-series-post-one`). | Each series page must be prefixed accordingly.
|
||||||
|
`weight` | The series pages order is easy to set up transparently.<br>First series post has weight `1`, second series post has weight `2` and so on. | Each series page must have its weight set accordingly.
|
||||||
|
`date` | The series pages order can be configured once in the series section configuration. No need to do anything on each series page. | The series pages order has to be reversed because the first page is usually the oldest. This can only be achieved by paginating the series section (`paginate_by = 9999`) and reversing its order (`paginate_reversed = true`).
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{{ admonition(type="danger", title="Zola version to sort by date", text="In order to properly reverse dates, Zola v0.19.3+ (unreleased) is required so that pagination information is available through the `get_section` function. Anything relying on the series pages order won't be correct in a series page otherwise (e.g. previous/next series page, ordered and unordered list…) See [Zola PR #2653](https://github.com/getzola/zola/pull/2653).") }}
|
||||||
|
|
||||||
|
### Page Indexing
|
||||||
|
|
||||||
|
Pages in a series are indexed starting from 1, following their `sort_by` order. To reverse the indexing (making the first page have the highest index instead), add this setting to `_index.md` or `config.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra]
|
||||||
|
post_listing_index_reversed = true # Defaults to false if unset.
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ dual_theme_image(light_src="blog/series/img/series_reversed_light.webp", dark_src="blog/series/img/series_reversed_dark.webp" alt="a series with indexes reversed", full_width=true) }}
|
||||||
|
|
||||||
|
This setting follows [the hierarchy](@/blog/mastering-tabi-settings/index.md#settings-hierarchy).
|
||||||
|
|
||||||
|
## Intro and Outro Templates
|
||||||
|
|
||||||
|
Series articles can have automatic introduction and conclusion sections. These are configured in your series' `_index.md`. A basic example:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
default = "This article is part of the $SERIES_HTML_LINK series."
|
||||||
|
|
||||||
|
[extra.series_outro_templates]
|
||||||
|
default = "Thanks for reading part $SERIES_PAGE_INDEX of $SERIES_HTML_LINK!"
|
||||||
|
```
|
||||||
|
|
||||||
|
The intro and outro sections each have their own CSS classes (`series-page-intro` and `series-page-outro`), allowing you to customize their appearance through [custom CSS](@/blog/mastering-tabi-settings/index.md#custom-css).
|
||||||
|
|
||||||
|
### Template Types
|
||||||
|
|
||||||
|
The series system uses different templates based on an article's position in the series:
|
||||||
|
|
||||||
|
- `next_only` - Used for the first article (has next article but no previous)
|
||||||
|
- `middle` - Used for articles with both previous and next articles
|
||||||
|
- `prev_only` - Used for the last article (has previous article but no next)
|
||||||
|
- `default` - Fallback template used when a specific position template isn't defined
|
||||||
|
|
||||||
|
The system automatically determines which template to use based on the article's position. The templates are defined in the series configuration (`_index.md`), as `extra.series_intro_templates` and `extra.series_outro_templates`.:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
next_only = "Welcome to part 1! Next up: $NEXT_HTML_LINK"
|
||||||
|
middle = "Previous: $PREV_HTML_LINK | Next: $NEXT_HTML_LINK"
|
||||||
|
prev_only = "The final chapter! Previously: $PREV_HTML_LINK"
|
||||||
|
default = "Part $SERIES_PAGE_INDEX of $SERIES_PAGES_NUMBER"
|
||||||
|
```
|
||||||
|
|
||||||
|
All templates are optional. Template selection follows a priority system:
|
||||||
|
|
||||||
|
1. If a position-specific template exists (`next_only`, `middle`, or `prev_only`), it will be used
|
||||||
|
2. Otherwise, the `default` template is used
|
||||||
|
3. If no templates are defined at all, no series information will be displayed
|
||||||
|
|
||||||
|
See the [template example](#template-example) for a more elaborate example.
|
||||||
|
|
||||||
|
### Placement in Content
|
||||||
|
|
||||||
|
By default:
|
||||||
|
|
||||||
|
- Series introductions appear at the start of your article
|
||||||
|
- Series outro appears at the end (before footnotes, if any)
|
||||||
|
|
||||||
|
You can control exactly where these appear using `<!-- series_intro -->` and `<!-- series_outro -->` in your Markdown:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
This paragraph appears before the series introduction.
|
||||||
|
|
||||||
|
<!-- series_intro -->
|
||||||
|
|
||||||
|
Main content of the article.
|
||||||
|
|
||||||
|
<!-- series_outro -->
|
||||||
|
|
||||||
|
## Learning Resources
|
||||||
|
|
||||||
|
Extra content…
|
||||||
|
|
||||||
|
[^1]: Footnotes will always appear at the end.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
Series templates use a flexible variable system that lets you:
|
||||||
|
|
||||||
|
1. Reference series information (title, links)
|
||||||
|
2. Add navigation between articles
|
||||||
|
3. Show progress indicators
|
||||||
|
4. Include custom information using your own variables
|
||||||
|
|
||||||
|
Variables are placeholders starting with `$` that get replaced with actual content when your site builds. For example, `$SERIES_HTML_LINK` becomes a clickable link to your series index page.
|
||||||
|
|
||||||
|
There are three types of variables:
|
||||||
|
|
||||||
|
- [**Basic Series Variables**](#basic-series-variables): General information about the series
|
||||||
|
- [**Navigation Variables**](#navigation-variables): Links to previous/next articles
|
||||||
|
- [**Custom Variables**](#custom-variables): Your own placeholders for additional information
|
||||||
|
|
||||||
|
### Basic Series Variables
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
| Variable | Availability | Returns | Description | Example Usage | Example Output |
|
||||||
|
|----------|-------------|---------|-------------|---------------|----------------|
|
||||||
|
| `$SERIES_TITLE` | Always | Text | Plain text title of the series | `Part of $SERIES_TITLE` | Part of Learn Rust |
|
||||||
|
| `$SERIES_PERMALINK` | Always | Text | URL to series index | `[See all posts]($SERIES_PERMALINK)` | [See all posts](/series/learn-rust) |
|
||||||
|
| `$SERIES_HTML_LINK` | Always | HTML | Ready-to-use link to series | `Welcome to $SERIES_HTML_LINK!` | Welcome to <a href="/series/learn-rust">Learn Rust</a>! |
|
||||||
|
| `$SERIES_PAGES_NUMBER` | Always | Number | Total articles in series | `A $SERIES_PAGES_NUMBER part series` | A 5 part series |
|
||||||
|
| `$SERIES_PAGE_INDEX` | Always | Number | Current article's position | `Part $SERIES_PAGE_INDEX of $SERIES_PAGES_NUMBER` | Part 3 of 5 |
|
||||||
|
| `$SERIES_PAGES_OLIST` | Always | HTML | Ordered list of all articles | `Articles in series: $SERIES_PAGES_OLIST` | Articles in series: <ol><li>Current article</li><li><a href="...">Other articles</a></li></ol> |
|
||||||
|
| `$SERIES_PAGES_ULIST` | Always | HTML | Unordered list of all articles | `Articles in series: $SERIES_PAGES_ULIST` | Articles in series: <ul><li>Current article</li><li><a href="...">Other articles</a></li></ul> |
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
### Navigation Variables
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
| Variable | Availability | Returns | Description | Example Usage | Example Output |
|
||||||
|
|----------|-------------|---------|-------------|---------------|----------------|
|
||||||
|
| `$PREV_TITLE` | Previous exists | Text | Previous article's title | `Previously: $PREV_TITLE` | Previously: Setting Up Your Environment |
|
||||||
|
| `$PREV_PERMALINK` | Previous exists | Text | URL to previous article | `[← Back]($PREV_PERMALINK)` | [← Back](/series/learn-rust/setup) |
|
||||||
|
| `$PREV_HTML_LINK` | Previous exists | HTML | Ready-to-use link to previous | `Read $PREV_HTML_LINK first` | Read <a href="/series/learn-rust/setup">Setting Up Your Environment</a> first |
|
||||||
|
| `$PREV_DESCRIPTION` | Previous exists | Text | Description of previous article | `Recap: $PREV_DESCRIPTION` | Recap: Setting up Rust |
|
||||||
|
| `$NEXT_TITLE` | Next exists | Text | Next article's title | `Next up: $NEXT_TITLE` | Next up: Advanced Patterns |
|
||||||
|
| `$NEXT_PERMALINK` | Next exists | Text | URL to next article | `[Continue →]($NEXT_PERMALINK)` | [Continue →](/series/learn-rust/patterns) |
|
||||||
|
| `$NEXT_HTML_LINK` | Next exists | HTML | Ready-to-use link to next | `Continue with $NEXT_HTML_LINK` | Continue with <a href="/series/learn-rust/patterns">Advanced Patterns</a> |
|
||||||
|
| `$NEXT_DESCRIPTION` | Next exists | Text | Description of next article | `Coming up: $NEXT_DESCRIPTION` | Coming up: Learn about Rust's advanced pattern matching features |
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
### First Article Reference
|
||||||
|
|
||||||
|
{% wide_container() %}
|
||||||
|
|
||||||
|
| Variable | Availability | Returns | Description | Example Usage | Example Output |
|
||||||
|
|----------|-------------|---------|-------------|---------------|----------------|
|
||||||
|
| `$FIRST_TITLE` | Always | Text | First article's title | `Start with $FIRST_TITLE` | Start with Introduction to Rust |
|
||||||
|
| `$FIRST_HTML_LINK` | Always | HTML | Ready-to-use link to first article | `Begin at $FIRST_HTML_LINK` | Begin at <a href="/series/learn-rust/intro">Introduction to Rust</a> |
|
||||||
|
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
### Template Example
|
||||||
|
|
||||||
|
{{ admonition(type="tip", title="HTML vs text variables", text="Use HTML variables (ending in `_HTML_LINK`) when you want ready-made links. Use text variables (ending in `_TITLE` or `_PERMALINK`) when you want more control over the formatting.") }}
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Introduction.
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
next_only = """
|
||||||
|
Welcome to $SERIES_HTML_LINK! This $SERIES_PAGES_NUMBER-part series will teach you Rust from scratch.
|
||||||
|
|
||||||
|
Up next: $NEXT_HTML_LINK - $NEXT_DESCRIPTION
|
||||||
|
"""
|
||||||
|
|
||||||
|
middle = """
|
||||||
|
📚 Part $SERIES_PAGE_INDEX of $SERIES_PAGES_NUMBER in $SERIES_HTML_LINK
|
||||||
|
|
||||||
|
Previously: $PREV_HTML_LINK
|
||||||
|
Next up: $NEXT_HTML_LINK
|
||||||
|
"""
|
||||||
|
|
||||||
|
prev_only = """
|
||||||
|
Welcome to the final part of $SERIES_HTML_LINK!
|
||||||
|
New here? Start with $FIRST_HTML_LINK to build a strong foundation.
|
||||||
|
|
||||||
|
Previously: $PREV_HTML_LINK
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Fallback template.
|
||||||
|
default = "This article is part of the $SERIES_HTML_LINK series."
|
||||||
|
|
||||||
|
# Outro.
|
||||||
|
[extra.series_outro_templates]
|
||||||
|
next_only = """
|
||||||
|
Thanks for reading! 🙌
|
||||||
|
|
||||||
|
Continue your journey with $NEXT_HTML_LINK, where $NEXT_DESCRIPTION
|
||||||
|
Or check out the complete [$SERIES_TITLE]($SERIES_PERMALINK) series outline.
|
||||||
|
"""
|
||||||
|
|
||||||
|
middle = """
|
||||||
|
---
|
||||||
|
📝 Series Navigation
|
||||||
|
|
||||||
|
- Previous: $PREV_HTML_LINK
|
||||||
|
- Next: $NEXT_HTML_LINK
|
||||||
|
- [Series Overview]($SERIES_PERMALINK)
|
||||||
|
"""
|
||||||
|
|
||||||
|
prev_only = """
|
||||||
|
🎉 Congratulations! You've completed $SERIES_HTML_LINK.
|
||||||
|
|
||||||
|
Want to review? Here's where we started: $FIRST_HTML_LINK
|
||||||
|
Or check what we just covered in $PREV_HTML_LINK.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Fallback.
|
||||||
|
default = """
|
||||||
|
---
|
||||||
|
This article is part $SERIES_PAGE_INDEX of $SERIES_PAGES_NUMBER in $SERIES_HTML_LINK.
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Variables
|
||||||
|
|
||||||
|
Series templates support custom variables for additional information you want to include across your series. The process takes two steps:
|
||||||
|
|
||||||
|
1. First, define your **placeholders** in your series configuration (`_index.md`):
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra]
|
||||||
|
series = true
|
||||||
|
series_template_placeholders = ["$POSITION", "$TOPIC", "$DIFFICULTY"]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Then, in each series article, provide the values for these placeholders in `series_template_variables`:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/article.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_template_variables]
|
||||||
|
position = "first"
|
||||||
|
topic = "Variables and Types"
|
||||||
|
difficulty = "Beginner"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Custom Variables
|
||||||
|
|
||||||
|
You can use your custom variables in any template, alongside the built-in variables:
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[extra.series_intro_templates]
|
||||||
|
default = """
|
||||||
|
This is the $POSITION article in $SERIES_HTML_LINK.
|
||||||
|
Today's topic: $TOPIC
|
||||||
|
Difficulty level: $DIFFICULTY
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ admonition(type="warning", text="While placeholders are defined with uppercase (`$POSITION`), the variable names in `series_template_variables` must be lowercase (`position`).") }}
|
||||||
|
|
||||||
|
### Example with Custom Variables
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/_index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# In the series configuration.
|
||||||
|
[extra]
|
||||||
|
series = true
|
||||||
|
series_template_placeholders = ["$LEARNING_TIME", "$KEY_CONCEPTS"]
|
||||||
|
|
||||||
|
series_intro_templates.default = """
|
||||||
|
📚 Part $SERIES_PAGE_INDEX of $SERIES_PAGES_NUMBER
|
||||||
|
⏱️ Estimated time: $LEARNING_TIME
|
||||||
|
🔑 Key concepts: $KEY_CONCEPTS
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ add_src_to_code_block(src="series/02-learning-rust/index.md") }}
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# In an article of the series.
|
||||||
|
[extra.series_template_variables]
|
||||||
|
learning_time = "30 minutes"
|
||||||
|
key_concepts = "Functions, Error Handling, Pattern Matching"
|
||||||
|
```
|
||||||
|
|
||||||
|
This will output:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
📚 Part 2 of 5
|
||||||
|
⏱️ Estimated time: 30 minutes
|
||||||
|
🔑 Key concepts: Functions, Error Handling, Pattern Matching
|
||||||
|
```
|
||||||
|
|
||||||
|
{{ admonition(type="warning", title="Missing Variables", text="If you use a placeholder in your templates but don't provide its value in `series_template_variables`, the build will fail with an error listing the missing variables.") }}
|
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1,162 @@
|
|||||||
|
{#
|
||||||
|
Those macros deal with introduction and navigation for series pages.
|
||||||
|
Using macros have been prefered over partial inclusion or inline code to make sure series_ordered_pages is forced to be used.
|
||||||
|
A section's pages natural order is invalid in case of reversed pagination which would lead to invalid series' pages order.
|
||||||
|
To prevent this, pages are ordered correctly in a separate variable which must be used instead of the series section pages.
|
||||||
|
#}
|
||||||
|
|
||||||
|
{#
|
||||||
|
Computes the introduction of a series's page.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `page`: The page object being part of the series.
|
||||||
|
- `series_section`: The series' section the page belongs to.
|
||||||
|
- `series_ordered_pages`: The series' pages properly ordered (see at the top of this file for an explanation).
|
||||||
|
- `language_strings`: A dictionary containing the translation strings.
|
||||||
|
#}
|
||||||
|
{% macro process_series_template(template_type, page, series_section, series_ordered_pages, language_strings) %}
|
||||||
|
{%- if "series" in series_section.extra and series_section.extra.series -%}
|
||||||
|
{# Prepare variables for substitution #}
|
||||||
|
{%- set series_title = series_section.title -%}
|
||||||
|
{%- set series_permalink = series_section.permalink -%}
|
||||||
|
{%- set series_html_link = '<a href="' ~ series_section.permalink ~ '" aria_label="' ~ series_section.title ~ '">' ~ series_section.title ~ '</a>' -%}
|
||||||
|
{# Build series pages list #}
|
||||||
|
{%- set series_pages_list = [] -%}
|
||||||
|
{%- for series_page in series_ordered_pages -%}
|
||||||
|
{%- if series_page.relative_path == page.relative_path -%}
|
||||||
|
{%- set series_pages_list_item = '<li>' ~ series_page.title ~ '</li>' -%}
|
||||||
|
{%- else -%}
|
||||||
|
{%- set series_pages_list_item = '<li><a href="' ~ series_page.permalink ~ '" aria_label="' ~ series_page.title ~ '">' ~ series_page.title ~ '</a></li>' -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- set_global series_pages_list = series_pages_list | concat(with=series_pages_list_item) -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
{%- set series_pages_list = series_pages_list | join(sep="") -%}
|
||||||
|
{%- if macros_settings::evaluate_setting_priority(setting="post_listing_index_reversed", page=series_section, default_global_value=false) == "true" -%}
|
||||||
|
{%- set series_pages_ordered_list = '<ol reversed>' ~ series_pages_list ~ '</ol>' -%}
|
||||||
|
{%- else -%}
|
||||||
|
{%- set series_pages_ordered_list = '<ol>' ~ series_pages_list ~ '</ol>' -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- set series_pages_unordered_list = '<ul>' ~ series_pages_list ~ '</ul>' -%}
|
||||||
|
|
||||||
|
{# Get page position and navigation info #}
|
||||||
|
{%- set series_pages_number = 0 -%}
|
||||||
|
{%- set series_page_index = 0 -%}
|
||||||
|
{%- set first_page = series_ordered_pages | first -%}
|
||||||
|
{%- set is_found = false -%}
|
||||||
|
|
||||||
|
{%- for series_page in series_ordered_pages -%}
|
||||||
|
{%- set_global series_pages_number = series_pages_number + 1 -%}
|
||||||
|
{%- if series_page.relative_path == page.relative_path -%}
|
||||||
|
{%- set_global series_page_index = series_pages_number -%}
|
||||||
|
{%- set_global is_found = true -%}
|
||||||
|
{%- else -%}
|
||||||
|
{%- if not is_found -%}
|
||||||
|
{%- set_global prev_page = series_page -%}
|
||||||
|
{%- elif not next_page is defined -%}
|
||||||
|
{%- set_global next_page = series_page -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
|
||||||
|
{# Determine template to use based on available navigation #}
|
||||||
|
{%- set position = "middle" -%}
|
||||||
|
{%- if prev_page is defined and not next_page is defined -%}
|
||||||
|
{%- set_global position = "prev_only" -%}
|
||||||
|
{%- elif next_page is defined and not prev_page is defined -%}
|
||||||
|
{%- set_global position = "next_only" -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
{# Get template from config #}
|
||||||
|
{%- set templates_key = "series_" ~ template_type ~ "_templates" -%}
|
||||||
|
{%- set template = "" -%}
|
||||||
|
{%- if series_section.extra[templates_key] is defined -%}
|
||||||
|
{%- if series_section.extra[templates_key][position] is defined -%}
|
||||||
|
{%- set_global template = series_section.extra[templates_key][position] -%}
|
||||||
|
{%- elif series_section.extra[templates_key].default is defined -%}
|
||||||
|
{%- set_global template = series_section.extra[templates_key].default -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
{# Prepare navigation variables #}
|
||||||
|
{%- if prev_page is defined -%}
|
||||||
|
{%- set prev_title = prev_page.title -%}
|
||||||
|
{%- set prev_permalink = prev_page.permalink -%}
|
||||||
|
{%- set prev_html_link = '<a href="' ~ prev_page.permalink ~ '" aria_label="' ~ prev_page.title ~ '">' ~ prev_page.title ~ '</a>' -%}
|
||||||
|
{%- set prev_description = prev_page.description | default(value="") -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- if next_page is defined -%}
|
||||||
|
{%- set next_title = next_page.title -%}
|
||||||
|
{%- set next_permalink = next_page.permalink -%}
|
||||||
|
{%- set next_html_link = '<a href="' ~ next_page.permalink ~ '" aria_label="' ~ next_page.title ~ '">' ~ next_page.title ~ '</a>' -%}
|
||||||
|
{%- set next_description = next_page.description | default(value="") -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
{# Replace standard variables #}
|
||||||
|
{%- set template = template
|
||||||
|
| replace(from="$SERIES_TITLE", to=series_title)
|
||||||
|
| replace(from="$SERIES_PERMALINK", to=series_permalink)
|
||||||
|
| replace(from="$SERIES_HTML_LINK", to=series_html_link)
|
||||||
|
| replace(from="$FIRST_TITLE", to=first_page.title)
|
||||||
|
| replace(from="$FIRST_HTML_LINK", to='<a href="' ~ first_page.permalink ~ '">' ~ first_page.title ~ '</a>')
|
||||||
|
| replace(from="$SERIES_PAGES_NUMBER", to=series_pages_number | as_str)
|
||||||
|
| replace(from="$SERIES_PAGE_INDEX", to=series_page_index | as_str)
|
||||||
|
| replace(from="$SERIES_PAGES_OLIST", to=series_pages_ordered_list)
|
||||||
|
| replace(from="$SERIES_PAGES_ULIST", to=series_pages_unordered_list)
|
||||||
|
-%}
|
||||||
|
|
||||||
|
{# Replace navigation variables if they exist #}
|
||||||
|
{%- if prev_page is defined -%}
|
||||||
|
{%- set template = template
|
||||||
|
| replace(from="$PREV_TITLE", to=prev_title)
|
||||||
|
| replace(from="$PREV_PERMALINK", to=prev_permalink)
|
||||||
|
| replace(from="$PREV_HTML_LINK", to=prev_html_link)
|
||||||
|
| replace(from="$PREV_DESCRIPTION", to=prev_description)
|
||||||
|
-%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
{%- if next_page is defined -%}
|
||||||
|
{%- set template = template
|
||||||
|
| replace(from="$NEXT_TITLE", to=next_title)
|
||||||
|
| replace(from="$NEXT_PERMALINK", to=next_permalink)
|
||||||
|
| replace(from="$NEXT_HTML_LINK", to=next_html_link)
|
||||||
|
| replace(from="$NEXT_DESCRIPTION", to=next_description)
|
||||||
|
-%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
{# Custom placeholders #}
|
||||||
|
{%- if series_section.extra.series_template_placeholders is defined -%}
|
||||||
|
{%- set missing_vars = [] -%}
|
||||||
|
{%- for placeholder in series_section.extra.series_template_placeholders -%}
|
||||||
|
{%- if placeholder in template -%}
|
||||||
|
{%- set var_name = placeholder | replace(from="$", to="") | lower -%}
|
||||||
|
{%- if page.extra.series_template_variables is defined and page.extra.series_template_variables[var_name] is defined -%}
|
||||||
|
{%- set_global template = template | replace(from=placeholder, to=page.extra.series_template_variables[var_name]) -%}
|
||||||
|
{%- else -%}
|
||||||
|
{%- set_global missing_vars = missing_vars | concat(with=var_name) -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
{%- if missing_vars | length > 0 -%}
|
||||||
|
{%- set missing_vars_str = missing_vars | join(sep=", ") -%}
|
||||||
|
{{ throw(message="ERROR: The following variables are included in this page's series templates (`series_template_placeholders`) but have not been set in the `series_template_variables` of this page: " ~ missing_vars_str) }}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
{# Output the processed template if not empty #}
|
||||||
|
{%- if template | length > 0 -%}
|
||||||
|
<section class="series-page-{{ template_type }}">
|
||||||
|
{{ template | markdown | safe }}
|
||||||
|
</section>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{# Macro for series introduction #}
|
||||||
|
{% macro get_introduction(page, series_section, series_ordered_pages, language_strings) %}
|
||||||
|
{{ macros_series_page::process_series_template(template_type="intro", page=page, series_section=series_section, series_ordered_pages=series_ordered_pages, language_strings=language_strings) }}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{# Macro for series outro #}
|
||||||
|
{% macro get_outro(page, series_section, series_ordered_pages, language_strings) %}
|
||||||
|
{{ macros_series_page::process_series_template(template_type="outro", page=page, series_section=series_section, series_ordered_pages=series_ordered_pages, language_strings=language_strings) }}
|
||||||
|
{% endmacro %}
|
@ -0,0 +1,62 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block main_content %}
|
||||||
|
|
||||||
|
{# Throw an error if the section is not flagged as a series. #}
|
||||||
|
{# This page would be displayed properly but it would become impossible for the series' child pages to reference their series. #}
|
||||||
|
{%- if "series" not in section.extra or not section.extra.series -%}
|
||||||
|
{{ throw(message="Section is not flagged as a series. Set `section.extra.series` to `true` if you want to use `series.html` template.") }}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{%- if section.extra.header %}
|
||||||
|
{%- include "partials/home_banner.html" -%}
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
{%- set show_jump = false -%}
|
||||||
|
{%- set show_jump_hierarchy = macros_settings::evaluate_setting_priority(setting="show_jump_to_posts", page=section) -%}
|
||||||
|
{%- if show_jump_hierarchy == "true" -%}
|
||||||
|
{%- set show_jump = true -%}
|
||||||
|
{%- elif show_jump_hierarchy != "false" -%}
|
||||||
|
{#- Default to true if the content is long and var is unset #}
|
||||||
|
{%- if section.content | length > 2000 -%}
|
||||||
|
{%- set show_jump = true -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
{%- if show_jump -%}
|
||||||
|
<div class="title-with-jump bottom-divider">
|
||||||
|
<h1 class="title-container section-title">{{ section.title }}</h1>
|
||||||
|
<a href="#posts-list" class="jump-link">{{ macros_translate::translate(key="jump_to_posts", default="Jump to posts", language_strings=language_strings) }} ↓</a>
|
||||||
|
</div>
|
||||||
|
{%- else -%}
|
||||||
|
{{ macros_page_header::page_header(title=section.title) }}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
<section class="body">
|
||||||
|
{{ section.content | safe }}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div id="posts-list">
|
||||||
|
<h2 class="bottom-divider">
|
||||||
|
{{ macros_translate::translate(key="all_posts", default="All posts", language_strings=language_strings) }}
|
||||||
|
</h2>
|
||||||
|
{%- if paginator %}
|
||||||
|
{%- set pages = paginator.pages -%}
|
||||||
|
{% else %}
|
||||||
|
{%- set pages = section.pages -%}
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
{% set max_posts = section.extra.max_posts | default(value=999999) %}
|
||||||
|
{{ macros_list_posts::list_posts(posts=pages, max=max_posts, metadata="indexes", language_strings=language_strings, section_path=section.path, paginator=paginator | default(value="")) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if paginator %}
|
||||||
|
{%- include "partials/paginate.html" -%}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{%- include "partials/extra_features.html" -%}
|
||||||
|
|
||||||
|
{% endblock main_content %}
|