Table of Contents
About WebCalendar
WebCalendar is a PHP-based calendar application that can be configured as a single-user calendar, a multi-user calendar for groups of users, or as an event calendar viewable by visitors. MySQL/MariaDB, SQLite3, PostgreSQL, Oracle, DB2, Interbase, MS SQL Server, or ODBC is required. The version 1.9.X releases are still a little rough around the edges since these include an overhaul of the UI to use Bootstrap and jQuery and a complete rewrite of the web-based installer.
WebCalendar can be setup in a variety of ways, such as…
- A schedule management system for a single person
- A schedule management system for a group of people, allowing one or more assistants to manage the calendar of another user
- An events schedule that anyone can view, allowing visitors to submit new events
- A calendar server that can be viewed with iCalendar-compliant calendar applications like Mozilla Sunbird, Apple iCal or GNOME Evolution or RSS-enabled applications like Firefox, Thunderbird, RSSOwl, FeedDemon, or BlogExpress.
Overview of Features
- Multi-user support
- 30 supported languages: Basque, Bulgarian, Chinese-Big5, Chinese-GB2312, Czech, Danish, Dutch, English-US, Estonian, Finnish, French, Galician, German, Greek, Holo-Big5, Hungarian, Icelandic, Italian, Japanese, Korean, Norwegian, Polish, Portuguese_BR, Portuguese, Romanian, Russian, Spanish, Swedish, Turkish, Welsh (see current list of translations here)
- Web-based installer
- Auto-detect user’s language preference from browser settings
- View calendars by day, week, month or year
- View another user’s calendar
- View one or more users’ calendar via layers on top of your own calendar
- Add/Edit/Delete users
- Add/Edit/Delete events
- Repeating events including support for overriding or deleting (exceptions)
- Configurable custom event fields
- User-configurable preferences for colors, 12/24 time format, Sun/Mon week start
- Checks for scheduling conflicts
- Email reminders for upcoming events
- Email notifications for new/updated/deleted events
- Export events to iCalendar
- Import from iCalendar/ics format
- Optional general access (no login required) to allow calendar to be viewed by people without a login (useful for event calendars)
- Users can make their calendar available publicly to anyone with an iCalendar-compliant calendar program (such as Apple’s iCal, Mozilla Calendar or Sunbird)
- Publishing of free/busy schedules (part of the iCalendar standard)
- RSS support that puts a user’s calendar into RSS
- Subscribe to “remote” calendars (hosted elsewhere on the net) in either iCalendar or hCalendar formats (WebCalendar 1.1+)
- User authentication: Web-based, HTTP, LDAP or NIS
System Requirements
- PHP 8 or later
- PHP support and access to one of the following databases:
- SQLite
- MySQL/MariaDB
- Oracle
- Postgres
- IBM DB2
- Access to cron for Linux/Unix systems (to send out reminders)
Development Cost
The following metrics from Ohloh show how much it would have cost to commercially develop WebCalendar.
- Codebase Size: 138,588 lines
- Estimated Effort: 34 person-years
- Estimated Cost: $1,884,469
- (As of 11 August 2024)
Donations
If you’d like to help support the costs of developing, maintaining and supporting WebCalendar, please consider donating.
Developer Resources
- Github page for WebCalendar:
- Issues
- Pull requests
- Wiki
- Download the development code as a zip file
License
WebCalendar is available under the GNU General Public License, version 2.
For more information on this license:
Documentation
- System Administrator’s Guide
Introduction, installation instructions and FAQ - UPGRADING (WebCalendar 1.3.0)
Provides instruction on upgrading to version 1.3.7 from an older version - Database Design (WebCalendar 1.3.0)
Version 1.2.7 database schema
Most Recent Changes
Below are the most recent source code commits to github on the master branch.
- fix(tests): release-files CI failure + harden the consistency check (…by craigk5n on April 24, 2026 at 1:21 pm
fix(tests): release-files CI failure + harden the consistency check (#233) The first master CI run after the signed-manifest feature landed (24891039995) failed on all three PHP versions with: 3 stale entry(ies) in release-files: line 185: pub/bootstrap.bundle.min.js.sha line 187: pub/bootstrap.min.css.sha line 189: pub/jquery.min.js.sha Root cause: these are SRI integrity sidecar files that `make` generates into pub/ from vendor/. They’re .gitignored (*.sha) so they DON’T exist in a fresh CI checkout. Story 2.5’s cleanup kept them in release-files because they existed on my dev disk — my local test passed (is_file returns true) but CI fails. They’ve never actually shipped in any release either — the old cp loop silently skipped them (no set -e before Story 2.5). Fixes: 1. Remove the 3 .sha lines from release-files. They aren’t generated in the release workflow (no `make` step), so omitting them matches what’s actually been shipping. If they’re ever needed in releases, the workflow would have to run `make` first AND they’d need to be re-added here. 2. Harden ReleaseFilesConsistencyTest to also check git-tracked-ness, not just is_file(). Shells out to `git -C <root> ls-files` once per test run and errors on files that exist-locally-but-aren’t-tracked. Drift of this exact shape now fails locally, not just on CI. Falls back gracefully to existence-only if .git isn’t present (e.g. tarball install of the tests). 3. Add docs/release-signing.md to the mkdocs nav so the Sigstore runbook is discoverable from the rendered documentation site. Note: The Deploy Documentation workflow was ALSO failing on the same push, but that failure is pre-existing (3 strict-mode link warnings in unrelated .md files from commit #634, 9 days old) — not caused by this feature. Not touched here. Full signed-manifest suite: 200 tests / 522 assertions green. Full CI suite: 388 tests / 1313 assertions, 6 skipped, 0 failures.
- feat: add tools/sign-manifest.php Ed25519 manifest signer (#233)by craigk5n on April 24, 2026 at 1:05 pm
feat: add tools/sign-manifest.php Ed25519 manifest signer (#233) Story 2.2 of the signed-manifest feature. Signs MANIFEST.sha256 with the Ed25519 secret key in env var RELEASE_SIGNING_KEY and writes a base64 detached signature to MANIFEST.sha256.sig alongside. – includes/classes/Security/ManifestSigner.php — pure logic. Envelope return (not throw) so the workflow prints clean operator messages and reason strings stay in our control. #[\SensitiveParameter] on the secret arg; sodium_memzero() scrubs the decoded key after use. – tools/sign-manifest.php — CLI wrapper, single positional arg for manifest path, <path>.sig is the implied output. – tests/ManifestSignerTest.php — 12 tests covering verifiable round-trip, Ed25519 determinism (RFC 8032), tamper detection (full-message and single-bit flip), empty/null/invalid-base64/ wrong-length secret rejection, and two log-safety tests that assert the secret never appears in any reason string. End-to-end smoke tested: build-manifest -> sign-manifest -> external libsodium verify = true; one-byte flip in the manifest -> verify = false. Full signed-manifest suite now: 57 tests / 116 assertions green. Epic 2 crypto pipeline is complete; Story 2.3 (release.yml wiring) is unblocked.
- feat(ci): wire signed-manifest build+sign into release.yml (#233)by craigk5n on April 24, 2026 at 1:05 pm
feat(ci): wire signed-manifest build+sign into release.yml (#233) Story 2.3 of the signed-manifest feature. Retrofits .github/workflows/release.yml with the four steps that turn every tagged release into a signed bundle: 1. Set up PHP 8.4 with ext-sodium (was implicit before; pin it). 2. Pin SOURCE_DATE_EPOCH to the HEAD commit’s committer timestamp so MANIFEST.sha256 is byte-identical across re-runs. 3. Build MANIFEST.sha256 against the staged WebCalendar-${VERSION}/ tree via tools/build-manifest.php (strict: fails on any missing release-files entry). 4. Sign MANIFEST.sha256 via tools/sign-manifest.php with RELEASE_SIGNING_KEY injected from secrets. Scopes the build job to `environment: release` so the secret is not exposed to forked-PR workflows. Adds two `actions/upload-release-asset` steps so MANIFEST.sha256 and MANIFEST.sha256.sig are published as top-level release assets in addition to being inside the zip. Each bash block runs under `set -euo pipefail`, and GitHub Actions fails the job on any non-zero exit — no partial releases. Locally simulated the full pipeline against the 266-entry release-files list: 269-line manifest generated, 89-byte detached signature produced, external libsodium verify returned true, zip contained MANIFEST.sha256, MANIFEST.sha256.sig, and release-signing-pubkey.pem at the expected root locations. Story 2.4 was satisfied as a side-effect (pubkey already listed in release-files, MANIFEST files intentionally not listed) — marked complete with no code changes needed. Epic 2 is now code-complete. Only remaining Epic 2 item is Story 2.5 AC2 (which new docs/*.md to ship), a maintainer decision.
- feat: add ManifestVerifier + VerifyResult classes (#233)by craigk5n on April 24, 2026 at 1:05 pm
feat: add ManifestVerifier + VerifyResult classes (#233) Story 3.1 of the signed-manifest feature. The app-side crypto entry point: given the paths to MANIFEST.sha256, MANIFEST.sha256.sig, and release-signing-pubkey.pem, return an immutable VerifyResult{valid, reason}. Uses sodium_crypto_sign_verify_detached() exclusively (no OpenSSL). Reuses ReleaseKeyGenerator::parsePublicKeyPem for the 32-byte pubkey invariant — no duplicated PEM logic. VerifyResult is final + readonly-per-property (PHP 8.1 parse-compatible equivalent of the spec’d final-readonly-class). Mutation raises fatal Error, locked in by testVerifyResultIsImmutable. Every failure mode returns a clean human-readable reason suitable for the admin UI display: – missing manifest / sig / pubkey file – empty / invalid-base64 / wrong-length signature – malformed or truncated PEM – tampered manifest (full-message or one-bit-flip) – swapped pubkey (different keypair) Signature-file whitespace is tolerated via trim() — base64 has no whitespace, so CRLF / extra LFs from pipe-chained tools are safe to strip. 15 new unit tests / 29 assertions. Full signed-manifest suite now 72 tests / 145 assertions green. PHPStan level-0 clean on new files.
- feat: add ManifestParser + ManifestData classes (#233)by craigk5n on April 24, 2026 at 1:05 pm
feat: add ManifestParser + ManifestData classes (#233) Story 3.2 of the signed-manifest feature. Turns signature-verified MANIFEST.sha256 bytes into an immutable ManifestData value object with version, build timestamp, git SHA, and a relpath -> sha256hex map ready for the scanner (Story 3.3) to walk. Strict-format parser: – HASH_LINE_REGEX `/^([0-9a-f]{64}) (\S.*)$/` rejects anything that isn’t lowercase-hex with exactly two spaces and a non-empty path. – HEADER_LINE_REGEX captures `# key: value` into a map; unknown keys are tolerated (forward-compatibility). – Blank lines, tabs, wrong hash length, uppercase hex, duplicate paths, malformed timestamps, missing required headers, and empty bodies all throw RuntimeException with `line N:` context where applicable. Round-trip contract locked in by testRoundTripsWithManifestBuilder: a manifest produced by ManifestBuilder parses cleanly back into ManifestData. Future format changes on either side now break a test rather than drifting silently. 22 new unit tests / 41 assertions. Full signed-manifest suite: 94 tests / 186 assertions green. PHPStan level-0 clean.
Download Metrics
- Downloads via Github: 20145
- Downloads via SourceForge:
Related Links
- Standards
- Calendar client applications – You can use the applications to view events stored in WebCalendar if you enable its publishing settings.
- iCalendar/ics download sites – These sites contain calendars for holidays, sports teams schedules, music converts, etc. You can import these files into WebCalendar.
- iCalShare
- Apple iCal Library
- DateDex
- Project24: holiday and weather calendars