feat(cms): U1 content model + migration scaffold for blog/news/events

Establish content/ as the source of truth for editable content and
back-fill it from the existing detail pages, per U1 of the no-code CMS
plan.

- docs/content-model.md: front-matter schema (EN/MK/SI), body_format
  html-vs-markdown, content/events -> projects/ output mapping.
- tools/migrate_pages_to_content.py: bs4/yaml extractor; pulls title,
  subtitle, category, ISO date (from ARTICLE_DATES), hero image+caption;
  preserves the article body PLUS trailing sibling sections (event
  galleries) verbatim as body_format: html. Reports unparseable pages
  instead of emitting half-formed files.
- content/**: 51 files (17 entries x EN/MK/SI), 0 skips.
- tools/tests/test_migrate_roundtrip.py: 12 characterization tests
  (structured fields, news-has-no-subtitle, event gallery/lightbox
  preserved, Cyrillic UTF-8 round-trip, every page parses).
- .gitignore: ignore __pycache__/ and *.pyc.

U1 is unblocked and standalone. U2 (render content -> pages) is next;
U5/U6 remain blocked on Open Question Q1 (Gitea<->GitHub topology).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
popovskikandClaude Opus 4.8 committed 2026-08-08 16:18:29 +02:00
1 parent 6204db5f0c
commit 9ddd0f88ef
55 files changed
+4572

No files matched your search

@@ -0,0 +1,40 @@
---
type: event
slug: in-memory-of-frosina-kulakova
lang: en
source_folder: projects
title: In Memory of Frosina Kulakova
subtitle: On February 8, 2025, Macedonian students in Ljubljana gathered for a peaceful public event to honor the memory of Frosina Kulakova, who tragically lost her life in Skopje just days earlier.
date: '2025-02-08'
date_line: Published on February 8, 2025
hero:
src: /images/frosina-1.webp
alt: Candles and flowers in memory of Frosina Kulakova
width: '720'
height: '480'
body_format: html
---
<div class="article-body">
<p>The news of Frosina’s tragic passing on January 29, 2025, deeply shook students across Slovenia. Even though we are far from home, the pain was shared, and the need to come together was clear. Out of this feeling grew the initiative to organize a peaceful public gathering in Ljubljana, where young people could unite, light candles, lay flowers, and dedicate a moment of silence in her memory.</p>
<figure class="article-inline-image">
<img alt="Students lighting candles in memory of Frosina" decoding="async" height="480" loading="lazy" src="../../../images/frosina-2.webp" width="720"/>
</figure>
<h2 id="section-1">A message of unity</h2>
<p>This gathering was not about politics or criticism. It was about togetherness, humanity, and shared responsibility. As students abroad, we wanted to show that we care, that we remember, and that we hope for a better tomorrow where such injustices will no longer happen.</p>
<p>By standing side by side in moments like these, we learn that our strength lies not only in our studies and ambitions but also in our ability to support one another when it matters most.</p>
<figure class="article-inline-image">
<img alt="Flowers and candles laid in remembrance" decoding="async" height="480" loading="lazy" src="../../../images/frosina-3.webp" width="720"/>
</figure>
<h2 id="section-2">Closing thoughts</h2>
<p>Frosina’s story reminded us of the fragility of life and the importance of solidarity. We are grateful to all who came, who lit a candle, or who simply stood with us in silence.</p>
<p>As an organization, MSOS will always strive to provide a space where students can come together, in joy, in struggle, and in remembrance, to shape a community that carries both hope and responsibility for the future.</p>
<p>Rest in peace, Frosina. 🕊️🙏🏻</p>
</div>
<section class="event-gallery">
<h2 class="event-gallery-title">Photos from the event</h2>
<div class="event-gallery-grid js-lightbox">
<button class="eg-thumb" data-caption="In Memory of Frosina Kulakova" data-full="../../../images/frosina-1.webp" type="button"><img alt="In Memory of Frosina Kulakova, photo 1" decoding="async" height="480" loading="lazy" src="../../../images/frosina-1.webp" width="720"/></button>
<button class="eg-thumb" data-caption="In Memory of Frosina Kulakova" data-full="../../../images/frosina-2.webp" type="button"><img alt="In Memory of Frosina Kulakova, photo 2" decoding="async" height="480" loading="lazy" src="../../../images/frosina-2.webp" width="720"/></button>
<button class="eg-thumb" data-caption="In Memory of Frosina Kulakova" data-full="../../../images/frosina-3.webp" type="button"><img alt="In Memory of Frosina Kulakova, photo 3" decoding="async" height="480" loading="lazy" src="../../../images/frosina-3.webp" width="720"/></button>
</div>
</section>
@@ -0,0 +1,40 @@
---
type: event
slug: in-memory-of-frosina-kulakova
lang: mk
source_folder: projects
title: Во спомен на Фросина Кулакова
subtitle: На 8 февруари 2025 година, македонските студенти во Љубљана се собраа на мирен јавен настан за да ја почестат меморијата на Фросина Кулакова, која трагично го загуби животот во Скопје само неколку дена претходно.
date: '2025-02-08'
date_line: Објавено на 8 февруари 2025
hero:
src: /images/frosina-1.webp
alt: Свеќи и цвеќе во спомен на Фросина Кулакова
width: '720'
height: '480'
body_format: html
---
<div class="article-body">
<p>Веста за трагичната смрт на Фросина на 29 јануари 2025 година длабоко ги потресе студентите низ Словенија. И покрај тоа што сме далеку од дома, болката беше споделена, а потребата да се собереме заедно беше јасна. Од ова чувство се роди иницијативата да се организира мирен јавен собир во Љубљана, каде младите можеа да се обединат, да запалат свеќи, да остават цвеќе и да посветат момент на тишина во нејзин спомен.</p>
<figure class="article-inline-image">
<img alt="Студенти палат свеќи во спомен на Фросина" decoding="async" height="480" loading="lazy" src="../../../images/frosina-2.webp" width="720"/>
</figure>
<h2 id="section-1">Порака на единство</h2>
<p>Овој собир не беше за политика или критика. Беше за заедништво, хуманост и споделена одговорност. Како студенти во странство, сакавме да покажеме дека ни е грижа, дека помниме и дека се надеваме на подобро утро каде таквите неправди повеќе нема да се случуваат.</p>
<p>Стоејќи рамо до рамо во моменти како овие, учиме дека нашата сила не лежи само во нашите студии и амбиции, туку и во нашата способност да се поддржуваме еден со друг кога тоа најмногу значи.</p>
<figure class="article-inline-image">
<img alt="Цвеќе и свеќи оставени во спомен" decoding="async" height="480" loading="lazy" src="../../../images/frosina-3.webp" width="720"/>
</figure>
<h2 id="section-2">Завршни мисли</h2>
<p>Приказната на Фросина нè потсети на кревкоста на животот и важноста на солидарноста. Благодарни сме им на сите што дојдоа, што запалија свеќа или што едноставно застанаа со нас во тишина.</p>
<p>Како организација, МСОС секогаш ќе се стреми да обезбеди простор каде студентите можат да се соберат, во радост, во борба и во спомен, за да обликуваат заедница што носи и надеж и одговорност за иднината.</p>
<p>Почивај во мир, Фросина. 🕊️🙏🏻</p>
</div>
<section class="event-gallery">
<h2 class="event-gallery-title">Фотографии од настанот</h2>
<div class="event-gallery-grid js-lightbox">
<button class="eg-thumb" data-caption="Во спомен на Фросина Кулакова" data-full="../../../images/frosina-1.webp" type="button"><img alt="Во спомен на Фросина Кулакова, photo 1" decoding="async" height="480" loading="lazy" src="../../../images/frosina-1.webp" width="720"/></button>
<button class="eg-thumb" data-caption="Во спомен на Фросина Кулакова" data-full="../../../images/frosina-2.webp" type="button"><img alt="Во спомен на Фросина Кулакова, photo 2" decoding="async" height="480" loading="lazy" src="../../../images/frosina-2.webp" width="720"/></button>
<button class="eg-thumb" data-caption="Во спомен на Фросина Кулакова" data-full="../../../images/frosina-3.webp" type="button"><img alt="Во спомен на Фросина Кулакова, photo 3" decoding="async" height="480" loading="lazy" src="../../../images/frosina-3.webp" width="720"/></button>
</div>
</section>
@@ -0,0 +1,40 @@
---
type: event
slug: in-memory-of-frosina-kulakova
lang: si
source_folder: projects
title: V spomin na Frosino Kulakovo
subtitle: 8. februarja 2025 so se makedonski študenti v Ljubljani zbrali na mirnem javnem dogodku v počastitev spomina na Frosino Kulakovo, ki je le nekaj dni prej tragično izgubila življenje v Skopju.
date: '2025-02-08'
date_line: Objavljeno 8. februarja 2025
hero:
src: /images/frosina-1.webp
alt: Sveče in cvetje v spomin na Frosino Kulakovo
width: '720'
height: '480'
body_format: html
---
<div class="article-body">
<p>Novica o tragični smrti Frosine 29. januarja 2025 je globoko pretresla študente po vsej Sloveniji. Čeprav smo daleč od doma, je bila bolečina skupna in potreba po povezovanju jasna. Iz tega občutka je zrasla pobuda za organizacijo mirnega javnega zbora v Ljubljani, kjer bi se mladi lahko združili, prižgali sveče, položili cvetje in ji posvetili trenutek tišine v spomin.</p>
<figure class="article-inline-image">
<img alt="Študenti prižigajo sveče v spomin na Frosino" decoding="async" height="480" loading="lazy" src="../../../images/frosina-2.webp" width="720"/>
</figure>
<h2 id="section-1">Sporočilo enotnosti</h2>
<p>To srečanje ni bilo namenjeno politiki ali kritiki. Šlo je za sočutje, človečnost in skupno odgovornost. Kot študenti v tujini smo želeli pokazati, da nam ni vseeno, da se spominjamo in da upamo na boljši jutri, ko se takšne krivice ne bodo več dogajale.</p>
<p>Ko stojimo drug ob drugem v takšnih trenutkih, se učimo, da naša moč ni le v študiju in ambicijah, ampak tudi v sposobnosti, da drug drugega podpremo, ko je to najpomembnejše.</p>
<figure class="article-inline-image">
<img alt="Cvetje in sveče, položene v spomin" decoding="async" height="480" loading="lazy" src="../../../images/frosina-3.webp" width="720"/>
</figure>
<h2 id="section-2">Sklepne misli</h2>
<p>Frosinina zgodba nas je opomnila na krhkost življenja in pomen solidarnosti. Hvaležni smo vsem, ki so prišli, ki so prižgali svečo ali ki so preprosto stali z nami v tišini.</p>
<p>Kot organizacija si bo MŠOS vedno prizadeval zagotavljati prostor, kjer se študenti lahko zberejo, v veselju, v stiski in v spominu, ter oblikujejo skupnost, ki nosi tako upanje kot odgovornost za prihodnost.</p>
<p>Počivaj v miru, Frosina. 🕊️🙏🏻</p>
</div>
<section class="event-gallery">
<h2 class="event-gallery-title">Fotografije z dogodka</h2>
<div class="event-gallery-grid js-lightbox">
<button class="eg-thumb" data-caption="V spomin na Frosino Kulakovo" data-full="../../../images/frosina-1.webp" type="button"><img alt="V spomin na Frosino Kulakovo, photo 1" decoding="async" height="480" loading="lazy" src="../../../images/frosina-1.webp" width="720"/></button>
<button class="eg-thumb" data-caption="V spomin na Frosino Kulakovo" data-full="../../../images/frosina-2.webp" type="button"><img alt="V spomin na Frosino Kulakovo, photo 2" decoding="async" height="480" loading="lazy" src="../../../images/frosina-2.webp" width="720"/></button>
<button class="eg-thumb" data-caption="V spomin na Frosino Kulakovo" data-full="../../../images/frosina-3.webp" type="button"><img alt="V spomin na Frosino Kulakovo, photo 3" decoding="async" height="480" loading="lazy" src="../../../images/frosina-3.webp" width="720"/></button>
</div>
</section>