Commit graph

578 commits

Author SHA1 Message Date
2d688a14a3 Update .forgejo/workflows/deploy.yaml
Some checks failed
deploy blog / deploy (push) Failing after 58s
2026-08-25 20:47:17 +02:00
4d692c5b48 Update .forgejo/workflows/deploy.yaml
Some checks failed
deploy blog / deploy (push) Failing after 1m5s
2026-08-25 20:45:27 +02:00
746583df5b Merge pull request 'Fixes fuer unseren Kalender auf der website' (#57) from hauke/www:kalender into staging
Some checks failed
deploy blog / deploy (push) Failing after 1m25s
Reviewed-on: #57
2026-08-23 01:14:45 +02:00
6782da49d4 Move what the two calendar views share into one module
Both views read the same file and ask it the same question, only the
answer is presented differently: the start page lists the next few
occurrences, the calendar page marks the occurrences of one month. Since
they were taught to read a calendar properly they also carry the same
code for it, twice and word for word, some 130 lines of `eventUrl()`,
`exceptionsByUid()`, `iterationEnd()`, the fetch with its check of the
response and the walk over the events of the calendar. Every fix so far
had to be written twice, and the next one that is only written once
leaves the two views disagreeing about the same calendar.

Put it into `assets/js/events.js`, which offers what both need:

- `loadCalendar()` fetches and parses `/calendars/all.ics`
- `occurrencesBetween()` walks the occurrences that touch a window,
  expanding recurring events and resolving the ones that were modified
  on their own
- `eventUrl()` reads the URL of an event

`upcoming.js` and `calendar.js` keep what is really theirs, the shape of
their entries and how they are drawn, and both lose their own import of
ical.js: it is the concern of the module that reads the calendar now.
The two of them shrink from 259 and 549 lines to 105 and 406.

Hugo bundles the module into both scripts, so no shortcode changes and
no second request.

The walk keeps an occurrence when it starts at or before the end of the
window and ends after its start. That is the condition the start page
used; the calendar page compared against the start of the month with "<"
instead of "<=". Its window reaches two days past the month, so the day
this can differ on is nowhere near it.

No behaviour changes with this: the upcoming list over 120 days and
every day of the month view from 2025 to 2028, taken from
https://berlin.ccc.de/calendars/all.ics with the name, URL, start, end
and all day flag of each entry, are identical before and after, and so
are the results of the tests for moved occurrences, for modifications
between two series and for all day events in Berlin, Tokyo, Los Angeles
and Kiritimati.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-08-23 01:07:25 +02:00
75dd9ca997 Separate date and name in the upcoming events table
The two columns of the table on the start page touched each other, so the entry
read "Donnerstag, 27.08., 19:00 UhrClub Discordia".

The table is created with the classes "table table-condensed", which no
stylesheet of the site defines, and the table styling that the theme applies
inside prose addresses "tbody td". The table is delivered empty and its rows
are added through the DOM, where a tr appended to a table stays a direct child
instead of being put into a tbody the way the HTML parser would. The rows are
therefore outside of any tbody and the padding of the theme never applied.

Give the column holding the date its own padding, and keep the date on one
line, it is one piece of information and reads badly broken after the weekday.

The padding alone does not fit, though. The table stands in a prose column that
the theme limits to 65 characters so that running text stays readable, and a
date and the name of an event next to each other are wider than that, so the
names would be wrapped over several lines. Lift the limit off the column and
put it back on everything in it except the table, which leaves the table room
to grow while the heading and the paragraph around it keep their width.

That much space then has to be filled sensibly. The theme lays a table out as a
block, "table { display: block; overflow: auto }", so that a wide one can be
scrolled sideways, and a block fills its parent instead of shrinking to its
content the way a table does. Spanning the page the entries would all sit at
its left edge. Ask for the width of the content with fit-content, which the
automatic margins then centre.

Addressing the table by its id keeps all of this to the start page and takes
precedence over the theme, whose prose rules are written with :where() and
carry no specificity.

Measured in a browser at 1280, 768 and 500 pixels: date and name share one line
at the first two, the table is 602 pixels wide with the same distance left and
right, and at 500 the column is narrower than the table, so the table fills it
and only the longest name wraps. The page never scrolls sideways.

Fixes: c28f04c6e8 ("switch to ics files; make calendars work; fix some minor issues")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-08-23 01:07:25 +02:00
60ab628270 Parse the calendar with ical.js
The calendar page brought its own ICS parser and its own RRULE expansion. Both
only covered the cases that happened to be needed when they were written, and
the calendar has moved on since. Against the published calendar, for September
2026:

  Spieleabend  FREQ=WEEKLY;INTERVAL=2;BYDAY=SA  shown 05. 12. 19. 26., correct 05. 19.
  CCCB Plenum  FREQ=MONTHLY;BYDAY=TU;BYSETPOS=2 shown 01.,               correct 08.
  CCCB Plenum  FREQ=MONTHLY;BYDAY=TU;BYSETPOS=4 shown 01.,               correct 22.

INTERVAL was only read for monthly rules, so the Spieleabend was shown twice as
often as it takes place. BYSETPOS was not implemented at all, and since
parseInt("TU") is NaN the fallback turned both Plenum rules into "first
Tuesday", putting two Plenums on a day without one and none on the two days
with one. UNTIL, COUNT, EXDATE, RECURRENCE-ID, BYMONTHDAY and a BYDAY listing
more than one weekday were not handled either.

The text was no better. Content lines longer than 75 characters are continued
on the next line, of which there are 477 in the calendar, and the parser did
not join them, so it cut values off in the middle of a word. It also split
every line at the first colon, which lands inside the parameter of
DESCRIPTION;ALTREP="data:text/html,...". And it never resolved the escaping, so
"\n" was shown as those two characters. 30 of 31 descriptions were wrong:

  before: "Der Club Discordia ist ein öffentliches Treffen in den Clubr"
  after:  "Der Club Discordia ist ein öffentliches Treffen in den Clubräumen des CCC Berlin"

Hand the parsing and the expansion to ical.js, which is vendored for the start
page anyway. The month view now asks the library for the occurrences that touch
the month, which removes the reimplementation along with all of the above.

While the events are being reduced to what the view needs:

- An event is entered on every day it covers, so the Amateurfunk trip from
  30.10. to 01.11. is no longer marked on 30.10. alone. The end of an event is
  not part of it, so one ending at midnight stays on the day before.
- A time that names a zone is converted to Europe/Berlin instead of being read
  off the digits of the ICS string. Every event currently carries
  TZID=Europe/Berlin, so the wall clock time shown does not change, but a UTC
  timestamp would have been shown in UTC. A date is a different matter, see
  further down.
- The URL of the event is used for the link in the detail panel. Events without
  one are shown without a link, as on the start page. This replaces
  createEventLink(), which guessed URLs from the title and was never called,
  and the panel no longer builds an <h> element, which is not an element.

Descriptions may contain line breaks, so keep them in the panel. The times of
an event are labelled in German like the rest of the page, "Beginn" and "Ende"
instead of "Start" and "End".

An all day event carries a date, and a date has neither a time nor a zone: its
digits are the day itself. `toJSDate()` reads them as midnight in the zone of
the browser, and deriving the Berlin day from that afterwards moves the event
by the offset between the two, so the promise of the same days everywhere would
have held for every event except the ones that consist of nothing but days. An
event on 30. and 31.08. would have been marked on:

  Berlin       30. 31.08.
  Los Angeles  30. 31.08. 01.09.
  Tokio        29. 30. 31.08.

Take the day from the ICAL time, which still knows whether it names a day or a
point in time, and convert only the latter. Stepping to the end of the event
moves by a day where it is made of days and by a second where it is not, which
also expresses "the end is not part of the event" in the terms of the event
itself. The occurrences of a day are sorted by their start, which for an all
day event is that same midnight, so order them before the timed events instead.

Resolving an occurrence that was modified on its own needs two more things to
be right. Unless it is told which modifications belong to an event,
`ICAL.Event` relates every VEVENT with a RECURRENCE-ID in the file to every
recurring event and keys them by the recurrence id alone; the UID is only
compared with `strictExceptions`, which then throws instead of skipping. A
modification would therefore also override the occurrence another series holds
at the same instant, so the detail panel of that day would show the wrong event
and the modified one twice. Group the modifications by the UID of the event
they belong to and hand each event its own.

And the expansion walks the unmodified recurrence times, so an occurrence
pulled forward into the month from a later one would never be reached: the walk
stops at its original time, and the month it was moved out of drops it because
it no longer falls into it, which loses it from the calendar altogether.
Iterate far enough that the largest move towards the past can still reach the
month. Moving a Plenum or a Club Discordia to the week before, out of the way
of a holiday, is exactly what produces such a modification. With a weekly
series whose occurrence of 07.09. is moved to 28.08.:

  before: August     03. 10. 17. 24. 31.
          September  14. 21. 28.
  after:  August     03. 10. 17. 24. 28. 31.
          September  14. 21. 28.

The URL ends up in the href of a link and the calendar is exported from a
CalDAV server, so whoever may write to it decides what that is;
`URL:javascript:alert(1)` on an event would run that script when a visitor
clicks the name. Pass on nothing but http and https.

The stylesheet of the page goes through `minify | fingerprint` while the script
next to it is rewritten, so both are delivered the way the assets of the start
page already are: smaller, under a name that carries their content hash, and
with an integrity hash in the tag.

The published calendar has neither all day events nor RECURRENCE-ID today, both
can be created in the CalDAV calendar the export comes from. Checked in Berlin,
Tokyo, Los Angeles and Kiritimati: an all day event over 30. and 31.08. is
marked on those two days in all four, one from 31.08. to 02.09. is marked
across the month boundary, and the month view of the published calendar is the
same in all of them.

Fixes: 4068fab565 ("improved calendar and fixed url temporarily")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-08-23 01:07:25 +02:00
a0e1ef046a Vendor ical.js instead of loading it from unpkg.com
The upcoming events table pulled its ICS parser straight from a CDN with
`import ICAL from "https://unpkg.com/ical.js/dist/ical.min.js"`. That sends
every visitor of the start page to unpkg.com, which hands their IP address and
user agent to a third party before any of our own code runs. The URL is not
even pinned to a version, so whatever ical.js publishes next is executed on our
site without anybody looking at it, and the start page silently breaks when the
CDN is unreachable.

Check the parser into `assets/js/vendor/` and let Hugo bundle it. This is what
`js.Build` is for: it runs the esbuild that is built into Hugo, so it resolves
the import at build time and needs no node_modules and no extra tooling in the
build environment. The result is minified and fingerprinted like the other
scripts of the site, and the script tag carries a subresource integrity hash.

Since the bundle now has a content hash in its name, its URL cannot be written
by hand in the markdown any more. Move the table and the script tag into an
`upcoming` shortcode, which is the same pattern `calendar.html` already uses,
and move `upcoming.js` from `static/` to `assets/` so Hugo can process it.

The vendored file is the unminified `dist/ical.js` of the pinned release: it
carries the MPL-2.0 header and is the source form of what we ship, and Hugo
minifies it for delivery anyway. `assets/js/vendor/README.md` records the
version, where it came from and how to update it.

The built bundle renders the same table as before, checked against
https://berlin.ccc.de/calendars/all.ics.

Fixes: c28f04c6e8 ("switch to ics files; make calendars work; fix some minor issues")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-08-23 01:07:25 +02:00
4545852327 Report errors while loading the calendar
The response of the fetch went to the parser without ever looking at it. When
the calendar could not be loaded the error page of the web server was parsed as
a calendar, which threw inside a promise nobody was waiting on. The result was
an empty table, an unhandled rejection in the console and an error message
about broken calendar syntax that says nothing about the actual problem, a
calendar that is not there.

Refuse a response that is not ok, naming the status, and log failures in the
chain, like the calendar page already does.

  before: Uncaught (in promise) Error: invalid line (no token ";" or ":")
                                       "<html>404 Not Found</html>"
  after:  Fehler beim Laden der Termine:
          Error: /calendars/all.ics: 404 Not Found

The table stays empty either way, there is nothing to show without a calendar.

Fixes: c28f04c6e8 ("switch to ics files; make calendars work; fix some minor issues")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-08-23 01:07:25 +02:00
cd39842ac0 Show event times in Berlin time
`toLocaleString()` was given the German locale but no time zone, so it printed
the time in whatever zone the browser of the visitor is set to. The events
happen in Berlin, so this is only correct for visitors who are in Berlin.
Somebody reading the start page from Sydney was told the Plenum of Tuesday
20:00 takes place on Wednesday at 04:00.

Format in Europe/Berlin explicitly. For an event that names a point in time
only the printing was wrong, picking and sorting work on absolute points in
time and were not affected.

An all day event carries a date, and a date has neither a time nor a zone: its
digits are the day itself. `toJSDate()` reads them as midnight in the zone of
the browser, so the point in time that is then printed in Berlin time is off by
the offset between the two, which puts a bogus time of day on the entry and,
far enough east or west, the wrong day. An event on 30.08. was announced as:

  Berlin       Sonntag, 30.08., 00:00 Uhr
  Los Angeles  Sonntag, 30.08., 09:00 Uhr
  Tokio        Samstag, 29.08., 17:00 Uhr

Keep the digits of the date and read them as UTC, which no browser setting
moves, and print an all day event in UTC and without a time of day. Berlin,
Tokyo, Los Angeles and Kiritimati all say "Sonntag, 30.08." now, while a timed
event keeps saying "Sonntag, 30.08., 19:00 Uhr" everywhere. Sorting improves
with it, an all day event no longer changes its place in the list depending on
where the visitor sits.

The published calendar has no all day events today, they can be created in the
CalDAV calendar the export comes from.

Drop the two replacements around the formatted date while touching it. The
first removes the comma after the weekday and the second puts it back, so they
cancel each other out:

  "Samstag, 22.08., 17:00" -> "Samstag 22.08., 17:00" -> "Samstag, 22.08., 17:00"

Fixes: c28f04c6e8 ("switch to ics files; make calendars work; fix some minor issues")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-08-23 01:07:25 +02:00
6d99a39190 Do not list a moved event occurrence twice
An occurrence of a recurring event that was changed on its own carries a
RECURRENCE-ID and is stored as an additional VEVENT next to the event it
belongs to. `getAllSubcomponents("vevent")` returns those components as well,
and since they have no RRULE of their own they were handled as separate single
events. The occurrence therefore ended up in the list twice: once from
expanding the recurring event, which resolves the modified time through the
exception, and once more from the extra VEVENT.

To make it worse the two rows disagreed, because name and URL were taken from
the recurring event while the time came from the modification, so the first row
showed the new time under the old name.

Skip components that are a recurrence exception, they are already covered by
the event they modify, and take name and URL from the occurrence details, which
point at the modification where there is one and at the event itself otherwise.
Events whose RECURRENCE-ID refers to an event that is not in the file are
dropped by this, which cannot happen in a full calendar export.

With a recurring Plenum whose 25.08. occurrence is moved two hours earlier and
renamed:

  before: 25.08. 18:00  CCCB Plenum
          25.08. 18:00  CCCB Plenum (verschoben)
  after:  25.08. 18:00  CCCB Plenum (verschoben)

Leaning on the resolution ical.js does here needs two more things to be right.

The first is which modifications an event is asked to resolve. Unless it is
told, `ICAL.Event` relates every VEVENT with a RECURRENCE-ID in the file to
every recurring event and keys them by the recurrence id alone; the UID is only
compared with `strictExceptions`, which then throws instead of skipping. A
modification would therefore also override the occurrence another series holds
at the same instant, so the wrong entry is shown and the modified one twice
over. Group the modifications by the UID of the event they belong to and hand
each event its own, which also turns the relating off for events that have
none. Recognise a modification on the component instead of on the event,
because relating exceptions to an exception throws. With two unrelated weekly
series that both meet on Thursday at 19:00, of which only A has its occurrence
of 03.09. moved to 17:00:

  before: 27.08. 19:00 Serie A          after: 27.08. 19:00 Serie A
          27.08. 19:00 Serie B                 27.08. 19:00 Serie B
          03.09. 17:00 Serie A (versch.)       03.09. 17:00 Serie A (versch.)
          03.09. 17:00 Serie A (versch.)       03.09. 19:00 Serie B

The second is how far the expansion has to run. It walks the unmodified
recurrence times and stopped at the end of the window, but the occurrence
handed to `getOccurrenceDetails()` may have been moved somewhere else entirely.
Both directions were wrong: an occurrence pulled forward from beyond the window
was never reached, because the walk had already stopped at its original time,
and one pushed out of the window was still listed, because only its original
time was ever compared against the window. Iterate far enough that the largest
move towards the past can still reach the window, and decide by the time the
occurrence really takes place at. The stop condition keeps using the raw
recurrence time, which stays monotonic, so the iteration still terminates. With
a weekly series whose occurrence of 05.10. is pulled forward to 25.08. and
whose occurrence of 31.08. is pushed to 02.11., seen from 23.08. through the 20
day window:

  before: 24.08. Serie                 after: 24.08. Serie
          31.08. Serie                        25.08. Serie (vorgezogen)
          07.09. Serie                        07.09. Serie

Reading the URL becomes a function of its own while name and URL move to the
occurrence details, and it now looks at what it reads. The calendar is exported
from a CalDAV server, so whoever may write to it decides what ends up in the
href of the link, and `URL:javascript:alert(1)` on an event would run that
script when a visitor clicks the entry. Pass on nothing but http and https; a
value that is not a URL at all no longer reaches the href either.

The published calendar contains no RECURRENCE-ID at all today, so nothing about
the modifications changes for it. It is exported from a CalDAV server though,
and moving a single Club Discordia or Plenum out of the way of a holiday is
exactly what creates such a modification. Checked against
https://berlin.ccc.de/calendars/all.ics: the list and the links of the 30
events that carry a URL are unchanged.

Fixes: c28f04c6e8 ("switch to ics files; make calendars work; fix some minor issues")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-08-23 01:07:25 +02:00
8b692a2e88 Keep running events in the upcoming list until they end
The "Nächste Veranstaltungen" table selected events with `start > now`, so an
event disappeared from the list the moment it began. Someone looking at the
start page at 20:30 no longer saw the Plenum that had started at 20:00 and ran
until 22:00, which is exactly when that information is most useful.

Select on the end of the event instead, so an event stays listed for as long as
it is still running. A currently running event sorts first, because the list is
ordered by start time.

For recurring events the end of the individual occurrence is needed, not the
end of the series, so go through `getOccurrenceDetails()`. That also resolves
occurrences overridden via RECURRENCE-ID. The chronological break out of the
iteration keeps using the raw occurrence time, which stays monotonic even when
an override moves a single occurrence. `ICAL.Event.endDate` falls back to
DURATION and, for all-day events, to the following day, so events without an
explicit DTEND keep working.

The `maxDays` window still applies to the start of an event, so a long running
event does not extend the window.

This is not a regression from the commit below, the Python generator it
replaced filtered on `dtstart >= start` in the same way.

Checked against https://berlin.ccc.de/calendars/all.ics: the Plenum (20:00 to
22:00) is now listed at 20:30 and 21:59 and gone at 22:01, and the multi-day
Amateurfunk trip (30.10. 12:00 to 01.11. 18:00) stays listed throughout.

Fixes: c28f04c6e8 ("switch to ics files; make calendars work; fix some minor issues")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-08-23 01:07:25 +02:00
b8ec457818 Link upcoming events to their ICS URL
The "Nächste Veranstaltungen" table read the event URL via `ICAL.Event.url`,
but ical.js does not expose a `url` getter on `ICAL.Event` (it only has uid,
summary, description, color, location, sequence, the dates, organizer and
attendees). `event.url` was therefore always `undefined` and the `?? ""`
fallback turned it into an empty string, so every row rendered as
`<a href="">`, a dead link that just reloads the start page.

Read the URL from the VEVENT component instead. This also picks up the
`URL;VALUE=URI:` form used by most events in the published calendar, which is
exported from a CalDAV client and does not use a bare `URL:` property.

Not every event has a URL, so only wrap the name in a link when one is
present and emit plain text otherwise.

Checked against https://berlin.ccc.de/calendars/all.ics (31 events, 1 of them
without a URL):

  before: <td><a href="">CCCB Plenum</a></td>
  after:  <td><a href="https://wiki.berlin.ccc.de/Plenum">CCCB Plenum</a></td>
  after:  <td>Aktionstag gegen Überwachung im Chaos Computer Club Berlin</td>

Fixes: c28f04c6e8 ("switch to ics files; make calendars work; fix some minor issues")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-08-23 01:07:25 +02:00
b11d80aac2 README: describe the calendar as it is built today
`build.sh` no longer post-processes anything: since the switch to ICS files it
just deletes `public/` and runs `hugo`. The `CALENDAR` placeholder, the Python
dependency on `icalendar`, `python-dateutil` and `pytz`, and the `de_DE.UTF-8`
locale requirement are all gone, and nothing in `packages.nix`, `devShells.nix`
or `flake.nix` pulls in Python any more. Drop those claims.

Describe instead where the two calendar views actually come from: the
"Nächste Veranstaltungen" table and the calendar page are rendered in the
browser from `/calendars/all.ics`, which is published next to the site and is
not built from this repository. That also replaces the old advice to preview
them via `./build.sh` plus a local HTTP server, which no longer helps.

Without that file the two of them stay empty. Hugo serves everything below
`static/` from the root of the site, so it can simply be put there, and the
command that downloads it is worth writing down. Checked from an empty state:
afterwards the file is served under `/calendars/all.ics` by `hugo serve` and the
table on the start page fills with the upcoming events. It is a copy of
published data and has no business in the repository, so ignore it.

Mention the per-section `.ics` feeds Hugo does generate, so the ICS templates
under `layouts/` are not mistaken for the source of `all.ics`.

Fixes: c28f04c6e8 ("switch to ics files; make calendars work; fix some minor issues")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-08-22 21:22:02 +02:00
9be6287a68 Delete .forgejo/workflows/test.yaml
Some checks failed
deploy blog / deploy (push) Failing after 58s
2026-08-22 15:22:52 +02:00
df7add59ee Add .forgejo/workflows/test.yaml
Some checks failed
deploy blog / deploy (push) Failing after 12s
2026-08-21 22:32:15 +02:00
e93b399854 Update .forgejo/workflows/deploy.yaml
Some checks failed
deploy blog / deploy (push) Failing after 12s
Signed-off-by: xengi <cccb-git@xengi.de>
2026-08-21 21:36:22 +02:00
f6b5413d19 Delete .forgejo/workflows/miau.yaml
All checks were successful
deploy blog / deploy (push) Successful in 40s
2026-08-12 02:26:23 +02:00
2696f0af4e python is not needed anymore
Some checks failed
deploy blog / deploy (push) Failing after 37s
Signed-off-by: xengi <cccb-git@xengi.de>
2026-08-12 02:06:53 +02:00
b9739ca58c Merge branch 'production' into staging
Some checks failed
deploy blog / deploy (push) Has been cancelled
2026-08-12 02:05:42 +02:00
facc7d15bf Merge pull request 'switch to ics files; make calendars work; fix some minor issues' (#53) from caldav into staging
Some checks failed
deploy blog / deploy (push) Failing after 38s
Reviewed-on: #53
2026-08-12 02:03:34 +02:00
c28f04c6e8
switch to ics files; make calendars work; fix some minor issues 2026-08-12 02:01:58 +02:00
1dcc6c473b Merge pull request 'staging' (#52) from staging into production
All checks were successful
deploy blog / deploy (push) Successful in 54s
Reviewed-on: #52
2026-08-11 23:32:50 +02:00
5368e75240 Add .forgejo/workflows/miau.yaml
All checks were successful
deploy blog / deploy (push) Successful in 49s
2026-08-11 23:31:40 +02:00
b1ff3727c8 Update .forgejo/workflows/deploy.yaml
All checks were successful
deploy blog / deploy (push) Successful in 56s
Signed-off-by: xengi <cccb-git@xengi.de>
2026-08-11 23:21:32 +02:00
4e2c358668 Merge pull request 'fu github pipelines' (#51) from staging into production
Some checks failed
deploy blog / deploy (push) Failing after 50s
Reviewed-on: #51
2026-08-11 23:15:28 +02:00
e690426b3f Merge branch 'production' into staging
Some checks failed
deploy blog / deploy (push) Failing after 50s
2026-08-11 23:15:14 +02:00
7689abaf3e Update .forgejo/workflows/deploy.yaml
Some checks failed
deploy blog / deploy (push) Failing after 51s
2026-08-11 23:13:51 +02:00
bafe2bcb81 Update .forgejo/workflows/deploy.yaml
All checks were successful
deploy blog / deploy (push) Successful in 52s
2026-08-11 23:05:26 +02:00
bac896e60f Update .forgejo/workflows/deploy.yaml
All checks were successful
deploy blog / deploy (push) Successful in 51s
2026-08-11 22:57:01 +02:00
87e1230301
unfix pipeline
All checks were successful
deploy blog / deploy (push) Successful in 52s
2026-08-11 22:45:17 +02:00
f839b1b01a Update .forgejo/workflows/deploy.yaml
All checks were successful
deploy blog / deploy (push) Successful in 53s
Signed-off-by: xengi <cccb-git@xengi.de>
2026-08-11 22:37:45 +02:00
2ba5b8cfa8
fix pipeline
Some checks failed
deploy blog / deploy (push) Failing after 54s
2026-08-11 22:29:30 +02:00
96919a7188
fix pipeline
Some checks failed
deploy blog / deploy (push) Failing after 52s
2026-08-11 22:20:30 +02:00
dfae28d575 Update .forgejo/workflows/deploy.yaml
All checks were successful
deploy blog / deploy (push) Successful in 52s
2026-08-11 22:10:47 +02:00
c2a2f6e160 Update .forgejo/workflows/deploy.yaml
All checks were successful
deploy blog / deploy (push) Successful in 55s
2026-08-11 22:07:01 +02:00
d3fd2ec8a7 Update .forgejo/workflows/deploy.yaml
All checks were successful
deploy blog / deploy (push) Successful in 51s
2026-08-11 22:05:43 +02:00
c18cce53f8 Update .forgejo/workflows/deploy.yaml
Some checks failed
deploy blog / deploy (push) Has been cancelled
2026-08-11 22:05:13 +02:00
9cca35810f Merge pull request 'staging' (#50) from staging into production
All checks were successful
deploy blog / deploy (push) Successful in 55s
Reviewed-on: #50
2026-08-11 21:42:16 +02:00
5f603574e3 :nerd: minor spelling mistake ❇️
All checks were successful
deploy blog / deploy (push) Successful in 53s
Signed-off-by: aprl <aprl@noreply.git.berlin.ccc.de>
2026-08-11 21:41:01 +02:00
3afd801344 Merge pull request '2026_08_23_Aktionstag_gegen_Ueberwachung' (#49) from aprl/www:2026_08_23_Aktionstag_gegen_Ueberwachung into staging
All checks were successful
deploy blog / deploy (push) Successful in 1m14s
Reviewed-on: #49
2026-08-11 21:39:38 +02:00
April John
9b8fbc0106 Text natürlicher formuliert, Gedankenstriche entfernt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 21:34:13 +02:00
April John
8b4f6d3c95 Aktionstag gegen Überwachung 2026-08-23: Veranstaltung + Ankündigungspost
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 21:16:19 +02:00
43ae6bc869 Update .forgejo/workflows/deploy.yaml
All checks were successful
deploy blog / deploy (push) Successful in 1m16s
2026-07-22 22:11:21 +02:00
c67560bd94 Merge pull request 'staging' (#48) from staging into production
All checks were successful
deploy blog / deploy (push) Successful in 59s
Reviewed-on: #48
2026-07-05 00:38:00 +02:00
42f999443f Merge pull request 'diday-end-recurrence' (#47) from hauke/www:diday-end-recurrence into staging
All checks were successful
deploy blog / deploy (push) Successful in 1m0s
Reviewed-on: #47
2026-07-05 00:18:48 +02:00
d8773f4f6f Weise auf Ausfall des DI.Day am 5. Juli im CCCB hin
Der DI.Day findet am 5. Juli 2026 nicht im Chaos Computer Club Berlin
statt. Der neue Blog-Eintrag informiert darüber und verweist auf die
alternativen Berliner DI.Day-Veranstaltungen (c-base, Stadtschloss
Moabit).

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-07-04 23:41:25 +02:00
ba76280a3f Merge pull request 'staging' (#46) from staging into production
All checks were successful
deploy blog / deploy (push) Successful in 55s
Reviewed-on: #46
2026-07-04 23:40:44 +02:00
e2a5fce03b Merge pull request 'fix typo' (#45) from 2026_07_05_DiDay_im_Club into staging
All checks were successful
deploy blog / deploy (push) Successful in 58s
Reviewed-on: #45
2026-07-04 23:37:47 +02:00
Bruno Ranieri
0318a0496a fix typo 2026-07-04 23:37:19 +02:00
1130d7eef0 Beende die Di.Day-Reihe nach dem 3. Mai 2026
Der Digital Independence Day findet ab Juli 2026 nicht mehr im
zweimonatlichen Turnus statt. Mit UNTIL in der RRULE endet die
Wiederholung nach dem letzten Termin am 3. Mai 2026, sodass keine
künftigen Instanzen mehr in den Kalendern (all.ics,
veranstaltungen/index.ics) und in der Terminübersicht erscheinen.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2026-07-04 23:35:50 +02:00