Showing preview only (798K chars total). Download the full file or copy to clipboard to get everything.
Repository: tchapi/davis
Branch: main
Commit: 82d577822192
Files: 174
Total size: 746.3 KB
Directory structure:
gitextract_80g_njsc/
├── .dockerignore
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ └── feature_request.yml
│ └── workflows/
│ ├── ci.yml
│ └── main.yml
├── .gitignore
├── .hadolint.yaml
├── .php-cs-fixer.php
├── LICENSE
├── README.md
├── bin/
│ ├── console
│ └── phpunit
├── composer.json
├── config/
│ ├── bundles.php
│ ├── packages/
│ │ ├── cache.yaml
│ │ ├── dev/
│ │ │ ├── debug.yaml
│ │ │ ├── monolog.yaml
│ │ │ └── web_profiler.yaml
│ │ ├── doctrine.yaml
│ │ ├── doctrine_migrations.yaml
│ │ ├── framework.yaml
│ │ ├── mailer.yaml
│ │ ├── prod/
│ │ │ ├── deprecations.yaml
│ │ │ ├── doctrine.yaml
│ │ │ ├── monolog.yaml
│ │ │ └── routing.yaml
│ │ ├── routing.yaml
│ │ ├── security.yaml
│ │ ├── test/
│ │ │ ├── framework.yaml
│ │ │ ├── monolog.yaml
│ │ │ ├── twig.yaml
│ │ │ ├── validator.yaml
│ │ │ └── web_profiler.yaml
│ │ ├── translation.yaml
│ │ ├── twig.yaml
│ │ └── validator.yaml
│ ├── reference.php
│ ├── routes/
│ │ ├── attributes.yaml
│ │ └── dev/
│ │ ├── framework.yaml
│ │ └── web_profiler.yaml
│ ├── routes.yaml
│ └── services.yaml
├── docker/
│ ├── Dockerfile
│ ├── Dockerfile-standalone
│ ├── configurations/
│ │ ├── Caddyfile
│ │ ├── nginx.conf
│ │ ├── opcache.ini
│ │ └── supervisord.conf
│ ├── docker-compose-postgresql.yml
│ ├── docker-compose-sqlite.yml
│ ├── docker-compose-standalone.yml
│ └── docker-compose.yml
├── docs/
│ └── api/
│ ├── README.md
│ └── v1/
│ ├── calendars/
│ │ ├── all.md
│ │ ├── create.md
│ │ ├── delete.md
│ │ ├── details.md
│ │ ├── edit.md
│ │ ├── share_add.md
│ │ ├── share_remove.md
│ │ └── shares.md
│ ├── health.md
│ └── users/
│ ├── all.md
│ └── details.md
├── migrations/
│ ├── Version20191030113307.php
│ ├── Version20191113170650.php
│ ├── Version20191125093508.php
│ ├── Version20191202091507.php
│ ├── Version20191203111729.php
│ ├── Version20210928132307.php
│ ├── Version20221106220411.php
│ ├── Version20221106220412.php
│ ├── Version20221211154443.php
│ ├── Version20230209142217.php
│ ├── Version20231001214111.php
│ ├── Version20231001214112.php
│ ├── Version20231001214113.php
│ ├── Version20231229203515.php
│ ├── Version20250409193948.php
│ ├── Version20250421163214.php
│ └── Version20260131161930.php
├── phpunit.xml.dist
├── public/
│ ├── .htaccess
│ ├── css/
│ │ └── style.css
│ ├── index.php
│ ├── js/
│ │ ├── app.js
│ │ └── color.mode.toggler.js
│ ├── robots.txt
│ └── site.webmanifest
├── src/
│ ├── Command/
│ │ ├── ApiGenerateCommand.php
│ │ └── SyncBirthdayCalendars.php
│ ├── Constants.php
│ ├── Controller/
│ │ ├── Admin/
│ │ │ ├── AddressBookController.php
│ │ │ ├── CalendarController.php
│ │ │ ├── DashboardController.php
│ │ │ └── UserController.php
│ │ ├── Api/
│ │ │ └── ApiController.php
│ │ ├── DAVController.php
│ │ └── SecurityController.php
│ ├── DataFixtures/
│ │ └── AppFixtures.php
│ ├── Entity/
│ │ ├── AddressBook.php
│ │ ├── AddressBookChange.php
│ │ ├── Calendar.php
│ │ ├── CalendarChange.php
│ │ ├── CalendarInstance.php
│ │ ├── CalendarObject.php
│ │ ├── CalendarSubscription.php
│ │ ├── Card.php
│ │ ├── Lock.php
│ │ ├── Principal.php
│ │ ├── PropertyStorage.php
│ │ ├── SchedulingObject.php
│ │ └── User.php
│ ├── Form/
│ │ ├── AddressBookType.php
│ │ ├── CalendarInstanceType.php
│ │ └── UserType.php
│ ├── Kernel.php
│ ├── Logging/
│ │ └── Monolog/
│ │ └── PasswordFilterProcessor.php
│ ├── Plugins/
│ │ ├── BirthdayCalendarPlugin.php
│ │ ├── DavisIMipPlugin.php
│ │ └── PublicAwareDAVACLPlugin.php
│ ├── Repository/
│ │ ├── CalendarInstanceRepository.php
│ │ └── PrincipalRepository.php
│ ├── Security/
│ │ ├── AdminUser.php
│ │ ├── AdminUserProvider.php
│ │ ├── ApiKeyAuthenticator.php
│ │ └── LoginFormAuthenticator.php
│ ├── Services/
│ │ ├── BasicAuth.php
│ │ ├── BirthdayService.php
│ │ ├── IMAPAuth.php
│ │ ├── LDAPAuth.php
│ │ └── Utils.php
│ └── Version.php
├── templates/
│ ├── _partials/
│ │ ├── add_delegate_modal.html.twig
│ │ ├── back_button.html.twig
│ │ ├── delegate_row.html.twig
│ │ ├── delete_modal.html.twig
│ │ ├── flashes.html.twig
│ │ ├── navigation.html.twig
│ │ └── share_modal.html.twig
│ ├── addressbooks/
│ │ ├── edit.html.twig
│ │ └── index.html.twig
│ ├── base.html.twig
│ ├── calendars/
│ │ ├── edit.html.twig
│ │ └── index.html.twig
│ ├── dashboard.html.twig
│ ├── index.html.twig
│ ├── mails/
│ │ ├── scheduling.html.twig
│ │ └── scheduling.txt.twig
│ ├── security/
│ │ └── login.html.twig
│ └── users/
│ ├── delegates.html.twig
│ ├── edit.html.twig
│ └── index.html.twig
├── tests/
│ ├── .gitignore
│ ├── Functional/
│ │ ├── Commands/
│ │ │ └── SyncBirthdayCalendarTest.php
│ │ ├── Controllers/
│ │ │ ├── AddressBookControllerTest.php
│ │ │ ├── ApiControllerTest.php
│ │ │ ├── CalendarControllerTest.php
│ │ │ ├── DashboardTest.php
│ │ │ └── UserControllerTest.php
│ │ ├── DavTest.php
│ │ └── Service/
│ │ └── BirthdayServiceTest.php
│ └── bootstrap.php
└── translations/
├── .gitignore
├── messages+intl-icu.de.xlf
├── messages+intl-icu.en.xlf
├── messages+intl-icu.fr.xliff
├── security.de.xlf
├── security.en.xlf
├── security.fr.xlf
├── validators.de.xlf
├── validators.en.xlf
└── validators.fr.xlf
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
_screenshots
.DS_Store
README.md
LICENSE
.git
.gitignore
.github
.env.local
.env.test.local
phpunit.xml.dist
.php-cs*
.phpunit.*
.dockerignore
var/cache/*
var/log/*
================================================
FILE: .github/FUNDING.yml
================================================
custom: ['https://www.paypal.me/tchap']
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: Something is not working as expected
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Before opening an issue, please:
- Check `./var/log/prod.log` for errors (only written when errors occur, thanks to the `fingers_crossed` filter)
- Check your web server / reverse proxy logs
- Read the [Troubleshooting section of the README](https://github.com/tchapi/davis/blob/main/README.md#troubleshooting)
- type: input
id: davis_version
attributes:
label: Davis version
description: Tag or commit SHA. Found on the `/` status page or via `git describe --tags`.
placeholder: "e.g. v5.1.0"
validations:
required: true
- type: dropdown
id: install_method
attributes:
label: Installation method
options:
- Docker – standalone (with Caddy)
- Docker – barebone (no reverse proxy)
- Bare metal / manual
- NixOS module
- Other (describe below)
validations:
required: true
- type: input
id: php_version
attributes:
label: PHP version (bare metal only)
description: Output of `php --version`. Skip if using Docker.
placeholder: "e.g. 8.2.18"
- type: dropdown
id: database
attributes:
label: Database
options:
- MySQL
- MariaDB
- PostgreSQL
- SQLite
- Other
validations:
required: true
- type: dropdown
id: reverse_proxy
attributes:
label: Reverse proxy
options:
- None (standalone Docker with Caddy)
- Nginx
- Apache
- Caddy
- Traefik
- Other
validations:
required: true
- type: dropdown
id: auth_method
attributes:
label: Authentication method
options:
- Internal (ADMIN_LOGIN / ADMIN_PASSWORD)
- IMAP
- LDAP
validations:
required: true
- type: checkboxes
id: protocols
attributes:
label: Affected protocol(s)
options:
- label: CalDAV
- label: CardDAV
- label: WebDAV
- label: Admin dashboard
- label: API
- type: textarea
id: client
attributes:
label: Client(s) exhibiting the issue
description: Name, version, OS/platform. Add multiple if relevant.
placeholder: |
- DAVx⁵ 4.3.14 on Android 14
- Apple Calendar on macOS 14.4
- Thunderbird 115.10 on Ubuntu 22.04
validations:
required: true
- type: textarea
id: env_vars
attributes:
label: Relevant environment variables
description: |
Sanitize secrets (APP_SECRET, DATABASE_URL password, API_KEY, etc.).
Include at minimum: APP_ENV, CALDAV_ENABLED, CARDDAV_ENABLED, WEBDAV_ENABLED, AUTH_METHOD, and any env vars you think are relevant.
render: shell
placeholder: |
APP_ENV=prod
AUTH_METHOD=...
CALDAV_ENABLED=true
CARDDAV_ENABLED=true
WEBDAV_ENABLED=false
DATABASE_URL=mysql://user:***@host:3306/davis
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
placeholder: |
1. Configure client with URL https://dav.example.com/dav
2. ...
3. Observe error
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behaviour
validations:
required: true
- type: textarea
id: actual
attributes:
label: Actual behaviour
description: Include any error messages shown in the UI or client.
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs
description: |
Paste the relevant section of `./var/log/prod.log` (relative to your Davis installation root, **not** `/var/log`).
Also include web server / reverse proxy error logs if applicable.
Set `APP_ENV=dev` temporarily to get verbose output if `prod.log` is empty (you need to install dev dependencies with composer).
render: text
- type: textarea
id: additional
attributes:
label: Additional context
description: Anything else that might be relevant (docker-compose snippet, nginx config excerpt, network topology, etc.).
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: Feature Request
description: Suggest an improvement or new functionality
labels: ["feature request"]
body:
- type: markdown
attributes:
value: |
Please check [existing issues](https://github.com/tchapi/davis/issues) and the [Davis roadmap](https://github.com/users/tchapi/projects/1) first to avoid duplicates.
- type: textarea
id: problem
attributes:
label: Problem / motivation
description: What are you trying to do, and why is it currently not possible or inconvenient?
placeholder: "e.g. I cannot do X, which forces me to..."
validations:
required: true
- type: textarea
id: solution
attributes:
label: Proposed solution
description: Describe the feature you have in mind. Be as specific as possible.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Other approaches you have thought of or tried, and why they fall short.
- type: dropdown
id: area
attributes:
label: Area
options:
- CalDAV
- CardDAV
- WebDAV
- Admin dashboard
- API
- Authentication (IMAP / LDAP / internal)
- Docker / deployment
- Documentation
- Other
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional context
description: Links, screenshots, references to relevant RFCs or client behaviour, etc.
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches:
- main # Only run on pushes to main
pull_request:
# Run on all PRs (no path restrictions)
env:
COMPOSER_ALLOW_SUPERUSER: '1'
SYMFONY_DEPRECATIONS_HELPER: max[self]=0
ADMIN_LOGIN: admin
ADMIN_PASSWORD: test
jobs:
dockerfile-checks:
name: Dockerfile Checks
runs-on: ubuntu-latest
strategy:
matrix:
dockerfile:
- docker/Dockerfile
- docker/Dockerfile-standalone
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Lint with Hadolint
uses: hadolint/hadolint-action@v3.1.0
with:
dockerfile: ${{ matrix.dockerfile }}
failure-threshold: warning
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Validate Dockerfile syntax
run: |
docker buildx build \
--file ${{ matrix.dockerfile }} \
--platform linux/amd64 \
--check \
.
analyze:
name: Analyze
runs-on: ubuntu-latest
container:
image: php:8.4-alpine
options: >-
--tmpfs /tmp:exec
--tmpfs /var/tmp:exec
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install GD / ZIP PHP extension
run: |
apk add $PHPIZE_DEPS libpng-dev libzip-dev
docker-php-ext-configure gd
docker-php-ext-configure zip
docker-php-ext-install gd zip
- name: Install Composer
run: wget -qO - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet
- name: Validate Composer
run: composer validate
- name: Update to highest dependencies with Composer
run: composer install --no-interaction --no-progress --ansi
- name: Analyze
run: vendor/bin/php-cs-fixer fix --ansi
phpunit:
name: PHPUnit (PHP ${{ matrix.php }})
runs-on: ubuntu-latest
container:
image: php:${{ matrix.php }}-alpine
options: >-
--tmpfs /tmp:exec
--tmpfs /var/tmp:exec
services:
mysql:
image: mariadb:10.11
env:
# Corresponds to what is in .env.test
MYSQL_DATABASE: davis_test
MYSQL_USER: davis
MYSQL_PASSWORD: davis
MYSQL_ROOT_PASSWORD: root
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 3306:3306
strategy:
matrix:
php:
- '8.2'
- '8.3'
- '8.4'
- '8.5'
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install MySQL / GD / ZIP PHP extensions
run: |
apk add $PHPIZE_DEPS icu-libs icu-dev libpng-dev libzip-dev
docker-php-ext-configure intl
docker-php-ext-configure gd
docker-php-ext-configure zip
docker-php-ext-install pdo pdo_mysql intl gd zip
- name: Install Composer
run: wget -qO - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet
- name: Install dependencies with Composer
run: composer install --no-progress --no-interaction --ansi
- name: Run tests with PHPUnit
env:
DATABASE_URL: mysql://davis:davis@mysql:3306/davis_test
run: vendor/bin/phpunit --process-isolation --colors=always
migrations:
name: Migrations (${{ matrix.database }})
runs-on: ubuntu-latest
container:
image: php:8.4-alpine
options: >-
--tmpfs /tmp:exec
--tmpfs /var/tmp:exec
services:
mysql:
image: mariadb:10.11
env:
MYSQL_DATABASE: davis_test
MYSQL_USER: davis
MYSQL_PASSWORD: davis
MYSQL_ROOT_PASSWORD: root
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
postgres:
image: postgres:15-alpine
env:
POSTGRES_DB: davis_test
POSTGRES_USER: davis
POSTGRES_PASSWORD: davis
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
strategy:
matrix:
database:
- mysql
- postgresql
- sqlite
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install database extensions
run: |
apk add $PHPIZE_DEPS icu-libs icu-dev libpng-dev libzip-dev postgresql-dev sqlite-dev
docker-php-ext-configure intl
docker-php-ext-configure gd
docker-php-ext-configure zip
docker-php-ext-install pdo pdo_mysql pdo_pgsql pdo_sqlite intl gd zip
- name: Install Composer
run: wget -qO - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet
- name: Install dependencies with Composer
run: composer install --no-progress --no-interaction --ansi
- name: Run migrations (MySQL)
if: matrix.database == 'mysql'
env:
DATABASE_URL: mysql://davis:davis@mysql:3306/davis_test
run: |
php bin/console doctrine:database:create --if-not-exists --env=test
php bin/console doctrine:migrations:migrate --no-interaction --env=test
php bin/console doctrine:schema:validate --env=test
- name: Run migrations (PostgreSQL)
if: matrix.database == 'postgresql'
env:
DATABASE_URL: postgresql://davis:davis@postgres:5432/davis_test?serverVersion=15&charset=utf8
run: |
php bin/console doctrine:database:create --if-not-exists --env=test
php bin/console doctrine:migrations:migrate --no-interaction --env=test
php bin/console doctrine:schema:validate --skip-sync --env=test
- name: Run migrations (SQLite)
if: matrix.database == 'sqlite'
env:
DATABASE_URL: sqlite:///%kernel.project_dir%/var/data_test.db
run: |
php bin/console doctrine:migrations:migrate --no-interaction --env=test
php bin/console doctrine:schema:validate --skip-sync --env=test
smoke-test:
name: Application Smoke Test
runs-on: ubuntu-latest
container:
image: php:8.4-alpine
options: >-
--tmpfs /tmp:exec
--tmpfs /var/tmp:exec
services:
mysql:
image: mariadb:10.11
env:
MYSQL_DATABASE: davis_test
MYSQL_USER: davis
MYSQL_PASSWORD: davis
MYSQL_ROOT_PASSWORD: root
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install extensions and curl
run: |
apk add $PHPIZE_DEPS icu-libs icu-dev libpng-dev libzip-dev curl
docker-php-ext-configure intl
docker-php-ext-configure gd
docker-php-ext-configure zip
docker-php-ext-install pdo pdo_mysql intl gd zip
- name: Install Composer
run: wget -qO - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet
- name: Install dependencies with Composer
run: composer install --no-progress --no-interaction --ansi --optimize-autoloader
- name: Prepare application
env:
DATABASE_URL: mysql://davis:davis@mysql:3306/davis_test
APP_ENV: test
run: |
php bin/console doctrine:database:create --if-not-exists --env=test
php bin/console doctrine:migrations:migrate --no-interaction --env=test
php bin/console cache:clear --env=test
- name: Start Symfony server in background
env:
DATABASE_URL: mysql://davis:davis@mysql:3306/davis_test
APP_ENV: test
run: |
php -S 127.0.0.1:8000 -t public/ &
echo $! > server.pid
sleep 3
- name: Test application responds
run: |
# Test that the app responds with a successful HTTP status
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8000/)
echo "HTTP Response code: $RESPONSE"
if [ "$RESPONSE" -ge 200 ] && [ "$RESPONSE" -lt 400 ]; then
echo "✅ Application is responding correctly"
else
echo "❌ Application returned unexpected status code: $RESPONSE"
exit 1
fi
- name: Test dashboard
continue-on-error: true
run: |
curl -f http://127.0.0.1:8000/dashboard || echo "No health endpoint available"
- name: Stop server
if: always()
run: |
if [ -f server.pid ]; then
kill $(cat server.pid) || true
fi
================================================
FILE: .github/workflows/main.yml
================================================
name: Publish Docker image
on:
workflow_dispatch:
release:
types: [published]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
ACCOUNT: tchapi
jobs:
build:
name: Build Docker images
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
image:
- davis
- davis-standalone
platform:
- linux/amd64
- linux/arm64
include:
- image: davis
dockerfile: docker/Dockerfile
- image: davis-standalone
dockerfile: docker/Dockerfile-standalone
steps:
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Checkout
uses: actions/checkout@v4
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
context: git
images: ${{ env.REGISTRY }}/${{ env.ACCOUNT }}/${{ matrix.image }}
tags: type=raw,value=
- name: Set up QEMU
if: matrix.platform == 'linux/arm64'
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: latest
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: ${{ matrix.platform }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.ACCOUNT }}/${{ matrix.image }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ matrix.image }}-${{ env.PLATFORM_PAIR }}
cache-to: type=gha,mode=max,scope=${{ matrix.image }}-${{ env.PLATFORM_PAIR }}
- name: Export digest
run: |
mkdir -p /tmp/digests/
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ matrix.image }}_${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
- name: Build summary
run: |
echo "### ✅ Build Complete" >> $GITHUB_STEP_SUMMARY
echo "- **Image**: \`${{ matrix.image }}\`" >> $GITHUB_STEP_SUMMARY
echo "- **Platform**: \`${{ matrix.platform }}\`" >> $GITHUB_STEP_SUMMARY
echo "- **Digest**: \`${{ steps.build.outputs.digest }}\`" >> $GITHUB_STEP_SUMMARY
merge:
name: Create merged manifest
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
image:
- davis
- davis-standalone
needs: build
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests/${{ matrix.image }}/
pattern: digests-${{ matrix.image }}_*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: latest
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
context: git
images: ${{ env.REGISTRY }}/${{ env.ACCOUNT }}/${{ matrix.image }}
tags: |
type=semver,pattern={{version}}
type=edge,branch=${{ github.ref_name }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create manifest list and push
working-directory: /tmp/digests/${{ matrix.image }}/
run: |
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
--annotation index:org.opencontainers.image.created="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}" \
--annotation index:org.opencontainers.image.description="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }}" \
--annotation index:org.opencontainers.image.version="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}" \
--annotation index:org.opencontainers.image.licenses="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.licenses'] }}" \
--annotation index:org.opencontainers.image.title="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.title'] }}" \
--annotation index:org.opencontainers.image.source="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.source'] }}" \
--annotation index:org.opencontainers.image.url="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.url'] }}" \
--annotation index:org.opencontainers.image.revision="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}" \
$(printf '${{ env.REGISTRY }}/${{ env.ACCOUNT }}/${{ matrix.image }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect \
${{ env.REGISTRY }}/${{ env.ACCOUNT }}/${{ matrix.image }}:${{ steps.meta.outputs.version }}
================================================
FILE: .gitignore
================================================
###> symfony/framework-bundle ###
/.env.local
/.env.local.php
/.env.*.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/
/vendor/
###< symfony/framework-bundle ###
###> symfony/phpunit-bridge ###
.phpunit
.phpunit.result.cache
/phpunit.xml
###< symfony/phpunit-bridge ###
###> friendsofphp/php-cs-fixer ###
/.php_cs.cache
###< friendsofphp/php-cs-fixer ###
.DS_Store
TODO.todo
webdav_*
================================================
FILE: .hadolint.yaml
================================================
ignored:
- DL3018 # We don't pin apk versions (Alpine is rolling)
failure-threshold: error # Only fail on errors, not warnings
================================================
FILE: .php-cs-fixer.php
================================================
<?php
$finder = (new PhpCsFixer\Finder())
->in(__DIR__)
->exclude('var')
;
return (new PhpCsFixer\Config())
->setRules([
'@Symfony' => true,
'ordered_imports' => true, // Order "use" alphabetically
'array_syntax' => ['syntax' => 'short'], // Replace array() by []
'no_useless_return' => true, // Keep return null;
'phpdoc_order' => true, // Clean up the /** php doc */
'linebreak_after_opening_tag' => true,
'multiline_whitespace_before_semicolons' => false,
'phpdoc_add_missing_param_annotation' => true,
'single_trait_insert_per_statement' => false
])
->setUnsupportedPhpVersionAllowed(true)
->setUsingCache(false)
->setFinder($finder)
;
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019 tchap
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
Davis
---
[![Build Status][ci_badge]][ci_link]
[](https://github.com/tchapi/davis/actions/workflows/main.yml)
[![Latest release][release_badge]][release_link]
[](https://github.com/tchapi/davis/blob/main/LICENSE)


[![Sponsor me][sponsor_badge]][sponsor_link]
A modern, simple, feature-packed, fully translatable DAV server, admin interface and frontend based on `sabre/dav`, built with [Symfony 7](https://symfony.com/) and [Bootstrap 5](https://getbootstrap.com/), initially inspired by [Baïkal](https://github.com/sabre-io/Baikal) (_see dependencies table below for more detail_)
### Web admin dashboard
Provides user edition, calendar creation and sharing, and address book creation. The interface is simple and straightforward, responsive, and provides a light and a dark mode.
Supports **Basic authentication**, as well as **IMAP** and **LDAP** (_via external providers_).
### DAV Server
The underlying server implementation supports (*non-exhaustive list*) CalDAV, CardDAV, WebDAV, calendar sharing, scheduling, mail notifications, and server-side subscriptions (*depending on the capabilities of the client*).
### Additional features ✨
- Subscriptions (to be added via the client, such as the macOS calendar, for instance)
- Public calendars, available to anyone with the link
- Automatic birthday calendar, updated on the fly when birthdates change in your contacts
### Deployment
Easily containerisable (_`Dockerfile` and sample `docker-compose` configuration file provided_).
NixOS [package](https://search.nixos.org/packages?channel=unstable&show=davis&from=0&size=50&sort=relevance&type=packages&query=davis) and module available.
Comes with already built Docker images in two flavours: [standalone](https://github.com/tchapi/davis/pkgs/container/davis-standalone) (with included Caddy reverse proxy) or [barebone](https://github.com/tchapi/davis/pkgs/container/davis).
- - -
✨ Created and maintained (with the help of the community) by [@tchapi](https://github.com/tchapi). ✨



| Dark / Light mode | Useful information at hand |
|--------------------|----------------------------|
| | |
# 🔩 Requirements
- PHP > 8.2 (with `pdo_mysql` [or `pdo_pgsql`, `pdo_sqlite`], `gd` and `intl` extensions), compatible up to PHP 8.5 (_See dependencies table below_)
- A compatible database layer, such as MySQL or MariaDB (recommended), PostgreSQL (not extensively tested yet) or SQLite (not extensively tested yet)
- Composer > 2 (_The last release compatible with Composer 1 is [v1.6.2](https://github.com/tchapi/davis/releases/tag/v1.6.2)_)
- The [`imap`](https://www.php.net/manual/en/imap.installation.php) and [`ldap`](https://www.php.net/manual/en/ldap.installation.php) PHP extensions if you want to use either authentication methods (_these are not enabled / compiled by default except in the Docker image_)
Dependencies
------------
| Release | Status | PHP version |
|--------------------|----------------------------|--------------------|
| `main` (edge) | development branch | PHP 8.2+ |
| `v5.x` | stable | PHP 8.2+ |
| `v4.x` | security fixes only | PHP 8.0 → 8.3 |
| `v3.x` | :warning: unmaintained | PHP 7.3 → 8.2 |
# 🧰 Installation
0. Clone this repository
1. Retrieve the dependencies:
a. If you plan to run Davis locally, for development purposes
```
composer install
```
b. If you plan to run Davis on production
```
composer install --no-dev
```
And set `APP_ENV=prod` in your `.env.local` file (see below)
3. At least put the correct credentials to your database (driver and url) in your `.env.local` file so you can easily create the necessary tables.
4. Run the migrations to create all the necessary tables:
```
bin/console doctrine:migrations:migrate
```
**Davis** can also be used with a pre-existing MySQL database (_for instance, one previously managed by Baïkal_). See the paragraph "Migrating from Baikal" for more info.
> [!NOTE]
>
> The tables are not _exactly_ equivalent to those of Baïkal, and allow for a bit more room in columns for instance (among other things)
## Configuration
Create your own `.env.local` file to change the necessary variables, if you plan on using `symfony/dotenv`.
> [!NOTE]
>
> If your installation is behind a web server like Apache or Nginx, you can setup the env vars directly in your Apache or Nginx configuration (see below). Skip this part in this case.
> [!CAUTION]
>
> In a production environnement, the `APP_ENV` variable MUST be set to `prod` to prevent leaking sensitive data.
**a. The database driver and url** (_you should already have it configured since you created the database previously_)
```shell
DATABASE_DRIVER=mysql # or postgresql, or sqlite
DATABASE_URL=mysql://db_user:db_pass@host:3306/db_name?serverVersion=10.9.3-MariaDB&charset=utf8mb4
```
**b. The admin password for the backend**
```shell
ADMIN_LOGIN=admin
ADMIN_PASSWORD=test
```
> [!NOTE]
>
> You can bypass auth entirely if you use a third party authorization provider such as Authelia. In that case, set the `ADMIN_AUTH_BYPASS` env var to `true` (case-sensitive, this is actually the string `true`, not a boolean) to allow full access to the dashboard. This does not change the behaviour of the DAV server.
**c. The auth Realm and method for HTTP auth**
```shell
AUTH_REALM=SabreDAV
AUTH_METHOD=Basic # can be "Basic", "IMAP" or "LDAP"
```
> See [the following paragraph](#specific-environment-variables-for-imap-and-ldap-authentication-methods) for more information if you choose either IMAP or LDAP.
**d. The global flags to enable CalDAV, CardDAV and WebDAV**. You can also disable the option to have calendars public
```shell
CALDAV_ENABLED=true
CARDDAV_ENABLED=true
WEBDAV_ENABLED=false
PUBLIC_CALENDARS_ENABLED=true
```
> [!NOTE]
>
> By default, `PUBLIC_CALENDARS_ENABLED` is true. That doesn't mean that all calendars are public by default — it just means that you have an option, upon calendar creation, to set the calendar public (but it's not public by default).
**e. Mailer configuration**
It includes:
- the mailer uri (`MAILER_DSN`)
- The email address that your invites are going to be sent from
```shell
MAILER_DSN=smtp://user:pass@smtp.example.com:port
INVITE_FROM_ADDRESS=no-reply@example.org
```
> [!WARNING]
> If the username, password or host contain any character considered special in a URI (such as `: / ? # [ ] @ ! $ & ' ( ) * + , ; =`), you MUST encode them.
> See [here](https://symfony.com/doc/current/mailer.html#transport-setup) for more details.
**f. The reminder offset for all birthdays**
You must specify a relative duration, as specified in [the RFC 5545 spec](https://www.rfc-editor.org/rfc/rfc5545.html#section-3.3.6)
```shell
BIRTHDAY_REMINDER_OFFSET=PT9H
```
If you don't want a reminder for birthday events, set it to the `false` value (lowercase):
```shell
BIRTHDAY_REMINDER_OFFSET=false
```
> [!NOTE]
>
> By default, if the env var is not set or empty, we use `PT9H` (9am on the date of the birthday).
**g. The paths for the WebDAV installation**
> [!TIP]
>
> I recommend that you use absolute directories so you know exactly where your files reside.
```shell
WEBDAV_TMP_DIR=/webdav/tmp
WEBDAV_PUBLIC_DIR=/webdav/public
WEBDAV_HOMES_DIR=
```
> [!NOTE]
>
> In a docker setup, I recommend setting `WEBDAV_TMP_DIR` to `/tmp`.
> [!NOTE]
>
> By default, home directories are disabled totally (the env var is set to an empty string). If needed, it is recommended to use a folder that is **NOT** a child of the public dir, such as `/webdav/homes` for instance, so that users cannot access other users' homes.
**h. The log file path**
You can use an absolute file path here, and you can use Symfony's `%kernel.logs_dir%` and `%kernel.environment%` placeholders if needed (as in the default value). Setting it to `/dev/null` will disable logging altogether.
```shell
LOG_FILE_PATH="%kernel.logs_dir%/%kernel.environment%.log"
```
**i. The timezone you want for the app**
This must comply with the [official list](https://www.php.net/manual/en/timezones.php)
```shell
APP_TIMEZONE=Australia/Lord_Howe
```
> Set a void value like so:
> ```shell
> APP_TIMEZONE=
> ```
> in your environment file if you wish to use the **actual default timezone of the server**, and not enforcing it.
**j. Trusting forwarded headers**
If you're behind one or several proxies, the TLS termination might be upstream and the application might not be aware of the HTTPS context. In order for urls to be generated with the correct scheme, you should indicate that you trust the chain of proxies until the TLS termination one. You can use the Symfony mechanism for that (see [documentation](https://symfony.com/doc/7.2/deployment/proxies.html) for possible values):
```shell
SYMFONY_TRUSTED_PROXIES=127.0.0.1,REMOTE_ADDR
```
#### Overriding the dotenv (`.env`) path
You can override the expected location of the environment files (`.env`, `.env.local`, etc) by setting the `ENV_DIR` variable.
The value should be to a _folder_ containing the env files. This value must be specified in the actual environment and *not* in an `.env` file as it is read and evaluated **before** the env files are read.
For instance, you can use it to call `bin/console` with a specific dotenv directory:
```shell
> ENV_DIR=/var/lib/davis bin/console
```
Or use it directly in the Apache configuration
```apache
<VirtualHost *:80>
# .. rest of config (see ¶ below)
SetEnv ENV_DIR /var/lib/davis
# ... other env vars if needed
</VirtualHost>
```
### Specific environment variables for IMAP and LDAP authentication methods
In case you use the `IMAP` auth type, you must specify the auth url (_the "mailbox" url_) in `IMAP_AUTH_URL` as `host:port`, the encryption method (SSL, TLS or None) and whether the certificate should be validated.
You should also explicitely define whether you want new authenticated users to be created upon login:
```shell
IMAP_AUTH_URL=imap.mydomain.com:993
IMAP_ENCRYPTION_METHOD=ssl # ssl, tls or false
IMAP_CERTIFICATE_VALIDATION=true
IMAP_AUTH_USER_AUTOCREATE=true # false by default
```
Same goes for LDAP, where you must specify the LDAP server url, the DN pattern, the Mail attribute, as well as whether you want new authenticated users to be created upon login (_like for IMAP_):
```shell
LDAP_AUTH_URL=ldap://127.0.0.1:3890 # default LDAP port
LDAP_DN_PATTERN=uid=%u,ou=users,dc=domain,dc=com
LDAP_MAIL_ATTRIBUTE=mail
LDAP_AUTH_USER_AUTOCREATE=true # false by default
LDAP_CERTIFICATE_CHECKING_STRATEGY=try # try by default. Other values are: never, hard, demand or allow
```
> Ex: for [Zimbra LDAP](https://zimbra.github.io/adminguide/latest/#zimbra_ldap_service), you might want to use the `zimbraMailDeliveryAddress` attribute to retrieve the principal user email:
> ```shell
> LDAP_MAIL_ATTRIBUTE=zimbraMailDeliveryAddress
> ```
## Migrating from Baïkal?
If you're migrating from Baïkal, then you will likely want to do the following :
1. Get a backup of your data (without the `CREATE` statements, but with complete `INSERT` statements):
```shell
mysqldump -u root -p --no-create-info --complete-insert baikal > baikal_to_davis.sql # baikal is the actual name of your database
```
2. Create a new database for Davis (let's name it `davis`) and create the base schema:
```shell
bin/console doctrine:migrations:migrate 'DoctrineMigrations\Version20191030113307' --no-interaction
```
3. Reimport the data back:
```
mysql -uroot -p davis < baikal_to_davis.sql
```
4. Run the necessary remaining migrations:
```
bin/console doctrine:migrations:migrate
```
> [!NOTE]
> Some details / steps to resolve are also available in https://github.com/tchapi/davis/issues/226.
# 🌐 Access / Webserver
A simple status page is available on the root `/` of the server.
The administration interface is available at `/dashboard`. You need to login to use it (See `ADMIN_LOGIN` and `ADMIN_PASSWORD` env vars).
The main endpoint for CalDAV, WebDAV or CardDAV is at `/dav`.
> [!TIP]
>
> For shared hosting, the `symfony/apache-pack` is included and provides a standard `.htaccess` file in the public directory so redirections should work out of the box.
## API Endpoint
For user and calendar management there is an API endpoint. See [the API documentation](docs/api/README.md) for more information.
> [!TIP]
>
> The API endpoint requires an environment variable `API_KEY` set to a secret key that you will use in the `X-Davis-API-Token` header of your requests to authenticate. You can generate it with `bin/console api:generate`
## Webserver Configuration Examples
### Example Caddy 2 configuration
```
dav.domain.tld {
# General settings
encode zstd gzip
header {
-Server
-X-Powered-By
# enable HSTS
Strict-Transport-Security max-age=31536000;
# disable clients from sniffing the media type
X-Content-Type-Options nosniff
# keep referrer data off of HTTP connections
Referrer-Policy no-referrer-when-downgrade
}
root * /var/www/davis/public
php_fastcgi 127.0.0.1:8000
file_server
}
```
### Example Apache 2.4 configuration
```apache
<VirtualHost *:80>
ServerName dav.domain.tld
DocumentRoot /var/www/davis/public
DirectoryIndex /index.php
<Directory /var/www/davis/public>
AllowOverride None
Order Allow,Deny
Allow from All
FallbackResource /index.php
</Directory>
# Apache > 2.4.25, else remove this part
<Directory /var/www/davis/public/bundles>
FallbackResource disabled
</Directory>
# Env vars (if you did not use .env.local)
SetEnv APP_ENV prod
SetEnv APP_SECRET <app-secret-id>
SetEnv DATABASE_DRIVER "mysql"
SetEnv DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name?serverVersion=10.9.3-MariaDB&charset=utf8mb4"
# ... etc
</VirtualHost>
```
### Example Nginx configuration
```nginx
server {
server_name dav.domain.tld;
root /var/www/davis/public;
location / {
try_files $uri /index.php$is_args$args;
}
location /bundles {
try_files $uri =404;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; # Change for your PHP version
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# Env vars (if you did not use .env.local)
fastcgi_param APP_ENV prod;
fastcgi_param APP_SECRET <app-secret-id>;
fastcgi_param DATABASE_DRIVER "mysql";
fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name?serverVersion=10.9.3-MariaDB&charset=utf8mb4";
# ... etc ...
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
internal;
}
location ~ \.php$ {
return 404;
}
}
```
More examples and information [here](https://symfony.com/doc/current/setup/web_server_configuration.html).
## Well-known redirections for CalDAV and CardDAV
Web-based protocols like CalDAV and CardDAV can be found using a discovery service. Some clients require that you implement a path prefix to point to the correct location for your service. See [here](https://en.wikipedia.org/wiki/List_of_/.well-known/_services_offered_by_webservers) for more info.
If you use Apache as your webserver, you can enable the redirections with:
```apache
RewriteEngine On
RewriteRule ^\.well-known/carddav /dav/ [R=301,L]
RewriteRule ^\.well-known/caldav /dav/ [R=301,L]
```
Make sure that `mod_rewrite` is enabled on your installation beforehand.
If you use Nginx, you can add this to your configuration:
```nginx
location / {
rewrite ^/.well-known/carddav /dav/ redirect;
rewrite ^/.well-known/caldav /dav/ redirect;
}
```
# 🐳 Dockerized installation
A `Dockerfile` is available for you to compile the image.
To build the checked out version, just run:
docker build --pull --file docker/Dockerfile --tag davis:latest --build-arg fpm_user=82:82 .
> [!TIP]
>
> The `fpm_user` build arg allows to set:
> - the uid FPM will run with
> - the owner of the app folder
>
> This is helpful if you have a proxy that does not use the same default PHP Alpine uid/gid for www-data (82:82). For instance, in the docker compose file, nginx uses 101:101
>
This will build a `davis:latest` image that you can directly use. Do not forget to pass sensible environment variables to the container since the _dist_ `.env` file will take precedence if no `.env.local` or environment variable is found.
You can use `--platform` to specify the platform to build for. Currently, `arm64` (ARMv8) and `amd64` (x86) are supported.
> [!IMPORTANT]
>
> ⚠ Do not forget to run all the database migrations the first time you run the container :
>
> docker exec -it davis sh -c "APP_ENV=prod bin/console doctrine:migrations:migrate --no-interaction"
## Docker images
For each release, a Docker image is built and published in the [Github package repository](https://github.com/tchapi/davis/pkgs/container/davis).
### Release images
Each release builds and tags two images: one for the standard build (no reverse-proxy) and one for the standalone build (including Caddy as a reverse-proxy). Example:
```
docker pull ghcr.io/tchapi/davis:v4.4.0
```
```
docker pull ghcr.io/tchapi/davis-standalone:v4.4.0
```
### Edge image
The edge image is generally built from the tip of the main branch, but might sometimes be used for specific branch testing:
```
docker pull ghcr.io/tchapi/davis:edge
```
> [!WARNING]
>
> The `edge` image must not be considered stable. **Use only release images for production setups**.
## Full stack
A few `docker-compose.yml` files are also included (in the `docker` folder) as minimal example setups, with various databases for instance.
You can start the containers with :
cd docker && docker compose up -d
> [!NOTE]
>
> The default recipe above uses MariaDB.
> [!IMPORTANT]
>
> ⚠ Do not forget to run all the database migrations the first time you run the container :
>
> docker exec -it davis sh -c "APP_ENV=prod bin/console doctrine:migrations:migrate --no-interaction"
> [!WARNING]
>
> For SQLite, you must also make sure that the folder the database will reside in AND the database file in itself have the right permissions! You can do for instance:
> `chown -R www-data: /data` if `/data` is the folder your SQLite database will be in, just after you have run the migrations
### Updating from a previous version
If you update the code, you need to make sure the database structure is in sync.
**Before v3.0.0**, you need to force the update:
docker exec -it davis sh -c "APP_ENV=prod bin/console doctrine:schema:update --force --no-interaction"
**For v3.0.0 and after**, you can just migrate again (_provided you correctly followed the migration notes in the v3.0.0 release_):
docker exec -it davis sh -c "APP_ENV=prod bin/console doctrine:migrations:migrate --no-interaction"
Then, head up to `http://<YOUR_DOCKER_IP>:9000` to see the status display :

> Note that there is no user and no principals created by default.
# NixOS Installation
To install Davis on NixOS, you can use the builtin NixOS module [`services.davis`](https://search.nixos.org/options?channel=unstable&query=services.davis).
Currently the NixOS module and package are in the nixos-unstable channel, but they are slated to enter the stable channel in the 24.05 release.
* [All `services.davis` options](https://search.nixos.org/options?channel=unstable&query=services.davis)
* [Basic Guide](https://nixos.org/manual/nixos/unstable/#module-services-davis)
If you encounter a bug or problem with the NixOS Davis module please open an issue [at the nixpkgs repo](https://github.com/NixOS/nixpkgs/issues/new/choose) so the module maintainers can assist.
# Development
You can spin off a local PHP webserver with:
php -S localhost:8000 -t public
If you change or add translations, you need to update the `messages` XLIFF file with:
bin/console translation:extract en --force --domain=messages+intl-icu
## Testing
You can use:
./vendor/bin/phpunit
## ✨ Code linting
We use [PHP-CS-Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) with:
PHP_CS_FIXER_IGNORE_ENV=True ./vendor/bin/php-cs-fixer fix
## ❓ How-to's
Below are some issues that can bring more info / insight into custom setups that Davis users have experienced in the past. Hopefully it can help:
- **Davis on Proxmox / TrueNAS Scale**: https://github.com/tchapi/davis/issues/164
## 🐛 Troubleshooting
Depending on how you run Davis, logs are either:
- [dev] printed out directly in the console
- [dev] available in the Symfony Debug Bar in the [Profiler](https://symfony.com/doc/current/profiler.html)
- [dev] logged in `./var/log/dev.log`
- [prod] logged in `./var/log/prod.log`, but only if there has been an error (_it's the fingers_crossed filter, explained [here](https://symfony.com/doc/current/logging.html#handlers-that-modify-log-entries)_)
> [!NOTE]
>
> It's `./var/log` (relative to the Davis installation), not `/var/log`.
>
> To tail the aplication log on Docker, do:
> ```
> docker exec -it davis tail /var/www/davis/var/log/prod.log
> ```
### I have a "Bad timezone configuration env var" error on the dashboard
If you see this:

It means that the value you set for the `APP_TIMEZONE` env var is not a correct timezone, as per [the official list](https://www.php.net/manual/en/timezones.php). Your timezone has thus not been set and is the server's default (Here, UTC). Adjust the setting accordingly.
### I have a 500 and no tables have been created
You probably forgot to run the migration once to create the necessary DB schema
In Docker:
```shell
docker exec -it davis sh -c "APP_ENV=prod bin/console doctrine:migrations:migrate --no-interaction"
```
In a shell, if you run Davis locally:
bin/console doctrine:migrations:migrate
### I have a 500 and a log about `Uncaught Error: Class "Symfony\Bundle\WebProfilerBundle\WebProfilerBundle" not found`
You are running the app in dev mode, but you haven't installed the dev dependencies. Either:
a. Set `APP_ENV=prod` in your local env file (See configuration above)
b. Or `composer install` (without the `--no-dev` flag)
### The LDAP connection is not working
> [!NOTE]
>
> Make sure all environment parameters are in plain text (no quotes).
Check if your instance can reach your LDAP server:
- For Docker instances: make sure it is on the same network
- Check connection via `ldapsearch`:
```shell
# For docker: connect into container's shell
docker exec -it davis sh
# install ldap utils (for alpine linux)
apk add openldap-clients
# User checking their own entry
ldapsearch -H ldap://lldap-server:3890 -D "uid=someuser,ou=users,dc=domain,dc=com" -W -b "dc=domain,dc=com" "(uid=someuser)"
```
- Check that the `LDAP_DN_PATTERN` filter is compliant with your LDAP service
- Example: `uid=%u,ou=people,dc=domain,dc=com`: [LLDAP](https://github.com/lldap/lldap) uses `people` instead of `users`.
### The birthday calendar is not synced / not up to date
An update event might have been missed. In this case, it's easy to resync all contacts by issuing the command:
```
bin/console dav:sync-birthday-calendar
```
# 📚 Libraries used
- Symfony 7 (Licence : MIT)
- Sabre-io/dav (Licence : BSD-3-Clause)
- Bootstrap 5 (Licence : MIT)
_This project does not use any pipeline for the assets since the frontend side is relatively simple, and based on Bootstrap._
# ⚖️ Licence
This project is release under the MIT licence. See the LICENCE file
[ci_badge]: https://github.com/tchapi/davis/workflows/CI/badge.svg
[ci_link]: https://github.com/tchapi/davis/actions?query=workflow%3ACI
[sponsor_badge]: https://img.shields.io/badge/sponsor%20me-🙏-blue?logo=paypal
[sponsor_link]: https://paypal.me/tchap
[release_badge]: https://img.shields.io/github/v/release/tchapi/davis
[release_link]: https://github.com/tchapi/davis/releases
================================================
FILE: bin/console
================================================
#!/usr/bin/env php
<?php
$overridenEnvDir = getenv('ENV_DIR') ?: null;
if ($overridenEnvDir) {
// Tell the Runtime not to touch dotenv so we can load our own file.
// This is needed if the ENV_DIR is outside of the project directory
$_SERVER['APP_RUNTIME_OPTIONS']['disable_dotenv'] = true;
}
use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Dotenv\Dotenv;
if (!is_dir(dirname(__DIR__).'/vendor')) {
throw new LogicException('Dependencies are missing. Try running "composer install".');
}
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) {
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
}
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
if ($overridenEnvDir) {
// Load our own now, after the runtime has booted
(new Dotenv())->bootEnv($overridenEnvDir.'/.env');
}
return function (array $context) {
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
return new Application($kernel);
};
================================================
FILE: bin/phpunit
================================================
#!/usr/bin/env php
<?php
if (!ini_get('date.timezone')) {
ini_set('date.timezone', 'UTC');
}
if (is_file(dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit')) {
define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
PHPUnit\TextUI\Command::main();
} else {
if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) {
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n";
exit(1);
}
require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php';
}
================================================
FILE: composer.json
================================================
{
"name": "tchapi/davis",
"description": "A simple, fully translatable admin interface and frontend for sabre/dav based on Symfony",
"type": "project",
"license": "MIT",
"require": {
"php": "^8.2",
"ext-ctype": "*",
"ext-gd": "*",
"ext-iconv": "*",
"ext-zip": "*",
"composer-runtime-api": "^2",
"dantsu/php-osm-static-api": "^0.6.4",
"doctrine/doctrine-bundle": "^2.15.1",
"doctrine/doctrine-migrations-bundle": "^3.4.2",
"doctrine/orm": "^2.20.6",
"sabre/dav": "^4.7.0",
"symfony/apache-pack": "^1.0.1",
"symfony/asset": "^7.4",
"symfony/console": "^7.4",
"symfony/dotenv": "^7.4",
"symfony/expression-language": "^7.4",
"symfony/flex": "^2.7.1",
"symfony/form": "^7.4",
"symfony/framework-bundle": "^7.4",
"symfony/http-client": "^7.4",
"symfony/intl": "^7.4",
"symfony/mailer": "^7.4",
"symfony/monolog-bundle": "^3.10.0",
"symfony/polyfill-intl-messageformatter": "^1.31",
"symfony/process": "^7.4",
"symfony/property-access": "^7.4",
"symfony/property-info": "^7.4",
"symfony/runtime": "^7.4",
"symfony/security-bundle": "^7.4",
"symfony/serializer": "^7.4",
"symfony/translation": "^7.4",
"symfony/twig-bundle": "^7.4",
"symfony/validator": "^7.4",
"symfony/web-link": "^7.4",
"symfony/yaml": "^7.4",
"webklex/php-imap": "^6.2"
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.5",
"friendsofphp/php-cs-fixer": "^3.49.0",
"phpunit/phpunit": "^10.5.10",
"symfony/browser-kit": "^7.4",
"symfony/css-selector": "^7.4",
"symfony/debug-bundle": "^7.4",
"symfony/maker-bundle": "^1.54",
"symfony/phpunit-bridge": "^7.4",
"symfony/stopwatch": "^7.4",
"symfony/web-profiler-bundle": "^7.4"
},
"config": {
"preferred-install": {
"*": "dist"
},
"sort-packages": true,
"platform": {
"php": "8.2.15"
},
"allow-plugins": {
"composer/package-versions-deprecated": true,
"symfony/flex": true,
"symfony/runtime": true
}
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
]
},
"conflict": {
"symfony/symfony": "*"
},
"extra": {
"symfony": {
"allow-contrib": false,
"require": "7.4.*"
}
}
}
================================================
FILE: config/bundles.php
================================================
<?php
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
];
================================================
FILE: config/packages/cache.yaml
================================================
framework:
cache:
# Unique name of your app: used to compute stable namespaces for cache keys.
#prefix_seed: your_vendor_name/app_name
# The "app" cache stores to the filesystem by default.
# The data in this cache should persist between deploys.
# Other options include:
# Redis
#app: cache.adapter.redis
#default_redis_provider: redis://localhost
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
#app: cache.adapter.apcu
# Namespaced pools use the above "app" backend by default
#pools:
#my.dedicated.cache: null
================================================
FILE: config/packages/dev/debug.yaml
================================================
debug:
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser.
# See the "server:dump" command to start a new server.
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%"
================================================
FILE: config/packages/dev/monolog.yaml
================================================
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
# uncomment to get logging in your browser
# you may have to allow bigger header sizes in your Web server configuration
#firephp:
# type: firephp
# level: info
#chromephp:
# type: chromephp
# level: info
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine", "!console"]
================================================
FILE: config/packages/dev/web_profiler.yaml
================================================
web_profiler:
toolbar: true
intercept_redirects: false
framework:
profiler: { only_exceptions: false }
================================================
FILE: config/packages/doctrine.yaml
================================================
doctrine:
dbal:
# The server_version must be configured directly in the
# DATABASE_URL to allow different drivers without adding
# too many env vars
driver: 'pdo_%env(string:default:default_database_driver:DATABASE_DRIVER)%'
url: '%env(resolve:DATABASE_URL)%'
orm:
auto_generate_proxy_classes: true
report_fields_where_declared: true
enable_lazy_ghost_objects: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
controller_resolver:
auto_mapping: false
mappings:
App:
is_bundle: false
type: attribute
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
================================================
FILE: config/packages/doctrine_migrations.yaml
================================================
doctrine_migrations:
migrations_paths:
# namespace is arbitrary but should be different from App\Migrations
# as migrations classes should NOT be autoloaded
'DoctrineMigrations': '%kernel.project_dir%/migrations'
================================================
FILE: config/packages/framework.yaml
================================================
# see https://symfony.com/doc/current/reference/configuration/framework.html
framework:
secret: '%env(APP_SECRET)%'
handle_all_throwables: true
#csrf_protection: true
http_method_override: false
# Enables session support. Note that the session will ONLY be started if you read or write from it.
# Remove or comment this section to explicitly disable session support.
session:
handler_id: null
cookie_secure: auto
cookie_samesite: lax
name: 'DAVIS_SESSION'
storage_factory_id: session.storage.factory.native
profiler:
collect_serializer_data: true
property_info:
with_constructor_extractor: false
php_errors:
log: true
================================================
FILE: config/packages/mailer.yaml
================================================
framework:
mailer:
dsn: '%env(MAILER_DSN)%'
================================================
FILE: config/packages/prod/deprecations.yaml
================================================
# As of Symfony 5.1, deprecations are logged in the dedicated "deprecation" channel when it exists
#monolog:
# channels: [deprecation]
# handlers:
# deprecation:
# type: stream
# channels: [deprecation]
# path: "%kernel.logs_dir%/%kernel.environment%.deprecations.log"
================================================
FILE: config/packages/prod/doctrine.yaml
================================================
doctrine:
orm:
auto_generate_proxy_classes: false
metadata_cache_driver:
type: pool
pool: doctrine.system_cache_pool
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool
framework:
cache:
pools:
doctrine.result_cache_pool:
adapter: cache.app
doctrine.system_cache_pool:
adapter: cache.system
================================================
FILE: config/packages/prod/monolog.yaml
================================================
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
excluded_http_codes: [404, 405]
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
nested:
type: stream
path: "%env(resolve:LOG_FILE_PATH)%"
level: debug
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine"]
================================================
FILE: config/packages/prod/routing.yaml
================================================
framework:
router:
strict_requirements: null
================================================
FILE: config/packages/routing.yaml
================================================
framework:
router:
utf8: true
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
#default_uri: http://localhost
================================================
FILE: config/packages/security.yaml
================================================
security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
providers:
admin_user_provider:
id: App\Security\AdminUserProvider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_v1:
pattern: ^/api/v1
stateless: true
custom_authenticators:
- App\Security\ApiKeyAuthenticator
main:
lazy: true
custom_authenticators:
- App\Security\LoginFormAuthenticator
provider: admin_user_provider
logout:
path: app_logout
target: dashboard
access_control:
- { path: ^/$, roles: PUBLIC_ACCESS }
- { path: ^/dav, roles: PUBLIC_ACCESS }
- { path: ^/dashboard, roles: ROLE_ADMIN, allow_if: "'%env(default:default_admin_auth_bypass:ADMIN_AUTH_BYPASS)%' === 'true'" }
- { path: ^/users, roles: ROLE_ADMIN, allow_if: "'%env(default:default_admin_auth_bypass:ADMIN_AUTH_BYPASS)%' === 'true'" }
- { path: ^/calendars, roles: ROLE_ADMIN, allow_if: "'%env(default:default_admin_auth_bypass:ADMIN_AUTH_BYPASS)%' === 'true'" }
- { path: ^/adressbooks, roles: ROLE_ADMIN, allow_if: "'%env(default:default_admin_auth_bypass:ADMIN_AUTH_BYPASS)%' === 'true'" }
- { path: ^/api/v1/health$, roles: PUBLIC_ACCESS }
- { path: ^/api, roles: IS_AUTHENTICATED }
================================================
FILE: config/packages/test/framework.yaml
================================================
framework:
test: true
session:
storage_factory_id: session.storage.factory.mock_file
================================================
FILE: config/packages/test/monolog.yaml
================================================
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
excluded_http_codes: [404, 405]
channels: ["!event"]
nested:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
================================================
FILE: config/packages/test/twig.yaml
================================================
twig:
strict_variables: true
================================================
FILE: config/packages/test/validator.yaml
================================================
framework:
validation:
not_compromised_password: false
================================================
FILE: config/packages/test/web_profiler.yaml
================================================
web_profiler:
toolbar: false
intercept_redirects: false
framework:
profiler: { collect: false }
================================================
FILE: config/packages/translation.yaml
================================================
framework:
default_locale: en
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks:
- en
================================================
FILE: config/packages/twig.yaml
================================================
twig:
default_path: '%kernel.project_dir%/templates'
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
form_themes: ['bootstrap_5_horizontal_layout.html.twig']
globals:
invite_from_address: '%env(INVITE_FROM_ADDRESS)%'
calDAVEnabled: '%env(bool:CALDAV_ENABLED)%'
cardDAVEnabled: '%env(bool:CARDDAV_ENABLED)%'
webDAVEnabled: '%env(bool:WEBDAV_ENABLED)%'
authRealm: '%env(AUTH_REALM)%'
authMethod: '%env(AUTH_METHOD)%'
================================================
FILE: config/packages/validator.yaml
================================================
framework:
validation:
enable_attributes: true
email_validation_mode: html5
# Enables validator auto-mapping support.
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
#auto_mapping:
# App\Entity\: []
================================================
FILE: config/reference.php
================================================
<?php
// This file is auto-generated and is for apps only. Bundles SHOULD NOT rely on its content.
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Component\Config\Loader\ParamConfigurator as Param;
/**
* This class provides array-shapes for configuring the services and bundles of an application.
*
* Services declared with the config() method below are autowired and autoconfigured by default.
*
* This is for apps only. Bundles SHOULD NOT use it.
*
* Example:
*
* ```php
* // config/services.php
* namespace Symfony\Component\DependencyInjection\Loader\Configurator;
*
* return App::config([
* 'services' => [
* 'App\\' => [
* 'resource' => '../src/',
* ],
* ],
* ]);
* ```
*
* @psalm-type ImportsConfig = list<string|array{
* resource: string,
* type?: string|null,
* ignore_errors?: bool,
* }>
* @psalm-type ParametersConfig = array<string, scalar|\UnitEnum|array<scalar|\UnitEnum|array<mixed>|Param|null>|Param|null>
* @psalm-type ArgumentsType = list<mixed>|array<string, mixed>
* @psalm-type CallType = array<string, ArgumentsType>|array{0:string, 1?:ArgumentsType, 2?:bool}|array{method:string, arguments?:ArgumentsType, returns_clone?:bool}
* @psalm-type TagsType = list<string|array<string, array<string, mixed>>> // arrays inside the list must have only one element, with the tag name as the key
* @psalm-type CallbackType = string|array{0:string|ReferenceConfigurator,1:string}|\Closure|ReferenceConfigurator|ExpressionConfigurator
* @psalm-type DeprecationType = array{package: string, version: string, message?: string}
* @psalm-type DefaultsType = array{
* public?: bool,
* tags?: TagsType,
* resource_tags?: TagsType,
* autowire?: bool,
* autoconfigure?: bool,
* bind?: array<string, mixed>,
* }
* @psalm-type InstanceofType = array{
* shared?: bool,
* lazy?: bool|string,
* public?: bool,
* properties?: array<string, mixed>,
* configurator?: CallbackType,
* calls?: list<CallType>,
* tags?: TagsType,
* resource_tags?: TagsType,
* autowire?: bool,
* bind?: array<string, mixed>,
* constructor?: string,
* }
* @psalm-type DefinitionType = array{
* class?: string,
* file?: string,
* parent?: string,
* shared?: bool,
* synthetic?: bool,
* lazy?: bool|string,
* public?: bool,
* abstract?: bool,
* deprecated?: DeprecationType,
* factory?: CallbackType,
* configurator?: CallbackType,
* arguments?: ArgumentsType,
* properties?: array<string, mixed>,
* calls?: list<CallType>,
* tags?: TagsType,
* resource_tags?: TagsType,
* decorates?: string,
* decoration_inner_name?: string,
* decoration_priority?: int,
* decoration_on_invalid?: 'exception'|'ignore'|null,
* autowire?: bool,
* autoconfigure?: bool,
* bind?: array<string, mixed>,
* constructor?: string,
* from_callable?: CallbackType,
* }
* @psalm-type AliasType = string|array{
* alias: string,
* public?: bool,
* deprecated?: DeprecationType,
* }
* @psalm-type PrototypeType = array{
* resource: string,
* namespace?: string,
* exclude?: string|list<string>,
* parent?: string,
* shared?: bool,
* lazy?: bool|string,
* public?: bool,
* abstract?: bool,
* deprecated?: DeprecationType,
* factory?: CallbackType,
* arguments?: ArgumentsType,
* properties?: array<string, mixed>,
* configurator?: CallbackType,
* calls?: list<CallType>,
* tags?: TagsType,
* resource_tags?: TagsType,
* autowire?: bool,
* autoconfigure?: bool,
* bind?: array<string, mixed>,
* constructor?: string,
* }
* @psalm-type StackType = array{
* stack: list<DefinitionType|AliasType|PrototypeType|array<class-string, ArgumentsType|null>>,
* public?: bool,
* deprecated?: DeprecationType,
* }
* @psalm-type ServicesConfig = array{
* _defaults?: DefaultsType,
* _instanceof?: InstanceofType,
* ...<string, DefinitionType|AliasType|PrototypeType|StackType|ArgumentsType|null>
* }
* @psalm-type ExtensionType = array<string, mixed>
* @psalm-type FrameworkConfig = array{
* secret?: scalar|Param|null,
* http_method_override?: bool|Param, // Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. // Default: false
* allowed_http_method_override?: list<string|Param>|null,
* trust_x_sendfile_type_header?: scalar|Param|null, // Set true to enable support for xsendfile in binary file responses. // Default: "%env(bool:default::SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER)%"
* ide?: scalar|Param|null, // Default: "%env(default::SYMFONY_IDE)%"
* test?: bool|Param,
* default_locale?: scalar|Param|null, // Default: "en"
* set_locale_from_accept_language?: bool|Param, // Whether to use the Accept-Language HTTP header to set the Request locale (only when the "_locale" request attribute is not passed). // Default: false
* set_content_language_from_locale?: bool|Param, // Whether to set the Content-Language HTTP header on the Response using the Request locale. // Default: false
* enabled_locales?: list<scalar|Param|null>,
* trusted_hosts?: list<scalar|Param|null>,
* trusted_proxies?: mixed, // Default: ["%env(default::SYMFONY_TRUSTED_PROXIES)%"]
* trusted_headers?: list<scalar|Param|null>,
* error_controller?: scalar|Param|null, // Default: "error_controller"
* handle_all_throwables?: bool|Param, // HttpKernel will handle all kinds of \Throwable. // Default: true
* csrf_protection?: bool|array{
* enabled?: scalar|Param|null, // Default: null
* stateless_token_ids?: list<scalar|Param|null>,
* check_header?: scalar|Param|null, // Whether to check the CSRF token in a header in addition to a cookie when using stateless protection. // Default: false
* cookie_name?: scalar|Param|null, // The name of the cookie to use when using stateless protection. // Default: "csrf-token"
* },
* form?: bool|array{ // Form configuration
* enabled?: bool|Param, // Default: true
* csrf_protection?: bool|array{
* enabled?: scalar|Param|null, // Default: null
* token_id?: scalar|Param|null, // Default: null
* field_name?: scalar|Param|null, // Default: "_token"
* field_attr?: array<string, scalar|Param|null>,
* },
* },
* http_cache?: bool|array{ // HTTP cache configuration
* enabled?: bool|Param, // Default: false
* debug?: bool|Param, // Default: "%kernel.debug%"
* trace_level?: "none"|"short"|"full"|Param,
* trace_header?: scalar|Param|null,
* default_ttl?: int|Param,
* private_headers?: list<scalar|Param|null>,
* skip_response_headers?: list<scalar|Param|null>,
* allow_reload?: bool|Param,
* allow_revalidate?: bool|Param,
* stale_while_revalidate?: int|Param,
* stale_if_error?: int|Param,
* terminate_on_cache_hit?: bool|Param,
* },
* esi?: bool|array{ // ESI configuration
* enabled?: bool|Param, // Default: false
* },
* ssi?: bool|array{ // SSI configuration
* enabled?: bool|Param, // Default: false
* },
* fragments?: bool|array{ // Fragments configuration
* enabled?: bool|Param, // Default: false
* hinclude_default_template?: scalar|Param|null, // Default: null
* path?: scalar|Param|null, // Default: "/_fragment"
* },
* profiler?: bool|array{ // Profiler configuration
* enabled?: bool|Param, // Default: false
* collect?: bool|Param, // Default: true
* collect_parameter?: scalar|Param|null, // The name of the parameter to use to enable or disable collection on a per request basis. // Default: null
* only_exceptions?: bool|Param, // Default: false
* only_main_requests?: bool|Param, // Default: false
* dsn?: scalar|Param|null, // Default: "file:%kernel.cache_dir%/profiler"
* collect_serializer_data?: bool|Param, // Enables the serializer data collector and profiler panel. // Default: false
* },
* workflows?: bool|array{
* enabled?: bool|Param, // Default: false
* workflows?: array<string, array{ // Default: []
* audit_trail?: bool|array{
* enabled?: bool|Param, // Default: false
* },
* type?: "workflow"|"state_machine"|Param, // Default: "state_machine"
* marking_store?: array{
* type?: "method"|Param,
* property?: scalar|Param|null,
* service?: scalar|Param|null,
* },
* supports?: list<scalar|Param|null>,
* definition_validators?: list<scalar|Param|null>,
* support_strategy?: scalar|Param|null,
* initial_marking?: list<scalar|Param|null>,
* events_to_dispatch?: list<string|Param>|null,
* places?: list<array{ // Default: []
* name?: scalar|Param|null,
* metadata?: array<string, mixed>,
* }>,
* transitions?: list<array{ // Default: []
* name?: string|Param,
* guard?: string|Param, // An expression to block the transition.
* from?: list<array{ // Default: []
* place?: string|Param,
* weight?: int|Param, // Default: 1
* }>,
* to?: list<array{ // Default: []
* place?: string|Param,
* weight?: int|Param, // Default: 1
* }>,
* weight?: int|Param, // Default: 1
* metadata?: array<string, mixed>,
* }>,
* metadata?: array<string, mixed>,
* }>,
* },
* router?: bool|array{ // Router configuration
* enabled?: bool|Param, // Default: false
* resource?: scalar|Param|null,
* type?: scalar|Param|null,
* cache_dir?: scalar|Param|null, // Deprecated: Setting the "framework.router.cache_dir.cache_dir" configuration option is deprecated. It will be removed in version 8.0. // Default: "%kernel.build_dir%"
* default_uri?: scalar|Param|null, // The default URI used to generate URLs in a non-HTTP context. // Default: null
* http_port?: scalar|Param|null, // Default: 80
* https_port?: scalar|Param|null, // Default: 443
* strict_requirements?: scalar|Param|null, // set to true to throw an exception when a parameter does not match the requirements set to false to disable exceptions when a parameter does not match the requirements (and return null instead) set to null to disable parameter checks against requirements 'true' is the preferred configuration in development mode, while 'false' or 'null' might be preferred in production // Default: true
* utf8?: bool|Param, // Default: true
* },
* session?: bool|array{ // Session configuration
* enabled?: bool|Param, // Default: false
* storage_factory_id?: scalar|Param|null, // Default: "session.storage.factory.native"
* handler_id?: scalar|Param|null, // Defaults to using the native session handler, or to the native *file* session handler if "save_path" is not null.
* name?: scalar|Param|null,
* cookie_lifetime?: scalar|Param|null,
* cookie_path?: scalar|Param|null,
* cookie_domain?: scalar|Param|null,
* cookie_secure?: true|false|"auto"|Param, // Default: "auto"
* cookie_httponly?: bool|Param, // Default: true
* cookie_samesite?: null|"lax"|"strict"|"none"|Param, // Default: "lax"
* use_cookies?: bool|Param,
* gc_divisor?: scalar|Param|null,
* gc_probability?: scalar|Param|null,
* gc_maxlifetime?: scalar|Param|null,
* save_path?: scalar|Param|null, // Defaults to "%kernel.cache_dir%/sessions" if the "handler_id" option is not null.
* metadata_update_threshold?: int|Param, // Seconds to wait between 2 session metadata updates. // Default: 0
* sid_length?: int|Param, // Deprecated: Setting the "framework.session.sid_length.sid_length" configuration option is deprecated. It will be removed in version 8.0. No alternative is provided as PHP 8.4 has deprecated the related option.
* sid_bits_per_character?: int|Param, // Deprecated: Setting the "framework.session.sid_bits_per_character.sid_bits_per_character" configuration option is deprecated. It will be removed in version 8.0. No alternative is provided as PHP 8.4 has deprecated the related option.
* },
* request?: bool|array{ // Request configuration
* enabled?: bool|Param, // Default: false
* formats?: array<string, string|list<scalar|Param|null>>,
* },
* assets?: bool|array{ // Assets configuration
* enabled?: bool|Param, // Default: true
* strict_mode?: bool|Param, // Throw an exception if an entry is missing from the manifest.json. // Default: false
* version_strategy?: scalar|Param|null, // Default: null
* version?: scalar|Param|null, // Default: null
* version_format?: scalar|Param|null, // Default: "%%s?%%s"
* json_manifest_path?: scalar|Param|null, // Default: null
* base_path?: scalar|Param|null, // Default: ""
* base_urls?: list<scalar|Param|null>,
* packages?: array<string, array{ // Default: []
* strict_mode?: bool|Param, // Throw an exception if an entry is missing from the manifest.json. // Default: false
* version_strategy?: scalar|Param|null, // Default: null
* version?: scalar|Param|null,
* version_format?: scalar|Param|null, // Default: null
* json_manifest_path?: scalar|Param|null, // Default: null
* base_path?: scalar|Param|null, // Default: ""
* base_urls?: list<scalar|Param|null>,
* }>,
* },
* asset_mapper?: bool|array{ // Asset Mapper configuration
* enabled?: bool|Param, // Default: false
* paths?: array<string, scalar|Param|null>,
* excluded_patterns?: list<scalar|Param|null>,
* exclude_dotfiles?: bool|Param, // If true, any files starting with "." will be excluded from the asset mapper. // Default: true
* server?: bool|Param, // If true, a "dev server" will return the assets from the public directory (true in "debug" mode only by default). // Default: true
* public_prefix?: scalar|Param|null, // The public path where the assets will be written to (and served from when "server" is true). // Default: "/assets/"
* missing_import_mode?: "strict"|"warn"|"ignore"|Param, // Behavior if an asset cannot be found when imported from JavaScript or CSS files - e.g. "import './non-existent.js'". "strict" means an exception is thrown, "warn" means a warning is logged, "ignore" means the import is left as-is. // Default: "warn"
* extensions?: array<string, scalar|Param|null>,
* importmap_path?: scalar|Param|null, // The path of the importmap.php file. // Default: "%kernel.project_dir%/importmap.php"
* importmap_polyfill?: scalar|Param|null, // The importmap name that will be used to load the polyfill. Set to false to disable. // Default: "es-module-shims"
* importmap_script_attributes?: array<string, scalar|Param|null>,
* vendor_dir?: scalar|Param|null, // The directory to store JavaScript vendors. // Default: "%kernel.project_dir%/assets/vendor"
* precompress?: bool|array{ // Precompress assets with Brotli, Zstandard and gzip.
* enabled?: bool|Param, // Default: false
* formats?: list<scalar|Param|null>,
* extensions?: list<scalar|Param|null>,
* },
* },
* translator?: bool|array{ // Translator configuration
* enabled?: bool|Param, // Default: true
* fallbacks?: list<scalar|Param|null>,
* logging?: bool|Param, // Default: false
* formatter?: scalar|Param|null, // Default: "translator.formatter.default"
* cache_dir?: scalar|Param|null, // Default: "%kernel.cache_dir%/translations"
* default_path?: scalar|Param|null, // The default path used to load translations. // Default: "%kernel.project_dir%/translations"
* paths?: list<scalar|Param|null>,
* pseudo_localization?: bool|array{
* enabled?: bool|Param, // Default: false
* accents?: bool|Param, // Default: true
* expansion_factor?: float|Param, // Default: 1.0
* brackets?: bool|Param, // Default: true
* parse_html?: bool|Param, // Default: false
* localizable_html_attributes?: list<scalar|Param|null>,
* },
* providers?: array<string, array{ // Default: []
* dsn?: scalar|Param|null,
* domains?: list<scalar|Param|null>,
* locales?: list<scalar|Param|null>,
* }>,
* globals?: array<string, string|array{ // Default: []
* value?: mixed,
* message?: string|Param,
* parameters?: array<string, scalar|Param|null>,
* domain?: string|Param,
* }>,
* },
* validation?: bool|array{ // Validation configuration
* enabled?: bool|Param, // Default: true
* cache?: scalar|Param|null, // Deprecated: Setting the "framework.validation.cache.cache" configuration option is deprecated. It will be removed in version 8.0.
* enable_attributes?: bool|Param, // Default: true
* static_method?: list<scalar|Param|null>,
* translation_domain?: scalar|Param|null, // Default: "validators"
* email_validation_mode?: "html5"|"html5-allow-no-tld"|"strict"|"loose"|Param, // Default: "html5"
* mapping?: array{
* paths?: list<scalar|Param|null>,
* },
* not_compromised_password?: bool|array{
* enabled?: bool|Param, // When disabled, compromised passwords will be accepted as valid. // Default: true
* endpoint?: scalar|Param|null, // API endpoint for the NotCompromisedPassword Validator. // Default: null
* },
* disable_translation?: bool|Param, // Default: false
* auto_mapping?: array<string, array{ // Default: []
* services?: list<scalar|Param|null>,
* }>,
* },
* annotations?: bool|array{
* enabled?: bool|Param, // Default: false
* },
* serializer?: bool|array{ // Serializer configuration
* enabled?: bool|Param, // Default: true
* enable_attributes?: bool|Param, // Default: true
* name_converter?: scalar|Param|null,
* circular_reference_handler?: scalar|Param|null,
* max_depth_handler?: scalar|Param|null,
* mapping?: array{
* paths?: list<scalar|Param|null>,
* },
* default_context?: array<string, mixed>,
* named_serializers?: array<string, array{ // Default: []
* name_converter?: scalar|Param|null,
* default_context?: array<string, mixed>,
* include_built_in_normalizers?: bool|Param, // Whether to include the built-in normalizers // Default: true
* include_built_in_encoders?: bool|Param, // Whether to include the built-in encoders // Default: true
* }>,
* },
* property_access?: bool|array{ // Property access configuration
* enabled?: bool|Param, // Default: true
* magic_call?: bool|Param, // Default: false
* magic_get?: bool|Param, // Default: true
* magic_set?: bool|Param, // Default: true
* throw_exception_on_invalid_index?: bool|Param, // Default: false
* throw_exception_on_invalid_property_path?: bool|Param, // Default: true
* },
* type_info?: bool|array{ // Type info configuration
* enabled?: bool|Param, // Default: true
* aliases?: array<string, scalar|Param|null>,
* },
* property_info?: bool|array{ // Property info configuration
* enabled?: bool|Param, // Default: true
* with_constructor_extractor?: bool|Param, // Registers the constructor extractor.
* },
* cache?: array{ // Cache configuration
* prefix_seed?: scalar|Param|null, // Used to namespace cache keys when using several apps with the same shared backend. // Default: "_%kernel.project_dir%.%kernel.container_class%"
* app?: scalar|Param|null, // App related cache pools configuration. // Default: "cache.adapter.filesystem"
* system?: scalar|Param|null, // System related cache pools configuration. // Default: "cache.adapter.system"
* directory?: scalar|Param|null, // Default: "%kernel.share_dir%/pools/app"
* default_psr6_provider?: scalar|Param|null,
* default_redis_provider?: scalar|Param|null, // Default: "redis://localhost"
* default_valkey_provider?: scalar|Param|null, // Default: "valkey://localhost"
* default_memcached_provider?: scalar|Param|null, // Default: "memcached://localhost"
* default_doctrine_dbal_provider?: scalar|Param|null, // Default: "database_connection"
* default_pdo_provider?: scalar|Param|null, // Default: null
* pools?: array<string, array{ // Default: []
* adapters?: list<scalar|Param|null>,
* tags?: scalar|Param|null, // Default: null
* public?: bool|Param, // Default: false
* default_lifetime?: scalar|Param|null, // Default lifetime of the pool.
* provider?: scalar|Param|null, // Overwrite the setting from the default provider for this adapter.
* early_expiration_message_bus?: scalar|Param|null,
* clearer?: scalar|Param|null,
* }>,
* },
* php_errors?: array{ // PHP errors handling configuration
* log?: mixed, // Use the application logger instead of the PHP logger for logging PHP errors. // Default: true
* throw?: bool|Param, // Throw PHP errors as \ErrorException instances. // Default: true
* },
* exceptions?: array<string, array{ // Default: []
* log_level?: scalar|Param|null, // The level of log message. Null to let Symfony decide. // Default: null
* status_code?: scalar|Param|null, // The status code of the response. Null or 0 to let Symfony decide. // Default: null
* log_channel?: scalar|Param|null, // The channel of log message. Null to let Symfony decide. // Default: null
* }>,
* web_link?: bool|array{ // Web links configuration
* enabled?: bool|Param, // Default: true
* },
* lock?: bool|string|array{ // Lock configuration
* enabled?: bool|Param, // Default: false
* resources?: array<string, string|list<scalar|Param|null>>,
* },
* semaphore?: bool|string|array{ // Semaphore configuration
* enabled?: bool|Param, // Default: false
* resources?: array<string, scalar|Param|null>,
* },
* messenger?: bool|array{ // Messenger configuration
* enabled?: bool|Param, // Default: false
* routing?: array<string, string|array{ // Default: []
* senders?: list<scalar|Param|null>,
* }>,
* serializer?: array{
* default_serializer?: scalar|Param|null, // Service id to use as the default serializer for the transports. // Default: "messenger.transport.native_php_serializer"
* symfony_serializer?: array{
* format?: scalar|Param|null, // Serialization format for the messenger.transport.symfony_serializer service (which is not the serializer used by default). // Default: "json"
* context?: array<string, mixed>,
* },
* },
* transports?: array<string, string|array{ // Default: []
* dsn?: scalar|Param|null,
* serializer?: scalar|Param|null, // Service id of a custom serializer to use. // Default: null
* options?: array<string, mixed>,
* failure_transport?: scalar|Param|null, // Transport name to send failed messages to (after all retries have failed). // Default: null
* retry_strategy?: string|array{
* service?: scalar|Param|null, // Service id to override the retry strategy entirely. // Default: null
* max_retries?: int|Param, // Default: 3
* delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000
* multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: this delay = (delay * (multiple ^ retries)). // Default: 2
* max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0
* jitter?: float|Param, // Randomness to apply to the delay (between 0 and 1). // Default: 0.1
* },
* rate_limiter?: scalar|Param|null, // Rate limiter name to use when processing messages. // Default: null
* }>,
* failure_transport?: scalar|Param|null, // Transport name to send failed messages to (after all retries have failed). // Default: null
* stop_worker_on_signals?: list<scalar|Param|null>,
* default_bus?: scalar|Param|null, // Default: null
* buses?: array<string, array{ // Default: {"messenger.bus.default":{"default_middleware":{"enabled":true,"allow_no_handlers":false,"allow_no_senders":true},"middleware":[]}}
* default_middleware?: bool|string|array{
* enabled?: bool|Param, // Default: true
* allow_no_handlers?: bool|Param, // Default: false
* allow_no_senders?: bool|Param, // Default: true
* },
* middleware?: list<string|array{ // Default: []
* id?: scalar|Param|null,
* arguments?: list<mixed>,
* }>,
* }>,
* },
* scheduler?: bool|array{ // Scheduler configuration
* enabled?: bool|Param, // Default: false
* },
* disallow_search_engine_index?: bool|Param, // Enabled by default when debug is enabled. // Default: true
* http_client?: bool|array{ // HTTP Client configuration
* enabled?: bool|Param, // Default: true
* max_host_connections?: int|Param, // The maximum number of connections to a single host.
* default_options?: array{
* headers?: array<string, mixed>,
* vars?: array<string, mixed>,
* max_redirects?: int|Param, // The maximum number of redirects to follow.
* http_version?: scalar|Param|null, // The default HTTP version, typically 1.1 or 2.0, leave to null for the best version.
* resolve?: array<string, scalar|Param|null>,
* proxy?: scalar|Param|null, // The URL of the proxy to pass requests through or null for automatic detection.
* no_proxy?: scalar|Param|null, // A comma separated list of hosts that do not require a proxy to be reached.
* timeout?: float|Param, // The idle timeout, defaults to the "default_socket_timeout" ini parameter.
* max_duration?: float|Param, // The maximum execution time for the request+response as a whole.
* bindto?: scalar|Param|null, // A network interface name, IP address, a host name or a UNIX socket to bind to.
* verify_peer?: bool|Param, // Indicates if the peer should be verified in a TLS context.
* verify_host?: bool|Param, // Indicates if the host should exist as a certificate common name.
* cafile?: scalar|Param|null, // A certificate authority file.
* capath?: scalar|Param|null, // A directory that contains multiple certificate authority files.
* local_cert?: scalar|Param|null, // A PEM formatted certificate file.
* local_pk?: scalar|Param|null, // A private key file.
* passphrase?: scalar|Param|null, // The passphrase used to encrypt the "local_pk" file.
* ciphers?: scalar|Param|null, // A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)
* peer_fingerprint?: array{ // Associative array: hashing algorithm => hash(es).
* sha1?: mixed,
* pin-sha256?: mixed,
* md5?: mixed,
* },
* crypto_method?: scalar|Param|null, // The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants.
* extra?: array<string, mixed>,
* rate_limiter?: scalar|Param|null, // Rate limiter name to use for throttling requests. // Default: null
* caching?: bool|array{ // Caching configuration.
* enabled?: bool|Param, // Default: false
* cache_pool?: string|Param, // The taggable cache pool to use for storing the responses. // Default: "cache.http_client"
* shared?: bool|Param, // Indicates whether the cache is shared (public) or private. // Default: true
* max_ttl?: int|Param, // The maximum TTL (in seconds) allowed for cached responses. Null means no cap. // Default: null
* },
* retry_failed?: bool|array{
* enabled?: bool|Param, // Default: false
* retry_strategy?: scalar|Param|null, // service id to override the retry strategy. // Default: null
* http_codes?: array<string, array{ // Default: []
* code?: int|Param,
* methods?: list<string|Param>,
* }>,
* max_retries?: int|Param, // Default: 3
* delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000
* multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries). // Default: 2
* max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0
* jitter?: float|Param, // Randomness in percent (between 0 and 1) to apply to the delay. // Default: 0.1
* },
* },
* mock_response_factory?: scalar|Param|null, // The id of the service that should generate mock responses. It should be either an invokable or an iterable.
* scoped_clients?: array<string, string|array{ // Default: []
* scope?: scalar|Param|null, // The regular expression that the request URL must match before adding the other options. When none is provided, the base URI is used instead.
* base_uri?: scalar|Param|null, // The URI to resolve relative URLs, following rules in RFC 3985, section 2.
* auth_basic?: scalar|Param|null, // An HTTP Basic authentication "username:password".
* auth_bearer?: scalar|Param|null, // A token enabling HTTP Bearer authorization.
* auth_ntlm?: scalar|Param|null, // A "username:password" pair to use Microsoft NTLM authentication (requires the cURL extension).
* query?: array<string, scalar|Param|null>,
* headers?: array<string, mixed>,
* max_redirects?: int|Param, // The maximum number of redirects to follow.
* http_version?: scalar|Param|null, // The default HTTP version, typically 1.1 or 2.0, leave to null for the best version.
* resolve?: array<string, scalar|Param|null>,
* proxy?: scalar|Param|null, // The URL of the proxy to pass requests through or null for automatic detection.
* no_proxy?: scalar|Param|null, // A comma separated list of hosts that do not require a proxy to be reached.
* timeout?: float|Param, // The idle timeout, defaults to the "default_socket_timeout" ini parameter.
* max_duration?: float|Param, // The maximum execution time for the request+response as a whole.
* bindto?: scalar|Param|null, // A network interface name, IP address, a host name or a UNIX socket to bind to.
* verify_peer?: bool|Param, // Indicates if the peer should be verified in a TLS context.
* verify_host?: bool|Param, // Indicates if the host should exist as a certificate common name.
* cafile?: scalar|Param|null, // A certificate authority file.
* capath?: scalar|Param|null, // A directory that contains multiple certificate authority files.
* local_cert?: scalar|Param|null, // A PEM formatted certificate file.
* local_pk?: scalar|Param|null, // A private key file.
* passphrase?: scalar|Param|null, // The passphrase used to encrypt the "local_pk" file.
* ciphers?: scalar|Param|null, // A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...).
* peer_fingerprint?: array{ // Associative array: hashing algorithm => hash(es).
* sha1?: mixed,
* pin-sha256?: mixed,
* md5?: mixed,
* },
* crypto_method?: scalar|Param|null, // The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants.
* extra?: array<string, mixed>,
* rate_limiter?: scalar|Param|null, // Rate limiter name to use for throttling requests. // Default: null
* caching?: bool|array{ // Caching configuration.
* enabled?: bool|Param, // Default: false
* cache_pool?: string|Param, // The taggable cache pool to use for storing the responses. // Default: "cache.http_client"
* shared?: bool|Param, // Indicates whether the cache is shared (public) or private. // Default: true
* max_ttl?: int|Param, // The maximum TTL (in seconds) allowed for cached responses. Null means no cap. // Default: null
* },
* retry_failed?: bool|array{
* enabled?: bool|Param, // Default: false
* retry_strategy?: scalar|Param|null, // service id to override the retry strategy. // Default: null
* http_codes?: array<string, array{ // Default: []
* code?: int|Param,
* methods?: list<string|Param>,
* }>,
* max_retries?: int|Param, // Default: 3
* delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000
* multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries). // Default: 2
* max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0
* jitter?: float|Param, // Randomness in percent (between 0 and 1) to apply to the delay. // Default: 0.1
* },
* }>,
* },
* mailer?: bool|array{ // Mailer configuration
* enabled?: bool|Param, // Default: true
* message_bus?: scalar|Param|null, // The message bus to use. Defaults to the default bus if the Messenger component is installed. // Default: null
* dsn?: scalar|Param|null, // Default: null
* transports?: array<string, scalar|Param|null>,
* envelope?: array{ // Mailer Envelope configuration
* sender?: scalar|Param|null,
* recipients?: list<scalar|Param|null>,
* allowed_recipients?: list<scalar|Param|null>,
* },
* headers?: array<string, string|array{ // Default: []
* value?: mixed,
* }>,
* dkim_signer?: bool|array{ // DKIM signer configuration
* enabled?: bool|Param, // Default: false
* key?: scalar|Param|null, // Key content, or path to key (in PEM format with the `file://` prefix) // Default: ""
* domain?: scalar|Param|null, // Default: ""
* select?: scalar|Param|null, // Default: ""
* passphrase?: scalar|Param|null, // The private key passphrase // Default: ""
* options?: array<string, mixed>,
* },
* smime_signer?: bool|array{ // S/MIME signer configuration
* enabled?: bool|Param, // Default: false
* key?: scalar|Param|null, // Path to key (in PEM format) // Default: ""
* certificate?: scalar|Param|null, // Path to certificate (in PEM format without the `file://` prefix) // Default: ""
* passphrase?: scalar|Param|null, // The private key passphrase // Default: null
* extra_certificates?: scalar|Param|null, // Default: null
* sign_options?: int|Param, // Default: null
* },
* smime_encrypter?: bool|array{ // S/MIME encrypter configuration
* enabled?: bool|Param, // Default: false
* repository?: scalar|Param|null, // S/MIME certificate repository service. This service shall implement the `Symfony\Component\Mailer\EventListener\SmimeCertificateRepositoryInterface`. // Default: ""
* cipher?: int|Param, // A set of algorithms used to encrypt the message // Default: null
* },
* },
* secrets?: bool|array{
* enabled?: bool|Param, // Default: true
* vault_directory?: scalar|Param|null, // Default: "%kernel.project_dir%/config/secrets/%kernel.runtime_environment%"
* local_dotenv_file?: scalar|Param|null, // Default: "%kernel.project_dir%/.env.%kernel.environment%.local"
* decryption_env_var?: scalar|Param|null, // Default: "base64:default::SYMFONY_DECRYPTION_SECRET"
* },
* notifier?: bool|array{ // Notifier configuration
* enabled?: bool|Param, // Default: false
* message_bus?: scalar|Param|null, // The message bus to use. Defaults to the default bus if the Messenger component is installed. // Default: null
* chatter_transports?: array<string, scalar|Param|null>,
* texter_transports?: array<string, scalar|Param|null>,
* notification_on_failed_messages?: bool|Param, // Default: false
* channel_policy?: array<string, string|list<scalar|Param|null>>,
* admin_recipients?: list<array{ // Default: []
* email?: scalar|Param|null,
* phone?: scalar|Param|null, // Default: ""
* }>,
* },
* rate_limiter?: bool|array{ // Rate limiter configuration
* enabled?: bool|Param, // Default: false
* limiters?: array<string, array{ // Default: []
* lock_factory?: scalar|Param|null, // The service ID of the lock factory used by this limiter (or null to disable locking). // Default: "auto"
* cache_pool?: scalar|Param|null, // The cache pool to use for storing the current limiter state. // Default: "cache.rate_limiter"
* storage_service?: scalar|Param|null, // The service ID of a custom storage implementation, this precedes any configured "cache_pool". // Default: null
* policy?: "fixed_window"|"token_bucket"|"sliding_window"|"compound"|"no_limit"|Param, // The algorithm to be used by this limiter.
* limiters?: list<scalar|Param|null>,
* limit?: int|Param, // The maximum allowed hits in a fixed interval or burst.
* interval?: scalar|Param|null, // Configures the fixed interval if "policy" is set to "fixed_window" or "sliding_window". The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent).
* rate?: array{ // Configures the fill rate if "policy" is set to "token_bucket".
* interval?: scalar|Param|null, // Configures the rate interval. The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent).
* amount?: int|Param, // Amount of tokens to add each interval. // Default: 1
* },
* }>,
* },
* uid?: bool|array{ // Uid configuration
* enabled?: bool|Param, // Default: false
* default_uuid_version?: 7|6|4|1|Param, // Default: 7
* name_based_uuid_version?: 5|3|Param, // Default: 5
* name_based_uuid_namespace?: scalar|Param|null,
* time_based_uuid_version?: 7|6|1|Param, // Default: 7
* time_based_uuid_node?: scalar|Param|null,
* },
* html_sanitizer?: bool|array{ // HtmlSanitizer configuration
* enabled?: bool|Param, // Default: false
* sanitizers?: array<string, array{ // Default: []
* allow_safe_elements?: bool|Param, // Allows "safe" elements and attributes. // Default: false
* allow_static_elements?: bool|Param, // Allows all static elements and attributes from the W3C Sanitizer API standard. // Default: false
* allow_elements?: array<string, mixed>,
* block_elements?: list<string|Param>,
* drop_elements?: list<string|Param>,
* allow_attributes?: array<string, mixed>,
* drop_attributes?: array<string, mixed>,
* force_attributes?: array<string, array<string, string|Param>>,
* force_https_urls?: bool|Param, // Transforms URLs using the HTTP scheme to use the HTTPS scheme instead. // Default: false
* allowed_link_schemes?: list<string|Param>,
* allowed_link_hosts?: list<string|Param>|null,
* allow_relative_links?: bool|Param, // Allows relative URLs to be used in links href attributes. // Default: false
* allowed_media_schemes?: list<string|Param>,
* allowed_media_hosts?: list<string|Param>|null,
* allow_relative_medias?: bool|Param, // Allows relative URLs to be used in media source attributes (img, audio, video, ...). // Default: false
* with_attribute_sanitizers?: list<string|Param>,
* without_attribute_sanitizers?: list<string|Param>,
* max_input_length?: int|Param, // The maximum length allowed for the sanitized input. // Default: 0
* }>,
* },
* webhook?: bool|array{ // Webhook configuration
* enabled?: bool|Param, // Default: false
* message_bus?: scalar|Param|null, // The message bus to use. // Default: "messenger.default_bus"
* routing?: array<string, array{ // Default: []
* service?: scalar|Param|null,
* secret?: scalar|Param|null, // Default: ""
* }>,
* },
* remote-event?: bool|array{ // RemoteEvent configuration
* enabled?: bool|Param, // Default: false
* },
* json_streamer?: bool|array{ // JSON streamer configuration
* enabled?: bool|Param, // Default: false
* },
* }
* @psalm-type DoctrineConfig = array{
* dbal?: array{
* default_connection?: scalar|Param|null,
* types?: array<string, string|array{ // Default: []
* class?: scalar|Param|null,
* commented?: bool|Param, // Deprecated: The doctrine-bundle type commenting features were removed; the corresponding config parameter was deprecated in 2.0 and will be dropped in 3.0.
* }>,
* driver_schemes?: array<string, scalar|Param|null>,
* connections?: array<string, array{ // Default: []
* url?: scalar|Param|null, // A URL with connection information; any parameter value parsed from this string will override explicitly set parameters
* dbname?: scalar|Param|null,
* host?: scalar|Param|null, // Defaults to "localhost" at runtime.
* port?: scalar|Param|null, // Defaults to null at runtime.
* user?: scalar|Param|null, // Defaults to "root" at runtime.
* password?: scalar|Param|null, // Defaults to null at runtime.
* override_url?: bool|Param, // Deprecated: The "doctrine.dbal.override_url" configuration key is deprecated.
* dbname_suffix?: scalar|Param|null, // Adds the given suffix to the configured database name, this option has no effects for the SQLite platform
* application_name?: scalar|Param|null,
* charset?: scalar|Param|null,
* path?: scalar|Param|null,
* memory?: bool|Param,
* unix_socket?: scalar|Param|null, // The unix socket to use for MySQL
* persistent?: bool|Param, // True to use as persistent connection for the ibm_db2 driver
* protocol?: scalar|Param|null, // The protocol to use for the ibm_db2 driver (default to TCPIP if omitted)
* service?: bool|Param, // True to use SERVICE_NAME as connection parameter instead of SID for Oracle
* servicename?: scalar|Param|null, // Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter for Oracle depending on the service parameter.
* sessionMode?: scalar|Param|null, // The session mode to use for the oci8 driver
* server?: scalar|Param|null, // The name of a running database server to connect to for SQL Anywhere.
* default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connexion.
* sslmode?: scalar|Param|null, // Determines whether or with what priority a SSL TCP/IP connection will be negotiated with the server for PostgreSQL.
* sslrootcert?: scalar|Param|null, // The name of a file containing SSL certificate authority (CA) certificate(s). If the file exists, the server's certificate will be verified to be signed by one of these authorities.
* sslcert?: scalar|Param|null, // The path to the SSL client certificate file for PostgreSQL.
* sslkey?: scalar|Param|null, // The path to the SSL client key file for PostgreSQL.
* sslcrl?: scalar|Param|null, // The file name of the SSL certificate revocation list for PostgreSQL.
* pooled?: bool|Param, // True to use a pooled server with the oci8/pdo_oracle driver
* MultipleActiveResultSets?: bool|Param, // Configuring MultipleActiveResultSets for the pdo_sqlsrv driver
* use_savepoints?: bool|Param, // Use savepoints for nested transactions
* instancename?: scalar|Param|null, // Optional parameter, complete whether to add the INSTANCE_NAME parameter in the connection. It is generally used to connect to an Oracle RAC server to select the name of a particular instance.
* connectstring?: scalar|Param|null, // Complete Easy Connect connection descriptor, see https://docs.oracle.com/database/121/NETAG/naming.htm.When using this option, you will still need to provide the user and password parameters, but the other parameters will no longer be used. Note that when using this parameter, the getHost and getPort methods from Doctrine\DBAL\Connection will no longer function as expected.
* driver?: scalar|Param|null, // Default: "pdo_mysql"
* platform_service?: scalar|Param|null, // Deprecated: The "platform_service" configuration key is deprecated since doctrine-bundle 2.9. DBAL 4 will not support setting a custom platform via connection params anymore.
* auto_commit?: bool|Param,
* schema_filter?: scalar|Param|null,
* logging?: bool|Param, // Default: true
* profiling?: bool|Param, // Default: true
* profiling_collect_backtrace?: bool|Param, // Enables collecting backtraces when profiling is enabled // Default: false
* profiling_collect_schema_errors?: bool|Param, // Enables collecting schema errors when profiling is enabled // Default: true
* disable_type_comments?: bool|Param,
* server_version?: scalar|Param|null,
* idle_connection_ttl?: int|Param, // Default: 600
* driver_class?: scalar|Param|null,
* wrapper_class?: scalar|Param|null,
* keep_slave?: bool|Param, // Deprecated: The "keep_slave" configuration key is deprecated since doctrine-bundle 2.2. Use the "keep_replica" configuration key instead.
* keep_replica?: bool|Param,
* options?: array<string, mixed>,
* mapping_types?: array<string, scalar|Param|null>,
* default_table_options?: array<string, scalar|Param|null>,
* schema_manager_factory?: scalar|Param|null, // Default: "doctrine.dbal.legacy_schema_manager_factory"
* result_cache?: scalar|Param|null,
* slaves?: array<string, array{ // Default: []
* url?: scalar|Param|null, // A URL with connection information; any parameter value parsed from this string will override explicitly set parameters
* dbname?: scalar|Param|null,
* host?: scalar|Param|null, // Defaults to "localhost" at runtime.
* port?: scalar|Param|null, // Defaults to null at runtime.
* user?: scalar|Param|null, // Defaults to "root" at runtime.
* password?: scalar|Param|null, // Defaults to null at runtime.
* override_url?: bool|Param, // Deprecated: The "doctrine.dbal.override_url" configuration key is deprecated.
* dbname_suffix?: scalar|Param|null, // Adds the given suffix to the configured database name, this option has no effects for the SQLite platform
* application_name?: scalar|Param|null,
* charset?: scalar|Param|null,
* path?: scalar|Param|null,
* memory?: bool|Param,
* unix_socket?: scalar|Param|null, // The unix socket to use for MySQL
* persistent?: bool|Param, // True to use as persistent connection for the ibm_db2 driver
* protocol?: scalar|Param|null, // The protocol to use for the ibm_db2 driver (default to TCPIP if omitted)
* service?: bool|Param, // True to use SERVICE_NAME as connection parameter instead of SID for Oracle
* servicename?: scalar|Param|null, // Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter for Oracle depending on the service parameter.
* sessionMode?: scalar|Param|null, // The session mode to use for the oci8 driver
* server?: scalar|Param|null, // The name of a running database server to connect to for SQL Anywhere.
* default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connexion.
* sslmode?: scalar|Param|null, // Determines whether or with what priority a SSL TCP/IP connection will be negotiated with the server for PostgreSQL.
* sslrootcert?: scalar|Param|null, // The name of a file containing SSL certificate authority (CA) certificate(s). If the file exists, the server's certificate will be verified to be signed by one of these authorities.
* sslcert?: scalar|Param|null, // The path to the SSL client certificate file for PostgreSQL.
* sslkey?: scalar|Param|null, // The path to the SSL client key file for PostgreSQL.
* sslcrl?: scalar|Param|null, // The file name of the SSL certificate revocation list for PostgreSQL.
* pooled?: bool|Param, // True to use a pooled server with the oci8/pdo_oracle driver
* MultipleActiveResultSets?: bool|Param, // Configuring MultipleActiveResultSets for the pdo_sqlsrv driver
* use_savepoints?: bool|Param, // Use savepoints for nested transactions
* instancename?: scalar|Param|null, // Optional parameter, complete whether to add the INSTANCE_NAME parameter in the connection. It is generally used to connect to an Oracle RAC server to select the name of a particular instance.
* connectstring?: scalar|Param|null, // Complete Easy Connect connection descriptor, see https://docs.oracle.com/database/121/NETAG/naming.htm.When using this option, you will still need to provide the user and password parameters, but the other parameters will no longer be used. Note that when using this parameter, the getHost and getPort methods from Doctrine\DBAL\Connection will no longer function as expected.
* }>,
* replicas?: array<string, array{ // Default: []
* url?: scalar|Param|null, // A URL with connection information; any parameter value parsed from this string will override explicitly set parameters
* dbname?: scalar|Param|null,
* host?: scalar|Param|null, // Defaults to "localhost" at runtime.
* port?: scalar|Param|null, // Defaults to null at runtime.
* user?: scalar|Param|null, // Defaults to "root" at runtime.
* password?: scalar|Param|null, // Defaults to null at runtime.
* override_url?: bool|Param, // Deprecated: The "doctrine.dbal.override_url" configuration key is deprecated.
* dbname_suffix?: scalar|Param|null, // Adds the given suffix to the configured database name, this option has no effects for the SQLite platform
* application_name?: scalar|Param|null,
* charset?: scalar|Param|null,
* path?: scalar|Param|null,
* memory?: bool|Param,
* unix_socket?: scalar|Param|null, // The unix socket to use for MySQL
* persistent?: bool|Param, // True to use as persistent connection for the ibm_db2 driver
* protocol?: scalar|Param|null, // The protocol to use for the ibm_db2 driver (default to TCPIP if omitted)
* service?: bool|Param, // True to use SERVICE_NAME as connection parameter instead of SID for Oracle
* servicename?: scalar|Param|null, // Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter for Oracle depending on the service parameter.
* sessionMode?: scalar|Param|null, // The session mode to use for the oci8 driver
* server?: scalar|Param|null, // The name of a running database server to connect to for SQL Anywhere.
* default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connexion.
* sslmode?: scalar|Param|null, // Determines whether or with what priority a SSL TCP/IP connection will be negotiated with the server for PostgreSQL.
* sslrootcert?: scalar|Param|null, // The name of a file containing SSL certificate authority (CA) certificate(s). If the file exists, the server's certificate will be verified to be signed by one of these authorities.
* sslcert?: scalar|Param|null, // The path to the SSL client certificate file for PostgreSQL.
* sslkey?: scalar|Param|null, // The path to the SSL client key file for PostgreSQL.
* sslcrl?: scalar|Param|null, // The file name of the SSL certificate revocation list for PostgreSQL.
* pooled?: bool|Param, // True to use a pooled server with the oci8/pdo_oracle driver
* MultipleActiveResultSets?: bool|Param, // Configuring MultipleActiveResultSets for the pdo_sqlsrv driver
* use_savepoints?: bool|Param, // Use savepoints for nested transactions
* instancename?: scalar|Param|null, // Optional parameter, complete whether to add the INSTANCE_NAME parameter in the connection. It is generally used to connect to an Oracle RAC server to select the name of a particular instance.
* connectstring?: scalar|Param|null, // Complete Easy Connect connection descriptor, see https://docs.oracle.com/database/121/NETAG/naming.htm.When using this option, you will still need to provide the user and password parameters, but the other parameters will no longer be used. Note that when using this parameter, the getHost and getPort methods from Doctrine\DBAL\Connection will no longer function as expected.
* }>,
* }>,
* },
* orm?: array{
* default_entity_manager?: scalar|Param|null,
* auto_generate_proxy_classes?: scalar|Param|null, // Auto generate mode possible values are: "NEVER", "ALWAYS", "FILE_NOT_EXISTS", "EVAL", "FILE_NOT_EXISTS_OR_CHANGED", this option is ignored when the "enable_native_lazy_objects" option is true // Default: false
* enable_lazy_ghost_objects?: bool|Param, // Enables the new implementation of proxies based on lazy ghosts instead of using the legacy implementation // Default: false
* enable_native_lazy_objects?: bool|Param, // Enables the new native implementation of PHP lazy objects instead of generated proxies // Default: false
* proxy_dir?: scalar|Param|null, // Configures the path where generated proxy classes are saved when using non-native lazy objects, this option is ignored when the "enable_native_lazy_objects" option is true // Default: "%kernel.build_dir%/doctrine/orm/Proxies"
* proxy_namespace?: scalar|Param|null, // Defines the root namespace for generated proxy classes when using non-native lazy objects, this option is ignored when the "enable_native_lazy_objects" option is true // Default: "Proxies"
* controller_resolver?: bool|array{
* enabled?: bool|Param, // Default: true
* auto_mapping?: bool|Param|null, // Set to false to disable using route placeholders as lookup criteria when the primary key doesn't match the argument name // Default: null
* evict_cache?: bool|Param, // Set to true to fetch the entity from the database instead of using the cache, if any // Default: false
* },
* entity_managers?: array<string, array{ // Default: []
* query_cache_driver?: string|array{
* type?: scalar|Param|null, // Default: null
* id?: scalar|Param|null,
* pool?: scalar|Param|null,
* },
* metadata_cache_driver?: string|array{
* type?: scalar|Param|null, // Default: null
* id?: scalar|Param|null,
* pool?: scalar|Param|null,
* },
* result_cache_driver?: string|array{
* type?: scalar|Param|null, // Default: null
* id?: scalar|Param|null,
* pool?: scalar|Param|null,
* },
* entity_listeners?: array{
* entities?: array<string, array{ // Default: []
* listeners?: array<string, array{ // Default: []
* events?: list<array{ // Default: []
* type?: scalar|Param|null,
* method?: scalar|Param|null, // Default: null
* }>,
* }>,
* }>,
* },
* connection?: scalar|Param|null,
* class_metadata_factory_name?: scalar|Param|null, // Default: "Doctrine\\ORM\\Mapping\\ClassMetadataFactory"
* default_repository_class?: scalar|Param|null, // Default: "Doctrine\\ORM\\EntityRepository"
* auto_mapping?: scalar|Param|null, // Default: false
* naming_strategy?: scalar|Param|null, // Default: "doctrine.orm.naming_strategy.default"
* quote_strategy?: scalar|Param|null, // Default: "doctrine.orm.quote_strategy.default"
* typed_field_mapper?: scalar|Param|null, // Default: "doctrine.orm.typed_field_mapper.default"
* entity_listener_resolver?: scalar|Param|null, // Default: null
* fetch_mode_subselect_batch_size?: scalar|Param|null,
* repository_factory?: scalar|Param|null, // Default: "doctrine.orm.container_repository_factory"
* schema_ignore_classes?: list<scalar|Param|null>,
* report_fields_where_declared?: bool|Param, // Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.16 and will be mandatory in ORM 3.0. See https://github.com/doctrine/orm/pull/10455. // Default: false
* validate_xml_mapping?: bool|Param, // Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.14. See https://github.com/doctrine/orm/pull/6728. // Default: false
* second_level_cache?: array{
* region_cache_driver?: string|array{
* type?: scalar|Param|null, // Default: null
* id?: scalar|Param|null,
* pool?: scalar|Param|null,
* },
* region_lock_lifetime?: scalar|Param|null, // Default: 60
* log_enabled?: bool|Param, // Default: true
* region_lifetime?: scalar|Param|null, // Default: 3600
* enabled?: bool|Param, // Default: true
* factory?: scalar|Param|null,
* regions?: array<string, array{ // Default: []
* cache_driver?: string|array{
* type?: scalar|Param|null, // Default: null
* id?: scalar|Param|null,
* pool?: scalar|Param|null,
* },
* lock_path?: scalar|Param|null, // Default: "%kernel.cache_dir%/doctrine/orm/slc/filelock"
* lock_lifetime?: scalar|Param|null, // Default: 60
* type?: scalar|Param|null, // Default: "default"
* lifetime?: scalar|Param|null, // Default: 0
* service?: scalar|Param|null,
* name?: scalar|Param|null,
* }>,
* loggers?: array<string, array{ // Default: []
* name?: scalar|Param|null,
* service?: scalar|Param|null,
* }>,
* },
* hydrators?: array<string, scalar|Param|null>,
* mappings?: array<string, bool|string|array{ // Default: []
* mapping?: scalar|Param|null, // Default: true
* type?: scalar|Param|null,
* dir?: scalar|Param|null,
* alias?: scalar|Param|null,
* prefix?: scalar|Param|null,
* is_bundle?: bool|Param,
* }>,
* dql?: array{
* string_functions?: array<string, scalar|Param|null>,
* numeric_functions?: array<string, scalar|Param|null>,
* datetime_functions?: array<string, scalar|Param|null>,
* },
* filters?: array<string, string|array{ // Default: []
* class?: scalar|Param|null,
* enabled?: bool|Param, // Default: false
* parameters?: array<string, mixed>,
* }>,
* identity_generation_preferences?: array<string, scalar|Param|null>,
* }>,
* resolve_target_entities?: array<string, scalar|Param|null>,
* },
* }
* @psalm-type DoctrineMigrationsConfig = array{
* enable_service_migrations?: bool|Param, // Whether to enable fetching migrations from the service container. // Default: false
* migrations_paths?: array<string, scalar|Param|null>,
* services?: array<string, scalar|Param|null>,
* factories?: array<string, scalar|Param|null>,
* storage?: array{ // Storage to use for migration status metadata.
* table_storage?: array{ // The default metadata storage, implemented as a table in the database.
* table_name?: scalar|Param|null, // Default: null
* version_column_name?: scalar|Param|null, // Default: null
* version_column_length?: scalar|Param|null, // Default: null
* executed_at_column_name?: scalar|Param|null, // Default: null
* execution_time_column_name?: scalar|Param|null, // Default: null
* },
* },
* migrations?: list<scalar|Param|null>,
* connection?: scalar|Param|null, // Connection name to use for the migrations database. // Default: null
* em?: scalar|Param|null, // Entity manager name to use for the migrations database (available when doctrine/orm is installed). // Default: null
* all_or_nothing?: scalar|Param|null, // Run all migrations in a transaction. // Default: false
* check_database_platform?: scalar|Param|null, // Adds an extra check in the generated migrations to allow execution only on the same platform as they were initially generated on. // Default: true
* custom_template?: scalar|Param|null, // Custom template path for generated migration classes. // Default: null
* organize_migrations?: scalar|Param|null, // Organize migrations mode. Possible values are: "BY_YEAR", "BY_YEAR_AND_MONTH", false // Default: false
* enable_profiler?: bool|Param, // Whether or not to enable the profiler collector to calculate and visualize migration status. This adds some queries overhead. // Default: false
* transactional?: bool|Param, // Whether or not to wrap migrations in a single transaction. // Default: true
* }
* @psalm-type SecurityConfig = array{
* access_denied_url?: scalar|Param|null, // Default: null
* session_fixation_strategy?: "none"|"migrate"|"invalidate"|Param, // Default: "migrate"
* hide_user_not_found?: bool|Param, // Deprecated: The "hide_user_not_found" option is deprecated and will be removed in 8.0. Use the "expose_security_errors" option instead.
* expose_security_errors?: \Symfony\Component\Security\Http\Authentication\ExposeSecurityLevel::None|\Symfony\Component\Security\Http\Authentication\ExposeSecurityLevel::AccountStatus|\Symfony\Component\Security\Http\Authentication\ExposeSecurityLevel::All|Param, // Default: "none"
* erase_credentials?: bool|Param, // Default: true
* access_decision_manager?: array{
* strategy?: "affirmative"|"consensus"|"unanimous"|"priority"|Param,
* service?: scalar|Param|null,
* strategy_service?: scalar|Param|null,
* allow_if_all_abstain?: bool|Param, // Default: false
* allow_if_equal_granted_denied?: bool|Param, // Default: true
* },
* password_hashers?: array<string, string|array{ // Default: []
* algorithm?: scalar|Param|null,
* migrate_from?: list<scalar|Param|null>,
* hash_algorithm?: scalar|Param|null, // Name of hashing algorithm for PBKDF2 (i.e. sha256, sha512, etc..) See hash_algos() for a list of supported algorithms. // Default: "sha512"
* key_length?: scalar|Param|null, // Default: 40
* ignore_case?: bool|Param, // Default: false
* encode_as_base64?: bool|Param, // Default: true
* iterations?: scalar|Param|null, // Default: 5000
* cost?: int|Param, // Default: null
* memory_cost?: scalar|Param|null, // Default: null
* time_cost?: scalar|Param|null, // Default: null
* id?: scalar|Param|null,
* }>,
* providers?: array<string, array{ // Default: []
* id?: scalar|Param|null,
* chain?: array{
* providers?: list<scalar|Param|null>,
* },
* entity?: array{
* class?: scalar|Param|null, // The full entity class name of your user class.
* property?: scalar|Param|null, // Default: null
* manager_name?: scalar|Param|null, // Default: null
* },
* memory?: array{
* users?: array<string, array{ // Default: []
* password?: scalar|Param|null, // Default: null
* roles?: list<scalar|Param|null>,
* }>,
* },
* ldap?: array{
* service?: scalar|Param|null,
* base_dn?: scalar|Param|null,
* search_dn?: scalar|Param|null, // Default: null
* search_password?: scalar|Param|null, // Default: null
* extra_fields?: list<scalar|Param|null>,
* default_roles?: list<scalar|Param|null>,
* role_fetcher?: scalar|Param|null, // Default: null
* uid_key?: scalar|Param|null, // Default: "sAMAccountName"
* filter?: scalar|Param|null, // Default: "({uid_key}={user_identifier})"
* password_attribute?: scalar|Param|null, // Default: null
* },
* }>,
* firewalls?: array<string, array{ // Default: []
* pattern?: scalar|Param|null,
* host?: scalar|Param|null,
* methods?: list<scalar|Param|null>,
* security?: bool|Param, // Default: true
* user_checker?: scalar|Param|null, // The UserChecker to use when authenticating users in this firewall. // Default: "security.user_checker"
* request_matcher?: scalar|Param|null,
* access_denied_url?: scalar|Param|null,
* access_denied_handler?: scalar|Param|null,
* entry_point?: scalar|Param|null, // An enabled authenticator name or a service id that implements "Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface".
* provider?: scalar|Param|null,
* stateless?: bool|Param, // Default: false
* lazy?: bool|Param, // Default: false
* context?: scalar|Param|null,
* logout?: array{
* enable_csrf?: bool|Param|null, // Default: null
* csrf_token_id?: scalar|Param|null, // Default: "logout"
* csrf_parameter?: scalar|Param|null, // Default: "_csrf_token"
* csrf_token_manager?: scalar|Param|null,
* path?: scalar|Param|null, // Default: "/logout"
* target?: scalar|Param|null, // Default: "/"
* invalidate_session?: bool|Param, // Default: true
* clear_site_data?: list<"*"|"cache"|"cookies"|"storage"|"executionContexts"|Param>,
* delete_cookies?: array<string, array{ // Default: []
* path?: scalar|Param|null, // Default: null
* domain?: scalar|Param|null, // Default: null
* secure?: scalar|Param|null, // Default: false
* samesite?: scalar|Param|null, // Default: null
* partitioned?: scalar|Param|null, // Default: false
* }>,
* },
* switch_user?: array{
* provider?: scalar|Param|null,
* parameter?: scalar|Param|null, // Default: "_switch_user"
* role?: scalar|Param|null, // Default: "ROLE_ALLOWED_TO_SWITCH"
* target_route?: scalar|Param|null, // Default: null
* },
* required_badges?: list<scalar|Param|null>,
* custom_authenticators?: list<scalar|Param|null>,
* login_throttling?: array{
* limiter?: scalar|Param|null, // A service id implementing "Symfony\Component\HttpFoundation\RateLimiter\RequestRateLimiterInterface".
* max_attempts?: int|Param, // Default: 5
* interval?: scalar|Param|null, // Default: "1 minute"
* lock_factory?: scalar|Param|null, // The service ID of the lock factory used by the login rate limiter (or null to disable locking). // Default: null
* cache_pool?: string|Param, // The cache pool to use for storing the limiter state // Default: "cache.rate_limiter"
* storage_service?: string|Param, // The service ID of a custom storage implementation, this precedes any configured "cache_pool" // Default: null
* },
* x509?: array{
* provider?: scalar|Param|null,
* user?: scalar|Param|null, // Default: "SSL_CLIENT_S_DN_Email"
* credentials?: scalar|Param|null, // Default: "SSL_CLIENT_S_DN"
* user_identifier?: scalar|Param|null, // Default: "emailAddress"
* },
* remote_user?: array{
* provider?: scalar|Param|null,
* user?: scalar|Param|null, // Default: "REMOTE_USER"
* },
* login_link?: array{
* check_route?: scalar|Param|null, // Route that will validate the login link - e.g. "app_login_link_verify".
* check_post_only?: scalar|Param|null, // If true, only HTTP POST requests to "check_route" will be handled by the authenticator. // Default: false
* signature_properties?: list<scalar|Param|null>,
* lifetime?: int|Param, // The lifetime of the login link in seconds. // Default: 600
* max_uses?: int|Param, // Max number of times a login link can be used - null means unlimited within lifetime. // Default: null
* used_link_cache?: scalar|Param|null, // Cache service id used to expired links of max_uses is set.
* success_handler?: scalar|Param|null, // A service id that implements Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface.
* failure_handler?: scalar|Param|null, // A service id that implements Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface.
* provider?: scalar|Param|null, // The user provider to load users from.
* secret?: scalar|Param|null, // Default: "%kernel.secret%"
* always_use_default_target_path?: bool|Param, // Default: false
* default_target_path?: scalar|Param|null, // Default: "/"
* login_path?: scalar|Param|null, // Default: "/login"
* target_path_parameter?: scalar|Param|null, // Default: "_target_path"
* use_referer?: bool|Param, // Default: false
* failure_path?: scalar|Param|null, // Default: null
* failure_forward?: bool|Param, // Default: false
* failure_path_parameter?: scalar|Param|null, // Default: "_failure_path"
* },
* form_login?: array{
* provider?: scalar|Param|null,
* remember_me?: bool|Param, // Default: true
* success_handler?: scalar|Param|null,
* failure_handler?: scalar|Param|null,
* check_path?: scalar|Param|null, // Default: "/login_check"
* use_forward?: bool|Param, // Default: false
* login_path?: scalar|Param|null, // Default: "/login"
* username_parameter?: scalar|Param|null, // Default: "_username"
* password_parameter?: scalar|Param|null, // Default: "_password"
* csrf_parameter?: scalar|Param|null, // Default: "_csrf_token"
* csrf_token_id?: scalar|Param|null, // Default: "authenticate"
* enable_csrf?: bool|Param, // Default: false
* post_only?: bool|Param, // Default: true
* form_only?: bool|Param, // Default: false
* always_use_default_target_path?: bool|Param, // Default: false
* default_target_path?: scalar|Param|null, // Default: "/"
* target_path_parameter?: scalar|Param|null, // Default: "_target_path"
* use_referer?: bool|Param, // Default: false
* failure_path?: scalar|Param|null, // Default: null
* failure_forward?: bool|Param, // Default: false
* failure_path_parameter?: scalar|Param|null, // Default: "_failure_path"
* },
* form_login_ldap?: array{
* provider?: scalar|Param|null,
* remember_me?: bool|Param, // Default: true
* success_handler?: scalar|Param|null,
* failure_handler?: scalar|Param|null,
* check_path?: scalar|Param|null, // Default: "/login_check"
* use_forward?: bool|Param, // Default: false
* login_path?: scalar|Param|null, // Default: "/login"
* username_parameter?: scalar|Param|null, // Default: "_username"
* password_parameter?: scalar|Param|null, // Default: "_password"
* csrf_parameter?: scalar|Param|null, // Default: "_csrf_token"
* csrf_token_id?: scalar|Param|null, // Default: "authenticate"
* enable_csrf?: bool|Param, // Default: false
* post_only?: bool|Param, // Default: true
* form_only?: bool|Param, // Default: false
* always_use_default_target_path?: bool|Param, // Default: false
* default_target_path?: scalar|Param|null, // Default: "/"
* target_path_parameter?: scalar|Param|null, // Default: "_target_path"
* use_referer?: bool|Param, // Default: false
* failure_path?: scalar|Param|null, // Default: null
* failure_forward?: bool|Param, // Default: false
* failure_path_parameter?: scalar|Param|null, // Default: "_failure_path"
* service?: scalar|Param|null, // Default: "ldap"
* dn_string?: scalar|Param|null, // Default: "{user_identifier}"
* query_string?: scalar|Param|null,
* search_dn?: scalar|Param|null, // Default: ""
* search_password?: scalar|Param|null, // Default: ""
* },
* json_login?: array{
* provider?: scalar|Param|null,
* remember_me?: bool|Param, // Default: true
* success_handler?: scalar|Param|null,
* failure_handler?: scalar|Param|null,
* check_path?: scalar|Param|null, // Default: "/login_check"
* use_forward?: bool|Param, // Default: false
* login_path?: scalar|Param|null, // Default: "/login"
* username_path?: scalar|Param|null, // Default: "username"
* password_path?: scalar|Param|null, // Default: "password"
* },
* json_login_ldap?: array{
* provider?: scalar|Param|null,
* remember_me?: bool|Param, // Default: true
* success_handler?: scalar|Param|null,
* failure_handler?: scalar|Param|null,
* check_path?: scalar|Param|null, // Default: "/login_check"
* use_forward?: bool|Param, // Default: false
* login_path?: scalar|Param|null, // Default: "/login"
* username_path?: scalar|Param|null, // Default: "username"
* password_path?: scalar|Param|null, // Default: "password"
* service?: scalar|Param|null, // Default: "ldap"
* dn_string?: scalar|Param|null, // Default: "{user_identifier}"
* query_string?: scalar|Param|null,
* search_dn?: scalar|Param|null, // Default: ""
* search_password?: scalar|Param|null, // Default: ""
* },
* access_token?: array{
* provider?: scalar|Param|null,
* remember_me?: bool|Param, // Default: true
* success_handler?: scalar|Param|null,
* failure_handler?: scalar|Param|null,
* realm?: scalar|Param|null, // Default: null
* token_extractors?: list<scalar|Param|null>,
* token_handler?: string|array{
* id?: scalar|Param|null,
* oidc_user_info?: string|array{
* base_uri?: scalar|Param|null, // Base URI of the userinfo endpoint on the OIDC server, or the OIDC server URI to use the discovery (require "discovery" to be configured).
* discovery?: array{ // Enable the OIDC discovery.
* cache?: array{
* id?: scalar|Param|null, // Cache service id to use to cache the OIDC discovery configuration.
* },
* },
* claim?: scalar|Param|null, // Claim which contains the user identifier (e.g. sub, email, etc.). // Default: "sub"
* client?: scalar|Param|null, // HttpClient service id to use to call the OIDC server.
* },
* oidc?: array{
* discovery?: array{ // Enable the OIDC discovery.
* base_uri?: list<scalar|Param|null>,
* cache?: array{
* id?: scalar|Param|null, // Cache service id to use to cache the OIDC discovery configuration.
* },
* },
* claim?: scalar|Param|null, // Claim which contains the user identifier (e.g.: sub, email..). // Default: "sub"
* audience?: scalar|Param|null, // Audience set in the token, for validation purpose.
* issuers?: list<scalar|Param|null>,
* algorithm?: array<mixed>,
* algorithms?: list<scalar|Param|null>,
* key?: scalar|Param|null, // Deprecated: The "key" option is deprecated and will be removed in 8.0. Use the "keyset" option instead. // JSON-encoded JWK used to sign the token (must contain a "kty" key).
* keyset?: scalar|Param|null, // JSON-encoded JWKSet used to sign the token (must contain a list of valid public keys).
* encryption?: bool|array{
* enabled?: bool|Param, // Default: false
* enforce?: bool|Param, // When enabled, the token shall be encrypted. // Default: false
* algorithms?: list<scalar|Param|null>,
* keyset?: scalar|Param|null, // JSON-encoded JWKSet used to decrypt the token (must contain a list of valid private keys).
* },
* },
* cas?: array{
* validation_url?: scalar|Param|null, // CAS server validation URL
* prefix?: scalar|Param|null, // CAS prefix // Default: "cas"
* http_client?: scalar|Param|null, // HTTP Client service // Default: null
* },
* oauth2?: scalar|Param|null,
* },
* },
* http_basic?: array{
* provider?: scalar|Param|null,
* realm?: scalar|Param|null, // Default: "Secured Area"
* },
* http_basic_ldap?: array{
* provider?: scalar|Param|null,
* realm?: scalar|Param|null, // Default: "Secured Area"
* service?: scalar|Param|null, // Default: "ldap"
* dn_string?: scalar|Param|null, // Default: "{user_identifier}"
* query_string?: scalar|Param|null,
* search_dn?: scalar|Param|null, // Default: ""
* search_password?: scalar|Param|null, // Default: ""
* },
* remember_me?: array{
* secret?: scalar|Param|null, // Default: "%kernel.secret%"
* service?: scalar|Param|null,
* user_providers?: list<scalar|Param|null>,
* catch_exceptions?: bool|Param, // Default: true
* signature_properties?: list<scalar|Param|null>,
* token_provider?: string|array{
* service?: scalar|Param|null, // The service ID of a custom remember-me token provider.
* doctrine?: bool|array{
* enabled?: bool|Param, // Default: false
* connection?: scalar|Param|null, // Default: null
* },
* },
* token_verifier?: scalar|Param|null, // The service ID of a custom rememberme token verifier.
* name?: scalar|Param|null, // Default: "REMEMBERME"
* lifetime?: int|Param, // Default: 31536000
* path?: scalar|Param|null, // Default: "/"
* domain?: scalar|Param|null, // Default: null
* secure?: true|false|"auto"|Param, // Default: null
* httponly?: bool|Param, // Default: true
* samesite?: null|"lax"|"strict"|"none"|Param, // Default: "lax"
* always_remember_me?: bool|Param, // Default: false
* remember_me_parameter?: scalar|Param|null, // Default: "_remember_me"
* },
* }>,
* access_control?: list<array{ // Default: []
* request_matcher?: scalar|Param|null, // Default: null
* requires_channel?: scalar|Param|null, // Default: null
* path?: scalar|Param|null, // Use the urldecoded format. // Default: null
* host?: scalar|Param|null, // Default: null
* port?: int|Param, // Default: null
* ips?: list<scalar|Param|null>,
* attributes?: array<string, scalar|Param|null>,
* route?: scalar|Param|null, // Default: null
* methods?: list<scalar|Param|null>,
* allow_if?: scalar|Param|null, // Default: null
* roles?: list<scalar|Param|null>,
* }>,
* role_hierarchy?: array<string, string|list<scalar|Param|null>>,
* }
* @psalm-type TwigConfig = array{
* form_themes?: list<scalar|Param|null>,
* globals?: array<string, array{ // Default: []
* id?: scalar|Param|null,
* type?: scalar|Param|null,
* value?: mixed,
* }>,
* autoescape_service?: scalar|Param|null, // Default: null
* autoescape_service_method?: scalar|Param|null, // Default: null
* base_template_class?: scalar|Param|null, // Deprecated: The child node "base_template_class" at path "twig.base_template_class" is deprecated.
* cache?: scalar|Param|null, // Default: true
* charset?: scalar|Param|null, // Default: "%kernel.charset%"
* debug?: bool|Param, // Default: "%kernel.debug%"
* strict_variables?: bool|Param, // Default: "%kernel.debug%"
* auto_reload?: scalar|Param|null,
* optimizations?: int|Param,
* default_path?: scalar|Param|null, // The default path used to load templates. // Default: "%kernel.project_dir%/templates"
* file_name_pattern?: list<scalar|Param|null>,
* paths?: array<string, mixed>,
* date?: array{ // The default format options used by the date filter.
* format?: scalar|Param|null, // Default: "F j, Y H:i"
* interval_format?: scalar|Param|null, // Default: "%d days"
* timezone?: scalar|Param|null, // The timezone used when formatting dates, when set to null, the timezone returned by date_default_timezone_get() is used. // Default: null
* },
* number_format?: array{ // The default format options for the number_format filter.
* decimals?: int|Param, // Default: 0
* decimal_point?: scalar|Param|null, // Default: "."
* thousands_separator?: scalar|Param|null, // Default: ","
* },
* mailer?: array{
* html_to_text_converter?: scalar|Param|null, // A service implementing the "Symfony\Component\Mime\HtmlToTextConverter\HtmlToTextConverterInterface". // Default: null
* },
* }
* @psalm-type WebProfilerConfig = array{
* toolbar?: bool|array{ // Profiler toolbar configuration
* enabled?: bool|Param, // Default: false
* ajax_replace?: bool|Param, // Replace toolbar on AJAX requests // Default: false
* },
* intercept_redirects?: bool|Param, // Default: false
* excluded_ajax_paths?: scalar|Param|null, // Default: "^/((index|app(_[\\w]+)?)\\.php/)?_wdt"
* }
* @psalm-type MonologConfig = array{
* use_microseconds?: scalar|Param|null, // Default: true
* channels?: list<scalar|Param|null>,
* handlers?: array<string, array{ // Default: []
* type?: scalar|Param|null,
* id?: scalar|Param|null,
* enabled?: bool|Param, // Default: true
* priority?: scalar|Param|null, // Default: 0
* level?: scalar|Param|null, // Default: "DEBUG"
* bubble?: bool|Param, // Default: true
* interactive_only?: bool|Param, // Default: false
* app_name?: scalar|Param|null, // Default: null
* fill_extra_context?: bool|Param, // Default: false
* include_stacktraces?: bool|Param, // Default: false
* process_psr_3_messages?: array{
* enabled?: bool|Param|null, // Default: null
* date_format?: scalar|Param|null,
* remove_used_context_fields?: bool|Param,
* },
* path?: scalar|Param|null, // Default: "%kernel.logs_dir%/%kernel.environment%.log"
* file_permission?: scalar|Param|null, // Default: null
* use_locking?: bool|Param, // Default: false
* filename_format?: scalar|Param|null, // Default: "{filename}-{date}"
* date_format?: scalar|Param|null, // Default: "Y-m-d"
* ident?: scalar|Param|null, // Default: false
* logopts?: scalar|Param|null, // Default: 1
* facility?: scalar|Param|null, // Default: "user"
* max_files?: scalar|Param|null, // Default: 0
* action_level?: scalar|Param|null, // Default: "WARNING"
* activation_strategy?: scalar|Param|null, // Default: null
* stop_buffering?: bool|Param, // Default: true
* passthru_level?: scalar|Param|null, // Default: null
* excluded_404s?: list<scalar|Param|null>,
* excluded_http_codes?: list<array{ // Default: []
* code?: scalar|Param|null,
* urls?: list<scalar|Param|null>,
* }>,
* accepted_levels?: list<scalar|Param|null>,
* min_level?: scalar|Param|null, // Default: "DEBUG"
* max_level?: scalar|Param|null, // Default: "EMERGENCY"
* buffer_size?: scalar|Param|null, // Default: 0
* flush_on_overflow?: bool|Param, // Default: false
* handler?: scalar|Param|null,
* url?: scalar|Param|null,
* exchange?: scalar|Param|null,
* exchange_name?: scalar|Param|null, // Default: "log"
* room?: scalar|Param|null,
* message_format?: scalar|Param|null, // Default: "text"
* api_version?: scalar|Param|null, // Default: null
* channel?: scalar|Param|null, // Default: null
* bot_name?: scalar|Param|null, // Default: "Monolog"
* use_attachment?: scalar|Param|null, // Default: true
* use_short_attachment?: scalar|Param|null, // Default: false
* include_extra?: scalar|Param|null, // Default: false
* icon_emoji?: scalar|Param|null, // Default: null
* webhook_url?: scalar|Param|null,
* exclude_fields?: list<scalar|Param|null>,
* team?: scalar|Param|null,
* notify?: scalar|Param|null, // Default: false
* nickname?: scalar|Param|null, // Default: "Monolog"
* token?: scalar|Param|null,
* region?: scalar|Param|null,
* source?: scalar|Param|null,
* use_ssl?: bool|Param, // Default: true
* user?: mixed,
* title?: scalar|Param|null, // Default: null
* host?: scalar|Param|null, // Default: null
* port?: scalar|Param|null, // Default: 514
* config?: list<scalar|Param|null>,
* members?: list<scalar|Param|null>,
* connection_string?: scalar|Param|null,
* timeout?: scalar|Param|null,
* time?: scalar|Param|null, // Default: 60
* deduplication_level?: scalar|Param|null, // Default: 400
* store?: scalar|Param|null, // Default: null
* connection_timeout?: scalar|Param|null,
* persistent?: bool|Param,
* dsn?: scalar|Param|null,
* hub_id?: scalar|Param|null, // Default: null
* client_id?: scalar|Param|null, // Default: null
* auto_log_stacks?: scalar|Param|null, // Default: false
* release?: scalar|Param|null, // Default: null
* environment?: scalar|Param|null, // Default: null
* message_type?: scalar|Param|null, // Default: 0
* parse_mode?: scalar|Param|null, // Default: null
* disable_webpage_preview?: bool|Param|null, // Default: null
* disable_notification?: bool|Param|null, // Default: null
* split_long_messages?: bool|Param, // Default: false
* delay_between_messages?: bool|Param, // Default: false
* topic?: int|Param, // Default: null
* factor?: int|Param, // Default: 1
* tags?: list<scalar|Param|null>,
* console_formater_options?: mixed, // Deprecated: "monolog.handlers..console_formater_options.console_formater_options" is deprecated, use "monolog.handlers..console_formater_options.console_formatter_options" instead.
* console_formatter_options?: mixed, // Default: []
* formatter?: scalar|Param|null,
* nested?: bool|Param, // Default: false
* publisher?: string|array{
* id?: scalar|Param|null,
* hostname?: scalar|Param|null,
* port?: scalar|Param|null, // Default: 12201
* chunk_size?: scalar|Param|null, // Default: 1420
* encoder?: "json"|"compressed_json"|Param,
* },
* mongo?: string|array{
* id?: scalar|Param|null,
* host?: scalar|Param|null,
* port?: scalar|Param|null, // Default: 27017
* user?: scalar|Param|null,
* pass?: scalar|Param|null,
* database?: scalar|Param|null, // Default: "monolog"
* collection?: scalar|Param|null, // Default: "logs"
* },
* mongodb?: string|array{
* id?: scalar|Param|null, // ID of a MongoDB\Client service
* uri?: scalar|Param|null,
* username?: scalar|Param|null,
* password?: scalar|Param|null,
* database?: scalar|Param|null, // Default: "monolog"
* collection?: scalar|Param|null, // Default: "logs"
* },
* elasticsearch?: string|array{
* id?: scalar|Param|null,
* hosts?: list<scalar|Param|null>,
* host?: scalar|Param|null,
* port?: scalar|Param|null, // Default: 9200
* transport?: scalar|Param|null, // Default: "Http"
* user?: scalar|Param|null, // Default: null
* password?: scalar|Param|null, // Default: null
* },
* index?: scalar|Param|null, // Default: "monolog"
* document_type?: scalar|Param|null, // Default: "logs"
* ignore_error?: scalar|Param|null, // Default: false
* redis?: string|array{
* id?: scalar|Param|null,
* host?: scalar|Param|null,
* password?: scalar|Param|null, // Default: null
* port?: scalar|Param|null, // Default: 6379
* database?: scalar|Param|null, // Default: 0
* key_name?: scalar|Param|null, // Default: "monolog_redis"
* },
* predis?: string|array{
* id?: scalar|Param|null,
* host?: scalar|Param|null,
* },
* from_email?: scalar|Param|null,
* to_email?: list<scalar|Param|null>,
* subject?: scalar|Param|null,
* content_type?: scalar|Param|null, // Default: null
* headers?: list<scalar|Param|null>,
* mailer?: scalar|Param|null, // Default: null
* email_prototype?: string|array{
* id?: scalar|Param|null,
* method?: scalar|Param|null, // Default: null
* },
* lazy?: bool|Param, // Default: true
* verbosity_levels?: array{
* VERBOSITY_QUIET?: scalar|Param|null, // Default: "ERROR"
* VERBOSITY_NORMAL?: scalar|Param|null, // Default: "WARNING"
* VERBOSITY_VERBOSE?: scalar|Param|null, // Default: "NOTICE"
* VERBOSITY_VERY_VERBOSE?: scalar|Param|null, // Default: "INFO"
* VERBOSITY_DEBUG?: scalar|Param|null, // Default: "DEBUG"
* },
* channels?: string|array{
* type?: scalar|Param|null,
* elements?: list<scalar|Param|null>,
* },
* }>,
* }
* @psalm-type DebugConfig = array{
* max_items?: int|Param, // Max number of displayed items past the first level, -1 means no limit. // Default: 2500
* min_depth?: int|Param, // Minimum tree depth to clone all the items, 1 is default. // Default: 1
* max_string_length?: int|Param, // Max length of displayed strings, -1 means no limit. // Default: -1
* dump_destination?: scalar|Param|null, // A stream URL where dumps should be written to. // Default: null
* theme?: "dark"|"light"|Param, // Changes the color of the dump() output when rendered directly on the templating. "dark" (default) or "light". // Default: "dark"
* }
* @psalm-type MakerConfig = array{
* root_namespace?: scalar|Param|null, // Default: "App"
* generate_final_classes?: bool|Param, // Default: true
* generate_final_entities?: bool|Param, // Default: false
* }
* @psalm-type ConfigType = array{
* imports?: ImportsConfig,
* parameters?: ParametersConfig,
* services?: ServicesConfig,
* framework?: FrameworkConfig,
* doctrine?: DoctrineConfig,
* doctrine_migrations?: DoctrineMigrationsConfig,
* security?: SecurityConfig,
* twig?: TwigConfig,
* web_profiler?: WebProfilerConfig,
* monolog?: MonologConfig,
* debug?: DebugConfig,
* "when@dev"?: array{
* imports?: ImportsConfig,
* parameters?: ParametersConfig,
* services?: ServicesConfig,
* framework?: FrameworkConfig,
* doctrine?: DoctrineConfig,
* doctrine_migrations?: DoctrineMigrationsConfig,
* security?: SecurityConfig,
* twig?: TwigConfig,
* web_profiler?: WebProfilerConfig,
* monolog?: MonologConfig,
* debug?: DebugConfig,
* maker?: MakerConfig,
* },
* "when@test"?: array{
* imports?: ImportsConfig,
* parameters?: ParametersConfig,
* services?: ServicesConfig,
* framework?: FrameworkConfig,
* doctrine?: DoctrineConfig,
* doctrine_migrations?: DoctrineMigrationsConfig,
* security?: SecurityConfig,
* twig?: TwigConfig,
* web_profiler?: WebProfilerConfig,
* monolog?: MonologConfig,
* debug?: DebugConfig,
* },
* ...<string, ExtensionType|array{ // extra keys must follow the when@%env% pattern or match an extension alias
* imports?: ImportsConfig,
* parameters?: ParametersConfig,
* services?: ServicesConfig,
* ...<string, ExtensionType>,
* }>
* }
*/
final class App
{
/**
* @param ConfigType $config
*
* @psalm-return ConfigType
*/
public static function config(array $config): array
{
/** @var ConfigType $config */
$config = AppReference::config($config);
return $config;
}
}
namespace Symfony\Component\Routing\Loader\Configurator;
/**
* This class provides array-shapes for configuring the routes of an application.
*
* Example:
*
* ```php
* // config/routes.php
* namespace Symfony\Component\Routing\Loader\Configurator;
*
* return Routes::config([
* 'controllers' => [
* 'resource' => 'routing.controllers',
* ],
* ]);
* ```
*
* @psalm-type RouteConfig = array{
* path: string|array<string,string>,
* controller?: string,
* methods?: string|list<string>,
* requirements?: array<string,string>,
* defaults?: array<string,mixed>,
* options?: array<string,mixed>,
* host?: string|array<string,string>,
* schemes?: string|list<string>,
* condition?: string,
* locale?: string,
* format?: string,
* utf8?: bool,
* stateless?: bool,
* }
* @psalm-type ImportConfig = array{
* resource: string,
* type?: string,
* exclude?: string|list<string>,
* prefix?: string|array<string,string>,
* name_prefix?: string,
* trailing_slash_on_root?: bool,
* controller?: string,
* methods?: string|list<string>,
* requirements?: array<string,string>,
* defaults?: array<string,mixed>,
* options?: array<string,mixed>,
* host?: string|array<string,string>,
* schemes?: string|list<string>,
* condition?: string,
* locale?: string,
* format?: string,
* utf8?: bool,
* stateless?: bool,
* }
* @psalm-type AliasConfig = array{
* alias: string,
* deprecated?: array{package:string, version:string, message?:string},
* }
* @psalm-type RoutesConfig = array{
* "when@dev"?: array<string, RouteConfig|ImportConfig|AliasConfig>,
* "when@test"?: array<string, RouteConfig|ImportConfig|AliasConfig>,
* ...<string, RouteConfig|ImportConfig|AliasConfig>
* }
*/
final class Routes
{
/**
* @param RoutesConfig $config
*
* @psalm-return RoutesConfig
*/
public static function config(array $config): array
{
return $config;
}
}
================================================
FILE: config/routes/attributes.yaml
================================================
controllers:
resource: ../../src/Controller/
type: attribute
kernel:
resource: App\Kernel
type: attribute
================================================
FILE: config/routes/dev/framework.yaml
================================================
_errors:
resource: '@FrameworkBundle/Resources/config/routing/errors.php'
prefix: /_error
================================================
FILE: config/routes/dev/web_profiler.yaml
================================================
web_profiler_wdt:
resource: '@WebProfilerBundle/Resources/config/routing/wdt.php'
prefix: /_wdt
web_profiler_profiler:
resource: '@WebProfilerBundle/Resources/config/routing/profiler.php'
prefix: /_profiler
================================================
FILE: config/routes.yaml
================================================
#index:
# path: /
# controller: App\Controller\DefaultController::index
================================================
FILE: config/services.yaml
================================================
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
default_database_driver: "mysql"
default_admin_auth_bypass: "false"
timezone: '%env(APP_TIMEZONE)%'
public_calendars_enabled: '%env(default:default_public_calendars_enabled:bool:PUBLIC_CALENDARS_ENABLED)%'
default_public_calendars_enabled: "true"
birthday_reminder_offset: '%env(default:default_birthday_reminder_offset:BIRTHDAY_REMINDER_OFFSET)%'
default_birthday_reminder_offset: "PT9H"
caldav_enabled: "%env(bool:CALDAV_ENABLED)%"
carddav_enabled: "%env(bool:CARDDAV_ENABLED)%"
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Services\Utils:
arguments:
$authRealm: "%env(AUTH_REALM)%"
App\Services\IMAPAuth:
arguments:
$IMAPAuthUrl: "%env(IMAP_AUTH_URL)%"
$IMAPEncryptionMethod: "%env(IMAP_ENCRYPTION_METHOD)%"
$IMAPCertificateValidation: "%env(bool:IMAP_CERTIFICATE_VALIDATION)%"
$autoCreate: "%env(bool:IMAP_AUTH_USER_AUTOCREATE)%"
App\Services\LDAPAuth:
arguments:
$LDAPAuthUrl: "%env(LDAP_AUTH_URL)%"
$LDAPDnPattern: "%env(LDAP_DN_PATTERN)%"
$LDAPMailAttribute: "%env(LDAP_MAIL_ATTRIBUTE)%"
$LDAPCertificateCheckingStrategy: "%env(LDAP_CERTIFICATE_CHECKING_STRATEGY)%"
$autoCreate: "%env(bool:LDAP_AUTH_USER_AUTOCREATE)%"
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
App\Controller\DAVController:
arguments:
$publicDir: "%kernel.project_dir%/public"
$calDAVEnabled: "%env(bool:CALDAV_ENABLED)%"
$cardDAVEnabled: "%env(bool:CARDDAV_ENABLED)%"
$webDAVEnabled: "%env(bool:WEBDAV_ENABLED)%"
$publicCalendarsEnabled: "%public_calendars_enabled%"
$inviteAddress: "%env(INVITE_FROM_ADDRESS)%"
$authMethod: "%env(AUTH_METHOD)%"
$authRealm: "%env(AUTH_REALM)%"
$webdavPublicDir: "%env(resolve:WEBDAV_PUBLIC_DIR)%"
$webdavHomesDir: "%env(resolve:WEBDAV_HOMES_DIR)%"
$webdavTmpDir: "%env(resolve:WEBDAV_TMP_DIR)%"
App\Security\LoginFormAuthenticator:
arguments:
$adminLogin: "%env(ADMIN_LOGIN)%"
$adminPassword: "%env(ADMIN_PASSWORD)%"
App\Logging\Monolog\PasswordFilterProcessor:
tags:
- { name: monolog.processor }
App\Services\BirthdayService:
arguments:
$birthdayReminderOffset: "%birthday_reminder_offset%"
App\Security\ApiKeyAuthenticator:
arguments:
$apiKey: "%env(API_KEY)%"
when@dev:
services:
Symfony\Component\HttpKernel\Profiler\Profiler: '@profiler'
when@test:
services:
Symfony\Component\HttpKernel\Profiler\Profiler: '@profiler'
================================================
FILE: docker/Dockerfile
================================================
# syntax=docker/dockerfile:1
# Initial PHP image is available here:
# https://github.com/docker-library/php/blob/master/8.2/alpine3.18/fpm/Dockerfile#L33
ARG fpm_user=82:82
# Base image, used to build extensions and the final image ———————————————————————
FROM php:8.3-fpm-alpine AS base-image
# Run update, and gets basic packages and packages for runtime
RUN apk --no-progress --update add --no-cache \
curl unzip fcgi \
# These are for php-intl
icu-libs \
# This one for LDAP
libldap \
# This one for IMAP
libzip \
# These are for GD (map image in mail)
freetype \
libjpeg-turbo \
libpng \
# This is for PostgreSQL
libpq
# Build all extensions in a separate image ———————————————————————————————————————
FROM base-image AS extension-builder
# Install ALL build dependencies at once
# hadolint ignore=SC2086
RUN apk --update --virtual build-deps add --no-cache \
# Intl support
icu-dev \
# PDO: PostgreSQL
libpq-dev \
# GD (map image in mail)
freetype-dev libjpeg-turbo-dev libpng-dev \
# LDAP auth support
openldap-dev \
# Zip lib for PHP-IMAP
libzip-dev \
$PHPIZE_DEPS
# Build ALL extensions in one RUN command to reduce layers
# and allow parallel compilation for extensions
RUN docker-php-ext-configure intl \
&& docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd \
&& docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \
&& docker-php-ext-configure gd --with-freetype \
&& docker-php-ext-configure ldap \
&& docker-php-ext-configure zip \
&& docker-php-ext-install -j"$(nproc)" \
intl \
pdo_mysql \
pgsql \
pdo_pgsql \
gd \
ldap \
zip \
opcache \
&& docker-php-ext-enable gd opcache \
&& apk del build-deps \
&& rm -rf /tmp/* /var/cache/apk/*
COPY ./docker/configurations/opcache.ini /usr/local/etc/php/conf.d/opcache.ini
# Final image ————————————————————————————————————————————————————————————————————
FROM base-image
ARG fpm_user=82:82
ENV FPM_USER=${fpm_user}
ENV PHP_OPCACHE_MEMORY_CONSUMPTION="256" \
PHP_OPCACHE_MAX_WASTED_PERCENTAGE="10"
LABEL org.opencontainers.image.authors="tchap@tchap.me"
LABEL org.opencontainers.image.url="https://github.com/tchapi/davis/pkgs/container/davis"
LABEL org.opencontainers.image.description="A simple, fully translatable admin interface for sabre/dav based on Symfony 7 and Bootstrap 5"
# Rapatriate built extensions
COPY --from=extension-builder /usr/local/etc/php/conf.d /usr/local/etc/php/conf.d/
COPY --from=extension-builder /usr/local/lib/php/extensions /usr/local/lib/php/extensions/
# This is for Hadolint
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
# PHP-FPM healthcheck
RUN set -xe && echo "pm.status_path = /status" >> /usr/local/etc/php-fpm.d/zz-docker.conf
RUN curl https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/v0.5.0/php-fpm-healthcheck \
-o /usr/local/bin/php-fpm-healthcheck -s \
&& chmod +x /usr/local/bin/php-fpm-healthcheck
# Davis installation
COPY --chown=${FPM_USER} . /var/www/davis
WORKDIR /var/www/davis
# Install Composer 2, then dependencies, compress the rather big INTL package, and then cleanup (only useful when using --squash)
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN APP_ENV=prod COMPOSER_ALLOW_SUPERUSER=1 composer install --no-ansi --no-dev --no-interaction --no-progress --prefer-dist --optimize-autoloader \
&& php ./vendor/symfony/intl/Resources/bin/compress \
&& rm -rf /var/www/davis/docker
USER $FPM_USER
HEALTHCHECK --interval=30s --timeout=1s CMD php-fpm-healthcheck || exit 1
================================================
FILE: docker/Dockerfile-standalone
================================================
# Initial PHP image is available here:
# https://github.com/docker-library/php/blob/master/8.2/alpine3.18/fpm/Dockerfile#L33
# Base image, used to build extensions and the final image ———————————————————————
FROM php:8.3-fpm-alpine AS base-image
# Run update, and gets basic packages and packages for runtime
RUN apk --no-progress --update add --no-cache \
curl unzip \
# These are for php-intl
icu-libs \
# This one for LDAP
libldap \
# This one for IMAP
libzip \
# These are for GD (map image in mail)
freetype \
libjpeg-turbo \
libpng \
# This is for PostgreSQL
libpq \
# For the webserver and process manager
caddy supervisor
# Build all extensions in a separate image ———————————————————————————————————————
FROM base-image AS extension-builder
# Install ALL build dependencies at once
# hadolint ignore=SC2086
RUN apk --update --virtual build-deps add --no-cache \
# Intl support
icu-dev \
# PDO: PostgreSQL
libpq-dev \
# GD (map image in mail)
freetype-dev libjpeg-turbo-dev libpng-dev \
# LDAP auth support
openldap-dev \
# Zip lib for PHP-IMAP
libzip-dev \
$PHPIZE_DEPS
# Build ALL extensions in one RUN command to reduce layers
# and allow parallel compilation for extensions
RUN docker-php-ext-configure intl \
&& docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd \
&& docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \
&& docker-php-ext-configure gd --with-freetype \
&& docker-php-ext-configure ldap \
&& docker-php-ext-configure zip \
&& docker-php-ext-install -j"$(nproc)" \
intl \
pdo_mysql \
pgsql \
pdo_pgsql \
gd \
ldap \
zip \
opcache \
&& docker-php-ext-enable gd opcache \
&& apk del build-deps \
&& rm -rf /tmp/* /var/cache/apk/*
COPY ./docker/configurations/opcache.ini /usr/local/etc/php/conf.d/opcache.ini
# Final image ————————————————————————————————————————————————————————————————————
FROM base-image
ENV PHP_OPCACHE_MEMORY_CONSUMPTION="256" \
PHP_OPCACHE_MAX_WASTED_PERCENTAGE="10"
LABEL org.opencontainers.image.authors="tchap@tchap.me"
LABEL org.opencontainers.image.url="https://github.com/tchapi/davis/pkgs/container/davis-standalone"
LABEL org.opencontainers.image.description="A simple, fully translatable admin interface for sabre/dav based on Symfony 7 and Bootstrap 5 (Standalone version with reverse-proxy)"
# Rapatriate built extensions
COPY --from=extension-builder /usr/local/etc/php/conf.d /usr/local/etc/php/conf.d/
COPY --from=extension-builder /usr/local/lib/php/extensions /usr/local/lib/php/extensions/
# Davis source
# The app folder needs to be owned by www-data so PHP-fpm can execute files
COPY --chown=www-data:www-data . /var/www/davis
WORKDIR /var/www/davis
# This is for Hadolint
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
# Install Composer 2, then dependencies, compress the rather big INTL package
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN APP_ENV=prod COMPOSER_ALLOW_SUPERUSER=1 composer install --no-ansi --no-dev --no-interaction --no-progress --prefer-dist --optimize-autoloader \
&& php ./vendor/symfony/intl/Resources/bin/compress
# Caddy: web server
RUN mkdir -p /var/log/caddy
COPY ./docker/configurations/Caddyfile /etc/caddy/Caddyfile
# Supervisor: Process manager
RUN mkdir -p /var/log/supervisor && mkdir -p /var/log/php-fpm
COPY ./docker/configurations/supervisord.conf /etc/supervisord.conf
# We want to use sockets inside the container between Caddy and PHP-fpm
# NOTE: Creating a custom zzzz-custom.conf overrides the www.conf setting (files are processed alphabetically)
RUN mkdir /var/run/php-fpm && chown -R www-data:www-data /var/run/php-fpm \
&& { \
echo '[www]'; \
echo 'listen = /var/run/php-fpm/php-fpm.sock'; \
} | tee /usr/local/etc/php-fpm.d/zzzz-custom-docker.conf
RUN mkdir -p ./var/log ./var/cache && chown -R www-data:www-data ./var
# Cleanup (only useful when using --squash)
RUN docker-php-source delete && \
rm -rf /var/www/davis/docker
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
HEALTHCHECK --interval=120s --timeout=10s --start-period=60s --retries=3 \
CMD curl --fail http://localhost:9000 || exit 1
# It's the Caddy port, not the PHP-fpm one (as we use sockets)
EXPOSE 9000
================================================
FILE: docker/configurations/Caddyfile
================================================
{
auto_https off
}
:9000 {
# Redirect .well-known
redir /.well-known/caldav /dav/
redir /.well-known/carddav /dav/
root * /var/www/davis/public
php_fastcgi unix//var/run/php-fpm/php-fpm.sock {
# Preserve the original X-Forwarded-Proto from upstream, as it might be HTTPS
header_up X-Forwarded-Proto {http.request.header.X-Forwarded-Proto}
header_up X-Forwarded-Host {http.request.header.X-Forwarded-Host}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
}
file_server {
# Safety net, just in case
hide .git .gitignore
}
# enable compression
encode zstd gzip
# Remove leaky headers
header {
-Server
-X-Powered-By
# keep referrer data off of HTTP connections
Referrer-Policy no-referrer-when-downgrade
# disable clients from sniffing the media type
X-Content-Type-Options nosniff
}
}
================================================
FILE: docker/configurations/nginx.conf
================================================
# This is a very simple / naive configuration for nginx + Davis
#
# USE HTTPS IN PRODUCTION
#
upstream docker-davis {
server davis:9000;
}
server {
listen 80;
access_log off;
root /var/www/davis/public/;
index index.php;
rewrite ^/.well-known/caldav /dav/ redirect;
rewrite ^/.well-known/carddav /dav/ redirect;
charset utf-8;
location ~ /(\.ht) {
deny all;
return 404;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ ^(.+\.php)(.*)$ {
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_pass docker-davis;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_split_path_info ^(.+\.php)(.*)$;
}
}
================================================
FILE: docker/configurations/opcache.ini
================================================
opcache.enable=1
opcache.jit=1255
opcache.jit_buffer_size=128M
opcache.revalidate_freq=60
opcache.save_comments=1
opcache.validate_timestamps=1
opcache.max_accelerated_files=32531
opcache.memory_consumption=${PHP_OPCACHE_MEMORY_CONSUMPTION}
opcache.max_wasted_percentage=${PHP_OPCACHE_MAX_WASTED_PERCENTAGE}
opcache.interned_strings_buffer=64
================================================
FILE: docker/configurations/supervisord.conf
================================================
[supervisord]
nodaemon=true
user=root
pidfile=/run/supervisord.pid
logfile=/dev/null
logfile_maxbytes=0
[unix_http_server]
file=/run/supervisord.sock ; the path to the socket file
[supervisorctl]
serverurl=unix:///run/supervisord.sock ; use a unix:// URL for a unix socket
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[program:caddy]
command=/usr/sbin/caddy run -c /etc/caddy/Caddyfile
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/caddy/access.log
stdout_logfile_maxbytes = 0
[program:php-fpm]
command=/usr/local/sbin/php-fpm --nodaemonize
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/php-fpm/access.log
stdout_logfile_maxbytes = 0
================================================
FILE: docker/docker-compose-postgresql.yml
================================================
version: "3.7"
name: "davis-docker"
services:
nginx:
image: nginx:1.25-alpine
container_name: nginx
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
depends_on:
- davis
volumes:
- davis_www:/var/www/davis
- type: bind
source: ./configurations/nginx.conf
target: /etc/nginx/conf.d/default.conf
ports:
- 9000:80
postgresql:
image: postgres:16-alpine
container_name: postgresql
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_DATABASE}
- POSTGRES_USER=${DB_USER}
volumes:
- database_pg:/var/lib/postgresql/data
davis:
build:
context: ../
dockerfile: ./docker/Dockerfile
image: davis:latest
# If you want to use a prebuilt image from Github
# image: ghcr.io/tchapi/davis:edge
container_name: davis
env_file: .env
environment:
- DATABASE_DRIVER=postgresql
- DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@postgresql:5432/${DB_DATABASE}?serverVersion=15&charset=UTF-8
- MAILER_DSN=smtp://${MAIL_USERNAME}:${MAIL_PASSWORD}@${MAIL_HOST}:${MAIL_PORT}
depends_on:
- postgresql
volumes:
- davis_www:/var/www/davis
volumes:
davis_www:
name: davis_www
database_pg:
name: database_pg
================================================
FILE: docker/docker-compose-sqlite.yml
================================================
version: "3.7"
name: "davis-docker"
services:
nginx:
image: nginx:1.25-alpine
container_name: nginx
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
depends_on:
- davis
volumes:
- davis_www:/var/www/davis
- type: bind
source: ./configurations/nginx.conf
target: /etc/nginx/conf.d/default.conf
ports:
- 9000:80
davis:
build:
context: ../
dockerfile: ./docker/Dockerfile
image: davis:latest
# If you want to use a prebuilt image from Github
# image: ghcr.io/tchapi/davis:edge
container_name: davis
env_file: .env
environment:
- DATABASE_DRIVER=sqlite
- DATABASE_URL=sqlite:////data/davis-database.db # ⚠️ 4 slashes for an absolute path ⚠️ + no quotes (so Symfony can resolve it)
- MAILER_DSN=smtp://${MAIL_USERNAME}:${MAIL_PASSWORD}@${MAIL_HOST}:${MAIL_PORT}
volumes:
- davis_www:/var/www/davis
- davis_data:/data
volumes:
davis_www:
name: davis_www
davis_data:
name: davis_data
================================================
FILE: docker/docker-compose-standalone.yml
================================================
version: "3.7"
name: "davis-docker"
services:
mysql:
image: mariadb:10.6.10
container_name: mysql
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MYSQL_DATABASE=${DB_DATABASE}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
volumes:
- database:/var/lib/mysql
davis:
build:
context: ../
dockerfile: ./docker/Dockerfile-standalone
image: davis:latest
# If you want to use a prebuilt image from Github
# image: ghcr.io/tchapi/davis-standalone:edge
container_name: davis-standalone
env_file: .env
environment:
- DATABASE_DRIVER=mysql
- DATABASE_URL=mysql://${DB_USER}:${DB_PASSWORD}@mysql:3306/${DB_DATABASE}?serverVersion=mariadb-10.6.10&charset=utf8mb4
- MAILER_DSN=smtp://${MAIL_USERNAME}:${MAIL_PASSWORD}@${MAIL_HOST}:${MAIL_PORT}
depends_on:
- mysql
ports:
- 9000:9000
volumes:
database:
name: database
================================================
FILE: docker/docker-compose.yml
================================================
version: "3.7"
name: "davis-docker"
services:
nginx:
image: nginx:1.25-alpine
container_name: nginx
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
depends_on:
- davis
volumes:
- davis_www:/var/www/davis
- type: bind
source: ./configurations/nginx.conf
target: /etc/nginx/conf.d/default.conf
ports:
- 9000:80
mysql:
image: mariadb:10.11
container_name: mysql
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MYSQL_DATABASE=${DB_DATABASE}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
volumes:
- database:/var/lib/mysql
davis:
build:
context: ../
dockerfile: ./docker/Dockerfile
args:
fpm_user: 101:101
image: davis:latest
# If you want to use a prebuilt image from Github
# image: ghcr.io/tchapi/davis:edge
container_name: davis
env_file: .env
environment:
- DATABASE_DRIVER=mysql
- DATABASE_URL=mysql://${DB_USER}:${DB_PASSWORD}@mysql:3306/${DB_DATABASE}?serverVersion=mariadb-10.6.10&charset=utf8mb4
- MAILER_DSN=smtp://${MAIL_USERNAME}:${MAIL_PASSWORD}@${MAIL_HOST}:${MAIL_PORT}
depends_on:
- mysql
volumes:
- davis_www:/var/www/davis
volumes:
davis_www:
name: davis_www
database:
name: database
================================================
FILE: docs/api/README.md
================================================
# Davis API
## API Version 1
### Open Endpoints
Open endpoints require no Authentication.
* [Health](v1/health.md) : `GET /api/v1/health`
### Endpoints that require Authentication
Closed endpoints require a valid `X-Davis-API-Token` to be included in the header of the request. Token needs to be configured in .env file (as a environment variable `API_KEY`) and can be generated using `php bin/console api:generate` command.
When `API_KEY` is not set, the API endpoints are disabled and will return a 500 error if accessed.
#### User related
Each endpoint displays information related to the User:
* [Get Users](v1/users/all.md) : `GET /api/v1/users`
* [Get User Details](v1/users/details.md) : `GET /api/v1/users/:user_id`
#### Calendars related
Endpoints for viewing and modifying user calendars.
* [Show All User Calendars](v1/calendars/all.md) : `GET /api/v1/calendars/:user_id`
* [Show User Calendar Details](v1/calendars/details.md) : `GET /api/v1/calendars/:user_id/:calendar_id`
* [Create User Calendar](v1/calendars/create.md) : `POST /api/v1/calendars/:user_id/create`
* [Edit User Calendar](v1/calendars/edit.md) : `PUT /api/v1/calendars/:user_id/:calendar_id`
* [Delete User Calendar](v1/calendars/delete.md) : `DELETE /api/v1/calendars/:user_id/:calendar_id`
* [Show User Calendar Shares](v1/calendars/shares.md) : `GET /api/v1/calendars/:user_id/shares/:calendar_id`
* [Share User Calendar](v1/calendars/share_add.md) : `POST /api/v1/calendars/:user_id/share/:calendar_id/add`
* [Remove Share User Calendar](v1/calendars/share_remove.md) : `POST /api/v1/calendars/:user_id/share/:calendar_id/remove`
================================================
FILE: docs/api/v1/calendars/all.md
================================================
# User Calendars
Gets a list of all available calendars for a specific user.
**URL** : `/api/v1/calendars/:user_id`
**Method** : `GET`
**Auth required** : YES
**Params constraints**
```
:user_id -> "[user id as an int]",
```
**URL example**
```json
/api/v1/calendars/jdoe
```
## Success Response
**Code** : `200 OK`
**Notes**: The `events`, `notes`, and `tasks` fields return a count (number) if the component is enabled for the calendar, or `null` if the component is disabled.
**Content examples**
```json
{
"status": "success",
"data": {
"user_calendars": [
{
"id": 1,
"uri": "default",
"displayname": "Default Calendar",
"events": 0,
"notes": null,
"tasks": null
}
],
"shared_calendars": [
{
"id": 10,
"uri": "c2152eb0-ada1-451f-bf33-b4a9571ec92e",
"displayname": "Default Calendar",
"events": 0,
"notes": null,
"tasks": null
}
],
"subscriptions": []
},
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
Shown when user does not have calendars:
```json
{
"status": "success",
"data": {
"user_calendars": [],
"shared_calendars": [],
"subscriptions": []
},
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
## Error Response
**Condition** : If 'X-Davis-API-Token' is not present or mismatched in headers.
**Code** : `401 UNAUTHORIZED`
**Content** :
```json
{
"message": "No API token provided",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
or
```json
{
"message": "Invalid API token",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
**Condition** : If user is not found.
**Code** : `404 NOT FOUND`
**Content** :
```json
{
"status": "error",
"message": "User Not Found",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
================================================
FILE: docs/api/v1/calendars/create.md
================================================
# Create User Calendar
Creates a new calendar for a specific user.
**URL** : `/api/v1/calendars/:user_id/create`
**Method** : `POST`
**Auth required** : YES
**Params constraints**
```
:user_id -> "[user id as an int]",
```
**Request Body constraints**
```json
{
"name": "[string: calendar name, alphanumeric, spaces, underscores and hyphens, max 64 chars]",
"uri": "[string: calendar URI, lowercase alphanumeric, underscores and hyphens, max 128 chars]",
"description": "[string: calendar description, alphanumeric, spaces, underscores and hyphens, max 256 chars, optional]",
"events_support": "[string: 'true' or 'false', default 'true', optional]",
"notes_support": "[string: 'true' or 'false', default 'false', optional]",
"tasks_support": "[string: 'true' or 'false', default 'false', optional]"
}
```
**URL example**
```
/api/v1/calendars/jdoe/create
```
**Body example**
```json
{
"name": "Work Calendar",
"uri": "work-calendar",
"description": "Calendar for work events",
"events_support": "true",
"notes_support": "false",
"tasks_support": "true"
}
```
## Success Response
**Code** : `200 OK`
**Content examples**
```json
{
"status": "success",
"data": {
"calendar_id": 5,
"calendar_uri": "work-calendar"
},
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
## Error Response
**Condition** : If 'X-Davis-API-Token' is not present or mismatched in headers.
**Code** : `401 UNAUTHORIZED`
**Content** :
```json
{
"message": "No API token provided",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
or
```json
{
"message": "Invalid API token",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
**Condition** : If user is not found.
**Code** : `404 NOT FOUND`
**Content** :
```json
{
"status": "error",
"message": "User Not Found",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
**Condition** : If request body contains invalid JSON.
**Code** : `400 BAD REQUEST`
**Content** :
```json
{
"status": "error",
"message": "Invalid JSON",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
**Condition** : If 'name' parameter is invalid (not matching the regex or exceeds length).
**Code** : `400 BAD REQUEST`
**Content** :
```json
{
"status": "error",
"message": "Invalid Calendar Name",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
**Condition** : If 'uri' parameter is invalid (not matching the regex or exceeds length).
**Code** : `400 BAD REQUEST`
**Content** :
```json
{
"status": "error",
"message": "Invalid Calendar URI",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
**Condition** : If calendar with specified URI already exists for the user.
**Code** : `400 BAD REQUEST`
**Content** :
```json
{
"status": "error",
"message": "Calendar URI Already Exists",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
**Condition** : If 'description' parameter is invalid (not matching the regex or exceeds length).
**Code** : `400 BAD REQUEST`
**Content** :
```json
{
"status": "error",
"message": "Invalid Calendar Description",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
**Condition** : If no calendar components are enabled (all of `events_support`, `notes_support`, and `tasks_support` are false).
**Code** : `400 BAD REQUEST`
**Content** :
```json
{
"status": "error",
"message": "At least one calendar component must be enabled (events, notes, or tasks)",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
================================================
FILE: docs/api/v1/calendars/delete.md
================================================
# Delete User Calendar
Deletes a specific calendar for a specific user.
**URL** : `/api/v1/calendars/:user_id/:calendar_id`
**Method** : `DELETE`
**Auth required** : YES
**Params constraints**
```
:user_id -> "[user id as an int]",
:calendar_id -> "[numeric id of a calendar owned by the user]",
```
**URL example**
```
/api/v1/calendars/jdoe/1
```
## Success Response
**Code** : `200 OK`
**Content examples**
```json
{
"status": "success",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
## Error Response
**Condition** : If 'X-Davis-API-Token' is not present or mismatched in headers.
**Code** : `401 UNAUTHORIZED`
**Content** :
```json
{
"message": "No API token provided",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
or
```json
{
"message": "Invalid API token",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
**Condition** : If user is not found.
**Code** : `404 NOT FOUND`
**Content** :
```json
{
"status": "error",
"message": "User Not Found",
"timestamp": "2026-01-23T15:01:33+01:00"
}
```
**Condition** : If ':calendar_id' is not owned by the specified ':username' or calendar instance is not found.
**Code** : `400 BAD REQUEST`
**Content** :
```jso
gitextract_80g_njsc/
├── .dockerignore
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ └── feature_request.yml
│ └── workflows/
│ ├── ci.yml
│ └── main.yml
├── .gitignore
├── .hadolint.yaml
├── .php-cs-fixer.php
├── LICENSE
├── README.md
├── bin/
│ ├── console
│ └── phpunit
├── composer.json
├── config/
│ ├── bundles.php
│ ├── packages/
│ │ ├── cache.yaml
│ │ ├── dev/
│ │ │ ├── debug.yaml
│ │ │ ├── monolog.yaml
│ │ │ └── web_profiler.yaml
│ │ ├── doctrine.yaml
│ │ ├── doctrine_migrations.yaml
│ │ ├── framework.yaml
│ │ ├── mailer.yaml
│ │ ├── prod/
│ │ │ ├── deprecations.yaml
│ │ │ ├── doctrine.yaml
│ │ │ ├── monolog.yaml
│ │ │ └── routing.yaml
│ │ ├── routing.yaml
│ │ ├── security.yaml
│ │ ├── test/
│ │ │ ├── framework.yaml
│ │ │ ├── monolog.yaml
│ │ │ ├── twig.yaml
│ │ │ ├── validator.yaml
│ │ │ └── web_profiler.yaml
│ │ ├── translation.yaml
│ │ ├── twig.yaml
│ │ └── validator.yaml
│ ├── reference.php
│ ├── routes/
│ │ ├── attributes.yaml
│ │ └── dev/
│ │ ├── framework.yaml
│ │ └── web_profiler.yaml
│ ├── routes.yaml
│ └── services.yaml
├── docker/
│ ├── Dockerfile
│ ├── Dockerfile-standalone
│ ├── configurations/
│ │ ├── Caddyfile
│ │ ├── nginx.conf
│ │ ├── opcache.ini
│ │ └── supervisord.conf
│ ├── docker-compose-postgresql.yml
│ ├── docker-compose-sqlite.yml
│ ├── docker-compose-standalone.yml
│ └── docker-compose.yml
├── docs/
│ └── api/
│ ├── README.md
│ └── v1/
│ ├── calendars/
│ │ ├── all.md
│ │ ├── create.md
│ │ ├── delete.md
│ │ ├── details.md
│ │ ├── edit.md
│ │ ├── share_add.md
│ │ ├── share_remove.md
│ │ └── shares.md
│ ├── health.md
│ └── users/
│ ├── all.md
│ └── details.md
├── migrations/
│ ├── Version20191030113307.php
│ ├── Version20191113170650.php
│ ├── Version20191125093508.php
│ ├── Version20191202091507.php
│ ├── Version20191203111729.php
│ ├── Version20210928132307.php
│ ├── Version20221106220411.php
│ ├── Version20221106220412.php
│ ├── Version20221211154443.php
│ ├── Version20230209142217.php
│ ├── Version20231001214111.php
│ ├── Version20231001214112.php
│ ├── Version20231001214113.php
│ ├── Version20231229203515.php
│ ├── Version20250409193948.php
│ ├── Version20250421163214.php
│ └── Version20260131161930.php
├── phpunit.xml.dist
├── public/
│ ├── .htaccess
│ ├── css/
│ │ └── style.css
│ ├── index.php
│ ├── js/
│ │ ├── app.js
│ │ └── color.mode.toggler.js
│ ├── robots.txt
│ └── site.webmanifest
├── src/
│ ├── Command/
│ │ ├── ApiGenerateCommand.php
│ │ └── SyncBirthdayCalendars.php
│ ├── Constants.php
│ ├── Controller/
│ │ ├── Admin/
│ │ │ ├── AddressBookController.php
│ │ │ ├── CalendarController.php
│ │ │ ├── DashboardController.php
│ │ │ └── UserController.php
│ │ ├── Api/
│ │ │ └── ApiController.php
│ │ ├── DAVController.php
│ │ └── SecurityController.php
│ ├── DataFixtures/
│ │ └── AppFixtures.php
│ ├── Entity/
│ │ ├── AddressBook.php
│ │ ├── AddressBookChange.php
│ │ ├── Calendar.php
│ │ ├── CalendarChange.php
│ │ ├── CalendarInstance.php
│ │ ├── CalendarObject.php
│ │ ├── CalendarSubscription.php
│ │ ├── Card.php
│ │ ├── Lock.php
│ │ ├── Principal.php
│ │ ├── PropertyStorage.php
│ │ ├── SchedulingObject.php
│ │ └── User.php
│ ├── Form/
│ │ ├── AddressBookType.php
│ │ ├── CalendarInstanceType.php
│ │ └── UserType.php
│ ├── Kernel.php
│ ├── Logging/
│ │ └── Monolog/
│ │ └── PasswordFilterProcessor.php
│ ├── Plugins/
│ │ ├── BirthdayCalendarPlugin.php
│ │ ├── DavisIMipPlugin.php
│ │ └── PublicAwareDAVACLPlugin.php
│ ├── Repository/
│ │ ├── CalendarInstanceRepository.php
│ │ └── PrincipalRepository.php
│ ├── Security/
│ │ ├── AdminUser.php
│ │ ├── AdminUserProvider.php
│ │ ├── ApiKeyAuthenticator.php
│ │ └── LoginFormAuthenticator.php
│ ├── Services/
│ │ ├── BasicAuth.php
│ │ ├── BirthdayService.php
│ │ ├── IMAPAuth.php
│ │ ├── LDAPAuth.php
│ │ └── Utils.php
│ └── Version.php
├── templates/
│ ├── _partials/
│ │ ├── add_delegate_modal.html.twig
│ │ ├── back_button.html.twig
│ │ ├── delegate_row.html.twig
│ │ ├── delete_modal.html.twig
│ │ ├── flashes.html.twig
│ │ ├── navigation.html.twig
│ │ └── share_modal.html.twig
│ ├── addressbooks/
│ │ ├── edit.html.twig
│ │ └── index.html.twig
│ ├── base.html.twig
│ ├── calendars/
│ │ ├── edit.html.twig
│ │ └── index.html.twig
│ ├── dashboard.html.twig
│ ├── index.html.twig
│ ├── mails/
│ │ ├── scheduling.html.twig
│ │ └── scheduling.txt.twig
│ ├── security/
│ │ └── login.html.twig
│ └── users/
│ ├── delegates.html.twig
│ ├── edit.html.twig
│ └── index.html.twig
├── tests/
│ ├── .gitignore
│ ├── Functional/
│ │ ├── Commands/
│ │ │ └── SyncBirthdayCalendarTest.php
│ │ ├── Controllers/
│ │ │ ├── AddressBookControllerTest.php
│ │ │ ├── ApiControllerTest.php
│ │ │ ├── CalendarControllerTest.php
│ │ │ ├── DashboardTest.php
│ │ │ └── UserControllerTest.php
│ │ ├── DavTest.php
│ │ └── Service/
│ │ └── BirthdayServiceTest.php
│ └── bootstrap.php
└── translations/
├── .gitignore
├── messages+intl-icu.de.xlf
├── messages+intl-icu.en.xlf
├── messages+intl-icu.fr.xliff
├── security.de.xlf
├── security.en.xlf
├── security.fr.xlf
├── validators.de.xlf
├── validators.en.xlf
└── validators.fr.xlf
SYMBOL INDEX (522 symbols across 70 files)
FILE: config/reference.php
class App (line 1525) | final class App
method config (line 1532) | public static function config(array $config): array
class Routes (line 1604) | final class Routes
method config (line 1611) | public static function config(array $config): array
FILE: migrations/Version20191030113307.php
class Version20191030113307 (line 13) | final class Version20191030113307 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 44) | public function down(Schema $schema): void
FILE: migrations/Version20191113170650.php
class Version20191113170650 (line 13) | final class Version20191113170650 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 30) | public function down(Schema $schema): void
FILE: migrations/Version20191125093508.php
class Version20191125093508 (line 13) | final class Version20191125093508 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 27) | public function down(Schema $schema): void
FILE: migrations/Version20191202091507.php
class Version20191202091507 (line 13) | final class Version20191202091507 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 27) | public function down(Schema $schema): void
FILE: migrations/Version20191203111729.php
class Version20191203111729 (line 13) | final class Version20191203111729 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 27) | public function down(Schema $schema): void
FILE: migrations/Version20210928132307.php
class Version20210928132307 (line 13) | final class Version20210928132307 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 27) | public function down(Schema $schema): void
FILE: migrations/Version20221106220411.php
class Version20221106220411 (line 13) | final class Version20221106220411 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 40) | public function down(Schema $schema): void
FILE: migrations/Version20221106220412.php
class Version20221106220412 (line 13) | final class Version20221106220412 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 69) | public function down(Schema $schema): void
FILE: migrations/Version20221211154443.php
class Version20221211154443 (line 13) | final class Version20221211154443 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 49) | public function down(Schema $schema): void
FILE: migrations/Version20230209142217.php
class Version20230209142217 (line 13) | final class Version20230209142217 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 41) | public function down(Schema $schema): void
FILE: migrations/Version20231001214111.php
class Version20231001214111 (line 13) | final class Version20231001214111 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 29) | public function down(Schema $schema): void
FILE: migrations/Version20231001214112.php
class Version20231001214112 (line 13) | final class Version20231001214112 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 29) | public function down(Schema $schema): void
FILE: migrations/Version20231001214113.php
class Version20231001214113 (line 13) | final class Version20231001214113 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 43) | public function down(Schema $schema): void
FILE: migrations/Version20231229203515.php
class Version20231229203515 (line 15) | final class Version20231229203515 extends AbstractMigration
method getDescription (line 17) | public function getDescription(): string
method up (line 22) | public function up(Schema $schema): void
method down (line 31) | public function down(Schema $schema): void
FILE: migrations/Version20250409193948.php
class Version20250409193948 (line 13) | final class Version20250409193948 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 43) | public function down(Schema $schema): void
FILE: migrations/Version20250421163214.php
class Version20250421163214 (line 13) | final class Version20250421163214 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 33) | public function down(Schema $schema): void
FILE: migrations/Version20260131161930.php
class Version20260131161930 (line 13) | final class Version20260131161930 extends AbstractMigration
method getDescription (line 15) | public function getDescription(): string
method up (line 20) | public function up(Schema $schema): void
method down (line 41) | public function down(Schema $schema): void
FILE: src/Command/ApiGenerateCommand.php
class ApiGenerateCommand (line 11) | #[AsCommand(
method __construct (line 18) | public function __construct()
method configure (line 23) | protected function configure(): void
method execute (line 32) | protected function execute(InputInterface $input, OutputInterface $out...
FILE: src/Command/SyncBirthdayCalendars.php
class SyncBirthdayCalendars (line 15) | class SyncBirthdayCalendars extends Command
method __construct (line 17) | public function __construct(
method configure (line 28) | protected function configure(): void
method execute (line 38) | protected function execute(InputInterface $input, OutputInterface $out...
FILE: src/Constants.php
class Constants (line 7) | class Constants
FILE: src/Controller/Admin/AddressBookController.php
class AddressBookController (line 17) | #[Route('/addressbooks', name: 'addressbook_')]
method addressBooks (line 20) | #[Route('/{userId}', name: 'index')]
method addressbookCreate (line 41) | #[Route('/{userId}/new', name: 'create')]
method addressbookDelete (line 109) | #[Route('/{userId}/delete/{id}', name: 'delete', requirements: ['id' =...
FILE: src/Controller/Admin/CalendarController.php
class CalendarController (line 22) | #[Route('/calendars', name: 'calendar_')]
method calendars (line 25) | #[Route('/{userId}', name: 'index')]
method calendarEdit (line 78) | #[Route('/{userId}/new', name: 'create')]
method calendarShares (line 173) | #[Route('/{userId}/shares/{calendarid}', name: 'shares', requirements:...
method calendarShareAdd (line 193) | #[Route('/{userId}/share/{instanceid}', name: 'share_add', requirement...
method calendarDelete (line 239) | #[Route('/{userId}/delete/{id}', name: 'delete', requirements: ['id' =...
method calendarRevoke (line 285) | #[Route('/{userId}/revoke/{id}', name: 'revoke', requirements: ['id' =...
FILE: src/Controller/Admin/DashboardController.php
class DashboardController (line 15) | class DashboardController extends AbstractController
method dashboard (line 17) | #[Route('/dashboard', name: 'dashboard')]
FILE: src/Controller/Admin/UserController.php
class UserController (line 22) | #[Route('/users', name: 'user_')]
method users (line 25) | #[Route('/', name: 'index')]
method userCreate (line 35) | #[Route('/new', name: 'create')]
method userDelete (line 127) | #[Route('/delete/{userId}', name: 'delete')]
method userDelegates (line 195) | #[Route('/delegates/{userId}', name: 'delegates')]
method userToggleDelegation (line 223) | #[Route('/delegation/{userId}/{toggle}', name: 'delegation_toggle', re...
method userDelegateAdd (line 267) | #[Route('/delegates/{userId}/add', name: 'delegate_add')]
method userDelegateRemove (line 312) | #[Route('/delegates/{userId}/remove/{principalProxyId}/{delegateId}', ...
FILE: src/Controller/Api/ApiController.php
class ApiController (line 18) | #[Route('/api/v1', name: 'api_v1_')]
method validateUsername (line 28) | private function validateUsername(string $username): bool
method getTimestamp (line 38) | private function getTimestamp(): string
method resolveUser (line 48) | private function resolveUser(ManagerRegistry $doctrine, int $userId): ...
method healthCheck (line 60) | #[Route('/health', name: 'health', methods: ['GET'])]
method getUsers (line 73) | #[Route('/users', name: 'users', methods: ['GET'])]
method getUserDetails (line 106) | #[Route('/users/{userId}', name: 'user_detail', methods: ['GET'], requ...
method getUserCalendars (line 146) | #[Route('/calendars/{userId}', name: 'calendars', methods: ['GET'], re...
method getUserCalendarDetails (line 225) | #[Route('/calendars/{userId}/{calendar_id}', name: 'calendar_details',...
method createNewUserCalendar (line 283) | #[Route('/calendars/{userId}/create', name: 'calendar_create', methods...
method editUserCalendar (line 386) | #[Route('/calendars/{userId}/{calendar_id}', name: 'calendar_edit', me...
method deleteUserCalendar (line 473) | #[Route('/calendars/{userId}/{calendar_id}', name: 'calendar_delete', ...
method getUserCalendarsShares (line 542) | #[Route('/calendars/{userId}/shares/{calendar_id}', name: 'calendars_s...
method setUserCalendarsShare (line 603) | #[Route('/calendars/{userId}/share/{calendar_id}/add', name: 'calendar...
method removeUserCalendarsShare (line 681) | #[Route('/calendars/{userId}/share/{calendar_id}/remove', name: 'calen...
FILE: src/Controller/DAVController.php
class DAVController (line 25) | class DAVController extends AbstractController
method __construct (line 152) | public function __construct(MailerInterface $mailer, BasicAuth $basicA...
method home (line 180) | #[Route('/', name: 'home')]
method initServer (line 188) | private function initServer(string $authMethod, string $authRealm = Us...
method initExceptionListener (line 300) | private function initExceptionListener()
method dav (line 315) | #[Route('/dav/{path}', name: 'dav', requirements: ['path' => '.*'])]
FILE: src/Controller/SecurityController.php
class SecurityController (line 10) | class SecurityController extends AbstractController
method login (line 12) | #[Route('/login', name: 'app_login')]
method logout (line 27) | #[Route('/logout', name: 'app_logout')]
FILE: src/DataFixtures/AppFixtures.php
class AppFixtures (line 13) | class AppFixtures extends Fixture
method load (line 15) | public function load(ObjectManager $manager): void
FILE: src/Entity/AddressBook.php
class AddressBook (line 11) | #[ORM\Entity()]
method __construct (line 46) | public function __construct()
method getId (line 54) | public function getId(): ?int
method getPrincipalUri (line 59) | public function getPrincipalUri(): ?string
method setPrincipalUri (line 64) | public function setPrincipalUri(string $principalUri): self
method getDisplayName (line 71) | public function getDisplayName(): ?string
method setDisplayName (line 76) | public function setDisplayName(string $displayName): self
method isIncludedInBirthdayCalendar (line 83) | public function isIncludedInBirthdayCalendar(): ?bool
method setIncludedInBirthdayCalendar (line 88) | public function setIncludedInBirthdayCalendar(bool $includedInBirthday...
method getUri (line 95) | public function getUri(): ?string
method setUri (line 100) | public function setUri(string $uri): self
method getDescription (line 107) | public function getDescription(): ?string
method setDescription (line 112) | public function setDescription(string $description): self
method getSynctoken (line 119) | public function getSynctoken(): ?string
method setSynctoken (line 124) | public function setSynctoken(string $synctoken): self
method getCards (line 134) | public function getCards(): Collection
method addCard (line 139) | public function addCard(Card $card): self
method removeCard (line 149) | public function removeCard(Card $card): self
method getChanges (line 165) | public function getChanges(): Collection
method addChange (line 170) | public function addChange(AddressBookChange $change): self
method removeChange (line 180) | public function removeChange(AddressBookChange $change): self
FILE: src/Entity/AddressBookChange.php
class AddressBookChange (line 7) | #[ORM\Entity()]
method getId (line 29) | public function getId(): ?int
method getUri (line 34) | public function getUri(): ?string
method setUri (line 39) | public function setUri(string $uri): self
method getSynctoken (line 46) | public function getSynctoken(): ?string
method setSynctoken (line 51) | public function setSynctoken(string $synctoken): self
method getAddressBook (line 58) | public function getAddressBook(): ?AddressBook
method setAddressBook (line 63) | public function setAddressBook(?AddressBook $addressBook): self
method getOperation (line 70) | public function getOperation(): ?int
method setOperation (line 75) | public function setOperation(int $operation): self
FILE: src/Entity/Calendar.php
class Calendar (line 9) | #[ORM\Entity()]
method __construct (line 37) | public function __construct()
method getId (line 45) | public function getId(): ?int
method getSynctoken (line 50) | public function getSynctoken(): ?string
method setSynctoken (line 55) | public function setSynctoken(string $synctoken): self
method getComponents (line 62) | public function getComponents(): ?string
method setComponents (line 67) | public function setComponents(?string $components): self
method getObjects (line 77) | public function getObjects(): Collection
method addObject (line 82) | public function addObject(CalendarObject $object): self
method removeObject (line 92) | public function removeObject(CalendarObject $object): self
method getChanges (line 108) | public function getChanges(): Collection
method addChange (line 113) | public function addChange(CalendarChange $change): self
method removeChange (line 123) | public function removeChange(CalendarChange $change): self
method getInstances (line 139) | public function getInstances(): Collection
method isComponentEnabled (line 151) | public function isComponentEnabled(string $componentType): bool
FILE: src/Entity/CalendarChange.php
class CalendarChange (line 7) | #[ORM\Entity()]
method getId (line 29) | public function getId(): ?int
method getUri (line 34) | public function getUri(): ?string
method setUri (line 39) | public function setUri(string $uri): self
method getSynctoken (line 46) | public function getSynctoken(): ?int
method setSynctoken (line 51) | public function setSynctoken(int $synctoken): self
method getCalendar (line 58) | public function getCalendar(): ?Calendar
method setCalendar (line 63) | public function setCalendar(?Calendar $calendar): self
method getOperation (line 70) | public function getOperation(): ?int
method setOperation (line 75) | public function setOperation(int $operation): self
FILE: src/Entity/CalendarInstance.php
class CalendarInstance (line 11) | #[ORM\Entity(repositoryClass: "App\Repository\CalendarInstanceRepository")]
method getOwnerAccesses (line 16) | public static function getOwnerAccesses(): array
method __construct (line 74) | public function __construct()
method getId (line 83) | public function getId(): ?int
method getCalendar (line 88) | public function getCalendar(): ?Calendar
method setCalendar (line 93) | public function setCalendar(?Calendar $calendar): self
method getPrincipalUri (line 100) | public function getPrincipalUri(): ?string
method setPrincipalUri (line 105) | public function setPrincipalUri(?string $principalUri): self
method getAccess (line 112) | public function getAccess(): ?int
method setAccess (line 117) | public function setAccess(int $access): self
method isShared (line 124) | public function isShared(): bool
method setPublic (line 129) | public function setPublic(bool $public): self
method isPublic (line 136) | public function isPublic(): bool
method isAutomaticallyGenerated (line 141) | public function isAutomaticallyGenerated(): bool
method getDisplayName (line 146) | public function getDisplayName(): ?string
method setDisplayName (line 151) | public function setDisplayName(?string $displayName): self
method getUri (line 158) | public function getUri(): ?string
method setUri (line 163) | public function setUri(?string $uri): self
method getDescription (line 170) | public function getDescription(): ?string
method setDescription (line 175) | public function setDescription(?string $description): self
method getCalendarOrder (line 182) | public function getCalendarOrder(): ?int
method setCalendarOrder (line 187) | public function setCalendarOrder(int $calendarOrder): self
method getCalendarColor (line 194) | public function getCalendarColor(): ?string
method setCalendarColor (line 199) | public function setCalendarColor(?string $calendarColor): self
method getTimezone (line 206) | public function getTimezone(): ?string
method setTimezone (line 211) | public function setTimezone(?string $timezone): self
method getTransparent (line 218) | public function getTransparent(): ?int
method setTransparent (line 223) | public function setTransparent(?int $transparent): self
method getShareHref (line 230) | public function getShareHref(): ?string
method setShareHref (line 235) | public function setShareHref(?string $shareHref): self
method getShareDisplayName (line 242) | public function getShareDisplayName(): ?string
method setShareDisplayName (line 247) | public function setShareDisplayName(?string $shareDisplayName): self
method getShareInviteStatus (line 254) | public function getShareInviteStatus(): ?int
method setShareInviteStatus (line 259) | public function setShareInviteStatus(int $shareInviteStatus): self
FILE: src/Entity/CalendarObject.php
class CalendarObject (line 7) | #[ORM\Entity()]
method getId (line 50) | public function getId(): ?int
method getCalendarData (line 55) | public function getCalendarData(): ?string
method setCalendarData (line 60) | public function setCalendarData(?string $calendarData): self
method getUri (line 67) | public function getUri(): ?string
method setUri (line 72) | public function setUri(?string $uri): self
method getCalendar (line 79) | public function getCalendar(): ?Calendar
method setCalendar (line 84) | public function setCalendar(?Calendar $calendar): self
method getLastModifier (line 91) | public function getLastModifier(): ?int
method setLastModifier (line 96) | public function setLastModifier(?int $lastModifier): self
method getEtag (line 103) | public function getEtag(): ?string
method setEtag (line 108) | public function setEtag(?string $etag): self
method getSize (line 115) | public function getSize(): ?int
method setSize (line 120) | public function setSize(int $size): self
method getComponentType (line 127) | public function getComponentType(): ?string
method setComponentType (line 132) | public function setComponentType(?string $componentType): self
method getFirstOccurence (line 139) | public function getFirstOccurence(): ?int
method setFirstOccurence (line 144) | public function setFirstOccurence(?int $firstOccurence): self
method getLastOccurence (line 151) | public function getLastOccurence(): ?int
method setLastOccurence (line 156) | public function setLastOccurence(?int $lastOccurence): self
method getUid (line 163) | public function getUid(): ?string
method setUid (line 168) | public function setUid(?string $uid): self
method getLastModified (line 175) | public function getLastModified(): ?int
method setLastModified (line 180) | public function setLastModified(?int $lastModified): self
FILE: src/Entity/CalendarSubscription.php
class CalendarSubscription (line 7) | #[ORM\Entity()]
method getId (line 49) | public function getId(): ?int
method getUri (line 54) | public function getUri(): ?string
method setUri (line 59) | public function setUri(string $uri): self
method getPrincipalUri (line 66) | public function getPrincipalUri(): ?string
method setPrincipalUri (line 71) | public function setPrincipalUri(string $principalUri): self
method getSource (line 78) | public function getSource(): ?string
method setSource (line 83) | public function setSource(?string $source): self
method getDisplayName (line 90) | public function getDisplayName(): ?string
method setDisplayName (line 95) | public function setDisplayName(?string $displayName): self
method getRefreshRate (line 102) | public function getRefreshRate(): ?string
method setRefreshRate (line 107) | public function setRefreshRate(?string $refreshRate): self
method getCalendarOrder (line 114) | public function getCalendarOrder(): ?int
method setCalendarOrder (line 119) | public function setCalendarOrder(int $calendarOrder): self
method getCalendarColor (line 126) | public function getCalendarColor(): ?string
method setCalendarColor (line 131) | public function setCalendarColor(?string $calendarColor): self
method getStripTodos (line 138) | public function getStripTodos(): ?int
method setStripTodos (line 143) | public function setStripTodos(?int $stripTodos): self
method getStripAlarms (line 150) | public function getStripAlarms(): ?int
method setStripAlarms (line 155) | public function setStripAlarms(?int $stripAlarms): self
method getStripAttachments (line 162) | public function getStripAttachments(): ?int
method setStripAttachments (line 167) | public function setStripAttachments(?int $stripAttachments): self
method getLastModified (line 174) | public function getLastModified(): ?int
method setLastModified (line 179) | public function setLastModified(?int $lastModified): self
FILE: src/Entity/Card.php
class Card (line 7) | #[ORM\Entity()]
method getId (line 38) | public function getId(): ?int
method getAddressBook (line 43) | public function getAddressBook(): ?AddressBook
method setAddressBook (line 48) | public function setAddressBook(?AddressBook $addressBook): self
method getCardData (line 55) | public function getCardData(): ?string
method setCardData (line 60) | public function setCardData(?string $cardData): self
method getUri (line 67) | public function getUri(): ?string
method setUri (line 72) | public function setUri(?string $uri): self
method getLastModified (line 79) | public function getLastModified(): ?int
method setLastModified (line 84) | public function setLastModified(?int $lastModified): self
method getEtag (line 91) | public function getEtag(): ?string
method setEtag (line 96) | public function setEtag(?string $etag): self
method getSize (line 103) | public function getSize(): ?int
method setSize (line 108) | public function setSize(int $size): self
FILE: src/Entity/Lock.php
class Lock (line 7) | #[ORM\Entity()]
method getId (line 37) | public function getId(): ?int
method getOwner (line 42) | public function getOwner(): ?string
method setOwner (line 47) | public function setOwner(?string $owner): self
method getTimeout (line 54) | public function getTimeout(): ?int
method setTimeout (line 59) | public function setTimeout(?int $timeout): self
method getCreated (line 66) | public function getCreated(): ?int
method setCreated (line 71) | public function setCreated(?int $created): self
method getToken (line 78) | public function getToken(): ?string
method setToken (line 83) | public function setToken(?string $token): self
method getScope (line 90) | public function getScope(): ?int
method setScope (line 95) | public function setScope(?int $scope): self
method getDepth (line 102) | public function getDepth(): ?int
method setDepth (line 107) | public function setDepth(?int $depth): self
method getUri (line 114) | public function getUri(): ?string
method setUri (line 119) | public function setUri(?string $uri): self
FILE: src/Entity/Principal.php
class Principal (line 11) | #[ORM\Entity(repositoryClass: "App\Repository\PrincipalRepository")]
method __construct (line 53) | public function __construct()
method getId (line 60) | public function getId(): ?int
method getUri (line 65) | public function getUri(): ?string
method setUri (line 70) | public function setUri(string $uri): self
method getUsername (line 77) | public function getUsername(): ?string
method getEmail (line 82) | public function getEmail(): ?string
method setEmail (line 87) | public function setEmail(?string $email): self
method getDisplayName (line 94) | public function getDisplayName(): ?string
method setDisplayName (line 99) | public function setDisplayName(?string $displayName): self
method getDelegees (line 109) | public function getDelegees(): Collection
method addDelegee (line 114) | public function addDelegee(Principal $delegee): self
method removeDelegee (line 123) | public function removeDelegee(Principal $delegee): self
method removeAllDelegees (line 132) | public function removeAllDelegees(): self
method getIsMain (line 139) | public function getIsMain(): ?bool
method setIsMain (line 144) | public function setIsMain(bool $isMain): self
method getIsAdmin (line 151) | public function getIsAdmin(): ?bool
method setIsAdmin (line 156) | public function setIsAdmin(bool $isAdmin): self
FILE: src/Entity/PropertyStorage.php
class PropertyStorage (line 7) | #[ORM\Entity()]
method getId (line 28) | public function getId(): ?int
method getPath (line 33) | public function getPath(): ?string
method setPath (line 38) | public function setPath(string $path): self
method getName (line 45) | public function getName(): ?string
method setName (line 50) | public function setName(string $name): self
method getValueType (line 57) | public function getValueType(): ?int
method setValueType (line 62) | public function setValueType(?int $valueType): self
method getValue (line 69) | public function getValue(): ?string
method setValue (line 74) | public function setValue(?string $value): self
FILE: src/Entity/SchedulingObject.php
class SchedulingObject (line 8) | #[ORM\Entity()]
method getId (line 39) | public function getId(): ?int
method getPrincipalUri (line 44) | public function getPrincipalUri(): ?string
method setPrincipalUri (line 49) | public function setPrincipalUri(?string $principalUri): self
method getCalendarData (line 56) | public function getCalendarData(): ?string
method setCalendarData (line 61) | public function setCalendarData(?string $calendarData): self
method getUri (line 68) | public function getUri(): ?string
method setUri (line 73) | public function setUri(?string $uri): self
method getLastModified (line 80) | public function getLastModified(): ?int
method setLastModified (line 85) | public function setLastModified(?int $lastModified): self
method getEtag (line 92) | public function getEtag(): ?string
method setEtag (line 97) | public function setEtag(?string $etag): self
method getSize (line 104) | public function getSize(): ?int
method setSize (line 109) | public function setSize(int $size): self
FILE: src/Entity/User.php
class User (line 9) | #[ORM\Entity()]
method getId (line 28) | public function getId(): ?int
method getUsername (line 33) | public function getUsername(): ?string
method setUsername (line 38) | public function setUsername(string $username): self
method getPassword (line 45) | public function getPassword(): ?string
method setPassword (line 52) | public function setPassword(?string $password): self
FILE: src/Form/AddressBookType.php
class AddressBookType (line 15) | class AddressBookType extends AbstractType
method buildForm (line 17) | public function buildForm(FormBuilderInterface $builder, array $option...
method configureOptions (line 51) | public function configureOptions(OptionsResolver $resolver): void
FILE: src/Form/CalendarInstanceType.php
class CalendarInstanceType (line 16) | class CalendarInstanceType extends AbstractType
method buildForm (line 18) | public function buildForm(FormBuilderInterface $builder, array $option...
method configureOptions (line 81) | public function configureOptions(OptionsResolver $resolver): void
FILE: src/Form/UserType.php
class UserType (line 16) | class UserType extends AbstractType
method buildForm (line 18) | public function buildForm(FormBuilderInterface $builder, array $option...
method configureOptions (line 53) | public function configureOptions(OptionsResolver $resolver): void
FILE: src/Kernel.php
class Kernel (line 8) | class Kernel extends BaseKernel
method boot (line 12) | public function boot(): void
FILE: src/Logging/Monolog/PasswordFilterProcessor.php
class PasswordFilterProcessor (line 8) | final class PasswordFilterProcessor implements ProcessorInterface
method redactContextRecursive (line 14) | public static function redactContextRecursive(array $context): array
method __invoke (line 30) | public function __invoke(LogRecord $record): LogRecord
FILE: src/Plugins/BirthdayCalendarPlugin.php
class BirthdayCalendarPlugin (line 10) | class BirthdayCalendarPlugin extends DAV\ServerPlugin
method __construct (line 22) | public function __construct(BirthdayService $birthdayService, Calendar...
method initialize (line 28) | public function initialize(DAV\Server $server)
method afterCardCreate (line 44) | public function afterCardCreate(string $path, DAV\ICollection $parentN...
method afterCardUpdate (line 52) | public function afterCardUpdate(string $path, DAV\IFile $node): void
method beforeCardDelete (line 67) | public function beforeCardDelete(string $path): void
method handleCardChange (line 87) | private function handleCardChange(string $path, CardDAV\AddressBook $p...
method getPluginName (line 96) | public function getPluginName(): string
FILE: src/Plugins/DavisIMipPlugin.php
class DavisIMipPlugin (line 18) | final class DavisIMipPlugin extends SabreBaseIMipPlugin
method __construct (line 40) | public function __construct(MailerInterface $mailer, string $senderEma...
method schedule (line 50) | public function schedule(ITip\Message $itip)
method getPluginInfo (line 266) | public function getPluginInfo()
FILE: src/Plugins/PublicAwareDAVACLPlugin.php
class PublicAwareDAVACLPlugin (line 10) | class PublicAwareDAVACLPlugin extends \Sabre\DAVACL\Plugin
method __construct (line 22) | public function __construct(EntityManagerInterface $entityManager, boo...
method beforeMethod (line 32) | public function beforeMethod(RequestInterface $request, ResponseInterf...
method getAcl (line 42) | public function getAcl($node): array
FILE: src/Repository/CalendarInstanceRepository.php
class CalendarInstanceRepository (line 19) | class CalendarInstanceRepository extends ServiceEntityRepository
method __construct (line 21) | public function __construct(ManagerRegistry $registry)
method findSharedInstancesOfInstance (line 29) | public function findSharedInstancesOfInstance(int $calendarId, bool $w...
method findSharedInstanceOfInstanceFor (line 53) | public function findSharedInstanceOfInstanceFor(int $calendarId, strin...
method hasDifferentOwner (line 66) | public function hasDifferentOwner(int $calendarId, string $principalUr...
method findAllSchedulingObjectsForCalendar (line 81) | public function findAllSchedulingObjectsForCalendar(int $calendarInsta...
method getObjectCountsByComponentType (line 104) | public function getObjectCountsByComponentType(int $calendarId): array
FILE: src/Repository/PrincipalRepository.php
class PrincipalRepository (line 15) | class PrincipalRepository extends ServiceEntityRepository
method __construct (line 17) | public function __construct(ManagerRegistry $registry)
method findAllExceptPrincipal (line 25) | public function findAllExceptPrincipal(string $principalUri)
method findAllMainPrincipalsWithUserIds (line 39) | public function findAllMainPrincipalsWithUserIds(): array
FILE: src/Security/AdminUser.php
class AdminUser (line 8) | class AdminUser implements UserInterface, PasswordAuthenticatedUserInter...
method __construct (line 13) | public function __construct(string $username, string $password)
method getRoles (line 22) | public function getRoles(): array
method getPassword (line 30) | public function getPassword(): string
method getSalt (line 42) | public function getSalt()
method getUsername (line 52) | public function getUsername()
method getUserIdentifier (line 57) | public function getUserIdentifier(): string
method eraseCredentials (line 68) | public function eraseCredentials(): void
FILE: src/Security/AdminUserProvider.php
class AdminUserProvider (line 10) | class AdminUserProvider implements UserProviderInterface
method loadUserByUsername (line 23) | public function loadUserByUsername($username)
method loadUserByIdentifier (line 28) | public function loadUserByIdentifier(string $identifier): UserInterface
method refreshUser (line 44) | public function refreshUser(UserInterface $user): UserInterface
method supportsClass (line 56) | public function supportsClass($class): bool
FILE: src/Security/ApiKeyAuthenticator.php
class ApiKeyAuthenticator (line 16) | class ApiKeyAuthenticator extends AbstractAuthenticator
method __construct (line 20) | public function __construct(string $apiKey)
method supports (line 30) | public function supports(Request $request): ?bool
method authenticate (line 42) | public function authenticate(Request $request): Passport
method onAuthenticationSuccess (line 56) | public function onAuthenticationSuccess(Request $request, TokenInterfa...
method onAuthenticationFailure (line 61) | public function onAuthenticationFailure(Request $request, Authenticati...
FILE: src/Security/LoginFormAuthenticator.php
class LoginFormAuthenticator (line 20) | class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
method __construct (line 30) | public function __construct(UrlGeneratorInterface $urlGenerator, CsrfT...
method getLoginUrl (line 38) | protected function getLoginUrl(Request $request): string
method supports (line 43) | public function supports(Request $request): bool
method authenticate (line 49) | public function authenticate(Request $request): Passport
method onAuthenticationSuccess (line 77) | public function onAuthenticationSuccess(Request $request, TokenInterfa...
FILE: src/Services/BasicAuth.php
class BasicAuth (line 9) | final class BasicAuth extends AbstractBasic
method __construct (line 25) | public function __construct(ManagerRegistry $doctrine, Utils $utils)
method validateUserPass (line 31) | protected function validateUserPass($username, $password): bool
FILE: src/Services/BirthdayService.php
class BirthdayService (line 32) | class BirthdayService
method __construct (line 39) | public function __construct(
method setBackend (line 45) | public function setBackend(CalendarBackend $calendarBackend)
method onCardChanged (line 50) | public function onCardChanged(int $addressBookId, string $cardUri, str...
method onCardDeleted (line 64) | public function onCardDeleted(int $addressBookId, string $cardUri): void
method shouldBirthdayCalendarExist (line 87) | public function shouldBirthdayCalendarExist(string $principalUri): bool
method ensureBirthdayCalendarExists (line 96) | public function ensureBirthdayCalendarExists(string $principalUri): Ca...
method deleteBirthdayCalendar (line 126) | public function deleteBirthdayCalendar(string $principalUri): void
method buildDataFromContact (line 144) | public function buildDataFromContact(string $cardData): ?VCalendar
method resetForPrincipal (line 261) | public function resetForPrincipal(string $principal): void
method syncUser (line 279) | public function syncUser(string $username): void
method syncPrincipal (line 284) | public function syncPrincipal(string $principal): void
method birthdayEventChanged (line 308) | public function birthdayEventChanged(string $existingCalendarData, VCa...
method updateCalendar (line 325) | private function updateCalendar(string $cardUri, string $cardData, Add...
FILE: src/Services/IMAPAuth.php
class IMAPAuth (line 11) | final class IMAPAuth extends AbstractBasic
method __construct (line 63) | public function __construct(ManagerRegistry $doctrine, Utils $utils, s...
method imapOpen (line 105) | protected function imapOpen(string $username, string $password): bool
method validateUserPass (line 153) | protected function validateUserPass($username, $password): bool
FILE: src/Services/LDAPAuth.php
class LDAPAuth (line 9) | final class LDAPAuth extends AbstractBasic
method __construct (line 74) | public function __construct(ManagerRegistry $doctrine, Utils $utils, s...
method ldapOpen (line 94) | protected function ldapOpen($username, $password)
method validateUserPass (line 229) | protected function validateUserPass($username, $password): bool
FILE: src/Services/Utils.php
class Utils (line 13) | final class Utils
method __construct (line 36) | public function __construct(ManagerRegistry $doctrine, TranslatorInter...
method hashPassword (line 47) | public function hashPassword(string $username, string $password): string
method createPasswordlessUserWithDefaultObjects (line 52) | public function createPasswordlessUserWithDefaultObjects(string $usern...
FILE: src/Version.php
class Version (line 5) | final class Version
FILE: tests/Functional/Commands/SyncBirthdayCalendarTest.php
class SyncBirthdayCalendarTest (line 21) | class SyncBirthdayCalendarTest extends KernelTestCase
method setUp (line 26) | protected function setUp(): void
method tearDown (line 43) | protected function tearDown(): void
method createUser (line 49) | private function createUser(string $username): User
method createAddressBookWithCard (line 67) | private function createAddressBookWithCard(string $username, string $c...
method assertBirthdayEventExists (line 92) | private function assertBirthdayEventExists(string $principalUri, strin...
method testExecuteSyncsAllUsers (line 109) | public function testExecuteSyncsAllUsers(): void
method testExecuteSyncsSingleUser (line 135) | public function testExecuteSyncsSingleUser(): void
method testExecuteThrowsExceptionForUnknownUser (line 168) | public function testExecuteThrowsExceptionForUnknownUser(): void
method testExecuteWithNoUsersInDatabaseSucceeds (line 176) | public function testExecuteWithNoUsersInDatabaseSucceeds(): void
FILE: tests/Functional/Controllers/AddressBookControllerTest.php
class AddressBookControllerTest (line 10) | class AddressBookControllerTest extends WebTestCase
method getUserId (line 12) | private function getUserId($client, string $username): int
method testAddressBookIndex (line 20) | public function testAddressBookIndex(): void
method testAddressBookEdit (line 39) | public function testAddressBookEdit(): void
method testAddressBookNew (line 66) | public function testAddressBookNew(): void
method testAddressBookDelete (line 98) | public function testAddressBookDelete(): void
FILE: tests/Functional/Controllers/ApiControllerTest.php
class ApiControllerTest (line 7) | class ApiControllerTest extends WebTestCase
method getUserId (line 17) | private function getUserId($client, int $index): int
method getUserUsername (line 43) | private function getUserUsername($client, int $index): string
method getCalendarId (line 70) | private function getCalendarId($client, int $userId, bool $default = t...
method testHealth (line 94) | public function testHealth(): void
method testApiInvalidToken (line 109) | public function testApiInvalidToken(): void
method testApiMissingToken (line 127) | public function testApiMissingToken(): void
method testUserList (line 144) | public function testUserList(): void
method testUserDetails (line 177) | public function testUserDetails(): void
method testUserCalendarsList (line 209) | public function testUserCalendarsList(): void
method testUserCalendarDetails (line 236) | public function testUserCalendarDetails(): void
method testCreateUserCalendar (line 280) | public function testCreateUserCalendar(): void
method testEditUserCalendar (line 334) | public function testEditUserCalendar(): void
method testGetUserCalendarSharesEmpty (line 384) | public function testGetUserCalendarSharesEmpty(): void
method testShareUserCalendar (line 408) | public function testShareUserCalendar(): void
method testUnshareUserCalendar (line 452) | public function testUnshareUserCalendar(): void
method testCreateUserCalendarNoComponents (line 491) | public function testCreateUserCalendarNoComponents(): void
method testEditUserCalendarNoComponents (line 523) | public function testEditUserCalendarNoComponents(): void
method testDeleteUserCalendar (line 555) | public function testDeleteUserCalendar(): void
FILE: tests/Functional/Controllers/CalendarControllerTest.php
class CalendarControllerTest (line 10) | class CalendarControllerTest extends WebTestCase
method getUserId (line 12) | private function getUserId($client, string $username): int
method testCalendarIndex (line 20) | public function testCalendarIndex(): void
method testCalendarEdit (line 39) | public function testCalendarEdit(): void
method testCalendarNew (line 66) | public function testCalendarNew(): void
method testCalendarDelete (line 99) | public function testCalendarDelete(): void
FILE: tests/Functional/Controllers/DashboardTest.php
class DashboardTest (line 7) | class DashboardTest extends WebTestCase
method testIndexPage (line 9) | public function testIndexPage(): void
method testDashboardPageUnlogged (line 22) | public function testDashboardPageUnlogged(): void
method testLoginPage (line 30) | public function testLoginPage(): void
method testLoginIncorrectUsername (line 40) | public function testLoginIncorrectUsername(): void
method testLoginIncorrectPassword (line 57) | public function testLoginIncorrectPassword(): void
method testLoginCorrect (line 74) | public function testLoginCorrect(): void
FILE: tests/Functional/Controllers/UserControllerTest.php
class UserControllerTest (line 9) | class UserControllerTest extends WebTestCase
method getUserId (line 11) | private function getUserId($client, string $username): int
method testUserIndex (line 19) | public function testUserIndex(): void
method testUserEdit (line 35) | public function testUserEdit(): void
method testUserNew (line 59) | public function testUserNew(): void
method testUserDelete (line 91) | public function testUserDelete(): void
method testUserDelegates (line 116) | public function testUserDelegates(): void
FILE: tests/Functional/DavTest.php
class DavTest (line 8) | class DavTest extends WebTestCase
method requestDavClient (line 15) | public static function requestDavClient(string $method, string $path):...
method testUnauthorized (line 26) | public function testUnauthorized(): void
FILE: tests/Functional/Service/BirthdayServiceTest.php
class BirthdayServiceTest (line 18) | class BirthdayServiceTest extends KernelTestCase
method setUp (line 23) | protected function setUp(): void
method tearDown (line 36) | protected function tearDown(): void
method createAddressBook (line 42) | private function createAddressBook(
method testBuildDataFromContactReturnsNullForEmptyData (line 69) | public function testBuildDataFromContactReturnsNullForEmptyData(): void
method testBuildDataFromContactReturnsNullIfNoBday (line 74) | public function testBuildDataFromContactReturnsNullIfNoBday(): void
method testBuildDataFromContactReturnsNullIfNoFn (line 80) | public function testBuildDataFromContactReturnsNullIfNoFn(): void
method testBuildDataFromContactReturnsVCalendarWithBday (line 86) | public function testBuildDataFromContactReturnsVCalendarWithBday(): void
method testBuildDataFromContactHandlesLeapDay (line 98) | public function testBuildDataFromContactHandlesLeapDay(): void
method testBuildDataFromContactHandlesOmitYear (line 107) | public function testBuildDataFromContactHandlesOmitYear(): void
method testBuildDataFromContactAddsAlarm (line 116) | public function testBuildDataFromContactAddsAlarm(): void
method testBirthdayEventChangedReturnsFalseWhenSame (line 128) | public function testBirthdayEventChangedReturnsFalseWhenSame(): void
method testBirthdayEventChangedReturnsTrueWhenDifferentDate (line 135) | public function testBirthdayEventChangedReturnsTrueWhenDifferentDate()...
method testBirthdayEventChangedReturnsTrueWhenDifferentName (line 143) | public function testBirthdayEventChangedReturnsTrueWhenDifferentName()...
method testBirthdayEventChangedReturnsTrueOnInvalidExistingData (line 151) | public function testBirthdayEventChangedReturnsTrueOnInvalidExistingDa...
method testOnCardChangedSkipsIfNotIncludedInBirthdayCalendar (line 162) | public function testOnCardChangedSkipsIfNotIncludedInBirthdayCalendar(...
method testOnCardChangedCreatesCalendarObject (line 178) | public function testOnCardChangedCreatesCalendarObject(): void
method testOnCardChangedUpdatesExistingCalendarObject (line 195) | public function testOnCardChangedUpdatesExistingCalendarObject(): void
method testOnCardChangedDoesNotCreateObjectIfNoBday (line 240) | public function testOnCardChangedDoesNotCreateObjectIfNoBday(): void
method testOnCardDeletedRemovesCalendarObject (line 260) | public function testOnCardDeletedRemovesCalendarObject(): void
method testOnCardDeletedIsNoopIfNoCalendarObject (line 278) | public function testOnCardDeletedIsNoopIfNoCalendarObject(): void
method testEnsureBirthdayCalendarExistsCreatesCalendar (line 292) | public function testEnsureBirthdayCalendarExistsCreatesCalendar(): void
method testEnsureBirthdayCalendarExistsIsIdempotent (line 305) | public function testEnsureBirthdayCalendarExistsIsIdempotent(): void
method testSyncPrincipalDeletesBirthdayCalendarIfNoAddressBooksIncluded (line 322) | public function testSyncPrincipalDeletesBirthdayCalendarIfNoAddressBoo...
Condensed preview — 174 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (805K chars).
[
{
"path": ".dockerignore",
"chars": 165,
"preview": "_screenshots\n.DS_Store\nREADME.md\nLICENSE\n.git\n.gitignore\n.github\n.env.local\n.env.test.local\nphpunit.xml.dist\n.php-cs*\n.p"
},
{
"path": ".github/FUNDING.yml",
"chars": 40,
"preview": "custom: ['https://www.paypal.me/tchap']\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 4352,
"preview": "name: Bug Report\ndescription: Something is not working as expected\nlabels: [\"bug\"]\nbody:\n - type: markdown\n attribut"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yml",
"chars": 1528,
"preview": "name: Feature Request\ndescription: Suggest an improvement or new functionality\nlabels: [\"feature request\"]\nbody:\n - typ"
},
{
"path": ".github/workflows/ci.yml",
"chars": 9071,
"preview": "name: CI\n\non:\n push:\n branches:\n - main # Only run on pushes to main\n pull_request:\n # Run on all PRs (no "
},
{
"path": ".github/workflows/main.yml",
"chars": 6134,
"preview": "name: Publish Docker image\n\non:\n workflow_dispatch:\n release:\n types: [published]\n\nconcurrency:\n group: ${{ github"
},
{
"path": ".gitignore",
"chars": 411,
"preview": "###> symfony/framework-bundle ###\n/.env.local\n/.env.local.php\n/.env.*.local\n/config/secrets/prod/prod.decrypt.private.ph"
},
{
"path": ".hadolint.yaml",
"chars": 130,
"preview": "ignored:\n - DL3018 # We don't pin apk versions (Alpine is rolling)\nfailure-threshold: error # Only fail on errors, no"
},
{
"path": ".php-cs-fixer.php",
"chars": 813,
"preview": "<?php\n\n$finder = (new PhpCsFixer\\Finder())\n ->in(__DIR__)\n ->exclude('var')\n;\n\nreturn (new PhpCsFixer\\Config())\n "
},
{
"path": "LICENSE",
"chars": 1062,
"preview": "MIT License\n\nCopyright (c) 2019 tchap\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
},
{
"path": "README.md",
"chars": 25007,
"preview": "Davis\n---\n\n[![Build Status][ci_badge]][ci_link]\n[ ?: null;\n\nif ($overridenEnvDir) {\n // Tell the Runtime"
},
{
"path": "bin/phpunit",
"chars": 642,
"preview": "#!/usr/bin/env php\n<?php\n\nif (!ini_get('date.timezone')) {\n ini_set('date.timezone', 'UTC');\n}\n\nif (is_file(dirname(_"
},
{
"path": "composer.json",
"chars": 2998,
"preview": "{\n \"name\": \"tchapi/davis\",\n \"description\": \"A simple, fully translatable admin interface and frontend for sabre/da"
},
{
"path": "config/bundles.php",
"chars": 836,
"preview": "<?php\n\nreturn [\n Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle::class => ['all' => true],\n Doctrine\\Bundle\\Doctri"
},
{
"path": "config/packages/cache.yaml",
"chars": 687,
"preview": "framework:\n cache:\n # Unique name of your app: used to compute stable namespaces for cache keys.\n #pref"
},
{
"path": "config/packages/dev/debug.yaml",
"chars": 235,
"preview": "debug:\n # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser."
},
{
"path": "config/packages/dev/monolog.yaml",
"chars": 606,
"preview": "monolog:\n handlers:\n main:\n type: stream\n path: \"%kernel.logs_dir%/%kernel.environment%."
},
{
"path": "config/packages/dev/web_profiler.yaml",
"chars": 116,
"preview": "web_profiler:\n toolbar: true\n intercept_redirects: false\n\nframework:\n profiler: { only_exceptions: false }\n"
},
{
"path": "config/packages/doctrine.yaml",
"chars": 798,
"preview": "doctrine:\n dbal:\n # The server_version must be configured directly in the\n # DATABASE_URL to allow diff"
},
{
"path": "config/packages/doctrine_migrations.yaml",
"chars": 241,
"preview": "doctrine_migrations:\n migrations_paths:\n # namespace is arbitrary but should be different from App\\Migrations\n"
},
{
"path": "config/packages/framework.yaml",
"chars": 726,
"preview": "# see https://symfony.com/doc/current/reference/configuration/framework.html\nframework:\n secret: '%env(APP_SECRET)%'\n"
},
{
"path": "config/packages/mailer.yaml",
"chars": 56,
"preview": "framework:\n mailer:\n dsn: '%env(MAILER_DSN)%'\n"
},
{
"path": "config/packages/prod/deprecations.yaml",
"chars": 315,
"preview": "# As of Symfony 5.1, deprecations are logged in the dedicated \"deprecation\" channel when it exists\n#monolog:\n# channe"
},
{
"path": "config/packages/prod/doctrine.yaml",
"chars": 545,
"preview": "doctrine:\n orm:\n auto_generate_proxy_classes: false\n metadata_cache_driver:\n type: pool\n "
},
{
"path": "config/packages/prod/monolog.yaml",
"chars": 507,
"preview": "monolog:\n handlers:\n main:\n type: fingers_crossed\n action_level: error\n handl"
},
{
"path": "config/packages/prod/routing.yaml",
"chars": 57,
"preview": "framework:\n router:\n strict_requirements: null\n"
},
{
"path": "config/packages/routing.yaml",
"chars": 254,
"preview": "framework:\n router:\n utf8: true\n\n # Configure how to generate URLs in non-HTTP contexts, such as CLI co"
},
{
"path": "config/packages/security.yaml",
"chars": 1518,
"preview": "security:\n password_hashers:\n Symfony\\Component\\Security\\Core\\User\\PasswordAuthenticatedUserInterface: 'auto'\n"
},
{
"path": "config/packages/test/framework.yaml",
"chars": 100,
"preview": "framework:\n test: true\n session:\n storage_factory_id: session.storage.factory.mock_file"
},
{
"path": "config/packages/test/monolog.yaml",
"chars": 337,
"preview": "monolog:\n handlers:\n main:\n type: fingers_crossed\n action_level: error\n handl"
},
{
"path": "config/packages/test/twig.yaml",
"chars": 33,
"preview": "twig:\n strict_variables: true\n"
},
{
"path": "config/packages/test/validator.yaml",
"chars": 67,
"preview": "framework:\n validation:\n not_compromised_password: false\n"
},
{
"path": "config/packages/test/web_profiler.yaml",
"chars": 109,
"preview": "web_profiler:\n toolbar: false\n intercept_redirects: false\n\nframework:\n profiler: { collect: false }\n"
},
{
"path": "config/packages/translation.yaml",
"chars": 144,
"preview": "framework:\n default_locale: en\n translator:\n default_path: '%kernel.project_dir%/translations'\n fall"
},
{
"path": "config/packages/twig.yaml",
"chars": 494,
"preview": "twig:\n default_path: '%kernel.project_dir%/templates'\n debug: '%kernel.debug%'\n strict_variables: '%kernel.debu"
},
{
"path": "config/packages/validator.yaml",
"chars": 295,
"preview": "framework:\n validation:\n enable_attributes: true\n email_validation_mode: html5\n\n # Enables valid"
},
{
"path": "config/reference.php",
"chars": 101458,
"preview": "<?php\n\n// This file is auto-generated and is for apps only. Bundles SHOULD NOT rely on its content.\n\nnamespace Symfony\\C"
},
{
"path": "config/routes/attributes.yaml",
"chars": 123,
"preview": "controllers:\n resource: ../../src/Controller/\n type: attribute\n\nkernel:\n resource: App\\Kernel\n type: attribu"
},
{
"path": "config/routes/dev/framework.yaml",
"chars": 97,
"preview": "_errors:\n resource: '@FrameworkBundle/Resources/config/routing/errors.php'\n prefix: /_error"
},
{
"path": "config/routes/dev/web_profiler.yaml",
"chars": 223,
"preview": "web_profiler_wdt:\n resource: '@WebProfilerBundle/Resources/config/routing/wdt.php'\n prefix: /_wdt\n\nweb_profiler_pr"
},
{
"path": "config/routes.yaml",
"chars": 78,
"preview": "#index:\n# path: /\n# controller: App\\Controller\\DefaultController::index\n"
},
{
"path": "config/services.yaml",
"chars": 3764,
"preview": "# This file is the entry point to configure your own services.\n# Files in the packages/ subdirectory configure your depe"
},
{
"path": "docker/Dockerfile",
"chars": 3768,
"preview": "# syntax=docker/dockerfile:1\n\n# Initial PHP image is available here:\n# https://github.com/docker-library/php/blob/master"
},
{
"path": "docker/Dockerfile-standalone",
"chars": 4508,
"preview": "# Initial PHP image is available here:\n# https://github.com/docker-library/php/blob/master/8.2/alpine3.18/fpm/Dockerfile"
},
{
"path": "docker/configurations/Caddyfile",
"chars": 1089,
"preview": "{\n auto_https off\n}\n\n:9000 {\n\t# Redirect .well-known\n\tredir /.well-known/caldav /dav/\n\tredir /.well-known/cardda"
},
{
"path": "docker/configurations/nginx.conf",
"chars": 871,
"preview": "# This is a very simple / naive configuration for nginx + Davis\n#\n# USE HTTPS IN PRODUCTION\n#\n\nupstream docker-davis {\n "
},
{
"path": "docker/configurations/opcache.ini",
"chars": 342,
"preview": "opcache.enable=1\nopcache.jit=1255\nopcache.jit_buffer_size=128M\nopcache.revalidate_freq=60\nopcache.save_comments=1\nopcach"
},
{
"path": "docker/configurations/supervisord.conf",
"chars": 762,
"preview": "[supervisord]\nnodaemon=true\nuser=root\npidfile=/run/supervisord.pid\nlogfile=/dev/null\nlogfile_maxbytes=0\n\n[unix_http_serv"
},
{
"path": "docker/docker-compose-postgresql.yml",
"chars": 1358,
"preview": "version: \"3.7\"\nname: \"davis-docker\"\n\nservices:\n\n nginx:\n image: nginx:1.25-alpine\n container_name: nginx\n comm"
},
{
"path": "docker/docker-compose-sqlite.yml",
"chars": 1094,
"preview": "version: \"3.7\"\nname: \"davis-docker\"\n\nservices:\n\n nginx:\n image: nginx:1.25-alpine\n container_name: nginx\n comm"
},
{
"path": "docker/docker-compose-standalone.yml",
"chars": 959,
"preview": "version: \"3.7\"\nname: \"davis-docker\"\n\nservices:\n\n mysql:\n image: mariadb:10.6.10\n container_name: mysql\n enviro"
},
{
"path": "docker/docker-compose.yml",
"chars": 1402,
"preview": "version: \"3.7\"\nname: \"davis-docker\"\n\nservices:\n\n nginx:\n image: nginx:1.25-alpine\n container_name: nginx\n comm"
},
{
"path": "docs/api/README.md",
"chars": 1627,
"preview": "# Davis API\n\n## API Version 1\n\n### Open Endpoints\n\nOpen endpoints require no Authentication.\n\n* [Health](v1/health.md) :"
},
{
"path": "docs/api/v1/calendars/all.md",
"chars": 1733,
"preview": "# User Calendars\n\nGets a list of all available calendars for a specific user.\n\n**URL** : `/api/v1/calendars/:user_id`\n\n*"
},
{
"path": "docs/api/v1/calendars/create.md",
"chars": 3394,
"preview": "# Create User Calendar\n\nCreates a new calendar for a specific user.\n\n**URL** : `/api/v1/calendars/:user_id/create`\n\n**Me"
},
{
"path": "docs/api/v1/calendars/delete.md",
"chars": 1549,
"preview": "# Delete User Calendar\n\nDeletes a specific calendar for a specific user.\n\n**URL** : `/api/v1/calendars/:user_id/:calenda"
},
{
"path": "docs/api/v1/calendars/details.md",
"chars": 1288,
"preview": "# User Calendar Details\n\nGets a list of all available calendars for a specific user.\n\n**URL** : `/api/v1/calendars/:user"
},
{
"path": "docs/api/v1/calendars/edit.md",
"chars": 3253,
"preview": "# Edit User Calendar\n\nEdits an existing calendar for a specific user.\n\n**URL** : `/api/v1/calendars/:user_id/:calendar_i"
},
{
"path": "docs/api/v1/calendars/share_add.md",
"chars": 2323,
"preview": "# Share User Calendar\n\nShares (or updates write access) a calendar owned by the specified user to another user.\n\n**URL**"
},
{
"path": "docs/api/v1/calendars/share_remove.md",
"chars": 2128,
"preview": "# Remove Share User Calendar\n\nRemoves access to a specific shared calendar for a specific user.\n\n**URL** : `/api/v1/cale"
},
{
"path": "docs/api/v1/calendars/shares.md",
"chars": 1825,
"preview": "# User Calendar Shares\n\nGets a list of all users with whom a specific user calendar is shared.\n\n**URL** : `/api/v1/calen"
},
{
"path": "docs/api/v1/health.md",
"chars": 266,
"preview": "# Health\n\nUsed to check if the API endpoint is active.\n\n**URL** : `/api/v1/health`\n\n**Method** : `GET`\n\n**Auth required*"
},
{
"path": "docs/api/v1/users/all.md",
"chars": 851,
"preview": "# Get Users\n\nGets a list of all available users.\n\n**URL** : `/api/v1/users`\n\n**Method** : `GET`\n\n**Auth required** : YES"
},
{
"path": "docs/api/v1/users/details.md",
"chars": 1089,
"preview": "# User Details\n\nGets details about a specific user account.\n\n**URL** : `/api/v1/users/:user_id`\n\n**Method** : `GET`\n\n**A"
},
{
"path": "migrations/Version20191030113307.php",
"chars": 7594,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20191113170650.php",
"chars": 1675,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20191125093508.php",
"chars": 949,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20191202091507.php",
"chars": 1494,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20191203111729.php",
"chars": 1056,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20210928132307.php",
"chars": 1011,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20221106220411.php",
"chars": 5043,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20221106220412.php",
"chars": 10236,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20221211154443.php",
"chars": 6407,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20230209142217.php",
"chars": 3810,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20231001214111.php",
"chars": 1431,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20231001214112.php",
"chars": 1645,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20231001214113.php",
"chars": 3619,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20231229203515.php",
"chars": 1694,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20250409193948.php",
"chars": 2061,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20250421163214.php",
"chars": 1297,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "migrations/Version20260131161930.php",
"chars": 2174,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace DoctrineMigrations;\n\nuse Doctrine\\DBAL\\Schema\\Schema;\nuse Doctrine\\Migrations"
},
{
"path": "phpunit.xml.dist",
"chars": 914,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->\n<phpunit xml"
},
{
"path": "public/.htaccess",
"chars": 3439,
"preview": "# Use the front controller as index file. It serves as a fallback solution when\n# every other rewrite/redirect fails (e."
},
{
"path": "public/css/style.css",
"chars": 1719,
"preview": "body {\n padding-top: calc(56px + 30px);\n -webkit-touch-callout: none; /* iOS Safari */\n -webkit-user-select: no"
},
{
"path": "public/index.php",
"chars": 654,
"preview": "<?php\n\n$overridenEnvDir = getenv('ENV_DIR') ?: null;\n\nif ($overridenEnvDir) {\n // Tell the Runtime not to touch doten"
},
{
"path": "public/js/app.js",
"chars": 4951,
"preview": "'use strict'\n\n// Calendar share modal\nconst shareModal = document.getElementById('shareModal')\nif (shareModal) {\n sha"
},
{
"path": "public/js/color.mode.toggler.js",
"chars": 2693,
"preview": "/* Based on Bootstrap' color mode toggler */\n/*!\n * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)\n"
},
{
"path": "public/robots.txt",
"chars": 26,
"preview": "User-agent: *\nDisallow: /\n"
},
{
"path": "public/site.webmanifest",
"chars": 263,
"preview": "{\"name\":\"\",\"short_name\":\"\",\"icons\":[{\"src\":\"/android-chrome-192x192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\"},{\"src\":\"/"
},
{
"path": "src/Command/ApiGenerateCommand.php",
"chars": 1161,
"preview": "<?php\n\nnamespace App\\Command;\n\nuse Symfony\\Component\\Console\\Attribute\\AsCommand;\nuse Symfony\\Component\\Console\\Command\\"
},
{
"path": "src/Command/SyncBirthdayCalendars.php",
"chars": 2164,
"preview": "<?php\n\nnamespace App\\Command;\n\nuse App\\Entity\\User;\nuse App\\Services\\BirthdayService;\nuse Doctrine\\Persistence\\ManagerRe"
},
{
"path": "src/Constants.php",
"chars": 174,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace App;\n\nclass Constants\n{\n public const BIRTHDAY_CALENDAR_URI = 'birthday-ca"
},
{
"path": "src/Controller/Admin/AddressBookController.php",
"chars": 5560,
"preview": "<?php\n\nnamespace App\\Controller\\Admin;\n\nuse App\\Entity\\AddressBook;\nuse App\\Entity\\Principal;\nuse App\\Entity\\User;\nuse A"
},
{
"path": "src/Controller/Admin/CalendarController.php",
"chars": 13129,
"preview": "<?php\n\nnamespace App\\Controller\\Admin;\n\nuse App\\Entity\\Calendar;\nuse App\\Entity\\CalendarInstance;\nuse App\\Entity\\Calenda"
},
{
"path": "src/Controller/Admin/DashboardController.php",
"chars": 1661,
"preview": "<?php\n\nnamespace App\\Controller\\Admin;\n\nuse App\\Entity\\AddressBook;\nuse App\\Entity\\CalendarInstance;\nuse App\\Entity\\Cale"
},
{
"path": "src/Controller/Admin/UserController.php",
"chars": 15297,
"preview": "<?php\n\nnamespace App\\Controller\\Admin;\n\nuse App\\Entity\\AddressBook;\nuse App\\Entity\\Calendar;\nuse App\\Entity\\CalendarInst"
},
{
"path": "src/Controller/Api/ApiController.php",
"chars": 34149,
"preview": "<?php\n\nnamespace App\\Controller\\Api;\n\nuse App\\Entity\\Calendar;\nuse App\\Entity\\CalendarInstance;\nuse App\\Entity\\CalendarS"
},
{
"path": "src/Controller/DAVController.php",
"chars": 12780,
"preview": "<?php\n\nnamespace App\\Controller;\n\nuse App\\Entity\\Principal;\nuse App\\Entity\\User;\nuse App\\Plugins\\BirthdayCalendarPlugin;"
},
{
"path": "src/Controller/SecurityController.php",
"chars": 1110,
"preview": "<?php\n\nnamespace App\\Controller;\n\nuse Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController;\nuse Symfony\\Componen"
},
{
"path": "src/DataFixtures/AppFixtures.php",
"chars": 4072,
"preview": "<?php\n\nnamespace App\\DataFixtures;\n\nuse App\\Entity\\AddressBook;\nuse App\\Entity\\Calendar;\nuse App\\Entity\\CalendarInstance"
},
{
"path": "src/Entity/AddressBook.php",
"chars": 4553,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collectio"
},
{
"path": "src/Entity/AddressBookChange.php",
"chars": 1558,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity()]\n#[ORM\\Table(name: 'addressbookchanges')]"
},
{
"path": "src/Entity/Calendar.php",
"chars": 3798,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collectio"
},
{
"path": "src/Entity/CalendarChange.php",
"chars": 1541,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity()]\n#[ORM\\Table(name: 'calendarchanges')]\ncl"
},
{
"path": "src/Entity/CalendarInstance.php",
"chars": 6143,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse App\\Constants;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Sabre\\DAV\\Sharing\\Plugin as Sharin"
},
{
"path": "src/Entity/CalendarObject.php",
"chars": 3812,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity()]\n#[ORM\\Table(name: 'calendarobjects')]\ncl"
},
{
"path": "src/Entity/CalendarSubscription.php",
"chars": 3928,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity()]\n#[ORM\\Table(name: 'calendarsubscriptions"
},
{
"path": "src/Entity/Card.php",
"chars": 2226,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity()]\n#[ORM\\Table(name: 'cards')]\nclass Card\n{"
},
{
"path": "src/Entity/Lock.php",
"chars": 2219,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity()]\n#[ORM\\Table(name: 'locks')]\nclass Lock\n{"
},
{
"path": "src/Entity/Principal.php",
"chars": 3575,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\Common\\Collections\\ArrayCollection;\nuse Doctrine\\Common\\Collections\\Collectio"
},
{
"path": "src/Entity/PropertyStorage.php",
"chars": 1411,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n#[ORM\\Entity()]\n#[ORM\\Table(name: 'propertystorage')]\ncl"
},
{
"path": "src/Entity/SchedulingObject.php",
"chars": 2312,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Symfony\\Component\\Validator\\Constraints as Assert;\n\n#"
},
{
"path": "src/Entity/User.php",
"chars": 1227,
"preview": "<?php\n\nnamespace App\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\nuse Symfony\\Bridge\\Doctrine\\Validator\\Constraints\\UniqueE"
},
{
"path": "src/Form/AddressBookType.php",
"chars": 2065,
"preview": "<?php\n\nnamespace App\\Form;\n\nuse App\\Entity\\AddressBook;\nuse Symfony\\Component\\Form\\AbstractType;\nuse Symfony\\Component\\F"
},
{
"path": "src/Form/CalendarInstanceType.php",
"chars": 3253,
"preview": "<?php\n\nnamespace App\\Form;\n\nuse App\\Entity\\CalendarInstance;\nuse Symfony\\Component\\Form\\AbstractType;\nuse Symfony\\Compon"
},
{
"path": "src/Form/UserType.php",
"chars": 2210,
"preview": "<?php\n\nnamespace App\\Form;\n\nuse App\\Entity\\User;\nuse Symfony\\Component\\Form\\AbstractType;\nuse Symfony\\Component\\Form\\Ext"
},
{
"path": "src/Kernel.php",
"chars": 607,
"preview": "<?php\n\nnamespace App;\n\nuse Symfony\\Bundle\\FrameworkBundle\\Kernel\\MicroKernelTrait;\nuse Symfony\\Component\\HttpKernel\\Kern"
},
{
"path": "src/Logging/Monolog/PasswordFilterProcessor.php",
"chars": 1308,
"preview": "<?php\n\nnamespace App\\Logging\\Monolog;\n\nuse Monolog\\LogRecord;\nuse Monolog\\Processor\\ProcessorInterface;\n\nfinal class Pas"
},
{
"path": "src/Plugins/BirthdayCalendarPlugin.php",
"chars": 2766,
"preview": "<?php\n\nnamespace App\\Plugins;\n\nuse App\\Services\\BirthdayService;\nuse Sabre\\CalDAV\\Backend\\PDO as CalendarBackend;\nuse Sa"
},
{
"path": "src/Plugins/DavisIMipPlugin.php",
"chars": 10028,
"preview": "<?php\n\nnamespace App\\Plugins;\n\nuse DantSu\\OpenStreetMapStaticAPI\\LatLng;\nuse DantSu\\OpenStreetMapStaticAPI\\Markers;\nuse "
},
{
"path": "src/Plugins/PublicAwareDAVACLPlugin.php",
"chars": 2532,
"preview": "<?php\n\nnamespace App\\Plugins;\n\nuse App\\Entity\\CalendarInstance;\nuse Doctrine\\ORM\\EntityManagerInterface;\nuse Sabre\\HTTP\\"
},
{
"path": "src/Repository/CalendarInstanceRepository.php",
"chars": 5409,
"preview": "<?php\n\nnamespace App\\Repository;\n\nuse App\\Entity\\Calendar;\nuse App\\Entity\\CalendarInstance;\nuse App\\Entity\\CalendarObjec"
},
{
"path": "src/Repository/PrincipalRepository.php",
"chars": 1732,
"preview": "<?php\n\nnamespace App\\Repository;\n\nuse App\\Entity\\Principal;\nuse Doctrine\\Bundle\\DoctrineBundle\\Repository\\ServiceEntityR"
},
{
"path": "src/Security/AdminUser.php",
"chars": 1579,
"preview": "<?php\n\nnamespace App\\Security;\n\nuse Symfony\\Component\\Security\\Core\\User\\PasswordAuthenticatedUserInterface;\nuse Symfony"
},
{
"path": "src/Security/AdminUserProvider.php",
"chars": 1871,
"preview": "<?php\n\nnamespace App\\Security;\n\nuse Symfony\\Component\\Security\\Core\\Exception\\UnsupportedUserException;\nuse Symfony\\Comp"
},
{
"path": "src/Security/ApiKeyAuthenticator.php",
"chars": 2522,
"preview": "<?php\n\nnamespace App\\Security;\n\nuse Symfony\\Component\\HttpFoundation\\JsonResponse;\nuse Symfony\\Component\\HttpFoundation\\"
},
{
"path": "src/Security/LoginFormAuthenticator.php",
"chars": 3275,
"preview": "<?php\n\nnamespace App\\Security;\n\nuse Symfony\\Component\\HttpFoundation\\RedirectResponse;\nuse Symfony\\Component\\HttpFoundat"
},
{
"path": "src/Services/BasicAuth.php",
"chars": 1129,
"preview": "<?php\n\nnamespace App\\Services;\n\nuse App\\Entity\\User;\nuse Doctrine\\Persistence\\ManagerRegistry;\nuse Sabre\\DAV\\Auth\\Backen"
},
{
"path": "src/Services/BirthdayService.php",
"chars": 12423,
"preview": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * Largely inspired by https://github.com/nextcloud/server/blob/master/apps/dav/lib"
},
{
"path": "src/Services/IMAPAuth.php",
"chars": 4429,
"preview": "<?php\n\nnamespace App\\Services;\n\nuse App\\Entity\\User;\nuse Doctrine\\Persistence\\ManagerRegistry;\nuse Sabre\\DAV\\Auth\\Backen"
},
{
"path": "src/Services/LDAPAuth.php",
"chars": 7145,
"preview": "<?php\n\nnamespace App\\Services;\n\nuse App\\Entity\\User;\nuse Doctrine\\Persistence\\ManagerRegistry;\nuse Sabre\\DAV\\Auth\\Backen"
},
{
"path": "src/Services/Utils.php",
"chars": 3511,
"preview": "<?php\n\nnamespace App\\Services;\n\nuse App\\Entity\\AddressBook;\nuse App\\Entity\\Calendar;\nuse App\\Entity\\CalendarInstance;\nus"
},
{
"path": "src/Version.php",
"chars": 83,
"preview": "<?php\n\nnamespace App;\n\nfinal class Version\n{\n public const VERSION = '5.4.1';\n}\n"
},
{
"path": "templates/_partials/add_delegate_modal.html.twig",
"chars": 1678,
"preview": "<div class=\"modal fade\" id=\"addDelegateModal\" tabindex=\"-1\" role=\"dialog\" aria-hidden=\"true\">\n <div class=\"modal-dialog"
},
{
"path": "templates/_partials/back_button.html.twig",
"chars": 98,
"preview": "<div class=\"mb-3\"><a href=\"{{ url }}\" class=\"link-opacity-75 link-offset-2\">« {{ text }}</a></div>"
},
{
"path": "templates/_partials/delegate_row.html.twig",
"chars": 1642,
"preview": "<div class=\"list-group-item p-3\">\n <div class=\"d-flex w-100 justify-content-between\">\n <h5 class=\"me-auto\">\n "
},
{
"path": "templates/_partials/delete_modal.html.twig",
"chars": 832,
"preview": "<div id=\"deleteModal-{{ flavour }}\" class=\"modal fade\" tabindex=\"-1\" role=\"dialog\" rel=\"deleteModal\">\n <div class=\"moda"
},
{
"path": "templates/_partials/flashes.html.twig",
"chars": 599,
"preview": "<div aria-live=\"polite\" aria-atomic=\"true\" class=\"flashes\">\n <div class=\"inner\">\n {% for label, messages in app.flas"
},
{
"path": "templates/_partials/navigation.html.twig",
"chars": 2901,
"preview": "<nav class=\"navbar fixed-top navbar-expand-lg bg-body-tertiary\">\n <div class=\"container\">\n <a class=\"navbar-brand\" h"
},
{
"path": "templates/_partials/share_modal.html.twig",
"chars": 2683,
"preview": "<div class=\"modal fade\" id=\"shareModal\" tabindex=\"-1\" role=\"dialog\" aria-hidden=\"true\">\n <div class=\"modal-dialog\" role"
},
{
"path": "templates/addressbooks/edit.html.twig",
"chars": 584,
"preview": "{% extends 'base.html.twig' %}\n{% set menu = 'resources' %}\n\n{% block body %}\n\n{% include '_partials/back_button.html.tw"
},
{
"path": "templates/addressbooks/index.html.twig",
"chars": 2272,
"preview": "{% extends 'base.html.twig' %}\n{% set menu = 'resources' %}\n\n{% block body %}\n\n{% include '_partials/back_button.html.tw"
},
{
"path": "templates/base.html.twig",
"chars": 729,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"UTF-8\">\n <title>{% block title %}Davis{% endblock %}</ti"
},
{
"path": "templates/calendars/edit.html.twig",
"chars": 566,
"preview": "{% extends 'base.html.twig' %}\n{% set menu = 'resources' %}\n\n{% block body %}\n\n{% include '_partials/back_button.html.tw"
},
{
"path": "templates/calendars/index.html.twig",
"chars": 11762,
"preview": "{% extends 'base.html.twig' %}\n{% set menu = 'resources' %}\n\n{% block body %}\n\n{% include '_partials/back_button.html.tw"
},
{
"path": "templates/dashboard.html.twig",
"chars": 4523,
"preview": "{% extends 'base.html.twig' %}\n{% set menu = 'dashboard' %}\n\n{% block body %}\n\n<h1 class=\"display-4 fw-lighter\">{{ \"titl"
},
{
"path": "templates/index.html.twig",
"chars": 2346,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-wid"
},
{
"path": "templates/mails/scheduling.html.twig",
"chars": 5067,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <title>Calendar notification from {{ senderName }}</title>\n <meta http-equiv=\"content-t"
},
{
"path": "templates/mails/scheduling.txt.twig",
"chars": 1360,
"preview": "Calendar notification from {{ senderName }}.\n\n-----------------------------------------------------------\n\n{% if action "
},
{
"path": "templates/security/login.html.twig",
"chars": 1632,
"preview": "{% extends 'base.html.twig' %}\n{% set menu = null %}\n\n{% block body %}\n\n{% if app.user %}\n <div class=\"mb-3\">\n "
},
{
"path": "templates/users/delegates.html.twig",
"chars": 1776,
"preview": "{% extends 'base.html.twig' %}\n{% set menu = 'resources' %}\n\n{% block body %}\n\n{% include '_partials/back_button.html.tw"
},
{
"path": "templates/users/edit.html.twig",
"chars": 421,
"preview": "{% extends 'base.html.twig' %}\n{% set menu = 'resources' %}\n\n{% block body %}\n\n{% include '_partials/back_button.html.tw"
},
{
"path": "templates/users/index.html.twig",
"chars": 3548,
"preview": "{% extends 'base.html.twig' %}\n{% set menu = 'resources' %}\n\n{% block body %}\n\n<h1 class=\"display-4 d-flex fw-lighter ju"
},
{
"path": "tests/.gitignore",
"chars": 0,
"preview": ""
},
{
"path": "tests/Functional/Commands/SyncBirthdayCalendarTest.php",
"chars": 6631,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Tests\\Functional\\Command;\n\nuse App\\Constants;\nuse App\\Entity\\AddressBook;"
},
{
"path": "tests/Functional/Controllers/AddressBookControllerTest.php",
"chars": 4056,
"preview": "<?php\n\nnamespace App\\Tests\\Functional;\n\nuse App\\Entity\\AddressBook;\nuse App\\Entity\\User;\nuse App\\Security\\AdminUser;\nuse"
},
{
"path": "tests/Functional/Controllers/ApiControllerTest.php",
"chars": 23630,
"preview": "<?php\n\nnamespace App\\Tests\\Functional;\n\nuse Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase;\n\nclass ApiControllerTest ex"
},
{
"path": "tests/Functional/Controllers/CalendarControllerTest.php",
"chars": 3998,
"preview": "<?php\n\nnamespace App\\Tests\\Functional;\n\nuse App\\Entity\\User;\nuse App\\Repository\\CalendarInstanceRepository;\nuse App\\Secu"
},
{
"path": "tests/Functional/Controllers/DashboardTest.php",
"chars": 3117,
"preview": "<?php\n\nnamespace App\\Tests\\Functional;\n\nuse Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase;\n\nclass DashboardTest extend"
},
{
"path": "tests/Functional/Controllers/UserControllerTest.php",
"chars": 4752,
"preview": "<?php\n\nnamespace App\\Tests\\Functional;\n\nuse App\\Entity\\User;\nuse App\\Security\\AdminUser;\nuse Symfony\\Bundle\\FrameworkBun"
},
{
"path": "tests/Functional/DavTest.php",
"chars": 914,
"preview": "<?php\n\nnamespace App\\Tests\\Functional;\n\nuse Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase;\nuse Symfony\\Component\\Brows"
},
{
"path": "tests/Functional/Service/BirthdayServiceTest.php",
"chars": 12888,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Tests\\Services;\n\nuse App\\Constants;\nuse App\\Entity\\AddressBook;\nuse App\\E"
},
{
"path": "tests/bootstrap.php",
"chars": 830,
"preview": "<?php\n\nuse Symfony\\Component\\Dotenv\\Dotenv;\n\nrequire dirname(__DIR__).'/vendor/autoload.php';\n\nif (file_exists(dirname(_"
},
{
"path": "translations/.gitignore",
"chars": 0,
"preview": ""
},
{
"path": "translations/messages+intl-icu.de.xlf",
"chars": 27601,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xliff xmlns=\"urn:oasis:names:tc:xliff:document:1.2\" version=\"1.2\">\n <file sourc"
},
{
"path": "translations/messages+intl-icu.en.xlf",
"chars": 26762,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xliff xmlns=\"urn:oasis:names:tc:xliff:document:1.2\" version=\"1.2\">\n <file sourc"
},
{
"path": "translations/messages+intl-icu.fr.xliff",
"chars": 27564,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xliff xmlns=\"urn:oasis:names:tc:xliff:document:1.2\" version=\"1.2\">\n <file sourc"
},
{
"path": "translations/security.de.xlf",
"chars": 3836,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xliff xmlns=\"urn:oasis:names:tc:xliff:document:1.2\" version=\"1.2\">\n <file sourc"
},
{
"path": "translations/security.en.xlf",
"chars": 3684,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xliff xmlns=\"urn:oasis:names:tc:xliff:document:1.2\" version=\"1.2\">\n <file sourc"
},
{
"path": "translations/security.fr.xlf",
"chars": 4375,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xliff xmlns=\"urn:oasis:names:tc:xliff:document:1.2\" version=\"1.2\">\n <file sou"
},
{
"path": "translations/validators.de.xlf",
"chars": 27093,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xliff xmlns=\"urn:oasis:names:tc:xliff:document:1.2\" version=\"1.2\">\n <file sourc"
},
{
"path": "translations/validators.en.xlf",
"chars": 26412,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xliff xmlns=\"urn:oasis:names:tc:xliff:document:1.2\" version=\"1.2\">\n <file sourc"
},
{
"path": "translations/validators.fr.xlf",
"chars": 36029,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xliff xmlns=\"urn:oasis:names:tc:xliff:document:1.2\" version=\"1.2\">\n <file sou"
}
]
About this extraction
This page contains the full source code of the tchapi/davis GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 174 files (746.3 KB), approximately 189.5k tokens, and a symbol index with 522 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.