🏗️(caldav) migrate from davical to sabre/dav
This commit is contained in:
53
docker/sabredav/Dockerfile
Normal file
53
docker/sabredav/Dockerfile
Normal file
@@ -0,0 +1,53 @@
|
||||
# sabre/dav CalDAV Server
|
||||
# Based on Debian with Apache and PHP
|
||||
FROM php:8.2-apache-bookworm
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libpq-dev \
|
||||
postgresql-client \
|
||||
git \
|
||||
unzip \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install PHP extensions
|
||||
RUN docker-php-ext-install pdo pdo_pgsql
|
||||
|
||||
# Install Composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Create application directory
|
||||
WORKDIR /var/www/sabredav
|
||||
|
||||
# Copy composer files
|
||||
COPY composer.json ./
|
||||
|
||||
# Install sabre/dav and dependencies
|
||||
RUN composer install --no-dev --optimize-autoloader --no-interaction
|
||||
|
||||
# Copy server configuration
|
||||
COPY server.php ./
|
||||
COPY sabredav.conf /etc/apache2/sites-available/sabredav.conf
|
||||
COPY init-database.sh /usr/local/bin/init-database.sh
|
||||
|
||||
# Copy SQL schema files for database initialization
|
||||
COPY sql/ ./sql/
|
||||
|
||||
# Copy custom principal backend
|
||||
COPY src/ ./src/
|
||||
|
||||
# Enable Apache modules and site
|
||||
RUN a2enmod rewrite headers \
|
||||
&& a2dissite 000-default \
|
||||
&& a2ensite sabredav \
|
||||
&& chmod +x /usr/local/bin/init-database.sh
|
||||
|
||||
# Set permissions
|
||||
RUN chown -R www-data:www-data /var/www/sabredav \
|
||||
&& chmod -R 755 /var/www/sabredav
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["apache2-foreground"]
|
||||
16
docker/sabredav/composer.json
Normal file
16
docker/sabredav/composer.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "calendars/sabredav-server",
|
||||
"description": "sabre/dav CalDAV server for calendars",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"sabre/dav": "^4.5",
|
||||
"ext-pdo": "*",
|
||||
"ext-pdo_pgsql": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Calendars\\SabreDav\\": "src/"
|
||||
}
|
||||
}
|
||||
}
|
||||
87
docker/sabredav/init-database.sh
Normal file
87
docker/sabredav/init-database.sh
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
###
|
||||
# Initialize sabre/dav database schema in PostgreSQL
|
||||
# This script creates all necessary tables for sabre/dav to work
|
||||
###
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z ${PGHOST+x} ]; then
|
||||
echo "PGHOST must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z ${PGDATABASE+x} ]; then
|
||||
echo "PGDATABASE must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z ${PGUSER+x} ]; then
|
||||
echo "PGUSER must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z ${PGPASSWORD+x} ]; then
|
||||
echo "PGPASSWORD must be set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export PGHOST
|
||||
export PGPORT=${PGPORT:-5432}
|
||||
export PGDATABASE
|
||||
export PGUSER
|
||||
export PGPASSWORD
|
||||
|
||||
# Wait for PostgreSQL to be ready
|
||||
retries=30
|
||||
until pg_isready -q -h "$PGHOST" -p "$PGPORT" -U "$PGUSER"; do
|
||||
[[ retries -eq 0 ]] && echo "Could not connect to Postgres" && exit 1
|
||||
echo "Waiting for Postgres to be available..."
|
||||
retries=$((retries-1))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "PostgreSQL is ready. Initializing sabre/dav database schema..."
|
||||
|
||||
# SQL files directory (will be copied into container)
|
||||
SQL_DIR="/var/www/sabredav/sql"
|
||||
|
||||
# Check if tables already exist
|
||||
TABLES_EXIST=$(psql -tAc "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name IN ('users', 'principals', 'calendars')" 2>/dev/null || echo "0")
|
||||
|
||||
if [ "$TABLES_EXIST" -gt "0" ]; then
|
||||
echo "sabre/dav tables already exist, skipping initialization"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create tables
|
||||
echo "Creating sabre/dav tables..."
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.users.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.users.sql"
|
||||
echo "Created users table"
|
||||
fi
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.principals.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.principals.sql"
|
||||
echo "Created principals table"
|
||||
fi
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.calendars.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.calendars.sql"
|
||||
echo "Created calendars table"
|
||||
fi
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.addressbooks.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.addressbooks.sql"
|
||||
echo "Created addressbooks table"
|
||||
fi
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.locks.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.locks.sql"
|
||||
echo "Created locks table"
|
||||
fi
|
||||
|
||||
if [ -f "$SQL_DIR/pgsql.propertystorage.sql" ]; then
|
||||
psql -f "$SQL_DIR/pgsql.propertystorage.sql"
|
||||
echo "Created propertystorage table"
|
||||
fi
|
||||
|
||||
echo "sabre/dav database schema initialized successfully!"
|
||||
28
docker/sabredav/sabredav.conf
Normal file
28
docker/sabredav/sabredav.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
<VirtualHost *:80>
|
||||
ServerName localhost
|
||||
DocumentRoot /var/www/sabredav
|
||||
|
||||
<Directory /var/www/sabredav>
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
Options -Indexes +FollowSymLinks
|
||||
</Directory>
|
||||
|
||||
# Set REMOTE_USER from X-Forwarded-User header (set by Django proxy)
|
||||
# This allows sabre/dav to use Apache auth backend
|
||||
<IfModule mod_headers.c>
|
||||
RequestHeader set REMOTE_USER %{HTTP:X-Forwarded-User}e env=HTTP_X_FORWARDED_USER
|
||||
</IfModule>
|
||||
|
||||
# Rewrite rules for CalDAV
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ server.php [QSA,L]
|
||||
|
||||
# Well-known CalDAV discovery
|
||||
RewriteRule ^\.well-known/caldav / [R=301,L]
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/sabredav_error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/sabredav_access.log combined
|
||||
</VirtualHost>
|
||||
79
docker/sabredav/server.php
Normal file
79
docker/sabredav/server.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* sabre/dav CalDAV Server
|
||||
* Configured to use PostgreSQL backend and Apache authentication
|
||||
*/
|
||||
|
||||
use Sabre\DAV\Auth;
|
||||
use Sabre\DAVACL;
|
||||
use Sabre\CalDAV;
|
||||
use Sabre\CardDAV;
|
||||
use Sabre\DAV;
|
||||
use Calendars\SabreDav\AutoCreatePrincipalBackend;
|
||||
|
||||
// Composer autoloader
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
// Set REMOTE_USER from X-Forwarded-User header (set by Django proxy)
|
||||
// This allows sabre/dav Apache auth backend to work with proxied requests
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_USER']) && !isset($_SERVER['REMOTE_USER'])) {
|
||||
$_SERVER['REMOTE_USER'] = $_SERVER['HTTP_X_FORWARDED_USER'];
|
||||
}
|
||||
|
||||
// Get base URI from environment variable (set by compose.yaml)
|
||||
// This ensures sabre/dav generates URLs with the correct proxy path
|
||||
$baseUri = getenv('CALENDARS_BASE_URI') ?: '/';
|
||||
|
||||
// Database connection from environment variables
|
||||
$dbHost = getenv('PGHOST') ?: 'postgresql';
|
||||
$dbPort = getenv('PGPORT') ?: '5432';
|
||||
$dbName = getenv('PGDATABASE') ?: 'calendars';
|
||||
$dbUser = getenv('PGUSER') ?: 'pgroot';
|
||||
$dbPass = getenv('PGPASSWORD') ?: 'pass';
|
||||
|
||||
// Create PDO connection
|
||||
$pdo = new PDO(
|
||||
"pgsql:host={$dbHost};port={$dbPort};dbname={$dbName}",
|
||||
$dbUser,
|
||||
$dbPass,
|
||||
[
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]
|
||||
);
|
||||
|
||||
// Create backend
|
||||
$authBackend = new Auth\Backend\Apache();
|
||||
|
||||
// Create authentication plugin
|
||||
$authPlugin = new Auth\Plugin($authBackend);
|
||||
|
||||
// Create CalDAV backend
|
||||
$caldavBackend = new CalDAV\Backend\PDO($pdo);
|
||||
|
||||
// Create CardDAV backend (optional, for future use)
|
||||
$carddavBackend = new CardDAV\Backend\PDO($pdo);
|
||||
|
||||
// Create principal backend with auto-creation support
|
||||
$principalBackend = new AutoCreatePrincipalBackend($pdo);
|
||||
|
||||
// Create directory tree
|
||||
$nodes = [
|
||||
new CalDAV\Principal\Collection($principalBackend),
|
||||
new CalDAV\CalendarRoot($principalBackend, $caldavBackend),
|
||||
new CardDAV\AddressBookRoot($principalBackend, $carddavBackend),
|
||||
];
|
||||
|
||||
// Create server
|
||||
$server = new DAV\Server($nodes);
|
||||
$server->setBaseUri($baseUri);
|
||||
|
||||
// Add plugins
|
||||
$server->addPlugin($authPlugin);
|
||||
$server->addPlugin(new CalDAV\Plugin());
|
||||
$server->addPlugin(new CardDAV\Plugin());
|
||||
$server->addPlugin(new DAVACL\Plugin());
|
||||
$server->addPlugin(new DAV\Browser\Plugin());
|
||||
|
||||
// Start server
|
||||
$server->start();
|
||||
44
docker/sabredav/sql/pgsql.addressbooks.sql
Normal file
44
docker/sabredav/sql/pgsql.addressbooks.sql
Normal file
@@ -0,0 +1,44 @@
|
||||
CREATE TABLE addressbooks (
|
||||
id SERIAL NOT NULL,
|
||||
principaluri VARCHAR(255),
|
||||
displayname VARCHAR(255),
|
||||
uri VARCHAR(200),
|
||||
description TEXT,
|
||||
synctoken INTEGER NOT NULL DEFAULT 1
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY addressbooks
|
||||
ADD CONSTRAINT addressbooks_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX addressbooks_ukey
|
||||
ON addressbooks USING btree (principaluri, uri);
|
||||
|
||||
CREATE TABLE cards (
|
||||
id SERIAL NOT NULL,
|
||||
addressbookid INTEGER NOT NULL,
|
||||
carddata BYTEA,
|
||||
uri VARCHAR(200),
|
||||
lastmodified INTEGER,
|
||||
etag VARCHAR(32),
|
||||
size INTEGER NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY cards
|
||||
ADD CONSTRAINT cards_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX cards_ukey
|
||||
ON cards USING btree (addressbookid, uri);
|
||||
|
||||
CREATE TABLE addressbookchanges (
|
||||
id SERIAL NOT NULL,
|
||||
uri VARCHAR(200) NOT NULL,
|
||||
synctoken INTEGER NOT NULL,
|
||||
addressbookid INTEGER NOT NULL,
|
||||
operation SMALLINT NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY addressbookchanges
|
||||
ADD CONSTRAINT addressbookchanges_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE INDEX addressbookchanges_addressbookid_synctoken_ix
|
||||
ON addressbookchanges USING btree (addressbookid, synctoken);
|
||||
108
docker/sabredav/sql/pgsql.calendars.sql
Normal file
108
docker/sabredav/sql/pgsql.calendars.sql
Normal file
@@ -0,0 +1,108 @@
|
||||
CREATE TABLE calendarobjects (
|
||||
id SERIAL NOT NULL,
|
||||
calendardata BYTEA,
|
||||
uri VARCHAR(200),
|
||||
calendarid INTEGER NOT NULL,
|
||||
lastmodified INTEGER,
|
||||
etag VARCHAR(32),
|
||||
size INTEGER NOT NULL,
|
||||
componenttype VARCHAR(8),
|
||||
firstoccurence INTEGER,
|
||||
lastoccurence INTEGER,
|
||||
uid VARCHAR(200)
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendarobjects
|
||||
ADD CONSTRAINT calendarobjects_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX calendarobjects_ukey
|
||||
ON calendarobjects USING btree (calendarid, uri);
|
||||
|
||||
|
||||
CREATE TABLE calendars (
|
||||
id SERIAL NOT NULL,
|
||||
synctoken INTEGER NOT NULL DEFAULT 1,
|
||||
components VARCHAR(21)
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendars
|
||||
ADD CONSTRAINT calendars_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
CREATE TABLE calendarinstances (
|
||||
id SERIAL NOT NULL,
|
||||
calendarid INTEGER NOT NULL,
|
||||
principaluri VARCHAR(100),
|
||||
access SMALLINT NOT NULL DEFAULT '1', -- '1 = owner, 2 = read, 3 = readwrite'
|
||||
displayname VARCHAR(100),
|
||||
uri VARCHAR(200),
|
||||
description TEXT,
|
||||
calendarorder INTEGER NOT NULL DEFAULT 0,
|
||||
calendarcolor VARCHAR(10),
|
||||
timezone TEXT,
|
||||
transparent SMALLINT NOT NULL DEFAULT '0',
|
||||
share_href VARCHAR(100),
|
||||
share_displayname VARCHAR(100),
|
||||
share_invitestatus SMALLINT NOT NULL DEFAULT '2' -- '1 = noresponse, 2 = accepted, 3 = declined, 4 = invalid'
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendarinstances
|
||||
ADD CONSTRAINT calendarinstances_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX calendarinstances_principaluri_uri
|
||||
ON calendarinstances USING btree (principaluri, uri);
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX calendarinstances_principaluri_calendarid
|
||||
ON calendarinstances USING btree (principaluri, calendarid);
|
||||
|
||||
CREATE UNIQUE INDEX calendarinstances_principaluri_share_href
|
||||
ON calendarinstances USING btree (principaluri, share_href);
|
||||
|
||||
CREATE TABLE calendarsubscriptions (
|
||||
id SERIAL NOT NULL,
|
||||
uri VARCHAR(200) NOT NULL,
|
||||
principaluri VARCHAR(100) NOT NULL,
|
||||
source TEXT,
|
||||
displayname VARCHAR(100),
|
||||
refreshrate VARCHAR(10),
|
||||
calendarorder INTEGER NOT NULL DEFAULT 0,
|
||||
calendarcolor VARCHAR(10),
|
||||
striptodos SMALLINT NULL,
|
||||
stripalarms SMALLINT NULL,
|
||||
stripattachments SMALLINT NULL,
|
||||
lastmodified INTEGER
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendarsubscriptions
|
||||
ADD CONSTRAINT calendarsubscriptions_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX calendarsubscriptions_ukey
|
||||
ON calendarsubscriptions USING btree (principaluri, uri);
|
||||
|
||||
CREATE TABLE calendarchanges (
|
||||
id SERIAL NOT NULL,
|
||||
uri VARCHAR(200) NOT NULL,
|
||||
synctoken INTEGER NOT NULL,
|
||||
calendarid INTEGER NOT NULL,
|
||||
operation SMALLINT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendarchanges
|
||||
ADD CONSTRAINT calendarchanges_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE INDEX calendarchanges_calendarid_synctoken_ix
|
||||
ON calendarchanges USING btree (calendarid, synctoken);
|
||||
|
||||
CREATE TABLE schedulingobjects (
|
||||
id SERIAL NOT NULL,
|
||||
principaluri VARCHAR(255),
|
||||
calendardata BYTEA,
|
||||
uri VARCHAR(200),
|
||||
lastmodified INTEGER,
|
||||
etag VARCHAR(32),
|
||||
size INTEGER NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY schedulingobjects
|
||||
ADD CONSTRAINT schedulingobjects_pkey PRIMARY KEY (id);
|
||||
19
docker/sabredav/sql/pgsql.locks.sql
Normal file
19
docker/sabredav/sql/pgsql.locks.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE locks (
|
||||
id SERIAL NOT NULL,
|
||||
owner VARCHAR(100),
|
||||
timeout INTEGER,
|
||||
created INTEGER,
|
||||
token VARCHAR(100),
|
||||
scope SMALLINT,
|
||||
depth SMALLINT,
|
||||
uri TEXT
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY locks
|
||||
ADD CONSTRAINT locks_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE INDEX locks_token_ix
|
||||
ON locks USING btree (token);
|
||||
|
||||
CREATE INDEX locks_uri_ix
|
||||
ON locks USING btree (uri);
|
||||
30
docker/sabredav/sql/pgsql.principals.sql
Normal file
30
docker/sabredav/sql/pgsql.principals.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
CREATE TABLE principals (
|
||||
id SERIAL NOT NULL,
|
||||
uri VARCHAR(200) NOT NULL,
|
||||
email VARCHAR(80),
|
||||
displayname VARCHAR(80)
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY principals
|
||||
ADD CONSTRAINT principals_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX principals_ukey
|
||||
ON principals USING btree (uri);
|
||||
|
||||
CREATE TABLE groupmembers (
|
||||
id SERIAL NOT NULL,
|
||||
principal_id INTEGER NOT NULL,
|
||||
member_id INTEGER NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY groupmembers
|
||||
ADD CONSTRAINT groupmembers_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX groupmembers_ukey
|
||||
ON groupmembers USING btree (principal_id, member_id);
|
||||
|
||||
INSERT INTO principals (uri,email,displayname) VALUES
|
||||
('principals/admin', 'admin@example.org','Administrator'),
|
||||
('principals/admin/calendar-proxy-read', null, null),
|
||||
('principals/admin/calendar-proxy-write', null, null);
|
||||
|
||||
13
docker/sabredav/sql/pgsql.propertystorage.sql
Normal file
13
docker/sabredav/sql/pgsql.propertystorage.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE propertystorage (
|
||||
id SERIAL NOT NULL,
|
||||
path VARCHAR(1024) NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
valuetype INT,
|
||||
value BYTEA
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY propertystorage
|
||||
ADD CONSTRAINT propertystorage_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX propertystorage_ukey
|
||||
ON propertystorage (path, name);
|
||||
14
docker/sabredav/sql/pgsql.users.sql
Normal file
14
docker/sabredav/sql/pgsql.users.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE users (
|
||||
id SERIAL NOT NULL,
|
||||
username VARCHAR(50),
|
||||
digesta1 VARCHAR(32)
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY users
|
||||
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX users_ukey
|
||||
ON users USING btree (username);
|
||||
|
||||
INSERT INTO users (username,digesta1) VALUES
|
||||
('admin', '87fd274b7b6c01e48d7c2f965da8ddf7');
|
||||
53
docker/sabredav/src/AutoCreatePrincipalBackend.php
Normal file
53
docker/sabredav/src/AutoCreatePrincipalBackend.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* Custom principal backend that auto-creates principals when they don't exist.
|
||||
* This allows Apache authentication to work without pre-creating principals.
|
||||
*/
|
||||
|
||||
namespace Calendars\SabreDav;
|
||||
|
||||
use Sabre\DAVACL\PrincipalBackend\PDO as BasePDO;
|
||||
use Sabre\DAV\MkCol;
|
||||
|
||||
class AutoCreatePrincipalBackend extends BasePDO
|
||||
{
|
||||
/**
|
||||
* Returns a specific principal, specified by it's path.
|
||||
* Auto-creates the principal if it doesn't exist.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getPrincipalByPath($path)
|
||||
{
|
||||
$principal = parent::getPrincipalByPath($path);
|
||||
|
||||
// If principal doesn't exist, create it automatically
|
||||
if (!$principal && strpos($path, 'principals/') === 0) {
|
||||
// Extract username from path (e.g., "principals/user@example.com" -> "user@example.com")
|
||||
$username = substr($path, strlen('principals/'));
|
||||
|
||||
// Create principal directly in database
|
||||
// Access protected pdo property from parent
|
||||
$pdo = $this->pdo;
|
||||
$tableName = $this->tableName;
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare(
|
||||
'INSERT INTO ' . $tableName . ' (uri, email, displayname) VALUES (?, ?, ?) ON CONFLICT (uri) DO NOTHING'
|
||||
);
|
||||
$stmt->execute([$path, $username, $username]);
|
||||
|
||||
// Retry getting the principal
|
||||
$principal = parent::getPrincipalByPath($path);
|
||||
} catch (\Exception $e) {
|
||||
// If creation fails, return null
|
||||
error_log("Failed to auto-create principal: " . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $principal;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user