Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ WEBDAV_ENABLED=false
# Do we allow calendars to be public ?
PUBLIC_CALENDARS_ENABLED=true

# For Birthday calendars, what should be the reminder offset ?
# (The default is PT9H, 9am on the day of the event)
BIRTHDAY_REMINDER_OFFSET=PT9H

# What mail is used as the sender for invites ?
[email protected]

Expand Down
109 changes: 73 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Davis
[![Latest release][release_badge]][release_link]
[![Sponsor me][sponsor_badge]][sponsor_link]

A simple, fully translatable and full-featured 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_)
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

Expand All @@ -18,6 +18,12 @@ Supports **Basic authentication**, as well as **IMAP** and **LDAP** (_via extern

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_).
Expand Down Expand Up @@ -61,17 +67,17 @@ Dependencies

1. Retrieve the dependencies:

```
composer install
```
```
composer install
```

2. 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.

3. Run the migrations to create all the necessary tables:

```
bin/console doctrine:migrations:migrate
```
```
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.

Expand All @@ -87,14 +93,14 @@ Create your own `.env.local` file to change the necessary variables, if you plan
>
> 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.

a. The database driver and url (_you should already have it configured since you created the database previously_)
**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=mariadb-10.6.10&charset=utf8mb4
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
**b. The admin password for the backend**

```shell
ADMIN_LOGIN=admin
Expand All @@ -105,15 +111,15 @@ ADMIN_PASSWORD=test
>
> 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
**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
**d. The global flags to enable CalDAV, CardDAV and WebDAV**. You can also disable the option to have calendars public

```shell
CALDAV_ENABLED=true
Expand All @@ -128,14 +134,34 @@ PUBLIC_CALENDARS_ENABLED=true
> 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. The email address that your invites are going to be sent from
**e. The email address that your invites are going to be sent from**

```shell
[email protected]
```

f. The paths for the WebDAV installation
**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
Expand All @@ -152,15 +178,15 @@ WEBDAV_HOMES_DIR=
>
> 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.

g. The log file path
**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"
```

h. The timezone you want for the app
**i. The timezone you want for the app**

This must comply with the [official list](https://www.php.net/manual/en/timezones.php)

Expand Down Expand Up @@ -230,29 +256,27 @@ 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
```

```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
```

```shell
bin/console doctrine:migrations:migrate 'DoctrineMigrations\Version20191030113307' --no-interaction
```

3. Reimport the data back:

```
mysql -uroot -p davis < baikal_to_davis.sql
```
```
mysql -uroot -p davis < baikal_to_davis.sql
```

4. Run the necessary remaining migrations:

```
bin/console doctrine:migrations:migrate
```
```
bin/console doctrine:migrations:migrate
```

# 🌐 Access / Webserver

Expand All @@ -262,7 +286,7 @@ The administration interface is available at `/dashboard`. You need to login to

The main endpoint for CalDAV, WebDAV or CardDAV is at `/dav`.

> [!NOTE]
> [!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.

Expand Down Expand Up @@ -316,7 +340,7 @@ dav.domain.tld {
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=mariadb-10.6.10&charset=utf8mb4"
SetEnv DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name?serverVersion=10.9.3-MariaDB&charset=utf8mb4"
# ... etc
</VirtualHost>
```
Expand Down Expand Up @@ -345,7 +369,7 @@ server {
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=mariadb-10.6.10&charset=utf8mb4";
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;
Expand Down Expand Up @@ -429,15 +453,15 @@ docker pull ghcr.io/tchapi/davis-standalone:v4.4.0

### Edge image

The edge image is built from the tip of the main branch:
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.
> The `edge` image must not be considered stable. **Use only release images for production setups**.

## Full stack

Expand Down Expand Up @@ -531,7 +555,12 @@ Depending on how you run Davis, logs are either:

> [!NOTE]
>
> It's `./var/log` (relative to the Davis installation), not `/var/log`
> 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

Expand Down Expand Up @@ -580,6 +609,14 @@ Check if your instance can reach your LDAP server:
- 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)
Expand Down
8 changes: 8 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ parameters:
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
Expand Down Expand Up @@ -68,6 +72,10 @@ services:
tags:
- { name: monolog.processor }

App\Services\BirthdayService:
arguments:
$birthdayReminderOffset: "%birthday_reminder_offset%"

when@dev:
services:
Symfony\Component\HttpKernel\Profiler\Profiler: '@profiler'
Expand Down
2 changes: 2 additions & 0 deletions docker/.env
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ CARDDAV_ENABLED=true
WEBDAV_ENABLED=false
PUBLIC_CALENDARS_ENABLED=true

BIRTHDAY_REMINDER_OFFSET=PT9H

APP_TIMEZONE=Europe/Paris

LOG_FILE_PATH="%kernel.logs_dir%/%kernel.environment%.log"
Expand Down
41 changes: 41 additions & 0 deletions migrations/Version20250421163214.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Add birthday calendar.
*/
final class Version20250421163214 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add birthday calendars';
}

public function up(Schema $schema): void
{
$engine = $this->connection->getDatabasePlatform()->getName();

if ('mysql' === $engine) {
$this->addSql('ALTER TABLE addressbooks ADD included_in_birthday_calendar TINYINT(1) DEFAULT 0');
} elseif ('postgresql' === $engine) {
$this->addSql('ALTER TABLE addressbooks ADD COLUMN included_in_birthday_calendar BOOLEAN DEFAULT FALSE;');
} elseif ('sqlite' === $engine) {
$this->addSql('ALTER TABLE addressbooks ADD COLUMN included_in_birthday_calendar INTEGER DEFAULT 0;');
}
}

public function down(Schema $schema): void
{
if ('mysql' === $this->connection->getDatabasePlatform()->getName()) {
$this->addSql('ALTER TABLE addressbooks DROP included_in_birthday_calendar');
} else {
$this->addSql('ALTER TABLE addressbooks DROP COLUMN included_in_birthday_calendar');
}
}
}
Loading