WebCalendar v1.9.24.
This release adds a command line interface, raises the minimum PHP version
to 8.2, and repairs a number of features that did not work — including
Purge Events, which did not work at all.
Breaking changes
PHP 8.2 is now the minimum. PHP 8.1 reached end of life on
31 December 2025 and is no longer supported. The wizard reports anything
below 8.2 as an error. The repository had previously stated four different
floors across composer.json, README.md, CONTRIBUTING.md and
docs/installation.md; they now agree, and a test keeps them that way.
Triggering reminders by URL now returns 403. Fetching
tools/send_reminders.php over HTTP without a token is refused — every
script under tools/ is now CLI-only, because PHP strips #! lines only
under the CLI SAPI and served them as page content otherwise.
docs/admin-guide.md used to offer the URL as an alternative for hosts
without a PHP CLI binary. Affected sites should move to a cron entry running
the PHP CLI binary (cPanel, Plesk and DirectAdmin all provide one), or
generate a token and add it to the URL.
A fatal database error now exits 1 instead of 0. dbi_fatal_error()
ended in a bare exit, which is status 0, so every fatal database error
told its caller it had succeeded — including tools/send_reminders.php
reporting success to cron when it had never connected. Failures that were
silent will now be reported, which is the point, but a monitor that has been
green through a real fault will start alerting.
Upgrading
Run the installation wizard as usual; see the
Upgrade Guide.
This release applies one schema change: recurring events created through the
MCP server were stored with cal_type = 'E' rather than 'M', because
add_recurring_event omitted the column and inherited the schema default.
Nothing displayed wrongly — the view and export queries accept both — so the
upgrade corrects the stored value rather than repairing a visible fault. It
is scoped by the join to webcal_entry_repeats, so a non-recurring event is
never touched, and re-running it is a no-op.
php bin/webcal.php db check reports whether an upgrade is pending without
applying anything: exit 0 up to date, 1 pending, 2 could not be determined.
New: the command line
bin/webcal.php gathers thirteen commands, documented in
docs/cli.md.
The ones worth knowing about:
user reset-password --login=admin— the answer to a locked-out
administrator. Passwords are bcrypt, so no hand-writtenUPDATErecovers
the account. The password is never taken as an argument, wherepsand
shell history would see it.diagnose— an environment report to attach to a bug report. No
passwords, tokens, host names or email addresses appear in it.db checkanddb dump— is an upgrade pending, and a backup of the
webcal_*tables only, with credentials taken from the existing
configuration rather than the argument list.export/import— the same code paths as the Export and Import pages.config list/get/set—webcal_config, for when a setting is
itself what stops you reaching Admin > Settings.email test --to=ADDRESS— sends one message and reports the mail
server's own refusal, which is what tells you which end is wrong.user list,reminders send,remotes refresh.
Repaired
Purge Events did not work at all. Its Delete button carried no value
attribute, so no browser submitted it. Behind that: it read
end_year/end_month/end_day while the form renders a single date field,
ignored "Purge deleted only" when All users was selected, errored when that
option was combined with a named user, reported row counts from a query
joining webcal_entry_user with no user filter, and labelled a completed
purge [Preview].
An export could be silently filtered to public events by an unrelated
request parameter. export_get_event_entry() tested
if ( ! empty ( $type ) && $type = 'publish' ) — an assignment, not a
comparison — so the restriction applied to any non-empty $type and
overwrote the caller's value on the way through. approve_entry.php takes
$type from the request and attaches an ICS to the approval mail, so
approving an event with any type parameter quietly cut that attachment
down to public events.
The Export page's "Include deleted entries" checkbox now does something.
The login page is centered rather than double-padded, and its labels are
associated with the inputs they name.
One unreachable remote calendar no longer stops the rest from
refreshing.
A test script no longer destroys a live installation.
tests/run-{mysql,postgresql,sqlite}-install-tests.sh drive the install
wizard, so they delete includes/settings.php to install from nothing. They
now refuse when that file is present unless
WEBCAL_TEST_ALLOW_DESTRUCTIVE=1 is set.
Documentation
docs/cli.md is new. docs/troubleshooting.md no longer answers "cannot
log in" with an SQL statement that leaves an unsalted MD5 hash in the table.
MCP_WRITE_ACCESS — which decides whether an assistant can change a
calendar, and is off by default — is now in the configuration reference and
the security guide, not only the MCP page. The MCP page documents all nine
tools rather than four. A test now locks the version claims and the tool
list to the code, after the README badge was found advertising a version
seven releases old.
Also
The test suite grew from a set of behaviour tests into a set of structural
guards, and then those guards were themselves tested: each was given the
defect it exists to catch, sixty-eight mutations across twenty guards. Seven
were missed, every one for the same reason — the assertion matched text
rather than the mechanism, frequently the guard's own explanatory comment.
All seven repaired, and the source-reading they all needed now lives in one
tested helper instead of fifteen private copies.
Continuous integration for a pull request roughly halved, from about seven
minutes to under four, mostly by installing dependencies from dist archives
instead of cloning them.
Verifying this release
The ZIP ships a signed manifest. MANIFEST.sha256 lists a SHA-256 for every
one of the 510 files; MANIFEST.sha256.sig is an Ed25519 signature over it,
verifiable with the release-signing-pubkey.pem included in the release.
WebCalendar-1.9.24.zip.sig and .zip.pem are an independent cosign keyless
signature of the archive, verifiable through Sigstore with no
WebCalendar-maintained key.
Both were checked before these notes were published: the manifest signature
verifies, and every file hash in the extracted tree matches it.
Full instructions, including the pure-PHP verifier and the cosign verify-blob command, are in
docs/release-signing.md.
Docker
craigk5n/webcalendar:1.9.24, 1.9.24-php8-apache, latest-php8-apache and
latest, all multi-arch (amd64 and arm64).
The full list, with the reasoning behind each change, is in
CHANGELOG.md.