| Code quality | Translation status | OpenAPI validation | |
|---|---|---|---|
| stable branch |
|
|
|
| dev branch |
|
|
|
An API (PSR‑7/15/17 compliant) written in PHP that will generate the liturgical calendar for any given year, based on the General Roman Calendar, calculating the mobile festivities and the precedence of solemnities, feasts, memorials... Can also produce calendar data for nations, dioceses, or groups of dioceses. This calendar data can be served in various formats such as JSON, YAML, XML, or ICS. More information on the website.
OpenAPI documentation can be found here (kudos to @MichaelRShelton for generating the docs from the Swagger docker image).
The API is packaged as a composer library: run composer install to setup the autoload functionality.
Some characteristics of this API:
- The data is based on official sources, not copied from random internet sources.
Sources used are the various editions of the Roman Missal in Latin, English, and Italian, Magisterial documents,
and the Decrees of the Dicastery for Divine Worship and the Discipline of the Sacraments
- Missale Romanum, Editio typica, 1970
- Missale Romanum, Reimpressio emendata, 1971
- Missale Romanum, Editio typica secunda, 1975
- Missale Romanum, Editio typica tertia, 2002
- Missale Romanum, Editio typica tertia emendata, 2008
- Mysterii Paschalis, PAULUS PP. VI, 1969
- Decrees of the Dicastery for Divine Worship
- The data is historically accurate, i.e. the liturgical calendar produced for the year 1979 will reflect the calendar as it was in that year, and not as it would be today (obviously future years will reflect the calendar as it is generated in the current year; as new decrees are issued by the Dicastery for Divine Worship and the Discipline of the Sacraments or new editions of the Roman Missal are published, the script will need to be updated to account for any new criteria)
There are a few proof of concept example applications for usage of the API at LitCal Usage, which demonstrate generating an HTML representation of the Liturgical Calendar.
- The first example uses cURL in PHP to make a request to the endpoint and handle the results.
- The second example uses
fetchin Javascript to make the request to the endpoint and handle the results. - The third example makes use of the FullCalendar javascript framework
to display the results from the
fetchrequest in a nicely formatted calendar view. - The fourth example is the same as the third except that it outputs the Messages first and the FullCalendar calendar view after.
All of these examples request JSON as the data exchange format generated by the endpoint.
Any application could use the endpoint in a similar manner: an Android App, a plugin for a Desktop Publishing App...
(See usage.php#calSubscription.)
- GOOGLE CALENDAR ON A DESKTOP COMPUTER: you can only add a calendar by URL using Google Calendar on a computer, I don't believe it is possible from smartphone / Android devices.
At the bottom left corner of the screen, next to
Other calendars, click on the+to add a new calendar and chooseFrom URL. Paste in the URL of the endpoint with the desired parameters, (make sure you useICSas value of thereturn_typeparameter). And remember, if you omit theyearparameter, it will use the current year. This should mean that as Google Calendar continues to poll the calendar URL (supposedly every 8 hours), on the turn of a new year new events should be created automatically for the new year. Once the calendar has been added from a computer, it should become available for the same gmail account on the Google Calendar app on a smartphone. - CALENDAR APPS ON AN ANDROID DEVICE: after you have added a calendar by URL in your Google Calendar on a Desktop Computer, you should then find that calendar synchronized with your Google account, so the calendar should become available to any Android Calendar apps that have access to your Google account to synchronize calendars.
- IPHONE: go to
Phone Settings->Accounts->Add account->Other->Add Calendar, and paste in the endpoint URL with the desired parameters, (make sure you useICSas value of thereturn_typeparameter). And remember, if you omit theyearparameter, it will use the current year. This should mean that as the iPhone Calendar continues to poll the calendar URL, on the turn of a new year new events should be created automatically for the new year. - MICROSOFT OUTLOOK (tested with Outlook 2013): at the bottom of the screen, switch from
Emailview toCalendarview. On the ribbon of theHomemenu item, click onOpen calendar->From the internet. Paste the endpoint URL with the desired parameters, (make sure you useICSas value of thereturn_typeparameter). And remember, if you omit theyearparameter, it will use the current year. On the following screen, check the checkbox along the lines of "Poll this calendar in the interval suggested by the creator", which would mean that Outlook Calendar should poll the calendar URL once a day. This means that without theyearparameter, on the turn of a new year new events should be created automatically for the new year. Make sure the Calendar is created in theOther calendarsfolder; if you find it under thePersonal calendarsfolder, drag it and drop it onto theOther calendarsfolder, this should ensure that it is treated as a subscription internet calendar. You can manually trigger an update against the calendar URL by clicking onSend/receive all(from theSEND/RECEIVEmenu item). One highlight of the calendar in Outlook is that it supports a minimal amount of HTML in the event description, so the event descriptions in the Liturgical Calendar are a little bit more "beautified" for Outlook.
System requirements:
- PHP >= 8.4 (we make use of more modern PHP functions such as
array_find) - PHP modules installed and enabled:
intl•zip•calendar•yaml•gettext - System package
gettextand language packs for all the supported languages - PHP module
apcuis optional and currently under testing. If enabled, it is also possible to test usage for the WebSocket server by settingapc.enable_cli=1inphp.ini.
To test the API locally, you can use PHP's built-in server.
However, you will need to spawn at least a couple of workers, since some routes will make a request internally to another route.
For example, a request to the /calendar route will make a request internally to the /calendars route.
To be on the safe side, you should spawn up to 6 workers.
PHP_CLI_SERVER_WORKERS=6 php -S localhost:8000 -t publicFor convenience when using VSCode, a tasks.json has been defined so that you can simply type CTRL+SHIFT+B
(CMD+SHIFT+B on macOS) to start the PHP built-in server and open the browser
(litcal-api-with-browser, or api-server-no-browser to just start the server without opening the browser).
The composer.json file also defines a couple scripts to simplify this process:
composer start: spawns six workers viastart-server.shcomposer stop: stops the server viastop-server.sh
You can also use the start-server.sh and stop-server.sh scripts directly to spawn and stop the server. Please ensure that both scripts are executable (chmod +x).
The start script writes the server PID to server.pid in the current directory,
and the stop script terminates the process by its PID and removes server.pid.
The following environment variables can be set to configure the API:
API_PROTOCOL: The protocol to use for the API (default ishttp). Example:API_PROTOCOL=httpsto use thehttpsprotocol.API_HOST: The hostname or IP address to use for the API (default islocalhost). Example:API_HOST=mydomain.comto use themydomain.comhost.API_PORT: The port to use for the API (default is8000). Example:API_PORT=8080to use port8080.API_BASE_PATH: The base path to use for the API (default is/). Example:API_BASE_PATH=/api/v1/to use the/api/v1/base path.
These environment variables should be set in a .env or .env.local file (the same files used by the PHP application to load environment variables).
You can copy the .env.example file to .env or .env.local (or .env.development or .env.production) and edit it as needed.
These environment variables are used when running the API in CLI mode, such as when using the start-server.sh script.
The defaults are suitable for development and testing, but may need to be overridden for staging or production environments.
The API now supports JWT authentication for protected write operations. To enable authentication, configure the following environment variables:
JWT_SECRET: Secret key for signing tokens (minimum 32 characters). Generate a secure 64-character hex string with:php -r "echo bin2hex(random_bytes(32));"JWT_ALGORITHM: Algorithm for signing tokens (default:HS256)JWT_EXPIRY: Access token expiry in seconds (default:3600= 1 hour)JWT_REFRESH_EXPIRY: Refresh token expiry in seconds (default:604800= 7 days)ADMIN_USERNAME: Admin username for authentication (default:admin)ADMIN_PASSWORD_HASH: Argon2id password hash. Generate with:php -r "echo password_hash('yourpassword', PASSWORD_ARGON2ID);"APP_ENV: Application environment (required). Must be one of:development,test,staging,production
Environment-Specific Security Behavior:
The API implements fail-closed authentication that requires APP_ENV to be explicitly set to a known value:
developmentandtest: Allow default password (password) ifADMIN_PASSWORD_HASHis not set or is not a valid hash format (for convenience in testing)stagingandproduction: RequireADMIN_PASSWORD_HASHto be a valid password hash (throwsRuntimeExceptionif missing or invalid)- Invalid or unset
APP_ENV: ThrowsRuntimeExceptionand denies authentication
This ensures that production environments cannot accidentally use weak default credentials.
Protected Routes (require JWT authentication via HttpOnly cookie or Authorization: Bearer <token> header):
PUT /data/{category}/{calendar}- Create calendar dataPATCH /data/{category}/{calendar}- Update calendar dataDELETE /data/{category}/{calendar}- Delete calendar data
Authentication Endpoints:
POST /auth/login- Authenticate with username/password, returns access and refresh tokens (also sets HttpOnly cookies)POST /auth/refresh- Refresh access token using refresh token (from cookie or request body)POST /auth/logout- Logout and clear HttpOnly cookies (stateless; clients should also discard any stored tokens)GET /auth/me- Get current authenticated user info (requires valid access token)
Cookie-Based Authentication Details:
- Token precedence: HttpOnly cookies are checked first; the
Authorizationheader is used only as a fallback when no cookie is present - Cookie handling: Browsers automatically send cookies with same-site requests when
credentials: 'include'is set in fetch options. For cross-origin requests, the server must also return appropriate CORS headers (Access-Control-Allow-Credentials: true) - Cookie attributes:
- Access token:
SameSite=Lax,HttpOnly,Secure(HTTPS only), path/ - Refresh token:
SameSite=Strict,HttpOnly,Secure(HTTPS only), path/auth
- Access token:
- CSRF protection: The
SameSiteattribute provides baseline CSRF protection by restricting when cookies are sent cross-site.Laxallows same-site requests and top-level cross-site navigations;Strict(used for refresh tokens) only allows same-site requests. For enhanced security in cross-origin scenarios, consider implementing additional CSRF tokens
For detailed implementation information, see docs/enhancements/AUTHENTICATION_ROADMAP.md.
For example, to run the API in production with a custom domain and HTTPS, you would set the following environment variables:
API_PROTOCOL=https
API_HOST=mydomain.com
API_PORT=443
API_BASE_PATH=/api/v1/
JWT_ALGORITHM=HS256
JWT_EXPIRY=3600
JWT_REFRESH_EXPIRY=604800
JWT_SECRET=change-this-to-a-secure-random-string-in-production-minimum-32-chars
ADMIN_PASSWORD_HASH=CHANGE_ME_GENERATE_WITH_password_hash
ADMIN_USERNAME=admin
APP_ENV=productionTo further simplify your setup, without having to worry about getting all the system requirements in place, you can also launch the API in a docker container using the repo Dockerfile:
# If you haven't cloned the repo locally, you can build directly from the remote repo (replace `{branch}` with the branch or tag from which you want to build):
DOCKER_BUILDKIT=1 docker build -t liturgy-api:{branch} https://github.com/Liturgical-Calendar/LiturgicalCalendarAPI.git#{branch}
# If instead you have cloned the repo locally, you can build from the local repo (replace `{branch}` with the branch or tag that you have checked out locally):
DOCKER_BUILDKIT=1 docker build -t liturgy-api:{branch} .
docker run -p 8000:8000 -d liturgy-api:{branch}This typically results in a Docker image of ~1.1 GB (subject to change). Unfortunately this cannot be reduced by means of an alpine image,
if we want to install system locales in order for gettext to work properly with all supported languages.
To test the API locally, first install all package dependencies with composer install.
To run static analysis tests, run composer analyse. You can even run this within VSCode's terminal,
and have clickable links to the interested lines in the source code, if you create a phpstan.neon file in the root directory
alongside the phpstan.neon.dist file. For VSCode running under WSL, phpstan.neon should look like this:
includes:
- phpstan.neon.dist
parameters:
editorUrl: 'vscode://vscode-remote/wsl+Ubuntu-24.04/%%file%%:%%line%%'Replace Ubuntu-24.04 with the name of your WSL distribution.
For other code editors, see the PHPStan documentation here.
There is a web interface that allows to run a number of integrity checks on the data output by the various routes. This interface has its own repository Liturgical-Calendar/UnitTestInterface.
You should clone this repository, and run composer install within the cloned repository folder.
This web interface communicates with a Web Socket backend included in the API repository.
In order to launch the WebSocket server, you can use CTRL+SHIFT+B (litcal-tests-websockets) from VSCode,
in the Liturgical Calendar API repository.
Then launch the web interface with CTRL+SHIFT+B (litcal-tests-webui) from VSCode,
in the UnitTestInterface repository.
To have all of the launch tasks available without having to open separate instances of VSCode,
it can be convenient to create a LiturgicalCalendar.code-workspace file outside of either repository folder,
and add both repository folders to it. For example:
{
"folders": [
{
"name": "LiturgicalCalendarAPI",
"path": "LiturgicalCalendarAPI"
},
{
"name": "LiturgicalCalendarFrontend",
"path": "LiturgicalCalendarFrontend"
},
{
"name": "UnitTestInterface",
"path": "UnitTestInterface"
}
]
}This will include the API repository, the frontend website repository, and the test interface repository all in the same workspace.
If you run code LiturgicalCalendar.code-workspace from the command line in WSL, you will open the whole workspace in VSCode,
with all of the folders for each repository and all of the launch tasks available in a single VSCode instance.
A few Unit Tests are available for testing the various API routes and their available operations and parameters.
To run unit tests, run composer test.
See CHANGELOG.md.