generated from Nodarx/template
Initial service DAV module
This commit is contained in:
@@ -0,0 +1,41 @@
|
|||||||
|
name: Renovate
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 3 * * *"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
renovate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v6.2.0
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: npm
|
||||||
|
|
||||||
|
- name: Set up PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.5'
|
||||||
|
tools: composer:v2
|
||||||
|
|
||||||
|
- name: Install Renovate
|
||||||
|
run: |
|
||||||
|
npm install --global --no-audit --fund=false \
|
||||||
|
--prefix "${{ runner.temp }}/renovate-npm" \
|
||||||
|
--cache "${{ runner.temp }}/renovate-npm-cache" \
|
||||||
|
renovate
|
||||||
|
"${{ runner.temp }}/renovate-npm/bin/renovate" --version
|
||||||
|
|
||||||
|
- name: Run Renovate
|
||||||
|
env:
|
||||||
|
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
|
||||||
|
RENOVATE_PLATFORM: gitea
|
||||||
|
RENOVATE_ENDPOINT: https://git.ktrix.dev/api/v1
|
||||||
|
run: |
|
||||||
|
"${{ runner.temp }}/renovate-npm/bin/renovate" ${{ gitea.repository }}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "ktrix/service-dav",
|
||||||
|
"description": "WebDAV / CalDAV / CardDAV service module for Ktrix",
|
||||||
|
"type": "library",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1",
|
||||||
|
"sabre/dav": "^4.7"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"vendor-dir": "lib/vendor",
|
||||||
|
"optimize-autoloader": true,
|
||||||
|
"sort-packages": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+516
@@ -0,0 +1,516 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "ad6d240cf2a7ee11decdfcd3995cf6f0",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "psr/log",
|
||||||
|
"version": "3.0.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/log.git",
|
||||||
|
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
|
||||||
|
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Log\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interface for logging libraries",
|
||||||
|
"homepage": "https://github.com/php-fig/log",
|
||||||
|
"keywords": [
|
||||||
|
"log",
|
||||||
|
"psr",
|
||||||
|
"psr-3"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/log/tree/3.0.2"
|
||||||
|
},
|
||||||
|
"time": "2024-09-11T13:17:53+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/dav",
|
||||||
|
"version": "4.7.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/dav.git",
|
||||||
|
"reference": "074373bcd689a30bcf5aaa6bbb20a3395964ce7a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/dav/zipball/074373bcd689a30bcf5aaa6bbb20a3395964ce7a",
|
||||||
|
"reference": "074373bcd689a30bcf5aaa6bbb20a3395964ce7a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"ext-date": "*",
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-iconv": "*",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"ext-pcre": "*",
|
||||||
|
"ext-simplexml": "*",
|
||||||
|
"ext-spl": "*",
|
||||||
|
"lib-libxml": ">=2.7.0",
|
||||||
|
"php": "^7.1.0 || ^8.0",
|
||||||
|
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||||
|
"sabre/event": "^5.0",
|
||||||
|
"sabre/http": "^5.0.5",
|
||||||
|
"sabre/uri": "^2.0",
|
||||||
|
"sabre/vobject": "^4.2.1",
|
||||||
|
"sabre/xml": "^2.0.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^2.19",
|
||||||
|
"monolog/monolog": "^1.27 || ^2.0",
|
||||||
|
"phpstan/phpstan": "^0.12 || ^1.0",
|
||||||
|
"phpstan/phpstan-phpunit": "^1.0",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-curl": "*",
|
||||||
|
"ext-imap": "*",
|
||||||
|
"ext-pdo": "*"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"bin/sabredav",
|
||||||
|
"bin/naturalselection"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "WebDAV Framework for PHP",
|
||||||
|
"homepage": "http://sabre.io/",
|
||||||
|
"keywords": [
|
||||||
|
"CalDAV",
|
||||||
|
"CardDAV",
|
||||||
|
"WebDAV",
|
||||||
|
"framework",
|
||||||
|
"iCalendar"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/dav/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-dav"
|
||||||
|
},
|
||||||
|
"time": "2024-10-29T11:46:02+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/event",
|
||||||
|
"version": "5.1.7",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/event.git",
|
||||||
|
"reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/event/zipball/86d57e305c272898ba3c28e9bd3d65d5464587c2",
|
||||||
|
"reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "~2.17.1||^3.63",
|
||||||
|
"phpstan/phpstan": "^0.12",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"lib/coroutine.php",
|
||||||
|
"lib/Loop/functions.php",
|
||||||
|
"lib/Promise/functions.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\Event\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "sabre/event is a library for lightweight event-based programming",
|
||||||
|
"homepage": "http://sabre.io/event/",
|
||||||
|
"keywords": [
|
||||||
|
"EventEmitter",
|
||||||
|
"async",
|
||||||
|
"coroutine",
|
||||||
|
"eventloop",
|
||||||
|
"events",
|
||||||
|
"hooks",
|
||||||
|
"plugin",
|
||||||
|
"promise",
|
||||||
|
"reactor",
|
||||||
|
"signal"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/event/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-event"
|
||||||
|
},
|
||||||
|
"time": "2024-08-27T11:23:05+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/http",
|
||||||
|
"version": "5.1.13",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/http.git",
|
||||||
|
"reference": "7c2a14097d1a0de2347dcbdc91a02f38e338f4db"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/http/zipball/7c2a14097d1a0de2347dcbdc91a02f38e338f4db",
|
||||||
|
"reference": "7c2a14097d1a0de2347dcbdc91a02f38e338f4db",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"ext-curl": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"php": "^7.1 || ^8.0",
|
||||||
|
"sabre/event": ">=4.0 <6.0",
|
||||||
|
"sabre/uri": "^2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "~2.17.1||3.63.2",
|
||||||
|
"phpstan/phpstan": "^0.12",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-curl": " to make http requests with the Client class"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"lib/functions.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\HTTP\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The sabre/http library provides utilities for dealing with http requests and responses. ",
|
||||||
|
"homepage": "https://github.com/fruux/sabre-http",
|
||||||
|
"keywords": [
|
||||||
|
"http"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/http/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-http"
|
||||||
|
},
|
||||||
|
"time": "2025-09-09T10:21:47+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/uri",
|
||||||
|
"version": "2.3.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/uri.git",
|
||||||
|
"reference": "b76524c22de90d80ca73143680a8e77b1266c291"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/uri/zipball/b76524c22de90d80ca73143680a8e77b1266c291",
|
||||||
|
"reference": "b76524c22de90d80ca73143680a8e77b1266c291",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.63",
|
||||||
|
"phpstan/extension-installer": "^1.4",
|
||||||
|
"phpstan/phpstan": "^1.12",
|
||||||
|
"phpstan/phpstan-phpunit": "^1.4",
|
||||||
|
"phpstan/phpstan-strict-rules": "^1.6",
|
||||||
|
"phpunit/phpunit": "^9.6"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"lib/functions.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\Uri\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Functions for making sense out of URIs.",
|
||||||
|
"homepage": "http://sabre.io/uri/",
|
||||||
|
"keywords": [
|
||||||
|
"rfc3986",
|
||||||
|
"uri",
|
||||||
|
"url"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/uri/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-uri"
|
||||||
|
},
|
||||||
|
"time": "2024-08-27T12:18:16+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/vobject",
|
||||||
|
"version": "4.5.8",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/vobject.git",
|
||||||
|
"reference": "d554eb24d64232922e1eab5896cc2f84b3b9ffb1"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/vobject/zipball/d554eb24d64232922e1eab5896cc2f84b3b9ffb1",
|
||||||
|
"reference": "d554eb24d64232922e1eab5896cc2f84b3b9ffb1",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"php": "^7.1 || ^8.0",
|
||||||
|
"sabre/xml": "^2.1 || ^3.0 || ^4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "~2.17.1",
|
||||||
|
"phpstan/phpstan": "^0.12 || ^1.12 || ^2.0",
|
||||||
|
"phpunit/php-invoker": "^2.0 || ^3.1",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"hoa/bench": "If you would like to run the benchmark scripts"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"bin/vobject",
|
||||||
|
"bin/generate_vcards"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "4.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\VObject\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Dominik Tobschall",
|
||||||
|
"email": "dominik@fruux.com",
|
||||||
|
"homepage": "http://tobschall.de/",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ivan Enderlin",
|
||||||
|
"email": "ivan.enderlin@hoa-project.net",
|
||||||
|
"homepage": "http://mnt.io/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The VObject library for PHP allows you to easily parse and manipulate iCalendar and vCard objects",
|
||||||
|
"homepage": "http://sabre.io/vobject/",
|
||||||
|
"keywords": [
|
||||||
|
"availability",
|
||||||
|
"freebusy",
|
||||||
|
"iCalendar",
|
||||||
|
"ical",
|
||||||
|
"ics",
|
||||||
|
"jCal",
|
||||||
|
"jCard",
|
||||||
|
"recurrence",
|
||||||
|
"rfc2425",
|
||||||
|
"rfc2426",
|
||||||
|
"rfc2739",
|
||||||
|
"rfc4770",
|
||||||
|
"rfc5545",
|
||||||
|
"rfc5546",
|
||||||
|
"rfc6321",
|
||||||
|
"rfc6350",
|
||||||
|
"rfc6351",
|
||||||
|
"rfc6474",
|
||||||
|
"rfc6638",
|
||||||
|
"rfc6715",
|
||||||
|
"rfc6868",
|
||||||
|
"vCalendar",
|
||||||
|
"vCard",
|
||||||
|
"vcf",
|
||||||
|
"xCal",
|
||||||
|
"xCard"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/vobject/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-vobject"
|
||||||
|
},
|
||||||
|
"time": "2026-01-12T10:45:19+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/xml",
|
||||||
|
"version": "2.2.11",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/xml.git",
|
||||||
|
"reference": "01a7927842abf3e10df3d9c2d9b0cc9d813a3fcc"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/xml/zipball/01a7927842abf3e10df3d9c2d9b0cc9d813a3fcc",
|
||||||
|
"reference": "01a7927842abf3e10df3d9c2d9b0cc9d813a3fcc",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-xmlreader": "*",
|
||||||
|
"ext-xmlwriter": "*",
|
||||||
|
"lib-libxml": ">=2.6.20",
|
||||||
|
"php": "^7.1 || ^8.0",
|
||||||
|
"sabre/uri": ">=1.0,<3.0.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "~2.17.1||3.63.2",
|
||||||
|
"phpstan/phpstan": "^0.12",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"lib/Deserializer/functions.php",
|
||||||
|
"lib/Serializer/functions.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\Xml\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Markus Staab",
|
||||||
|
"email": "markus.staab@redaxo.de",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "sabre/xml is an XML library that you may not hate.",
|
||||||
|
"homepage": "https://sabre.io/xml/",
|
||||||
|
"keywords": [
|
||||||
|
"XMLReader",
|
||||||
|
"XMLWriter",
|
||||||
|
"dom",
|
||||||
|
"xml"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/xml/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-xml"
|
||||||
|
},
|
||||||
|
"time": "2024-09-06T07:37:46+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": {
|
||||||
|
"php": ">=8.1"
|
||||||
|
},
|
||||||
|
"platform-dev": [],
|
||||||
|
"plugin-api-version": "2.3.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav\Auth;
|
||||||
|
|
||||||
|
use KTXC\Service\SecurityService;
|
||||||
|
use KTXC\SessionIdentity;
|
||||||
|
use Sabre\DAV\Auth\Backend\AbstractBasic;
|
||||||
|
use Sabre\HTTP\RequestInterface;
|
||||||
|
use Sabre\HTTP\ResponseInterface;
|
||||||
|
|
||||||
|
class DavAuthBackend extends AbstractBasic
|
||||||
|
{
|
||||||
|
public const PRINCIPAL_PREFIX = 'principals/';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly SessionIdentity $identity,
|
||||||
|
private readonly SecurityService $securityService,
|
||||||
|
string $realm = 'KTXC DAV',
|
||||||
|
) {
|
||||||
|
$this->realm = $realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check(RequestInterface $request, ResponseInterface $response): array
|
||||||
|
{
|
||||||
|
if ($this->identity->identity() !== null) {
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
return [true, self::PRINCIPAL_PREFIX . $userId];
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::check($request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validateUserPass($username, $password): bool
|
||||||
|
{
|
||||||
|
$user = $this->securityService->authenticatePassword($username, $password);
|
||||||
|
if ($user === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->identity->initialize($user, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav\Backends;
|
||||||
|
|
||||||
|
use KTXC\SessionIdentity;
|
||||||
|
use KTXC\SessionTenant;
|
||||||
|
use KTXM\ChronoManager\Manager as ChronoManager;
|
||||||
|
use KTXM\ServiceDav\DavProperties;
|
||||||
|
use KTXM\ServiceDav\DavUriMap;
|
||||||
|
use KTXF\Resource\Identifier\CollectionIdentifier;
|
||||||
|
use KTXF\Resource\Identifier\EntityIdentifier;
|
||||||
|
use KTXF\Resource\Identifier\ResourceIdentifiers;
|
||||||
|
use Sabre\CalDAV\Backend\AbstractBackend;
|
||||||
|
use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
|
||||||
|
use Sabre\VObject\Component\VCalendar;
|
||||||
|
use Sabre\VObject\Reader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CalDAV backend bridging sabre/dav to the KTXM ChronoManager.
|
||||||
|
*
|
||||||
|
* Calendar <=> Collection (within a provider service)
|
||||||
|
* Calendar object <=> Entity (VEVENT / VTODO)
|
||||||
|
*
|
||||||
|
* Calendar identifiers use the composite key "{providerId}:{serviceId}:{collectionId}".
|
||||||
|
* Object URIs are client-chosen names (e.g. "event-uid.ics") mapped to provider
|
||||||
|
* entity identifiers via DavUriMap.
|
||||||
|
*/
|
||||||
|
class CalDavBackend extends AbstractBackend
|
||||||
|
{
|
||||||
|
private const ID_SEP = ':';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly ChronoManager $manager,
|
||||||
|
private readonly SessionIdentity $identity,
|
||||||
|
private readonly SessionTenant $tenant,
|
||||||
|
private readonly DavUriMap $uriMap,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function getCalendarsForUser($principalUri): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
$allCollections = $this->manager->collectionList($this->tenant->identifier(), $this->identity->identifier());
|
||||||
|
|
||||||
|
foreach ($allCollections as $providerId => $providerServices) {
|
||||||
|
foreach ($providerServices as $serviceId => $collections) {
|
||||||
|
foreach ($collections as $collectionId => $collection) {
|
||||||
|
if (!is_object($collection)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$props = $collection->getProperties();
|
||||||
|
$label = $props->getLabel();
|
||||||
|
$calId = implode(self::ID_SEP, [(string) $providerId, (string) $serviceId, (string) $collectionId]);
|
||||||
|
|
||||||
|
$result[] = [
|
||||||
|
DavProperties::ID => $calId,
|
||||||
|
DavProperties::URI => $this->slugify($label),
|
||||||
|
DavProperties::DISPLAYNAME => $label,
|
||||||
|
DavProperties::CALDAV_DESCRIPTION => $props->getDescription() ?? '',
|
||||||
|
DavProperties::APPLE_COLOR => $props->getColor() ?? '#3a87ad',
|
||||||
|
DavProperties::CALDAV_COMPONENTS => new SupportedCalendarComponentSet(['VEVENT', 'VTODO']),
|
||||||
|
DavProperties::CS_CTAG => md5($calId . (string) ($collection->modified()?->getTimestamp() ?? 0)),
|
||||||
|
DavProperties::SYNC_TOKEN => '1',
|
||||||
|
DavProperties::PRINCIPAL_URI => $principalUri,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createCalendar($principalUri, $calendarUri, array $properties): string
|
||||||
|
{
|
||||||
|
throw new \Sabre\DAV\Exception\Forbidden('Calendar creation is not supported via DAV');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteCalendar($calendarId): void
|
||||||
|
{
|
||||||
|
throw new \Sabre\DAV\Exception\Forbidden('Calendar deletion is not supported via DAV');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCalendarObjects($calendarId): array
|
||||||
|
{
|
||||||
|
[$providerId, $serviceId, $collectionId] = $this->parseId($calendarId);
|
||||||
|
$tenantId = $this->tenant->identifier();
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
|
||||||
|
$targets = ResourceIdentifiers::fromArray([$calendarId]);
|
||||||
|
$all = $this->manager->entityListBulk($tenantId, $userId, $targets);
|
||||||
|
$entities = $all[(string) $providerId][(string) $serviceId][(string) $collectionId] ?? [];
|
||||||
|
|
||||||
|
$mapId = $this->mapId($tenantId, $userId, $calendarId);
|
||||||
|
$objects = [];
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
if (!is_object($entity)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$entityId = (string) $entity->identifier();
|
||||||
|
$objectUri = $this->uriMap->getObjectUri($mapId, $entityId) ?? ($entityId . '.ics');
|
||||||
|
$data = $this->entityToIcal($entity);
|
||||||
|
|
||||||
|
$objects[] = [
|
||||||
|
DavProperties::ID => $entityId,
|
||||||
|
DavProperties::URI => $objectUri,
|
||||||
|
DavProperties::LAST_MODIFIED => $entity->modified()?->getTimestamp() ?? time(),
|
||||||
|
DavProperties::ETAG => '"' . md5($data) . '"',
|
||||||
|
DavProperties::SIZE => strlen($data),
|
||||||
|
DavProperties::COMPONENT => 'VEVENT',
|
||||||
|
DavProperties::CALENDAR_DATA => null, // lazy - fetched on demand
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCalendarObject($calendarId, $objectUri): ?array
|
||||||
|
{
|
||||||
|
[$providerId, $serviceId, $collectionId] = $this->parseId($calendarId);
|
||||||
|
$tenantId = $this->tenant->identifier();
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
$mapId = $this->mapId($tenantId, $userId, $calendarId);
|
||||||
|
|
||||||
|
// Resolve URI => entityId via the map; fall back to treating stripped URI as entityId
|
||||||
|
$entityId = $this->uriMap->getEntityId($mapId, $objectUri)
|
||||||
|
?? preg_replace('/\.ics$/i', '', $objectUri);
|
||||||
|
|
||||||
|
$identifier = new EntityIdentifier($providerId, $serviceId, $collectionId, $entityId);
|
||||||
|
$fetched = $this->manager->entityFetchBulk($tenantId, $userId, $identifier);
|
||||||
|
$entity = reset($fetched) ?: null;
|
||||||
|
|
||||||
|
if ($entity === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $this->entityToIcal($entity);
|
||||||
|
|
||||||
|
return [
|
||||||
|
DavProperties::ID => $entityId,
|
||||||
|
DavProperties::URI => $objectUri,
|
||||||
|
DavProperties::LAST_MODIFIED => $entity->modified()?->getTimestamp() ?? time(),
|
||||||
|
DavProperties::ETAG => '"' . md5($data) . '"',
|
||||||
|
DavProperties::SIZE => strlen($data),
|
||||||
|
DavProperties::COMPONENT => 'VEVENT',
|
||||||
|
DavProperties::CALENDAR_DATA => $data,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMultipleCalendarObjects($calendarId, array $uris): array
|
||||||
|
{
|
||||||
|
return array_values(array_filter(array_map(
|
||||||
|
fn($uri) => $this->getCalendarObject($calendarId, $uri),
|
||||||
|
$uris,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createCalendarObject($calendarId, $objectUri, $calendarData): ?string
|
||||||
|
{
|
||||||
|
[$providerId, $serviceId, $collectionId] = $this->parseId($calendarId);
|
||||||
|
$tenantId = $this->tenant->identifier();
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
|
||||||
|
$vcal = Reader::read($calendarData);
|
||||||
|
$props = $this->icalToEntityProps($vcal);
|
||||||
|
$target = new CollectionIdentifier($providerId, $serviceId, $collectionId);
|
||||||
|
$entity = $this->manager->entityCreate($tenantId, $userId, $target, $props);
|
||||||
|
|
||||||
|
// Persist the client-chosen URI => provider entityId mapping
|
||||||
|
$this->uriMap->set(
|
||||||
|
$this->mapId($tenantId, $userId, $calendarId),
|
||||||
|
$objectUri,
|
||||||
|
(string) $entity->identifier(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return '"' . md5($this->entityToIcal($entity)) . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateCalendarObject($calendarId, $objectUri, $calendarData): ?string
|
||||||
|
{
|
||||||
|
[$providerId, $serviceId, $collectionId] = $this->parseId($calendarId);
|
||||||
|
$tenantId = $this->tenant->identifier();
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
$mapId = $this->mapId($tenantId, $userId, $calendarId);
|
||||||
|
|
||||||
|
$entityId = $this->uriMap->getEntityId($mapId, $objectUri)
|
||||||
|
?? preg_replace('/\.ics$/i', '', $objectUri);
|
||||||
|
|
||||||
|
$vcal = Reader::read($calendarData);
|
||||||
|
$props = $this->icalToEntityProps($vcal);
|
||||||
|
$target = new EntityIdentifier($providerId, $serviceId, $collectionId, $entityId);
|
||||||
|
$entity = $this->manager->entityModify($tenantId, $userId, $target, $props);
|
||||||
|
|
||||||
|
return '"' . md5($this->entityToIcal($entity)) . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteCalendarObject($calendarId, $objectUri): void
|
||||||
|
{
|
||||||
|
[$providerId, $serviceId, $collectionId] = $this->parseId($calendarId);
|
||||||
|
$tenantId = $this->tenant->identifier();
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
$mapId = $this->mapId($tenantId, $userId, $calendarId);
|
||||||
|
|
||||||
|
$entityId = $this->uriMap->getEntityId($mapId, $objectUri)
|
||||||
|
?? preg_replace('/\.ics$/i', '', $objectUri);
|
||||||
|
|
||||||
|
$target = new EntityIdentifier($providerId, $serviceId, $collectionId, $entityId);
|
||||||
|
$this->manager->entityDelete($tenantId, $userId, $target);
|
||||||
|
$this->uriMap->delete($mapId, $objectUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private function parseId(string $calendarId): array
|
||||||
|
{
|
||||||
|
return explode(self::ID_SEP, $calendarId, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapId(string $tenantId, string $userId, string $calendarId): string
|
||||||
|
{
|
||||||
|
return 'cal:' . $tenantId . ':' . $userId . ':' . $calendarId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function slugify(string $label): string
|
||||||
|
{
|
||||||
|
return strtolower(preg_replace('/[^a-z0-9]+/i', '-', trim($label)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize a domain entity to an iCalendar string.
|
||||||
|
*
|
||||||
|
* The raw data from getDataRaw() is a serialized EventObject array
|
||||||
|
* with fields matching EventCommonObject property names (label, startsOn, endsOn, ...).
|
||||||
|
*/
|
||||||
|
private function entityToIcal(object $entity): string
|
||||||
|
{
|
||||||
|
$raw = (array) ($entity->getProperties()->getDataRaw() ?? []);
|
||||||
|
$utc = new \DateTimeZone('UTC');
|
||||||
|
$vcal = new VCalendar();
|
||||||
|
|
||||||
|
// Create the VEVENT without sabre's auto-generated UID/DTSTAMP defaults,
|
||||||
|
// otherwise they appear twice (we set our own below), which is invalid.
|
||||||
|
/** @var \Sabre\VObject\Component $vevent */
|
||||||
|
$vevent = $vcal->add('VEVENT', [], false);
|
||||||
|
$vevent->add('UID', (string) $entity->identifier());
|
||||||
|
// Always emit date-times in UTC ("...Z"). A DateTime carrying a fixed
|
||||||
|
// numeric offset (e.g. from an RFC3339 string) would otherwise serialize
|
||||||
|
// as TZID=-04:00 with no matching VTIMEZONE, which clients reject.
|
||||||
|
// DTSTAMP participates in the serialized data used for the ETag. Using
|
||||||
|
// the current time here made an unchanged entity produce a different
|
||||||
|
// ETag on every PROPFIND/REPORT and triggered perpetual client syncs.
|
||||||
|
$stamp = $entity->modified() ?? $entity->created();
|
||||||
|
$vevent->add(
|
||||||
|
'DTSTAMP',
|
||||||
|
$stamp !== null
|
||||||
|
? \DateTime::createFromImmutable($stamp)->setTimezone($utc)
|
||||||
|
: (new \DateTime('@0'))->setTimezone($utc),
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($created = $entity->created()) {
|
||||||
|
$vevent->add('CREATED', \DateTime::createFromImmutable($created)->setTimezone($utc));
|
||||||
|
}
|
||||||
|
if ($modified = $entity->modified()) {
|
||||||
|
$vevent->add('LAST-MODIFIED', \DateTime::createFromImmutable($modified)->setTimezone($utc));
|
||||||
|
}
|
||||||
|
if (!empty($raw['label'])) {
|
||||||
|
$vevent->add('SUMMARY', (string) $raw['label']);
|
||||||
|
}
|
||||||
|
if (!empty($raw['description'])) {
|
||||||
|
$vevent->add('DESCRIPTION', (string) $raw['description']);
|
||||||
|
}
|
||||||
|
if (!empty($raw['startsOn'])) {
|
||||||
|
$vevent->add('DTSTART', (new \DateTime((string) $raw['startsOn']))->setTimezone($utc));
|
||||||
|
}
|
||||||
|
if (!empty($raw['endsOn'])) {
|
||||||
|
$vevent->add('DTEND', (new \DateTime((string) $raw['endsOn']))->setTimezone($utc));
|
||||||
|
}
|
||||||
|
// iCal supports one LOCATION property; use first physical location
|
||||||
|
if (!empty($raw['locationsPhysical']) && is_array($raw['locationsPhysical'])) {
|
||||||
|
foreach ($raw['locationsPhysical'] as $loc) {
|
||||||
|
$addr = $loc['address'] ?? $loc['name'] ?? $loc['label'] ?? null;
|
||||||
|
if ($addr) {
|
||||||
|
$vevent->add('LOCATION', (string) $addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $vcal->serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a parsed VCALENDAR to an array for entityCreate/entityUpdate.
|
||||||
|
* Field names match EventCommonObject property names used in serialization.
|
||||||
|
*/
|
||||||
|
private function icalToEntityProps(VCalendar $vcal): array
|
||||||
|
{
|
||||||
|
$comp = $vcal->VEVENT ?? $vcal->VTODO ?? $vcal->VJOURNAL ?? null;
|
||||||
|
if ($comp === null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$props = [];
|
||||||
|
if ($summary = (string) ($comp->SUMMARY ?? '')) {
|
||||||
|
$props['label'] = $summary;
|
||||||
|
}
|
||||||
|
if ($description = (string) ($comp->DESCRIPTION ?? '')) {
|
||||||
|
$props['description'] = $description;
|
||||||
|
}
|
||||||
|
if ($dtstart = $comp->DTSTART ?? null) {
|
||||||
|
$props['startsOn'] = $dtstart->getDateTime()->format(\DateTime::RFC3339);
|
||||||
|
}
|
||||||
|
if ($dtend = $comp->DTEND ?? $comp->DUE ?? null) {
|
||||||
|
$props['endsOn'] = $dtend->getDateTime()->format(\DateTime::RFC3339);
|
||||||
|
}
|
||||||
|
if ($location = (string) ($comp->LOCATION ?? '')) {
|
||||||
|
$props['locationsPhysical'] = [['label' => $location]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $props;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,370 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav\Backends;
|
||||||
|
|
||||||
|
use KTXC\SessionIdentity;
|
||||||
|
use KTXC\SessionTenant;
|
||||||
|
use KTXF\Resource\Identifier\CollectionIdentifier;
|
||||||
|
use KTXF\Resource\Identifier\EntityIdentifier;
|
||||||
|
use KTXF\Resource\Identifier\ResourceIdentifiers;
|
||||||
|
use KTXM\PeopleManager\Manager as PeopleManager;
|
||||||
|
use KTXM\ServiceDav\DavProperties;
|
||||||
|
use KTXM\ServiceDav\DavUriMap;
|
||||||
|
use Sabre\CardDAV\Backend\AbstractBackend;
|
||||||
|
use Sabre\VObject\Component\VCard;
|
||||||
|
use Sabre\VObject\Reader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CardDAV backend bridging sabre/dav to the KTXM PeopleManager.
|
||||||
|
*
|
||||||
|
* Address book <=> Collection (within a provider service)
|
||||||
|
* Card <=> Entity (individual / organization / group)
|
||||||
|
*
|
||||||
|
* Address book identifiers use the composite key "{providerId}:{serviceId}:{collectionId}".
|
||||||
|
* Card URIs are client-chosen names (e.g. "contact.vcf") mapped to provider
|
||||||
|
* entity identifiers via DavUriMap.
|
||||||
|
*/
|
||||||
|
class CardDavBackend extends AbstractBackend
|
||||||
|
{
|
||||||
|
private const ID_SEP = ':';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly PeopleManager $manager,
|
||||||
|
private readonly SessionIdentity $identity,
|
||||||
|
private readonly SessionTenant $tenant,
|
||||||
|
private readonly DavUriMap $uriMap,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Required: address book listing
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public function getAddressBooksForUser($principalUri): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
// collectionList returns [providerId][serviceId][collectionId] => CollectionBaseInterface
|
||||||
|
$allCollections = $this->manager->collectionList($this->tenant->identifier(), $this->identity->identifier());
|
||||||
|
|
||||||
|
foreach ($allCollections as $providerId => $providerServices) {
|
||||||
|
foreach ($providerServices as $serviceId => $collections) {
|
||||||
|
foreach ($collections as $collectionId => $collection) {
|
||||||
|
if (!is_object($collection)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$props = $collection->getProperties();
|
||||||
|
$label = $props->getLabel();
|
||||||
|
$abId = implode(self::ID_SEP, [(string) $providerId, (string) $serviceId, (string) $collectionId]);
|
||||||
|
|
||||||
|
$result[] = [
|
||||||
|
DavProperties::ID => $abId,
|
||||||
|
DavProperties::URI => $this->slugify($label),
|
||||||
|
DavProperties::DISPLAYNAME => $label,
|
||||||
|
DavProperties::CARDDAV_DESCRIPTION => $props->getDescription() ?? '',
|
||||||
|
DavProperties::CS_CTAG => $collection->signature() ?? '',
|
||||||
|
DavProperties::PRINCIPAL_URI => $principalUri,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch): void
|
||||||
|
{
|
||||||
|
// not supported
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createAddressBook($principalUri, $url, array $properties): string
|
||||||
|
{
|
||||||
|
throw new \Sabre\DAV\Exception\Forbidden('Address book creation is not supported via DAV');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteAddressBook($addressBookId): void
|
||||||
|
{
|
||||||
|
throw new \Sabre\DAV\Exception\Forbidden('Address book deletion is not supported via DAV');
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Required: cards
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public function getCards($addressBookId): array
|
||||||
|
{
|
||||||
|
$tenantId = $this->tenant->identifier();
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
|
||||||
|
$targets = ResourceIdentifiers::fromArray([$addressBookId]);
|
||||||
|
|
||||||
|
// entityListBulk returns [providerId][serviceId][collectionId] => [entityId => EntityBaseInterface]
|
||||||
|
$entitiesByLocation = $this->manager->entityListBulk($tenantId, $userId, $targets);
|
||||||
|
|
||||||
|
$mapId = $this->mapId($tenantId, $userId, $addressBookId);
|
||||||
|
$cards = [];
|
||||||
|
foreach ($entitiesByLocation as $providerServices) {
|
||||||
|
foreach ($providerServices as $serviceCollections) {
|
||||||
|
foreach ($serviceCollections as $entities) {
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
if (!is_object($entity)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$entityId = (string) $entity->identifier();
|
||||||
|
$objectUri = $this->uriMap->getObjectUri($mapId, $entityId) ?? ($entityId . '.vcf');
|
||||||
|
$data = $this->entityToVcard($entity);
|
||||||
|
|
||||||
|
$cards[] = [
|
||||||
|
DavProperties::ID => $entityId,
|
||||||
|
DavProperties::URI => $objectUri,
|
||||||
|
DavProperties::LAST_MODIFIED => $entity->modified()?->getTimestamp() ?? time(),
|
||||||
|
DavProperties::ETAG => '"' . md5($data) . '"',
|
||||||
|
DavProperties::SIZE => strlen($data),
|
||||||
|
DavProperties::CARD_DATA => null, // lazy
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCard($addressBookId, $cardUri): ?array
|
||||||
|
{
|
||||||
|
[$providerId, $serviceId, $collectionId] = $this->parseId($addressBookId);
|
||||||
|
$tenantId = $this->tenant->identifier();
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
$mapId = $this->mapId($tenantId, $userId, $addressBookId);
|
||||||
|
|
||||||
|
$entityId = $this->uriMap->getEntityId($mapId, $cardUri)
|
||||||
|
?? preg_replace('/\.vcf$/i', '', $cardUri);
|
||||||
|
|
||||||
|
$identifier = new EntityIdentifier($providerId, $serviceId, $collectionId, $entityId);
|
||||||
|
$fetched = $this->manager->entityFetchBulk($tenantId, $userId, $identifier);
|
||||||
|
$entity = reset($fetched) ?: null;
|
||||||
|
|
||||||
|
if ($entity === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $this->entityToVcard($entity);
|
||||||
|
|
||||||
|
return [
|
||||||
|
DavProperties::ID => $entityId,
|
||||||
|
DavProperties::URI => $cardUri,
|
||||||
|
DavProperties::LAST_MODIFIED => $entity->modified()?->getTimestamp() ?? time(),
|
||||||
|
DavProperties::ETAG => '"' . md5($data) . '"',
|
||||||
|
DavProperties::SIZE => strlen($data),
|
||||||
|
DavProperties::CARD_DATA => $data,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMultipleCards($addressBookId, array $uris): array
|
||||||
|
{
|
||||||
|
return array_values(array_filter(array_map(
|
||||||
|
fn($uri) => $this->getCard($addressBookId, $uri),
|
||||||
|
$uris,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createCard($addressBookId, $cardUri, $cardData): ?string
|
||||||
|
{
|
||||||
|
[$providerId, $serviceId, $collectionId] = $this->parseId($addressBookId);
|
||||||
|
$tenantId = $this->tenant->identifier();
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
|
||||||
|
$vcard = Reader::read($cardData);
|
||||||
|
$props = $this->vcardToEntityProps($vcard);
|
||||||
|
$target = new CollectionIdentifier($providerId, $serviceId, $collectionId);
|
||||||
|
$entity = $this->manager->entityCreate($tenantId, $userId, $target, $props);
|
||||||
|
|
||||||
|
$this->uriMap->set(
|
||||||
|
$this->mapId($tenantId, $userId, $addressBookId),
|
||||||
|
$cardUri,
|
||||||
|
(string) $entity->identifier(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return '"' . md5($this->entityToVcard($entity)) . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateCard($addressBookId, $cardUri, $cardData): ?string
|
||||||
|
{
|
||||||
|
[$providerId, $serviceId, $collectionId] = $this->parseId($addressBookId);
|
||||||
|
$tenantId = $this->tenant->identifier();
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
$mapId = $this->mapId($tenantId, $userId, $addressBookId);
|
||||||
|
|
||||||
|
$entityId = $this->uriMap->getEntityId($mapId, $cardUri)
|
||||||
|
?? preg_replace('/\.vcf$/i', '', $cardUri);
|
||||||
|
|
||||||
|
$vcard = Reader::read($cardData);
|
||||||
|
$props = $this->vcardToEntityProps($vcard);
|
||||||
|
$target = new EntityIdentifier($providerId, $serviceId, $collectionId, $entityId);
|
||||||
|
$entity = $this->manager->entityModify($tenantId, $userId, $target, $props);
|
||||||
|
|
||||||
|
return '"' . md5($this->entityToVcard($entity)) . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteCard($addressBookId, $cardUri): bool
|
||||||
|
{
|
||||||
|
[$providerId, $serviceId, $collectionId] = $this->parseId($addressBookId);
|
||||||
|
$tenantId = $this->tenant->identifier();
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
$mapId = $this->mapId($tenantId, $userId, $addressBookId);
|
||||||
|
|
||||||
|
$entityId = $this->uriMap->getEntityId($mapId, $cardUri)
|
||||||
|
?? preg_replace('/\.vcf$/i', '', $cardUri);
|
||||||
|
|
||||||
|
$target = new EntityIdentifier($providerId, $serviceId, $collectionId, $entityId);
|
||||||
|
$outcome = $this->manager->entityDelete($tenantId, $userId, $target);
|
||||||
|
|
||||||
|
// entityDelete returns outcomes keyed by source identifier; a non-error
|
||||||
|
// disposition ('deleted'|'moved') indicates the card no longer exists here.
|
||||||
|
$disposition = $outcome[(string) $target]['disposition'] ?? 'error';
|
||||||
|
$deleted = $disposition !== 'error';
|
||||||
|
if ($deleted) {
|
||||||
|
$this->uriMap->delete($mapId, $cardUri);
|
||||||
|
}
|
||||||
|
return $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private function parseId(string $id): array
|
||||||
|
{
|
||||||
|
return explode(self::ID_SEP, $id, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapId(string $tenantId, string $userId, string $addressBookId): string
|
||||||
|
{
|
||||||
|
return 'card:' . $tenantId . ':' . $userId . ':' . $addressBookId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function slugify(string $label): string
|
||||||
|
{
|
||||||
|
return strtolower(preg_replace('/[^a-z0-9]+/i', '-', trim($label)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize a domain people entity to a vCard 4.0 string.
|
||||||
|
*
|
||||||
|
* The raw data from getDataRaw() is a serialized IndividualObject array with
|
||||||
|
* fields: label, names (given/family/...), phones, emails, organizations, ...
|
||||||
|
*/
|
||||||
|
private function entityToVcard(object $entity): string
|
||||||
|
{
|
||||||
|
$raw = (array) ($entity->getProperties()->getDataRaw() ?? []);
|
||||||
|
$vcard = new VCard(['VERSION' => '4.0', 'UID' => (string) $entity->identifier()]);
|
||||||
|
|
||||||
|
// Full name (FN is required in vCard 4.0)
|
||||||
|
$fn = (string) ($raw['label'] ?? '');
|
||||||
|
$vcard->add('FN', $fn ?: 'Unknown');
|
||||||
|
|
||||||
|
// Structured name: N;family;given;additional;prefix;suffix
|
||||||
|
$names = (array) ($raw['names'] ?? []);
|
||||||
|
$family = (string) ($names['family'] ?? '');
|
||||||
|
$given = (string) ($names['given'] ?? '');
|
||||||
|
if ($family || $given) {
|
||||||
|
$vcard->add('N', [
|
||||||
|
'value' => [
|
||||||
|
$family,
|
||||||
|
$given,
|
||||||
|
(string) ($names['additional'] ?? ''),
|
||||||
|
(string) ($names['prefix'] ?? ''),
|
||||||
|
(string) ($names['suffix'] ?? ''),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// E-mail addresses
|
||||||
|
foreach ((array) ($raw['emails'] ?? []) as $email) {
|
||||||
|
if ($address = (string) ($email['address'] ?? '')) {
|
||||||
|
$vcard->add('EMAIL', $address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phone numbers
|
||||||
|
foreach ((array) ($raw['phones'] ?? []) as $phone) {
|
||||||
|
if ($number = (string) ($phone['number'] ?? '')) {
|
||||||
|
$vcard->add('TEL', $number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Organization (first entry only for simplicity)
|
||||||
|
foreach ((array) ($raw['organizations'] ?? []) as $org) {
|
||||||
|
$orgName = (string) ($org['Label'] ?? $org['label'] ?? '');
|
||||||
|
if ($orgName) {
|
||||||
|
$vcard->add('ORG', $orgName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $vcard->serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a parsed vCard into an array for entityCreate/entityUpdate.
|
||||||
|
* Field names match IndividualObject property names used in serialization.
|
||||||
|
*/
|
||||||
|
private function vcardToEntityProps(VCard $vcard): array
|
||||||
|
{
|
||||||
|
$props = [];
|
||||||
|
|
||||||
|
if ($fn = (string) ($vcard->FN ?? '')) {
|
||||||
|
$props['label'] = $fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($n = $vcard->N) {
|
||||||
|
$parts = $n->getParts();
|
||||||
|
$names = [];
|
||||||
|
if (!empty($parts[0])) {
|
||||||
|
$names['family'] = $parts[0];
|
||||||
|
}
|
||||||
|
if (!empty($parts[1])) {
|
||||||
|
$names['given'] = $parts[1];
|
||||||
|
}
|
||||||
|
if (!empty($parts[2])) {
|
||||||
|
$names['additional'] = $parts[2];
|
||||||
|
}
|
||||||
|
if (!empty($parts[3])) {
|
||||||
|
$names['prefix'] = $parts[3];
|
||||||
|
}
|
||||||
|
if (!empty($parts[4])) {
|
||||||
|
$names['suffix'] = $parts[4];
|
||||||
|
}
|
||||||
|
if ($names) {
|
||||||
|
$props['names'] = $names;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$emails = [];
|
||||||
|
foreach ($vcard->select('EMAIL') as $email) {
|
||||||
|
$emails[] = ['address' => (string) $email];
|
||||||
|
}
|
||||||
|
if ($emails) {
|
||||||
|
$props['emails'] = $emails;
|
||||||
|
}
|
||||||
|
|
||||||
|
$phones = [];
|
||||||
|
foreach ($vcard->select('TEL') as $tel) {
|
||||||
|
$phones[] = ['number' => (string) $tel];
|
||||||
|
}
|
||||||
|
if ($phones) {
|
||||||
|
$props['phones'] = $phones;
|
||||||
|
}
|
||||||
|
|
||||||
|
$orgs = [];
|
||||||
|
foreach ($vcard->select('ORG') as $org) {
|
||||||
|
$orgs[] = ['Label' => (string) $org];
|
||||||
|
}
|
||||||
|
if ($orgs) {
|
||||||
|
$props['organizations'] = $orgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $props;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav\Backends;
|
||||||
|
|
||||||
|
use KTXF\Documents\Collection\CollectionBaseInterface;
|
||||||
|
use KTXF\Documents\Service\ServiceBaseInterface;
|
||||||
|
use KTXF\Documents\Service\ServiceCollectionMutableInterface;
|
||||||
|
use KTXF\Documents\Service\ServiceEntityMutableInterface;
|
||||||
|
use KTXF\Resource\Identifier\CollectionIdentifier;
|
||||||
|
use KTXF\Resource\Identifier\EntityIdentifier;
|
||||||
|
use Sabre\DAV\Collection;
|
||||||
|
use Sabre\DAV\Exception\Forbidden;
|
||||||
|
use Sabre\DAV\INode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A folder-level WebDAV collection backed by a document collection.
|
||||||
|
*/
|
||||||
|
class FilesCollectionNode extends Collection
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ServiceBaseInterface $service,
|
||||||
|
private readonly CollectionBaseInterface $collection,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->collection->getProperties()->getLabel() ?? (string) $this->collection->identifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier of the collection backing this node
|
||||||
|
*/
|
||||||
|
private function collectionIdentifier(): CollectionIdentifier
|
||||||
|
{
|
||||||
|
return new CollectionIdentifier(
|
||||||
|
$this->service->provider(),
|
||||||
|
(string) $this->service->identifier(),
|
||||||
|
(string) $this->collection->identifier(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return INode[] */
|
||||||
|
public function getChildren(): array
|
||||||
|
{
|
||||||
|
$nodes = [];
|
||||||
|
|
||||||
|
// ----- collections (folders) -----
|
||||||
|
$decendents = $this->service->collectionList($this->collection->identifier());
|
||||||
|
foreach ($decendents as $decendent) {
|
||||||
|
$nodes[] = new FilesCollectionNode(
|
||||||
|
$this->service,
|
||||||
|
$decendent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- entities (files) -----
|
||||||
|
/** @var \KTXF\Documents\Entity\EntityBaseInterface[] $entities */
|
||||||
|
$entities = $this->service->entityListBulk($this->collection->identifier());
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
if (is_object($entity)) {
|
||||||
|
$nodes[] = new FilesEntityNode(
|
||||||
|
$this->service,
|
||||||
|
$entity,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createDirectory($name): void
|
||||||
|
{
|
||||||
|
if (!$this->service instanceof ServiceCollectionMutableInterface) {
|
||||||
|
throw new Forbidden('Service does not support collection creation');
|
||||||
|
}
|
||||||
|
|
||||||
|
$properties = $this->service->collectionFresh()->getProperties();
|
||||||
|
$properties->setLabel($name);
|
||||||
|
$this->service->collectionCreate($this->collectionIdentifier(), $properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createFile($name, $data = null): string
|
||||||
|
{
|
||||||
|
if (!$this->service instanceof ServiceEntityMutableInterface) {
|
||||||
|
throw new Forbidden('Service does not support entity creation');
|
||||||
|
}
|
||||||
|
|
||||||
|
$properties = $this->service->entityFresh()->getProperties();
|
||||||
|
$properties->setLabel($name);
|
||||||
|
$entity = $this->service->entityCreate($this->collectionIdentifier(), $properties);
|
||||||
|
|
||||||
|
if ($data !== null) {
|
||||||
|
$content = is_resource($data) ? stream_get_contents($data) : (string) $data;
|
||||||
|
$this->service->entityWrite(
|
||||||
|
new EntityIdentifier(
|
||||||
|
$this->service->provider(),
|
||||||
|
(string) $this->service->identifier(),
|
||||||
|
(string) $entity->collection(),
|
||||||
|
(string) $entity->identifier(),
|
||||||
|
),
|
||||||
|
$content,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '"' . md5($entity->identifier() . time()) . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(): void
|
||||||
|
{
|
||||||
|
if (!$this->service instanceof ServiceCollectionMutableInterface) {
|
||||||
|
throw new Forbidden('Service does not support collection deletion');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->service->collectionDelete($this->collectionIdentifier(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav\Backends;
|
||||||
|
|
||||||
|
use KTXF\Documents\Entity\EntityBaseInterface;
|
||||||
|
use KTXF\Documents\Service\ServiceBaseInterface;
|
||||||
|
use KTXF\Documents\Service\ServiceEntityMutableInterface;
|
||||||
|
use KTXF\Resource\Identifier\EntityIdentifier;
|
||||||
|
use Sabre\DAV\Exception\Forbidden;
|
||||||
|
use Sabre\DAV\File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A file node backed by a document entity.
|
||||||
|
*/
|
||||||
|
class FilesEntityNode extends File
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ServiceBaseInterface $service,
|
||||||
|
private readonly EntityBaseInterface $entity,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->entity->getProperties()->getLabel() ?? (string) $this->entity->identifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier of the entity backing this node
|
||||||
|
*/
|
||||||
|
private function entityIdentifier(): EntityIdentifier
|
||||||
|
{
|
||||||
|
return new EntityIdentifier(
|
||||||
|
$this->service->provider(),
|
||||||
|
(string) $this->service->identifier(),
|
||||||
|
(string) $this->entity->collection(),
|
||||||
|
(string) $this->entity->identifier(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return resource|string */
|
||||||
|
public function get()
|
||||||
|
{
|
||||||
|
return $this->service->entityRead($this->entityIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function put($data): ?string
|
||||||
|
{
|
||||||
|
if (!$this->service instanceof ServiceEntityMutableInterface) {
|
||||||
|
throw new Forbidden('Entity is immutable');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->service->entityWrite(
|
||||||
|
$this->entityIdentifier(),
|
||||||
|
is_resource($data) ? stream_get_contents($data) : (string) $data,
|
||||||
|
);
|
||||||
|
|
||||||
|
return '"' . md5($this->entity->identifier() . time()) . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(): void
|
||||||
|
{
|
||||||
|
if (!$this->service instanceof ServiceEntityMutableInterface) {
|
||||||
|
throw new Forbidden('Entity is immutable');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->service->entityDelete($this->entityIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContentType(): ?string
|
||||||
|
{
|
||||||
|
return $this->entity->getProperties()->getMime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getETag(): ?string
|
||||||
|
{
|
||||||
|
return '"' . $this->entity->signature() . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSize(): ?int
|
||||||
|
{
|
||||||
|
return $this->entity->getProperties()->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLastModified(): ?int
|
||||||
|
{
|
||||||
|
return $this->entity->modified()?->getTimestamp() ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav\Backends;
|
||||||
|
|
||||||
|
use KTXC\SessionIdentity;
|
||||||
|
use KTXC\SessionTenant;
|
||||||
|
use KTXM\DocumentsManager\Manager as DocumentsManager;
|
||||||
|
use Sabre\DAV\Collection;
|
||||||
|
use Sabre\DAV\Exception\Forbidden;
|
||||||
|
use Sabre\DAV\Exception\NotFound;
|
||||||
|
use Sabre\DAV\INode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The root WebDAV collection for a user's files.
|
||||||
|
*
|
||||||
|
* Structure: /dav/files/{providerLabel}/{serviceLabel}/...
|
||||||
|
*
|
||||||
|
* Each provider+service combination becomes a top-level folder.
|
||||||
|
*/
|
||||||
|
class FilesRootNode extends Collection
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DocumentsManager $manager,
|
||||||
|
private readonly SessionIdentity $identity,
|
||||||
|
private readonly SessionTenant $tenant,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'files';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return INode[] */
|
||||||
|
public function getChildren(): array
|
||||||
|
{
|
||||||
|
$nodes = [];
|
||||||
|
$services = $this->manager->serviceList($this->tenant->identifier(), $this->identity->identifier());
|
||||||
|
|
||||||
|
foreach ($services as $providerId => $providerServices) {
|
||||||
|
foreach ($providerServices as $serviceId => $service) {
|
||||||
|
$nodes[] = new FilesServiceNode(
|
||||||
|
$service,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createDirectory($name): void
|
||||||
|
{
|
||||||
|
throw new Forbidden('Cannot create a provider root directory');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav\Backends;
|
||||||
|
|
||||||
|
use KTXF\Documents\Service\ServiceBaseInterface;
|
||||||
|
use KTXF\Documents\Service\ServiceCollectionMutableInterface;
|
||||||
|
use Sabre\DAV\Collection;
|
||||||
|
use Sabre\DAV\Exception\Forbidden;
|
||||||
|
use Sabre\DAV\INode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service-level WebDAV collection.
|
||||||
|
*
|
||||||
|
* Maps one (providerId, serviceId) pair → a DAV folder whose children are
|
||||||
|
* the root-level document collections exposed by that service.
|
||||||
|
*/
|
||||||
|
class FilesServiceNode extends Collection
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ServiceBaseInterface $service,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->service->getLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return INode[] */
|
||||||
|
public function getChildren(): array
|
||||||
|
{
|
||||||
|
$nodes = [];
|
||||||
|
|
||||||
|
$collections = $this->service->collectionList(null);
|
||||||
|
foreach ($collections as $collection) {
|
||||||
|
$nodes[] = new FilesCollectionNode(
|
||||||
|
$this->service,
|
||||||
|
$collection,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var \KTXF\Documents\Entity\EntityBaseInterface[] $entities */
|
||||||
|
$entities = $this->service->entityListBulk(null);
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
if (is_object($entity)) {
|
||||||
|
$nodes[] = new FilesEntityNode(
|
||||||
|
$this->service,
|
||||||
|
$entity,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createDirectory($name): void
|
||||||
|
{
|
||||||
|
if (!$this->service instanceof ServiceCollectionMutableInterface) {
|
||||||
|
throw new Forbidden('Service does not support collection creation');
|
||||||
|
}
|
||||||
|
|
||||||
|
$properties = $this->service->collectionFresh()->getProperties();
|
||||||
|
$properties->setLabel($name);
|
||||||
|
$this->service->collectionCreate(null, $properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav\Backends;
|
||||||
|
|
||||||
|
use KTXC\SessionIdentity;
|
||||||
|
use KTXC\SessionTenant;
|
||||||
|
use KTXM\ServiceDav\DavProperties;
|
||||||
|
use Sabre\DAVACL\PrincipalBackend\BackendInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DAVACL principal backend.
|
||||||
|
*/
|
||||||
|
class PrincipalBackend implements BackendInterface
|
||||||
|
{
|
||||||
|
public const PRINCIPAL_PREFIX = 'principals';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly SessionIdentity $identity,
|
||||||
|
private readonly SessionTenant $tenant,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// BackendInterface
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public function getPrincipalsByPrefix($prefixPath): array
|
||||||
|
{
|
||||||
|
if ($prefixPath !== self::PRINCIPAL_PREFIX || $this->identity->identity() === null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$this->buildPrincipal()];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrincipalByPath($path): ?array
|
||||||
|
{
|
||||||
|
if ($this->identity->identity() === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$expected = self::PRINCIPAL_PREFIX . '/' . $this->identity->identifier();
|
||||||
|
if ($path !== $expected) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->buildPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroupMemberSet($principal): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroupMembership($principal): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGroupMemberSet($principal, $members): void
|
||||||
|
{
|
||||||
|
// not supported
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatePrincipal($path, $propPatch): int
|
||||||
|
{
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchPrincipals($prefixPath, $searchProperties, $test = 'allof'): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByUri($uri, $principalPrefix): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private function buildPrincipal(): array
|
||||||
|
{
|
||||||
|
$userId = $this->identity->identifier();
|
||||||
|
$label = $this->identity->label() ?? $userId;
|
||||||
|
$email = $this->identity->mailAddress() ?? '';
|
||||||
|
|
||||||
|
return [
|
||||||
|
DavProperties::URI => self::PRINCIPAL_PREFIX . '/' . $userId,
|
||||||
|
DavProperties::DISPLAYNAME => $label,
|
||||||
|
DavProperties::SABRE_EMAIL => $email,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav\Controllers;
|
||||||
|
|
||||||
|
use KTXC\Http\Request\Request;
|
||||||
|
use KTXC\Http\Response\Response;
|
||||||
|
use KTXF\Controller\ControllerAbstract;
|
||||||
|
use KTXF\Routing\Attributes\AnonymousPrefixRoute;
|
||||||
|
use KTXM\ServiceDav\DavServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry-point controller for all WebDAV / CalDAV / CardDAV requests.
|
||||||
|
*
|
||||||
|
* The route is registered with absolute: true so that the path is /dav/ (not
|
||||||
|
* /m/service_dav/dav/). Authentication is intentionally left anonymous at the
|
||||||
|
* KTXC framework level because sabre's own Auth plugin — which supports both
|
||||||
|
* HTTP Basic (for DAV clients) and the existing JWT session (for browser-based
|
||||||
|
* access) — handles all credential validation.
|
||||||
|
*/
|
||||||
|
class DavController extends ControllerAbstract
|
||||||
|
{
|
||||||
|
/** All HTTP methods that any compliant DAV client may issue. */
|
||||||
|
private const DAV_METHODS = [
|
||||||
|
'GET', 'OPTIONS', 'POST', 'PUT', 'DELETE',
|
||||||
|
'PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE',
|
||||||
|
'LOCK', 'UNLOCK', 'REPORT', 'ACL',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly DavServer $davServer,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
#[AnonymousPrefixRoute('/dav/', name: 'service.dav', methods: self::DAV_METHODS, absolute: true)]
|
||||||
|
public function handle(Request $request): Response
|
||||||
|
{
|
||||||
|
return $this->davServer->process($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Well-known DAV / CalDAV / CardDAV XML property names used across backends.
|
||||||
|
*/
|
||||||
|
final class DavProperties
|
||||||
|
{
|
||||||
|
// Generic DAV object fields
|
||||||
|
public const ID = 'id';
|
||||||
|
public const URI = 'uri';
|
||||||
|
public const PRINCIPAL_URI = 'principaluri';
|
||||||
|
public const LAST_MODIFIED = 'lastmodified';
|
||||||
|
public const ETAG = 'etag';
|
||||||
|
public const SIZE = 'size';
|
||||||
|
|
||||||
|
// CardDAV object data
|
||||||
|
public const CARD_DATA = 'carddata';
|
||||||
|
|
||||||
|
// CalDAV object data
|
||||||
|
public const CALENDAR_DATA = 'calendardata';
|
||||||
|
public const COMPONENT = 'component';
|
||||||
|
|
||||||
|
// DAV:
|
||||||
|
public const DISPLAYNAME = '{DAV:}displayname';
|
||||||
|
public const SYNC_TOKEN = '{DAV:}sync-token';
|
||||||
|
|
||||||
|
// SabreDAV
|
||||||
|
public const SABRE_EMAIL = '{http://sabredav.org/ns}email-address';
|
||||||
|
|
||||||
|
// CalDAV (RFC 4791)
|
||||||
|
public const CALDAV_DESCRIPTION = '{urn:ietf:params:xml:ns:caldav}calendar-description';
|
||||||
|
public const CALDAV_COMPONENTS = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
|
||||||
|
|
||||||
|
// CardDAV (RFC 6352)
|
||||||
|
public const CARDDAV_DESCRIPTION = '{urn:ietf:params:xml:ns:carddav}addressbook-description';
|
||||||
|
|
||||||
|
// Apple Calendar Server / iCal extensions
|
||||||
|
public const APPLE_COLOR = '{http://apple.com/ns/ical/}calendar-color';
|
||||||
|
public const CS_CTAG = '{http://calendarserver.org/ns/}getctag';
|
||||||
|
|
||||||
|
private function __construct() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav;
|
||||||
|
|
||||||
|
use DI\Attribute\Inject;
|
||||||
|
use KTXC\Http\Request\Request;
|
||||||
|
use KTXC\Http\Response\Response;
|
||||||
|
use KTXC\Http\Response\StreamedResponse;
|
||||||
|
use KTXC\Service\SecurityService;
|
||||||
|
use KTXC\SessionIdentity;
|
||||||
|
use KTXC\SessionTenant;
|
||||||
|
use KTXM\ChronoManager\Manager as ChronoManager;
|
||||||
|
use KTXM\DocumentsManager\Manager as DocumentsManager;
|
||||||
|
use KTXM\PeopleManager\Manager as PeopleManager;
|
||||||
|
use KTXM\ServiceDav\Auth\DavAuthBackend;
|
||||||
|
use KTXM\ServiceDav\Backends\CalDavBackend;
|
||||||
|
use KTXM\ServiceDav\Backends\CardDavBackend;
|
||||||
|
use KTXM\ServiceDav\Backends\FilesRootNode;
|
||||||
|
use KTXM\ServiceDav\Backends\PrincipalBackend;
|
||||||
|
use Sabre\CalDAV\CalendarRoot;
|
||||||
|
use Sabre\CalDAV\Plugin as CalDAVPlugin;
|
||||||
|
use Sabre\CardDAV\AddressBookRoot;
|
||||||
|
use Sabre\CardDAV\Plugin as CardDAVPlugin;
|
||||||
|
use Sabre\CardDAV\VCFExportPlugin;
|
||||||
|
use Sabre\DAV\Auth\Plugin as AuthPlugin;
|
||||||
|
use Sabre\DAV\Browser\Plugin as BrowserPlugin;
|
||||||
|
use Sabre\DAV\Locks\Backend\File as LocksBackendFile;
|
||||||
|
use Sabre\DAV\Locks\Plugin as LocksPlugin;
|
||||||
|
use Sabre\DAV\Server;
|
||||||
|
use Sabre\DAVACL\Plugin as DavACLPlugin;
|
||||||
|
use Sabre\DAVACL\PrincipalCollection;
|
||||||
|
use Sabre\HTTP\Request as SabreRequest;
|
||||||
|
use Sabre\HTTP\Response as SabreResponse;
|
||||||
|
|
||||||
|
class DavServer
|
||||||
|
{
|
||||||
|
private const BASE_URI = '/dav/';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly SessionTenant $tenant,
|
||||||
|
private readonly SessionIdentity $identity,
|
||||||
|
private readonly SecurityService $securityService,
|
||||||
|
private readonly DocumentsManager $documentsManager,
|
||||||
|
private readonly ChronoManager $chronoManager,
|
||||||
|
private readonly PeopleManager $peopleManager,
|
||||||
|
private readonly DavUriMap $uriMap,
|
||||||
|
#[Inject('rootDir')] private readonly string $rootDir,
|
||||||
|
#[Inject('kernel.debug')] private readonly bool $debug = false,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function process(Request $request): Response
|
||||||
|
{
|
||||||
|
$server = $this->getServer();
|
||||||
|
|
||||||
|
$sabreRequest = new SabreRequest(
|
||||||
|
$request->getMethod(),
|
||||||
|
$this->normalizeRequestUri($request->getRequestUri()),
|
||||||
|
$this->normalizeHeaders($request->headers->all()),
|
||||||
|
);
|
||||||
|
|
||||||
|
$body = $request->getContent(asResource: false);
|
||||||
|
if (!empty($body)) {
|
||||||
|
$sabreRequest->setBody($body);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sabreResponse = new SabreResponse();
|
||||||
|
|
||||||
|
$sabreRequest->setBaseUrl($server->getBaseUri());
|
||||||
|
|
||||||
|
$server->httpRequest = $sabreRequest;
|
||||||
|
$server->httpResponse = $sabreResponse;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$server->invokeMethod($sabreRequest, $sabreResponse, false);
|
||||||
|
} catch (\Sabre\DAV\Exception $e) {
|
||||||
|
try { $server->emit('exception', [$e]); } catch (\Throwable $ignore) {}
|
||||||
|
$sabreResponse->setStatus($e->getHTTPCode());
|
||||||
|
foreach ($e->getHTTPHeaders($server) as $name => $value) {
|
||||||
|
$sabreResponse->setHeader($name, $value);
|
||||||
|
}
|
||||||
|
$sabreResponse->setBody($this->serializeException($e, $server));
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
try { $server->emit('exception', [$e]); } catch (\Throwable $ignore) {}
|
||||||
|
$sabreResponse->setStatus(500);
|
||||||
|
$sabreResponse->setBody($this->serializeException($e, $server));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->buildKtxcResponse($sabreResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getServer(): Server
|
||||||
|
{
|
||||||
|
return $this->buildServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildServer(): Server
|
||||||
|
{
|
||||||
|
$principalBackend = new PrincipalBackend($this->identity, $this->tenant);
|
||||||
|
$calDavBackend = new CalDavBackend($this->chronoManager, $this->identity, $this->tenant, $this->uriMap);
|
||||||
|
$cardDavBackend = new CardDavBackend($this->peopleManager, $this->identity, $this->tenant, $this->uriMap);
|
||||||
|
|
||||||
|
$server = new Server([
|
||||||
|
new PrincipalCollection($principalBackend, PrincipalBackend::PRINCIPAL_PREFIX),
|
||||||
|
new CalendarRoot($principalBackend, $calDavBackend),
|
||||||
|
new AddressBookRoot($principalBackend, $cardDavBackend),
|
||||||
|
new FilesRootNode($this->documentsManager, $this->identity, $this->tenant),
|
||||||
|
]);
|
||||||
|
$server->setBaseUri(self::BASE_URI);
|
||||||
|
$server->debugExceptions = $this->debug;
|
||||||
|
|
||||||
|
// Authentication plugin — short-circuits when JWT/Bearer already populated
|
||||||
|
$authBackend = new DavAuthBackend($this->identity, $this->securityService);
|
||||||
|
$server->addPlugin(new AuthPlugin($authBackend));
|
||||||
|
|
||||||
|
// DAVACL — required for CalDAV / CardDAV principal resolution
|
||||||
|
$aclPlugin = new DavACLPlugin();
|
||||||
|
$userId = (string) ($this->identity->identifier() ?? 'anonymous');
|
||||||
|
$aclPlugin->adminPrincipals = [PrincipalBackend::PRINCIPAL_PREFIX . '/' . $userId];
|
||||||
|
$server->addPlugin($aclPlugin);
|
||||||
|
|
||||||
|
$server->addPlugin(new CalDAVPlugin());
|
||||||
|
$server->addPlugin(new CardDAVPlugin());
|
||||||
|
$server->addPlugin(new VCFExportPlugin());
|
||||||
|
|
||||||
|
// File-based lock backend (single-server; swap for DB/Redis in multi-node setups)
|
||||||
|
$davDir = $this->rootDir . '/var/dav';
|
||||||
|
if (!is_dir($davDir)) {
|
||||||
|
mkdir($davDir, 0750, true);
|
||||||
|
}
|
||||||
|
$server->addPlugin(new LocksPlugin(new LocksBackendFile($davDir . '/locks.db')));
|
||||||
|
|
||||||
|
$server->addPlugin(new BrowserPlugin());
|
||||||
|
|
||||||
|
return $server;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function serializeException(\Throwable $e, Server $server): string
|
||||||
|
{
|
||||||
|
$h = static fn (string $s): string => htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
|
||||||
|
|
||||||
|
$dom = new \DOMDocument('1.0', 'utf-8');
|
||||||
|
$dom->formatOutput = true;
|
||||||
|
$error = $dom->createElementNS('http://sabredav.org/ns', 's:error');
|
||||||
|
$error->setAttribute('xmlns:s', 'http://sabredav.org/ns');
|
||||||
|
$dom->appendChild($error);
|
||||||
|
$error->appendChild($dom->createElement('s:exception', $h(get_class($e))));
|
||||||
|
$error->appendChild($dom->createElement('s:message', $h($e->getMessage())));
|
||||||
|
|
||||||
|
if ($this->debug) {
|
||||||
|
$error->appendChild($dom->createElement('s:file', $h($e->getFile())));
|
||||||
|
$error->appendChild($dom->createElement('s:line', $h((string) $e->getLine())));
|
||||||
|
$error->appendChild($dom->createElement('s:code', $h((string) $e->getCode())));
|
||||||
|
$error->appendChild($dom->createElement('s:stacktrace', $h($e->getTraceAsString())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($e instanceof \Sabre\DAV\Exception) {
|
||||||
|
$e->serialize($server, $error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dom->saveXML();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// HTTP bridge helpers
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private function normalizeRequestUri(string $uri): string
|
||||||
|
{
|
||||||
|
// Ensure requests for the bare base path (e.g. /dav) include the trailing
|
||||||
|
// slash so sabre's getPath() keeps them within the base URI /dav/.
|
||||||
|
$path = strtok($uri, '?');
|
||||||
|
if ($path === rtrim(self::BASE_URI, '/')) {
|
||||||
|
return self::BASE_URI . (str_contains($uri, '?') ? '?' . explode('?', $uri, 2)[1] : '');
|
||||||
|
}
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizeHeaders(array $symfonyHeaders): array
|
||||||
|
{
|
||||||
|
$normalized = [];
|
||||||
|
foreach ($symfonyHeaders as $name => $values) {
|
||||||
|
$key = str_replace(' ', '-', ucwords(str_replace('-', ' ', strtolower($name))));
|
||||||
|
$normalized[$key] = implode(', ', (array) $values);
|
||||||
|
}
|
||||||
|
return $normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildKtxcResponse(SabreResponse $sabreResponse): Response
|
||||||
|
{
|
||||||
|
$status = $sabreResponse->getStatus();
|
||||||
|
$body = $sabreResponse->getBody();
|
||||||
|
|
||||||
|
// Collect headers; skip security headers that KTXC re-applies by default
|
||||||
|
$headers = [];
|
||||||
|
foreach ($sabreResponse->getHeaders() as $name => $value) {
|
||||||
|
$headers[$name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (is_resource($body)) {
|
||||||
|
// Stream the body to avoid buffering potentially large files
|
||||||
|
$stream = $body;
|
||||||
|
return new StreamedResponse(
|
||||||
|
static function () use ($stream): void {
|
||||||
|
while (!feof($stream)) {
|
||||||
|
echo fread($stream, 8192);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
$status,
|
||||||
|
$headers,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response((string) ($body ?? ''), $status, $headers);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav;
|
||||||
|
|
||||||
|
use DI\Attribute\Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bidirectional mapping between DAV object URIs (chosen by clients) and entity
|
||||||
|
* identifiers assigned by the provider stores.
|
||||||
|
*
|
||||||
|
* Stored as a pair of JSON files per logical "map" (keyed by a mapId that
|
||||||
|
* encodes tenant / user / calendar-or-addressbook context). File locking is
|
||||||
|
* used to guard against concurrent modifications.
|
||||||
|
*
|
||||||
|
* This class is intentionally simple: it is a best-effort persistence layer for
|
||||||
|
* single-server deployments. A database-backed implementation should be
|
||||||
|
* substituted for high-concurrency or multi-node environments.
|
||||||
|
*/
|
||||||
|
class DavUriMap
|
||||||
|
{
|
||||||
|
private string $dir;
|
||||||
|
|
||||||
|
public function __construct(#[Inject('rootDir')] string $rootDir)
|
||||||
|
{
|
||||||
|
$this->dir = $rootDir . '/var/dav';
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Public API
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up the provider entity identifier for a given DAV object URI.
|
||||||
|
*
|
||||||
|
* @param string $mapId Logical map bucket (e.g. "{tenant}:{user}:cal:{calId}")
|
||||||
|
* @param string $objectUri DAV leaf URI (e.g. "abc-123.ics")
|
||||||
|
*/
|
||||||
|
public function getEntityId(string $mapId, string $objectUri): ?string
|
||||||
|
{
|
||||||
|
return $this->read($mapId)['obj_to_ent'][$objectUri] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up the DAV object URI for a given provider entity identifier.
|
||||||
|
*/
|
||||||
|
public function getObjectUri(string $mapId, string $entityId): ?string
|
||||||
|
{
|
||||||
|
return $this->read($mapId)['ent_to_obj'][(string) $entityId] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record a mapping from a DAV object URI to a provider entity identifier.
|
||||||
|
*/
|
||||||
|
public function set(string $mapId, string $objectUri, string|int $entityId): void
|
||||||
|
{
|
||||||
|
$entityId = (string) $entityId;
|
||||||
|
$this->update($mapId, static function (array $data) use ($objectUri, $entityId): array {
|
||||||
|
$data['obj_to_ent'][$objectUri] = $entityId;
|
||||||
|
$data['ent_to_obj'][$entityId] = $objectUri;
|
||||||
|
return $data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the mapping for a DAV object URI.
|
||||||
|
*/
|
||||||
|
public function delete(string $mapId, string $objectUri): void
|
||||||
|
{
|
||||||
|
$this->update($mapId, static function (array $data) use ($objectUri): array {
|
||||||
|
$entityId = $data['obj_to_ent'][$objectUri] ?? null;
|
||||||
|
if ($entityId !== null) {
|
||||||
|
unset($data['ent_to_obj'][$entityId]);
|
||||||
|
}
|
||||||
|
unset($data['obj_to_ent'][$objectUri]);
|
||||||
|
return $data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Internal helpers
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private function mapFile(string $mapId): string
|
||||||
|
{
|
||||||
|
return $this->dir . '/map_' . md5($mapId) . '.json';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function read(string $mapId): array
|
||||||
|
{
|
||||||
|
$file = $this->mapFile($mapId);
|
||||||
|
if (!file_exists($file)) {
|
||||||
|
return ['obj_to_ent' => [], 'ent_to_obj' => []];
|
||||||
|
}
|
||||||
|
$contents = file_get_contents($file);
|
||||||
|
if ($contents === false) {
|
||||||
|
return ['obj_to_ent' => [], 'ent_to_obj' => []];
|
||||||
|
}
|
||||||
|
return json_decode($contents, true) ?? ['obj_to_ent' => [], 'ent_to_obj' => []];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function update(string $mapId, callable $updater): void
|
||||||
|
{
|
||||||
|
if (!is_dir($this->dir)) {
|
||||||
|
mkdir($this->dir, 0750, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->mapFile($mapId);
|
||||||
|
$fh = fopen($file, 'c+');
|
||||||
|
if ($fh === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
flock($fh, LOCK_EX);
|
||||||
|
$contents = stream_get_contents($fh);
|
||||||
|
$data = json_decode($contents ?: '{}', true)
|
||||||
|
?? ['obj_to_ent' => [], 'ent_to_obj' => []];
|
||||||
|
if (!isset($data['obj_to_ent'])) {
|
||||||
|
$data['obj_to_ent'] = [];
|
||||||
|
}
|
||||||
|
if (!isset($data['ent_to_obj'])) {
|
||||||
|
$data['ent_to_obj'] = [];
|
||||||
|
}
|
||||||
|
$data = $updater($data);
|
||||||
|
ftruncate($fh, 0);
|
||||||
|
rewind($fh);
|
||||||
|
fwrite($fh, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||||
|
} finally {
|
||||||
|
flock($fh, LOCK_UN);
|
||||||
|
fclose($fh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace KTXM\ServiceDav;
|
||||||
|
|
||||||
|
use KTXF\Module\ModuleInstanceAbstract;
|
||||||
|
|
||||||
|
class Module extends ModuleInstanceAbstract
|
||||||
|
{
|
||||||
|
public function handle(): string
|
||||||
|
{
|
||||||
|
return 'service_dav';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return 'DAV Service';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function author(): string
|
||||||
|
{
|
||||||
|
return 'Ktrix';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function description(): string
|
||||||
|
{
|
||||||
|
return 'WebDAV / CalDAV / CardDAV service — exposes files, calendars, and contacts to DAV clients';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function version(): string
|
||||||
|
{
|
||||||
|
return '0.0.1';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function permissions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'service_dav.access' => [
|
||||||
|
'label' => 'Access DAV Service',
|
||||||
|
'description' => 'Allow access to the WebDAV / CalDAV / CardDAV service',
|
||||||
|
'group' => 'DAV Service',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function boot(): void
|
||||||
|
{
|
||||||
|
// Load sabre/dav and its dependencies from the module-local vendor directory.
|
||||||
|
$vendorAutoload = __DIR__ . '/vendor/autoload.php';
|
||||||
|
if (file_exists($vendorAutoload)) {
|
||||||
|
require_once $vendorAutoload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+22
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 50600) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, $err);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo $err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException($err);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInita07591154e0386cfc9ad0f9217878d04::getLoader();
|
||||||
+120
@@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../sabre/vobject/bin/generate_vcards)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = $this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
include("phpvfscomposer://" . __DIR__ . '/..'.'/sabre/vobject/bin/generate_vcards');
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include __DIR__ . '/..'.'/sabre/vobject/bin/generate_vcards';
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
setlocal DISABLEDELAYEDEXPANSION
|
||||||
|
SET BIN_TARGET=%~dp0/generate_vcards
|
||||||
|
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
|
||||||
|
php "%BIN_TARGET%" %*
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Support bash to support `source` with fallback on $0 if this does not run with bash
|
||||||
|
# https://stackoverflow.com/a/35006505/6512
|
||||||
|
selfArg="$BASH_SOURCE"
|
||||||
|
if [ -z "$selfArg" ]; then
|
||||||
|
selfArg="$0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
self=$(realpath $selfArg 2> /dev/null)
|
||||||
|
if [ -z "$self" ]; then
|
||||||
|
self="$selfArg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dir=$(cd "${self%[/\\]*}" > /dev/null; cd '../sabre/dav/bin' && pwd)
|
||||||
|
|
||||||
|
if [ -d /proc/cygdrive ]; then
|
||||||
|
case $(which php) in
|
||||||
|
$(readlink -n /proc/cygdrive)/*)
|
||||||
|
# We are in Cygwin using Windows php, so the path must be translated
|
||||||
|
dir=$(cygpath -m "$dir");
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
export COMPOSER_RUNTIME_BIN_DIR="$(cd "${self%[/\\]*}" > /dev/null; pwd)"
|
||||||
|
|
||||||
|
# If bash is sourcing this file, we have to source the target as well
|
||||||
|
bashSource="$BASH_SOURCE"
|
||||||
|
if [ -n "$bashSource" ]; then
|
||||||
|
if [ "$bashSource" != "$0" ]; then
|
||||||
|
source "${dir}/naturalselection" "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${dir}/naturalselection" "$@"
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
setlocal DISABLEDELAYEDEXPANSION
|
||||||
|
SET BIN_TARGET=%~dp0/../sabre/dav/bin/naturalselection
|
||||||
|
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
|
||||||
|
python "%BIN_TARGET%" %*
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Support bash to support `source` with fallback on $0 if this does not run with bash
|
||||||
|
# https://stackoverflow.com/a/35006505/6512
|
||||||
|
selfArg="$BASH_SOURCE"
|
||||||
|
if [ -z "$selfArg" ]; then
|
||||||
|
selfArg="$0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
self=$(realpath $selfArg 2> /dev/null)
|
||||||
|
if [ -z "$self" ]; then
|
||||||
|
self="$selfArg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dir=$(cd "${self%[/\\]*}" > /dev/null; cd '../sabre/dav/bin' && pwd)
|
||||||
|
|
||||||
|
if [ -d /proc/cygdrive ]; then
|
||||||
|
case $(which php) in
|
||||||
|
$(readlink -n /proc/cygdrive)/*)
|
||||||
|
# We are in Cygwin using Windows php, so the path must be translated
|
||||||
|
dir=$(cygpath -m "$dir");
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
export COMPOSER_RUNTIME_BIN_DIR="$(cd "${self%[/\\]*}" > /dev/null; pwd)"
|
||||||
|
|
||||||
|
# If bash is sourcing this file, we have to source the target as well
|
||||||
|
bashSource="$BASH_SOURCE"
|
||||||
|
if [ -n "$bashSource" ]; then
|
||||||
|
if [ "$bashSource" != "$0" ]; then
|
||||||
|
source "${dir}/sabredav" "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${dir}/sabredav" "$@"
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
setlocal DISABLEDELAYEDEXPANSION
|
||||||
|
SET BIN_TARGET=%~dp0/../sabre/dav/bin/sabredav
|
||||||
|
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
|
||||||
|
sh "%BIN_TARGET%" %*
|
||||||
+120
@@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../sabre/vobject/bin/vobject)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = $this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
include("phpvfscomposer://" . __DIR__ . '/..'.'/sabre/vobject/bin/vobject');
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include __DIR__ . '/..'.'/sabre/vobject/bin/vobject';
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
setlocal DISABLEDELAYEDEXPANSION
|
||||||
|
SET BIN_TARGET=%~dp0/vobject
|
||||||
|
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
|
||||||
|
php "%BIN_TARGET%" %*
|
||||||
Vendored
+579
@@ -0,0 +1,579 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see https://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see https://www.php-fig.org/psr/psr-4/
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
/** @var \Closure(string):void */
|
||||||
|
private static $includeFile;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $vendorDir;
|
||||||
|
|
||||||
|
// PSR-4
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string, int>>
|
||||||
|
*/
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var array<string, list<string>>
|
||||||
|
*/
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
/**
|
||||||
|
* List of PSR-0 prefixes
|
||||||
|
*
|
||||||
|
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||||
|
*
|
||||||
|
* @var array<string, array<string, list<string>>>
|
||||||
|
*/
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $useIncludePath = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private $classMap = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, bool>
|
||||||
|
*/
|
||||||
|
private $missingClasses = array();
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $apcuPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, self>
|
||||||
|
*/
|
||||||
|
private static $registeredLoaders = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $vendorDir
|
||||||
|
*/
|
||||||
|
public function __construct($vendorDir = null)
|
||||||
|
{
|
||||||
|
$this->vendorDir = $vendorDir;
|
||||||
|
self::initializeIncludeClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string> Array of classname => path
|
||||||
|
*/
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $classMap Class to filename map
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 base directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||||
|
*
|
||||||
|
* @param string|null $apcuPrefix
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setApcuPrefix($apcuPrefix)
|
||||||
|
{
|
||||||
|
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getApcuPrefix()
|
||||||
|
{
|
||||||
|
return $this->apcuPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
|
||||||
|
if (null === $this->vendorDir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prepend) {
|
||||||
|
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||||
|
} else {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
|
||||||
|
if (null !== $this->vendorDir) {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return true|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
$includeFile = self::$includeFile;
|
||||||
|
$includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||||
|
if ($hit) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if (false === $file && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
apcu_add($this->apcuPrefix.$class, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $file) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
$this->missingClasses[$class] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||||
|
*
|
||||||
|
* @return array<string, self>
|
||||||
|
*/
|
||||||
|
public static function getRegisteredLoaders()
|
||||||
|
{
|
||||||
|
return self::$registeredLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class
|
||||||
|
* @param string $ext
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
$subPath = $class;
|
||||||
|
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||||
|
$subPath = substr($subPath, 0, $lastPos);
|
||||||
|
$search = $subPath . '\\';
|
||||||
|
if (isset($this->prefixDirsPsr4[$search])) {
|
||||||
|
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||||
|
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||||
|
if (file_exists($file = $dir . $pathEnd)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function initializeIncludeClosure()
|
||||||
|
{
|
||||||
|
if (self::$includeFile !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
self::$includeFile = \Closure::bind(static function($file) {
|
||||||
|
include $file;
|
||||||
|
}, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
+380
@@ -0,0 +1,380 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
|
use Composer\Semver\VersionParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is copied in every Composer installed project and available to all
|
||||||
|
*
|
||||||
|
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||||
|
*
|
||||||
|
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class InstalledVersions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private static $selfDir = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var mixed[]|null
|
||||||
|
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||||
|
*/
|
||||||
|
private static $installed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
private static $canGetVendors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static $installedByVendor = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackages()
|
||||||
|
{
|
||||||
|
$packages = array();
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
$packages[] = array_keys($installed['versions']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 === \count($packages)) {
|
||||||
|
return $packages[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names with a specific type e.g. 'library'
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackagesByType($type)
|
||||||
|
{
|
||||||
|
$packagesByType = array();
|
||||||
|
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
foreach ($installed['versions'] as $name => $package) {
|
||||||
|
if (isset($package['type']) && $package['type'] === $type) {
|
||||||
|
$packagesByType[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $packagesByType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package is installed
|
||||||
|
*
|
||||||
|
* This also returns true if the package name is provided or replaced by another package
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @param bool $includeDevRequirements
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (isset($installed['versions'][$packageName])) {
|
||||||
|
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package satisfies a version constraint
|
||||||
|
*
|
||||||
|
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||||
|
*
|
||||||
|
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||||
|
*
|
||||||
|
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||||
|
* @param string $packageName
|
||||||
|
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||||
|
{
|
||||||
|
$constraint = $parser->parseConstraints((string) $constraint);
|
||||||
|
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||||
|
|
||||||
|
return $provided->matches($constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||||
|
*
|
||||||
|
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||||
|
* whether a given version of a package is installed, and not just whether it exists
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string Version constraint usable with composer/semver
|
||||||
|
*/
|
||||||
|
public static function getVersionRanges($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ranges = array();
|
||||||
|
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(' || ', $ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getPrettyVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||||
|
*/
|
||||||
|
public static function getReference($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['reference'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||||
|
*/
|
||||||
|
public static function getInstallPath($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||||
|
*/
|
||||||
|
public static function getRootPackage()
|
||||||
|
{
|
||||||
|
$installed = self::getInstalled();
|
||||||
|
|
||||||
|
return $installed[0]['root'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw installed.php data for custom implementations
|
||||||
|
*
|
||||||
|
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||||
|
*/
|
||||||
|
public static function getRawData()
|
||||||
|
{
|
||||||
|
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
self::$installed = include __DIR__ . '/installed.php';
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
public static function getAllRawData()
|
||||||
|
{
|
||||||
|
return self::getInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you reload the static array from another file
|
||||||
|
*
|
||||||
|
* This is only useful for complex integrations in which a project needs to use
|
||||||
|
* this class but then also needs to execute another project's autoloader in process,
|
||||||
|
* and wants to ensure both projects have access to their version of installed.php.
|
||||||
|
*
|
||||||
|
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||||
|
* the data it needs from this class, then call reload() with
|
||||||
|
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||||
|
* the project in which it runs can then also use this class safely, without
|
||||||
|
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||||
|
*
|
||||||
|
* @param array[] $data A vendor/composer/installed.php data set
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||||
|
*/
|
||||||
|
public static function reload($data)
|
||||||
|
{
|
||||||
|
self::$installed = $data;
|
||||||
|
self::$installedByVendor = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function getSelfDir()
|
||||||
|
{
|
||||||
|
if (self::$selfDir === null) {
|
||||||
|
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$selfDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static function getInstalled()
|
||||||
|
{
|
||||||
|
if (null === self::$canGetVendors) {
|
||||||
|
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||||
|
}
|
||||||
|
|
||||||
|
$installed = array();
|
||||||
|
$copiedLocalDir = false;
|
||||||
|
|
||||||
|
if (self::$canGetVendors) {
|
||||||
|
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||||
|
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||||
|
$installed[] = self::$installedByVendor[$vendorDir];
|
||||||
|
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require $vendorDir.'/composer/installed.php';
|
||||||
|
self::$installedByVendor[$vendorDir] = $required;
|
||||||
|
$installed[] = $required;
|
||||||
|
if (strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||||
|
self::$installed = $required;
|
||||||
|
$copiedLocalDir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require __DIR__ . '/installed.php';
|
||||||
|
self::$installed = $required;
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||||
|
$installed[] = self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
|
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.
|
||||||
+370
@@ -0,0 +1,370 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname(dirname($vendorDir));
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||||
|
'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/src/AbstractLogger.php',
|
||||||
|
'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/src/InvalidArgumentException.php',
|
||||||
|
'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/src/LogLevel.php',
|
||||||
|
'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/src/LoggerAwareInterface.php',
|
||||||
|
'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/src/LoggerAwareTrait.php',
|
||||||
|
'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/src/LoggerInterface.php',
|
||||||
|
'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/src/LoggerTrait.php',
|
||||||
|
'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/src/NullLogger.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\AbstractBackend' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\BackendInterface' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/BackendInterface.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\NotificationSupport' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\PDO' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/PDO.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\SchedulingSupport' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\SharingSupport' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/SharingSupport.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\SimplePDO' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/SimplePDO.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\SubscriptionSupport' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\SyncSupport' => $vendorDir . '/sabre/dav/lib/CalDAV/Backend/SyncSupport.php',
|
||||||
|
'Sabre\\CalDAV\\Calendar' => $vendorDir . '/sabre/dav/lib/CalDAV/Calendar.php',
|
||||||
|
'Sabre\\CalDAV\\CalendarHome' => $vendorDir . '/sabre/dav/lib/CalDAV/CalendarHome.php',
|
||||||
|
'Sabre\\CalDAV\\CalendarObject' => $vendorDir . '/sabre/dav/lib/CalDAV/CalendarObject.php',
|
||||||
|
'Sabre\\CalDAV\\CalendarQueryValidator' => $vendorDir . '/sabre/dav/lib/CalDAV/CalendarQueryValidator.php',
|
||||||
|
'Sabre\\CalDAV\\CalendarRoot' => $vendorDir . '/sabre/dav/lib/CalDAV/CalendarRoot.php',
|
||||||
|
'Sabre\\CalDAV\\Exception\\InvalidComponentType' => $vendorDir . '/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php',
|
||||||
|
'Sabre\\CalDAV\\ICSExportPlugin' => $vendorDir . '/sabre/dav/lib/CalDAV/ICSExportPlugin.php',
|
||||||
|
'Sabre\\CalDAV\\ICalendar' => $vendorDir . '/sabre/dav/lib/CalDAV/ICalendar.php',
|
||||||
|
'Sabre\\CalDAV\\ICalendarObject' => $vendorDir . '/sabre/dav/lib/CalDAV/ICalendarObject.php',
|
||||||
|
'Sabre\\CalDAV\\ICalendarObjectContainer' => $vendorDir . '/sabre/dav/lib/CalDAV/ICalendarObjectContainer.php',
|
||||||
|
'Sabre\\CalDAV\\ISharedCalendar' => $vendorDir . '/sabre/dav/lib/CalDAV/ISharedCalendar.php',
|
||||||
|
'Sabre\\CalDAV\\Notifications\\Collection' => $vendorDir . '/sabre/dav/lib/CalDAV/Notifications/Collection.php',
|
||||||
|
'Sabre\\CalDAV\\Notifications\\ICollection' => $vendorDir . '/sabre/dav/lib/CalDAV/Notifications/ICollection.php',
|
||||||
|
'Sabre\\CalDAV\\Notifications\\INode' => $vendorDir . '/sabre/dav/lib/CalDAV/Notifications/INode.php',
|
||||||
|
'Sabre\\CalDAV\\Notifications\\Node' => $vendorDir . '/sabre/dav/lib/CalDAV/Notifications/Node.php',
|
||||||
|
'Sabre\\CalDAV\\Notifications\\Plugin' => $vendorDir . '/sabre/dav/lib/CalDAV/Notifications/Plugin.php',
|
||||||
|
'Sabre\\CalDAV\\Plugin' => $vendorDir . '/sabre/dav/lib/CalDAV/Plugin.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\Collection' => $vendorDir . '/sabre/dav/lib/CalDAV/Principal/Collection.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\IProxyRead' => $vendorDir . '/sabre/dav/lib/CalDAV/Principal/IProxyRead.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\IProxyWrite' => $vendorDir . '/sabre/dav/lib/CalDAV/Principal/IProxyWrite.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\ProxyRead' => $vendorDir . '/sabre/dav/lib/CalDAV/Principal/ProxyRead.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\ProxyWrite' => $vendorDir . '/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\User' => $vendorDir . '/sabre/dav/lib/CalDAV/Principal/User.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\IInbox' => $vendorDir . '/sabre/dav/lib/CalDAV/Schedule/IInbox.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\IMipPlugin' => $vendorDir . '/sabre/dav/lib/CalDAV/Schedule/IMipPlugin.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\IOutbox' => $vendorDir . '/sabre/dav/lib/CalDAV/Schedule/IOutbox.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\ISchedulingObject' => $vendorDir . '/sabre/dav/lib/CalDAV/Schedule/ISchedulingObject.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\Inbox' => $vendorDir . '/sabre/dav/lib/CalDAV/Schedule/Inbox.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\Outbox' => $vendorDir . '/sabre/dav/lib/CalDAV/Schedule/Outbox.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\Plugin' => $vendorDir . '/sabre/dav/lib/CalDAV/Schedule/Plugin.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\SchedulingObject' => $vendorDir . '/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php',
|
||||||
|
'Sabre\\CalDAV\\SharedCalendar' => $vendorDir . '/sabre/dav/lib/CalDAV/SharedCalendar.php',
|
||||||
|
'Sabre\\CalDAV\\SharingPlugin' => $vendorDir . '/sabre/dav/lib/CalDAV/SharingPlugin.php',
|
||||||
|
'Sabre\\CalDAV\\Subscriptions\\ISubscription' => $vendorDir . '/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php',
|
||||||
|
'Sabre\\CalDAV\\Subscriptions\\Plugin' => $vendorDir . '/sabre/dav/lib/CalDAV/Subscriptions/Plugin.php',
|
||||||
|
'Sabre\\CalDAV\\Subscriptions\\Subscription' => $vendorDir . '/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Filter\\CalendarData' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Filter\\CompFilter' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Filter\\ParamFilter' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Filter\\PropFilter' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Notification\\Invite' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Notification\\InviteReply' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Notification\\NotificationInterface' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Notification\\SystemStatus' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Notification/SystemStatus.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\AllowedSharingModes' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\EmailAddressSet' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\Invite' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Property/Invite.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\ScheduleCalendarTransp' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarData' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\SupportedCollationSet' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\CalendarMultiGetReport' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\CalendarQueryReport' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\FreeBusyQueryReport' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\InviteReply' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\MkCalendar' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\Share' => $vendorDir . '/sabre/dav/lib/CalDAV/Xml/Request/Share.php',
|
||||||
|
'Sabre\\CardDAV\\AddressBook' => $vendorDir . '/sabre/dav/lib/CardDAV/AddressBook.php',
|
||||||
|
'Sabre\\CardDAV\\AddressBookHome' => $vendorDir . '/sabre/dav/lib/CardDAV/AddressBookHome.php',
|
||||||
|
'Sabre\\CardDAV\\AddressBookRoot' => $vendorDir . '/sabre/dav/lib/CardDAV/AddressBookRoot.php',
|
||||||
|
'Sabre\\CardDAV\\Backend\\AbstractBackend' => $vendorDir . '/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php',
|
||||||
|
'Sabre\\CardDAV\\Backend\\BackendInterface' => $vendorDir . '/sabre/dav/lib/CardDAV/Backend/BackendInterface.php',
|
||||||
|
'Sabre\\CardDAV\\Backend\\PDO' => $vendorDir . '/sabre/dav/lib/CardDAV/Backend/PDO.php',
|
||||||
|
'Sabre\\CardDAV\\Backend\\SyncSupport' => $vendorDir . '/sabre/dav/lib/CardDAV/Backend/SyncSupport.php',
|
||||||
|
'Sabre\\CardDAV\\Card' => $vendorDir . '/sabre/dav/lib/CardDAV/Card.php',
|
||||||
|
'Sabre\\CardDAV\\IAddressBook' => $vendorDir . '/sabre/dav/lib/CardDAV/IAddressBook.php',
|
||||||
|
'Sabre\\CardDAV\\ICard' => $vendorDir . '/sabre/dav/lib/CardDAV/ICard.php',
|
||||||
|
'Sabre\\CardDAV\\IDirectory' => $vendorDir . '/sabre/dav/lib/CardDAV/IDirectory.php',
|
||||||
|
'Sabre\\CardDAV\\Plugin' => $vendorDir . '/sabre/dav/lib/CardDAV/Plugin.php',
|
||||||
|
'Sabre\\CardDAV\\VCFExportPlugin' => $vendorDir . '/sabre/dav/lib/CardDAV/VCFExportPlugin.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Filter\\AddressData' => $vendorDir . '/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Filter\\ParamFilter' => $vendorDir . '/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Filter\\PropFilter' => $vendorDir . '/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Property\\SupportedAddressData' => $vendorDir . '/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Property\\SupportedCollationSet' => $vendorDir . '/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Request\\AddressBookMultiGetReport' => $vendorDir . '/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Request\\AddressBookQueryReport' => $vendorDir . '/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php',
|
||||||
|
'Sabre\\DAVACL\\ACLTrait' => $vendorDir . '/sabre/dav/lib/DAVACL/ACLTrait.php',
|
||||||
|
'Sabre\\DAVACL\\AbstractPrincipalCollection' => $vendorDir . '/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php',
|
||||||
|
'Sabre\\DAVACL\\Exception\\AceConflict' => $vendorDir . '/sabre/dav/lib/DAVACL/Exception/AceConflict.php',
|
||||||
|
'Sabre\\DAVACL\\Exception\\NeedPrivileges' => $vendorDir . '/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php',
|
||||||
|
'Sabre\\DAVACL\\Exception\\NoAbstract' => $vendorDir . '/sabre/dav/lib/DAVACL/Exception/NoAbstract.php',
|
||||||
|
'Sabre\\DAVACL\\Exception\\NotRecognizedPrincipal' => $vendorDir . '/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php',
|
||||||
|
'Sabre\\DAVACL\\Exception\\NotSupportedPrivilege' => $vendorDir . '/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php',
|
||||||
|
'Sabre\\DAVACL\\FS\\Collection' => $vendorDir . '/sabre/dav/lib/DAVACL/FS/Collection.php',
|
||||||
|
'Sabre\\DAVACL\\FS\\File' => $vendorDir . '/sabre/dav/lib/DAVACL/FS/File.php',
|
||||||
|
'Sabre\\DAVACL\\FS\\HomeCollection' => $vendorDir . '/sabre/dav/lib/DAVACL/FS/HomeCollection.php',
|
||||||
|
'Sabre\\DAVACL\\IACL' => $vendorDir . '/sabre/dav/lib/DAVACL/IACL.php',
|
||||||
|
'Sabre\\DAVACL\\IPrincipal' => $vendorDir . '/sabre/dav/lib/DAVACL/IPrincipal.php',
|
||||||
|
'Sabre\\DAVACL\\IPrincipalCollection' => $vendorDir . '/sabre/dav/lib/DAVACL/IPrincipalCollection.php',
|
||||||
|
'Sabre\\DAVACL\\Plugin' => $vendorDir . '/sabre/dav/lib/DAVACL/Plugin.php',
|
||||||
|
'Sabre\\DAVACL\\Principal' => $vendorDir . '/sabre/dav/lib/DAVACL/Principal.php',
|
||||||
|
'Sabre\\DAVACL\\PrincipalBackend\\AbstractBackend' => $vendorDir . '/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php',
|
||||||
|
'Sabre\\DAVACL\\PrincipalBackend\\BackendInterface' => $vendorDir . '/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php',
|
||||||
|
'Sabre\\DAVACL\\PrincipalBackend\\CreatePrincipalSupport' => $vendorDir . '/sabre/dav/lib/DAVACL/PrincipalBackend/CreatePrincipalSupport.php',
|
||||||
|
'Sabre\\DAVACL\\PrincipalBackend\\PDO' => $vendorDir . '/sabre/dav/lib/DAVACL/PrincipalBackend/PDO.php',
|
||||||
|
'Sabre\\DAVACL\\PrincipalCollection' => $vendorDir . '/sabre/dav/lib/DAVACL/PrincipalCollection.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Property\\Acl' => $vendorDir . '/sabre/dav/lib/DAVACL/Xml/Property/Acl.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Property\\AclRestrictions' => $vendorDir . '/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Property\\CurrentUserPrivilegeSet' => $vendorDir . '/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Property\\Principal' => $vendorDir . '/sabre/dav/lib/DAVACL/Xml/Property/Principal.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Property\\SupportedPrivilegeSet' => $vendorDir . '/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Request\\AclPrincipalPropSetReport' => $vendorDir . '/sabre/dav/lib/DAVACL/Xml/Request/AclPrincipalPropSetReport.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Request\\ExpandPropertyReport' => $vendorDir . '/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Request\\PrincipalMatchReport' => $vendorDir . '/sabre/dav/lib/DAVACL/Xml/Request/PrincipalMatchReport.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Request\\PrincipalPropertySearchReport' => $vendorDir . '/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Request\\PrincipalSearchPropertySetReport' => $vendorDir . '/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\AbstractBasic' => $vendorDir . '/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\AbstractBearer' => $vendorDir . '/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\AbstractDigest' => $vendorDir . '/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\Apache' => $vendorDir . '/sabre/dav/lib/DAV/Auth/Backend/Apache.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\BackendInterface' => $vendorDir . '/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\BasicCallBack' => $vendorDir . '/sabre/dav/lib/DAV/Auth/Backend/BasicCallBack.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\File' => $vendorDir . '/sabre/dav/lib/DAV/Auth/Backend/File.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\IMAP' => $vendorDir . '/sabre/dav/lib/DAV/Auth/Backend/IMAP.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\PDO' => $vendorDir . '/sabre/dav/lib/DAV/Auth/Backend/PDO.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\PDOBasicAuth' => $vendorDir . '/sabre/dav/lib/DAV/Auth/Backend/PDOBasicAuth.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Plugin' => $vendorDir . '/sabre/dav/lib/DAV/Auth/Plugin.php',
|
||||||
|
'Sabre\\DAV\\Browser\\GuessContentType' => $vendorDir . '/sabre/dav/lib/DAV/Browser/GuessContentType.php',
|
||||||
|
'Sabre\\DAV\\Browser\\HtmlOutput' => $vendorDir . '/sabre/dav/lib/DAV/Browser/HtmlOutput.php',
|
||||||
|
'Sabre\\DAV\\Browser\\HtmlOutputHelper' => $vendorDir . '/sabre/dav/lib/DAV/Browser/HtmlOutputHelper.php',
|
||||||
|
'Sabre\\DAV\\Browser\\MapGetToPropFind' => $vendorDir . '/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php',
|
||||||
|
'Sabre\\DAV\\Browser\\Plugin' => $vendorDir . '/sabre/dav/lib/DAV/Browser/Plugin.php',
|
||||||
|
'Sabre\\DAV\\Browser\\PropFindAll' => $vendorDir . '/sabre/dav/lib/DAV/Browser/PropFindAll.php',
|
||||||
|
'Sabre\\DAV\\Client' => $vendorDir . '/sabre/dav/lib/DAV/Client.php',
|
||||||
|
'Sabre\\DAV\\Collection' => $vendorDir . '/sabre/dav/lib/DAV/Collection.php',
|
||||||
|
'Sabre\\DAV\\CorePlugin' => $vendorDir . '/sabre/dav/lib/DAV/CorePlugin.php',
|
||||||
|
'Sabre\\DAV\\Exception' => $vendorDir . '/sabre/dav/lib/DAV/Exception.php',
|
||||||
|
'Sabre\\DAV\\Exception\\BadRequest' => $vendorDir . '/sabre/dav/lib/DAV/Exception/BadRequest.php',
|
||||||
|
'Sabre\\DAV\\Exception\\Conflict' => $vendorDir . '/sabre/dav/lib/DAV/Exception/Conflict.php',
|
||||||
|
'Sabre\\DAV\\Exception\\ConflictingLock' => $vendorDir . '/sabre/dav/lib/DAV/Exception/ConflictingLock.php',
|
||||||
|
'Sabre\\DAV\\Exception\\Forbidden' => $vendorDir . '/sabre/dav/lib/DAV/Exception/Forbidden.php',
|
||||||
|
'Sabre\\DAV\\Exception\\InsufficientStorage' => $vendorDir . '/sabre/dav/lib/DAV/Exception/InsufficientStorage.php',
|
||||||
|
'Sabre\\DAV\\Exception\\InvalidResourceType' => $vendorDir . '/sabre/dav/lib/DAV/Exception/InvalidResourceType.php',
|
||||||
|
'Sabre\\DAV\\Exception\\InvalidSyncToken' => $vendorDir . '/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php',
|
||||||
|
'Sabre\\DAV\\Exception\\LengthRequired' => $vendorDir . '/sabre/dav/lib/DAV/Exception/LengthRequired.php',
|
||||||
|
'Sabre\\DAV\\Exception\\LockTokenMatchesRequestUri' => $vendorDir . '/sabre/dav/lib/DAV/Exception/LockTokenMatchesRequestUri.php',
|
||||||
|
'Sabre\\DAV\\Exception\\Locked' => $vendorDir . '/sabre/dav/lib/DAV/Exception/Locked.php',
|
||||||
|
'Sabre\\DAV\\Exception\\MethodNotAllowed' => $vendorDir . '/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php',
|
||||||
|
'Sabre\\DAV\\Exception\\NotAuthenticated' => $vendorDir . '/sabre/dav/lib/DAV/Exception/NotAuthenticated.php',
|
||||||
|
'Sabre\\DAV\\Exception\\NotFound' => $vendorDir . '/sabre/dav/lib/DAV/Exception/NotFound.php',
|
||||||
|
'Sabre\\DAV\\Exception\\NotImplemented' => $vendorDir . '/sabre/dav/lib/DAV/Exception/NotImplemented.php',
|
||||||
|
'Sabre\\DAV\\Exception\\PaymentRequired' => $vendorDir . '/sabre/dav/lib/DAV/Exception/PaymentRequired.php',
|
||||||
|
'Sabre\\DAV\\Exception\\PreconditionFailed' => $vendorDir . '/sabre/dav/lib/DAV/Exception/PreconditionFailed.php',
|
||||||
|
'Sabre\\DAV\\Exception\\ReportNotSupported' => $vendorDir . '/sabre/dav/lib/DAV/Exception/ReportNotSupported.php',
|
||||||
|
'Sabre\\DAV\\Exception\\RequestedRangeNotSatisfiable' => $vendorDir . '/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php',
|
||||||
|
'Sabre\\DAV\\Exception\\ServiceUnavailable' => $vendorDir . '/sabre/dav/lib/DAV/Exception/ServiceUnavailable.php',
|
||||||
|
'Sabre\\DAV\\Exception\\TooManyMatches' => $vendorDir . '/sabre/dav/lib/DAV/Exception/TooManyMatches.php',
|
||||||
|
'Sabre\\DAV\\Exception\\UnsupportedMediaType' => $vendorDir . '/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php',
|
||||||
|
'Sabre\\DAV\\FSExt\\Directory' => $vendorDir . '/sabre/dav/lib/DAV/FSExt/Directory.php',
|
||||||
|
'Sabre\\DAV\\FSExt\\File' => $vendorDir . '/sabre/dav/lib/DAV/FSExt/File.php',
|
||||||
|
'Sabre\\DAV\\FS\\Directory' => $vendorDir . '/sabre/dav/lib/DAV/FS/Directory.php',
|
||||||
|
'Sabre\\DAV\\FS\\File' => $vendorDir . '/sabre/dav/lib/DAV/FS/File.php',
|
||||||
|
'Sabre\\DAV\\FS\\Node' => $vendorDir . '/sabre/dav/lib/DAV/FS/Node.php',
|
||||||
|
'Sabre\\DAV\\File' => $vendorDir . '/sabre/dav/lib/DAV/File.php',
|
||||||
|
'Sabre\\DAV\\ICollection' => $vendorDir . '/sabre/dav/lib/DAV/ICollection.php',
|
||||||
|
'Sabre\\DAV\\ICopyTarget' => $vendorDir . '/sabre/dav/lib/DAV/ICopyTarget.php',
|
||||||
|
'Sabre\\DAV\\IExtendedCollection' => $vendorDir . '/sabre/dav/lib/DAV/IExtendedCollection.php',
|
||||||
|
'Sabre\\DAV\\IFile' => $vendorDir . '/sabre/dav/lib/DAV/IFile.php',
|
||||||
|
'Sabre\\DAV\\IMoveTarget' => $vendorDir . '/sabre/dav/lib/DAV/IMoveTarget.php',
|
||||||
|
'Sabre\\DAV\\IMultiGet' => $vendorDir . '/sabre/dav/lib/DAV/IMultiGet.php',
|
||||||
|
'Sabre\\DAV\\INode' => $vendorDir . '/sabre/dav/lib/DAV/INode.php',
|
||||||
|
'Sabre\\DAV\\INodeByPath' => $vendorDir . '/sabre/dav/lib/DAV/INodeByPath.php',
|
||||||
|
'Sabre\\DAV\\IProperties' => $vendorDir . '/sabre/dav/lib/DAV/IProperties.php',
|
||||||
|
'Sabre\\DAV\\IQuota' => $vendorDir . '/sabre/dav/lib/DAV/IQuota.php',
|
||||||
|
'Sabre\\DAV\\Locks\\Backend\\AbstractBackend' => $vendorDir . '/sabre/dav/lib/DAV/Locks/Backend/AbstractBackend.php',
|
||||||
|
'Sabre\\DAV\\Locks\\Backend\\BackendInterface' => $vendorDir . '/sabre/dav/lib/DAV/Locks/Backend/BackendInterface.php',
|
||||||
|
'Sabre\\DAV\\Locks\\Backend\\File' => $vendorDir . '/sabre/dav/lib/DAV/Locks/Backend/File.php',
|
||||||
|
'Sabre\\DAV\\Locks\\Backend\\PDO' => $vendorDir . '/sabre/dav/lib/DAV/Locks/Backend/PDO.php',
|
||||||
|
'Sabre\\DAV\\Locks\\LockInfo' => $vendorDir . '/sabre/dav/lib/DAV/Locks/LockInfo.php',
|
||||||
|
'Sabre\\DAV\\Locks\\Plugin' => $vendorDir . '/sabre/dav/lib/DAV/Locks/Plugin.php',
|
||||||
|
'Sabre\\DAV\\MkCol' => $vendorDir . '/sabre/dav/lib/DAV/MkCol.php',
|
||||||
|
'Sabre\\DAV\\Mount\\Plugin' => $vendorDir . '/sabre/dav/lib/DAV/Mount/Plugin.php',
|
||||||
|
'Sabre\\DAV\\Node' => $vendorDir . '/sabre/dav/lib/DAV/Node.php',
|
||||||
|
'Sabre\\DAV\\PartialUpdate\\IPatchSupport' => $vendorDir . '/sabre/dav/lib/DAV/PartialUpdate/IPatchSupport.php',
|
||||||
|
'Sabre\\DAV\\PartialUpdate\\Plugin' => $vendorDir . '/sabre/dav/lib/DAV/PartialUpdate/Plugin.php',
|
||||||
|
'Sabre\\DAV\\PropFind' => $vendorDir . '/sabre/dav/lib/DAV/PropFind.php',
|
||||||
|
'Sabre\\DAV\\PropPatch' => $vendorDir . '/sabre/dav/lib/DAV/PropPatch.php',
|
||||||
|
'Sabre\\DAV\\PropertyStorage\\Backend\\BackendInterface' => $vendorDir . '/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php',
|
||||||
|
'Sabre\\DAV\\PropertyStorage\\Backend\\PDO' => $vendorDir . '/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php',
|
||||||
|
'Sabre\\DAV\\PropertyStorage\\Plugin' => $vendorDir . '/sabre/dav/lib/DAV/PropertyStorage/Plugin.php',
|
||||||
|
'Sabre\\DAV\\Server' => $vendorDir . '/sabre/dav/lib/DAV/Server.php',
|
||||||
|
'Sabre\\DAV\\ServerPlugin' => $vendorDir . '/sabre/dav/lib/DAV/ServerPlugin.php',
|
||||||
|
'Sabre\\DAV\\Sharing\\ISharedNode' => $vendorDir . '/sabre/dav/lib/DAV/Sharing/ISharedNode.php',
|
||||||
|
'Sabre\\DAV\\Sharing\\Plugin' => $vendorDir . '/sabre/dav/lib/DAV/Sharing/Plugin.php',
|
||||||
|
'Sabre\\DAV\\SimpleCollection' => $vendorDir . '/sabre/dav/lib/DAV/SimpleCollection.php',
|
||||||
|
'Sabre\\DAV\\SimpleFile' => $vendorDir . '/sabre/dav/lib/DAV/SimpleFile.php',
|
||||||
|
'Sabre\\DAV\\StringUtil' => $vendorDir . '/sabre/dav/lib/DAV/StringUtil.php',
|
||||||
|
'Sabre\\DAV\\Sync\\ISyncCollection' => $vendorDir . '/sabre/dav/lib/DAV/Sync/ISyncCollection.php',
|
||||||
|
'Sabre\\DAV\\Sync\\Plugin' => $vendorDir . '/sabre/dav/lib/DAV/Sync/Plugin.php',
|
||||||
|
'Sabre\\DAV\\TemporaryFileFilterPlugin' => $vendorDir . '/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php',
|
||||||
|
'Sabre\\DAV\\Tree' => $vendorDir . '/sabre/dav/lib/DAV/Tree.php',
|
||||||
|
'Sabre\\DAV\\UUIDUtil' => $vendorDir . '/sabre/dav/lib/DAV/UUIDUtil.php',
|
||||||
|
'Sabre\\DAV\\Version' => $vendorDir . '/sabre/dav/lib/DAV/Version.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Element\\Prop' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Element/Prop.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Element\\Response' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Element/Response.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Element\\Sharee' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Element/Sharee.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\Complex' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Property/Complex.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\GetLastModified' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\Href' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Property/Href.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\Invite' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Property/Invite.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\LocalHref' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Property/LocalHref.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\LockDiscovery' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Property/LockDiscovery.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\ResourceType' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Property/ResourceType.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\ShareAccess' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Property/ShareAccess.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\SupportedLock' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\SupportedMethodSet' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\SupportedReportSet' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\Lock' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Request/Lock.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\MkCol' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Request/MkCol.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\PropFind' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Request/PropFind.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\PropPatch' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Request/PropPatch.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\ShareResource' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Request/ShareResource.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Response\\MultiStatus' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Service' => $vendorDir . '/sabre/dav/lib/DAV/Xml/Service.php',
|
||||||
|
'Sabre\\Event\\Emitter' => $vendorDir . '/sabre/event/lib/Emitter.php',
|
||||||
|
'Sabre\\Event\\EmitterInterface' => $vendorDir . '/sabre/event/lib/EmitterInterface.php',
|
||||||
|
'Sabre\\Event\\EmitterTrait' => $vendorDir . '/sabre/event/lib/EmitterTrait.php',
|
||||||
|
'Sabre\\Event\\EventEmitter' => $vendorDir . '/sabre/event/lib/EventEmitter.php',
|
||||||
|
'Sabre\\Event\\Loop\\Loop' => $vendorDir . '/sabre/event/lib/Loop/Loop.php',
|
||||||
|
'Sabre\\Event\\Promise' => $vendorDir . '/sabre/event/lib/Promise.php',
|
||||||
|
'Sabre\\Event\\PromiseAlreadyResolvedException' => $vendorDir . '/sabre/event/lib/PromiseAlreadyResolvedException.php',
|
||||||
|
'Sabre\\Event\\Version' => $vendorDir . '/sabre/event/lib/Version.php',
|
||||||
|
'Sabre\\Event\\WildcardEmitter' => $vendorDir . '/sabre/event/lib/WildcardEmitter.php',
|
||||||
|
'Sabre\\Event\\WildcardEmitterTrait' => $vendorDir . '/sabre/event/lib/WildcardEmitterTrait.php',
|
||||||
|
'Sabre\\HTTP\\Auth\\AWS' => $vendorDir . '/sabre/http/lib/Auth/AWS.php',
|
||||||
|
'Sabre\\HTTP\\Auth\\AbstractAuth' => $vendorDir . '/sabre/http/lib/Auth/AbstractAuth.php',
|
||||||
|
'Sabre\\HTTP\\Auth\\Basic' => $vendorDir . '/sabre/http/lib/Auth/Basic.php',
|
||||||
|
'Sabre\\HTTP\\Auth\\Bearer' => $vendorDir . '/sabre/http/lib/Auth/Bearer.php',
|
||||||
|
'Sabre\\HTTP\\Auth\\Digest' => $vendorDir . '/sabre/http/lib/Auth/Digest.php',
|
||||||
|
'Sabre\\HTTP\\Client' => $vendorDir . '/sabre/http/lib/Client.php',
|
||||||
|
'Sabre\\HTTP\\ClientException' => $vendorDir . '/sabre/http/lib/ClientException.php',
|
||||||
|
'Sabre\\HTTP\\ClientHttpException' => $vendorDir . '/sabre/http/lib/ClientHttpException.php',
|
||||||
|
'Sabre\\HTTP\\HttpException' => $vendorDir . '/sabre/http/lib/HttpException.php',
|
||||||
|
'Sabre\\HTTP\\Message' => $vendorDir . '/sabre/http/lib/Message.php',
|
||||||
|
'Sabre\\HTTP\\MessageDecoratorTrait' => $vendorDir . '/sabre/http/lib/MessageDecoratorTrait.php',
|
||||||
|
'Sabre\\HTTP\\MessageInterface' => $vendorDir . '/sabre/http/lib/MessageInterface.php',
|
||||||
|
'Sabre\\HTTP\\Request' => $vendorDir . '/sabre/http/lib/Request.php',
|
||||||
|
'Sabre\\HTTP\\RequestDecorator' => $vendorDir . '/sabre/http/lib/RequestDecorator.php',
|
||||||
|
'Sabre\\HTTP\\RequestInterface' => $vendorDir . '/sabre/http/lib/RequestInterface.php',
|
||||||
|
'Sabre\\HTTP\\Response' => $vendorDir . '/sabre/http/lib/Response.php',
|
||||||
|
'Sabre\\HTTP\\ResponseDecorator' => $vendorDir . '/sabre/http/lib/ResponseDecorator.php',
|
||||||
|
'Sabre\\HTTP\\ResponseInterface' => $vendorDir . '/sabre/http/lib/ResponseInterface.php',
|
||||||
|
'Sabre\\HTTP\\Sapi' => $vendorDir . '/sabre/http/lib/Sapi.php',
|
||||||
|
'Sabre\\HTTP\\Version' => $vendorDir . '/sabre/http/lib/Version.php',
|
||||||
|
'Sabre\\Uri\\InvalidUriException' => $vendorDir . '/sabre/uri/lib/InvalidUriException.php',
|
||||||
|
'Sabre\\Uri\\Version' => $vendorDir . '/sabre/uri/lib/Version.php',
|
||||||
|
'Sabre\\VObject\\BirthdayCalendarGenerator' => $vendorDir . '/sabre/vobject/lib/BirthdayCalendarGenerator.php',
|
||||||
|
'Sabre\\VObject\\Cli' => $vendorDir . '/sabre/vobject/lib/Cli.php',
|
||||||
|
'Sabre\\VObject\\Component' => $vendorDir . '/sabre/vobject/lib/Component.php',
|
||||||
|
'Sabre\\VObject\\Component\\Available' => $vendorDir . '/sabre/vobject/lib/Component/Available.php',
|
||||||
|
'Sabre\\VObject\\Component\\VAlarm' => $vendorDir . '/sabre/vobject/lib/Component/VAlarm.php',
|
||||||
|
'Sabre\\VObject\\Component\\VAvailability' => $vendorDir . '/sabre/vobject/lib/Component/VAvailability.php',
|
||||||
|
'Sabre\\VObject\\Component\\VCalendar' => $vendorDir . '/sabre/vobject/lib/Component/VCalendar.php',
|
||||||
|
'Sabre\\VObject\\Component\\VCard' => $vendorDir . '/sabre/vobject/lib/Component/VCard.php',
|
||||||
|
'Sabre\\VObject\\Component\\VEvent' => $vendorDir . '/sabre/vobject/lib/Component/VEvent.php',
|
||||||
|
'Sabre\\VObject\\Component\\VFreeBusy' => $vendorDir . '/sabre/vobject/lib/Component/VFreeBusy.php',
|
||||||
|
'Sabre\\VObject\\Component\\VJournal' => $vendorDir . '/sabre/vobject/lib/Component/VJournal.php',
|
||||||
|
'Sabre\\VObject\\Component\\VTimeZone' => $vendorDir . '/sabre/vobject/lib/Component/VTimeZone.php',
|
||||||
|
'Sabre\\VObject\\Component\\VTodo' => $vendorDir . '/sabre/vobject/lib/Component/VTodo.php',
|
||||||
|
'Sabre\\VObject\\DateTimeParser' => $vendorDir . '/sabre/vobject/lib/DateTimeParser.php',
|
||||||
|
'Sabre\\VObject\\Document' => $vendorDir . '/sabre/vobject/lib/Document.php',
|
||||||
|
'Sabre\\VObject\\ElementList' => $vendorDir . '/sabre/vobject/lib/ElementList.php',
|
||||||
|
'Sabre\\VObject\\EofException' => $vendorDir . '/sabre/vobject/lib/EofException.php',
|
||||||
|
'Sabre\\VObject\\FreeBusyData' => $vendorDir . '/sabre/vobject/lib/FreeBusyData.php',
|
||||||
|
'Sabre\\VObject\\FreeBusyGenerator' => $vendorDir . '/sabre/vobject/lib/FreeBusyGenerator.php',
|
||||||
|
'Sabre\\VObject\\ITip\\Broker' => $vendorDir . '/sabre/vobject/lib/ITip/Broker.php',
|
||||||
|
'Sabre\\VObject\\ITip\\ITipException' => $vendorDir . '/sabre/vobject/lib/ITip/ITipException.php',
|
||||||
|
'Sabre\\VObject\\ITip\\Message' => $vendorDir . '/sabre/vobject/lib/ITip/Message.php',
|
||||||
|
'Sabre\\VObject\\ITip\\SameOrganizerForAllComponentsException' => $vendorDir . '/sabre/vobject/lib/ITip/SameOrganizerForAllComponentsException.php',
|
||||||
|
'Sabre\\VObject\\InvalidDataException' => $vendorDir . '/sabre/vobject/lib/InvalidDataException.php',
|
||||||
|
'Sabre\\VObject\\Node' => $vendorDir . '/sabre/vobject/lib/Node.php',
|
||||||
|
'Sabre\\VObject\\PHPUnitAssertions' => $vendorDir . '/sabre/vobject/lib/PHPUnitAssertions.php',
|
||||||
|
'Sabre\\VObject\\Parameter' => $vendorDir . '/sabre/vobject/lib/Parameter.php',
|
||||||
|
'Sabre\\VObject\\ParseException' => $vendorDir . '/sabre/vobject/lib/ParseException.php',
|
||||||
|
'Sabre\\VObject\\Parser\\Json' => $vendorDir . '/sabre/vobject/lib/Parser/Json.php',
|
||||||
|
'Sabre\\VObject\\Parser\\MimeDir' => $vendorDir . '/sabre/vobject/lib/Parser/MimeDir.php',
|
||||||
|
'Sabre\\VObject\\Parser\\Parser' => $vendorDir . '/sabre/vobject/lib/Parser/Parser.php',
|
||||||
|
'Sabre\\VObject\\Parser\\XML' => $vendorDir . '/sabre/vobject/lib/Parser/XML.php',
|
||||||
|
'Sabre\\VObject\\Parser\\XML\\Element\\KeyValue' => $vendorDir . '/sabre/vobject/lib/Parser/XML/Element/KeyValue.php',
|
||||||
|
'Sabre\\VObject\\Property' => $vendorDir . '/sabre/vobject/lib/Property.php',
|
||||||
|
'Sabre\\VObject\\Property\\Binary' => $vendorDir . '/sabre/vobject/lib/Property/Binary.php',
|
||||||
|
'Sabre\\VObject\\Property\\Boolean' => $vendorDir . '/sabre/vobject/lib/Property/Boolean.php',
|
||||||
|
'Sabre\\VObject\\Property\\FlatText' => $vendorDir . '/sabre/vobject/lib/Property/FlatText.php',
|
||||||
|
'Sabre\\VObject\\Property\\FloatValue' => $vendorDir . '/sabre/vobject/lib/Property/FloatValue.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\CalAddress' => $vendorDir . '/sabre/vobject/lib/Property/ICalendar/CalAddress.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\Date' => $vendorDir . '/sabre/vobject/lib/Property/ICalendar/Date.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\DateTime' => $vendorDir . '/sabre/vobject/lib/Property/ICalendar/DateTime.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\Duration' => $vendorDir . '/sabre/vobject/lib/Property/ICalendar/Duration.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\Period' => $vendorDir . '/sabre/vobject/lib/Property/ICalendar/Period.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\Recur' => $vendorDir . '/sabre/vobject/lib/Property/ICalendar/Recur.php',
|
||||||
|
'Sabre\\VObject\\Property\\IntegerValue' => $vendorDir . '/sabre/vobject/lib/Property/IntegerValue.php',
|
||||||
|
'Sabre\\VObject\\Property\\Text' => $vendorDir . '/sabre/vobject/lib/Property/Text.php',
|
||||||
|
'Sabre\\VObject\\Property\\Time' => $vendorDir . '/sabre/vobject/lib/Property/Time.php',
|
||||||
|
'Sabre\\VObject\\Property\\Unknown' => $vendorDir . '/sabre/vobject/lib/Property/Unknown.php',
|
||||||
|
'Sabre\\VObject\\Property\\Uri' => $vendorDir . '/sabre/vobject/lib/Property/Uri.php',
|
||||||
|
'Sabre\\VObject\\Property\\UtcOffset' => $vendorDir . '/sabre/vobject/lib/Property/UtcOffset.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\Date' => $vendorDir . '/sabre/vobject/lib/Property/VCard/Date.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\DateAndOrTime' => $vendorDir . '/sabre/vobject/lib/Property/VCard/DateAndOrTime.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\DateTime' => $vendorDir . '/sabre/vobject/lib/Property/VCard/DateTime.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\LanguageTag' => $vendorDir . '/sabre/vobject/lib/Property/VCard/LanguageTag.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\PhoneNumber' => $vendorDir . '/sabre/vobject/lib/Property/VCard/PhoneNumber.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\TimeStamp' => $vendorDir . '/sabre/vobject/lib/Property/VCard/TimeStamp.php',
|
||||||
|
'Sabre\\VObject\\Reader' => $vendorDir . '/sabre/vobject/lib/Reader.php',
|
||||||
|
'Sabre\\VObject\\Recur\\EventIterator' => $vendorDir . '/sabre/vobject/lib/Recur/EventIterator.php',
|
||||||
|
'Sabre\\VObject\\Recur\\MaxInstancesExceededException' => $vendorDir . '/sabre/vobject/lib/Recur/MaxInstancesExceededException.php',
|
||||||
|
'Sabre\\VObject\\Recur\\NoInstancesException' => $vendorDir . '/sabre/vobject/lib/Recur/NoInstancesException.php',
|
||||||
|
'Sabre\\VObject\\Recur\\RDateIterator' => $vendorDir . '/sabre/vobject/lib/Recur/RDateIterator.php',
|
||||||
|
'Sabre\\VObject\\Recur\\RRuleIterator' => $vendorDir . '/sabre/vobject/lib/Recur/RRuleIterator.php',
|
||||||
|
'Sabre\\VObject\\Settings' => $vendorDir . '/sabre/vobject/lib/Settings.php',
|
||||||
|
'Sabre\\VObject\\Splitter\\ICalendar' => $vendorDir . '/sabre/vobject/lib/Splitter/ICalendar.php',
|
||||||
|
'Sabre\\VObject\\Splitter\\SplitterInterface' => $vendorDir . '/sabre/vobject/lib/Splitter/SplitterInterface.php',
|
||||||
|
'Sabre\\VObject\\Splitter\\VCard' => $vendorDir . '/sabre/vobject/lib/Splitter/VCard.php',
|
||||||
|
'Sabre\\VObject\\StringUtil' => $vendorDir . '/sabre/vobject/lib/StringUtil.php',
|
||||||
|
'Sabre\\VObject\\TimeZoneUtil' => $vendorDir . '/sabre/vobject/lib/TimeZoneUtil.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\FindFromOffset' => $vendorDir . '/sabre/vobject/lib/TimezoneGuesser/FindFromOffset.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\FindFromTimezoneIdentifier' => $vendorDir . '/sabre/vobject/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\FindFromTimezoneMap' => $vendorDir . '/sabre/vobject/lib/TimezoneGuesser/FindFromTimezoneMap.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\GuessFromLicEntry' => $vendorDir . '/sabre/vobject/lib/TimezoneGuesser/GuessFromLicEntry.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\GuessFromMsTzId' => $vendorDir . '/sabre/vobject/lib/TimezoneGuesser/GuessFromMsTzId.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\TimezoneFinder' => $vendorDir . '/sabre/vobject/lib/TimezoneGuesser/TimezoneFinder.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\TimezoneGuesser' => $vendorDir . '/sabre/vobject/lib/TimezoneGuesser/TimezoneGuesser.php',
|
||||||
|
'Sabre\\VObject\\UUIDUtil' => $vendorDir . '/sabre/vobject/lib/UUIDUtil.php',
|
||||||
|
'Sabre\\VObject\\VCardConverter' => $vendorDir . '/sabre/vobject/lib/VCardConverter.php',
|
||||||
|
'Sabre\\VObject\\Version' => $vendorDir . '/sabre/vobject/lib/Version.php',
|
||||||
|
'Sabre\\VObject\\Writer' => $vendorDir . '/sabre/vobject/lib/Writer.php',
|
||||||
|
'Sabre\\Xml\\ContextStackTrait' => $vendorDir . '/sabre/xml/lib/ContextStackTrait.php',
|
||||||
|
'Sabre\\Xml\\Element' => $vendorDir . '/sabre/xml/lib/Element.php',
|
||||||
|
'Sabre\\Xml\\Element\\Base' => $vendorDir . '/sabre/xml/lib/Element/Base.php',
|
||||||
|
'Sabre\\Xml\\Element\\Cdata' => $vendorDir . '/sabre/xml/lib/Element/Cdata.php',
|
||||||
|
'Sabre\\Xml\\Element\\Elements' => $vendorDir . '/sabre/xml/lib/Element/Elements.php',
|
||||||
|
'Sabre\\Xml\\Element\\KeyValue' => $vendorDir . '/sabre/xml/lib/Element/KeyValue.php',
|
||||||
|
'Sabre\\Xml\\Element\\Uri' => $vendorDir . '/sabre/xml/lib/Element/Uri.php',
|
||||||
|
'Sabre\\Xml\\Element\\XmlFragment' => $vendorDir . '/sabre/xml/lib/Element/XmlFragment.php',
|
||||||
|
'Sabre\\Xml\\LibXMLException' => $vendorDir . '/sabre/xml/lib/LibXMLException.php',
|
||||||
|
'Sabre\\Xml\\ParseException' => $vendorDir . '/sabre/xml/lib/ParseException.php',
|
||||||
|
'Sabre\\Xml\\Reader' => $vendorDir . '/sabre/xml/lib/Reader.php',
|
||||||
|
'Sabre\\Xml\\Service' => $vendorDir . '/sabre/xml/lib/Service.php',
|
||||||
|
'Sabre\\Xml\\Version' => $vendorDir . '/sabre/xml/lib/Version.php',
|
||||||
|
'Sabre\\Xml\\Writer' => $vendorDir . '/sabre/xml/lib/Writer.php',
|
||||||
|
'Sabre\\Xml\\XmlDeserializable' => $vendorDir . '/sabre/xml/lib/XmlDeserializable.php',
|
||||||
|
'Sabre\\Xml\\XmlSerializable' => $vendorDir . '/sabre/xml/lib/XmlSerializable.php',
|
||||||
|
);
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_files.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname(dirname($vendorDir));
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'383eaff206634a77a1be54e64e6459c7' => $vendorDir . '/sabre/uri/lib/functions.php',
|
||||||
|
'2b9d0f43f9552984cfa82fee95491826' => $vendorDir . '/sabre/event/lib/coroutine.php',
|
||||||
|
'd81bab31d3feb45bfe2f283ea3c8fdf7' => $vendorDir . '/sabre/event/lib/Loop/functions.php',
|
||||||
|
'a1cce3d26cc15c00fcd0b3354bd72c88' => $vendorDir . '/sabre/event/lib/Promise/functions.php',
|
||||||
|
'3569eecfeed3bcf0bad3c998a494ecb8' => $vendorDir . '/sabre/xml/lib/Deserializer/functions.php',
|
||||||
|
'93aa591bc4ca510c520999e34229ee79' => $vendorDir . '/sabre/xml/lib/Serializer/functions.php',
|
||||||
|
'ebdb698ed4152ae445614b69b5e4bb6a' => $vendorDir . '/sabre/http/lib/functions.php',
|
||||||
|
);
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname(dirname($vendorDir));
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname(dirname($vendorDir));
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Sabre\\Xml\\' => array($vendorDir . '/sabre/xml/lib'),
|
||||||
|
'Sabre\\VObject\\' => array($vendorDir . '/sabre/vobject/lib'),
|
||||||
|
'Sabre\\Uri\\' => array($vendorDir . '/sabre/uri/lib'),
|
||||||
|
'Sabre\\HTTP\\' => array($vendorDir . '/sabre/http/lib'),
|
||||||
|
'Sabre\\Event\\' => array($vendorDir . '/sabre/event/lib'),
|
||||||
|
'Sabre\\' => array($vendorDir . '/sabre/dav/lib'),
|
||||||
|
'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
|
||||||
|
);
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
|
class ComposerAutoloaderInita07591154e0386cfc9ad0f9217878d04
|
||||||
|
{
|
||||||
|
private static $loader;
|
||||||
|
|
||||||
|
public static function loadClassLoader($class)
|
||||||
|
{
|
||||||
|
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||||
|
require __DIR__ . '/ClassLoader.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Composer\Autoload\ClassLoader
|
||||||
|
*/
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
if (null !== self::$loader) {
|
||||||
|
return self::$loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
require __DIR__ . '/platform_check.php';
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInita07591154e0386cfc9ad0f9217878d04', 'loadClassLoader'), true, true);
|
||||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||||
|
spl_autoload_unregister(array('ComposerAutoloaderInita07591154e0386cfc9ad0f9217878d04', 'loadClassLoader'));
|
||||||
|
|
||||||
|
require __DIR__ . '/autoload_static.php';
|
||||||
|
call_user_func(\Composer\Autoload\ComposerStaticInita07591154e0386cfc9ad0f9217878d04::getInitializer($loader));
|
||||||
|
|
||||||
|
$loader->register(true);
|
||||||
|
|
||||||
|
$filesToLoad = \Composer\Autoload\ComposerStaticInita07591154e0386cfc9ad0f9217878d04::$files;
|
||||||
|
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
|
||||||
|
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||||
|
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||||
|
|
||||||
|
require $file;
|
||||||
|
}
|
||||||
|
}, null, null);
|
||||||
|
foreach ($filesToLoad as $fileIdentifier => $file) {
|
||||||
|
$requireFile($fileIdentifier, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
}
|
||||||
+439
@@ -0,0 +1,439 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_static.php @generated by Composer
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
class ComposerStaticInita07591154e0386cfc9ad0f9217878d04
|
||||||
|
{
|
||||||
|
public static $files = array (
|
||||||
|
'383eaff206634a77a1be54e64e6459c7' => __DIR__ . '/..' . '/sabre/uri/lib/functions.php',
|
||||||
|
'2b9d0f43f9552984cfa82fee95491826' => __DIR__ . '/..' . '/sabre/event/lib/coroutine.php',
|
||||||
|
'd81bab31d3feb45bfe2f283ea3c8fdf7' => __DIR__ . '/..' . '/sabre/event/lib/Loop/functions.php',
|
||||||
|
'a1cce3d26cc15c00fcd0b3354bd72c88' => __DIR__ . '/..' . '/sabre/event/lib/Promise/functions.php',
|
||||||
|
'3569eecfeed3bcf0bad3c998a494ecb8' => __DIR__ . '/..' . '/sabre/xml/lib/Deserializer/functions.php',
|
||||||
|
'93aa591bc4ca510c520999e34229ee79' => __DIR__ . '/..' . '/sabre/xml/lib/Serializer/functions.php',
|
||||||
|
'ebdb698ed4152ae445614b69b5e4bb6a' => __DIR__ . '/..' . '/sabre/http/lib/functions.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixLengthsPsr4 = array (
|
||||||
|
'S' =>
|
||||||
|
array (
|
||||||
|
'Sabre\\Xml\\' => 10,
|
||||||
|
'Sabre\\VObject\\' => 14,
|
||||||
|
'Sabre\\Uri\\' => 10,
|
||||||
|
'Sabre\\HTTP\\' => 11,
|
||||||
|
'Sabre\\Event\\' => 12,
|
||||||
|
'Sabre\\' => 6,
|
||||||
|
),
|
||||||
|
'P' =>
|
||||||
|
array (
|
||||||
|
'Psr\\Log\\' => 8,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixDirsPsr4 = array (
|
||||||
|
'Sabre\\Xml\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/sabre/xml/lib',
|
||||||
|
),
|
||||||
|
'Sabre\\VObject\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/sabre/vobject/lib',
|
||||||
|
),
|
||||||
|
'Sabre\\Uri\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/sabre/uri/lib',
|
||||||
|
),
|
||||||
|
'Sabre\\HTTP\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/sabre/http/lib',
|
||||||
|
),
|
||||||
|
'Sabre\\Event\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/sabre/event/lib',
|
||||||
|
),
|
||||||
|
'Sabre\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/sabre/dav/lib',
|
||||||
|
),
|
||||||
|
'Psr\\Log\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/psr/log/src',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $classMap = array (
|
||||||
|
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||||
|
'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/src/AbstractLogger.php',
|
||||||
|
'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/src/InvalidArgumentException.php',
|
||||||
|
'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/src/LogLevel.php',
|
||||||
|
'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareInterface.php',
|
||||||
|
'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareTrait.php',
|
||||||
|
'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerInterface.php',
|
||||||
|
'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerTrait.php',
|
||||||
|
'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/src/NullLogger.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\AbstractBackend' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\BackendInterface' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Backend/BackendInterface.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\NotificationSupport' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\PDO' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Backend/PDO.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\SchedulingSupport' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\SharingSupport' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Backend/SharingSupport.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\SimplePDO' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Backend/SimplePDO.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\SubscriptionSupport' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php',
|
||||||
|
'Sabre\\CalDAV\\Backend\\SyncSupport' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Backend/SyncSupport.php',
|
||||||
|
'Sabre\\CalDAV\\Calendar' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Calendar.php',
|
||||||
|
'Sabre\\CalDAV\\CalendarHome' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/CalendarHome.php',
|
||||||
|
'Sabre\\CalDAV\\CalendarObject' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/CalendarObject.php',
|
||||||
|
'Sabre\\CalDAV\\CalendarQueryValidator' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/CalendarQueryValidator.php',
|
||||||
|
'Sabre\\CalDAV\\CalendarRoot' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/CalendarRoot.php',
|
||||||
|
'Sabre\\CalDAV\\Exception\\InvalidComponentType' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php',
|
||||||
|
'Sabre\\CalDAV\\ICSExportPlugin' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/ICSExportPlugin.php',
|
||||||
|
'Sabre\\CalDAV\\ICalendar' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/ICalendar.php',
|
||||||
|
'Sabre\\CalDAV\\ICalendarObject' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/ICalendarObject.php',
|
||||||
|
'Sabre\\CalDAV\\ICalendarObjectContainer' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/ICalendarObjectContainer.php',
|
||||||
|
'Sabre\\CalDAV\\ISharedCalendar' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/ISharedCalendar.php',
|
||||||
|
'Sabre\\CalDAV\\Notifications\\Collection' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Notifications/Collection.php',
|
||||||
|
'Sabre\\CalDAV\\Notifications\\ICollection' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Notifications/ICollection.php',
|
||||||
|
'Sabre\\CalDAV\\Notifications\\INode' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Notifications/INode.php',
|
||||||
|
'Sabre\\CalDAV\\Notifications\\Node' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Notifications/Node.php',
|
||||||
|
'Sabre\\CalDAV\\Notifications\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Notifications/Plugin.php',
|
||||||
|
'Sabre\\CalDAV\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Plugin.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\Collection' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Principal/Collection.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\IProxyRead' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Principal/IProxyRead.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\IProxyWrite' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Principal/IProxyWrite.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\ProxyRead' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Principal/ProxyRead.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\ProxyWrite' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php',
|
||||||
|
'Sabre\\CalDAV\\Principal\\User' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Principal/User.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\IInbox' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Schedule/IInbox.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\IMipPlugin' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Schedule/IMipPlugin.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\IOutbox' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Schedule/IOutbox.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\ISchedulingObject' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Schedule/ISchedulingObject.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\Inbox' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Schedule/Inbox.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\Outbox' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Schedule/Outbox.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Schedule/Plugin.php',
|
||||||
|
'Sabre\\CalDAV\\Schedule\\SchedulingObject' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php',
|
||||||
|
'Sabre\\CalDAV\\SharedCalendar' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/SharedCalendar.php',
|
||||||
|
'Sabre\\CalDAV\\SharingPlugin' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/SharingPlugin.php',
|
||||||
|
'Sabre\\CalDAV\\Subscriptions\\ISubscription' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php',
|
||||||
|
'Sabre\\CalDAV\\Subscriptions\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Subscriptions/Plugin.php',
|
||||||
|
'Sabre\\CalDAV\\Subscriptions\\Subscription' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Filter\\CalendarData' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Filter\\CompFilter' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Filter\\ParamFilter' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Filter\\PropFilter' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Notification\\Invite' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Notification\\InviteReply' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Notification\\NotificationInterface' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Notification\\SystemStatus' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Notification/SystemStatus.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\AllowedSharingModes' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\EmailAddressSet' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\Invite' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Property/Invite.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\ScheduleCalendarTransp' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarData' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Property\\SupportedCollationSet' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\CalendarMultiGetReport' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\CalendarQueryReport' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\FreeBusyQueryReport' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\InviteReply' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\MkCalendar' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php',
|
||||||
|
'Sabre\\CalDAV\\Xml\\Request\\Share' => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV/Xml/Request/Share.php',
|
||||||
|
'Sabre\\CardDAV\\AddressBook' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/AddressBook.php',
|
||||||
|
'Sabre\\CardDAV\\AddressBookHome' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/AddressBookHome.php',
|
||||||
|
'Sabre\\CardDAV\\AddressBookRoot' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/AddressBookRoot.php',
|
||||||
|
'Sabre\\CardDAV\\Backend\\AbstractBackend' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php',
|
||||||
|
'Sabre\\CardDAV\\Backend\\BackendInterface' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Backend/BackendInterface.php',
|
||||||
|
'Sabre\\CardDAV\\Backend\\PDO' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Backend/PDO.php',
|
||||||
|
'Sabre\\CardDAV\\Backend\\SyncSupport' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Backend/SyncSupport.php',
|
||||||
|
'Sabre\\CardDAV\\Card' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Card.php',
|
||||||
|
'Sabre\\CardDAV\\IAddressBook' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/IAddressBook.php',
|
||||||
|
'Sabre\\CardDAV\\ICard' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/ICard.php',
|
||||||
|
'Sabre\\CardDAV\\IDirectory' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/IDirectory.php',
|
||||||
|
'Sabre\\CardDAV\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Plugin.php',
|
||||||
|
'Sabre\\CardDAV\\VCFExportPlugin' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/VCFExportPlugin.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Filter\\AddressData' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Filter\\ParamFilter' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Filter\\PropFilter' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Property\\SupportedAddressData' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Property\\SupportedCollationSet' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Request\\AddressBookMultiGetReport' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php',
|
||||||
|
'Sabre\\CardDAV\\Xml\\Request\\AddressBookQueryReport' => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php',
|
||||||
|
'Sabre\\DAVACL\\ACLTrait' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/ACLTrait.php',
|
||||||
|
'Sabre\\DAVACL\\AbstractPrincipalCollection' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php',
|
||||||
|
'Sabre\\DAVACL\\Exception\\AceConflict' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Exception/AceConflict.php',
|
||||||
|
'Sabre\\DAVACL\\Exception\\NeedPrivileges' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php',
|
||||||
|
'Sabre\\DAVACL\\Exception\\NoAbstract' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Exception/NoAbstract.php',
|
||||||
|
'Sabre\\DAVACL\\Exception\\NotRecognizedPrincipal' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php',
|
||||||
|
'Sabre\\DAVACL\\Exception\\NotSupportedPrivilege' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php',
|
||||||
|
'Sabre\\DAVACL\\FS\\Collection' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/FS/Collection.php',
|
||||||
|
'Sabre\\DAVACL\\FS\\File' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/FS/File.php',
|
||||||
|
'Sabre\\DAVACL\\FS\\HomeCollection' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/FS/HomeCollection.php',
|
||||||
|
'Sabre\\DAVACL\\IACL' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/IACL.php',
|
||||||
|
'Sabre\\DAVACL\\IPrincipal' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/IPrincipal.php',
|
||||||
|
'Sabre\\DAVACL\\IPrincipalCollection' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/IPrincipalCollection.php',
|
||||||
|
'Sabre\\DAVACL\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Plugin.php',
|
||||||
|
'Sabre\\DAVACL\\Principal' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Principal.php',
|
||||||
|
'Sabre\\DAVACL\\PrincipalBackend\\AbstractBackend' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php',
|
||||||
|
'Sabre\\DAVACL\\PrincipalBackend\\BackendInterface' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php',
|
||||||
|
'Sabre\\DAVACL\\PrincipalBackend\\CreatePrincipalSupport' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/PrincipalBackend/CreatePrincipalSupport.php',
|
||||||
|
'Sabre\\DAVACL\\PrincipalBackend\\PDO' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/PrincipalBackend/PDO.php',
|
||||||
|
'Sabre\\DAVACL\\PrincipalCollection' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/PrincipalCollection.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Property\\Acl' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Xml/Property/Acl.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Property\\AclRestrictions' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Property\\CurrentUserPrivilegeSet' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Property\\Principal' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Xml/Property/Principal.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Property\\SupportedPrivilegeSet' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Request\\AclPrincipalPropSetReport' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Xml/Request/AclPrincipalPropSetReport.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Request\\ExpandPropertyReport' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Request\\PrincipalMatchReport' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Xml/Request/PrincipalMatchReport.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Request\\PrincipalPropertySearchReport' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php',
|
||||||
|
'Sabre\\DAVACL\\Xml\\Request\\PrincipalSearchPropertySetReport' => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\AbstractBasic' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\AbstractBearer' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\AbstractDigest' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\Apache' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Auth/Backend/Apache.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\BackendInterface' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\BasicCallBack' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Auth/Backend/BasicCallBack.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\File' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Auth/Backend/File.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\IMAP' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Auth/Backend/IMAP.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\PDO' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Auth/Backend/PDO.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Backend\\PDOBasicAuth' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Auth/Backend/PDOBasicAuth.php',
|
||||||
|
'Sabre\\DAV\\Auth\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Auth/Plugin.php',
|
||||||
|
'Sabre\\DAV\\Browser\\GuessContentType' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Browser/GuessContentType.php',
|
||||||
|
'Sabre\\DAV\\Browser\\HtmlOutput' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Browser/HtmlOutput.php',
|
||||||
|
'Sabre\\DAV\\Browser\\HtmlOutputHelper' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Browser/HtmlOutputHelper.php',
|
||||||
|
'Sabre\\DAV\\Browser\\MapGetToPropFind' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php',
|
||||||
|
'Sabre\\DAV\\Browser\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Browser/Plugin.php',
|
||||||
|
'Sabre\\DAV\\Browser\\PropFindAll' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Browser/PropFindAll.php',
|
||||||
|
'Sabre\\DAV\\Client' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Client.php',
|
||||||
|
'Sabre\\DAV\\Collection' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Collection.php',
|
||||||
|
'Sabre\\DAV\\CorePlugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/CorePlugin.php',
|
||||||
|
'Sabre\\DAV\\Exception' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception.php',
|
||||||
|
'Sabre\\DAV\\Exception\\BadRequest' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/BadRequest.php',
|
||||||
|
'Sabre\\DAV\\Exception\\Conflict' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/Conflict.php',
|
||||||
|
'Sabre\\DAV\\Exception\\ConflictingLock' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/ConflictingLock.php',
|
||||||
|
'Sabre\\DAV\\Exception\\Forbidden' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/Forbidden.php',
|
||||||
|
'Sabre\\DAV\\Exception\\InsufficientStorage' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/InsufficientStorage.php',
|
||||||
|
'Sabre\\DAV\\Exception\\InvalidResourceType' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/InvalidResourceType.php',
|
||||||
|
'Sabre\\DAV\\Exception\\InvalidSyncToken' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php',
|
||||||
|
'Sabre\\DAV\\Exception\\LengthRequired' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/LengthRequired.php',
|
||||||
|
'Sabre\\DAV\\Exception\\LockTokenMatchesRequestUri' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/LockTokenMatchesRequestUri.php',
|
||||||
|
'Sabre\\DAV\\Exception\\Locked' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/Locked.php',
|
||||||
|
'Sabre\\DAV\\Exception\\MethodNotAllowed' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php',
|
||||||
|
'Sabre\\DAV\\Exception\\NotAuthenticated' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/NotAuthenticated.php',
|
||||||
|
'Sabre\\DAV\\Exception\\NotFound' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/NotFound.php',
|
||||||
|
'Sabre\\DAV\\Exception\\NotImplemented' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/NotImplemented.php',
|
||||||
|
'Sabre\\DAV\\Exception\\PaymentRequired' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/PaymentRequired.php',
|
||||||
|
'Sabre\\DAV\\Exception\\PreconditionFailed' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/PreconditionFailed.php',
|
||||||
|
'Sabre\\DAV\\Exception\\ReportNotSupported' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/ReportNotSupported.php',
|
||||||
|
'Sabre\\DAV\\Exception\\RequestedRangeNotSatisfiable' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php',
|
||||||
|
'Sabre\\DAV\\Exception\\ServiceUnavailable' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/ServiceUnavailable.php',
|
||||||
|
'Sabre\\DAV\\Exception\\TooManyMatches' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/TooManyMatches.php',
|
||||||
|
'Sabre\\DAV\\Exception\\UnsupportedMediaType' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php',
|
||||||
|
'Sabre\\DAV\\FSExt\\Directory' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/FSExt/Directory.php',
|
||||||
|
'Sabre\\DAV\\FSExt\\File' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/FSExt/File.php',
|
||||||
|
'Sabre\\DAV\\FS\\Directory' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/FS/Directory.php',
|
||||||
|
'Sabre\\DAV\\FS\\File' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/FS/File.php',
|
||||||
|
'Sabre\\DAV\\FS\\Node' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/FS/Node.php',
|
||||||
|
'Sabre\\DAV\\File' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/File.php',
|
||||||
|
'Sabre\\DAV\\ICollection' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/ICollection.php',
|
||||||
|
'Sabre\\DAV\\ICopyTarget' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/ICopyTarget.php',
|
||||||
|
'Sabre\\DAV\\IExtendedCollection' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/IExtendedCollection.php',
|
||||||
|
'Sabre\\DAV\\IFile' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/IFile.php',
|
||||||
|
'Sabre\\DAV\\IMoveTarget' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/IMoveTarget.php',
|
||||||
|
'Sabre\\DAV\\IMultiGet' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/IMultiGet.php',
|
||||||
|
'Sabre\\DAV\\INode' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/INode.php',
|
||||||
|
'Sabre\\DAV\\INodeByPath' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/INodeByPath.php',
|
||||||
|
'Sabre\\DAV\\IProperties' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/IProperties.php',
|
||||||
|
'Sabre\\DAV\\IQuota' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/IQuota.php',
|
||||||
|
'Sabre\\DAV\\Locks\\Backend\\AbstractBackend' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Locks/Backend/AbstractBackend.php',
|
||||||
|
'Sabre\\DAV\\Locks\\Backend\\BackendInterface' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Locks/Backend/BackendInterface.php',
|
||||||
|
'Sabre\\DAV\\Locks\\Backend\\File' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Locks/Backend/File.php',
|
||||||
|
'Sabre\\DAV\\Locks\\Backend\\PDO' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Locks/Backend/PDO.php',
|
||||||
|
'Sabre\\DAV\\Locks\\LockInfo' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Locks/LockInfo.php',
|
||||||
|
'Sabre\\DAV\\Locks\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Locks/Plugin.php',
|
||||||
|
'Sabre\\DAV\\MkCol' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/MkCol.php',
|
||||||
|
'Sabre\\DAV\\Mount\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Mount/Plugin.php',
|
||||||
|
'Sabre\\DAV\\Node' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Node.php',
|
||||||
|
'Sabre\\DAV\\PartialUpdate\\IPatchSupport' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/PartialUpdate/IPatchSupport.php',
|
||||||
|
'Sabre\\DAV\\PartialUpdate\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/PartialUpdate/Plugin.php',
|
||||||
|
'Sabre\\DAV\\PropFind' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/PropFind.php',
|
||||||
|
'Sabre\\DAV\\PropPatch' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/PropPatch.php',
|
||||||
|
'Sabre\\DAV\\PropertyStorage\\Backend\\BackendInterface' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php',
|
||||||
|
'Sabre\\DAV\\PropertyStorage\\Backend\\PDO' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php',
|
||||||
|
'Sabre\\DAV\\PropertyStorage\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/PropertyStorage/Plugin.php',
|
||||||
|
'Sabre\\DAV\\Server' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Server.php',
|
||||||
|
'Sabre\\DAV\\ServerPlugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/ServerPlugin.php',
|
||||||
|
'Sabre\\DAV\\Sharing\\ISharedNode' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Sharing/ISharedNode.php',
|
||||||
|
'Sabre\\DAV\\Sharing\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Sharing/Plugin.php',
|
||||||
|
'Sabre\\DAV\\SimpleCollection' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/SimpleCollection.php',
|
||||||
|
'Sabre\\DAV\\SimpleFile' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/SimpleFile.php',
|
||||||
|
'Sabre\\DAV\\StringUtil' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/StringUtil.php',
|
||||||
|
'Sabre\\DAV\\Sync\\ISyncCollection' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Sync/ISyncCollection.php',
|
||||||
|
'Sabre\\DAV\\Sync\\Plugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Sync/Plugin.php',
|
||||||
|
'Sabre\\DAV\\TemporaryFileFilterPlugin' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php',
|
||||||
|
'Sabre\\DAV\\Tree' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Tree.php',
|
||||||
|
'Sabre\\DAV\\UUIDUtil' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/UUIDUtil.php',
|
||||||
|
'Sabre\\DAV\\Version' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Version.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Element\\Prop' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Element/Prop.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Element\\Response' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Element/Response.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Element\\Sharee' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Element/Sharee.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\Complex' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Property/Complex.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\GetLastModified' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\Href' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Property/Href.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\Invite' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Property/Invite.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\LocalHref' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Property/LocalHref.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\LockDiscovery' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Property/LockDiscovery.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\ResourceType' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Property/ResourceType.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\ShareAccess' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Property/ShareAccess.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\SupportedLock' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\SupportedMethodSet' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Property\\SupportedReportSet' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\Lock' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Request/Lock.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\MkCol' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Request/MkCol.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\PropFind' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Request/PropFind.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\PropPatch' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Request/PropPatch.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\ShareResource' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Request/ShareResource.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Response\\MultiStatus' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php',
|
||||||
|
'Sabre\\DAV\\Xml\\Service' => __DIR__ . '/..' . '/sabre/dav/lib/DAV/Xml/Service.php',
|
||||||
|
'Sabre\\Event\\Emitter' => __DIR__ . '/..' . '/sabre/event/lib/Emitter.php',
|
||||||
|
'Sabre\\Event\\EmitterInterface' => __DIR__ . '/..' . '/sabre/event/lib/EmitterInterface.php',
|
||||||
|
'Sabre\\Event\\EmitterTrait' => __DIR__ . '/..' . '/sabre/event/lib/EmitterTrait.php',
|
||||||
|
'Sabre\\Event\\EventEmitter' => __DIR__ . '/..' . '/sabre/event/lib/EventEmitter.php',
|
||||||
|
'Sabre\\Event\\Loop\\Loop' => __DIR__ . '/..' . '/sabre/event/lib/Loop/Loop.php',
|
||||||
|
'Sabre\\Event\\Promise' => __DIR__ . '/..' . '/sabre/event/lib/Promise.php',
|
||||||
|
'Sabre\\Event\\PromiseAlreadyResolvedException' => __DIR__ . '/..' . '/sabre/event/lib/PromiseAlreadyResolvedException.php',
|
||||||
|
'Sabre\\Event\\Version' => __DIR__ . '/..' . '/sabre/event/lib/Version.php',
|
||||||
|
'Sabre\\Event\\WildcardEmitter' => __DIR__ . '/..' . '/sabre/event/lib/WildcardEmitter.php',
|
||||||
|
'Sabre\\Event\\WildcardEmitterTrait' => __DIR__ . '/..' . '/sabre/event/lib/WildcardEmitterTrait.php',
|
||||||
|
'Sabre\\HTTP\\Auth\\AWS' => __DIR__ . '/..' . '/sabre/http/lib/Auth/AWS.php',
|
||||||
|
'Sabre\\HTTP\\Auth\\AbstractAuth' => __DIR__ . '/..' . '/sabre/http/lib/Auth/AbstractAuth.php',
|
||||||
|
'Sabre\\HTTP\\Auth\\Basic' => __DIR__ . '/..' . '/sabre/http/lib/Auth/Basic.php',
|
||||||
|
'Sabre\\HTTP\\Auth\\Bearer' => __DIR__ . '/..' . '/sabre/http/lib/Auth/Bearer.php',
|
||||||
|
'Sabre\\HTTP\\Auth\\Digest' => __DIR__ . '/..' . '/sabre/http/lib/Auth/Digest.php',
|
||||||
|
'Sabre\\HTTP\\Client' => __DIR__ . '/..' . '/sabre/http/lib/Client.php',
|
||||||
|
'Sabre\\HTTP\\ClientException' => __DIR__ . '/..' . '/sabre/http/lib/ClientException.php',
|
||||||
|
'Sabre\\HTTP\\ClientHttpException' => __DIR__ . '/..' . '/sabre/http/lib/ClientHttpException.php',
|
||||||
|
'Sabre\\HTTP\\HttpException' => __DIR__ . '/..' . '/sabre/http/lib/HttpException.php',
|
||||||
|
'Sabre\\HTTP\\Message' => __DIR__ . '/..' . '/sabre/http/lib/Message.php',
|
||||||
|
'Sabre\\HTTP\\MessageDecoratorTrait' => __DIR__ . '/..' . '/sabre/http/lib/MessageDecoratorTrait.php',
|
||||||
|
'Sabre\\HTTP\\MessageInterface' => __DIR__ . '/..' . '/sabre/http/lib/MessageInterface.php',
|
||||||
|
'Sabre\\HTTP\\Request' => __DIR__ . '/..' . '/sabre/http/lib/Request.php',
|
||||||
|
'Sabre\\HTTP\\RequestDecorator' => __DIR__ . '/..' . '/sabre/http/lib/RequestDecorator.php',
|
||||||
|
'Sabre\\HTTP\\RequestInterface' => __DIR__ . '/..' . '/sabre/http/lib/RequestInterface.php',
|
||||||
|
'Sabre\\HTTP\\Response' => __DIR__ . '/..' . '/sabre/http/lib/Response.php',
|
||||||
|
'Sabre\\HTTP\\ResponseDecorator' => __DIR__ . '/..' . '/sabre/http/lib/ResponseDecorator.php',
|
||||||
|
'Sabre\\HTTP\\ResponseInterface' => __DIR__ . '/..' . '/sabre/http/lib/ResponseInterface.php',
|
||||||
|
'Sabre\\HTTP\\Sapi' => __DIR__ . '/..' . '/sabre/http/lib/Sapi.php',
|
||||||
|
'Sabre\\HTTP\\Version' => __DIR__ . '/..' . '/sabre/http/lib/Version.php',
|
||||||
|
'Sabre\\Uri\\InvalidUriException' => __DIR__ . '/..' . '/sabre/uri/lib/InvalidUriException.php',
|
||||||
|
'Sabre\\Uri\\Version' => __DIR__ . '/..' . '/sabre/uri/lib/Version.php',
|
||||||
|
'Sabre\\VObject\\BirthdayCalendarGenerator' => __DIR__ . '/..' . '/sabre/vobject/lib/BirthdayCalendarGenerator.php',
|
||||||
|
'Sabre\\VObject\\Cli' => __DIR__ . '/..' . '/sabre/vobject/lib/Cli.php',
|
||||||
|
'Sabre\\VObject\\Component' => __DIR__ . '/..' . '/sabre/vobject/lib/Component.php',
|
||||||
|
'Sabre\\VObject\\Component\\Available' => __DIR__ . '/..' . '/sabre/vobject/lib/Component/Available.php',
|
||||||
|
'Sabre\\VObject\\Component\\VAlarm' => __DIR__ . '/..' . '/sabre/vobject/lib/Component/VAlarm.php',
|
||||||
|
'Sabre\\VObject\\Component\\VAvailability' => __DIR__ . '/..' . '/sabre/vobject/lib/Component/VAvailability.php',
|
||||||
|
'Sabre\\VObject\\Component\\VCalendar' => __DIR__ . '/..' . '/sabre/vobject/lib/Component/VCalendar.php',
|
||||||
|
'Sabre\\VObject\\Component\\VCard' => __DIR__ . '/..' . '/sabre/vobject/lib/Component/VCard.php',
|
||||||
|
'Sabre\\VObject\\Component\\VEvent' => __DIR__ . '/..' . '/sabre/vobject/lib/Component/VEvent.php',
|
||||||
|
'Sabre\\VObject\\Component\\VFreeBusy' => __DIR__ . '/..' . '/sabre/vobject/lib/Component/VFreeBusy.php',
|
||||||
|
'Sabre\\VObject\\Component\\VJournal' => __DIR__ . '/..' . '/sabre/vobject/lib/Component/VJournal.php',
|
||||||
|
'Sabre\\VObject\\Component\\VTimeZone' => __DIR__ . '/..' . '/sabre/vobject/lib/Component/VTimeZone.php',
|
||||||
|
'Sabre\\VObject\\Component\\VTodo' => __DIR__ . '/..' . '/sabre/vobject/lib/Component/VTodo.php',
|
||||||
|
'Sabre\\VObject\\DateTimeParser' => __DIR__ . '/..' . '/sabre/vobject/lib/DateTimeParser.php',
|
||||||
|
'Sabre\\VObject\\Document' => __DIR__ . '/..' . '/sabre/vobject/lib/Document.php',
|
||||||
|
'Sabre\\VObject\\ElementList' => __DIR__ . '/..' . '/sabre/vobject/lib/ElementList.php',
|
||||||
|
'Sabre\\VObject\\EofException' => __DIR__ . '/..' . '/sabre/vobject/lib/EofException.php',
|
||||||
|
'Sabre\\VObject\\FreeBusyData' => __DIR__ . '/..' . '/sabre/vobject/lib/FreeBusyData.php',
|
||||||
|
'Sabre\\VObject\\FreeBusyGenerator' => __DIR__ . '/..' . '/sabre/vobject/lib/FreeBusyGenerator.php',
|
||||||
|
'Sabre\\VObject\\ITip\\Broker' => __DIR__ . '/..' . '/sabre/vobject/lib/ITip/Broker.php',
|
||||||
|
'Sabre\\VObject\\ITip\\ITipException' => __DIR__ . '/..' . '/sabre/vobject/lib/ITip/ITipException.php',
|
||||||
|
'Sabre\\VObject\\ITip\\Message' => __DIR__ . '/..' . '/sabre/vobject/lib/ITip/Message.php',
|
||||||
|
'Sabre\\VObject\\ITip\\SameOrganizerForAllComponentsException' => __DIR__ . '/..' . '/sabre/vobject/lib/ITip/SameOrganizerForAllComponentsException.php',
|
||||||
|
'Sabre\\VObject\\InvalidDataException' => __DIR__ . '/..' . '/sabre/vobject/lib/InvalidDataException.php',
|
||||||
|
'Sabre\\VObject\\Node' => __DIR__ . '/..' . '/sabre/vobject/lib/Node.php',
|
||||||
|
'Sabre\\VObject\\PHPUnitAssertions' => __DIR__ . '/..' . '/sabre/vobject/lib/PHPUnitAssertions.php',
|
||||||
|
'Sabre\\VObject\\Parameter' => __DIR__ . '/..' . '/sabre/vobject/lib/Parameter.php',
|
||||||
|
'Sabre\\VObject\\ParseException' => __DIR__ . '/..' . '/sabre/vobject/lib/ParseException.php',
|
||||||
|
'Sabre\\VObject\\Parser\\Json' => __DIR__ . '/..' . '/sabre/vobject/lib/Parser/Json.php',
|
||||||
|
'Sabre\\VObject\\Parser\\MimeDir' => __DIR__ . '/..' . '/sabre/vobject/lib/Parser/MimeDir.php',
|
||||||
|
'Sabre\\VObject\\Parser\\Parser' => __DIR__ . '/..' . '/sabre/vobject/lib/Parser/Parser.php',
|
||||||
|
'Sabre\\VObject\\Parser\\XML' => __DIR__ . '/..' . '/sabre/vobject/lib/Parser/XML.php',
|
||||||
|
'Sabre\\VObject\\Parser\\XML\\Element\\KeyValue' => __DIR__ . '/..' . '/sabre/vobject/lib/Parser/XML/Element/KeyValue.php',
|
||||||
|
'Sabre\\VObject\\Property' => __DIR__ . '/..' . '/sabre/vobject/lib/Property.php',
|
||||||
|
'Sabre\\VObject\\Property\\Binary' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/Binary.php',
|
||||||
|
'Sabre\\VObject\\Property\\Boolean' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/Boolean.php',
|
||||||
|
'Sabre\\VObject\\Property\\FlatText' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/FlatText.php',
|
||||||
|
'Sabre\\VObject\\Property\\FloatValue' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/FloatValue.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\CalAddress' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/ICalendar/CalAddress.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\Date' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/ICalendar/Date.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\DateTime' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/ICalendar/DateTime.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\Duration' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/ICalendar/Duration.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\Period' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/ICalendar/Period.php',
|
||||||
|
'Sabre\\VObject\\Property\\ICalendar\\Recur' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/ICalendar/Recur.php',
|
||||||
|
'Sabre\\VObject\\Property\\IntegerValue' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/IntegerValue.php',
|
||||||
|
'Sabre\\VObject\\Property\\Text' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/Text.php',
|
||||||
|
'Sabre\\VObject\\Property\\Time' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/Time.php',
|
||||||
|
'Sabre\\VObject\\Property\\Unknown' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/Unknown.php',
|
||||||
|
'Sabre\\VObject\\Property\\Uri' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/Uri.php',
|
||||||
|
'Sabre\\VObject\\Property\\UtcOffset' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/UtcOffset.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\Date' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/VCard/Date.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\DateAndOrTime' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/VCard/DateAndOrTime.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\DateTime' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/VCard/DateTime.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\LanguageTag' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/VCard/LanguageTag.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\PhoneNumber' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/VCard/PhoneNumber.php',
|
||||||
|
'Sabre\\VObject\\Property\\VCard\\TimeStamp' => __DIR__ . '/..' . '/sabre/vobject/lib/Property/VCard/TimeStamp.php',
|
||||||
|
'Sabre\\VObject\\Reader' => __DIR__ . '/..' . '/sabre/vobject/lib/Reader.php',
|
||||||
|
'Sabre\\VObject\\Recur\\EventIterator' => __DIR__ . '/..' . '/sabre/vobject/lib/Recur/EventIterator.php',
|
||||||
|
'Sabre\\VObject\\Recur\\MaxInstancesExceededException' => __DIR__ . '/..' . '/sabre/vobject/lib/Recur/MaxInstancesExceededException.php',
|
||||||
|
'Sabre\\VObject\\Recur\\NoInstancesException' => __DIR__ . '/..' . '/sabre/vobject/lib/Recur/NoInstancesException.php',
|
||||||
|
'Sabre\\VObject\\Recur\\RDateIterator' => __DIR__ . '/..' . '/sabre/vobject/lib/Recur/RDateIterator.php',
|
||||||
|
'Sabre\\VObject\\Recur\\RRuleIterator' => __DIR__ . '/..' . '/sabre/vobject/lib/Recur/RRuleIterator.php',
|
||||||
|
'Sabre\\VObject\\Settings' => __DIR__ . '/..' . '/sabre/vobject/lib/Settings.php',
|
||||||
|
'Sabre\\VObject\\Splitter\\ICalendar' => __DIR__ . '/..' . '/sabre/vobject/lib/Splitter/ICalendar.php',
|
||||||
|
'Sabre\\VObject\\Splitter\\SplitterInterface' => __DIR__ . '/..' . '/sabre/vobject/lib/Splitter/SplitterInterface.php',
|
||||||
|
'Sabre\\VObject\\Splitter\\VCard' => __DIR__ . '/..' . '/sabre/vobject/lib/Splitter/VCard.php',
|
||||||
|
'Sabre\\VObject\\StringUtil' => __DIR__ . '/..' . '/sabre/vobject/lib/StringUtil.php',
|
||||||
|
'Sabre\\VObject\\TimeZoneUtil' => __DIR__ . '/..' . '/sabre/vobject/lib/TimeZoneUtil.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\FindFromOffset' => __DIR__ . '/..' . '/sabre/vobject/lib/TimezoneGuesser/FindFromOffset.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\FindFromTimezoneIdentifier' => __DIR__ . '/..' . '/sabre/vobject/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\FindFromTimezoneMap' => __DIR__ . '/..' . '/sabre/vobject/lib/TimezoneGuesser/FindFromTimezoneMap.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\GuessFromLicEntry' => __DIR__ . '/..' . '/sabre/vobject/lib/TimezoneGuesser/GuessFromLicEntry.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\GuessFromMsTzId' => __DIR__ . '/..' . '/sabre/vobject/lib/TimezoneGuesser/GuessFromMsTzId.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\TimezoneFinder' => __DIR__ . '/..' . '/sabre/vobject/lib/TimezoneGuesser/TimezoneFinder.php',
|
||||||
|
'Sabre\\VObject\\TimezoneGuesser\\TimezoneGuesser' => __DIR__ . '/..' . '/sabre/vobject/lib/TimezoneGuesser/TimezoneGuesser.php',
|
||||||
|
'Sabre\\VObject\\UUIDUtil' => __DIR__ . '/..' . '/sabre/vobject/lib/UUIDUtil.php',
|
||||||
|
'Sabre\\VObject\\VCardConverter' => __DIR__ . '/..' . '/sabre/vobject/lib/VCardConverter.php',
|
||||||
|
'Sabre\\VObject\\Version' => __DIR__ . '/..' . '/sabre/vobject/lib/Version.php',
|
||||||
|
'Sabre\\VObject\\Writer' => __DIR__ . '/..' . '/sabre/vobject/lib/Writer.php',
|
||||||
|
'Sabre\\Xml\\ContextStackTrait' => __DIR__ . '/..' . '/sabre/xml/lib/ContextStackTrait.php',
|
||||||
|
'Sabre\\Xml\\Element' => __DIR__ . '/..' . '/sabre/xml/lib/Element.php',
|
||||||
|
'Sabre\\Xml\\Element\\Base' => __DIR__ . '/..' . '/sabre/xml/lib/Element/Base.php',
|
||||||
|
'Sabre\\Xml\\Element\\Cdata' => __DIR__ . '/..' . '/sabre/xml/lib/Element/Cdata.php',
|
||||||
|
'Sabre\\Xml\\Element\\Elements' => __DIR__ . '/..' . '/sabre/xml/lib/Element/Elements.php',
|
||||||
|
'Sabre\\Xml\\Element\\KeyValue' => __DIR__ . '/..' . '/sabre/xml/lib/Element/KeyValue.php',
|
||||||
|
'Sabre\\Xml\\Element\\Uri' => __DIR__ . '/..' . '/sabre/xml/lib/Element/Uri.php',
|
||||||
|
'Sabre\\Xml\\Element\\XmlFragment' => __DIR__ . '/..' . '/sabre/xml/lib/Element/XmlFragment.php',
|
||||||
|
'Sabre\\Xml\\LibXMLException' => __DIR__ . '/..' . '/sabre/xml/lib/LibXMLException.php',
|
||||||
|
'Sabre\\Xml\\ParseException' => __DIR__ . '/..' . '/sabre/xml/lib/ParseException.php',
|
||||||
|
'Sabre\\Xml\\Reader' => __DIR__ . '/..' . '/sabre/xml/lib/Reader.php',
|
||||||
|
'Sabre\\Xml\\Service' => __DIR__ . '/..' . '/sabre/xml/lib/Service.php',
|
||||||
|
'Sabre\\Xml\\Version' => __DIR__ . '/..' . '/sabre/xml/lib/Version.php',
|
||||||
|
'Sabre\\Xml\\Writer' => __DIR__ . '/..' . '/sabre/xml/lib/Writer.php',
|
||||||
|
'Sabre\\Xml\\XmlDeserializable' => __DIR__ . '/..' . '/sabre/xml/lib/XmlDeserializable.php',
|
||||||
|
'Sabre\\Xml\\XmlSerializable' => __DIR__ . '/..' . '/sabre/xml/lib/XmlSerializable.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static function getInitializer(ClassLoader $loader)
|
||||||
|
{
|
||||||
|
return \Closure::bind(function () use ($loader) {
|
||||||
|
$loader->prefixLengthsPsr4 = ComposerStaticInita07591154e0386cfc9ad0f9217878d04::$prefixLengthsPsr4;
|
||||||
|
$loader->prefixDirsPsr4 = ComposerStaticInita07591154e0386cfc9ad0f9217878d04::$prefixDirsPsr4;
|
||||||
|
$loader->classMap = ComposerStaticInita07591154e0386cfc9ad0f9217878d04::$classMap;
|
||||||
|
|
||||||
|
}, null, ClassLoader::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+522
@@ -0,0 +1,522 @@
|
|||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "psr/log",
|
||||||
|
"version": "3.0.2",
|
||||||
|
"version_normalized": "3.0.2.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/log.git",
|
||||||
|
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
|
||||||
|
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"time": "2024-09-11T13:17:53+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Log\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interface for logging libraries",
|
||||||
|
"homepage": "https://github.com/php-fig/log",
|
||||||
|
"keywords": [
|
||||||
|
"log",
|
||||||
|
"psr",
|
||||||
|
"psr-3"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/log/tree/3.0.2"
|
||||||
|
},
|
||||||
|
"install-path": "../psr/log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/dav",
|
||||||
|
"version": "4.7.0",
|
||||||
|
"version_normalized": "4.7.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/dav.git",
|
||||||
|
"reference": "074373bcd689a30bcf5aaa6bbb20a3395964ce7a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/dav/zipball/074373bcd689a30bcf5aaa6bbb20a3395964ce7a",
|
||||||
|
"reference": "074373bcd689a30bcf5aaa6bbb20a3395964ce7a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"ext-date": "*",
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-iconv": "*",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"ext-pcre": "*",
|
||||||
|
"ext-simplexml": "*",
|
||||||
|
"ext-spl": "*",
|
||||||
|
"lib-libxml": ">=2.7.0",
|
||||||
|
"php": "^7.1.0 || ^8.0",
|
||||||
|
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||||
|
"sabre/event": "^5.0",
|
||||||
|
"sabre/http": "^5.0.5",
|
||||||
|
"sabre/uri": "^2.0",
|
||||||
|
"sabre/vobject": "^4.2.1",
|
||||||
|
"sabre/xml": "^2.0.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^2.19",
|
||||||
|
"monolog/monolog": "^1.27 || ^2.0",
|
||||||
|
"phpstan/phpstan": "^0.12 || ^1.0",
|
||||||
|
"phpstan/phpstan-phpunit": "^1.0",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-curl": "*",
|
||||||
|
"ext-imap": "*",
|
||||||
|
"ext-pdo": "*"
|
||||||
|
},
|
||||||
|
"time": "2024-10-29T11:46:02+00:00",
|
||||||
|
"bin": [
|
||||||
|
"bin/sabredav",
|
||||||
|
"bin/naturalselection"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "WebDAV Framework for PHP",
|
||||||
|
"homepage": "http://sabre.io/",
|
||||||
|
"keywords": [
|
||||||
|
"CalDAV",
|
||||||
|
"CardDAV",
|
||||||
|
"WebDAV",
|
||||||
|
"framework",
|
||||||
|
"iCalendar"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/dav/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-dav"
|
||||||
|
},
|
||||||
|
"install-path": "../sabre/dav"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/event",
|
||||||
|
"version": "5.1.7",
|
||||||
|
"version_normalized": "5.1.7.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/event.git",
|
||||||
|
"reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/event/zipball/86d57e305c272898ba3c28e9bd3d65d5464587c2",
|
||||||
|
"reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "~2.17.1||^3.63",
|
||||||
|
"phpstan/phpstan": "^0.12",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6"
|
||||||
|
},
|
||||||
|
"time": "2024-08-27T11:23:05+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"lib/coroutine.php",
|
||||||
|
"lib/Loop/functions.php",
|
||||||
|
"lib/Promise/functions.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\Event\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "sabre/event is a library for lightweight event-based programming",
|
||||||
|
"homepage": "http://sabre.io/event/",
|
||||||
|
"keywords": [
|
||||||
|
"EventEmitter",
|
||||||
|
"async",
|
||||||
|
"coroutine",
|
||||||
|
"eventloop",
|
||||||
|
"events",
|
||||||
|
"hooks",
|
||||||
|
"plugin",
|
||||||
|
"promise",
|
||||||
|
"reactor",
|
||||||
|
"signal"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/event/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-event"
|
||||||
|
},
|
||||||
|
"install-path": "../sabre/event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/http",
|
||||||
|
"version": "5.1.13",
|
||||||
|
"version_normalized": "5.1.13.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/http.git",
|
||||||
|
"reference": "7c2a14097d1a0de2347dcbdc91a02f38e338f4db"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/http/zipball/7c2a14097d1a0de2347dcbdc91a02f38e338f4db",
|
||||||
|
"reference": "7c2a14097d1a0de2347dcbdc91a02f38e338f4db",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"ext-curl": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"php": "^7.1 || ^8.0",
|
||||||
|
"sabre/event": ">=4.0 <6.0",
|
||||||
|
"sabre/uri": "^2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "~2.17.1||3.63.2",
|
||||||
|
"phpstan/phpstan": "^0.12",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-curl": " to make http requests with the Client class"
|
||||||
|
},
|
||||||
|
"time": "2025-09-09T10:21:47+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"lib/functions.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\HTTP\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The sabre/http library provides utilities for dealing with http requests and responses. ",
|
||||||
|
"homepage": "https://github.com/fruux/sabre-http",
|
||||||
|
"keywords": [
|
||||||
|
"http"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/http/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-http"
|
||||||
|
},
|
||||||
|
"install-path": "../sabre/http"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/uri",
|
||||||
|
"version": "2.3.4",
|
||||||
|
"version_normalized": "2.3.4.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/uri.git",
|
||||||
|
"reference": "b76524c22de90d80ca73143680a8e77b1266c291"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/uri/zipball/b76524c22de90d80ca73143680a8e77b1266c291",
|
||||||
|
"reference": "b76524c22de90d80ca73143680a8e77b1266c291",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.63",
|
||||||
|
"phpstan/extension-installer": "^1.4",
|
||||||
|
"phpstan/phpstan": "^1.12",
|
||||||
|
"phpstan/phpstan-phpunit": "^1.4",
|
||||||
|
"phpstan/phpstan-strict-rules": "^1.6",
|
||||||
|
"phpunit/phpunit": "^9.6"
|
||||||
|
},
|
||||||
|
"time": "2024-08-27T12:18:16+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"lib/functions.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\Uri\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Functions for making sense out of URIs.",
|
||||||
|
"homepage": "http://sabre.io/uri/",
|
||||||
|
"keywords": [
|
||||||
|
"rfc3986",
|
||||||
|
"uri",
|
||||||
|
"url"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/uri/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-uri"
|
||||||
|
},
|
||||||
|
"install-path": "../sabre/uri"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/vobject",
|
||||||
|
"version": "4.5.8",
|
||||||
|
"version_normalized": "4.5.8.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/vobject.git",
|
||||||
|
"reference": "d554eb24d64232922e1eab5896cc2f84b3b9ffb1"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/vobject/zipball/d554eb24d64232922e1eab5896cc2f84b3b9ffb1",
|
||||||
|
"reference": "d554eb24d64232922e1eab5896cc2f84b3b9ffb1",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"php": "^7.1 || ^8.0",
|
||||||
|
"sabre/xml": "^2.1 || ^3.0 || ^4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "~2.17.1",
|
||||||
|
"phpstan/phpstan": "^0.12 || ^1.12 || ^2.0",
|
||||||
|
"phpunit/php-invoker": "^2.0 || ^3.1",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"hoa/bench": "If you would like to run the benchmark scripts"
|
||||||
|
},
|
||||||
|
"time": "2026-01-12T10:45:19+00:00",
|
||||||
|
"bin": [
|
||||||
|
"bin/vobject",
|
||||||
|
"bin/generate_vcards"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "4.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\VObject\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Dominik Tobschall",
|
||||||
|
"email": "dominik@fruux.com",
|
||||||
|
"homepage": "http://tobschall.de/",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ivan Enderlin",
|
||||||
|
"email": "ivan.enderlin@hoa-project.net",
|
||||||
|
"homepage": "http://mnt.io/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The VObject library for PHP allows you to easily parse and manipulate iCalendar and vCard objects",
|
||||||
|
"homepage": "http://sabre.io/vobject/",
|
||||||
|
"keywords": [
|
||||||
|
"availability",
|
||||||
|
"freebusy",
|
||||||
|
"iCalendar",
|
||||||
|
"ical",
|
||||||
|
"ics",
|
||||||
|
"jCal",
|
||||||
|
"jCard",
|
||||||
|
"recurrence",
|
||||||
|
"rfc2425",
|
||||||
|
"rfc2426",
|
||||||
|
"rfc2739",
|
||||||
|
"rfc4770",
|
||||||
|
"rfc5545",
|
||||||
|
"rfc5546",
|
||||||
|
"rfc6321",
|
||||||
|
"rfc6350",
|
||||||
|
"rfc6351",
|
||||||
|
"rfc6474",
|
||||||
|
"rfc6638",
|
||||||
|
"rfc6715",
|
||||||
|
"rfc6868",
|
||||||
|
"vCalendar",
|
||||||
|
"vCard",
|
||||||
|
"vcf",
|
||||||
|
"xCal",
|
||||||
|
"xCard"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/vobject/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-vobject"
|
||||||
|
},
|
||||||
|
"install-path": "../sabre/vobject"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sabre/xml",
|
||||||
|
"version": "2.2.11",
|
||||||
|
"version_normalized": "2.2.11.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sabre-io/xml.git",
|
||||||
|
"reference": "01a7927842abf3e10df3d9c2d9b0cc9d813a3fcc"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sabre-io/xml/zipball/01a7927842abf3e10df3d9c2d9b0cc9d813a3fcc",
|
||||||
|
"reference": "01a7927842abf3e10df3d9c2d9b0cc9d813a3fcc",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-xmlreader": "*",
|
||||||
|
"ext-xmlwriter": "*",
|
||||||
|
"lib-libxml": ">=2.6.20",
|
||||||
|
"php": "^7.1 || ^8.0",
|
||||||
|
"sabre/uri": ">=1.0,<3.0.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "~2.17.1||3.63.2",
|
||||||
|
"phpstan/phpstan": "^0.12",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6"
|
||||||
|
},
|
||||||
|
"time": "2024-09-06T07:37:46+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"lib/Deserializer/functions.php",
|
||||||
|
"lib/Serializer/functions.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Sabre\\Xml\\": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage": "http://evertpot.com/",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Markus Staab",
|
||||||
|
"email": "markus.staab@redaxo.de",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "sabre/xml is an XML library that you may not hate.",
|
||||||
|
"homepage": "https://sabre.io/xml/",
|
||||||
|
"keywords": [
|
||||||
|
"XMLReader",
|
||||||
|
"XMLWriter",
|
||||||
|
"dom",
|
||||||
|
"xml"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"forum": "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"issues": "https://github.com/sabre-io/xml/issues",
|
||||||
|
"source": "https://github.com/fruux/sabre-xml"
|
||||||
|
},
|
||||||
|
"install-path": "../sabre/xml"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"dev-package-names": []
|
||||||
|
}
|
||||||
Vendored
+86
@@ -0,0 +1,86 @@
|
|||||||
|
<?php return array(
|
||||||
|
'root' => array(
|
||||||
|
'name' => 'ktrix/service-dav',
|
||||||
|
'pretty_version' => '1.0.0+no-version-set',
|
||||||
|
'version' => '1.0.0.0',
|
||||||
|
'reference' => null,
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev' => true,
|
||||||
|
),
|
||||||
|
'versions' => array(
|
||||||
|
'ktrix/service-dav' => array(
|
||||||
|
'pretty_version' => '1.0.0+no-version-set',
|
||||||
|
'version' => '1.0.0.0',
|
||||||
|
'reference' => null,
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'psr/log' => array(
|
||||||
|
'pretty_version' => '3.0.2',
|
||||||
|
'version' => '3.0.2.0',
|
||||||
|
'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../psr/log',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'sabre/dav' => array(
|
||||||
|
'pretty_version' => '4.7.0',
|
||||||
|
'version' => '4.7.0.0',
|
||||||
|
'reference' => '074373bcd689a30bcf5aaa6bbb20a3395964ce7a',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sabre/dav',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'sabre/event' => array(
|
||||||
|
'pretty_version' => '5.1.7',
|
||||||
|
'version' => '5.1.7.0',
|
||||||
|
'reference' => '86d57e305c272898ba3c28e9bd3d65d5464587c2',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sabre/event',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'sabre/http' => array(
|
||||||
|
'pretty_version' => '5.1.13',
|
||||||
|
'version' => '5.1.13.0',
|
||||||
|
'reference' => '7c2a14097d1a0de2347dcbdc91a02f38e338f4db',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sabre/http',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'sabre/uri' => array(
|
||||||
|
'pretty_version' => '2.3.4',
|
||||||
|
'version' => '2.3.4.0',
|
||||||
|
'reference' => 'b76524c22de90d80ca73143680a8e77b1266c291',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sabre/uri',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'sabre/vobject' => array(
|
||||||
|
'pretty_version' => '4.5.8',
|
||||||
|
'version' => '4.5.8.0',
|
||||||
|
'reference' => 'd554eb24d64232922e1eab5896cc2f84b3b9ffb1',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sabre/vobject',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'sabre/xml' => array(
|
||||||
|
'pretty_version' => '2.2.11',
|
||||||
|
'version' => '2.2.11.0',
|
||||||
|
'reference' => '01a7927842abf3e10df3d9c2d9b0cc9d813a3fcc',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sabre/xml',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// platform_check.php @generated by Composer
|
||||||
|
|
||||||
|
$issues = array();
|
||||||
|
|
||||||
|
if (!(PHP_VERSION_ID >= 80100)) {
|
||||||
|
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.1.0". You are running ' . PHP_VERSION . '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($issues) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trigger_error(
|
||||||
|
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2012 PHP Framework Interoperability Group
|
||||||
|
|
||||||
|
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.
|
||||||
Vendored
+58
@@ -0,0 +1,58 @@
|
|||||||
|
PSR Log
|
||||||
|
=======
|
||||||
|
|
||||||
|
This repository holds all interfaces/classes/traits related to
|
||||||
|
[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md).
|
||||||
|
|
||||||
|
Note that this is not a logger of its own. It is merely an interface that
|
||||||
|
describes a logger. See the specification for more details.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer require psr/log
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
If you need a logger, you can use the interface like this:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
private $logger;
|
||||||
|
|
||||||
|
public function __construct(LoggerInterface $logger = null)
|
||||||
|
{
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doSomething()
|
||||||
|
{
|
||||||
|
if ($this->logger) {
|
||||||
|
$this->logger->info('Doing work');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->doSomethingElse();
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
$this->logger->error('Oh no!', array('exception' => $exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
// do something useful
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then pick one of the implementations of the interface to get a logger.
|
||||||
|
|
||||||
|
If you want to implement the interface, you can require this package and
|
||||||
|
implement `Psr\Log\LoggerInterface` in your code. Please read the
|
||||||
|
[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
|
||||||
|
for details.
|
||||||
Vendored
+26
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "psr/log",
|
||||||
|
"description": "Common interface for logging libraries",
|
||||||
|
"keywords": ["psr", "psr-3", "log"],
|
||||||
|
"homepage": "https://github.com/php-fig/log",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Log\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.x-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a simple Logger implementation that other Loggers can inherit from.
|
||||||
|
*
|
||||||
|
* It simply delegates all log-level-specific methods to the `log` method to
|
||||||
|
* reduce boilerplate code that a simple Logger that does the same thing with
|
||||||
|
* messages regardless of the error level has to implement.
|
||||||
|
*/
|
||||||
|
abstract class AbstractLogger implements LoggerInterface
|
||||||
|
{
|
||||||
|
use LoggerTrait;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
class InvalidArgumentException extends \InvalidArgumentException
|
||||||
|
{
|
||||||
|
}
|
||||||
Vendored
+18
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes log levels.
|
||||||
|
*/
|
||||||
|
class LogLevel
|
||||||
|
{
|
||||||
|
const EMERGENCY = 'emergency';
|
||||||
|
const ALERT = 'alert';
|
||||||
|
const CRITICAL = 'critical';
|
||||||
|
const ERROR = 'error';
|
||||||
|
const WARNING = 'warning';
|
||||||
|
const NOTICE = 'notice';
|
||||||
|
const INFO = 'info';
|
||||||
|
const DEBUG = 'debug';
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a logger-aware instance.
|
||||||
|
*/
|
||||||
|
interface LoggerAwareInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Sets a logger instance on the object.
|
||||||
|
*/
|
||||||
|
public function setLogger(LoggerInterface $logger): void;
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic Implementation of LoggerAwareInterface.
|
||||||
|
*/
|
||||||
|
trait LoggerAwareTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The logger instance.
|
||||||
|
*/
|
||||||
|
protected ?LoggerInterface $logger = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a logger.
|
||||||
|
*/
|
||||||
|
public function setLogger(LoggerInterface $logger): void
|
||||||
|
{
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
}
|
||||||
+98
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a logger instance.
|
||||||
|
*
|
||||||
|
* The message MUST be a string or object implementing __toString().
|
||||||
|
*
|
||||||
|
* The message MAY contain placeholders in the form: {foo} where foo
|
||||||
|
* will be replaced by the context data in key "foo".
|
||||||
|
*
|
||||||
|
* The context array can contain arbitrary data. The only assumption that
|
||||||
|
* can be made by implementors is that if an Exception instance is given
|
||||||
|
* to produce a stack trace, it MUST be in a key named "exception".
|
||||||
|
*
|
||||||
|
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
|
||||||
|
* for the full interface specification.
|
||||||
|
*/
|
||||||
|
interface LoggerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* System is unusable.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function emergency(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action must be taken immediately.
|
||||||
|
*
|
||||||
|
* Example: Entire website down, database unavailable, etc. This should
|
||||||
|
* trigger the SMS alerts and wake you up.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function alert(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Critical conditions.
|
||||||
|
*
|
||||||
|
* Example: Application component unavailable, unexpected exception.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function critical(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime errors that do not require immediate action but should typically
|
||||||
|
* be logged and monitored.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function error(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exceptional occurrences that are not errors.
|
||||||
|
*
|
||||||
|
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||||
|
* that are not necessarily wrong.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function warning(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normal but significant events.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function notice(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interesting events.
|
||||||
|
*
|
||||||
|
* Example: User logs in, SQL logs.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function info(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detailed debug information.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function debug(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs with an arbitrary level.
|
||||||
|
*
|
||||||
|
* @param mixed $level
|
||||||
|
* @param mixed[] $context
|
||||||
|
*
|
||||||
|
* @throws \Psr\Log\InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function log($level, string|\Stringable $message, array $context = []): void;
|
||||||
|
}
|
||||||
+98
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a simple Logger trait that classes unable to extend AbstractLogger
|
||||||
|
* (because they extend another class, etc) can include.
|
||||||
|
*
|
||||||
|
* It simply delegates all log-level-specific methods to the `log` method to
|
||||||
|
* reduce boilerplate code that a simple Logger that does the same thing with
|
||||||
|
* messages regardless of the error level has to implement.
|
||||||
|
*/
|
||||||
|
trait LoggerTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* System is unusable.
|
||||||
|
*/
|
||||||
|
public function emergency(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::EMERGENCY, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action must be taken immediately.
|
||||||
|
*
|
||||||
|
* Example: Entire website down, database unavailable, etc. This should
|
||||||
|
* trigger the SMS alerts and wake you up.
|
||||||
|
*/
|
||||||
|
public function alert(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::ALERT, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Critical conditions.
|
||||||
|
*
|
||||||
|
* Example: Application component unavailable, unexpected exception.
|
||||||
|
*/
|
||||||
|
public function critical(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::CRITICAL, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime errors that do not require immediate action but should typically
|
||||||
|
* be logged and monitored.
|
||||||
|
*/
|
||||||
|
public function error(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::ERROR, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exceptional occurrences that are not errors.
|
||||||
|
*
|
||||||
|
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||||
|
* that are not necessarily wrong.
|
||||||
|
*/
|
||||||
|
public function warning(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::WARNING, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normal but significant events.
|
||||||
|
*/
|
||||||
|
public function notice(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::NOTICE, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interesting events.
|
||||||
|
*
|
||||||
|
* Example: User logs in, SQL logs.
|
||||||
|
*/
|
||||||
|
public function info(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::INFO, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detailed debug information.
|
||||||
|
*/
|
||||||
|
public function debug(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::DEBUG, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs with an arbitrary level.
|
||||||
|
*
|
||||||
|
* @param mixed $level
|
||||||
|
*
|
||||||
|
* @throws \Psr\Log\InvalidArgumentException
|
||||||
|
*/
|
||||||
|
abstract public function log($level, string|\Stringable $message, array $context = []): void;
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Logger can be used to avoid conditional log calls.
|
||||||
|
*
|
||||||
|
* Logging should always be optional, and if no logger is provided to your
|
||||||
|
* library creating a NullLogger instance to have something to throw logs at
|
||||||
|
* is a good way to avoid littering your code with `if ($this->logger) { }`
|
||||||
|
* blocks.
|
||||||
|
*/
|
||||||
|
class NullLogger extends AbstractLogger
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Logs with an arbitrary level.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*
|
||||||
|
* @throws \Psr\Log\InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function log($level, string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+27
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (C) 2007-2016 fruux GmbH (https://fruux.com/).
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of SabreDAV nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
Vendored
+39
@@ -0,0 +1,39 @@
|
|||||||
|
 sabre/dav
|
||||||
|
=======================================================
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
sabre/dav is the most popular WebDAV framework for PHP. Use it to create WebDAV, CalDAV and CardDAV servers.
|
||||||
|
|
||||||
|
Full documentation can be found on the website:
|
||||||
|
|
||||||
|
http://sabre.io/
|
||||||
|
|
||||||
|
|
||||||
|
Build status
|
||||||
|
------------
|
||||||
|
|
||||||
|
| branch | status | PHP version |
|
||||||
|
|------------|---------------------------------------------------------------------------|--------------------|
|
||||||
|
| master 4.* |  | PHP 7.1 up, 8.0 up |
|
||||||
|
| 3.2 | unmaintained | PHP 5.5 to 7.1 |
|
||||||
|
| 3.1 | unmaintained | PHP 5.5 |
|
||||||
|
| 3.0 | unmaintained | PHP 5.4 |
|
||||||
|
| 2.1 | unmaintained | PHP 5.4 |
|
||||||
|
| 2.0 | unmaintained | PHP 5.4 |
|
||||||
|
| 1.8 | unmaintained | PHP 5.3 |
|
||||||
|
| 1.7 | unmaintained | PHP 5.3 |
|
||||||
|
| 1.6 | unmaintained | PHP 5.3 |
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
* [Introduction](http://sabre.io/dav/).
|
||||||
|
* [Installation](http://sabre.io/dav/install/).
|
||||||
|
|
||||||
|
|
||||||
|
Made at fruux
|
||||||
|
-------------
|
||||||
|
|
||||||
|
SabreDAV is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support.
|
||||||
+169
@@ -0,0 +1,169 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$tasks = [
|
||||||
|
'buildzip' => [
|
||||||
|
'init', 'test', 'clean',
|
||||||
|
],
|
||||||
|
'markrelease' => [
|
||||||
|
'init', 'test', 'clean',
|
||||||
|
],
|
||||||
|
'clean' => [],
|
||||||
|
'test' => [
|
||||||
|
'composerupdate',
|
||||||
|
],
|
||||||
|
'init' => [],
|
||||||
|
'composerupdate' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$default = 'buildzip';
|
||||||
|
|
||||||
|
$baseDir = __DIR__.'/../';
|
||||||
|
chdir($baseDir);
|
||||||
|
|
||||||
|
$currentTask = $default;
|
||||||
|
if ($argc > 1) {
|
||||||
|
$currentTask = $argv[1];
|
||||||
|
}
|
||||||
|
$version = null;
|
||||||
|
if ($argc > 2) {
|
||||||
|
$version = $argv[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($tasks[$currentTask])) {
|
||||||
|
echo 'Task not found: ', $currentTask, "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating the dependency graph
|
||||||
|
$newTaskList = [];
|
||||||
|
$oldTaskList = [$currentTask => true];
|
||||||
|
|
||||||
|
while (count($oldTaskList) > 0) {
|
||||||
|
foreach ($oldTaskList as $task => $foo) {
|
||||||
|
if (!isset($tasks[$task])) {
|
||||||
|
echo 'Dependency not found: '.$task, "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
$dependencies = $tasks[$task];
|
||||||
|
|
||||||
|
$fullFilled = true;
|
||||||
|
foreach ($dependencies as $dependency) {
|
||||||
|
if (isset($newTaskList[$dependency])) {
|
||||||
|
// Already in the fulfilled task list.
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$oldTaskList[$dependency] = true;
|
||||||
|
$fullFilled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($fullFilled) {
|
||||||
|
unset($oldTaskList[$task]);
|
||||||
|
$newTaskList[$task] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array_keys($newTaskList) as $task) {
|
||||||
|
echo 'task: '.$task, "\n";
|
||||||
|
call_user_func($task);
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
function init()
|
||||||
|
{
|
||||||
|
global $version;
|
||||||
|
if (!$version) {
|
||||||
|
include __DIR__.'/../vendor/autoload.php';
|
||||||
|
$version = Sabre\DAV\Version::VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ' Building sabre/dav '.$version, "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
function clean()
|
||||||
|
{
|
||||||
|
global $baseDir;
|
||||||
|
echo " Removing build files\n";
|
||||||
|
$outputDir = $baseDir.'/build/SabreDAV';
|
||||||
|
if (is_dir($outputDir)) {
|
||||||
|
system('rm -r '.$baseDir.'/build/SabreDAV');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function composerupdate()
|
||||||
|
{
|
||||||
|
global $baseDir;
|
||||||
|
echo " Updating composer packages to latest version\n\n";
|
||||||
|
system('cd '.$baseDir.'; composer update');
|
||||||
|
}
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
global $baseDir;
|
||||||
|
|
||||||
|
echo " Running all unittests.\n";
|
||||||
|
echo " This may take a while.\n\n";
|
||||||
|
system(__DIR__.'/phpunit --configuration '.$baseDir.'/tests/phpunit.xml.dist --stop-on-failure', $code);
|
||||||
|
if (0 != $code) {
|
||||||
|
echo "PHPUnit reported error code $code\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildzip()
|
||||||
|
{
|
||||||
|
global $baseDir, $version;
|
||||||
|
echo " Generating composer.json\n";
|
||||||
|
|
||||||
|
$input = json_decode(file_get_contents(__DIR__.'/../composer.json'), true);
|
||||||
|
$newComposer = [
|
||||||
|
'require' => $input['require'],
|
||||||
|
'config' => [
|
||||||
|
'bin-dir' => './bin',
|
||||||
|
],
|
||||||
|
'prefer-stable' => true,
|
||||||
|
'minimum-stability' => 'alpha',
|
||||||
|
];
|
||||||
|
unset(
|
||||||
|
$newComposer['require']['sabre/vobject'],
|
||||||
|
$newComposer['require']['sabre/http'],
|
||||||
|
$newComposer['require']['sabre/uri'],
|
||||||
|
$newComposer['require']['sabre/event']
|
||||||
|
);
|
||||||
|
$newComposer['require']['sabre/dav'] = $version;
|
||||||
|
mkdir('build/SabreDAV');
|
||||||
|
file_put_contents('build/SabreDAV/composer.json', json_encode($newComposer, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
echo " Downloading dependencies\n";
|
||||||
|
system('cd build/SabreDAV; composer install -n', $code);
|
||||||
|
if (0 !== $code) {
|
||||||
|
echo "Composer reported error code $code\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo " Removing pointless files\n";
|
||||||
|
unlink('build/SabreDAV/composer.json');
|
||||||
|
unlink('build/SabreDAV/composer.lock');
|
||||||
|
|
||||||
|
echo " Moving important files to the root of the project\n";
|
||||||
|
|
||||||
|
$fileNames = [
|
||||||
|
'CHANGELOG.md',
|
||||||
|
'LICENSE',
|
||||||
|
'README.md',
|
||||||
|
'examples',
|
||||||
|
];
|
||||||
|
foreach ($fileNames as $fileName) {
|
||||||
|
echo " $fileName\n";
|
||||||
|
rename('build/SabreDAV/vendor/sabre/dav/'.$fileName, 'build/SabreDAV/'.$fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// <zip destfile="build/SabreDAV-${sabredav.version}.zip" basedir="build/SabreDAV" prefix="SabreDAV/" />
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
echo "Zipping the sabredav distribution\n\n";
|
||||||
|
system('cd build; zip -qr sabredav-'.$version.'.zip SabreDAV');
|
||||||
|
|
||||||
|
echo 'Done.';
|
||||||
|
}
|
||||||
+414
@@ -0,0 +1,414 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo "SabreDAV migrate script for version 2.0\n";
|
||||||
|
|
||||||
|
if ($argc < 2) {
|
||||||
|
echo <<<HELLO
|
||||||
|
|
||||||
|
This script help you migrate from a pre-2.0 database to 2.0 and later
|
||||||
|
|
||||||
|
The 'calendars', 'addressbooks' and 'cards' tables will be upgraded, and new
|
||||||
|
tables (calendarchanges, addressbookchanges, propertystorage) will be added.
|
||||||
|
|
||||||
|
If you don't use the default PDO CalDAV or CardDAV backend, it's pointless to
|
||||||
|
run this script.
|
||||||
|
|
||||||
|
Keep in mind that ALTER TABLE commands will be executed. If you have a large
|
||||||
|
dataset this may mean that this process takes a while.
|
||||||
|
|
||||||
|
Lastly: Make a back-up first. This script has been tested, but the amount of
|
||||||
|
potential variants are extremely high, so it's impossible to deal with every
|
||||||
|
possible situation.
|
||||||
|
|
||||||
|
In the worst case, you will lose all your data. This is not an overstatement.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
php {$argv[0]} [pdo-dsn] [username] [password]
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
php {$argv[0]} "mysql:host=localhost;dbname=sabredav" root password
|
||||||
|
php {$argv[0]} sqlite:data/sabredav.db
|
||||||
|
|
||||||
|
HELLO;
|
||||||
|
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's a bunch of places where the autoloader could be, so we'll try all of
|
||||||
|
// them.
|
||||||
|
$paths = [
|
||||||
|
__DIR__.'/../vendor/autoload.php',
|
||||||
|
__DIR__.'/../../../autoload.php',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
if (file_exists($path)) {
|
||||||
|
include $path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dsn = $argv[1];
|
||||||
|
$user = isset($argv[2]) ? $argv[2] : null;
|
||||||
|
$pass = isset($argv[3]) ? $argv[3] : null;
|
||||||
|
|
||||||
|
echo 'Connecting to database: '.$dsn."\n";
|
||||||
|
|
||||||
|
$pdo = new PDO($dsn, $user, $pass);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
echo "Detected MySQL.\n";
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
echo "Detected SQLite.\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
echo 'Error: unsupported driver: '.$driver."\n";
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (['calendar', 'addressbook'] as $itemType) {
|
||||||
|
$tableName = $itemType.'s';
|
||||||
|
$tableNameOld = $tableName.'_old';
|
||||||
|
$changesTable = $itemType.'changes';
|
||||||
|
|
||||||
|
echo "Upgrading '$tableName'\n";
|
||||||
|
|
||||||
|
// The only cross-db way to do this, is to just fetch a single record.
|
||||||
|
$row = $pdo->query("SELECT * FROM $tableName LIMIT 1")->fetch();
|
||||||
|
|
||||||
|
if (!$row) {
|
||||||
|
echo "No records were found in the '$tableName' table.\n";
|
||||||
|
echo "\n";
|
||||||
|
echo "We're going to rename the old table to $tableNameOld (just in case).\n";
|
||||||
|
echo "and re-create the new table.\n";
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec("RENAME TABLE $tableName TO $tableNameOld");
|
||||||
|
switch ($itemType) {
|
||||||
|
case 'calendar':
|
||||||
|
$pdo->exec("
|
||||||
|
CREATE TABLE calendars (
|
||||||
|
id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
principaluri VARCHAR(100),
|
||||||
|
displayname VARCHAR(100),
|
||||||
|
uri VARCHAR(200),
|
||||||
|
synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1',
|
||||||
|
description TEXT,
|
||||||
|
calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
calendarcolor VARCHAR(10),
|
||||||
|
timezone TEXT,
|
||||||
|
components VARCHAR(20),
|
||||||
|
transparent TINYINT(1) NOT NULL DEFAULT '0',
|
||||||
|
UNIQUE(principaluri, uri)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||||
|
");
|
||||||
|
break;
|
||||||
|
case 'addressbook':
|
||||||
|
$pdo->exec("
|
||||||
|
CREATE TABLE addressbooks (
|
||||||
|
id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
principaluri VARCHAR(255),
|
||||||
|
displayname VARCHAR(255),
|
||||||
|
uri VARCHAR(200),
|
||||||
|
description TEXT,
|
||||||
|
synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1',
|
||||||
|
UNIQUE(principaluri, uri)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||||
|
");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec("ALTER TABLE $tableName RENAME TO $tableNameOld");
|
||||||
|
|
||||||
|
switch ($itemType) {
|
||||||
|
case 'calendar':
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE calendars (
|
||||||
|
id integer primary key asc,
|
||||||
|
principaluri text,
|
||||||
|
displayname text,
|
||||||
|
uri text,
|
||||||
|
synctoken integer,
|
||||||
|
description text,
|
||||||
|
calendarorder integer,
|
||||||
|
calendarcolor text,
|
||||||
|
timezone text,
|
||||||
|
components text,
|
||||||
|
transparent bool
|
||||||
|
);
|
||||||
|
');
|
||||||
|
break;
|
||||||
|
case 'addressbook':
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE addressbooks (
|
||||||
|
id integer primary key asc,
|
||||||
|
principaluri text,
|
||||||
|
displayname text,
|
||||||
|
uri text,
|
||||||
|
description text,
|
||||||
|
synctoken integer
|
||||||
|
);
|
||||||
|
');
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
echo "Creation of 2.0 $tableName table is complete\n";
|
||||||
|
} else {
|
||||||
|
// Checking if there's a synctoken field already.
|
||||||
|
if (array_key_exists('synctoken', $row)) {
|
||||||
|
echo "The 'synctoken' field already exists in the $tableName table.\n";
|
||||||
|
echo "It's likely you already upgraded, so we're simply leaving\n";
|
||||||
|
echo "the $tableName table alone\n";
|
||||||
|
} else {
|
||||||
|
echo "1.8 table schema detected\n";
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec("ALTER TABLE $tableName ADD synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1'");
|
||||||
|
$pdo->exec("ALTER TABLE $tableName DROP ctag");
|
||||||
|
$pdo->exec("UPDATE $tableName SET synctoken = '1'");
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec("ALTER TABLE $tableName ADD synctoken integer");
|
||||||
|
$pdo->exec("UPDATE $tableName SET synctoken = '1'");
|
||||||
|
echo "Note: there's no easy way to remove fields in sqlite.\n";
|
||||||
|
echo "The ctag field is no longer used, but it's kept in place\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Upgraded '$tableName' to 2.0 schema.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo->query("SELECT * FROM $changesTable LIMIT 1");
|
||||||
|
|
||||||
|
echo "'$changesTable' already exists. Assuming that this part of the\n";
|
||||||
|
echo "upgrade was already completed.\n";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "Creating '$changesTable' table.\n";
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec("
|
||||||
|
CREATE TABLE $changesTable (
|
||||||
|
id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
uri VARCHAR(200) NOT NULL,
|
||||||
|
synctoken INT(11) UNSIGNED NOT NULL,
|
||||||
|
{$itemType}id INT(11) UNSIGNED NOT NULL,
|
||||||
|
operation TINYINT(1) NOT NULL,
|
||||||
|
INDEX {$itemType}id_synctoken ({$itemType}id, synctoken)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||||
|
|
||||||
|
");
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec("
|
||||||
|
|
||||||
|
CREATE TABLE $changesTable (
|
||||||
|
id integer primary key asc,
|
||||||
|
uri text,
|
||||||
|
synctoken integer,
|
||||||
|
{$itemType}id integer,
|
||||||
|
operation bool
|
||||||
|
);
|
||||||
|
|
||||||
|
");
|
||||||
|
$pdo->exec("CREATE INDEX {$itemType}id_synctoken ON $changesTable ({$itemType}id, synctoken);");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo->query('SELECT * FROM calendarsubscriptions LIMIT 1');
|
||||||
|
|
||||||
|
echo "'calendarsubscriptions' already exists. Assuming that this part of the\n";
|
||||||
|
echo "upgrade was already completed.\n";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "Creating calendarsubscriptions table.\n";
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec("
|
||||||
|
CREATE TABLE calendarsubscriptions (
|
||||||
|
id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
uri VARCHAR(200) NOT NULL,
|
||||||
|
principaluri VARCHAR(100) NOT NULL,
|
||||||
|
source TEXT,
|
||||||
|
displayname VARCHAR(100),
|
||||||
|
refreshrate VARCHAR(10),
|
||||||
|
calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
calendarcolor VARCHAR(10),
|
||||||
|
striptodos TINYINT(1) NULL,
|
||||||
|
stripalarms TINYINT(1) NULL,
|
||||||
|
stripattachments TINYINT(1) NULL,
|
||||||
|
lastmodified INT(11) UNSIGNED,
|
||||||
|
UNIQUE(principaluri, uri)
|
||||||
|
);
|
||||||
|
");
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec('
|
||||||
|
|
||||||
|
CREATE TABLE calendarsubscriptions (
|
||||||
|
id integer primary key asc,
|
||||||
|
uri text,
|
||||||
|
principaluri text,
|
||||||
|
source text,
|
||||||
|
displayname text,
|
||||||
|
refreshrate text,
|
||||||
|
calendarorder integer,
|
||||||
|
calendarcolor text,
|
||||||
|
striptodos bool,
|
||||||
|
stripalarms bool,
|
||||||
|
stripattachments bool,
|
||||||
|
lastmodified int
|
||||||
|
);
|
||||||
|
');
|
||||||
|
|
||||||
|
$pdo->exec('CREATE INDEX principaluri_uri ON calendarsubscriptions (principaluri, uri);');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo->query('SELECT * FROM propertystorage LIMIT 1');
|
||||||
|
|
||||||
|
echo "'propertystorage' already exists. Assuming that this part of the\n";
|
||||||
|
echo "upgrade was already completed.\n";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "Creating propertystorage table.\n";
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE propertystorage (
|
||||||
|
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
path VARBINARY(1024) NOT NULL,
|
||||||
|
name VARBINARY(100) NOT NULL,
|
||||||
|
value MEDIUMBLOB
|
||||||
|
);
|
||||||
|
');
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE UNIQUE INDEX path_property ON propertystorage (path(600), name(100));
|
||||||
|
');
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE propertystorage (
|
||||||
|
id integer primary key asc,
|
||||||
|
path TEXT,
|
||||||
|
name TEXT,
|
||||||
|
value TEXT
|
||||||
|
);
|
||||||
|
');
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE UNIQUE INDEX path_property ON propertystorage (path, name);
|
||||||
|
');
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Upgrading cards table to 2.0 schema\n";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$create = false;
|
||||||
|
$row = $pdo->query('SELECT * FROM cards LIMIT 1')->fetch();
|
||||||
|
if (!$row) {
|
||||||
|
$random = mt_rand(1000, 9999);
|
||||||
|
echo "There was no data in the cards table, so we're re-creating it\n";
|
||||||
|
echo "The old table will be renamed to cards_old$random, just in case.\n";
|
||||||
|
|
||||||
|
$create = true;
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec("RENAME TABLE cards TO cards_old$random");
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec("ALTER TABLE cards RENAME TO cards_old$random");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "Exception while checking cards table. Assuming that the table does not yet exist.\n";
|
||||||
|
echo 'Debug: ', $e->getMessage(), "\n";
|
||||||
|
$create = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($create) {
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE cards (
|
||||||
|
id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
addressbookid INT(11) UNSIGNED NOT NULL,
|
||||||
|
carddata MEDIUMBLOB,
|
||||||
|
uri VARCHAR(200),
|
||||||
|
lastmodified INT(11) UNSIGNED,
|
||||||
|
etag VARBINARY(32),
|
||||||
|
size INT(11) UNSIGNED NOT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||||
|
|
||||||
|
');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE cards (
|
||||||
|
id integer primary key asc,
|
||||||
|
addressbookid integer,
|
||||||
|
carddata blob,
|
||||||
|
uri text,
|
||||||
|
lastmodified integer,
|
||||||
|
etag text,
|
||||||
|
size integer
|
||||||
|
);
|
||||||
|
');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec('
|
||||||
|
ALTER TABLE cards
|
||||||
|
ADD etag VARBINARY(32),
|
||||||
|
ADD size INT(11) UNSIGNED NOT NULL;
|
||||||
|
');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec('
|
||||||
|
ALTER TABLE cards ADD etag text;
|
||||||
|
ALTER TABLE cards ADD size integer;
|
||||||
|
');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
echo "Reading all old vcards and populating etag and size fields.\n";
|
||||||
|
$result = $pdo->query('SELECT id, carddata FROM cards');
|
||||||
|
$stmt = $pdo->prepare('UPDATE cards SET etag = ?, size = ? WHERE id = ?');
|
||||||
|
while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
|
||||||
|
$stmt->execute([
|
||||||
|
md5($row['carddata']),
|
||||||
|
strlen($row['carddata']),
|
||||||
|
$row['id'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Upgrade to 2.0 schema completed.\n";
|
||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo "SabreDAV migrate script for version 2.1\n";
|
||||||
|
|
||||||
|
if ($argc < 2) {
|
||||||
|
echo <<<HELLO
|
||||||
|
|
||||||
|
This script help you migrate from a pre-2.1 database to 2.1.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
The 'calendarobjects' table will be upgraded.
|
||||||
|
'schedulingobjects' will be created.
|
||||||
|
|
||||||
|
If you don't use the default PDO CalDAV or CardDAV backend, it's pointless to
|
||||||
|
run this script.
|
||||||
|
|
||||||
|
Keep in mind that ALTER TABLE commands will be executed. If you have a large
|
||||||
|
dataset this may mean that this process takes a while.
|
||||||
|
|
||||||
|
Lastly: Make a back-up first. This script has been tested, but the amount of
|
||||||
|
potential variants are extremely high, so it's impossible to deal with every
|
||||||
|
possible situation.
|
||||||
|
|
||||||
|
In the worst case, you will lose all your data. This is not an overstatement.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
php {$argv[0]} [pdo-dsn] [username] [password]
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
php {$argv[0]} "mysql:host=localhost;dbname=sabredav" root password
|
||||||
|
php {$argv[0]} sqlite:data/sabredav.db
|
||||||
|
|
||||||
|
HELLO;
|
||||||
|
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's a bunch of places where the autoloader could be, so we'll try all of
|
||||||
|
// them.
|
||||||
|
$paths = [
|
||||||
|
__DIR__.'/../vendor/autoload.php',
|
||||||
|
__DIR__.'/../../../autoload.php',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
if (file_exists($path)) {
|
||||||
|
include $path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dsn = $argv[1];
|
||||||
|
$user = isset($argv[2]) ? $argv[2] : null;
|
||||||
|
$pass = isset($argv[3]) ? $argv[3] : null;
|
||||||
|
|
||||||
|
echo 'Connecting to database: '.$dsn."\n";
|
||||||
|
|
||||||
|
$pdo = new PDO($dsn, $user, $pass);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
echo "Detected MySQL.\n";
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
echo "Detected SQLite.\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
echo 'Error: unsupported driver: '.$driver."\n";
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Upgrading 'calendarobjects'\n";
|
||||||
|
$addUid = false;
|
||||||
|
try {
|
||||||
|
$result = $pdo->query('SELECT * FROM calendarobjects LIMIT 1');
|
||||||
|
$row = $result->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$row) {
|
||||||
|
echo "No data in table. Going to try to add the uid field anyway.\n";
|
||||||
|
$addUid = true;
|
||||||
|
} elseif (array_key_exists('uid', $row)) {
|
||||||
|
echo "uid field exists. Assuming that this part of the migration has\n";
|
||||||
|
echo "Already been completed.\n";
|
||||||
|
} else {
|
||||||
|
echo "2.0 schema detected.\n";
|
||||||
|
$addUid = true;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "Could not find a calendarobjects table. Skipping this part of the\n";
|
||||||
|
echo "upgrade.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($addUid) {
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec('ALTER TABLE calendarobjects ADD uid VARCHAR(200)');
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec('ALTER TABLE calendarobjects ADD uid TEXT');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $pdo->query('SELECT id, calendardata FROM calendarobjects');
|
||||||
|
$stmt = $pdo->prepare('UPDATE calendarobjects SET uid = ? WHERE id = ?');
|
||||||
|
$counter = 0;
|
||||||
|
|
||||||
|
while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
|
||||||
|
try {
|
||||||
|
$vobj = \Sabre\VObject\Reader::read($row['calendardata']);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
echo "Warning! Item with id $row[id] could not be parsed!\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$uid = null;
|
||||||
|
$item = $vobj->getBaseComponent();
|
||||||
|
if (!isset($item->UID)) {
|
||||||
|
echo "Warning! Item with id $item[id] does NOT have a UID property and this is required.\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$uid = (string) $item->UID;
|
||||||
|
$stmt->execute([$uid, $row['id']]);
|
||||||
|
++$counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Creating 'schedulingobjects'\n";
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects
|
||||||
|
(
|
||||||
|
id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
principaluri VARCHAR(255),
|
||||||
|
calendardata MEDIUMBLOB,
|
||||||
|
uri VARCHAR(200),
|
||||||
|
lastmodified INT(11) UNSIGNED,
|
||||||
|
etag VARCHAR(32),
|
||||||
|
size INT(11) UNSIGNED NOT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||||
|
');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects (
|
||||||
|
id integer primary key asc,
|
||||||
|
principaluri text,
|
||||||
|
calendardata blob,
|
||||||
|
uri text,
|
||||||
|
lastmodified integer,
|
||||||
|
etag text,
|
||||||
|
size integer
|
||||||
|
)
|
||||||
|
');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Done.\n";
|
||||||
|
|
||||||
|
echo "Upgrade to 2.1 schema completed.\n";
|
||||||
+161
@@ -0,0 +1,161 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo "SabreDAV migrate script for version 3.0\n";
|
||||||
|
|
||||||
|
if ($argc < 2) {
|
||||||
|
echo <<<HELLO
|
||||||
|
|
||||||
|
This script help you migrate from a pre-3.0 database to 3.0 and later
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
* The propertystorage table has changed to allow storage of complex
|
||||||
|
properties.
|
||||||
|
* the vcardurl field in the principals table is no more. This was moved to
|
||||||
|
the propertystorage table.
|
||||||
|
|
||||||
|
Keep in mind that ALTER TABLE commands will be executed. If you have a large
|
||||||
|
dataset this may mean that this process takes a while.
|
||||||
|
|
||||||
|
Lastly: Make a back-up first. This script has been tested, but the amount of
|
||||||
|
potential variants are extremely high, so it's impossible to deal with every
|
||||||
|
possible situation.
|
||||||
|
|
||||||
|
In the worst case, you will lose all your data. This is not an overstatement.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
php {$argv[0]} [pdo-dsn] [username] [password]
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
php {$argv[0]} "mysql:host=localhost;dbname=sabredav" root password
|
||||||
|
php {$argv[0]} sqlite:data/sabredav.db
|
||||||
|
|
||||||
|
HELLO;
|
||||||
|
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's a bunch of places where the autoloader could be, so we'll try all of
|
||||||
|
// them.
|
||||||
|
$paths = [
|
||||||
|
__DIR__.'/../vendor/autoload.php',
|
||||||
|
__DIR__.'/../../../autoload.php',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
if (file_exists($path)) {
|
||||||
|
include $path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dsn = $argv[1];
|
||||||
|
$user = isset($argv[2]) ? $argv[2] : null;
|
||||||
|
$pass = isset($argv[3]) ? $argv[3] : null;
|
||||||
|
|
||||||
|
echo 'Connecting to database: '.$dsn."\n";
|
||||||
|
|
||||||
|
$pdo = new PDO($dsn, $user, $pass);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
echo "Detected MySQL.\n";
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
echo "Detected SQLite.\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
echo 'Error: unsupported driver: '.$driver."\n";
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Upgrading 'propertystorage'\n";
|
||||||
|
$addValueType = false;
|
||||||
|
try {
|
||||||
|
$result = $pdo->query('SELECT * FROM propertystorage LIMIT 1');
|
||||||
|
$row = $result->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$row) {
|
||||||
|
echo "No data in table. Going to re-create the table.\n";
|
||||||
|
$random = mt_rand(1000, 9999);
|
||||||
|
echo "Renaming propertystorage -> propertystorage_old$random and creating new table.\n";
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec('RENAME TABLE propertystorage TO propertystorage_old'.$random);
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE propertystorage (
|
||||||
|
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
path VARBINARY(1024) NOT NULL,
|
||||||
|
name VARBINARY(100) NOT NULL,
|
||||||
|
valuetype INT UNSIGNED,
|
||||||
|
value MEDIUMBLOB
|
||||||
|
);
|
||||||
|
');
|
||||||
|
$pdo->exec('CREATE UNIQUE INDEX path_property_'.$random.' ON propertystorage (path(600), name(100));');
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec('ALTER TABLE propertystorage RENAME TO propertystorage_old'.$random);
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE propertystorage (
|
||||||
|
id integer primary key asc,
|
||||||
|
path text,
|
||||||
|
name text,
|
||||||
|
valuetype integer,
|
||||||
|
value blob
|
||||||
|
);');
|
||||||
|
|
||||||
|
$pdo->exec('CREATE UNIQUE INDEX path_property_'.$random.' ON propertystorage (path, name);');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} elseif (array_key_exists('valuetype', $row)) {
|
||||||
|
echo "valuetype field exists. Assuming that this part of the migration has\n";
|
||||||
|
echo "Already been completed.\n";
|
||||||
|
} else {
|
||||||
|
echo "2.1 schema detected. Going to perform upgrade.\n";
|
||||||
|
$addValueType = true;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "Could not find a propertystorage table. Skipping this part of the\n";
|
||||||
|
echo "upgrade.\n";
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($addValueType) {
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec('ALTER TABLE propertystorage ADD valuetype INT UNSIGNED');
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec('ALTER TABLE propertystorage ADD valuetype INT');
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->exec('UPDATE propertystorage SET valuetype = 1 WHERE valuetype IS NULL ');
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Migrating vcardurl\n";
|
||||||
|
|
||||||
|
$result = $pdo->query('SELECT id, uri, vcardurl FROM principals WHERE vcardurl IS NOT NULL');
|
||||||
|
$stmt1 = $pdo->prepare('INSERT INTO propertystorage (path, name, valuetype, value) VALUES (?, ?, 3, ?)');
|
||||||
|
|
||||||
|
while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
|
||||||
|
// Inserting the new record
|
||||||
|
$stmt1->execute([
|
||||||
|
'addressbooks/'.basename($row['uri']),
|
||||||
|
'{http://calendarserver.org/ns/}me-card',
|
||||||
|
serialize(new Sabre\DAV\Xml\Property\Href($row['vcardurl'])),
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo serialize(new Sabre\DAV\Xml\Property\Href($row['vcardurl']));
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Done.\n";
|
||||||
|
echo "Upgrade to 3.0 schema completed.\n";
|
||||||
+258
@@ -0,0 +1,258 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo "SabreDAV migrate script for version 3.2\n";
|
||||||
|
|
||||||
|
if ($argc < 2) {
|
||||||
|
echo <<<HELLO
|
||||||
|
|
||||||
|
This script help you migrate from a 3.1 database to 3.2 and later
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
* Created a new calendarinstances table to support calendar sharing.
|
||||||
|
* Remove a lot of columns from calendars.
|
||||||
|
|
||||||
|
Keep in mind that ALTER TABLE commands will be executed. If you have a large
|
||||||
|
dataset this may mean that this process takes a while.
|
||||||
|
|
||||||
|
Make a back-up first. This script has been tested, but the amount of
|
||||||
|
potential variants are extremely high, so it's impossible to deal with every
|
||||||
|
possible situation.
|
||||||
|
|
||||||
|
In the worst case, you will lose all your data. This is not an overstatement.
|
||||||
|
|
||||||
|
Lastly, if you are upgrading from an older version than 3.1, make sure you run
|
||||||
|
the earlier migration script first. Migration scripts must be ran in order.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
php {$argv[0]} [pdo-dsn] [username] [password]
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
php {$argv[0]} "mysql:host=localhost;dbname=sabredav" root password
|
||||||
|
php {$argv[0]} sqlite:data/sabredav.db
|
||||||
|
|
||||||
|
HELLO;
|
||||||
|
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's a bunch of places where the autoloader could be, so we'll try all of
|
||||||
|
// them.
|
||||||
|
$paths = [
|
||||||
|
__DIR__.'/../vendor/autoload.php',
|
||||||
|
__DIR__.'/../../../autoload.php',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
if (file_exists($path)) {
|
||||||
|
include $path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dsn = $argv[1];
|
||||||
|
$user = isset($argv[2]) ? $argv[2] : null;
|
||||||
|
$pass = isset($argv[3]) ? $argv[3] : null;
|
||||||
|
|
||||||
|
$backupPostfix = time();
|
||||||
|
|
||||||
|
echo 'Connecting to database: '.$dsn."\n";
|
||||||
|
|
||||||
|
$pdo = new PDO($dsn, $user, $pass);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
echo "Detected MySQL.\n";
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
echo "Detected SQLite.\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
echo 'Error: unsupported driver: '.$driver."\n";
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Creating 'calendarinstances'\n";
|
||||||
|
$addValueType = false;
|
||||||
|
try {
|
||||||
|
$result = $pdo->query('SELECT * FROM calendarinstances LIMIT 1');
|
||||||
|
$result->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
echo "calendarinstances exists. Assuming this part of the migration has already been done.\n";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "calendarinstances does not yet exist. Creating table and migrating data.\n";
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec(<<<SQL
|
||||||
|
CREATE TABLE calendarinstances (
|
||||||
|
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
calendarid INTEGER UNSIGNED NOT NULL,
|
||||||
|
principaluri VARBINARY(100),
|
||||||
|
access TINYINT(1) NOT NULL DEFAULT '1' COMMENT '1 = owner, 2 = read, 3 = readwrite',
|
||||||
|
displayname VARCHAR(100),
|
||||||
|
uri VARBINARY(200),
|
||||||
|
description TEXT,
|
||||||
|
calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
calendarcolor VARBINARY(10),
|
||||||
|
timezone TEXT,
|
||||||
|
transparent TINYINT(1) NOT NULL DEFAULT '0',
|
||||||
|
share_href VARBINARY(100),
|
||||||
|
share_displayname VARCHAR(100),
|
||||||
|
share_invitestatus TINYINT(1) NOT NULL DEFAULT '2' COMMENT '1 = noresponse, 2 = accepted, 3 = declined, 4 = invalid',
|
||||||
|
UNIQUE(principaluri, uri),
|
||||||
|
UNIQUE(calendarid, principaluri),
|
||||||
|
UNIQUE(calendarid, share_href)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
SQL
|
||||||
|
);
|
||||||
|
$pdo->exec('
|
||||||
|
INSERT INTO calendarinstances
|
||||||
|
(
|
||||||
|
calendarid,
|
||||||
|
principaluri,
|
||||||
|
access,
|
||||||
|
displayname,
|
||||||
|
uri,
|
||||||
|
description,
|
||||||
|
calendarorder,
|
||||||
|
calendarcolor,
|
||||||
|
transparent
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
principaluri,
|
||||||
|
1,
|
||||||
|
displayname,
|
||||||
|
uri,
|
||||||
|
description,
|
||||||
|
calendarorder,
|
||||||
|
calendarcolor,
|
||||||
|
transparent
|
||||||
|
FROM calendars
|
||||||
|
');
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec(<<<SQL
|
||||||
|
CREATE TABLE calendarinstances (
|
||||||
|
id integer primary key asc NOT NULL,
|
||||||
|
calendarid integer,
|
||||||
|
principaluri text,
|
||||||
|
access integer COMMENT '1 = owner, 2 = read, 3 = readwrite' NOT NULL DEFAULT '1',
|
||||||
|
displayname text,
|
||||||
|
uri text NOT NULL,
|
||||||
|
description text,
|
||||||
|
calendarorder integer,
|
||||||
|
calendarcolor text,
|
||||||
|
timezone text,
|
||||||
|
transparent bool,
|
||||||
|
share_href text,
|
||||||
|
share_displayname text,
|
||||||
|
share_invitestatus integer DEFAULT '2',
|
||||||
|
UNIQUE (principaluri, uri),
|
||||||
|
UNIQUE (calendarid, principaluri),
|
||||||
|
UNIQUE (calendarid, share_href)
|
||||||
|
);
|
||||||
|
SQL
|
||||||
|
);
|
||||||
|
$pdo->exec('
|
||||||
|
INSERT INTO calendarinstances
|
||||||
|
(
|
||||||
|
calendarid,
|
||||||
|
principaluri,
|
||||||
|
access,
|
||||||
|
displayname,
|
||||||
|
uri,
|
||||||
|
description,
|
||||||
|
calendarorder,
|
||||||
|
calendarcolor,
|
||||||
|
transparent
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
principaluri,
|
||||||
|
1,
|
||||||
|
displayname,
|
||||||
|
uri,
|
||||||
|
description,
|
||||||
|
calendarorder,
|
||||||
|
calendarcolor,
|
||||||
|
transparent
|
||||||
|
FROM calendars
|
||||||
|
');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$result = $pdo->query('SELECT * FROM calendars LIMIT 1');
|
||||||
|
$row = $result->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$row) {
|
||||||
|
echo "Source table is empty.\n";
|
||||||
|
$migrateCalendars = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$columnCount = count($row);
|
||||||
|
if (3 === $columnCount) {
|
||||||
|
echo "The calendars table has 3 columns already. Assuming this part of the migration was already done.\n";
|
||||||
|
$migrateCalendars = false;
|
||||||
|
} else {
|
||||||
|
echo 'The calendars table has '.$columnCount." columns.\n";
|
||||||
|
$migrateCalendars = true;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "calendars table does not exist. This is a major problem. Exiting.\n";
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($migrateCalendars) {
|
||||||
|
$calendarBackup = 'calendars_3_1_'.$backupPostfix;
|
||||||
|
echo "Backing up 'calendars' to '", $calendarBackup, "'\n";
|
||||||
|
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec('RENAME TABLE calendars TO '.$calendarBackup);
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec('ALTER TABLE calendars RENAME TO '.$calendarBackup);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Creating new calendars table.\n";
|
||||||
|
switch ($driver) {
|
||||||
|
case 'mysql':
|
||||||
|
$pdo->exec(<<<SQL
|
||||||
|
CREATE TABLE calendars (
|
||||||
|
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
synctoken INTEGER UNSIGNED NOT NULL DEFAULT '1',
|
||||||
|
components VARBINARY(21)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||||
|
SQL
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$pdo->exec(<<<SQL
|
||||||
|
CREATE TABLE calendars (
|
||||||
|
id integer primary key asc NOT NULL,
|
||||||
|
synctoken integer DEFAULT 1 NOT NULL,
|
||||||
|
components text NOT NULL
|
||||||
|
);
|
||||||
|
SQL
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Migrating data from old to new table\n";
|
||||||
|
|
||||||
|
$pdo->exec(<<<SQL
|
||||||
|
INSERT INTO calendars (id, synctoken, components) SELECT id, synctoken, COALESCE(components,"VEVENT,VTODO,VJOURNAL") as components FROM $calendarBackup
|
||||||
|
SQL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Upgrade to 3.2 schema completed.\n";
|
||||||
+140
@@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2009-2010 Evert Pot
|
||||||
|
# All rights reserved.
|
||||||
|
# http://www.rooftopsolutions.nl/
|
||||||
|
#
|
||||||
|
# This utility is distributed along with SabreDAV
|
||||||
|
# license: http://sabre.io/license/ Modified BSD License
|
||||||
|
|
||||||
|
import os
|
||||||
|
from optparse import OptionParser
|
||||||
|
import time
|
||||||
|
|
||||||
|
def getfreespace(path):
|
||||||
|
stat = os.statvfs(path)
|
||||||
|
return stat.f_frsize * stat.f_bavail
|
||||||
|
|
||||||
|
def getbytesleft(path,threshold):
|
||||||
|
return getfreespace(path)-threshold
|
||||||
|
|
||||||
|
def run(cacheDir, threshold, sleep=5, simulate=False, min_erase = 0):
|
||||||
|
|
||||||
|
bytes = getbytesleft(cacheDir,threshold)
|
||||||
|
if (bytes>0):
|
||||||
|
print "Bytes to go before we hit threshold:", bytes
|
||||||
|
else:
|
||||||
|
print "Threshold exceeded with:", -bytes, "bytes"
|
||||||
|
dir = os.listdir(cacheDir)
|
||||||
|
dir2 = []
|
||||||
|
for file in dir:
|
||||||
|
path = cacheDir + '/' + file
|
||||||
|
dir2.append({
|
||||||
|
"path" : path,
|
||||||
|
"atime": os.stat(path).st_atime,
|
||||||
|
"size" : os.stat(path).st_size
|
||||||
|
})
|
||||||
|
|
||||||
|
dir2.sort(lambda x,y: int(x["atime"]-y["atime"]))
|
||||||
|
|
||||||
|
filesunlinked = 0
|
||||||
|
gainedspace = 0
|
||||||
|
|
||||||
|
# Left is the amount of bytes that need to be freed up
|
||||||
|
# The default is the 'min_erase setting'
|
||||||
|
left = min_erase
|
||||||
|
|
||||||
|
# If the min_erase setting is lower than the amount of bytes over
|
||||||
|
# the threshold, we use that number instead.
|
||||||
|
if left < -bytes :
|
||||||
|
left = -bytes
|
||||||
|
|
||||||
|
print "Need to delete at least:", left;
|
||||||
|
|
||||||
|
for file in dir2:
|
||||||
|
|
||||||
|
# Only deleting files if we're not simulating
|
||||||
|
if not simulate: os.unlink(file["path"])
|
||||||
|
left = int(left - file["size"])
|
||||||
|
gainedspace = gainedspace + file["size"]
|
||||||
|
filesunlinked = filesunlinked + 1
|
||||||
|
|
||||||
|
if(left<0):
|
||||||
|
break
|
||||||
|
|
||||||
|
print "%d files deleted (%d bytes)" % (filesunlinked, gainedspace)
|
||||||
|
|
||||||
|
|
||||||
|
time.sleep(sleep)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = OptionParser(
|
||||||
|
version="naturalselection v0.3",
|
||||||
|
description="Cache directory manager. Deletes cache entries based on accesstime and free space thresholds.\n" +
|
||||||
|
"This utility is distributed alongside SabreDAV.",
|
||||||
|
usage="usage: %prog [options] cacheDirectory",
|
||||||
|
)
|
||||||
|
parser.add_option(
|
||||||
|
'-s',
|
||||||
|
dest="simulate",
|
||||||
|
action="store_true",
|
||||||
|
help="Don't actually make changes, but just simulate the behaviour",
|
||||||
|
)
|
||||||
|
parser.add_option(
|
||||||
|
'-r','--runs',
|
||||||
|
help="How many times to check before exiting. -1 is infinite, which is the default",
|
||||||
|
type="int",
|
||||||
|
dest="runs",
|
||||||
|
default=-1
|
||||||
|
)
|
||||||
|
parser.add_option(
|
||||||
|
'-n','--interval',
|
||||||
|
help="Sleep time in seconds (default = 5)",
|
||||||
|
type="int",
|
||||||
|
dest="sleep",
|
||||||
|
default=5
|
||||||
|
)
|
||||||
|
parser.add_option(
|
||||||
|
'-l','--threshold',
|
||||||
|
help="Threshold in bytes (default = 10737418240, which is 10GB)",
|
||||||
|
type="int",
|
||||||
|
dest="threshold",
|
||||||
|
default=10737418240
|
||||||
|
)
|
||||||
|
parser.add_option(
|
||||||
|
'-m', '--min-erase',
|
||||||
|
help="Minimum number of bytes to erase when the threshold is reached. " +
|
||||||
|
"Setting this option higher will reduce the number of times the cache directory will need to be scanned. " +
|
||||||
|
"(the default is 1073741824, which is 1GB.)",
|
||||||
|
type="int",
|
||||||
|
dest="min_erase",
|
||||||
|
default=1073741824
|
||||||
|
)
|
||||||
|
|
||||||
|
options,args = parser.parse_args()
|
||||||
|
if len(args)<1:
|
||||||
|
parser.error("This utility requires at least 1 argument")
|
||||||
|
cacheDir = args[0]
|
||||||
|
|
||||||
|
print "Natural Selection"
|
||||||
|
print "Cache directory:", cacheDir
|
||||||
|
free = getfreespace(cacheDir);
|
||||||
|
print "Current free disk space:", free
|
||||||
|
|
||||||
|
runs = options.runs;
|
||||||
|
while runs!=0 :
|
||||||
|
run(
|
||||||
|
cacheDir,
|
||||||
|
sleep=options.sleep,
|
||||||
|
simulate=options.simulate,
|
||||||
|
threshold=options.threshold,
|
||||||
|
min_erase=options.min_erase
|
||||||
|
)
|
||||||
|
if runs>0:
|
||||||
|
runs = runs - 1
|
||||||
|
|
||||||
|
if __name__ == '__main__' :
|
||||||
|
main()
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
php -S 0.0.0.0:8080 `dirname $0`/sabredav.php
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// SabreDAV test server.
|
||||||
|
|
||||||
|
class CliLog
|
||||||
|
{
|
||||||
|
protected $stream;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->stream = fopen('php://stdout', 'w');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function log($msg)
|
||||||
|
{
|
||||||
|
fwrite($this->stream, $msg."\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$log = new CliLog();
|
||||||
|
|
||||||
|
if ('cli-server' !== php_sapi_name()) {
|
||||||
|
exit('This script is intended to run on the built-in php webserver');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finding composer
|
||||||
|
|
||||||
|
$paths = [
|
||||||
|
__DIR__.'/../vendor/autoload.php',
|
||||||
|
__DIR__.'/../../../autoload.php',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
if (file_exists($path)) {
|
||||||
|
include $path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use Sabre\DAV;
|
||||||
|
|
||||||
|
// Root
|
||||||
|
$root = new DAV\FS\Directory(getcwd());
|
||||||
|
|
||||||
|
// Setting up server.
|
||||||
|
$server = new DAV\Server($root);
|
||||||
|
|
||||||
|
// Browser plugin
|
||||||
|
$server->addPlugin(new DAV\Browser\Plugin());
|
||||||
|
|
||||||
|
$server->exec();
|
||||||
Vendored
+81
@@ -0,0 +1,81 @@
|
|||||||
|
{
|
||||||
|
"name": "sabre/dav",
|
||||||
|
"type": "library",
|
||||||
|
"description": "WebDAV Framework for PHP",
|
||||||
|
"keywords": ["Framework", "WebDAV", "CalDAV", "CardDAV", "iCalendar"],
|
||||||
|
"homepage": "http://sabre.io/",
|
||||||
|
"license" : "BSD-3-Clause",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Evert Pot",
|
||||||
|
"email": "me@evertpot.com",
|
||||||
|
"homepage" : "http://evertpot.com/",
|
||||||
|
"role" : "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1.0 || ^8.0",
|
||||||
|
"sabre/vobject": "^4.2.1",
|
||||||
|
"sabre/event" : "^5.0",
|
||||||
|
"sabre/xml" : "^2.0.1",
|
||||||
|
"sabre/http" : "^5.0.5",
|
||||||
|
"sabre/uri" : "^2.0",
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-pcre": "*",
|
||||||
|
"ext-spl": "*",
|
||||||
|
"ext-simplexml": "*",
|
||||||
|
"ext-mbstring" : "*",
|
||||||
|
"ext-ctype" : "*",
|
||||||
|
"ext-date" : "*",
|
||||||
|
"ext-iconv" : "*",
|
||||||
|
"lib-libxml" : ">=2.7.0",
|
||||||
|
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||||
|
"ext-json": "*"
|
||||||
|
},
|
||||||
|
"require-dev" : {
|
||||||
|
"friendsofphp/php-cs-fixer": "^2.19",
|
||||||
|
"monolog/monolog": "^1.27 || ^2.0",
|
||||||
|
"phpstan/phpstan": "^0.12 || ^1.0",
|
||||||
|
"phpstan/phpstan-phpunit": "^1.0",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6"
|
||||||
|
},
|
||||||
|
"suggest" : {
|
||||||
|
"ext-curl" : "*",
|
||||||
|
"ext-pdo" : "*",
|
||||||
|
"ext-imap": "*"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4" : {
|
||||||
|
"Sabre\\" : "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev" : {
|
||||||
|
"psr-4" : {
|
||||||
|
"Sabre\\" : "tests/Sabre/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"support" : {
|
||||||
|
"forum" : "https://groups.google.com/group/sabredav-discuss",
|
||||||
|
"source" : "https://github.com/fruux/sabre-dav"
|
||||||
|
},
|
||||||
|
"bin" : [
|
||||||
|
"bin/sabredav",
|
||||||
|
"bin/naturalselection"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"phpstan": [
|
||||||
|
"phpstan analyse lib tests"
|
||||||
|
],
|
||||||
|
"cs-fixer": [
|
||||||
|
"php-cs-fixer fix"
|
||||||
|
],
|
||||||
|
"phpunit": [
|
||||||
|
"phpunit --configuration tests/phpunit.xml"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"composer phpstan",
|
||||||
|
"composer cs-fixer",
|
||||||
|
"composer phpunit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Backend;
|
||||||
|
|
||||||
|
use Sabre\CalDAV;
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract Calendaring backend. Extend this class to create your own backends.
|
||||||
|
*
|
||||||
|
* Checkout the BackendInterface for all the methods that must be implemented.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
abstract class AbstractBackend implements BackendInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Updates properties for a calendar.
|
||||||
|
*
|
||||||
|
* The list of mutations is stored in a Sabre\DAV\PropPatch object.
|
||||||
|
* To do the actual updates, you must tell this object which properties
|
||||||
|
* you're going to process with the handle() method.
|
||||||
|
*
|
||||||
|
* Calling the handle method is like telling the PropPatch object "I
|
||||||
|
* promise I can handle updating this property".
|
||||||
|
*
|
||||||
|
* Read the PropPatch documentation for more info and examples.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
*/
|
||||||
|
public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of calendar objects.
|
||||||
|
*
|
||||||
|
* This method should work identical to getCalendarObject, but instead
|
||||||
|
* return all the calendar objects in the list as an array.
|
||||||
|
*
|
||||||
|
* If the backend supports this, it may allow for some speed-ups.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getMultipleCalendarObjects($calendarId, array $uris)
|
||||||
|
{
|
||||||
|
return array_map(function ($uri) use ($calendarId) {
|
||||||
|
return $this->getCalendarObject($calendarId, $uri);
|
||||||
|
}, $uris);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a calendar-query on the contents of this calendar.
|
||||||
|
*
|
||||||
|
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||||
|
* calendar-query it is possible for a client to request a specific set of
|
||||||
|
* object, based on contents of iCalendar properties, date-ranges and
|
||||||
|
* iCalendar component types (VTODO, VEVENT).
|
||||||
|
*
|
||||||
|
* This method should just return a list of (relative) urls that match this
|
||||||
|
* query.
|
||||||
|
*
|
||||||
|
* The list of filters are specified as an array. The exact array is
|
||||||
|
* documented by \Sabre\CalDAV\CalendarQueryParser.
|
||||||
|
*
|
||||||
|
* Note that it is extremely likely that getCalendarObject for every path
|
||||||
|
* returned from this method will be called almost immediately after. You
|
||||||
|
* may want to anticipate this to speed up these requests.
|
||||||
|
*
|
||||||
|
* This method provides a default implementation, which parses *all* the
|
||||||
|
* iCalendar objects in the specified calendar.
|
||||||
|
*
|
||||||
|
* This default may well be good enough for personal use, and calendars
|
||||||
|
* that aren't very large. But if you anticipate high usage, big calendars
|
||||||
|
* or high loads, you are strongly advised to optimize certain paths.
|
||||||
|
*
|
||||||
|
* The best way to do so is override this method and to optimize
|
||||||
|
* specifically for 'common filters'.
|
||||||
|
*
|
||||||
|
* Requests that are extremely common are:
|
||||||
|
* * requests for just VEVENTS
|
||||||
|
* * requests for just VTODO
|
||||||
|
* * requests with a time-range-filter on either VEVENT or VTODO.
|
||||||
|
*
|
||||||
|
* ..and combinations of these requests. It may not be worth it to try to
|
||||||
|
* handle every possible situation and just rely on the (relatively
|
||||||
|
* easy to use) CalendarQueryValidator to handle the rest.
|
||||||
|
*
|
||||||
|
* Note that especially time-range-filters may be difficult to parse. A
|
||||||
|
* time-range filter specified on a VEVENT must for instance also handle
|
||||||
|
* recurrence rules correctly.
|
||||||
|
* A good example of how to interpret all these filters can also simply
|
||||||
|
* be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
|
||||||
|
* as possible, so it gives you a good idea on what type of stuff you need
|
||||||
|
* to think of.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function calendarQuery($calendarId, array $filters)
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
$objects = $this->getCalendarObjects($calendarId);
|
||||||
|
|
||||||
|
foreach ($objects as $object) {
|
||||||
|
if ($this->validateFilterForObject($object, $filters)) {
|
||||||
|
$result[] = $object['uri'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method validates if a filter (as passed to calendarQuery) matches
|
||||||
|
* the given object.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validateFilterForObject(array $object, array $filters)
|
||||||
|
{
|
||||||
|
// Unfortunately, setting the 'calendardata' here is optional. If
|
||||||
|
// it was excluded, we actually need another call to get this as
|
||||||
|
// well.
|
||||||
|
if (!isset($object['calendardata'])) {
|
||||||
|
$object = $this->getCalendarObject($object['calendarid'], $object['uri']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$vObject = VObject\Reader::read($object['calendardata']);
|
||||||
|
|
||||||
|
$validator = new CalDAV\CalendarQueryValidator();
|
||||||
|
$result = $validator->validate($vObject, $filters);
|
||||||
|
|
||||||
|
// Destroy circular references so PHP will GC the object.
|
||||||
|
$vObject->destroy();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches through all of a users calendars and calendar objects to find
|
||||||
|
* an object with a specific UID.
|
||||||
|
*
|
||||||
|
* This method should return the path to this object, relative to the
|
||||||
|
* calendar home, so this path usually only contains two parts:
|
||||||
|
*
|
||||||
|
* calendarpath/objectpath.ics
|
||||||
|
*
|
||||||
|
* If the uid is not found, return null.
|
||||||
|
*
|
||||||
|
* This method should only consider * objects that the principal owns, so
|
||||||
|
* any calendars owned by other principals that also appear in this
|
||||||
|
* collection should be ignored.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @param string $uid
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getCalendarObjectByUID($principalUri, $uid)
|
||||||
|
{
|
||||||
|
// Note: this is a super slow naive implementation of this method. You
|
||||||
|
// are highly recommended to optimize it, if your backend allows it.
|
||||||
|
foreach ($this->getCalendarsForUser($principalUri) as $calendar) {
|
||||||
|
// We must ignore calendars owned by other principals.
|
||||||
|
if ($calendar['principaluri'] !== $principalUri) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore calendars that are shared.
|
||||||
|
if (isset($calendar['{http://sabredav.org/ns}owner-principal']) && $calendar['{http://sabredav.org/ns}owner-principal'] !== $principalUri) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = $this->calendarQuery(
|
||||||
|
$calendar['id'],
|
||||||
|
[
|
||||||
|
'name' => 'VCALENDAR',
|
||||||
|
'prop-filters' => [],
|
||||||
|
'comp-filters' => [
|
||||||
|
[
|
||||||
|
'name' => 'VEVENT',
|
||||||
|
'is-not-defined' => false,
|
||||||
|
'time-range' => null,
|
||||||
|
'comp-filters' => [],
|
||||||
|
'prop-filters' => [
|
||||||
|
[
|
||||||
|
'name' => 'UID',
|
||||||
|
'is-not-defined' => false,
|
||||||
|
'time-range' => null,
|
||||||
|
'text-match' => [
|
||||||
|
'value' => $uid,
|
||||||
|
'negate-condition' => false,
|
||||||
|
'collation' => 'i;octet',
|
||||||
|
],
|
||||||
|
'param-filters' => [],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
if ($results) {
|
||||||
|
// We have a match
|
||||||
|
return $calendar['uri'].'/'.$results[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,273 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every CalDAV backend must at least implement this interface.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface BackendInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns a list of calendars for a principal.
|
||||||
|
*
|
||||||
|
* Every project is an array with the following keys:
|
||||||
|
* * id, a unique id that will be used by other functions to modify the
|
||||||
|
* calendar. This can be the same as the uri or a database key.
|
||||||
|
* * uri, which is the basename of the uri with which the calendar is
|
||||||
|
* accessed.
|
||||||
|
* * principaluri. The owner of the calendar. Almost always the same as
|
||||||
|
* principalUri passed to this method.
|
||||||
|
*
|
||||||
|
* Furthermore it can contain webdav properties in clark notation. A very
|
||||||
|
* common one is '{DAV:}displayname'.
|
||||||
|
*
|
||||||
|
* Many clients also require:
|
||||||
|
* {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
|
||||||
|
* For this property, you can just return an instance of
|
||||||
|
* Sabre\CalDAV\Property\SupportedCalendarComponentSet.
|
||||||
|
*
|
||||||
|
* If you return {http://sabredav.org/ns}read-only and set the value to 1,
|
||||||
|
* ACL will automatically be put in read-only mode.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCalendarsForUser($principalUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new calendar for a principal.
|
||||||
|
*
|
||||||
|
* If the creation was a success, an id must be returned that can be used to
|
||||||
|
* reference this calendar in other methods, such as updateCalendar.
|
||||||
|
*
|
||||||
|
* The id can be any type, including ints, strings, objects or array.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @param string $calendarUri
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function createCalendar($principalUri, $calendarUri, array $properties);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates properties for a calendar.
|
||||||
|
*
|
||||||
|
* The list of mutations is stored in a Sabre\DAV\PropPatch object.
|
||||||
|
* To do the actual updates, you must tell this object which properties
|
||||||
|
* you're going to process with the handle() method.
|
||||||
|
*
|
||||||
|
* Calling the handle method is like telling the PropPatch object "I
|
||||||
|
* promise I can handle updating this property".
|
||||||
|
*
|
||||||
|
* Read the PropPatch documentation for more info and examples.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
*/
|
||||||
|
public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a calendar and all its objects.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
*/
|
||||||
|
public function deleteCalendar($calendarId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all calendar objects within a calendar.
|
||||||
|
*
|
||||||
|
* Every item contains an array with the following keys:
|
||||||
|
* * calendardata - The iCalendar-compatible calendar data
|
||||||
|
* * uri - a unique key which will be used to construct the uri. This can
|
||||||
|
* be any arbitrary string, but making sure it ends with '.ics' is a
|
||||||
|
* good idea. This is only the basename, or filename, not the full
|
||||||
|
* path.
|
||||||
|
* * lastmodified - a timestamp of the last modification time
|
||||||
|
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
|
||||||
|
* '"abcdef"')
|
||||||
|
* * size - The size of the calendar objects, in bytes.
|
||||||
|
* * component - optional, a string containing the type of object, such
|
||||||
|
* as 'vevent' or 'vtodo'. If specified, this will be used to populate
|
||||||
|
* the Content-Type header.
|
||||||
|
*
|
||||||
|
* Note that the etag is optional, but it's highly encouraged to return for
|
||||||
|
* speed reasons.
|
||||||
|
*
|
||||||
|
* The calendardata is also optional. If it's not returned
|
||||||
|
* 'getCalendarObject' will be called later, which *is* expected to return
|
||||||
|
* calendardata.
|
||||||
|
*
|
||||||
|
* If neither etag or size are specified, the calendardata will be
|
||||||
|
* used/fetched to determine these numbers. If both are specified the
|
||||||
|
* amount of times this is needed is reduced by a great degree.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCalendarObjects($calendarId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns information from a single calendar object, based on it's object
|
||||||
|
* uri.
|
||||||
|
*
|
||||||
|
* The object uri is only the basename, or filename and not a full path.
|
||||||
|
*
|
||||||
|
* The returned array must have the same keys as getCalendarObjects. The
|
||||||
|
* 'calendardata' object is required here though, while it's not required
|
||||||
|
* for getCalendarObjects.
|
||||||
|
*
|
||||||
|
* This method must return null if the object did not exist.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getCalendarObject($calendarId, $objectUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of calendar objects.
|
||||||
|
*
|
||||||
|
* This method should work identical to getCalendarObject, but instead
|
||||||
|
* return all the calendar objects in the list as an array.
|
||||||
|
*
|
||||||
|
* If the backend supports this, it may allow for some speed-ups.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getMultipleCalendarObjects($calendarId, array $uris);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new calendar object.
|
||||||
|
*
|
||||||
|
* The object uri is only the basename, or filename and not a full path.
|
||||||
|
*
|
||||||
|
* It is possible to return an etag from this function, which will be used
|
||||||
|
* in the response to this PUT request. Note that the ETag must be
|
||||||
|
* surrounded by double-quotes.
|
||||||
|
*
|
||||||
|
* However, you should only really return this ETag if you don't mangle the
|
||||||
|
* calendar-data. If the result of a subsequent GET to this object is not
|
||||||
|
* the exact same as this request body, you should omit the ETag.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
* @param string $calendarData
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function createCalendarObject($calendarId, $objectUri, $calendarData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing calendarobject, based on it's uri.
|
||||||
|
*
|
||||||
|
* The object uri is only the basename, or filename and not a full path.
|
||||||
|
*
|
||||||
|
* It is possible return an etag from this function, which will be used in
|
||||||
|
* the response to this PUT request. Note that the ETag must be surrounded
|
||||||
|
* by double-quotes.
|
||||||
|
*
|
||||||
|
* However, you should only really return this ETag if you don't mangle the
|
||||||
|
* calendar-data. If the result of a subsequent GET to this object is not
|
||||||
|
* the exact same as this request body, you should omit the ETag.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
* @param string $calendarData
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function updateCalendarObject($calendarId, $objectUri, $calendarData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an existing calendar object.
|
||||||
|
*
|
||||||
|
* The object uri is only the basename, or filename and not a full path.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
*/
|
||||||
|
public function deleteCalendarObject($calendarId, $objectUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a calendar-query on the contents of this calendar.
|
||||||
|
*
|
||||||
|
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||||
|
* calendar-query it is possible for a client to request a specific set of
|
||||||
|
* object, based on contents of iCalendar properties, date-ranges and
|
||||||
|
* iCalendar component types (VTODO, VEVENT).
|
||||||
|
*
|
||||||
|
* This method should just return a list of (relative) urls that match this
|
||||||
|
* query.
|
||||||
|
*
|
||||||
|
* The list of filters are specified as an array. The exact array is
|
||||||
|
* documented by Sabre\CalDAV\CalendarQueryParser.
|
||||||
|
*
|
||||||
|
* Note that it is extremely likely that getCalendarObject for every path
|
||||||
|
* returned from this method will be called almost immediately after. You
|
||||||
|
* may want to anticipate this to speed up these requests.
|
||||||
|
*
|
||||||
|
* This method provides a default implementation, which parses *all* the
|
||||||
|
* iCalendar objects in the specified calendar.
|
||||||
|
*
|
||||||
|
* This default may well be good enough for personal use, and calendars
|
||||||
|
* that aren't very large. But if you anticipate high usage, big calendars
|
||||||
|
* or high loads, you are strongly advised to optimize certain paths.
|
||||||
|
*
|
||||||
|
* The best way to do so is override this method and to optimize
|
||||||
|
* specifically for 'common filters'.
|
||||||
|
*
|
||||||
|
* Requests that are extremely common are:
|
||||||
|
* * requests for just VEVENTS
|
||||||
|
* * requests for just VTODO
|
||||||
|
* * requests with a time-range-filter on either VEVENT or VTODO.
|
||||||
|
*
|
||||||
|
* ..and combinations of these requests. It may not be worth it to try to
|
||||||
|
* handle every possible situation and just rely on the (relatively
|
||||||
|
* easy to use) CalendarQueryValidator to handle the rest.
|
||||||
|
*
|
||||||
|
* Note that especially time-range-filters may be difficult to parse. A
|
||||||
|
* time-range filter specified on a VEVENT must for instance also handle
|
||||||
|
* recurrence rules correctly.
|
||||||
|
* A good example of how to interprete all these filters can also simply
|
||||||
|
* be found in Sabre\CalDAV\CalendarQueryFilter. This class is as correct
|
||||||
|
* as possible, so it gives you a good idea on what type of stuff you need
|
||||||
|
* to think of.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function calendarQuery($calendarId, array $filters);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches through all of a users calendars and calendar objects to find
|
||||||
|
* an object with a specific UID.
|
||||||
|
*
|
||||||
|
* This method should return the path to this object, relative to the
|
||||||
|
* calendar home, so this path usually only contains two parts:
|
||||||
|
*
|
||||||
|
* calendarpath/objectpath.ics
|
||||||
|
*
|
||||||
|
* If the uid is not found, return null.
|
||||||
|
*
|
||||||
|
* This method should only consider * objects that the principal owns, so
|
||||||
|
* any calendars owned by other principals that also appear in this
|
||||||
|
* collection should be ignored.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @param string $uid
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getCalendarObjectByUID($principalUri, $uid);
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Backend;
|
||||||
|
|
||||||
|
use Sabre\CalDAV\Xml\Notification\NotificationInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds caldav notification support to a backend.
|
||||||
|
*
|
||||||
|
* Note: This feature is experimental, and may change in between different
|
||||||
|
* SabreDAV versions.
|
||||||
|
*
|
||||||
|
* Notifications are defined at:
|
||||||
|
* http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-notifications.txt
|
||||||
|
*
|
||||||
|
* These notifications are basically a list of server-generated notifications
|
||||||
|
* displayed to the user. Users can dismiss notifications by deleting them.
|
||||||
|
*
|
||||||
|
* The primary usecase is to allow for calendar-sharing.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface NotificationSupport extends BackendInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns a list of notifications for a given principal url.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
*
|
||||||
|
* @return NotificationInterface[]
|
||||||
|
*/
|
||||||
|
public function getNotificationsForPrincipal($principalUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This deletes a specific notification.
|
||||||
|
*
|
||||||
|
* This may be called by a client once it deems a notification handled.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
*/
|
||||||
|
public function deleteNotification($principalUri, NotificationInterface $notification);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when a user replied to a request to share.
|
||||||
|
*
|
||||||
|
* If the user chose to accept the share, this method should return the
|
||||||
|
* newly created calendar url.
|
||||||
|
*
|
||||||
|
* @param string $href The sharee who is replying (often a mailto: address)
|
||||||
|
* @param int $status One of the SharingPlugin::STATUS_* constants
|
||||||
|
* @param string $calendarUri The url to the calendar thats being shared
|
||||||
|
* @param string $inReplyTo The unique id this message is a response to
|
||||||
|
* @param string $summary A description of the reply
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null);
|
||||||
|
}
|
||||||
+1487
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementing this interface adds CalDAV Scheduling support to your caldav
|
||||||
|
* server, as defined in rfc6638.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface SchedulingSupport extends BackendInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns a single scheduling object for the inbox collection.
|
||||||
|
*
|
||||||
|
* The returned array should contain the following elements:
|
||||||
|
* * uri - A unique basename for the object. This will be used to
|
||||||
|
* construct a full uri.
|
||||||
|
* * calendardata - The iCalendar object
|
||||||
|
* * lastmodified - The last modification date. Can be an int for a unix
|
||||||
|
* timestamp, or a PHP DateTime object.
|
||||||
|
* * etag - A unique token that must change if the object changed.
|
||||||
|
* * size - The size of the object, in bytes.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @param string $objectUri
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getSchedulingObject($principalUri, $objectUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all scheduling objects for the inbox collection.
|
||||||
|
*
|
||||||
|
* These objects should be returned as an array. Every item in the array
|
||||||
|
* should follow the same structure as returned from getSchedulingObject.
|
||||||
|
*
|
||||||
|
* The main difference is that 'calendardata' is optional.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getSchedulingObjects($principalUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a scheduling object from the inbox collection.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @param string $objectUri
|
||||||
|
*/
|
||||||
|
public function deleteSchedulingObject($principalUri, $objectUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new scheduling object. This should land in a users' inbox.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @param string $objectUri
|
||||||
|
* @param string|resource $objectData
|
||||||
|
*/
|
||||||
|
public function createSchedulingObject($principalUri, $objectUri, $objectData);
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds support for sharing features to a CalDAV server.
|
||||||
|
*
|
||||||
|
* CalDAV backends that implement this interface, must make the following
|
||||||
|
* modifications to getCalendarsForUser:
|
||||||
|
*
|
||||||
|
* 1. Return shared calendars for users.
|
||||||
|
* 2. For every calendar, return calendar-resource-uri. This strings is a URI or
|
||||||
|
* relative URI reference that must be unique for every calendar, but
|
||||||
|
* identical for every instance of the same shared calendar.
|
||||||
|
* 3. For every calendar, you must return a share-access element. This element
|
||||||
|
* should contain one of the Sabre\DAV\Sharing\Plugin:ACCESS_* constants and
|
||||||
|
* indicates the access level the user has.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface SharingSupport extends BackendInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Updates the list of shares.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param \Sabre\DAV\Xml\Element\Sharee[] $sharees
|
||||||
|
*/
|
||||||
|
public function updateInvites($calendarId, array $sharees);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of people whom this calendar is shared with.
|
||||||
|
*
|
||||||
|
* Every item in the returned list must be a Sharee object with at
|
||||||
|
* least the following properties set:
|
||||||
|
* $href
|
||||||
|
* $shareAccess
|
||||||
|
* $inviteStatus
|
||||||
|
*
|
||||||
|
* and optionally:
|
||||||
|
* $properties
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
*
|
||||||
|
* @return \Sabre\DAV\Xml\Element\Sharee[]
|
||||||
|
*/
|
||||||
|
public function getInvites($calendarId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publishes a calendar.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param bool $value
|
||||||
|
*/
|
||||||
|
public function setPublishStatus($calendarId, $value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Backend;
|
||||||
|
|
||||||
|
use Sabre\CalDAV;
|
||||||
|
use Sabre\DAV;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple PDO CalDAV backend.
|
||||||
|
*
|
||||||
|
* This class is basically the most minimum example to get a caldav backend up
|
||||||
|
* and running. This class uses the following schema (MySQL example):
|
||||||
|
*
|
||||||
|
* CREATE TABLE simple_calendars (
|
||||||
|
* id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
* uri VARBINARY(200) NOT NULL,
|
||||||
|
* principaluri VARBINARY(200) NOT NULL
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* CREATE TABLE simple_calendarobjects (
|
||||||
|
* id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
* calendarid INT UNSIGNED NOT NULL,
|
||||||
|
* uri VARBINARY(200) NOT NULL,
|
||||||
|
* calendardata MEDIUMBLOB
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* To make this class work, you absolutely need to have the PropertyStorage
|
||||||
|
* plugin enabled.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/).
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class SimplePDO extends AbstractBackend
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* pdo.
|
||||||
|
*
|
||||||
|
* @var \PDO
|
||||||
|
*/
|
||||||
|
protected $pdo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the backend.
|
||||||
|
*/
|
||||||
|
public function __construct(\PDO $pdo)
|
||||||
|
{
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of calendars for a principal.
|
||||||
|
*
|
||||||
|
* Every project is an array with the following keys:
|
||||||
|
* * id, a unique id that will be used by other functions to modify the
|
||||||
|
* calendar. This can be the same as the uri or a database key.
|
||||||
|
* * uri. This is just the 'base uri' or 'filename' of the calendar.
|
||||||
|
* * principaluri. The owner of the calendar. Almost always the same as
|
||||||
|
* principalUri passed to this method.
|
||||||
|
*
|
||||||
|
* Furthermore it can contain webdav properties in clark notation. A very
|
||||||
|
* common one is '{DAV:}displayname'.
|
||||||
|
*
|
||||||
|
* Many clients also require:
|
||||||
|
* {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
|
||||||
|
* For this property, you can just return an instance of
|
||||||
|
* Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet.
|
||||||
|
*
|
||||||
|
* If you return {http://sabredav.org/ns}read-only and set the value to 1,
|
||||||
|
* ACL will automatically be put in read-only mode.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCalendarsForUser($principalUri)
|
||||||
|
{
|
||||||
|
// Making fields a comma-delimited list
|
||||||
|
$stmt = $this->pdo->prepare('SELECT id, uri FROM simple_calendars WHERE principaluri = ? ORDER BY id ASC');
|
||||||
|
$stmt->execute([$principalUri]);
|
||||||
|
|
||||||
|
$calendars = [];
|
||||||
|
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||||
|
$calendars[] = [
|
||||||
|
'id' => $row['id'],
|
||||||
|
'uri' => $row['uri'],
|
||||||
|
'principaluri' => $principalUri,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $calendars;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new calendar for a principal.
|
||||||
|
*
|
||||||
|
* If the creation was a success, an id must be returned that can be used
|
||||||
|
* to reference this calendar in other methods, such as updateCalendar.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @param string $calendarUri
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function createCalendar($principalUri, $calendarUri, array $properties)
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare('INSERT INTO simple_calendars (principaluri, uri) VALUES (?, ?)');
|
||||||
|
$stmt->execute([$principalUri, $calendarUri]);
|
||||||
|
|
||||||
|
return $this->pdo->lastInsertId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a calendar and all it's objects.
|
||||||
|
*
|
||||||
|
* @param string $calendarId
|
||||||
|
*/
|
||||||
|
public function deleteCalendar($calendarId)
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ?');
|
||||||
|
$stmt->execute([$calendarId]);
|
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare('DELETE FROM simple_calendars WHERE id = ?');
|
||||||
|
$stmt->execute([$calendarId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all calendar objects within a calendar.
|
||||||
|
*
|
||||||
|
* Every item contains an array with the following keys:
|
||||||
|
* * calendardata - The iCalendar-compatible calendar data
|
||||||
|
* * uri - a unique key which will be used to construct the uri. This can
|
||||||
|
* be any arbitrary string, but making sure it ends with '.ics' is a
|
||||||
|
* good idea. This is only the basename, or filename, not the full
|
||||||
|
* path.
|
||||||
|
* * lastmodified - a timestamp of the last modification time
|
||||||
|
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
|
||||||
|
* ' "abcdef"')
|
||||||
|
* * size - The size of the calendar objects, in bytes.
|
||||||
|
* * component - optional, a string containing the type of object, such
|
||||||
|
* as 'vevent' or 'vtodo'. If specified, this will be used to populate
|
||||||
|
* the Content-Type header.
|
||||||
|
*
|
||||||
|
* Note that the etag is optional, but it's highly encouraged to return for
|
||||||
|
* speed reasons.
|
||||||
|
*
|
||||||
|
* The calendardata is also optional. If it's not returned
|
||||||
|
* 'getCalendarObject' will be called later, which *is* expected to return
|
||||||
|
* calendardata.
|
||||||
|
*
|
||||||
|
* If neither etag or size are specified, the calendardata will be
|
||||||
|
* used/fetched to determine these numbers. If both are specified the
|
||||||
|
* amount of times this is needed is reduced by a great degree.
|
||||||
|
*
|
||||||
|
* @param string $calendarId
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCalendarObjects($calendarId)
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ?');
|
||||||
|
$stmt->execute([$calendarId]);
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
|
||||||
|
$result[] = [
|
||||||
|
'id' => $row['id'],
|
||||||
|
'uri' => $row['uri'],
|
||||||
|
'etag' => '"'.md5($row['calendardata']).'"',
|
||||||
|
'calendarid' => $calendarId,
|
||||||
|
'size' => strlen($row['calendardata']),
|
||||||
|
'calendardata' => $row['calendardata'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns information from a single calendar object, based on it's object
|
||||||
|
* uri.
|
||||||
|
*
|
||||||
|
* The object uri is only the basename, or filename and not a full path.
|
||||||
|
*
|
||||||
|
* The returned array must have the same keys as getCalendarObjects. The
|
||||||
|
* 'calendardata' object is required here though, while it's not required
|
||||||
|
* for getCalendarObjects.
|
||||||
|
*
|
||||||
|
* This method must return null if the object did not exist.
|
||||||
|
*
|
||||||
|
* @param string $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getCalendarObject($calendarId, $objectUri)
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?');
|
||||||
|
$stmt->execute([$calendarId, $objectUri]);
|
||||||
|
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$row) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => $row['id'],
|
||||||
|
'uri' => $row['uri'],
|
||||||
|
'etag' => '"'.md5($row['calendardata']).'"',
|
||||||
|
'calendarid' => $calendarId,
|
||||||
|
'size' => strlen($row['calendardata']),
|
||||||
|
'calendardata' => $row['calendardata'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new calendar object.
|
||||||
|
*
|
||||||
|
* The object uri is only the basename, or filename and not a full path.
|
||||||
|
*
|
||||||
|
* It is possible return an etag from this function, which will be used in
|
||||||
|
* the response to this PUT request. Note that the ETag must be surrounded
|
||||||
|
* by double-quotes.
|
||||||
|
*
|
||||||
|
* However, you should only really return this ETag if you don't mangle the
|
||||||
|
* calendar-data. If the result of a subsequent GET to this object is not
|
||||||
|
* the exact same as this request body, you should omit the ETag.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
* @param string $calendarData
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function createCalendarObject($calendarId, $objectUri, $calendarData)
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare('INSERT INTO simple_calendarobjects (calendarid, uri, calendardata) VALUES (?,?,?)');
|
||||||
|
$stmt->execute([
|
||||||
|
$calendarId,
|
||||||
|
$objectUri,
|
||||||
|
$calendarData,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return '"'.md5($calendarData).'"';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing calendarobject, based on it's uri.
|
||||||
|
*
|
||||||
|
* The object uri is only the basename, or filename and not a full path.
|
||||||
|
*
|
||||||
|
* It is possible return an etag from this function, which will be used in
|
||||||
|
* the response to this PUT request. Note that the ETag must be surrounded
|
||||||
|
* by double-quotes.
|
||||||
|
*
|
||||||
|
* However, you should only really return this ETag if you don't mangle the
|
||||||
|
* calendar-data. If the result of a subsequent GET to this object is not
|
||||||
|
* the exact same as this request body, you should omit the ETag.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
* @param string $calendarData
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function updateCalendarObject($calendarId, $objectUri, $calendarData)
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare('UPDATE simple_calendarobjects SET calendardata = ? WHERE calendarid = ? AND uri = ?');
|
||||||
|
$stmt->execute([$calendarData, $calendarId, $objectUri]);
|
||||||
|
|
||||||
|
return '"'.md5($calendarData).'"';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an existing calendar object.
|
||||||
|
*
|
||||||
|
* The object uri is only the basename, or filename and not a full path.
|
||||||
|
*
|
||||||
|
* @param string $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
*/
|
||||||
|
public function deleteCalendarObject($calendarId, $objectUri)
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?');
|
||||||
|
$stmt->execute([$calendarId, $objectUri]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Backend;
|
||||||
|
|
||||||
|
use Sabre\DAV;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every CalDAV backend must at least implement this interface.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface SubscriptionSupport extends BackendInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns a list of subscriptions for a principal.
|
||||||
|
*
|
||||||
|
* Every subscription is an array with the following keys:
|
||||||
|
* * id, a unique id that will be used by other functions to modify the
|
||||||
|
* subscription. This can be the same as the uri or a database key.
|
||||||
|
* * uri. This is just the 'base uri' or 'filename' of the subscription.
|
||||||
|
* * principaluri. The owner of the subscription. Almost always the same as
|
||||||
|
* principalUri passed to this method.
|
||||||
|
*
|
||||||
|
* Furthermore, all the subscription info must be returned too:
|
||||||
|
*
|
||||||
|
* 1. {DAV:}displayname
|
||||||
|
* 2. {http://apple.com/ns/ical/}refreshrate
|
||||||
|
* 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
|
||||||
|
* should not be stripped).
|
||||||
|
* 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
|
||||||
|
* should not be stripped).
|
||||||
|
* 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
|
||||||
|
* attachments should not be stripped).
|
||||||
|
* 6. {http://calendarserver.org/ns/}source (Must be a
|
||||||
|
* Sabre\DAV\Property\Href).
|
||||||
|
* 7. {http://apple.com/ns/ical/}calendar-color
|
||||||
|
* 8. {http://apple.com/ns/ical/}calendar-order
|
||||||
|
* 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
|
||||||
|
* (should just be an instance of
|
||||||
|
* Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
|
||||||
|
* default components).
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getSubscriptionsForUser($principalUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new subscription for a principal.
|
||||||
|
*
|
||||||
|
* If the creation was a success, an id must be returned that can be used to reference
|
||||||
|
* this subscription in other methods, such as updateSubscription.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @param string $uri
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function createSubscription($principalUri, $uri, array $properties);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a subscription.
|
||||||
|
*
|
||||||
|
* The list of mutations is stored in a Sabre\DAV\PropPatch object.
|
||||||
|
* To do the actual updates, you must tell this object which properties
|
||||||
|
* you're going to process with the handle() method.
|
||||||
|
*
|
||||||
|
* Calling the handle method is like telling the PropPatch object "I
|
||||||
|
* promise I can handle updating this property".
|
||||||
|
*
|
||||||
|
* Read the PropPatch documentation for more info and examples.
|
||||||
|
*
|
||||||
|
* @param mixed $subscriptionId
|
||||||
|
* @param \Sabre\DAV\PropPatch $propPatch
|
||||||
|
*/
|
||||||
|
public function updateSubscription($subscriptionId, DAV\PropPatch $propPatch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a subscription.
|
||||||
|
*
|
||||||
|
* @param mixed $subscriptionId
|
||||||
|
*/
|
||||||
|
public function deleteSubscription($subscriptionId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebDAV-sync support for CalDAV backends.
|
||||||
|
*
|
||||||
|
* In order for backends to advertise support for WebDAV-sync, this interface
|
||||||
|
* must be implemented.
|
||||||
|
*
|
||||||
|
* Implementing this can result in a significant reduction of bandwidth and CPU
|
||||||
|
* time.
|
||||||
|
*
|
||||||
|
* For this to work, you _must_ return a {http://sabredav.org/ns}sync-token
|
||||||
|
* property from getCalendarsFromUser.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface SyncSupport extends BackendInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The getChanges method returns all the changes that have happened, since
|
||||||
|
* the specified syncToken in the specified calendar.
|
||||||
|
*
|
||||||
|
* This function should return an array, such as the following:
|
||||||
|
*
|
||||||
|
* [
|
||||||
|
* 'syncToken' => 'The current synctoken',
|
||||||
|
* 'added' => [
|
||||||
|
* 'new.txt',
|
||||||
|
* ],
|
||||||
|
* 'modified' => [
|
||||||
|
* 'modified.txt',
|
||||||
|
* ],
|
||||||
|
* 'deleted' => [
|
||||||
|
* 'foo.php.bak',
|
||||||
|
* 'old.txt'
|
||||||
|
* ]
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* The returned syncToken property should reflect the *current* syncToken
|
||||||
|
* of the calendar, as reported in the {http://sabredav.org/ns}sync-token
|
||||||
|
* property This is * needed here too, to ensure the operation is atomic.
|
||||||
|
*
|
||||||
|
* If the $syncToken argument is specified as null, this is an initial
|
||||||
|
* sync, and all members should be reported.
|
||||||
|
*
|
||||||
|
* The modified property is an array of nodenames that have changed since
|
||||||
|
* the last token.
|
||||||
|
*
|
||||||
|
* The deleted property is an array with nodenames, that have been deleted
|
||||||
|
* from collection.
|
||||||
|
*
|
||||||
|
* The $syncLevel argument is basically the 'depth' of the report. If it's
|
||||||
|
* 1, you only have to report changes that happened only directly in
|
||||||
|
* immediate descendants. If it's 2, it should also include changes from
|
||||||
|
* the nodes below the child collections. (grandchildren)
|
||||||
|
*
|
||||||
|
* The $limit argument allows a client to specify how many results should
|
||||||
|
* be returned at most. If the limit is not specified, it should be treated
|
||||||
|
* as infinite.
|
||||||
|
*
|
||||||
|
* If the limit (infinite or not) is higher than you're willing to return,
|
||||||
|
* you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
|
||||||
|
*
|
||||||
|
* If the syncToken is expired (due to data cleanup) or unknown, you must
|
||||||
|
* return null.
|
||||||
|
*
|
||||||
|
* The limit is 'suggestive'. You are free to ignore it.
|
||||||
|
*
|
||||||
|
* @param string $calendarId
|
||||||
|
* @param string $syncToken
|
||||||
|
* @param int $syncLevel
|
||||||
|
* @param int $limit
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null);
|
||||||
|
}
|
||||||
+460
@@ -0,0 +1,460 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV;
|
||||||
|
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\DAV\PropPatch;
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object represents a CalDAV calendar.
|
||||||
|
*
|
||||||
|
* A calendar can contain multiple TODO and or Events. These are represented
|
||||||
|
* as \Sabre\CalDAV\CalendarObject objects.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, DAV\IMultiGet
|
||||||
|
{
|
||||||
|
use DAVACL\ACLTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an array with calendar information.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $calendarInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CalDAV backend.
|
||||||
|
*
|
||||||
|
* @var Backend\BackendInterface
|
||||||
|
*/
|
||||||
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param array $calendarInfo
|
||||||
|
*/
|
||||||
|
public function __construct(Backend\BackendInterface $caldavBackend, $calendarInfo)
|
||||||
|
{
|
||||||
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
$this->calendarInfo = $calendarInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the calendar.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->calendarInfo['uri'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates properties on this node.
|
||||||
|
*
|
||||||
|
* This method received a PropPatch object, which contains all the
|
||||||
|
* information about the update.
|
||||||
|
*
|
||||||
|
* To update specific properties, call the 'handle' method on this object.
|
||||||
|
* Read the PropPatch documentation for more information.
|
||||||
|
*/
|
||||||
|
public function propPatch(PropPatch $propPatch)
|
||||||
|
{
|
||||||
|
return $this->caldavBackend->updateCalendar($this->calendarInfo['id'], $propPatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of properties.
|
||||||
|
*
|
||||||
|
* @param array $requestedProperties
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getProperties($requestedProperties)
|
||||||
|
{
|
||||||
|
$response = [];
|
||||||
|
|
||||||
|
foreach ($this->calendarInfo as $propName => $propValue) {
|
||||||
|
if (!is_null($propValue) && '{' === $propName[0]) {
|
||||||
|
$response[$propName] = $this->calendarInfo[$propName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a calendar object.
|
||||||
|
*
|
||||||
|
* The contained calendar objects are for example Events or Todo's.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return \Sabre\CalDAV\ICalendarObject
|
||||||
|
*/
|
||||||
|
public function getChild($name)
|
||||||
|
{
|
||||||
|
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
|
||||||
|
|
||||||
|
if (!$obj) {
|
||||||
|
throw new DAV\Exception\NotFound('Calendar object not found');
|
||||||
|
}
|
||||||
|
$obj['acl'] = $this->getChildACL();
|
||||||
|
|
||||||
|
return new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full list of calendar objects.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getChildren()
|
||||||
|
{
|
||||||
|
$objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
|
||||||
|
$children = [];
|
||||||
|
foreach ($objs as $obj) {
|
||||||
|
$obj['acl'] = $this->getChildACL();
|
||||||
|
$children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $children;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method receives a list of paths in it's first argument.
|
||||||
|
* It must return an array with Node objects.
|
||||||
|
*
|
||||||
|
* If any children are not found, you do not have to return them.
|
||||||
|
*
|
||||||
|
* @param string[] $paths
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getMultipleChildren(array $paths)
|
||||||
|
{
|
||||||
|
$objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths);
|
||||||
|
$children = [];
|
||||||
|
foreach ($objs as $obj) {
|
||||||
|
$obj['acl'] = $this->getChildACL();
|
||||||
|
$children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $children;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a child-node exists.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function childExists($name)
|
||||||
|
{
|
||||||
|
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
|
||||||
|
if (!$obj) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new directory.
|
||||||
|
*
|
||||||
|
* We actually block this, as subdirectories are not allowed in calendars.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*/
|
||||||
|
public function createDirectory($name)
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\MethodNotAllowed('Creating collections in calendar objects is not allowed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new file.
|
||||||
|
*
|
||||||
|
* The contents of the new file must be a valid ICalendar string.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param resource $data
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function createFile($name, $data = null)
|
||||||
|
{
|
||||||
|
if (is_resource($data)) {
|
||||||
|
$data = stream_get_contents($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'], $name, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the calendar.
|
||||||
|
*/
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
$this->caldavBackend->deleteCalendar($this->calendarInfo['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renames the calendar. Note that most calendars use the
|
||||||
|
* {DAV:}displayname to display a name to display a name.
|
||||||
|
*
|
||||||
|
* @param string $newName
|
||||||
|
*/
|
||||||
|
public function setName($newName)
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\MethodNotAllowed('Renaming calendars is not yet supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last modification date as a unix timestamp.
|
||||||
|
*/
|
||||||
|
public function getLastModified()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the owner principal.
|
||||||
|
*
|
||||||
|
* This must be a url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getOwner()
|
||||||
|
{
|
||||||
|
return $this->calendarInfo['principaluri'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of ACE's for this node.
|
||||||
|
*
|
||||||
|
* Each ACE has the following properties:
|
||||||
|
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||||
|
* currently the only supported privileges
|
||||||
|
* * 'principal', a url to the principal who owns the node
|
||||||
|
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||||
|
* be updated.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getACL()
|
||||||
|
{
|
||||||
|
$acl = [
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->getOwner().'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->getOwner().'/calendar-proxy-read',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{'.Plugin::NS_CALDAV.'}read-free-busy',
|
||||||
|
'principal' => '{DAV:}authenticated',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) {
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'principal' => $this->getOwner().'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $acl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the ACL's for calendar objects in this calendar.
|
||||||
|
* The result of this method automatically gets passed to the
|
||||||
|
* calendar-object nodes in the calendar.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getChildACL()
|
||||||
|
{
|
||||||
|
$acl = [
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->getOwner().'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->getOwner().'/calendar-proxy-read',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) {
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'principal' => $this->getOwner().'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $acl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a calendar-query on the contents of this calendar.
|
||||||
|
*
|
||||||
|
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||||
|
* calendar-query it is possible for a client to request a specific set of
|
||||||
|
* object, based on contents of iCalendar properties, date-ranges and
|
||||||
|
* iCalendar component types (VTODO, VEVENT).
|
||||||
|
*
|
||||||
|
* This method should just return a list of (relative) urls that match this
|
||||||
|
* query.
|
||||||
|
*
|
||||||
|
* The list of filters are specified as an array. The exact array is
|
||||||
|
* documented by Sabre\CalDAV\CalendarQueryParser.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function calendarQuery(array $filters)
|
||||||
|
{
|
||||||
|
return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the current sync-token for this collection.
|
||||||
|
* This can be any string.
|
||||||
|
*
|
||||||
|
* If null is returned from this function, the plugin assumes there's no
|
||||||
|
* sync information available.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getSyncToken()
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
$this->caldavBackend instanceof Backend\SyncSupport &&
|
||||||
|
isset($this->calendarInfo['{DAV:}sync-token'])
|
||||||
|
) {
|
||||||
|
return $this->calendarInfo['{DAV:}sync-token'];
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
$this->caldavBackend instanceof Backend\SyncSupport &&
|
||||||
|
isset($this->calendarInfo['{http://sabredav.org/ns}sync-token'])
|
||||||
|
) {
|
||||||
|
return $this->calendarInfo['{http://sabredav.org/ns}sync-token'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The getChanges method returns all the changes that have happened, since
|
||||||
|
* the specified syncToken and the current collection.
|
||||||
|
*
|
||||||
|
* This function should return an array, such as the following:
|
||||||
|
*
|
||||||
|
* [
|
||||||
|
* 'syncToken' => 'The current synctoken',
|
||||||
|
* 'added' => [
|
||||||
|
* 'new.txt',
|
||||||
|
* ],
|
||||||
|
* 'modified' => [
|
||||||
|
* 'modified.txt',
|
||||||
|
* ],
|
||||||
|
* 'deleted' => [
|
||||||
|
* 'foo.php.bak',
|
||||||
|
* 'old.txt'
|
||||||
|
* ],
|
||||||
|
* 'result_truncated' : true
|
||||||
|
* ];
|
||||||
|
*
|
||||||
|
* The syncToken property should reflect the *current* syncToken of the
|
||||||
|
* collection, as reported getSyncToken(). This is needed here too, to
|
||||||
|
* ensure the operation is atomic.
|
||||||
|
*
|
||||||
|
* If the syncToken is specified as null, this is an initial sync, and all
|
||||||
|
* members should be reported.
|
||||||
|
*
|
||||||
|
* If result is truncated due to server limitation or limit by client,
|
||||||
|
* set result_truncated to true, otherwise set to false or do not add the key.
|
||||||
|
*
|
||||||
|
* The modified property is an array of nodenames that have changed since
|
||||||
|
* the last token.
|
||||||
|
*
|
||||||
|
* The deleted property is an array with nodenames, that have been deleted
|
||||||
|
* from collection.
|
||||||
|
*
|
||||||
|
* The second argument is basically the 'depth' of the report. If it's 1,
|
||||||
|
* you only have to report changes that happened only directly in immediate
|
||||||
|
* descendants. If it's 2, it should also include changes from the nodes
|
||||||
|
* below the child collections. (grandchildren)
|
||||||
|
*
|
||||||
|
* The third (optional) argument allows a client to specify how many
|
||||||
|
* results should be returned at most. If the limit is not specified, it
|
||||||
|
* should be treated as infinite.
|
||||||
|
*
|
||||||
|
* If the limit (infinite or not) is higher than you're willing to return,
|
||||||
|
* the result should be truncated to fit the limit.
|
||||||
|
* Note that even when the result is truncated, syncToken must be consistent
|
||||||
|
* with the truncated result, not the result before truncation.
|
||||||
|
* (See RFC6578 Section 3.6 for detail)
|
||||||
|
*
|
||||||
|
* If the syncToken is expired (due to data cleanup) or unknown, you must
|
||||||
|
* return null.
|
||||||
|
*
|
||||||
|
* The limit is 'suggestive'. You are free to ignore it.
|
||||||
|
* TODO: RFC6578 Section 3.7 says that the server must fail when the server
|
||||||
|
* cannot truncate according to the limit, so it may not be just suggestive.
|
||||||
|
*
|
||||||
|
* @param string $syncToken
|
||||||
|
* @param int $syncLevel
|
||||||
|
* @param int $limit
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getChanges($syncToken, $syncLevel, $limit = null)
|
||||||
|
{
|
||||||
|
if (!$this->caldavBackend instanceof Backend\SyncSupport) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->caldavBackend->getChangesForCalendar(
|
||||||
|
$this->calendarInfo['id'],
|
||||||
|
$syncToken,
|
||||||
|
$syncLevel,
|
||||||
|
$limit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+356
@@ -0,0 +1,356 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV;
|
||||||
|
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\DAV\Exception\NotFound;
|
||||||
|
use Sabre\DAV\MkCol;
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
use Sabre\Uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CalendarHome represents a node that is usually in a users'
|
||||||
|
* calendar-homeset.
|
||||||
|
*
|
||||||
|
* It contains all the users' calendars, and can optionally contain a
|
||||||
|
* notifications collection, calendar subscriptions, a users' inbox, and a
|
||||||
|
* users' outbox.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL
|
||||||
|
{
|
||||||
|
use DAVACL\ACLTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CalDAV backend.
|
||||||
|
*
|
||||||
|
* @var Backend\BackendInterface
|
||||||
|
*/
|
||||||
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Principal information.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $principalInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param array $principalInfo
|
||||||
|
*/
|
||||||
|
public function __construct(Backend\BackendInterface $caldavBackend, $principalInfo)
|
||||||
|
{
|
||||||
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
$this->principalInfo = $principalInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this object.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
list(, $name) = Uri\split($this->principalInfo['uri']);
|
||||||
|
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the name of this object.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*/
|
||||||
|
public function setName($name)
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\Forbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes this object.
|
||||||
|
*/
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\Forbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last modification date.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getLastModified()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new file under this object.
|
||||||
|
*
|
||||||
|
* This is currently not allowed
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param resource $data
|
||||||
|
*/
|
||||||
|
public function createFile($name, $data = null)
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new directory under this object.
|
||||||
|
*
|
||||||
|
* This is currently not allowed.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
*/
|
||||||
|
public function createDirectory($filename)
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a single calendar, by name.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return Calendar
|
||||||
|
*/
|
||||||
|
public function getChild($name)
|
||||||
|
{
|
||||||
|
// Special nodes
|
||||||
|
if ('inbox' === $name && $this->caldavBackend instanceof Backend\SchedulingSupport) {
|
||||||
|
return new Schedule\Inbox($this->caldavBackend, $this->principalInfo['uri']);
|
||||||
|
}
|
||||||
|
if ('outbox' === $name && $this->caldavBackend instanceof Backend\SchedulingSupport) {
|
||||||
|
return new Schedule\Outbox($this->principalInfo['uri']);
|
||||||
|
}
|
||||||
|
if ('notifications' === $name && $this->caldavBackend instanceof Backend\NotificationSupport) {
|
||||||
|
return new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calendars
|
||||||
|
foreach ($this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']) as $calendar) {
|
||||||
|
if ($calendar['uri'] === $name) {
|
||||||
|
if ($this->caldavBackend instanceof Backend\SharingSupport) {
|
||||||
|
return new SharedCalendar($this->caldavBackend, $calendar);
|
||||||
|
} else {
|
||||||
|
return new Calendar($this->caldavBackend, $calendar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->caldavBackend instanceof Backend\SubscriptionSupport) {
|
||||||
|
foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) {
|
||||||
|
if ($subscription['uri'] === $name) {
|
||||||
|
return new Subscriptions\Subscription($this->caldavBackend, $subscription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotFound('Node with name \''.$name.'\' could not be found');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a calendar exists.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function childExists($name)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return (bool) $this->getChild($name);
|
||||||
|
} catch (NotFound $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of calendars.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getChildren()
|
||||||
|
{
|
||||||
|
$calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
|
||||||
|
$objs = [];
|
||||||
|
foreach ($calendars as $calendar) {
|
||||||
|
if ($this->caldavBackend instanceof Backend\SharingSupport) {
|
||||||
|
$objs[] = new SharedCalendar($this->caldavBackend, $calendar);
|
||||||
|
} else {
|
||||||
|
$objs[] = new Calendar($this->caldavBackend, $calendar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->caldavBackend instanceof Backend\SchedulingSupport) {
|
||||||
|
$objs[] = new Schedule\Inbox($this->caldavBackend, $this->principalInfo['uri']);
|
||||||
|
$objs[] = new Schedule\Outbox($this->principalInfo['uri']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're adding a notifications node, if it's supported by the backend.
|
||||||
|
if ($this->caldavBackend instanceof Backend\NotificationSupport) {
|
||||||
|
$objs[] = new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the backend supports subscriptions, we'll add those as well,
|
||||||
|
if ($this->caldavBackend instanceof Backend\SubscriptionSupport) {
|
||||||
|
foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) {
|
||||||
|
$objs[] = new Subscriptions\Subscription($this->caldavBackend, $subscription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $objs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new calendar or subscription.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @throws DAV\Exception\InvalidResourceType
|
||||||
|
*/
|
||||||
|
public function createExtendedCollection($name, MkCol $mkCol)
|
||||||
|
{
|
||||||
|
$isCalendar = false;
|
||||||
|
$isSubscription = false;
|
||||||
|
foreach ($mkCol->getResourceType() as $rt) {
|
||||||
|
switch ($rt) {
|
||||||
|
case '{DAV:}collection':
|
||||||
|
case '{http://calendarserver.org/ns/}shared-owner':
|
||||||
|
// ignore
|
||||||
|
break;
|
||||||
|
case '{urn:ietf:params:xml:ns:caldav}calendar':
|
||||||
|
$isCalendar = true;
|
||||||
|
break;
|
||||||
|
case '{http://calendarserver.org/ns/}subscribed':
|
||||||
|
$isSubscription = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new DAV\Exception\InvalidResourceType('Unknown resourceType: '.$rt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$properties = $mkCol->getRemainingValues();
|
||||||
|
$mkCol->setRemainingResultCode(201);
|
||||||
|
|
||||||
|
if ($isSubscription) {
|
||||||
|
if (!$this->caldavBackend instanceof Backend\SubscriptionSupport) {
|
||||||
|
throw new DAV\Exception\InvalidResourceType('This backend does not support subscriptions');
|
||||||
|
}
|
||||||
|
$this->caldavBackend->createSubscription($this->principalInfo['uri'], $name, $properties);
|
||||||
|
} elseif ($isCalendar) {
|
||||||
|
$this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties);
|
||||||
|
} else {
|
||||||
|
throw new DAV\Exception\InvalidResourceType('You can only create calendars and subscriptions in this collection');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the owner of the calendar home.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getOwner()
|
||||||
|
{
|
||||||
|
return $this->principalInfo['uri'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of ACE's for this node.
|
||||||
|
*
|
||||||
|
* Each ACE has the following properties:
|
||||||
|
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||||
|
* currently the only supported privileges
|
||||||
|
* * 'principal', a url to the principal who owns the node
|
||||||
|
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||||
|
* be updated.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getACL()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->principalInfo['uri'],
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'principal' => $this->principalInfo['uri'],
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->principalInfo['uri'].'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'principal' => $this->principalInfo['uri'].'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->principalInfo['uri'].'/calendar-proxy-read',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when a user replied to a request to share.
|
||||||
|
*
|
||||||
|
* This method should return the url of the newly created calendar if the
|
||||||
|
* share was accepted.
|
||||||
|
*
|
||||||
|
* @param string $href The sharee who is replying (often a mailto: address)
|
||||||
|
* @param int $status One of the SharingPlugin::STATUS_* constants
|
||||||
|
* @param string $calendarUri The url to the calendar thats being shared
|
||||||
|
* @param string $inReplyTo The unique id this message is a response to
|
||||||
|
* @param string $summary A description of the reply
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null)
|
||||||
|
{
|
||||||
|
if (!$this->caldavBackend instanceof Backend\SharingSupport) {
|
||||||
|
throw new DAV\Exception\NotImplemented('Sharing support is not implemented by this backend.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->caldavBackend->shareReply($href, $status, $calendarUri, $inReplyTo, $summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches through all of a users calendars and calendar objects to find
|
||||||
|
* an object with a specific UID.
|
||||||
|
*
|
||||||
|
* This method should return the path to this object, relative to the
|
||||||
|
* calendar home, so this path usually only contains two parts:
|
||||||
|
*
|
||||||
|
* calendarpath/objectpath.ics
|
||||||
|
*
|
||||||
|
* If the uid is not found, return null.
|
||||||
|
*
|
||||||
|
* This method should only consider * objects that the principal owns, so
|
||||||
|
* any calendars owned by other principals that also appear in this
|
||||||
|
* collection should be ignored.
|
||||||
|
*
|
||||||
|
* @param string $uid
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getCalendarObjectByUID($uid)
|
||||||
|
{
|
||||||
|
return $this->caldavBackend->getCalendarObjectByUID($this->principalInfo['uri'], $uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
+223
@@ -0,0 +1,223 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CalendarObject represents a single VEVENT or VTODO within a Calendar.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\DAVACL\IACL
|
||||||
|
{
|
||||||
|
use \Sabre\DAVACL\ACLTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sabre\CalDAV\Backend\BackendInterface.
|
||||||
|
*
|
||||||
|
* @var Backend\AbstractBackend
|
||||||
|
*/
|
||||||
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array with information about this CalendarObject.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $objectData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array with information about the containing calendar.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $calendarInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* The following properties may be passed within $objectData:
|
||||||
|
*
|
||||||
|
* * calendarid - This must refer to a calendarid from a caldavBackend
|
||||||
|
* * uri - A unique uri. Only the 'basename' must be passed.
|
||||||
|
* * calendardata (optional) - The iCalendar data
|
||||||
|
* * etag - (optional) The etag for this object, MUST be encloded with
|
||||||
|
* double-quotes.
|
||||||
|
* * size - (optional) The size of the data in bytes.
|
||||||
|
* * lastmodified - (optional) format as a unix timestamp.
|
||||||
|
* * acl - (optional) Use this to override the default ACL for the node.
|
||||||
|
*/
|
||||||
|
public function __construct(Backend\BackendInterface $caldavBackend, array $calendarInfo, array $objectData)
|
||||||
|
{
|
||||||
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
|
||||||
|
if (!isset($objectData['uri'])) {
|
||||||
|
throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->calendarInfo = $calendarInfo;
|
||||||
|
$this->objectData = $objectData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the uri for this object.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->objectData['uri'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ICalendar-formatted object.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get()
|
||||||
|
{
|
||||||
|
// Pre-populating the 'calendardata' is optional, if we don't have it
|
||||||
|
// already we fetch it from the backend.
|
||||||
|
if (!isset($this->objectData['calendardata'])) {
|
||||||
|
$this->objectData = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $this->objectData['uri']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->objectData['calendardata'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the ICalendar-formatted object.
|
||||||
|
*
|
||||||
|
* @param string|resource $calendarData
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function put($calendarData)
|
||||||
|
{
|
||||||
|
if (is_resource($calendarData)) {
|
||||||
|
$calendarData = stream_get_contents($calendarData);
|
||||||
|
}
|
||||||
|
$etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'], $this->objectData['uri'], $calendarData);
|
||||||
|
$this->objectData['calendardata'] = $calendarData;
|
||||||
|
$this->objectData['etag'] = $etag;
|
||||||
|
|
||||||
|
return $etag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the calendar object.
|
||||||
|
*/
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
$this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'], $this->objectData['uri']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the mime content-type.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContentType()
|
||||||
|
{
|
||||||
|
$mime = 'text/calendar; charset=utf-8';
|
||||||
|
if (isset($this->objectData['component']) && $this->objectData['component']) {
|
||||||
|
$mime .= '; component='.$this->objectData['component'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an ETag for this object.
|
||||||
|
*
|
||||||
|
* The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getETag()
|
||||||
|
{
|
||||||
|
if (isset($this->objectData['etag'])) {
|
||||||
|
return $this->objectData['etag'];
|
||||||
|
} else {
|
||||||
|
return '"'.md5($this->get()).'"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last modification date as a unix timestamp.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getLastModified()
|
||||||
|
{
|
||||||
|
return $this->objectData['lastmodified'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of this object in bytes.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getSize()
|
||||||
|
{
|
||||||
|
if (array_key_exists('size', $this->objectData)) {
|
||||||
|
return $this->objectData['size'];
|
||||||
|
} else {
|
||||||
|
return strlen($this->get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the owner principal.
|
||||||
|
*
|
||||||
|
* This must be a url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getOwner()
|
||||||
|
{
|
||||||
|
return $this->calendarInfo['principaluri'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of ACE's for this node.
|
||||||
|
*
|
||||||
|
* Each ACE has the following properties:
|
||||||
|
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||||
|
* currently the only supported privileges
|
||||||
|
* * 'principal', a url to the principal who owns the node
|
||||||
|
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||||
|
* be updated.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getACL()
|
||||||
|
{
|
||||||
|
// An alternative acl may be specified in the object data.
|
||||||
|
if (isset($this->objectData['acl'])) {
|
||||||
|
return $this->objectData['acl'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default ACL
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}all',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'],
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}all',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-read',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,354 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CalendarQuery Validator.
|
||||||
|
*
|
||||||
|
* This class is responsible for checking if an iCalendar object matches a set
|
||||||
|
* of filters. The main function to do this is 'validate'.
|
||||||
|
*
|
||||||
|
* This is used to determine which icalendar objects should be returned for a
|
||||||
|
* calendar-query REPORT request.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class CalendarQueryValidator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Verify if a list of filters applies to the calendar data object.
|
||||||
|
*
|
||||||
|
* The list of filters must be formatted as parsed by \Sabre\CalDAV\CalendarQueryParser
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function validate(VObject\Component\VCalendar $vObject, array $filters)
|
||||||
|
{
|
||||||
|
// The top level object is always a component filter.
|
||||||
|
// We'll parse it manually, as it's pretty simple.
|
||||||
|
if ($vObject->name !== $filters['name']) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
$this->validateCompFilters($vObject, $filters['comp-filters']) &&
|
||||||
|
$this->validatePropFilters($vObject, $filters['prop-filters']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks the validity of comp-filters.
|
||||||
|
*
|
||||||
|
* A list of comp-filters needs to be specified. Also the parent of the
|
||||||
|
* component we're checking should be specified, not the component to check
|
||||||
|
* itself.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validateCompFilters(VObject\Component $parent, array $filters)
|
||||||
|
{
|
||||||
|
foreach ($filters as $filter) {
|
||||||
|
$isDefined = isset($parent->{$filter['name']});
|
||||||
|
|
||||||
|
if ($filter['is-not-defined']) {
|
||||||
|
if ($isDefined) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$isDefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('time-range', $filter) && $filter['time-range']) {
|
||||||
|
foreach ($parent->{$filter['name']} as $subComponent) {
|
||||||
|
$start = null;
|
||||||
|
$end = null;
|
||||||
|
if (array_key_exists('start', $filter['time-range'])) {
|
||||||
|
$start = $filter['time-range']['start'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('end', $filter['time-range'])) {
|
||||||
|
$end = $filter['time-range']['end'];
|
||||||
|
}
|
||||||
|
if ($this->validateTimeRange($subComponent, $start, $end)) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$filter['comp-filters'] && !$filter['prop-filters']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are sub-filters, we need to find at least one component
|
||||||
|
// for which the subfilters hold true.
|
||||||
|
foreach ($parent->{$filter['name']} as $subComponent) {
|
||||||
|
if (
|
||||||
|
$this->validateCompFilters($subComponent, $filter['comp-filters']) &&
|
||||||
|
$this->validatePropFilters($subComponent, $filter['prop-filters'])) {
|
||||||
|
// We had a match, so this comp-filter succeeds
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here it means there were sub-comp-filters or
|
||||||
|
// sub-prop-filters and there was no match. This means this filter
|
||||||
|
// needs to return false.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here it means we got through all comp-filters alive so the
|
||||||
|
// filters were all true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks the validity of prop-filters.
|
||||||
|
*
|
||||||
|
* A list of prop-filters needs to be specified. Also the parent of the
|
||||||
|
* property we're checking should be specified, not the property to check
|
||||||
|
* itself.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validatePropFilters(VObject\Component $parent, array $filters)
|
||||||
|
{
|
||||||
|
foreach ($filters as $filter) {
|
||||||
|
$isDefined = isset($parent->{$filter['name']});
|
||||||
|
|
||||||
|
if ($filter['is-not-defined']) {
|
||||||
|
if ($isDefined) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$isDefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('time-range', $filter) && $filter['time-range']) {
|
||||||
|
foreach ($parent->{$filter['name']} as $subComponent) {
|
||||||
|
$start = null;
|
||||||
|
$end = null;
|
||||||
|
if (array_key_exists('start', $filter['time-range'])) {
|
||||||
|
$start = $filter['time-range']['start'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('end', $filter['time-range'])) {
|
||||||
|
$end = $filter['time-range']['end'];
|
||||||
|
}
|
||||||
|
if ($this->validateTimeRange($subComponent, $start, $end)) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$filter['param-filters'] && !$filter['text-match']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are sub-filters, we need to find at least one property
|
||||||
|
// for which the subfilters hold true.
|
||||||
|
foreach ($parent->{$filter['name']} as $subComponent) {
|
||||||
|
if (
|
||||||
|
$this->validateParamFilters($subComponent, $filter['param-filters']) &&
|
||||||
|
(!$filter['text-match'] || $this->validateTextMatch($subComponent, $filter['text-match']))
|
||||||
|
) {
|
||||||
|
// We had a match, so this prop-filter succeeds
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here it means there were sub-param-filters or
|
||||||
|
// text-match filters and there was no match. This means the
|
||||||
|
// filter needs to return false.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here it means we got through all prop-filters alive so the
|
||||||
|
// filters were all true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks the validity of param-filters.
|
||||||
|
*
|
||||||
|
* A list of param-filters needs to be specified. Also the parent of the
|
||||||
|
* parameter we're checking should be specified, not the parameter to check
|
||||||
|
* itself.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validateParamFilters(VObject\Property $parent, array $filters)
|
||||||
|
{
|
||||||
|
foreach ($filters as $filter) {
|
||||||
|
$isDefined = isset($parent[$filter['name']]);
|
||||||
|
|
||||||
|
if ($filter['is-not-defined']) {
|
||||||
|
if ($isDefined) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$isDefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$filter['text-match']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are sub-filters, we need to find at least one parameter
|
||||||
|
// for which the subfilters hold true.
|
||||||
|
foreach ($parent[$filter['name']]->getParts() as $paramPart) {
|
||||||
|
if ($this->validateTextMatch($paramPart, $filter['text-match'])) {
|
||||||
|
// We had a match, so this param-filter succeeds
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here it means there was a text-match filter and there
|
||||||
|
// were no matches. This means the filter needs to return false.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here it means we got through all param-filters alive so the
|
||||||
|
// filters were all true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks the validity of a text-match.
|
||||||
|
*
|
||||||
|
* A single text-match should be specified as well as the specific property
|
||||||
|
* or parameter we need to validate.
|
||||||
|
*
|
||||||
|
* @param VObject\Node|string $check value to check against
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validateTextMatch($check, array $textMatch)
|
||||||
|
{
|
||||||
|
if ($check instanceof VObject\Node) {
|
||||||
|
$check = $check->getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
$isMatching = \Sabre\DAV\StringUtil::textMatch($check, $textMatch['value'], $textMatch['collation']);
|
||||||
|
|
||||||
|
return $textMatch['negate-condition'] xor $isMatching;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates if a component matches the given time range.
|
||||||
|
*
|
||||||
|
* This is all based on the rules specified in rfc4791, which are quite
|
||||||
|
* complex.
|
||||||
|
*
|
||||||
|
* @param DateTime $start
|
||||||
|
* @param DateTime $end
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validateTimeRange(VObject\Node $component, $start, $end)
|
||||||
|
{
|
||||||
|
if (is_null($start)) {
|
||||||
|
$start = new DateTime('1900-01-01');
|
||||||
|
}
|
||||||
|
if (is_null($end)) {
|
||||||
|
$end = new DateTime('3000-01-01');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($component->name) {
|
||||||
|
case 'VEVENT':
|
||||||
|
case 'VTODO':
|
||||||
|
case 'VJOURNAL':
|
||||||
|
return $component->isInTimeRange($start, $end);
|
||||||
|
|
||||||
|
case 'VALARM':
|
||||||
|
// If the valarm is wrapped in a recurring event, we need to
|
||||||
|
// expand the recursions, and validate each.
|
||||||
|
//
|
||||||
|
// Our datamodel doesn't easily allow us to do this straight
|
||||||
|
// in the VALARM component code, so this is a hack, and an
|
||||||
|
// expensive one too.
|
||||||
|
if ('VEVENT' === $component->parent->name && $component->parent->RRULE) {
|
||||||
|
// Fire up the iterator!
|
||||||
|
$it = new VObject\Recur\EventIterator($component->parent->parent, (string) $component->parent->UID);
|
||||||
|
while ($it->valid()) {
|
||||||
|
$expandedEvent = $it->getEventObject();
|
||||||
|
|
||||||
|
// We need to check from these expanded alarms, which
|
||||||
|
// one is the first to trigger. Based on this, we can
|
||||||
|
// determine if we can 'give up' expanding events.
|
||||||
|
$firstAlarm = null;
|
||||||
|
if (null !== $expandedEvent->VALARM) {
|
||||||
|
foreach ($expandedEvent->VALARM as $expandedAlarm) {
|
||||||
|
$effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
|
||||||
|
if ($expandedAlarm->isInTimeRange($start, $end)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('DATE-TIME' === (string) $expandedAlarm->TRIGGER['VALUE']) {
|
||||||
|
// This is an alarm with a non-relative trigger
|
||||||
|
// time, likely created by a buggy client. The
|
||||||
|
// implication is that every alarm in this
|
||||||
|
// recurring event trigger at the exact same
|
||||||
|
// time. It doesn't make sense to traverse
|
||||||
|
// further.
|
||||||
|
} else {
|
||||||
|
// We store the first alarm as a means to
|
||||||
|
// figure out when we can stop traversing.
|
||||||
|
if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
|
||||||
|
$firstAlarm = $effectiveTrigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_null($firstAlarm)) {
|
||||||
|
// No alarm was found.
|
||||||
|
//
|
||||||
|
// Or technically: No alarm that will change for
|
||||||
|
// every instance of the recurrence was found,
|
||||||
|
// which means we can assume there was no match.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($firstAlarm > $end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$it->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return $component->isInTimeRange($start, $end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// no break
|
||||||
|
case 'VFREEBUSY':
|
||||||
|
throw new \Sabre\DAV\Exception\NotImplemented('time-range filters are currently not supported on '.$component->name.' components');
|
||||||
|
case 'COMPLETED':
|
||||||
|
case 'CREATED':
|
||||||
|
case 'DTEND':
|
||||||
|
case 'DTSTAMP':
|
||||||
|
case 'DTSTART':
|
||||||
|
case 'DUE':
|
||||||
|
case 'LAST-MODIFIED':
|
||||||
|
return $start <= $component->getDateTime() && $end >= $component->getDateTime();
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Sabre\DAV\Exception\BadRequest('You cannot create a time-range filter on a '.$component->name.' component');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV;
|
||||||
|
|
||||||
|
use Sabre\DAVACL\PrincipalBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calendars collection.
|
||||||
|
*
|
||||||
|
* This object is responsible for generating a list of calendar-homes for each
|
||||||
|
* user.
|
||||||
|
*
|
||||||
|
* This is the top-most node for the calendars tree. In most servers this class
|
||||||
|
* represents the "/calendars" path.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class CalendarRoot extends \Sabre\DAVACL\AbstractPrincipalCollection
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* CalDAV backend.
|
||||||
|
*
|
||||||
|
* @var Backend\BackendInterface
|
||||||
|
*/
|
||||||
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* This constructor needs both an authentication and a caldav backend.
|
||||||
|
*
|
||||||
|
* By default this class will show a list of calendar collections for
|
||||||
|
* principals in the 'principals' collection. If your main principals are
|
||||||
|
* actually located in a different path, use the $principalPrefix argument
|
||||||
|
* to override this.
|
||||||
|
*
|
||||||
|
* @param string $principalPrefix
|
||||||
|
*/
|
||||||
|
public function __construct(PrincipalBackend\BackendInterface $principalBackend, Backend\BackendInterface $caldavBackend, $principalPrefix = 'principals')
|
||||||
|
{
|
||||||
|
parent::__construct($principalBackend, $principalPrefix);
|
||||||
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the nodename.
|
||||||
|
*
|
||||||
|
* We're overriding this, because the default will be the 'principalPrefix',
|
||||||
|
* and we want it to be Sabre\CalDAV\Plugin::CALENDAR_ROOT
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return Plugin::CALENDAR_ROOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns a node for a principal.
|
||||||
|
*
|
||||||
|
* The passed array contains principal information, and is guaranteed to
|
||||||
|
* at least contain a uri item. Other properties may or may not be
|
||||||
|
* supplied by the authentication backend.
|
||||||
|
*
|
||||||
|
* @return \Sabre\DAV\INode
|
||||||
|
*/
|
||||||
|
public function getChildForPrincipal(array $principal)
|
||||||
|
{
|
||||||
|
return new CalendarHome($this->caldavBackend, $principal);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Exception;
|
||||||
|
|
||||||
|
use Sabre\CalDAV;
|
||||||
|
use Sabre\DAV;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InvalidComponentType.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class InvalidComponentType extends DAV\Exception\Forbidden
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Adds in extra information in the xml response.
|
||||||
|
*
|
||||||
|
* This method adds the {CALDAV:}supported-calendar-component as defined in rfc4791
|
||||||
|
*/
|
||||||
|
public function serialize(DAV\Server $server, \DOMElement $errorNode)
|
||||||
|
{
|
||||||
|
$doc = $errorNode->ownerDocument;
|
||||||
|
|
||||||
|
$np = $doc->createElementNS(CalDAV\Plugin::NS_CALDAV, 'cal:supported-calendar-component');
|
||||||
|
$errorNode->appendChild($np);
|
||||||
|
}
|
||||||
|
}
|
||||||
+377
@@ -0,0 +1,377 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use DateTimeZone;
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\DAV\Exception\BadRequest;
|
||||||
|
use Sabre\HTTP\RequestInterface;
|
||||||
|
use Sabre\HTTP\ResponseInterface;
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICS Exporter.
|
||||||
|
*
|
||||||
|
* This plugin adds the ability to export entire calendars as .ics files.
|
||||||
|
* This is useful for clients that don't support CalDAV yet. They often do
|
||||||
|
* support ics files.
|
||||||
|
*
|
||||||
|
* To use this, point a http client to a caldav calendar, and add ?expand to
|
||||||
|
* the url.
|
||||||
|
*
|
||||||
|
* Further options that can be added to the url:
|
||||||
|
* start=123456789 - Only return events after the given unix timestamp
|
||||||
|
* end=123245679 - Only return events from before the given unix timestamp
|
||||||
|
* expand=1 - Strip timezone information and expand recurring events.
|
||||||
|
* If you'd like to expand, you _must_ also specify start
|
||||||
|
* and end.
|
||||||
|
*
|
||||||
|
* By default this plugin returns data in the text/calendar format (iCalendar
|
||||||
|
* 2.0). If you'd like to receive jCal data instead, you can use an Accept
|
||||||
|
* header:
|
||||||
|
*
|
||||||
|
* Accept: application/calendar+json
|
||||||
|
*
|
||||||
|
* Alternatively, you can also specify this in the url using
|
||||||
|
* accept=application/calendar+json, or accept=jcal for short. If the url
|
||||||
|
* parameter and Accept header is specified, the url parameter wins.
|
||||||
|
*
|
||||||
|
* Note that specifying a start or end data implies that only events will be
|
||||||
|
* returned. VTODO and VJOURNAL will be stripped.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class ICSExportPlugin extends DAV\ServerPlugin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Reference to Server class.
|
||||||
|
*
|
||||||
|
* @var \Sabre\DAV\Server
|
||||||
|
*/
|
||||||
|
protected $server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the plugin and registers event handlers.
|
||||||
|
*
|
||||||
|
* @param \Sabre\DAV\Server $server
|
||||||
|
*/
|
||||||
|
public function initialize(DAV\Server $server)
|
||||||
|
{
|
||||||
|
$this->server = $server;
|
||||||
|
$server->on('method:GET', [$this, 'httpGet'], 90);
|
||||||
|
$server->on('browserButtonActions', function ($path, $node, &$actions) {
|
||||||
|
if ($node instanceof ICalendar) {
|
||||||
|
$actions .= '<a href="'.htmlspecialchars($path, ENT_QUOTES, 'UTF-8').'?export"><span class="oi" data-glyph="calendar"></span></a>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercepts GET requests on calendar urls ending with ?export.
|
||||||
|
*
|
||||||
|
* @throws BadRequest
|
||||||
|
* @throws DAV\Exception\NotFound
|
||||||
|
* @throws VObject\InvalidDataException
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function httpGet(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
$queryParams = $request->getQueryParameters();
|
||||||
|
if (!array_key_exists('export', $queryParams)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = $request->getPath();
|
||||||
|
|
||||||
|
$node = $this->server->getProperties($path, [
|
||||||
|
'{DAV:}resourcetype',
|
||||||
|
'{DAV:}displayname',
|
||||||
|
'{http://sabredav.org/ns}sync-token',
|
||||||
|
'{DAV:}sync-token',
|
||||||
|
'{http://apple.com/ns/ical/}calendar-color',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!isset($node['{DAV:}resourcetype']) || !$node['{DAV:}resourcetype']->is('{'.Plugin::NS_CALDAV.'}calendar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Marking the transactionType, for logging purposes.
|
||||||
|
$this->server->transactionType = 'get-calendar-export';
|
||||||
|
|
||||||
|
$properties = $node;
|
||||||
|
|
||||||
|
$start = null;
|
||||||
|
$end = null;
|
||||||
|
$expand = false;
|
||||||
|
$componentType = false;
|
||||||
|
if (isset($queryParams['start'])) {
|
||||||
|
if (!ctype_digit($queryParams['start'])) {
|
||||||
|
throw new BadRequest('The start= parameter must contain a unix timestamp');
|
||||||
|
}
|
||||||
|
$start = DateTime::createFromFormat('U', $queryParams['start']);
|
||||||
|
}
|
||||||
|
if (isset($queryParams['end'])) {
|
||||||
|
if (!ctype_digit($queryParams['end'])) {
|
||||||
|
throw new BadRequest('The end= parameter must contain a unix timestamp');
|
||||||
|
}
|
||||||
|
$end = DateTime::createFromFormat('U', $queryParams['end']);
|
||||||
|
}
|
||||||
|
if (isset($queryParams['expand']) && (bool) $queryParams['expand']) {
|
||||||
|
if (!$start || !$end) {
|
||||||
|
throw new BadRequest('If you\'d like to expand recurrences, you must specify both a start= and end= parameter.');
|
||||||
|
}
|
||||||
|
$expand = true;
|
||||||
|
$componentType = 'VEVENT';
|
||||||
|
}
|
||||||
|
if (isset($queryParams['componentType'])) {
|
||||||
|
if (!in_array($queryParams['componentType'], ['VEVENT', 'VTODO', 'VJOURNAL'])) {
|
||||||
|
throw new BadRequest('You are not allowed to search for components of type: '.$queryParams['componentType'].' here');
|
||||||
|
}
|
||||||
|
$componentType = $queryParams['componentType'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$format = \Sabre\HTTP\negotiateContentType(
|
||||||
|
$request->getHeader('Accept'),
|
||||||
|
[
|
||||||
|
'text/calendar',
|
||||||
|
'application/calendar+json',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isset($queryParams['accept'])) {
|
||||||
|
if ('application/calendar+json' === $queryParams['accept'] || 'jcal' === $queryParams['accept']) {
|
||||||
|
$format = 'application/calendar+json';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$format) {
|
||||||
|
$format = 'text/calendar';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, $response);
|
||||||
|
|
||||||
|
// Returning false to break the event chain
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is responsible for generating the actual, full response.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param DateTime|null $start
|
||||||
|
* @param DateTime|null $end
|
||||||
|
* @param bool $expand
|
||||||
|
* @param string $componentType
|
||||||
|
* @param string $format
|
||||||
|
* @param array $properties
|
||||||
|
*
|
||||||
|
* @throws DAV\Exception\NotFound
|
||||||
|
* @throws VObject\InvalidDataException
|
||||||
|
*/
|
||||||
|
protected function generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
$calDataProp = '{'.Plugin::NS_CALDAV.'}calendar-data';
|
||||||
|
$calendarNode = $this->server->tree->getNodeForPath($path);
|
||||||
|
|
||||||
|
$blobs = [];
|
||||||
|
if ($start || $end || $componentType) {
|
||||||
|
// If there was a start or end filter, we need to enlist
|
||||||
|
// calendarQuery for speed.
|
||||||
|
$queryResult = $calendarNode->calendarQuery([
|
||||||
|
'name' => 'VCALENDAR',
|
||||||
|
'comp-filters' => [
|
||||||
|
[
|
||||||
|
'name' => $componentType,
|
||||||
|
'comp-filters' => [],
|
||||||
|
'prop-filters' => [],
|
||||||
|
'is-not-defined' => false,
|
||||||
|
'time-range' => [
|
||||||
|
'start' => $start,
|
||||||
|
'end' => $end,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'prop-filters' => [],
|
||||||
|
'is-not-defined' => false,
|
||||||
|
'time-range' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// queryResult is just a list of base urls. We need to prefix the
|
||||||
|
// calendar path.
|
||||||
|
$queryResult = array_map(
|
||||||
|
function ($item) use ($path) {
|
||||||
|
return $path.'/'.$item;
|
||||||
|
},
|
||||||
|
$queryResult
|
||||||
|
);
|
||||||
|
$nodes = $this->server->getPropertiesForMultiplePaths($queryResult, [$calDataProp]);
|
||||||
|
unset($queryResult);
|
||||||
|
} else {
|
||||||
|
$nodes = $this->server->getPropertiesForPath($path, [$calDataProp], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flattening the arrays
|
||||||
|
foreach ($nodes as $node) {
|
||||||
|
if (isset($node[200][$calDataProp])) {
|
||||||
|
$blobs[$node['href']] = $node[200][$calDataProp];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($nodes);
|
||||||
|
|
||||||
|
$mergedCalendar = $this->mergeObjects(
|
||||||
|
$properties,
|
||||||
|
$blobs
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($expand) {
|
||||||
|
$calendarTimeZone = null;
|
||||||
|
// We're expanding, and for that we need to figure out the
|
||||||
|
// calendar's timezone.
|
||||||
|
$tzProp = '{'.Plugin::NS_CALDAV.'}calendar-timezone';
|
||||||
|
$tzResult = $this->server->getProperties($path, [$tzProp]);
|
||||||
|
if (isset($tzResult[$tzProp])) {
|
||||||
|
// This property contains a VCALENDAR with a single
|
||||||
|
// VTIMEZONE.
|
||||||
|
$vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]);
|
||||||
|
$calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
|
||||||
|
// Destroy circular references to PHP will GC the object.
|
||||||
|
$vtimezoneObj->destroy();
|
||||||
|
unset($vtimezoneObj);
|
||||||
|
} else {
|
||||||
|
// Defaulting to UTC.
|
||||||
|
$calendarTimeZone = new DateTimeZone('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
$mergedCalendar = $mergedCalendar->expand($start, $end, $calendarTimeZone);
|
||||||
|
}
|
||||||
|
|
||||||
|
$filenameExtension = '.ics';
|
||||||
|
|
||||||
|
switch ($format) {
|
||||||
|
case 'text/calendar':
|
||||||
|
$mergedCalendar = $mergedCalendar->serialize();
|
||||||
|
$filenameExtension = '.ics';
|
||||||
|
break;
|
||||||
|
case 'application/calendar+json':
|
||||||
|
$mergedCalendar = json_encode($mergedCalendar->jsonSerialize());
|
||||||
|
$filenameExtension = '.json';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = preg_replace(
|
||||||
|
'/[^a-zA-Z0-9-_ ]/um',
|
||||||
|
'',
|
||||||
|
$calendarNode->getName()
|
||||||
|
);
|
||||||
|
$filename .= '-'.date('Y-m-d').$filenameExtension;
|
||||||
|
|
||||||
|
$response->setHeader('Content-Disposition', 'attachment; filename="'.$filename.'"');
|
||||||
|
$response->setHeader('Content-Type', $format);
|
||||||
|
|
||||||
|
$response->setStatus(200);
|
||||||
|
$response->setBody($mergedCalendar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges all calendar objects, and builds one big iCalendar blob.
|
||||||
|
*
|
||||||
|
* @param array $properties Some CalDAV properties
|
||||||
|
*
|
||||||
|
* @return VObject\Component\VCalendar
|
||||||
|
*/
|
||||||
|
public function mergeObjects(array $properties, array $inputObjects)
|
||||||
|
{
|
||||||
|
$calendar = new VObject\Component\VCalendar();
|
||||||
|
$calendar->VERSION = '2.0';
|
||||||
|
if (DAV\Server::$exposeVersion) {
|
||||||
|
$calendar->PRODID = '-//SabreDAV//SabreDAV '.DAV\Version::VERSION.'//EN';
|
||||||
|
} else {
|
||||||
|
$calendar->PRODID = '-//SabreDAV//SabreDAV//EN';
|
||||||
|
}
|
||||||
|
if (isset($properties['{DAV:}displayname'])) {
|
||||||
|
$calendar->{'X-WR-CALNAME'} = $properties['{DAV:}displayname'];
|
||||||
|
}
|
||||||
|
if (isset($properties['{http://apple.com/ns/ical/}calendar-color'])) {
|
||||||
|
$calendar->{'X-APPLE-CALENDAR-COLOR'} = $properties['{http://apple.com/ns/ical/}calendar-color'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$collectedTimezones = [];
|
||||||
|
|
||||||
|
$timezones = [];
|
||||||
|
$objects = [];
|
||||||
|
|
||||||
|
foreach ($inputObjects as $href => $inputObject) {
|
||||||
|
$nodeComp = VObject\Reader::read($inputObject);
|
||||||
|
|
||||||
|
foreach ($nodeComp->children() as $child) {
|
||||||
|
switch ($child->name) {
|
||||||
|
case 'VEVENT':
|
||||||
|
case 'VTODO':
|
||||||
|
case 'VJOURNAL':
|
||||||
|
$objects[] = clone $child;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// VTIMEZONE is special, because we need to filter out the duplicates
|
||||||
|
case 'VTIMEZONE':
|
||||||
|
// Naively just checking tzid.
|
||||||
|
if (in_array((string) $child->TZID, $collectedTimezones)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timezones[] = clone $child;
|
||||||
|
$collectedTimezones[] = $child->TZID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Destroy circular references to PHP will GC the object.
|
||||||
|
$nodeComp->destroy();
|
||||||
|
unset($nodeComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($timezones as $tz) {
|
||||||
|
$calendar->add($tz);
|
||||||
|
}
|
||||||
|
foreach ($objects as $obj) {
|
||||||
|
$calendar->add($obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $calendar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a plugin name.
|
||||||
|
*
|
||||||
|
* Using this name other plugins will be able to access other plugins
|
||||||
|
* using \Sabre\DAV\Server::getPlugin
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPluginName()
|
||||||
|
{
|
||||||
|
return 'ics-export';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a bunch of meta-data about the plugin.
|
||||||
|
*
|
||||||
|
* Providing this information is optional, and is mainly displayed by the
|
||||||
|
* Browser plugin.
|
||||||
|
*
|
||||||
|
* The description key in the returned array may contain html and will not
|
||||||
|
* be sanitized.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getPluginInfo()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->getPluginName(),
|
||||||
|
'description' => 'Adds the ability to export CalDAV calendars as a single iCalendar file.',
|
||||||
|
'link' => 'http://sabre.io/dav/ics-export-plugin/',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV;
|
||||||
|
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calendar interface.
|
||||||
|
*
|
||||||
|
* Implement this interface to allow a node to be recognized as an calendar.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface ICalendar extends ICalendarObjectContainer, DAVACL\IACL
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV;
|
||||||
|
|
||||||
|
use Sabre\DAV;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CalendarObject interface.
|
||||||
|
*
|
||||||
|
* Extend the ICalendarObject interface to allow your custom nodes to be picked up as
|
||||||
|
* CalendarObjects.
|
||||||
|
*
|
||||||
|
* Calendar objects are resources such as Events, Todo's or Journals.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface ICalendarObject extends DAV\IFile
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface represents a node that may contain calendar objects.
|
||||||
|
*
|
||||||
|
* This is the shared parent for both the Inbox collection and calendars
|
||||||
|
* resources.
|
||||||
|
*
|
||||||
|
* In most cases you will likely want to look at ICalendar instead of this
|
||||||
|
* interface.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface ICalendarObjectContainer extends \Sabre\DAV\ICollection
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Performs a calendar-query on the contents of this calendar.
|
||||||
|
*
|
||||||
|
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||||
|
* calendar-query it is possible for a client to request a specific set of
|
||||||
|
* object, based on contents of iCalendar properties, date-ranges and
|
||||||
|
* iCalendar component types (VTODO, VEVENT).
|
||||||
|
*
|
||||||
|
* This method should just return a list of (relative) urls that match this
|
||||||
|
* query.
|
||||||
|
*
|
||||||
|
* The list of filters are specified as an array. The exact array is
|
||||||
|
* documented by \Sabre\CalDAV\CalendarQueryParser.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function calendarQuery(array $filters);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV;
|
||||||
|
|
||||||
|
use Sabre\DAV\Sharing\ISharedNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface represents a Calendar that is shared by a different user.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface ISharedCalendar extends ISharedNode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Marks this calendar as published.
|
||||||
|
*
|
||||||
|
* Publishing a calendar should automatically create a read-only, public,
|
||||||
|
* subscribable calendar.
|
||||||
|
*
|
||||||
|
* @param bool $value
|
||||||
|
*/
|
||||||
|
public function setPublishStatus($value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Notifications;
|
||||||
|
|
||||||
|
use Sabre\CalDAV;
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node represents a list of notifications.
|
||||||
|
*
|
||||||
|
* It provides no additional functionality, but you must implement this
|
||||||
|
* interface to allow the Notifications plugin to mark the collection
|
||||||
|
* as a notifications collection.
|
||||||
|
*
|
||||||
|
* This collection should only return Sabre\CalDAV\Notifications\INode nodes as
|
||||||
|
* its children.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class Collection extends DAV\Collection implements ICollection, DAVACL\IACL
|
||||||
|
{
|
||||||
|
use DAVACL\ACLTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The notification backend.
|
||||||
|
*
|
||||||
|
* @var CalDAV\Backend\NotificationSupport
|
||||||
|
*/
|
||||||
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Principal uri.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $principalUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
*/
|
||||||
|
public function __construct(CalDAV\Backend\NotificationSupport $caldavBackend, $principalUri)
|
||||||
|
{
|
||||||
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
$this->principalUri = $principalUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all notifications for a principal.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getChildren()
|
||||||
|
{
|
||||||
|
$children = [];
|
||||||
|
$notifications = $this->caldavBackend->getNotificationsForPrincipal($this->principalUri);
|
||||||
|
|
||||||
|
foreach ($notifications as $notification) {
|
||||||
|
$children[] = new Node(
|
||||||
|
$this->caldavBackend,
|
||||||
|
$this->principalUri,
|
||||||
|
$notification
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $children;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this object.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'notifications';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the owner principal.
|
||||||
|
*
|
||||||
|
* This must be a url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getOwner()
|
||||||
|
{
|
||||||
|
return $this->principalUri;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Notifications;
|
||||||
|
|
||||||
|
use Sabre\DAV;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node represents a list of notifications.
|
||||||
|
*
|
||||||
|
* It provides no additional functionality, but you must implement this
|
||||||
|
* interface to allow the Notifications plugin to mark the collection
|
||||||
|
* as a notifications collection.
|
||||||
|
*
|
||||||
|
* This collection should only return Sabre\CalDAV\Notifications\INode nodes as
|
||||||
|
* its children.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface ICollection extends DAV\ICollection
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Notifications;
|
||||||
|
|
||||||
|
use Sabre\CalDAV\Xml\Notification\NotificationInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node represents a single notification.
|
||||||
|
*
|
||||||
|
* The signature is mostly identical to that of Sabre\DAV\IFile, but the get() method
|
||||||
|
* MUST return an xml document that matches the requirements of the
|
||||||
|
* 'caldav-notifications.txt' spec.
|
||||||
|
*
|
||||||
|
* For a complete example, check out the Notification class, which contains
|
||||||
|
* some helper functions.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface INode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This method must return an xml element, using the
|
||||||
|
* Sabre\CalDAV\Xml\Notification\NotificationInterface classes.
|
||||||
|
*
|
||||||
|
* @return NotificationInterface
|
||||||
|
*/
|
||||||
|
public function getNotificationType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the etag for the notification.
|
||||||
|
*
|
||||||
|
* The etag must be surrounded by literal double-quotes.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getETag();
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Notifications;
|
||||||
|
|
||||||
|
use Sabre\CalDAV;
|
||||||
|
use Sabre\CalDAV\Xml\Notification\NotificationInterface;
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node represents a single notification.
|
||||||
|
*
|
||||||
|
* The signature is mostly identical to that of Sabre\DAV\IFile, but the get() method
|
||||||
|
* MUST return an xml document that matches the requirements of the
|
||||||
|
* 'caldav-notifications.txt' spec.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class Node extends DAV\File implements INode, DAVACL\IACL
|
||||||
|
{
|
||||||
|
use DAVACL\ACLTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The notification backend.
|
||||||
|
*
|
||||||
|
* @var CalDAV\Backend\NotificationSupport
|
||||||
|
*/
|
||||||
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual notification.
|
||||||
|
*
|
||||||
|
* @var NotificationInterface
|
||||||
|
*/
|
||||||
|
protected $notification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Owner principal of the notification.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $principalUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
*/
|
||||||
|
public function __construct(CalDAV\Backend\NotificationSupport $caldavBackend, $principalUri, NotificationInterface $notification)
|
||||||
|
{
|
||||||
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
$this->principalUri = $principalUri;
|
||||||
|
$this->notification = $notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path name for this notification.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->notification->getId().'.xml';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the etag for the notification.
|
||||||
|
*
|
||||||
|
* The etag must be surrounded by literal double-quotes.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getETag()
|
||||||
|
{
|
||||||
|
return $this->notification->getETag();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method must return an xml element, using the
|
||||||
|
* Sabre\CalDAV\Xml\Notification\NotificationInterface classes.
|
||||||
|
*
|
||||||
|
* @return NotificationInterface
|
||||||
|
*/
|
||||||
|
public function getNotificationType()
|
||||||
|
{
|
||||||
|
return $this->notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes this notification.
|
||||||
|
*/
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
$this->caldavBackend->deleteNotification($this->getOwner(), $this->notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the owner principal.
|
||||||
|
*
|
||||||
|
* This must be an url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getOwner()
|
||||||
|
{
|
||||||
|
return $this->principalUri;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Notifications;
|
||||||
|
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\DAV\INode as BaseINode;
|
||||||
|
use Sabre\DAV\PropFind;
|
||||||
|
use Sabre\DAV\Server;
|
||||||
|
use Sabre\DAV\ServerPlugin;
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
use Sabre\HTTP\RequestInterface;
|
||||||
|
use Sabre\HTTP\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifications plugin.
|
||||||
|
*
|
||||||
|
* This plugin implements several features required by the caldav-notification
|
||||||
|
* draft specification.
|
||||||
|
*
|
||||||
|
* Before version 2.1.0 this functionality was part of Sabre\CalDAV\Plugin but
|
||||||
|
* this has since been split up.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class Plugin extends ServerPlugin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This is the namespace for the proprietary calendarserver extensions.
|
||||||
|
*/
|
||||||
|
const NS_CALENDARSERVER = 'http://calendarserver.org/ns/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to the main server object.
|
||||||
|
*
|
||||||
|
* @var Server
|
||||||
|
*/
|
||||||
|
protected $server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a plugin name.
|
||||||
|
*
|
||||||
|
* Using this name other plugins will be able to access other plugins
|
||||||
|
* using \Sabre\DAV\Server::getPlugin
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPluginName()
|
||||||
|
{
|
||||||
|
return 'notifications';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This initializes the plugin.
|
||||||
|
*
|
||||||
|
* This function is called by Sabre\DAV\Server, after
|
||||||
|
* addPlugin is called.
|
||||||
|
*
|
||||||
|
* This method should set up the required event subscriptions.
|
||||||
|
*/
|
||||||
|
public function initialize(Server $server)
|
||||||
|
{
|
||||||
|
$this->server = $server;
|
||||||
|
$server->on('method:GET', [$this, 'httpGet'], 90);
|
||||||
|
$server->on('propFind', [$this, 'propFind']);
|
||||||
|
|
||||||
|
$server->xml->namespaceMap[self::NS_CALENDARSERVER] = 'cs';
|
||||||
|
$server->resourceTypeMapping['\\Sabre\\CalDAV\\Notifications\\ICollection'] = '{'.self::NS_CALENDARSERVER.'}notification';
|
||||||
|
|
||||||
|
array_push($server->protectedProperties,
|
||||||
|
'{'.self::NS_CALENDARSERVER.'}notification-URL',
|
||||||
|
'{'.self::NS_CALENDARSERVER.'}notificationtype'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropFind.
|
||||||
|
*/
|
||||||
|
public function propFind(PropFind $propFind, BaseINode $node)
|
||||||
|
{
|
||||||
|
$caldavPlugin = $this->server->getPlugin('caldav');
|
||||||
|
|
||||||
|
if ($node instanceof DAVACL\IPrincipal) {
|
||||||
|
$principalUrl = $node->getPrincipalUrl();
|
||||||
|
|
||||||
|
// notification-URL property
|
||||||
|
$propFind->handle('{'.self::NS_CALENDARSERVER.'}notification-URL', function () use ($principalUrl, $caldavPlugin) {
|
||||||
|
$notificationPath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl).'/notifications/';
|
||||||
|
|
||||||
|
return new DAV\Xml\Property\Href($notificationPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof INode) {
|
||||||
|
$propFind->handle(
|
||||||
|
'{'.self::NS_CALENDARSERVER.'}notificationtype',
|
||||||
|
[$node, 'getNotificationType']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is triggered before the usual GET request handler.
|
||||||
|
*
|
||||||
|
* We use this to intercept GET calls to notification nodes, and return the
|
||||||
|
* proper response.
|
||||||
|
*/
|
||||||
|
public function httpGet(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
$path = $request->getPath();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$node = $this->server->tree->getNodeForPath($path);
|
||||||
|
} catch (DAV\Exception\NotFound $e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$node instanceof INode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$writer = $this->server->xml->getWriter();
|
||||||
|
$writer->contextUri = $this->server->getBaseUri();
|
||||||
|
$writer->openMemory();
|
||||||
|
$writer->startDocument('1.0', 'UTF-8');
|
||||||
|
$writer->startElement('{http://calendarserver.org/ns/}notification');
|
||||||
|
$node->getNotificationType()->xmlSerializeFull($writer);
|
||||||
|
$writer->endElement();
|
||||||
|
|
||||||
|
$response->setHeader('Content-Type', 'application/xml');
|
||||||
|
$response->setHeader('ETag', $node->getETag());
|
||||||
|
$response->setStatus(200);
|
||||||
|
$response->setBody($writer->outputMemory());
|
||||||
|
|
||||||
|
// Return false to break the event chain.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a bunch of meta-data about the plugin.
|
||||||
|
*
|
||||||
|
* Providing this information is optional, and is mainly displayed by the
|
||||||
|
* Browser plugin.
|
||||||
|
*
|
||||||
|
* The description key in the returned array may contain html and will not
|
||||||
|
* be sanitized.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getPluginInfo()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->getPluginName(),
|
||||||
|
'description' => 'Adds support for caldav-notifications, which is required to enable caldav-sharing.',
|
||||||
|
'link' => 'http://sabre.io/dav/caldav-sharing/',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
+1011
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Principal;
|
||||||
|
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Principal collection.
|
||||||
|
*
|
||||||
|
* This is an alternative collection to the standard ACL principal collection.
|
||||||
|
* This collection adds support for the calendar-proxy-read and
|
||||||
|
* calendar-proxy-write sub-principals, as defined by the caldav-proxy
|
||||||
|
* specification.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class Collection extends DAVACL\PrincipalCollection
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns a child object based on principal information.
|
||||||
|
*
|
||||||
|
* @return User
|
||||||
|
*/
|
||||||
|
public function getChildForPrincipal(array $principalInfo)
|
||||||
|
{
|
||||||
|
return new User($this->principalBackend, $principalInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Principal;
|
||||||
|
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProxyRead principal interface.
|
||||||
|
*
|
||||||
|
* Any principal node implementing this interface will be picked up as a 'proxy
|
||||||
|
* principal group'.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface IProxyRead extends DAVACL\IPrincipal
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Principal;
|
||||||
|
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProxyWrite principal interface.
|
||||||
|
*
|
||||||
|
* Any principal node implementing this interface will be picked up as a 'proxy
|
||||||
|
* principal group'.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface IProxyWrite extends DAVACL\IPrincipal
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Principal;
|
||||||
|
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProxyRead principal.
|
||||||
|
*
|
||||||
|
* This class represents a principal group, hosted under the main principal.
|
||||||
|
* This is needed to implement 'Calendar delegation' support. This class is
|
||||||
|
* instantiated by User.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class ProxyRead implements IProxyRead
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Principal information from the parent principal.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $principalInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Principal backend.
|
||||||
|
*
|
||||||
|
* @var DAVACL\PrincipalBackend\BackendInterface
|
||||||
|
*/
|
||||||
|
protected $principalBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the object.
|
||||||
|
*
|
||||||
|
* Note that you MUST supply the parent principal information.
|
||||||
|
*/
|
||||||
|
public function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend, array $principalInfo)
|
||||||
|
{
|
||||||
|
$this->principalInfo = $principalInfo;
|
||||||
|
$this->principalBackend = $principalBackend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this principals name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'calendar-proxy-read';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last modification time.
|
||||||
|
*/
|
||||||
|
public function getLastModified()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the current node.
|
||||||
|
*
|
||||||
|
* @throws DAV\Exception\Forbidden
|
||||||
|
*/
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\Forbidden('Permission denied to delete node');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renames the node.
|
||||||
|
*
|
||||||
|
* @param string $name The new name
|
||||||
|
*
|
||||||
|
* @throws DAV\Exception\Forbidden
|
||||||
|
*/
|
||||||
|
public function setName($name)
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\Forbidden('Permission denied to rename file');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of alternative urls for a principal.
|
||||||
|
*
|
||||||
|
* This can for example be an email address, or ldap url.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAlternateUriSet()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full principal url.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPrincipalUrl()
|
||||||
|
{
|
||||||
|
return $this->principalInfo['uri'].'/'.$this->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of group members.
|
||||||
|
*
|
||||||
|
* If this principal is a group, this function should return
|
||||||
|
* all member principal uri's for the group.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getGroupMemberSet()
|
||||||
|
{
|
||||||
|
return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of groups this principal is member of.
|
||||||
|
*
|
||||||
|
* If this principal is a member of a (list of) groups, this function
|
||||||
|
* should return a list of principal uri's for it's members.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getGroupMembership()
|
||||||
|
{
|
||||||
|
return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a list of group members.
|
||||||
|
*
|
||||||
|
* If this principal is a group, this method sets all the group members.
|
||||||
|
* The list of members is always overwritten, never appended to.
|
||||||
|
*
|
||||||
|
* This method should throw an exception if the members could not be set.
|
||||||
|
*/
|
||||||
|
public function setGroupMemberSet(array $principals)
|
||||||
|
{
|
||||||
|
$this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the displayname.
|
||||||
|
*
|
||||||
|
* This should be a human readable name for the principal.
|
||||||
|
* If none is available, return the nodename.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Principal;
|
||||||
|
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProxyWrite principal.
|
||||||
|
*
|
||||||
|
* This class represents a principal group, hosted under the main principal.
|
||||||
|
* This is needed to implement 'Calendar delegation' support. This class is
|
||||||
|
* instantiated by User.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class ProxyWrite implements IProxyWrite
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Parent principal information.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $principalInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Principal Backend.
|
||||||
|
*
|
||||||
|
* @var DAVACL\PrincipalBackend\BackendInterface
|
||||||
|
*/
|
||||||
|
protected $principalBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the object.
|
||||||
|
*
|
||||||
|
* Note that you MUST supply the parent principal information.
|
||||||
|
*/
|
||||||
|
public function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend, array $principalInfo)
|
||||||
|
{
|
||||||
|
$this->principalInfo = $principalInfo;
|
||||||
|
$this->principalBackend = $principalBackend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this principals name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'calendar-proxy-write';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last modification time.
|
||||||
|
*/
|
||||||
|
public function getLastModified()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the current node.
|
||||||
|
*
|
||||||
|
* @throws DAV\Exception\Forbidden
|
||||||
|
*/
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\Forbidden('Permission denied to delete node');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renames the node.
|
||||||
|
*
|
||||||
|
* @param string $name The new name
|
||||||
|
*
|
||||||
|
* @throws DAV\Exception\Forbidden
|
||||||
|
*/
|
||||||
|
public function setName($name)
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\Forbidden('Permission denied to rename file');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of alternative urls for a principal.
|
||||||
|
*
|
||||||
|
* This can for example be an email address, or ldap url.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAlternateUriSet()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full principal url.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPrincipalUrl()
|
||||||
|
{
|
||||||
|
return $this->principalInfo['uri'].'/'.$this->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of group members.
|
||||||
|
*
|
||||||
|
* If this principal is a group, this function should return
|
||||||
|
* all member principal uri's for the group.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getGroupMemberSet()
|
||||||
|
{
|
||||||
|
return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of groups this principal is member of.
|
||||||
|
*
|
||||||
|
* If this principal is a member of a (list of) groups, this function
|
||||||
|
* should return a list of principal uri's for it's members.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getGroupMembership()
|
||||||
|
{
|
||||||
|
return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a list of group members.
|
||||||
|
*
|
||||||
|
* If this principal is a group, this method sets all the group members.
|
||||||
|
* The list of members is always overwritten, never appended to.
|
||||||
|
*
|
||||||
|
* This method should throw an exception if the members could not be set.
|
||||||
|
*/
|
||||||
|
public function setGroupMemberSet(array $principals)
|
||||||
|
{
|
||||||
|
$this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the displayname.
|
||||||
|
*
|
||||||
|
* This should be a human readable name for the principal.
|
||||||
|
* If none is available, return the nodename.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Principal;
|
||||||
|
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CalDAV principal.
|
||||||
|
*
|
||||||
|
* This is a standard user-principal for CalDAV. This principal is also a
|
||||||
|
* collection and returns the caldav-proxy-read and caldav-proxy-write child
|
||||||
|
* principals.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class User extends DAVACL\Principal implements DAV\ICollection
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a new file in the directory.
|
||||||
|
*
|
||||||
|
* @param string $name Name of the file
|
||||||
|
* @param resource $data initial payload, passed as a readable stream resource
|
||||||
|
*
|
||||||
|
* @throws DAV\Exception\Forbidden
|
||||||
|
*/
|
||||||
|
public function createFile($name, $data = null)
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\Forbidden('Permission denied to create file (filename '.$name.')');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new subdirectory.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @throws DAV\Exception\Forbidden
|
||||||
|
*/
|
||||||
|
public function createDirectory($name)
|
||||||
|
{
|
||||||
|
throw new DAV\Exception\Forbidden('Permission denied to create directory');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a specific child node, referenced by its name.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return DAV\INode
|
||||||
|
*/
|
||||||
|
public function getChild($name)
|
||||||
|
{
|
||||||
|
$principal = $this->principalBackend->getPrincipalByPath($this->getPrincipalURL().'/'.$name);
|
||||||
|
if (!$principal) {
|
||||||
|
throw new DAV\Exception\NotFound('Node with name '.$name.' was not found');
|
||||||
|
}
|
||||||
|
if ('calendar-proxy-read' === $name) {
|
||||||
|
return new ProxyRead($this->principalBackend, $this->principalProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('calendar-proxy-write' === $name) {
|
||||||
|
return new ProxyWrite($this->principalBackend, $this->principalProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new DAV\Exception\NotFound('Node with name '.$name.' was not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with all the child nodes.
|
||||||
|
*
|
||||||
|
* @return DAV\INode[]
|
||||||
|
*/
|
||||||
|
public function getChildren()
|
||||||
|
{
|
||||||
|
$r = [];
|
||||||
|
if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL().'/calendar-proxy-read')) {
|
||||||
|
$r[] = new ProxyRead($this->principalBackend, $this->principalProperties);
|
||||||
|
}
|
||||||
|
if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL().'/calendar-proxy-write')) {
|
||||||
|
$r[] = new ProxyWrite($this->principalBackend, $this->principalProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the child node exists.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function childExists($name)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->getChild($name);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (DAV\Exception\NotFound $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of ACE's for this node.
|
||||||
|
*
|
||||||
|
* Each ACE has the following properties:
|
||||||
|
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||||
|
* currently the only supported privileges
|
||||||
|
* * 'principal', a url to the principal who owns the node
|
||||||
|
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||||
|
* be updated.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getACL()
|
||||||
|
{
|
||||||
|
$acl = parent::getACL();
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->principalProperties['uri'].'/calendar-proxy-read',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->principalProperties['uri'].'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $acl;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Schedule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this interface to have a node be recognized as a CalDAV scheduling
|
||||||
|
* inbox.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface IInbox extends \Sabre\CalDAV\ICalendarObjectContainer, \Sabre\DAVACL\IACL
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Schedule;
|
||||||
|
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\VObject\ITip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iMIP handler.
|
||||||
|
*
|
||||||
|
* This class is responsible for sending out iMIP messages. iMIP is the
|
||||||
|
* email-based transport for iTIP. iTIP deals with scheduling operations for
|
||||||
|
* iCalendar objects.
|
||||||
|
*
|
||||||
|
* If you want to customize the email that gets sent out, you can do so by
|
||||||
|
* extending this class and overriding the sendMessage method.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class IMipPlugin extends DAV\ServerPlugin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Email address used in From: header.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $senderEmail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the email handler.
|
||||||
|
*
|
||||||
|
* @param string $senderEmail. The 'senderEmail' is the email that shows up
|
||||||
|
* in the 'From:' address. This should
|
||||||
|
* generally be some kind of no-reply email
|
||||||
|
* address you own.
|
||||||
|
*/
|
||||||
|
public function __construct($senderEmail)
|
||||||
|
{
|
||||||
|
$this->senderEmail = $senderEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This initializes the plugin.
|
||||||
|
*
|
||||||
|
* This function is called by Sabre\DAV\Server, after
|
||||||
|
* addPlugin is called.
|
||||||
|
*
|
||||||
|
* This method should set up the required event subscriptions.
|
||||||
|
*
|
||||||
|
* @param DAV\Server $server
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function initialize(DAV\Server $server)
|
||||||
|
{
|
||||||
|
$server->on('schedule', [$this, 'schedule'], 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a plugin name.
|
||||||
|
*
|
||||||
|
* Using this name other plugins will be able to access other plugins
|
||||||
|
* using \Sabre\DAV\Server::getPlugin
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPluginName()
|
||||||
|
{
|
||||||
|
return 'imip';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler for the 'schedule' event.
|
||||||
|
*/
|
||||||
|
public function schedule(ITip\Message $iTipMessage)
|
||||||
|
{
|
||||||
|
// Not sending any emails if the system considers the update
|
||||||
|
// insignificant.
|
||||||
|
if (!$iTipMessage->significantChange) {
|
||||||
|
if (!$iTipMessage->scheduleStatus) {
|
||||||
|
$iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email';
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$summary = $iTipMessage->message->VEVENT->SUMMARY;
|
||||||
|
|
||||||
|
if ('mailto' !== parse_url($iTipMessage->sender, PHP_URL_SCHEME)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('mailto' !== parse_url($iTipMessage->recipient, PHP_URL_SCHEME)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sender = substr($iTipMessage->sender, 7);
|
||||||
|
$recipient = substr($iTipMessage->recipient, 7);
|
||||||
|
|
||||||
|
if ($iTipMessage->senderName) {
|
||||||
|
$sender = $iTipMessage->senderName.' <'.$sender.'>';
|
||||||
|
}
|
||||||
|
if ($iTipMessage->recipientName && $iTipMessage->recipientName != $recipient) {
|
||||||
|
$recipient = $iTipMessage->recipientName.' <'.$recipient.'>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$subject = 'SabreDAV iTIP message';
|
||||||
|
switch (strtoupper($iTipMessage->method)) {
|
||||||
|
case 'REPLY':
|
||||||
|
$subject = 'Re: '.$summary;
|
||||||
|
break;
|
||||||
|
case 'REQUEST':
|
||||||
|
$subject = 'Invitation: '.$summary;
|
||||||
|
break;
|
||||||
|
case 'CANCEL':
|
||||||
|
$subject = 'Cancelled: '.$summary;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'Reply-To: '.$sender,
|
||||||
|
'From: '.$iTipMessage->senderName.' <'.$this->senderEmail.'>',
|
||||||
|
'MIME-Version: 1.0',
|
||||||
|
'Content-Type: text/calendar; charset=UTF-8; method='.$iTipMessage->method,
|
||||||
|
];
|
||||||
|
if (DAV\Server::$exposeVersion) {
|
||||||
|
$headers[] = 'X-Sabre-Version: '.DAV\Version::VERSION;
|
||||||
|
}
|
||||||
|
$this->mail(
|
||||||
|
$recipient,
|
||||||
|
$subject,
|
||||||
|
$iTipMessage->message->serialize(),
|
||||||
|
$headers
|
||||||
|
);
|
||||||
|
$iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip';
|
||||||
|
}
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
// This is deemed untestable in a reasonable manner
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is responsible for sending the actual email.
|
||||||
|
*
|
||||||
|
* @param string $to Recipient email address
|
||||||
|
* @param string $subject Subject of the email
|
||||||
|
* @param string $body iCalendar body
|
||||||
|
* @param array $headers List of headers
|
||||||
|
*/
|
||||||
|
protected function mail($to, $subject, $body, array $headers)
|
||||||
|
{
|
||||||
|
mail($to, $subject, $body, implode("\r\n", $headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a bunch of meta-data about the plugin.
|
||||||
|
*
|
||||||
|
* Providing this information is optional, and is mainly displayed by the
|
||||||
|
* Browser plugin.
|
||||||
|
*
|
||||||
|
* The description key in the returned array may contain html and will not
|
||||||
|
* be sanitized.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getPluginInfo()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->getPluginName(),
|
||||||
|
'description' => 'Email delivery (rfc6047) for CalDAV scheduling',
|
||||||
|
'link' => 'http://sabre.io/dav/scheduling/',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Schedule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this interface to have a node be recognized as a CalDAV scheduling
|
||||||
|
* outbox.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
interface IOutbox extends \Sabre\DAV\ICollection, \Sabre\DAVACL\IACL
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Schedule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SchedulingObject represents a scheduling object in the Inbox collection.
|
||||||
|
*
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
*/
|
||||||
|
interface ISchedulingObject extends \Sabre\CalDAV\ICalendarObject
|
||||||
|
{
|
||||||
|
}
|
||||||
+198
@@ -0,0 +1,198 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Schedule;
|
||||||
|
|
||||||
|
use Sabre\CalDAV;
|
||||||
|
use Sabre\CalDAV\Backend;
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CalDAV scheduling inbox.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class Inbox extends DAV\Collection implements IInbox
|
||||||
|
{
|
||||||
|
use DAVACL\ACLTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CalDAV backend.
|
||||||
|
*
|
||||||
|
* @var Backend\BackendInterface
|
||||||
|
*/
|
||||||
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The principal Uri.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $principalUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
*/
|
||||||
|
public function __construct(Backend\SchedulingSupport $caldavBackend, $principalUri)
|
||||||
|
{
|
||||||
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
$this->principalUri = $principalUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the node.
|
||||||
|
*
|
||||||
|
* This is used to generate the url.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'inbox';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with all the child nodes.
|
||||||
|
*
|
||||||
|
* @return \Sabre\DAV\INode[]
|
||||||
|
*/
|
||||||
|
public function getChildren()
|
||||||
|
{
|
||||||
|
$objs = $this->caldavBackend->getSchedulingObjects($this->principalUri);
|
||||||
|
$children = [];
|
||||||
|
foreach ($objs as $obj) {
|
||||||
|
//$obj['acl'] = $this->getACL();
|
||||||
|
$obj['principaluri'] = $this->principalUri;
|
||||||
|
$children[] = new SchedulingObject($this->caldavBackend, $obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $children;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new file in the directory.
|
||||||
|
*
|
||||||
|
* Data will either be supplied as a stream resource, or in certain cases
|
||||||
|
* as a string. Keep in mind that you may have to support either.
|
||||||
|
*
|
||||||
|
* After successful creation of the file, you may choose to return the ETag
|
||||||
|
* of the new file here.
|
||||||
|
*
|
||||||
|
* The returned ETag must be surrounded by double-quotes (The quotes should
|
||||||
|
* be part of the actual string).
|
||||||
|
*
|
||||||
|
* If you cannot accurately determine the ETag, you should not return it.
|
||||||
|
* If you don't store the file exactly as-is (you're transforming it
|
||||||
|
* somehow) you should also not return an ETag.
|
||||||
|
*
|
||||||
|
* This means that if a subsequent GET to this new file does not exactly
|
||||||
|
* return the same contents of what was submitted here, you are strongly
|
||||||
|
* recommended to omit the ETag.
|
||||||
|
*
|
||||||
|
* @param string $name Name of the file
|
||||||
|
* @param resource|string $data Initial payload
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function createFile($name, $data = null)
|
||||||
|
{
|
||||||
|
$this->caldavBackend->createSchedulingObject($this->principalUri, $name, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the owner principal.
|
||||||
|
*
|
||||||
|
* This must be a url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getOwner()
|
||||||
|
{
|
||||||
|
return $this->principalUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of ACE's for this node.
|
||||||
|
*
|
||||||
|
* Each ACE has the following properties:
|
||||||
|
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||||
|
* currently the only supported privileges
|
||||||
|
* * 'principal', a url to the principal who owns the node
|
||||||
|
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||||
|
* be updated.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getACL()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => '{DAV:}authenticated',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}write-properties',
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}unbind',
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}unbind',
|
||||||
|
'principal' => $this->getOwner().'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-deliver',
|
||||||
|
'principal' => '{DAV:}authenticated',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a calendar-query on the contents of this calendar.
|
||||||
|
*
|
||||||
|
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||||
|
* calendar-query it is possible for a client to request a specific set of
|
||||||
|
* object, based on contents of iCalendar properties, date-ranges and
|
||||||
|
* iCalendar component types (VTODO, VEVENT).
|
||||||
|
*
|
||||||
|
* This method should just return a list of (relative) urls that match this
|
||||||
|
* query.
|
||||||
|
*
|
||||||
|
* The list of filters are specified as an array. The exact array is
|
||||||
|
* documented by \Sabre\CalDAV\CalendarQueryParser.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function calendarQuery(array $filters)
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
$validator = new CalDAV\CalendarQueryValidator();
|
||||||
|
|
||||||
|
$objects = $this->caldavBackend->getSchedulingObjects($this->principalUri);
|
||||||
|
foreach ($objects as $object) {
|
||||||
|
$vObject = VObject\Reader::read($object['calendardata']);
|
||||||
|
if ($validator->validate($vObject, $filters)) {
|
||||||
|
$result[] = $object['uri'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy circular references to PHP will GC the object.
|
||||||
|
$vObject->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Schedule;
|
||||||
|
|
||||||
|
use Sabre\CalDAV;
|
||||||
|
use Sabre\DAV;
|
||||||
|
use Sabre\DAVACL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CalDAV scheduling outbox.
|
||||||
|
*
|
||||||
|
* The outbox is mainly used as an endpoint in the tree for a client to do
|
||||||
|
* free-busy requests. This functionality is completely handled by the
|
||||||
|
* Scheduling plugin, so this object is actually mostly static.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class Outbox extends DAV\Collection implements IOutbox
|
||||||
|
{
|
||||||
|
use DAVACL\ACLTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The principal Uri.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $principalUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
*/
|
||||||
|
public function __construct($principalUri)
|
||||||
|
{
|
||||||
|
$this->principalUri = $principalUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the node.
|
||||||
|
*
|
||||||
|
* This is used to generate the url.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'outbox';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with all the child nodes.
|
||||||
|
*
|
||||||
|
* @return \Sabre\DAV\INode[]
|
||||||
|
*/
|
||||||
|
public function getChildren()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the owner principal.
|
||||||
|
*
|
||||||
|
* This must be a url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getOwner()
|
||||||
|
{
|
||||||
|
return $this->principalUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of ACE's for this node.
|
||||||
|
*
|
||||||
|
* Each ACE has the following properties:
|
||||||
|
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||||
|
* currently the only supported privileges
|
||||||
|
* * 'principal', a url to the principal who owns the node
|
||||||
|
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||||
|
* be updated.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getACL()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'privilege' => '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-send',
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-send',
|
||||||
|
'principal' => $this->getOwner().'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->getOwner().'/calendar-proxy-read',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->getOwner().'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
+1006
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,130 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV\Schedule;
|
||||||
|
|
||||||
|
use Sabre\CalDAV\Backend;
|
||||||
|
use Sabre\DAV\Exception\MethodNotAllowed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SchedulingObject represents a scheduling object in the Inbox collection.
|
||||||
|
*
|
||||||
|
* @author Brett (https://github.com/bretten)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
*/
|
||||||
|
class SchedulingObject extends \Sabre\CalDAV\CalendarObject implements ISchedulingObject
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* The following properties may be passed within $objectData:
|
||||||
|
*
|
||||||
|
* * uri - A unique uri. Only the 'basename' must be passed.
|
||||||
|
* * principaluri - the principal that owns the object.
|
||||||
|
* * calendardata (optional) - The iCalendar data
|
||||||
|
* * etag - (optional) The etag for this object, MUST be encloded with
|
||||||
|
* double-quotes.
|
||||||
|
* * size - (optional) The size of the data in bytes.
|
||||||
|
* * lastmodified - (optional) format as a unix timestamp.
|
||||||
|
* * acl - (optional) Use this to override the default ACL for the node.
|
||||||
|
*/
|
||||||
|
public function __construct(Backend\SchedulingSupport $caldavBackend, array $objectData)
|
||||||
|
{
|
||||||
|
parent::__construct($caldavBackend, [], $objectData);
|
||||||
|
|
||||||
|
if (!isset($objectData['uri'])) {
|
||||||
|
throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ICalendar-formatted object.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get()
|
||||||
|
{
|
||||||
|
// Pre-populating the 'calendardata' is optional, if we don't have it
|
||||||
|
// already we fetch it from the backend.
|
||||||
|
if (!isset($this->objectData['calendardata'])) {
|
||||||
|
$this->objectData = $this->caldavBackend->getSchedulingObject($this->objectData['principaluri'], $this->objectData['uri']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->objectData['calendardata'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the ICalendar-formatted object.
|
||||||
|
*
|
||||||
|
* @param string|resource $calendarData
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function put($calendarData)
|
||||||
|
{
|
||||||
|
throw new MethodNotAllowed('Updating scheduling objects is not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the scheduling message.
|
||||||
|
*/
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
$this->caldavBackend->deleteSchedulingObject($this->objectData['principaluri'], $this->objectData['uri']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the owner principal.
|
||||||
|
*
|
||||||
|
* This must be a url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getOwner()
|
||||||
|
{
|
||||||
|
return $this->objectData['principaluri'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of ACE's for this node.
|
||||||
|
*
|
||||||
|
* Each ACE has the following properties:
|
||||||
|
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||||
|
* currently the only supported privileges
|
||||||
|
* * 'principal', a url to the principal who owns the node
|
||||||
|
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||||
|
* be updated.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getACL()
|
||||||
|
{
|
||||||
|
// An alternative acl may be specified in the object data.
|
||||||
|
//
|
||||||
|
|
||||||
|
if (isset($this->objectData['acl'])) {
|
||||||
|
return $this->objectData['acl'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default ACL
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}all',
|
||||||
|
'principal' => '{DAV:}owner',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}all',
|
||||||
|
'principal' => $this->objectData['principaluri'].'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->objectData['principaluri'].'/calendar-proxy-read',
|
||||||
|
'protected' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
+219
@@ -0,0 +1,219 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Sabre\CalDAV;
|
||||||
|
|
||||||
|
use Sabre\DAV\Sharing\Plugin as SPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object represents a CalDAV calendar that is shared by a different user.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||||
|
* @author Evert Pot (http://evertpot.com/)
|
||||||
|
* @license http://sabre.io/license/ Modified BSD License
|
||||||
|
*/
|
||||||
|
class SharedCalendar extends Calendar implements ISharedCalendar
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns the 'access level' for the instance of this shared resource.
|
||||||
|
*
|
||||||
|
* The value should be one of the Sabre\DAV\Sharing\Plugin::ACCESS_
|
||||||
|
* constants.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getShareAccess()
|
||||||
|
{
|
||||||
|
return isset($this->calendarInfo['share-access']) ? $this->calendarInfo['share-access'] : SPlugin::ACCESS_NOTSHARED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function must return a URI that uniquely identifies the shared
|
||||||
|
* resource. This URI should be identical across instances, and is
|
||||||
|
* also used in several other XML bodies to connect invites to
|
||||||
|
* resources.
|
||||||
|
*
|
||||||
|
* This may simply be a relative reference to the original shared instance,
|
||||||
|
* but it could also be a urn. As long as it's a valid URI and unique.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getShareResourceUri()
|
||||||
|
{
|
||||||
|
return $this->calendarInfo['share-resource-uri'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the list of sharees.
|
||||||
|
*
|
||||||
|
* Every item must be a Sharee object.
|
||||||
|
*
|
||||||
|
* @param \Sabre\DAV\Xml\Element\Sharee[] $sharees
|
||||||
|
*/
|
||||||
|
public function updateInvites(array $sharees)
|
||||||
|
{
|
||||||
|
$this->caldavBackend->updateInvites($this->calendarInfo['id'], $sharees);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of people whom this resource is shared with.
|
||||||
|
*
|
||||||
|
* Every item in the returned array must be a Sharee object with
|
||||||
|
* at least the following properties set:
|
||||||
|
*
|
||||||
|
* * $href
|
||||||
|
* * $shareAccess
|
||||||
|
* * $inviteStatus
|
||||||
|
*
|
||||||
|
* and optionally:
|
||||||
|
*
|
||||||
|
* * $properties
|
||||||
|
*
|
||||||
|
* @return \Sabre\DAV\Xml\Element\Sharee[]
|
||||||
|
*/
|
||||||
|
public function getInvites()
|
||||||
|
{
|
||||||
|
return $this->caldavBackend->getInvites($this->calendarInfo['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this calendar as published.
|
||||||
|
*
|
||||||
|
* Publishing a calendar should automatically create a read-only, public,
|
||||||
|
* subscribable calendar.
|
||||||
|
*
|
||||||
|
* @param bool $value
|
||||||
|
*/
|
||||||
|
public function setPublishStatus($value)
|
||||||
|
{
|
||||||
|
$this->caldavBackend->setPublishStatus($this->calendarInfo['id'], $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of ACE's for this node.
|
||||||
|
*
|
||||||
|
* Each ACE has the following properties:
|
||||||
|
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||||
|
* currently the only supported privileges
|
||||||
|
* * 'principal', a url to the principal who owns the node
|
||||||
|
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||||
|
* be updated.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getACL()
|
||||||
|
{
|
||||||
|
$acl = [];
|
||||||
|
|
||||||
|
switch ($this->getShareAccess()) {
|
||||||
|
case SPlugin::ACCESS_NOTSHARED:
|
||||||
|
case SPlugin::ACCESS_SHAREDOWNER:
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}share',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'],
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}share',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
// no break intentional!
|
||||||
|
case SPlugin::ACCESS_READWRITE:
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'],
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
// no break intentional!
|
||||||
|
case SPlugin::ACCESS_READ:
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}write-properties',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'],
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}write-properties',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'],
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-read',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{'.Plugin::NS_CALDAV.'}read-free-busy',
|
||||||
|
'principal' => '{DAV:}authenticated',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $acl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the ACL's for calendar objects in this calendar.
|
||||||
|
* The result of this method automatically gets passed to the
|
||||||
|
* calendar-object nodes in the calendar.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getChildACL()
|
||||||
|
{
|
||||||
|
$acl = [];
|
||||||
|
|
||||||
|
switch ($this->getShareAccess()) {
|
||||||
|
case SPlugin::ACCESS_NOTSHARED:
|
||||||
|
case SPlugin::ACCESS_SHAREDOWNER:
|
||||||
|
case SPlugin::ACCESS_READWRITE:
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'],
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
// no break intentional
|
||||||
|
case SPlugin::ACCESS_READ:
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'],
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
$acl[] = [
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-read',
|
||||||
|
'protected' => true,
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $acl;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user