WebCalendar

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

License

WebCalendar is available under the GNU General Public License, version 2.

For more information on this license:

Documentation

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
    • RFC 2445: Internet Calendaring and Scheduling Core Object Specification (iCalendar)
    • CalDAV: Calendaring and Scheduling Extensions to WebDAV (DRAFT) 
      [Note: WebCalendar does not yet support CalDAV.)
  • 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.