diff --git a/2026/2026-08-17--decidim.md b/2026/2026-08-17--decidim.md index 3e5bac5..f2481c1 100644 --- a/2026/2026-08-17--decidim.md +++ b/2026/2026-08-17--decidim.md @@ -35,7 +35,9 @@ login-health: probes + containers + linger + these units enabled. Loopback HTTP ## Public example + isoc - Process **example** (published, not private): `https://decidim.hacktivism.ch/processes/example` -- One published meeting in that process (readable without login). + — title **Öffentliche digitale Allmende** (public-by-default reading). +- Published meeting **Offene Runde: Lesen ohne Konto** + one public proposal + background page. - Homepage block `highlighted_processes` published. - Participant `isoc` on the host (`users.env`); password not in this repo. +- Avatar: gold ISOC globe (same crop as Bonfire `@isoc`); host file `~/koopa-decidim/assets/img/isoc-gold.png`, not in this repo. - Seed: `scripts/decidim/seed-example.sh` / `configs/decidim/seed_example.rb`. diff --git a/configs/decidim/seed_example.rb b/configs/decidim/seed_example.rb index ada85af..e51525c 100644 --- a/configs/decidim/seed_example.rb +++ b/configs/decidim/seed_example.rb @@ -1,16 +1,13 @@ # frozen_string_literal: true -# Public example process + meeting (readable without an account). +# Public process + meeting + page + proposal (readable without an account). # Optional participant from env: DECIDIM_ISOC_EMAIL / DECIDIM_ISOC_PASSWORD. +# Optional avatar: DECIDIM_ISOC_AVATAR (PNG path inside the container). # Usage: bundle exec rails runner /code/lib/koopa_seed_example.rb def t(en, de, fr) { "en" => en, "de" => de, "fr" => fr } end -def html(en, de, fr) - t("
#{en}
", "#{de}
", "#{fr}
") -end - def fail_model!(record) raise "#{record.class}: #{record.errors.full_messages.join("; ")}" end @@ -24,21 +21,40 @@ org.update!(force_users_to_authenticate_before_access_organization: false) slug = "example" process = Decidim::ParticipatoryProcess.find_or_initialize_by(organization: org, slug: slug) process.assign_attributes( - title: t("Example activity", "Beispielaktivität", "Activité exemple"), + title: t( + "Public digital commons", + "Öffentliche digitale Allmende", + "Communs numériques publics" + ), subtitle: t( - "Public demo — no account needed to read", - "Öffentliches Beispiel — Lesen ohne Konto", - "Démo publique — lecture sans compte" + "Which self-hosted services stay readable without an account?", + "Welche selbst gehosteten Dienste bleiben ohne Konto lesbar?", + "Quels services auto-hébergés restent lisibles sans compte ?" ), - short_description: html( - "A published participatory process so visitors can see Decidim without signing in.", - "Ein veröffentlichter Beteiligungsprozess, den Besucherinnen ohne Anmeldung lesen können.", - "Un processus participatif publié, lisible sans compte." + short_description: t( + "hacktivism.ch already runs Lemmy, Bonfire, Castopod and Decidim. This process asks what must stay readable without a login — and what may require an account to write.
", + "hacktivism.ch betreibt bereits Lemmy, Bonfire, Castopod und Decidim. Dieser Prozess fragt, was ohne Anmeldung lesbar bleiben muss — und wofür ein Konto zum Schreiben reicht.
", + "hacktivism.ch fait déjà tourner Lemmy, Bonfire, Castopod et Decidim. Ce processus demande ce qui doit rester lisible sans compte — et ce qui peut exiger un compte pour écrire.
" ), - description: html( - "This is a small public example on hacktivism. Meetings and the process page are visible to anyone. Signing in is only needed to take part.", - "Kleines öffentliches Beispiel auf hacktivism. Prozessseite und Treffen sind für alle sichtbar. Anmelden nur zum Mitmachen.", - "Petit exemple public sur hacktivism. La page du processus et la rencontre sont visibles sans compte. La connexion sert uniquement à participer." + description: t( + "Reading civic material should not depend on creating an account. Writing (proposals, comments, votes) can.
" \ + "Concrete questions:
Anyone can read this process. Sign in only to take part.
", + "Öffentliches Beteiligungsmaterial soll ohne Konto lesbar sein. Schreiben (Vorschläge, Kommentare, Stimmen) kann ein Konto brauchen.
" \ + "Konkrete Fragen:
Lesen ohne Anmeldung. Anmelden nur zum Mitmachen.
", + "Lire du matériel civique ne doit pas exiger un compte. Écrire (propositions, commentaires, votes) peut le faire.
" \ + "Questions concrètes :
Lecture sans compte. Connexion seulement pour participer.
" ), start_date: Date.current, end_date: Date.current + 180, @@ -53,8 +69,12 @@ puts "process id=#{process.id} slug=#{process.slug} published=#{process.publishe step = process.steps.find_or_initialize_by(position: 1) step.assign_attributes( - title: t("Open", "Offen", "Ouvert"), - description: html("Example step.", "Beispielphase.", "Étape exemple."), + title: t("Open discussion", "Offene Diskussion", "Discussion ouverte"), + description: t( + "Collect positions on public-by-default reading. No decision this step.
", + "Positionen zum Lesen ohne Konto sammeln. In dieser Phase keine Entscheidung.
", + "Recueillir les positions sur la lecture sans compte. Pas de décision à cette étape.
" + ), start_date: Date.current, end_date: Date.current + 180, active: true @@ -62,6 +82,32 @@ step.assign_attributes( step.save || fail_model!(step) puts "step id=#{step.id} active=#{step.active}" +pages = Decidim::Component.find_or_initialize_by( + participatory_space: process, + manifest_name: "pages" +) +pages.assign_attributes( + name: t("Background", "Hintergrund", "Contexte"), + published_at: pages.published_at || Time.current, + visible: true, + weight: 0 +) +pages.save || fail_model!(pages) +page = Decidim::Pages::Page.find_or_initialize_by(component: pages) +page.body = t( + "ISOC-style public-interest internet: people should inspect how a civic tool works without first handing over an identity.
" \ + "On this instance the stock cookie banner is hidden; only essential consent is set. The same idea applies to participation spaces: the text of a process is not a privilege of registered users.
" \ + "This page is part of that argument. It is published on purpose.
", + "Internet im öffentlichen Interesse: man soll sehen können, wie ein Beteiligungswerkzeug arbeitet, ohne zuerst eine Identität abzugeben.
" \ + "Auf dieser Instanz ist der Vorrats-Cookie-Banner ausgeblendet; es gilt nur essential. Dieselbe Idee gilt für Beteiligungsräume: der Text eines Prozesses ist kein Privileg registrierter Konten.
" \ + "Diese Seite ist absichtlich öffentlich.
", + "Internet d'intérêt public : on doit pouvoir voir comment un outil civique fonctionne sans d'abord céder une identité.
" \ + "Ici, la bannière cookies par défaut est masquée ; seul l'essentiel est posé. Même idée pour les espaces de participation : le texte d'un processus n'est pas un privilège des comptes.
" \ + "Cette page est publique volontairement.
" +) +page.save || fail_model!(page) +puts "pages component id=#{pages.id} page=#{page.id}" + meetings = Decidim::Component.find_or_initialize_by( participatory_space: process, manifest_name: "meetings" @@ -81,20 +127,38 @@ meeting = Decidim::Meetings::Meeting.find_or_initialize_by( component: meetings, author: admin ) -if meeting.new_record? || meeting.title.blank? - meeting.title = t("Open example meeting", "Offenes Beispieltreffen", "Rencontre exemple ouverte") -end +meeting.title = t( + "Open session: reading without an account", + "Offene Runde: Lesen ohne Konto", + "Séance ouverte : lire sans compte" +) meeting.assign_attributes( - description: html( - "Anyone can open this meeting page without an account. It is a demo activity on decidim.hacktivism.ch.", - "Diese Treffen-Seite ist ohne Konto lesbar. Beispielaktivität auf decidim.hacktivism.ch.", - "Cette page de rencontre est lisible sans compte. Activité exemple sur decidim.hacktivism.ch." + description: t( + "Public agenda (no registration to read):
Online / public. Signing in is optional.
", + "Öffentliche Traktanden (Lesen ohne Anmeldung):
Online / öffentlich. Anmelden ist freiwillig.
", + "Ordre du jour public (lecture sans inscription) :
En ligne / public. Connexion facultative.
" ), start_time: meeting.start_time || start_at, end_time: meeting.end_time || finish_at, - address: "hacktivism", - location: t("Online / public", "Online / öffentlich", "En ligne / public"), - location_hints: t("No registration required to read.", "Zum Lesen keine Anmeldung.", "Pas d'inscription pour lire."), + address: "hacktivism.ch", + location: t("Online, public stream notes", "Online, öffentliche Notizen", "En ligne, notes publiques"), + location_hints: t( + "No ticket. Page stays readable if you do not attend.", + "Kein Ticket. Die Seite bleibt lesbar, wenn man nicht teilnimmt.", + "Pas de billet. La page reste lisible sans y assister." + ), type_of_meeting: "in_person", registration_type: "registration_disabled", registrations_enabled: false, @@ -106,6 +170,36 @@ meeting.assign_attributes( meeting.save || fail_model!(meeting) puts "meeting id=#{meeting.id} published=#{meeting.published_at} start=#{meeting.start_time}" +proposals = Decidim::Component.find_or_initialize_by( + participatory_space: process, + manifest_name: "proposals" +) +proposals.assign_attributes( + name: t("Proposals", "Vorschläge", "Propositions"), + published_at: proposals.published_at || Time.current, + visible: true, + weight: 2 +) +proposals.save || fail_model!(proposals) + +proposal = Decidim::Proposals::Proposal.find_or_initialize_by(component: proposals) +if proposal.new_record? || proposal.title.blank? + proposal.title = t( + "Keep public processes readable without login", + "Öffentliche Prozesse ohne Anmeldung lesbar halten", + "Garder les processus publics lisibles sans connexion" + ) + proposal.body = t( + "Decide as a standing rule: published process pages, meetings and this proposal list stay readable without an account. Login is only for creating or endorsing proposals, commenting, and administration.
", + "Als Regel festhalten: veröffentlichte Prozessseiten, Treffen und diese Vorschlagsliste bleiben ohne Konto lesbar. Login nur zum Erstellen oder Unterstützen von Vorschlägen, Kommentieren und für die Administration.
", + "En faire une règle : les pages de processus publiées, les rencontres et cette liste de propositions restent lisibles sans compte. Connexion seulement pour créer ou soutenir, commenter, et pour l'administration.
" + ) + proposal.add_coauthor(admin) + proposal.published_at = Time.current +end +proposal.save || fail_model!(proposal) +puts "proposal id=#{proposal.id} published=#{proposal.published_at}" + block = Decidim::ContentBlock.find_or_initialize_by( organization: org, scope_name: "homepage", @@ -132,8 +226,22 @@ else isoc.accepted_tos_version = org.tos_version isoc.locale = org.default_locale isoc.save || fail_model!(isoc) + avatar_path = ENV["DECIDIM_ISOC_AVATAR"].to_s + if !avatar_path.empty? && File.file?(avatar_path) + isoc.avatar.purge if isoc.avatar.attached? + isoc.avatar.attach( + io: File.open(avatar_path, "rb"), + filename: "isoc-gold.png", + content_type: "image/png" + ) + isoc.save || fail_model!(isoc) + puts "isoc avatar attached from #{avatar_path} blob=#{isoc.avatar.blob&.byte_size}" + else + puts "isoc avatar skipped (DECIDIM_ISOC_AVATAR missing)" + end puts "isoc user id=#{isoc.id} email=#{isoc.email} nick=#{isoc.nickname} admin=#{isoc.admin} confirmed=#{isoc.confirmed_at}" end puts "public process: https://#{host}/processes/#{process.slug}" puts "public meeting: https://#{host}/processes/#{process.slug}/f/#{meetings.id}/meetings/#{meeting.id}" +puts "public proposal: https://#{host}/processes/#{process.slug}/f/#{proposals.id}/proposals/#{proposal.id}" diff --git a/scripts/decidim/seed-example.sh b/scripts/decidim/seed-example.sh index 89dbd05..51f1dba 100755 --- a/scripts/decidim/seed-example.sh +++ b/scripts/decidim/seed-example.sh @@ -20,6 +20,13 @@ else fi podman cp "${src}" koopa-decidim:/code/lib/koopa_seed_example.rb +avatar_host="${DECIDIM_ISOC_AVATAR:-${HOME}/koopa-decidim/assets/img/isoc-gold.png}" +avatar_ctr="" +if [ -f "${avatar_host}" ]; then + avatar_ctr="/tmp/isoc-gold.png" + podman cp "${avatar_host}" "koopa-decidim:${avatar_ctr}" +fi + podman exec \ -e DECIDIM_HOST="${DECIDIM_HOST:-decidim.hacktivism.ch}" \ -e DECIDIM_ADMIN_EMAIL \ @@ -27,5 +34,6 @@ podman exec \ -e DECIDIM_ISOC_PASSWORD \ -e DECIDIM_ISOC_NAME \ -e DECIDIM_ISOC_NICKNAME \ + -e DECIDIM_ISOC_AVATAR="${avatar_ctr}" \ koopa-decidim bundle exec rails runner /code/lib/koopa_seed_example.rb echo "seed done. public: https://decidim.hacktivism.ch/processes/example"