Compare commits

..

1 Commits

Author SHA1 Message Date
Ferdinand Thiessen 6f41f6a050 feat: provide server version as capability
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-19 17:59:45 +02:00
1410 changed files with 12800 additions and 39425 deletions
-7
View File
@@ -49,12 +49,5 @@ module.exports = {
'@typescript-eslint/no-explicit-any': 'warn',
},
},
{
files: ['*.vue'],
rules: {
'no-irregular-whitespace': 'off',
'vue/no-irregular-whitespace': 'error',
},
},
],
}
+4 -4
View File
@@ -52,13 +52,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
@@ -81,13 +81,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
@@ -31,7 +31,7 @@ jobs:
- 'version.php'
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
@@ -27,7 +27,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
build-mode: none
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
+1 -1
View File
@@ -103,7 +103,7 @@ jobs:
key: git-repo
- name: Checkout ${{ needs.init.outputs.head_ref }}
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
# Needed to allow force push later
persist-credentials: true
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
id: comment-branch
- name: Checkout ${{ steps.comment-branch.outputs.head_ref }}
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
fetch-depth: 0
+3 -3
View File
@@ -48,7 +48,7 @@ jobs:
exit 1
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
# We need to checkout submodules for 3rdparty
@@ -102,10 +102,10 @@ jobs:
matrix:
# Run multiple copies of the current job in parallel
# Please increase the number or runners as your tests suite grows (0 based index for e2e tests)
containers: ['component', 'setup', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
containers: ['component', 'setup', '0', '1', '2', '3', '4', '5', '6', '7']
# Hack as strategy.job-total includes the component and GitHub does not allow math expressions
# Always align this number with the total of e2e runners (max. index + 1)
total-containers: [10]
total-containers: [8]
services:
mysql:
+3 -3
View File
@@ -55,7 +55,7 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
@@ -71,7 +71,7 @@ jobs:
if [[ "${{ matrix.ftpd }}" == 'pure-ftpd' ]]; then docker run --name ftp -d --net host -e "PUBLICHOST=localhost" -e FTP_USER_NAME=test -e FTP_USER_PASS=test -e FTP_USER_HOME=/home/test -v /tmp/ftp:/home/test -v /tmp/ftp:/etc/pure-ftpd/passwd stilliard/pure-ftpd; fi
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -104,7 +104,7 @@ jobs:
- name: Upload code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.xml
flags: phpunit-files-external-ftp
+6 -6
View File
@@ -64,13 +64,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -104,7 +104,7 @@ jobs:
- name: Upload code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.xml
flags: phpunit-files-external-s3
@@ -152,13 +152,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -185,7 +185,7 @@ jobs:
- name: Upload code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.xml
flags: phpunit-files-external-s3
+3 -3
View File
@@ -55,7 +55,7 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
@@ -67,7 +67,7 @@ jobs:
if [[ '${{ matrix.sftpd }}' == 'openssh' ]]; then docker run -p 2222:22 --name sftp -d -v /tmp/sftp:/home/test atmoz/sftp 'test:test:::data'; fi
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -93,7 +93,7 @@ jobs:
- name: Upload code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.xml
flags: phpunit-files-external-sftp
@@ -46,13 +46,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Checkout user_saml
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
repository: nextcloud/user_saml
+3 -3
View File
@@ -60,13 +60,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -100,7 +100,7 @@ jobs:
- name: Upload code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.xml
flags: phpunit-files-external-smb
+3 -3
View File
@@ -60,13 +60,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -97,7 +97,7 @@ jobs:
- name: Upload code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.xml
flags: phpunit-files-external-webdav
+3 -3
View File
@@ -53,13 +53,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -85,7 +85,7 @@ jobs:
- name: Upload code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.xml
flags: phpunit-files-external-generic
@@ -24,14 +24,14 @@ jobs:
require: write
- name: Checkout github_helper
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
repository: nextcloud/github_helper
path: github_helper
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
path: server
@@ -47,13 +47,8 @@ jobs:
TAGS=$(git log --decorate --oneline | egrep 'tag: ' | sed -r 's/^.+tag: ([^,\)]+)[,\)].+$/\1/g')
CURRENT_TAG=$(echo "$TAGS" | head -n 1)
# If current tag is the first beta, we use the previous major RC1
if echo "$CURRENT_TAG" | grep -q 'beta1'; then
MAJOR=$(echo "$CURRENT_TAG" | sed -E 's/^v([0-9]+).*/\1/')
PREV=$((MAJOR - 1))
PREVIOUS_TAG="v${PREV}.0.0rc1"
# Get the previous tag - filter pre-releases only if current tag is stable
elif echo "$CURRENT_TAG" | grep -q 'rc\|beta\|alpha'; then
if echo "$CURRENT_TAG" | grep -q 'rc\|beta\|alpha'; then
# Current tag is pre-release, don't filter
PREVIOUS_TAG=$(echo "$TAGS" | sed -n '2p')
else
@@ -73,7 +68,7 @@ jobs:
fi
- name: Set up php 8.2
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 # v2.35.2
with:
php-version: 8.2
coverage: none
+3 -3
View File
@@ -53,13 +53,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -70,7 +70,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Python
uses: LizardByte/actions/actions/setup_python@bff0a193747a3ac7930a665fc1d4b23eba583b99 # v2025.814.40518
uses: LizardByte/actions/actions/setup_python@eddc8fc8b27048e25040e37e3585bd3ef9a968ed # v2025.715.25226
with:
python-version: '2.7'
+2 -2
View File
@@ -52,13 +52,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
+2 -2
View File
@@ -67,13 +67,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
+4 -4
View File
@@ -95,14 +95,14 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Checkout Talk app
if: ${{ matrix.test-suite == 'videoverification_features' }}
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
repository: nextcloud/spreed
@@ -111,7 +111,7 @@ jobs:
- name: Checkout Activity app
if: ${{ matrix.test-suite == 'sharing_features' }}
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
repository: nextcloud/activity
@@ -119,7 +119,7 @@ jobs:
ref: ${{ matrix.activity-versions }}
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
+1 -1
View File
@@ -56,7 +56,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
+2 -2
View File
@@ -48,12 +48,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Set up php8.1
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 # v2.35.2
with:
php-version: 8.1
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
+2 -2
View File
@@ -53,12 +53,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 # v2.35.2
with:
php-version: ${{ matrix.php-versions }}
coverage: none
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
+5 -5
View File
@@ -62,7 +62,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
@@ -85,7 +85,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
@@ -106,7 +106,7 @@ jobs:
run: npm run test:coverage --if-present
- name: Collect coverage
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./coverage/lcov.info
@@ -125,7 +125,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
@@ -155,7 +155,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
+1 -1
View File
@@ -56,7 +56,7 @@ jobs:
name: NPM build
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
ref: ${{ matrix.branches }}
+3 -3
View File
@@ -73,13 +73,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -110,7 +110,7 @@ jobs:
- name: Upload code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.xml
flags: phpunit-azure
+3 -3
View File
@@ -74,13 +74,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -116,7 +116,7 @@ jobs:
- name: Upload code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.xml
flags: phpunit-s3
+3 -3
View File
@@ -71,13 +71,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -106,7 +106,7 @@ jobs:
- name: Upload code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.xml
flags: phpunit-swift
+2 -2
View File
@@ -26,12 +26,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Set up php
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 # v2.35.2
with:
php-version: '8.1'
extensions: ctype, curl, dom, fileinfo, gd, json, libxml, mbstring, openssl, pcntl, pdo, posix, session, simplexml, xml, xmlreader, xmlwriter, zip, zlib
+2 -2
View File
@@ -35,14 +35,14 @@ jobs:
exit 1
- name: Checkout server before PR
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
ref: ${{ github.event.pull_request.base.ref }}
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 # v2.35.2
with:
php-version: ${{ matrix.php-versions }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
+2 -2
View File
@@ -34,7 +34,7 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
@@ -45,7 +45,7 @@ jobs:
sudo apt-get install -y ffmpeg imagemagick libmagickcore-6.q16-3-extra
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, imagick, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite, apcu, ldap
+3 -3
View File
@@ -90,13 +90,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 # v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -129,7 +129,7 @@ jobs:
- name: Upload db code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.db.xml
flags: phpunit-mariadb
+3 -3
View File
@@ -72,13 +72,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -103,7 +103,7 @@ jobs:
- name: Upload code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.xml
flags: phpunit-memcached
+3 -3
View File
@@ -121,13 +121,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -161,7 +161,7 @@ jobs:
- name: Upload db code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.db.xml
flags: phpunit-mysql
+3 -3
View File
@@ -90,13 +90,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 # v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -129,7 +129,7 @@ jobs:
- name: Upload db code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.db.xml
flags: phpunit-mysql
+3 -3
View File
@@ -75,13 +75,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -109,7 +109,7 @@ jobs:
- name: Upload nodb code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.nodb.xml
flags: phpunit-nodb
@@ -72,13 +72,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: ${{ matrix.php-versions }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
+3 -3
View File
@@ -101,13 +101,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 # v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -133,7 +133,7 @@ jobs:
- name: Upload db code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.db.xml
flags: phpunit-oci
+3 -3
View File
@@ -90,13 +90,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 # v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -124,7 +124,7 @@ jobs:
- name: Upload db code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.db.xml
flags: phpunit-postgres
+3 -3
View File
@@ -75,13 +75,13 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 # v2.35.2
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
@@ -110,7 +110,7 @@ jobs:
- name: Upload db code coverage
if: ${{ !cancelled() && matrix.coverage }}
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: ./clover.db.xml
flags: phpunit-sqlite
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest-low
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
+8 -8
View File
@@ -28,13 +28,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: true
- name: Set up php
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: '8.1'
extensions: apcu,ctype,curl,dom,fileinfo,ftp,gd,imagick,intl,json,ldap,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip
@@ -59,13 +59,13 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: true
- name: Set up php
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: '8.1'
extensions: ctype,curl,dom,fileinfo,ftp,gd,imagick,intl,json,ldap,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip
@@ -94,13 +94,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: true
- name: Set up php
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: '8.1'
extensions: ctype,curl,dom,fileinfo,gd,imagick,intl,json,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip
@@ -125,13 +125,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: true
- name: Set up php
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 #v2.35.4
uses: shivammathur/setup-php@ccf2c627fe61b1b4d924adfcbd19d661a18133a0 #v2.35.2
with:
php-version: '8.1'
extensions: ctype,curl,dom,fileinfo,gd,imagick,intl,json,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
name: update-ca-certificate-bundle-${{ matrix.branches }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
ref: ${{ matrix.branches }}
@@ -22,7 +22,7 @@ jobs:
name: update-code-signing-crl-${{ matrix.branches }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
ref: ${{ matrix.branches }}
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest-low
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
+1
View File
@@ -60,6 +60,7 @@
</IfModule>
<IfModule mod_php.c>
php_value mbstring.func_overload 0
php_value default_charset 'UTF-8'
php_value output_buffering 0
<IfModule mod_env.c>
+1 -3
View File
@@ -1,9 +1,7 @@
; SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
; SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
; SPDX-License-Identifier: AGPL-3.0-only
;
; NOTE: PHP caches this file for 300 seconds by default
;
mbstring.func_overload=0
always_populate_raw_post_data=-1
default_charset='UTF-8'
output_buffering=0
+129 -3
View File
File diff suppressed because one or more lines are too long
-1
View File
@@ -14,5 +14,4 @@ export default {
get: async () => ({ status: 200, data: {} }),
delete: async () => ({ status: 200, data: {} }),
post: async () => ({ status: 200, data: {} }),
head: async () => ({ status: 200, data: {} }),
}
+1 -7
View File
@@ -2,13 +2,7 @@
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
window.OC = {
...window.OC,
config: {
version: '32.0.0',
...(window.OC?.config ?? {}),
},
}
window.OC = { ...window.OC }
window.OCA = { ...window.OCA }
window.OCP = { ...window.OCP }
+49 -4
View File
@@ -9,14 +9,26 @@ declare(strict_types=1);
namespace OCA\CloudFederationAPI;
use OC\OCM\OCMDiscoveryService;
use NCU\Security\Signature\Exceptions\IdentityNotFoundException;
use NCU\Security\Signature\Exceptions\SignatoryException;
use OC\OCM\OCMSignatoryManager;
use OCP\Capabilities\ICapability;
use OCP\Capabilities\IInitialStateExcludedCapability;
use OCP\IAppConfig;
use OCP\IURLGenerator;
use OCP\OCM\Exceptions\OCMArgumentException;
use OCP\OCM\ICapabilityAwareOCMProvider;
use Psr\Log\LoggerInterface;
class Capabilities implements ICapability, IInitialStateExcludedCapability {
public const API_VERSION = '1.1.0';
public function __construct(
private readonly OCMDiscoveryService $ocmDiscoveryService,
private IURLGenerator $urlGenerator,
private IAppConfig $appConfig,
private ICapabilityAwareOCMProvider $provider,
private readonly OCMSignatoryManager $ocmSignatoryManager,
private readonly LoggerInterface $logger,
) {
}
@@ -27,7 +39,40 @@ class Capabilities implements ICapability, IInitialStateExcludedCapability {
* @throws OCMArgumentException
*/
public function getCapabilities() {
$provider = $this->ocmDiscoveryService->getLocalOCMProvider(false);
return ['ocm' => $provider->jsonSerialize()];
$url = $this->urlGenerator->linkToRouteAbsolute('cloud_federation_api.requesthandlercontroller.addShare');
$pos = strrpos($url, '/');
if ($pos === false) {
throw new OCMArgumentException('generated route should contain a slash character');
}
$this->provider->setEnabled(true);
$this->provider->setApiVersion(self::API_VERSION);
$this->provider->setCapabilities(['/invite-accepted', '/notifications', '/shares']);
$this->provider->setEndPoint(substr($url, 0, $pos));
$resource = $this->provider->createNewResourceType();
$resource->setName('file')
->setShareTypes(['user', 'group'])
->setProtocols(['webdav' => '/public.php/webdav/']);
$this->provider->addResourceType($resource);
// Adding a public key to the ocm discovery
try {
if (!$this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_DISABLED, lazy: true)) {
/**
* @experimental 31.0.0
* @psalm-suppress UndefinedInterfaceMethod
*/
$this->provider->setSignatory($this->ocmSignatoryManager->getLocalSignatory());
} else {
$this->logger->debug('ocm public key feature disabled');
}
} catch (SignatoryException|IdentityNotFoundException $e) {
$this->logger->warning('cannot generate local signatory', ['exception' => $e]);
}
return ['ocm' => $this->provider->jsonSerialize()];
}
}
-36
View File
@@ -1,36 +0,0 @@
OC.L10N.register(
"comments",
{
"Comments" : "Каментарыі",
"You commented" : "Вы пракаментавалі",
"{author} commented" : "{author} пракаментаваў(-ла)",
"You commented on %1$s" : "Вы пракаментавалі %1$s",
"You commented on {file}" : "Вы пракаментавалі {file}",
"%1$s commented on %2$s" : "%1$s пракаментаваў(-ла) %2$s",
"{author} commented on {file}" : "{author} пракаментаваў(-ла) {file}",
"<strong>Comments</strong> for files" : "<strong>Каментарыі</strong> да файлаў",
"Files" : "Файлы",
"You were mentioned on \"{file}\", in a comment by an account that has since been deleted" : "Вас згадалі ў каментарыі да \"{file}\" з уліковага запісу, які пазней быў выдалены.",
"{user} mentioned you in a comment on \"{file}\"" : "{user} згадаў(-ла) вас у каментарыі да \"{file}\"",
"Files app plugin to add comments to files" : "Убудова праграмы Файлы для дадавання каментарыяў да файлаў",
"Edit comment" : "Рэдагаваць каментарый",
"Delete comment" : "Выдаліць каментарый",
"Cancel edit" : "Скасаваць рэдагаванне",
"New comment" : "Новы каментарый",
"Write a comment …" : "Напішыце каментарый …",
"Post comment" : "Апублікаваць каментарый",
"@ for mentions, : for emoji, / for smart picker" : "@ - згадкі, : - эмодзі, / - разумны выбар",
"Could not reload comments" : "Не ўдалося перазагрузіць каментарыі",
"Failed to mark comments as read" : "Не атрымалася пазначыць каментарыі як прачытаныя",
"Unable to load the comments list" : "Не ўдалося загрузіць спіс каментарыяў",
"No comments yet, start the conversation!" : "Пакуль няма каментарыяў, пачніце размову!",
"No more messages" : "Больш паведамленняў няма",
"Retry" : "Паўтарыць спробу",
"_1 new comment_::_{unread} new comments_" : ["1 новы каментарый","{unread} новыя каментарыі","{unread} новых каментарыяў","{unread} новых каментарыяў"],
"Comment" : "Каментарый",
"An error occurred while trying to edit the comment" : "Падчас спробы рэдагавання каментарыя ўзнікла памылка",
"Comment deleted" : "Каментарый выдалены",
"An error occurred while trying to delete the comment" : "Падчас спробы выдалення каментарыя ўзнікла памылка",
"An error occurred while trying to create the comment" : "Падчас спробы стварэння каментарыя ўзнікла памылка"
},
"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);");
-34
View File
@@ -1,34 +0,0 @@
{ "translations": {
"Comments" : "Каментарыі",
"You commented" : "Вы пракаментавалі",
"{author} commented" : "{author} пракаментаваў(-ла)",
"You commented on %1$s" : "Вы пракаментавалі %1$s",
"You commented on {file}" : "Вы пракаментавалі {file}",
"%1$s commented on %2$s" : "%1$s пракаментаваў(-ла) %2$s",
"{author} commented on {file}" : "{author} пракаментаваў(-ла) {file}",
"<strong>Comments</strong> for files" : "<strong>Каментарыі</strong> да файлаў",
"Files" : "Файлы",
"You were mentioned on \"{file}\", in a comment by an account that has since been deleted" : "Вас згадалі ў каментарыі да \"{file}\" з уліковага запісу, які пазней быў выдалены.",
"{user} mentioned you in a comment on \"{file}\"" : "{user} згадаў(-ла) вас у каментарыі да \"{file}\"",
"Files app plugin to add comments to files" : "Убудова праграмы Файлы для дадавання каментарыяў да файлаў",
"Edit comment" : "Рэдагаваць каментарый",
"Delete comment" : "Выдаліць каментарый",
"Cancel edit" : "Скасаваць рэдагаванне",
"New comment" : "Новы каментарый",
"Write a comment …" : "Напішыце каментарый …",
"Post comment" : "Апублікаваць каментарый",
"@ for mentions, : for emoji, / for smart picker" : "@ - згадкі, : - эмодзі, / - разумны выбар",
"Could not reload comments" : "Не ўдалося перазагрузіць каментарыі",
"Failed to mark comments as read" : "Не атрымалася пазначыць каментарыі як прачытаныя",
"Unable to load the comments list" : "Не ўдалося загрузіць спіс каментарыяў",
"No comments yet, start the conversation!" : "Пакуль няма каментарыяў, пачніце размову!",
"No more messages" : "Больш паведамленняў няма",
"Retry" : "Паўтарыць спробу",
"_1 new comment_::_{unread} new comments_" : ["1 новы каментарый","{unread} новыя каментарыі","{unread} новых каментарыяў","{unread} новых каментарыяў"],
"Comment" : "Каментарый",
"An error occurred while trying to edit the comment" : "Падчас спробы рэдагавання каментарыя ўзнікла памылка",
"Comment deleted" : "Каментарый выдалены",
"An error occurred while trying to delete the comment" : "Падчас спробы выдалення каментарыя ўзнікла памылка",
"An error occurred while trying to create the comment" : "Падчас спробы стварэння каментарыя ўзнікла памылка"
},"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"
}
@@ -16,6 +16,5 @@ return array(
'OCA\\ContactsInteraction\\Db\\RecentContact' => $baseDir . '/../lib/Db/RecentContact.php',
'OCA\\ContactsInteraction\\Db\\RecentContactMapper' => $baseDir . '/../lib/Db/RecentContactMapper.php',
'OCA\\ContactsInteraction\\Listeners\\ContactInteractionListener' => $baseDir . '/../lib/Listeners/ContactInteractionListener.php',
'OCA\\ContactsInteraction\\Listeners\\UserDeletedListener' => $baseDir . '/../lib/Listeners/UserDeletedListener.php',
'OCA\\ContactsInteraction\\Migration\\Version010000Date20200304152605' => $baseDir . '/../lib/Migration/Version010000Date20200304152605.php',
);
@@ -31,7 +31,6 @@ class ComposerStaticInitContactsInteraction
'OCA\\ContactsInteraction\\Db\\RecentContact' => __DIR__ . '/..' . '/../lib/Db/RecentContact.php',
'OCA\\ContactsInteraction\\Db\\RecentContactMapper' => __DIR__ . '/..' . '/../lib/Db/RecentContactMapper.php',
'OCA\\ContactsInteraction\\Listeners\\ContactInteractionListener' => __DIR__ . '/..' . '/../lib/Listeners/ContactInteractionListener.php',
'OCA\\ContactsInteraction\\Listeners\\UserDeletedListener' => __DIR__ . '/..' . '/../lib/Listeners/UserDeletedListener.php',
'OCA\\ContactsInteraction\\Migration\\Version010000Date20200304152605' => __DIR__ . '/..' . '/../lib/Migration/Version010000Date20200304152605.php',
);
@@ -9,13 +9,11 @@ declare(strict_types=1);
namespace OCA\ContactsInteraction\AppInfo;
use OCA\ContactsInteraction\Listeners\ContactInteractionListener;
use OCA\ContactsInteraction\Listeners\UserDeletedListener;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Contacts\Events\ContactInteractedWithEvent;
use OCP\User\Events\UserDeletedEvent;
class Application extends App implements IBootstrap {
public const APP_ID = 'contactsinteraction';
@@ -26,7 +24,6 @@ class Application extends App implements IBootstrap {
public function register(IRegistrationContext $context): void {
$context->registerEventListener(ContactInteractedWithEvent::class, ContactInteractionListener::class);
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
}
public function boot(IBootContext $context): void {
@@ -112,14 +112,4 @@ class RecentContactMapper extends QBMapper {
$delete->executeStatement();
}
public function deleteByUserId(string $uid): void {
$qb = $this->db->getQueryBuilder();
$delete = $qb
->delete($this->getTableName())
->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
$delete->executeStatement();
}
}
@@ -1,35 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\ContactsInteraction\Listeners;
use OCA\ContactsInteraction\Db\RecentContactMapper;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\User\Events\UserDeletedEvent;
/**
* @template-implements IEventListener<Event|UserDeletedEvent>
*/
class UserDeletedListener implements IEventListener {
public function __construct(
private readonly RecentContactMapper $recentContactMapper,
) {
}
#[\Override]
public function handle(Event $event): void {
if (!($event instanceof UserDeletedEvent)) {
return;
}
$this->recentContactMapper->deleteByUserId($event->getUser()->getUID());
}
}
-196
View File
@@ -267,34 +267,6 @@
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
@@ -391,34 +363,6 @@
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
@@ -483,34 +427,6 @@
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
@@ -583,34 +499,6 @@
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
},
@@ -703,34 +591,6 @@
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
@@ -803,34 +663,6 @@
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
},
@@ -923,34 +755,6 @@
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
+1 -3
View File
@@ -10,7 +10,7 @@
<name>WebDAV</name>
<summary>WebDAV endpoint</summary>
<description>WebDAV endpoint</description>
<version>1.34.2</version>
<version>1.34.1</version>
<licence>agpl</licence>
<author>owncloud.org</author>
<namespace>DAV</namespace>
@@ -30,7 +30,6 @@
<job>OCA\DAV\BackgroundJob\EventReminderJob</job>
<job>OCA\DAV\BackgroundJob\CalendarRetentionJob</job>
<job>OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob</job>
<job>OCA\DAV\BackgroundJob\FederatedCalendarPeriodicSyncJob</job>
</background-jobs>
<repair-steps>
@@ -67,7 +66,6 @@
<command>OCA\DAV\Command\ExportCalendar</command>
<command>OCA\DAV\Command\FixCalendarSyncCommand</command>
<command>OCA\DAV\Command\GetAbsenceCommand</command>
<command>OCA\DAV\Command\ImportCalendar</command>
<command>OCA\DAV\Command\ListAddressbooks</command>
<command>OCA\DAV\Command\ListCalendarShares</command>
<command>OCA\DAV\Command\ListCalendars</command>
+2 -10
View File
@@ -10,8 +10,6 @@ use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\CalendarRoot;
use OCA\DAV\CalDAV\DefaultCalendarValidator;
use OCA\DAV\CalDAV\Federation\FederatedCalendarFactory;
use OCA\DAV\CalDAV\Federation\FederatedCalendarMapper;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\CalDAV\Schedule\IMipPlugin;
use OCA\DAV\CalDAV\Security\RateLimitingPlugin;
@@ -31,7 +29,6 @@ use OCP\IRequest;
use OCP\ISession;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\L10N\IFactory as IL10NFactory;
use OCP\Security\Bruteforce\IThrottler;
use OCP\Security\ISecureRandom;
use OCP\Server;
@@ -64,9 +61,6 @@ $random = Server::get(ISecureRandom::class);
$logger = Server::get(LoggerInterface::class);
$dispatcher = Server::get(IEventDispatcher::class);
$config = Server::get(IConfig::class);
$l10nFactory = Server::get(IL10NFactory::class);
$davL10n = $l10nFactory->get('dav');
$federatedCalendarFactory = Server::get(FederatedCalendarFactory::class);
$calDavBackend = new CalDavBackend(
$db,
@@ -77,8 +71,6 @@ $calDavBackend = new CalDavBackend(
$dispatcher,
$config,
Server::get(\OCA\DAV\CalDAV\Sharing\Backend::class),
Server::get(FederatedCalendarMapper::class),
Server::get(\OCP\ICacheFactory::class),
true
);
@@ -89,7 +81,7 @@ $sendInvitations = Server::get(IConfig::class)->getAppValue('dav', 'sendInvitati
$principalCollection = new \Sabre\CalDAV\Principal\Collection($principalBackend);
$principalCollection->disableListing = !$debugging; // Disable listing
$addressBookRoot = new CalendarRoot($principalBackend, $calDavBackend, 'principals', $logger, $davL10n, $config, $federatedCalendarFactory);
$addressBookRoot = new CalendarRoot($principalBackend, $calDavBackend, 'principals', $logger);
$addressBookRoot->disableListing = !$debugging; // Disable listing
$nodes = [
@@ -104,7 +96,7 @@ $server->httpRequest->setUrl(Server::get(IRequest::class)->getRequestUri());
$server->setBaseUri($baseuri);
// Add plugins
$server->addPlugin(new MaintenancePlugin(Server::get(IConfig::class), $davL10n));
$server->addPlugin(new MaintenancePlugin(Server::get(IConfig::class), \OC::$server->getL10N('dav')));
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend));
$server->addPlugin(new \Sabre\CalDAV\Plugin());
@@ -19,8 +19,6 @@ return array(
'OCA\\DAV\\BackgroundJob\\CleanupOrphanedChildrenJob' => $baseDir . '/../lib/BackgroundJob/CleanupOrphanedChildrenJob.php',
'OCA\\DAV\\BackgroundJob\\DeleteOutdatedSchedulingObjects' => $baseDir . '/../lib/BackgroundJob/DeleteOutdatedSchedulingObjects.php',
'OCA\\DAV\\BackgroundJob\\EventReminderJob' => $baseDir . '/../lib/BackgroundJob/EventReminderJob.php',
'OCA\\DAV\\BackgroundJob\\FederatedCalendarPeriodicSyncJob' => $baseDir . '/../lib/BackgroundJob/FederatedCalendarPeriodicSyncJob.php',
'OCA\\DAV\\BackgroundJob\\FederatedCalendarSyncJob' => $baseDir . '/../lib/BackgroundJob/FederatedCalendarSyncJob.php',
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => $baseDir . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
'OCA\\DAV\\BackgroundJob\\OutOfOfficeEventDispatcherJob' => $baseDir . '/../lib/BackgroundJob/OutOfOfficeEventDispatcherJob.php',
'OCA\\DAV\\BackgroundJob\\PruneOutdatedSyncTokensJob' => $baseDir . '/../lib/BackgroundJob/PruneOutdatedSyncTokensJob.php',
@@ -68,27 +66,9 @@ return array(
'OCA\\DAV\\CalDAV\\EventReaderRDate' => $baseDir . '/../lib/CalDAV/EventReaderRDate.php',
'OCA\\DAV\\CalDAV\\EventReaderRRule' => $baseDir . '/../lib/CalDAV/EventReaderRRule.php',
'OCA\\DAV\\CalDAV\\Export\\ExportService' => $baseDir . '/../lib/CalDAV/Export/ExportService.php',
'OCA\\DAV\\CalDAV\\Federation\\CalendarFederationConfig' => $baseDir . '/../lib/CalDAV/Federation/CalendarFederationConfig.php',
'OCA\\DAV\\CalDAV\\Federation\\CalendarFederationNotifier' => $baseDir . '/../lib/CalDAV/Federation/CalendarFederationNotifier.php',
'OCA\\DAV\\CalDAV\\Federation\\CalendarFederationProvider' => $baseDir . '/../lib/CalDAV/Federation/CalendarFederationProvider.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendar' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendar.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarAuth' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendarAuth.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarEntity' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendarEntity.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarFactory' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendarFactory.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarImpl' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendarImpl.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarMapper' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendarMapper.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarSyncService' => $baseDir . '/../lib/CalDAV/Federation/FederatedCalendarSyncService.php',
'OCA\\DAV\\CalDAV\\Federation\\FederationSharingService' => $baseDir . '/../lib/CalDAV/Federation/FederationSharingService.php',
'OCA\\DAV\\CalDAV\\Federation\\Protocol\\CalendarFederationProtocolV1' => $baseDir . '/../lib/CalDAV/Federation/Protocol/CalendarFederationProtocolV1.php',
'OCA\\DAV\\CalDAV\\Federation\\Protocol\\CalendarProtocolParseException' => $baseDir . '/../lib/CalDAV/Federation/Protocol/CalendarProtocolParseException.php',
'OCA\\DAV\\CalDAV\\Federation\\Protocol\\ICalendarFederationProtocol' => $baseDir . '/../lib/CalDAV/Federation/Protocol/ICalendarFederationProtocol.php',
'OCA\\DAV\\CalDAV\\Federation\\RemoteUserCalendarHome' => $baseDir . '/../lib/CalDAV/Federation/RemoteUserCalendarHome.php',
'OCA\\DAV\\CalDAV\\FreeBusy\\FreeBusyGenerator' => $baseDir . '/../lib/CalDAV/FreeBusy/FreeBusyGenerator.php',
'OCA\\DAV\\CalDAV\\ICSExportPlugin\\ICSExportPlugin' => $baseDir . '/../lib/CalDAV/ICSExportPlugin/ICSExportPlugin.php',
'OCA\\DAV\\CalDAV\\IRestorable' => $baseDir . '/../lib/CalDAV/IRestorable.php',
'OCA\\DAV\\CalDAV\\Import\\ImportService' => $baseDir . '/../lib/CalDAV/Import/ImportService.php',
'OCA\\DAV\\CalDAV\\Import\\TextImporter' => $baseDir . '/../lib/CalDAV/Import/TextImporter.php',
'OCA\\DAV\\CalDAV\\Import\\XmlImporter' => $baseDir . '/../lib/CalDAV/Import/XmlImporter.php',
'OCA\\DAV\\CalDAV\\Integration\\ExternalCalendar' => $baseDir . '/../lib/CalDAV/Integration/ExternalCalendar.php',
'OCA\\DAV\\CalDAV\\Integration\\ICalendarProvider' => $baseDir . '/../lib/CalDAV/Integration/ICalendarProvider.php',
'OCA\\DAV\\CalDAV\\InvitationResponse\\InvitationResponseServer' => $baseDir . '/../lib/CalDAV/InvitationResponse/InvitationResponseServer.php',
@@ -133,8 +113,6 @@ return array(
'OCA\\DAV\\CalDAV\\Sharing\\Backend' => $baseDir . '/../lib/CalDAV/Sharing/Backend.php',
'OCA\\DAV\\CalDAV\\Sharing\\Service' => $baseDir . '/../lib/CalDAV/Sharing/Service.php',
'OCA\\DAV\\CalDAV\\Status\\StatusService' => $baseDir . '/../lib/CalDAV/Status/StatusService.php',
'OCA\\DAV\\CalDAV\\SyncService' => $baseDir . '/../lib/CalDAV/SyncService.php',
'OCA\\DAV\\CalDAV\\SyncServiceResult' => $baseDir . '/../lib/CalDAV/SyncServiceResult.php',
'OCA\\DAV\\CalDAV\\TimeZoneFactory' => $baseDir . '/../lib/CalDAV/TimeZoneFactory.php',
'OCA\\DAV\\CalDAV\\TimezoneService' => $baseDir . '/../lib/CalDAV/TimezoneService.php',
'OCA\\DAV\\CalDAV\\TipBroker' => $baseDir . '/../lib/CalDAV/TipBroker.php',
@@ -188,7 +166,6 @@ return array(
'OCA\\DAV\\Command\\ExportCalendar' => $baseDir . '/../lib/Command/ExportCalendar.php',
'OCA\\DAV\\Command\\FixCalendarSyncCommand' => $baseDir . '/../lib/Command/FixCalendarSyncCommand.php',
'OCA\\DAV\\Command\\GetAbsenceCommand' => $baseDir . '/../lib/Command/GetAbsenceCommand.php',
'OCA\\DAV\\Command\\ImportCalendar' => $baseDir . '/../lib/Command/ImportCalendar.php',
'OCA\\DAV\\Command\\ListAddressbooks' => $baseDir . '/../lib/Command/ListAddressbooks.php',
'OCA\\DAV\\Command\\ListCalendarShares' => $baseDir . '/../lib/Command/ListCalendarShares.php',
'OCA\\DAV\\Command\\ListCalendars' => $baseDir . '/../lib/Command/ListCalendars.php',
@@ -262,7 +239,6 @@ return array(
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => $baseDir . '/../lib/DAV/CustomPropertiesBackend.php',
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => $baseDir . '/../lib/DAV/GroupPrincipalBackend.php',
'OCA\\DAV\\DAV\\PublicAuth' => $baseDir . '/../lib/DAV/PublicAuth.php',
'OCA\\DAV\\DAV\\RemoteUserPrincipalBackend' => $baseDir . '/../lib/DAV/RemoteUserPrincipalBackend.php',
'OCA\\DAV\\DAV\\Sharing\\Backend' => $baseDir . '/../lib/DAV/Sharing/Backend.php',
'OCA\\DAV\\DAV\\Sharing\\IShareable' => $baseDir . '/../lib/DAV/Sharing/IShareable.php',
'OCA\\DAV\\DAV\\Sharing\\Plugin' => $baseDir . '/../lib/DAV/Sharing/Plugin.php',
@@ -324,7 +300,6 @@ return array(
'OCA\\DAV\\Listener\\BirthdayListener' => $baseDir . '/../lib/Listener/BirthdayListener.php',
'OCA\\DAV\\Listener\\CalendarContactInteractionListener' => $baseDir . '/../lib/Listener/CalendarContactInteractionListener.php',
'OCA\\DAV\\Listener\\CalendarDeletionDefaultUpdaterListener' => $baseDir . '/../lib/Listener/CalendarDeletionDefaultUpdaterListener.php',
'OCA\\DAV\\Listener\\CalendarFederationNotificationListener' => $baseDir . '/../lib/Listener/CalendarFederationNotificationListener.php',
'OCA\\DAV\\Listener\\CalendarObjectReminderUpdaterListener' => $baseDir . '/../lib/Listener/CalendarObjectReminderUpdaterListener.php',
'OCA\\DAV\\Listener\\CalendarPublicationListener' => $baseDir . '/../lib/Listener/CalendarPublicationListener.php',
'OCA\\DAV\\Listener\\CalendarShareUpdateListener' => $baseDir . '/../lib/Listener/CalendarShareUpdateListener.php',
@@ -332,7 +307,6 @@ return array(
'OCA\\DAV\\Listener\\ClearPhotoCacheListener' => $baseDir . '/../lib/Listener/ClearPhotoCacheListener.php',
'OCA\\DAV\\Listener\\DavAdminSettingsListener' => $baseDir . '/../lib/Listener/DavAdminSettingsListener.php',
'OCA\\DAV\\Listener\\OutOfOfficeListener' => $baseDir . '/../lib/Listener/OutOfOfficeListener.php',
'OCA\\DAV\\Listener\\SabrePluginAuthInitListener' => $baseDir . '/../lib/Listener/SabrePluginAuthInitListener.php',
'OCA\\DAV\\Listener\\SubscriptionListener' => $baseDir . '/../lib/Listener/SubscriptionListener.php',
'OCA\\DAV\\Listener\\TrustedServerRemovedListener' => $baseDir . '/../lib/Listener/TrustedServerRemovedListener.php',
'OCA\\DAV\\Listener\\UserEventsListener' => $baseDir . '/../lib/Listener/UserEventsListener.php',
@@ -381,7 +355,6 @@ return array(
'OCA\\DAV\\Migration\\Version1029Date20231004091403' => $baseDir . '/../lib/Migration/Version1029Date20231004091403.php',
'OCA\\DAV\\Migration\\Version1030Date20240205103243' => $baseDir . '/../lib/Migration/Version1030Date20240205103243.php',
'OCA\\DAV\\Migration\\Version1031Date20240610134258' => $baseDir . '/../lib/Migration/Version1031Date20240610134258.php',
'OCA\\DAV\\Migration\\Version1034Date20250605132605' => $baseDir . '/../lib/Migration/Version1034Date20250605132605.php',
'OCA\\DAV\\Migration\\Version1034Date20250813093701' => $baseDir . '/../lib/Migration/Version1034Date20250813093701.php',
'OCA\\DAV\\Model\\ExampleEvent' => $baseDir . '/../lib/Model/ExampleEvent.php',
'OCA\\DAV\\Paginate\\LimitedCopyIterator' => $baseDir . '/../lib/Paginate/LimitedCopyIterator.php',
@@ -398,7 +371,6 @@ return array(
'OCA\\DAV\\Search\\TasksSearchProvider' => $baseDir . '/../lib/Search/TasksSearchProvider.php',
'OCA\\DAV\\Server' => $baseDir . '/../lib/Server.php',
'OCA\\DAV\\ServerFactory' => $baseDir . '/../lib/ServerFactory.php',
'OCA\\DAV\\Service\\ASyncService' => $baseDir . '/../lib/Service/ASyncService.php',
'OCA\\DAV\\Service\\AbsenceService' => $baseDir . '/../lib/Service/AbsenceService.php',
'OCA\\DAV\\Service\\ExampleContactService' => $baseDir . '/../lib/Service/ExampleContactService.php',
'OCA\\DAV\\Service\\ExampleEventService' => $baseDir . '/../lib/Service/ExampleEventService.php',
@@ -34,8 +34,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\BackgroundJob\\CleanupOrphanedChildrenJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupOrphanedChildrenJob.php',
'OCA\\DAV\\BackgroundJob\\DeleteOutdatedSchedulingObjects' => __DIR__ . '/..' . '/../lib/BackgroundJob/DeleteOutdatedSchedulingObjects.php',
'OCA\\DAV\\BackgroundJob\\EventReminderJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/EventReminderJob.php',
'OCA\\DAV\\BackgroundJob\\FederatedCalendarPeriodicSyncJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/FederatedCalendarPeriodicSyncJob.php',
'OCA\\DAV\\BackgroundJob\\FederatedCalendarSyncJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/FederatedCalendarSyncJob.php',
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
'OCA\\DAV\\BackgroundJob\\OutOfOfficeEventDispatcherJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/OutOfOfficeEventDispatcherJob.php',
'OCA\\DAV\\BackgroundJob\\PruneOutdatedSyncTokensJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/PruneOutdatedSyncTokensJob.php',
@@ -83,27 +81,9 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CalDAV\\EventReaderRDate' => __DIR__ . '/..' . '/../lib/CalDAV/EventReaderRDate.php',
'OCA\\DAV\\CalDAV\\EventReaderRRule' => __DIR__ . '/..' . '/../lib/CalDAV/EventReaderRRule.php',
'OCA\\DAV\\CalDAV\\Export\\ExportService' => __DIR__ . '/..' . '/../lib/CalDAV/Export/ExportService.php',
'OCA\\DAV\\CalDAV\\Federation\\CalendarFederationConfig' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/CalendarFederationConfig.php',
'OCA\\DAV\\CalDAV\\Federation\\CalendarFederationNotifier' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/CalendarFederationNotifier.php',
'OCA\\DAV\\CalDAV\\Federation\\CalendarFederationProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/CalendarFederationProvider.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendar' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendar.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarAuth' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendarAuth.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarEntity' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendarEntity.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarFactory' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendarFactory.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarImpl' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendarImpl.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarMapper' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendarMapper.php',
'OCA\\DAV\\CalDAV\\Federation\\FederatedCalendarSyncService' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederatedCalendarSyncService.php',
'OCA\\DAV\\CalDAV\\Federation\\FederationSharingService' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/FederationSharingService.php',
'OCA\\DAV\\CalDAV\\Federation\\Protocol\\CalendarFederationProtocolV1' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/Protocol/CalendarFederationProtocolV1.php',
'OCA\\DAV\\CalDAV\\Federation\\Protocol\\CalendarProtocolParseException' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/Protocol/CalendarProtocolParseException.php',
'OCA\\DAV\\CalDAV\\Federation\\Protocol\\ICalendarFederationProtocol' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/Protocol/ICalendarFederationProtocol.php',
'OCA\\DAV\\CalDAV\\Federation\\RemoteUserCalendarHome' => __DIR__ . '/..' . '/../lib/CalDAV/Federation/RemoteUserCalendarHome.php',
'OCA\\DAV\\CalDAV\\FreeBusy\\FreeBusyGenerator' => __DIR__ . '/..' . '/../lib/CalDAV/FreeBusy/FreeBusyGenerator.php',
'OCA\\DAV\\CalDAV\\ICSExportPlugin\\ICSExportPlugin' => __DIR__ . '/..' . '/../lib/CalDAV/ICSExportPlugin/ICSExportPlugin.php',
'OCA\\DAV\\CalDAV\\IRestorable' => __DIR__ . '/..' . '/../lib/CalDAV/IRestorable.php',
'OCA\\DAV\\CalDAV\\Import\\ImportService' => __DIR__ . '/..' . '/../lib/CalDAV/Import/ImportService.php',
'OCA\\DAV\\CalDAV\\Import\\TextImporter' => __DIR__ . '/..' . '/../lib/CalDAV/Import/TextImporter.php',
'OCA\\DAV\\CalDAV\\Import\\XmlImporter' => __DIR__ . '/..' . '/../lib/CalDAV/Import/XmlImporter.php',
'OCA\\DAV\\CalDAV\\Integration\\ExternalCalendar' => __DIR__ . '/..' . '/../lib/CalDAV/Integration/ExternalCalendar.php',
'OCA\\DAV\\CalDAV\\Integration\\ICalendarProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Integration/ICalendarProvider.php',
'OCA\\DAV\\CalDAV\\InvitationResponse\\InvitationResponseServer' => __DIR__ . '/..' . '/../lib/CalDAV/InvitationResponse/InvitationResponseServer.php',
@@ -148,8 +128,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CalDAV\\Sharing\\Backend' => __DIR__ . '/..' . '/../lib/CalDAV/Sharing/Backend.php',
'OCA\\DAV\\CalDAV\\Sharing\\Service' => __DIR__ . '/..' . '/../lib/CalDAV/Sharing/Service.php',
'OCA\\DAV\\CalDAV\\Status\\StatusService' => __DIR__ . '/..' . '/../lib/CalDAV/Status/StatusService.php',
'OCA\\DAV\\CalDAV\\SyncService' => __DIR__ . '/..' . '/../lib/CalDAV/SyncService.php',
'OCA\\DAV\\CalDAV\\SyncServiceResult' => __DIR__ . '/..' . '/../lib/CalDAV/SyncServiceResult.php',
'OCA\\DAV\\CalDAV\\TimeZoneFactory' => __DIR__ . '/..' . '/../lib/CalDAV/TimeZoneFactory.php',
'OCA\\DAV\\CalDAV\\TimezoneService' => __DIR__ . '/..' . '/../lib/CalDAV/TimezoneService.php',
'OCA\\DAV\\CalDAV\\TipBroker' => __DIR__ . '/..' . '/../lib/CalDAV/TipBroker.php',
@@ -203,7 +181,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Command\\ExportCalendar' => __DIR__ . '/..' . '/../lib/Command/ExportCalendar.php',
'OCA\\DAV\\Command\\FixCalendarSyncCommand' => __DIR__ . '/..' . '/../lib/Command/FixCalendarSyncCommand.php',
'OCA\\DAV\\Command\\GetAbsenceCommand' => __DIR__ . '/..' . '/../lib/Command/GetAbsenceCommand.php',
'OCA\\DAV\\Command\\ImportCalendar' => __DIR__ . '/..' . '/../lib/Command/ImportCalendar.php',
'OCA\\DAV\\Command\\ListAddressbooks' => __DIR__ . '/..' . '/../lib/Command/ListAddressbooks.php',
'OCA\\DAV\\Command\\ListCalendarShares' => __DIR__ . '/..' . '/../lib/Command/ListCalendarShares.php',
'OCA\\DAV\\Command\\ListCalendars' => __DIR__ . '/..' . '/../lib/Command/ListCalendars.php',
@@ -277,7 +254,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => __DIR__ . '/..' . '/../lib/DAV/CustomPropertiesBackend.php',
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/GroupPrincipalBackend.php',
'OCA\\DAV\\DAV\\PublicAuth' => __DIR__ . '/..' . '/../lib/DAV/PublicAuth.php',
'OCA\\DAV\\DAV\\RemoteUserPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/RemoteUserPrincipalBackend.php',
'OCA\\DAV\\DAV\\Sharing\\Backend' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Backend.php',
'OCA\\DAV\\DAV\\Sharing\\IShareable' => __DIR__ . '/..' . '/../lib/DAV/Sharing/IShareable.php',
'OCA\\DAV\\DAV\\Sharing\\Plugin' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Plugin.php',
@@ -339,7 +315,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Listener\\BirthdayListener' => __DIR__ . '/..' . '/../lib/Listener/BirthdayListener.php',
'OCA\\DAV\\Listener\\CalendarContactInteractionListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarContactInteractionListener.php',
'OCA\\DAV\\Listener\\CalendarDeletionDefaultUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarDeletionDefaultUpdaterListener.php',
'OCA\\DAV\\Listener\\CalendarFederationNotificationListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarFederationNotificationListener.php',
'OCA\\DAV\\Listener\\CalendarObjectReminderUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarObjectReminderUpdaterListener.php',
'OCA\\DAV\\Listener\\CalendarPublicationListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarPublicationListener.php',
'OCA\\DAV\\Listener\\CalendarShareUpdateListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarShareUpdateListener.php',
@@ -347,7 +322,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Listener\\ClearPhotoCacheListener' => __DIR__ . '/..' . '/../lib/Listener/ClearPhotoCacheListener.php',
'OCA\\DAV\\Listener\\DavAdminSettingsListener' => __DIR__ . '/..' . '/../lib/Listener/DavAdminSettingsListener.php',
'OCA\\DAV\\Listener\\OutOfOfficeListener' => __DIR__ . '/..' . '/../lib/Listener/OutOfOfficeListener.php',
'OCA\\DAV\\Listener\\SabrePluginAuthInitListener' => __DIR__ . '/..' . '/../lib/Listener/SabrePluginAuthInitListener.php',
'OCA\\DAV\\Listener\\SubscriptionListener' => __DIR__ . '/..' . '/../lib/Listener/SubscriptionListener.php',
'OCA\\DAV\\Listener\\TrustedServerRemovedListener' => __DIR__ . '/..' . '/../lib/Listener/TrustedServerRemovedListener.php',
'OCA\\DAV\\Listener\\UserEventsListener' => __DIR__ . '/..' . '/../lib/Listener/UserEventsListener.php',
@@ -396,7 +370,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1029Date20231004091403' => __DIR__ . '/..' . '/../lib/Migration/Version1029Date20231004091403.php',
'OCA\\DAV\\Migration\\Version1030Date20240205103243' => __DIR__ . '/..' . '/../lib/Migration/Version1030Date20240205103243.php',
'OCA\\DAV\\Migration\\Version1031Date20240610134258' => __DIR__ . '/..' . '/../lib/Migration/Version1031Date20240610134258.php',
'OCA\\DAV\\Migration\\Version1034Date20250605132605' => __DIR__ . '/..' . '/../lib/Migration/Version1034Date20250605132605.php',
'OCA\\DAV\\Migration\\Version1034Date20250813093701' => __DIR__ . '/..' . '/../lib/Migration/Version1034Date20250813093701.php',
'OCA\\DAV\\Model\\ExampleEvent' => __DIR__ . '/..' . '/../lib/Model/ExampleEvent.php',
'OCA\\DAV\\Paginate\\LimitedCopyIterator' => __DIR__ . '/..' . '/../lib/Paginate/LimitedCopyIterator.php',
@@ -413,7 +386,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Search\\TasksSearchProvider' => __DIR__ . '/..' . '/../lib/Search/TasksSearchProvider.php',
'OCA\\DAV\\Server' => __DIR__ . '/..' . '/../lib/Server.php',
'OCA\\DAV\\ServerFactory' => __DIR__ . '/..' . '/../lib/ServerFactory.php',
'OCA\\DAV\\Service\\ASyncService' => __DIR__ . '/..' . '/../lib/Service/ASyncService.php',
'OCA\\DAV\\Service\\AbsenceService' => __DIR__ . '/..' . '/../lib/Service/AbsenceService.php',
'OCA\\DAV\\Service\\ExampleContactService' => __DIR__ . '/..' . '/../lib/Service/ExampleContactService.php',
'OCA\\DAV\\Service\\ExampleEventService' => __DIR__ . '/..' . '/../lib/Service/ExampleEventService.php',
-1
View File
@@ -194,7 +194,6 @@ OC.L10N.register(
"Second Last" : "Azken aurrekoa",
"Third Last" : "Hirugarren azkena",
"Fourth Last" : "Laugarren azkena",
"Fifth Last" : "Bosgarren azkena",
"Contacts" : "Kontaktuak",
"{actor} created address book {addressbook}" : "{actor}-(e)k {addressbook} helbide-liburua sortu du ",
"You created address book {addressbook}" : "{addressbook} helbide-liburua sortu duzu",
-1
View File
@@ -192,7 +192,6 @@
"Second Last" : "Azken aurrekoa",
"Third Last" : "Hirugarren azkena",
"Fourth Last" : "Laugarren azkena",
"Fifth Last" : "Bosgarren azkena",
"Contacts" : "Kontaktuak",
"{actor} created address book {addressbook}" : "{actor}-(e)k {addressbook} helbide-liburua sortu du ",
"You created address book {addressbook}" : "{addressbook} helbide-liburua sortu duzu",
-14
View File
@@ -127,20 +127,6 @@ OC.L10N.register(
"_In a week on %1$s_::_In %n weeks on %1$s_" : ["Over een week op %1$s","Over %n weken op %1$s"],
"_In a month on %1$s_::_In %n months on %1$s_" : ["Over een maand op %1$s","Over %n maanden op %1$s"],
"_In a year on %1$s_::_In %n years on %1$s_" : ["Over een jaar op %1$s","Over %n jaar op %1$s"],
"In the past on %1$s then on %2$s" : "In het verleden op %1$s dan op %2$s",
"_In a minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["Over een minuut op %1$s dan op %2$s","Over %n minuten op %1$s dan op %2$s"],
"_In a hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["Over een uur op %1$s dan op %2$s","Over %n uren op %1$s dan op %2$s"],
"_In a day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["Over een dag op %1$s dan op %2$s","Over %n dagen op %1$s dan op %2$s"],
"_In a week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["Over een week op %1$s dan op %2$s","Over %n weken op %1$s dan op %2$s"],
"_In a month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["Over een maand op %1$s dan op %2$s","Over %n maanden op %1$s dan op %2$s"],
"_In a year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["Over een jaar op %1$s dan op %2$s","Over %n jaren op %1$s dan op %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "In het verleden op %1$s dan op %2$s en %3$s",
"_In a minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["Over een minuut op %1$s dan op %2$s en %3$s","Over %n minuten op %1$s dan op %2$s en %3$s"],
"_In a hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["Over een uur op %1$s dan op %2$s en %3$s","Over %n uren op %1$s dan op %2$s en %3$s"],
"_In a day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["Over een dag op %1$s dan op %2$s en %3$s","Over %n dagen op %1$s dan op %2$s en %3$s"],
"_In a week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["Over een week op %1$s dan op %2$s en %3$s","Over %n weken op %1$s dan op %2$s en %3$s"],
"_In a month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["Over een maand op %1$s dan op %2$s en %3$s","Over %n maanden op %1$s then on %2$s and %3$s"],
"_In a year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["Over een jaar op %1$s dan op %2$s en %3$s","Over %n jaren op %1$s dan op %2$s en %3$s"],
"Cancelled: %1$s" : "Geannuleerd: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" is geannuleerd",
"Re: %1$s" : "Re: %1$s",
-14
View File
@@ -125,20 +125,6 @@
"_In a week on %1$s_::_In %n weeks on %1$s_" : ["Over een week op %1$s","Over %n weken op %1$s"],
"_In a month on %1$s_::_In %n months on %1$s_" : ["Over een maand op %1$s","Over %n maanden op %1$s"],
"_In a year on %1$s_::_In %n years on %1$s_" : ["Over een jaar op %1$s","Over %n jaar op %1$s"],
"In the past on %1$s then on %2$s" : "In het verleden op %1$s dan op %2$s",
"_In a minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["Over een minuut op %1$s dan op %2$s","Over %n minuten op %1$s dan op %2$s"],
"_In a hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["Over een uur op %1$s dan op %2$s","Over %n uren op %1$s dan op %2$s"],
"_In a day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["Over een dag op %1$s dan op %2$s","Over %n dagen op %1$s dan op %2$s"],
"_In a week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["Over een week op %1$s dan op %2$s","Over %n weken op %1$s dan op %2$s"],
"_In a month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["Over een maand op %1$s dan op %2$s","Over %n maanden op %1$s dan op %2$s"],
"_In a year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["Over een jaar op %1$s dan op %2$s","Over %n jaren op %1$s dan op %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "In het verleden op %1$s dan op %2$s en %3$s",
"_In a minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["Over een minuut op %1$s dan op %2$s en %3$s","Over %n minuten op %1$s dan op %2$s en %3$s"],
"_In a hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["Over een uur op %1$s dan op %2$s en %3$s","Over %n uren op %1$s dan op %2$s en %3$s"],
"_In a day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["Over een dag op %1$s dan op %2$s en %3$s","Over %n dagen op %1$s dan op %2$s en %3$s"],
"_In a week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["Over een week op %1$s dan op %2$s en %3$s","Over %n weken op %1$s dan op %2$s en %3$s"],
"_In a month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["Over een maand op %1$s dan op %2$s en %3$s","Over %n maanden op %1$s then on %2$s and %3$s"],
"_In a year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["Over een jaar op %1$s dan op %2$s en %3$s","Over %n jaren op %1$s dan op %2$s en %3$s"],
"Cancelled: %1$s" : "Geannuleerd: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" is geannuleerd",
"Re: %1$s" : "Re: %1$s",
+1 -22
View File
@@ -13,7 +13,6 @@ use OCA\DAV\CalDAV\AppCalendar\AppCalendarPlugin;
use OCA\DAV\CalDAV\CachedSubscriptionProvider;
use OCA\DAV\CalDAV\CalendarManager;
use OCA\DAV\CalDAV\CalendarProvider;
use OCA\DAV\CalDAV\Federation\CalendarFederationProvider;
use OCA\DAV\CalDAV\Reminder\NotificationProvider\AudioProvider;
use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider;
use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider;
@@ -37,7 +36,6 @@ use OCA\DAV\Events\CalendarUpdatedEvent;
use OCA\DAV\Events\CardCreatedEvent;
use OCA\DAV\Events\CardDeletedEvent;
use OCA\DAV\Events\CardUpdatedEvent;
use OCA\DAV\Events\SabrePluginAuthInitEvent;
use OCA\DAV\Events\SubscriptionCreatedEvent;
use OCA\DAV\Events\SubscriptionDeletedEvent;
use OCA\DAV\Listener\ActivityUpdaterListener;
@@ -46,7 +44,6 @@ use OCA\DAV\Listener\AddressbookListener;
use OCA\DAV\Listener\BirthdayListener;
use OCA\DAV\Listener\CalendarContactInteractionListener;
use OCA\DAV\Listener\CalendarDeletionDefaultUpdaterListener;
use OCA\DAV\Listener\CalendarFederationNotificationListener;
use OCA\DAV\Listener\CalendarObjectReminderUpdaterListener;
use OCA\DAV\Listener\CalendarPublicationListener;
use OCA\DAV\Listener\CalendarShareUpdateListener;
@@ -54,7 +51,6 @@ use OCA\DAV\Listener\CardListener;
use OCA\DAV\Listener\ClearPhotoCacheListener;
use OCA\DAV\Listener\DavAdminSettingsListener;
use OCA\DAV\Listener\OutOfOfficeListener;
use OCA\DAV\Listener\SabrePluginAuthInitListener;
use OCA\DAV\Listener\SubscriptionListener;
use OCA\DAV\Listener\TrustedServerRemovedListener;
use OCA\DAV\Listener\UserEventsListener;
@@ -86,7 +82,6 @@ use OCP\Config\BeforePreferenceSetEvent;
use OCP\Contacts\IManager as IContactsManager;
use OCP\DB\Events\AddMissingIndicesEvent;
use OCP\Federation\Events\TrustedServerRemovedEvent;
use OCP\Federation\ICloudFederationProviderManager;
use OCP\IUserSession;
use OCP\Server;
use OCP\Settings\Events\DeclarativeSettingsGetValueEvent;
@@ -203,12 +198,6 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(UserChangedEvent::class, UserEventsListener::class);
$context->registerEventListener(UserUpdatedEvent::class, UserEventsListener::class);
$context->registerEventListener(SabrePluginAuthInitEvent::class, SabrePluginAuthInitListener::class);
$context->registerEventListener(CalendarObjectCreatedEvent::class, CalendarFederationNotificationListener::class);
$context->registerEventListener(CalendarObjectUpdatedEvent::class, CalendarFederationNotificationListener::class);
$context->registerEventListener(CalendarObjectDeletedEvent::class, CalendarFederationNotificationListener::class);
$context->registerNotifierService(Notifier::class);
$context->registerCalendarProvider(CalendarProvider::class);
@@ -224,6 +213,7 @@ class Application extends App implements IBootstrap {
$context->registerDeclarativeSettings(SystemAddressBookSettings::class);
$context->registerEventListener(DeclarativeSettingsGetValueEvent::class, DavAdminSettingsListener::class);
$context->registerEventListener(DeclarativeSettingsSetValueEvent::class, DavAdminSettingsListener::class);
}
public function boot(IBootContext $context): void {
@@ -233,7 +223,6 @@ class Application extends App implements IBootstrap {
$context->injectFn($this->registerContactsManager(...));
$context->injectFn($this->registerCalendarManager(...));
$context->injectFn($this->registerCalendarReminders(...));
$context->injectFn($this->registerCloudFederationProvider(...));
}
public function registerContactsManager(IContactsManager $cm, IAppContainer $container): void {
@@ -290,14 +279,4 @@ class Application extends App implements IBootstrap {
$logger->error($ex->getMessage(), ['exception' => $ex]);
}
}
public function registerCloudFederationProvider(
ICloudFederationProviderManager $manager,
): void {
$manager->addCloudFederationProvider(
CalendarFederationProvider::PROVIDER_ID,
'Calendar Federation',
static fn () => Server::get(CalendarFederationProvider::class),
);
}
}
@@ -1,62 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\BackgroundJob;
use OCA\DAV\CalDAV\Federation\CalendarFederationConfig;
use OCA\DAV\CalDAV\Federation\FederatedCalendarMapper;
use OCA\DAV\CalDAV\Federation\FederatedCalendarSyncService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Log\LoggerInterface;
class FederatedCalendarPeriodicSyncJob extends TimedJob {
private const DOWNLOAD_LIMIT = 500;
public function __construct(
ITimeFactory $time,
private readonly FederatedCalendarSyncService $syncService,
private readonly FederatedCalendarMapper $federatedCalendarMapper,
private readonly CalendarFederationConfig $calendarFederationConfig,
private readonly LoggerInterface $logger,
) {
parent::__construct($time);
$this->setTimeSensitivity(self::TIME_SENSITIVE);
$this->setAllowParallelRuns(false);
$this->setInterval(3600);
}
protected function run($argument): void {
if (!$this->calendarFederationConfig->isFederationEnabled()) {
return;
}
$downloadedEvents = 0;
$oneHourAgo = $this->time->getTime() - 3600;
$calendars = $this->federatedCalendarMapper->findUnsyncedSinceBefore($oneHourAgo);
foreach ($calendars as $calendar) {
try {
$downloadedEvents += $this->syncService->syncOne($calendar);
} catch (ClientExceptionInterface $e) {
$name = $calendar->getUri();
$this->logger->error("Failed to sync federated calendar $name: " . $e->getMessage(), [
'exception' => $e,
'calendar' => $calendar->toCalendarInfo(),
]);
}
// Prevent stalling the background job queue for too long
if ($downloadedEvents >= self::DOWNLOAD_LIMIT) {
break;
}
}
}
}
@@ -1,67 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\BackgroundJob;
use OCA\DAV\CalDAV\Federation\CalendarFederationConfig;
use OCA\DAV\CalDAV\Federation\FederatedCalendarMapper;
use OCA\DAV\CalDAV\Federation\FederatedCalendarSyncService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\QueuedJob;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Log\LoggerInterface;
class FederatedCalendarSyncJob extends QueuedJob {
public const ARGUMENT_ID = 'id';
public function __construct(
ITimeFactory $time,
private readonly FederatedCalendarSyncService $syncService,
private readonly FederatedCalendarMapper $federatedCalendarMapper,
private readonly CalendarFederationConfig $calendarFederationConfig,
private readonly LoggerInterface $logger,
) {
parent::__construct($time);
$this->setAllowParallelRuns(false);
}
protected function run($argument): void {
if (!$this->calendarFederationConfig->isFederationEnabled()) {
return;
}
$id = $argument[self::ARGUMENT_ID] ?? null;
if (!is_numeric($id)) {
return;
}
$id = (int)$id;
try {
$calendar = $this->federatedCalendarMapper->find($id);
} catch (DoesNotExistException $e) {
return;
}
try {
$this->syncService->syncOne($calendar);
} catch (ClientExceptionInterface $e) {
$name = $calendar->getUri();
$this->logger->error("Failed to sync federated calendar $name: " . $e->getMessage(), [
'exception' => $e,
'calendar' => $calendar->toCalendarInfo(),
]);
// Let the periodic background job pick up the calendar at a later point
$calendar->setLastSync(1);
$this->federatedCalendarMapper->update($calendar);
}
}
}
@@ -11,7 +11,6 @@ namespace OCA\DAV\CalDAV\AppCalendar;
use OCA\DAV\CalDAV\CachedSubscriptionImpl;
use OCA\DAV\CalDAV\CalendarImpl;
use OCA\DAV\CalDAV\Federation\FederatedCalendarImpl;
use OCA\DAV\CalDAV\Integration\ExternalCalendar;
use OCA\DAV\CalDAV\Integration\ICalendarProvider;
use OCP\Calendar\IManager;
@@ -52,11 +51,7 @@ class AppCalendarPlugin implements ICalendarProvider {
return array_values(
array_filter($this->manager->getCalendarsForPrincipal($principalUri, $calendarUris), function ($c) {
// We must not provide a wrapper for DAV calendars
return !(
($c instanceof CalendarImpl)
|| ($c instanceof CachedSubscriptionImpl)
|| ($c instanceof FederatedCalendarImpl)
);
return ! (($c instanceof CalendarImpl) || ($c instanceof CachedSubscriptionImpl));
})
);
}
+10 -100
View File
@@ -12,8 +12,6 @@ use DateTimeImmutable;
use DateTimeInterface;
use Generator;
use OCA\DAV\AppInfo\Application;
use OCA\DAV\CalDAV\Federation\FederatedCalendarEntity;
use OCA\DAV\CalDAV\Federation\FederatedCalendarMapper;
use OCA\DAV\CalDAV\Sharing\Backend;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\DAV\DAV\Sharing\IShareable;
@@ -43,8 +41,6 @@ use OCP\Calendar\Exceptions\CalendarException;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IUserManager;
@@ -114,7 +110,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
public const CALENDAR_TYPE_CALENDAR = 0;
public const CALENDAR_TYPE_SUBSCRIPTION = 1;
public const CALENDAR_TYPE_FEDERATED = 2;
public const PERSONAL_CALENDAR_URI = 'personal';
public const PERSONAL_CALENDAR_NAME = 'Personal';
@@ -204,8 +199,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
private string $dbObjectInvitationsTable = 'calendar_invitations';
private array $cachedObjects = [];
private readonly ICache $publishStatusCache;
public function __construct(
private IDBConnection $db,
private Principal $principalBackend,
@@ -215,11 +208,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
private IEventDispatcher $dispatcher,
private IConfig $config,
private Sharing\Backend $calendarSharingBackend,
private FederatedCalendarMapper $federatedCalendarMapper,
ICacheFactory $cacheFactory,
private bool $legacyEndpoint = false,
) {
$this->publishStatusCache = $cacheFactory->createInMemory();
}
/**
@@ -545,7 +535,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
'{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($row['principaluri'], $this->legacyEndpoint),
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => true,
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => (int)$row['access'] === Backend::ACCESS_READ,
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}public' => (int)$row['access'] === self::ACCESS_PUBLIC,
];
@@ -610,7 +600,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
'{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => true,
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => (int)$row['access'] === Backend::ACCESS_READ,
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}public' => (int)$row['access'] === self::ACCESS_PUBLIC,
];
@@ -929,8 +919,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @return void
*/
public function deleteCalendar($calendarId, bool $forceDeletePermanently = false) {
$this->publishStatusCache->remove((string)$calendarId);
$this->atomic(function () use ($calendarId, $forceDeletePermanently): void {
// The calendar is deleted right away if this is either enforced by the caller
// or the special contacts birthday calendar or when the preference of an empty
@@ -1420,12 +1408,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
$shares = $this->getShares($calendarId);
$this->dispatcher->dispatchTyped(new CalendarObjectCreatedEvent($calendarId, $calendarRow, $shares, $objectRow));
} elseif ($calendarType === self::CALENDAR_TYPE_SUBSCRIPTION) {
} else {
$subscriptionRow = $this->getSubscriptionById($calendarId);
$this->dispatcher->dispatchTyped(new CachedCalendarObjectCreatedEvent($calendarId, $subscriptionRow, [], $objectRow));
} elseif ($calendarType === self::CALENDAR_TYPE_FEDERATED) {
// TODO: implement custom event for federated calendars
}
return '"' . $extraData['etag'] . '"';
@@ -1482,12 +1468,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
$shares = $this->getShares($calendarId);
$this->dispatcher->dispatchTyped(new CalendarObjectUpdatedEvent($calendarId, $calendarRow, $shares, $objectRow));
} elseif ($calendarType === self::CALENDAR_TYPE_SUBSCRIPTION) {
} else {
$subscriptionRow = $this->getSubscriptionById($calendarId);
$this->dispatcher->dispatchTyped(new CachedCalendarObjectUpdatedEvent($calendarId, $subscriptionRow, [], $objectRow));
} elseif ($calendarType === self::CALENDAR_TYPE_FEDERATED) {
// TODO: implement custom event for federated calendars
}
}
@@ -1994,8 +1978,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
if (isset($calendarInfo['source'])) {
$calendarType = self::CALENDAR_TYPE_SUBSCRIPTION;
} elseif (isset($calendarInfo['federated'])) {
$calendarType = self::CALENDAR_TYPE_FEDERATED;
} else {
$calendarType = self::CALENDAR_TYPE_CALENDAR;
}
@@ -2442,7 +2424,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @param string $uid
* @return string|null
*/
public function getCalendarObjectByUID($principalUri, $uid, $calendarUri = null) {
public function getCalendarObjectByUID($principalUri, $uid) {
$query = $this->db->getQueryBuilder();
$query->selectAlias('c.uri', 'calendaruri')->selectAlias('co.uri', 'objecturi')
->from('calendarobjects', 'co')
@@ -2450,11 +2432,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
->where($query->expr()->eq('c.principaluri', $query->createNamedParameter($principalUri)))
->andWhere($query->expr()->eq('co.uid', $query->createNamedParameter($uid)))
->andWhere($query->expr()->isNull('co.deleted_at'));
if ($calendarUri !== null) {
$query->andWhere($query->expr()->eq('c.uri', $query->createNamedParameter($calendarUri)));
}
$stmt = $query->executeQuery();
$row = $stmt->fetch();
$stmt->closeCursor();
@@ -3215,10 +3192,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
return $this->calendarSharingBackend->getShares($resourceId);
}
public function getSharesByShareePrincipal(string $principal): array {
return $this->calendarSharingBackend->getSharesByShareePrincipal($principal);
}
public function preloadShares(array $resourceIds): void {
$this->calendarSharingBackend->preloadShares($resourceIds);
}
@@ -3229,7 +3202,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @return string|null
*/
public function setPublishStatus($value, $calendar) {
$publishStatus = $this->atomic(function () use ($value, $calendar) {
return $this->atomic(function () use ($value, $calendar) {
$calendarId = $calendar->getResourceId();
$calendarData = $this->getCalendarById($calendarId);
@@ -3257,21 +3230,13 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
$this->dispatcher->dispatchTyped(new CalendarUnpublishedEvent($calendarId, $calendarData));
return null;
}, $this->db);
$this->publishStatusCache->set((string)$calendar->getResourceId(), $publishStatus ?? false);
return $publishStatus;
}
/**
* @param Calendar $calendar
* @return string|false
* @return mixed
*/
public function getPublishStatus($calendar) {
$cached = $this->publishStatusCache->get((string)$calendar->getResourceId());
if ($cached !== null) {
return $cached;
}
$query = $this->db->getQueryBuilder();
$result = $query->select('publicuri')
->from('dav_shares')
@@ -3279,46 +3244,9 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
->andWhere($query->expr()->eq('access', $query->createNamedParameter(self::ACCESS_PUBLIC)))
->executeQuery();
$publishStatus = $result->fetchOne();
$result->closeCursor();
$this->publishStatusCache->set((string)$calendar->getResourceId(), $publishStatus);
return $publishStatus;
}
/**
* @param int[] $resourceIds
*/
public function preloadPublishStatuses(array $resourceIds): void {
$query = $this->db->getQueryBuilder();
$result = $query->select('resourceid', 'publicuri')
->from('dav_shares')
->where($query->expr()->in(
'resourceid',
$query->createNamedParameter($resourceIds, IQueryBuilder::PARAM_INT_ARRAY),
IQueryBuilder::PARAM_INT_ARRAY,
))
->andWhere($query->expr()->eq(
'access',
$query->createNamedParameter(self::ACCESS_PUBLIC, IQueryBuilder::PARAM_INT),
IQueryBuilder::PARAM_INT,
))
->executeQuery();
$hasPublishStatuses = [];
while ($row = $result->fetch()) {
$this->publishStatusCache->set((string)$row['resourceid'], $row['publicuri']);
$hasPublishStatuses[(int)$row['resourceid']] = true;
}
// Also remember resources with no publish status
foreach ($resourceIds as $resourceId) {
if (!isset($hasPublishStatuses[$resourceId])) {
$this->publishStatusCache->set((string)$resourceId, false);
}
}
$row = $result->fetch();
$result->closeCursor();
return $row ? reset($row) : false;
}
/**
@@ -3637,9 +3565,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
$uri = $calendarInfo['principaluri'];
}
$principalInformation = $this->principalBackend->getPrincipalPropertiesByPath($uri, [
'{DAV:}displayname',
]);
$principalInformation = $this->principalBackend->getPrincipalByPath($uri);
if (isset($principalInformation['{DAV:}displayname'])) {
$calendarInfo[$displaynameKey] = $principalInformation['{DAV:}displayname'];
}
@@ -3761,20 +3687,4 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
}
}, $this->db);
}
/**
* @return array<string, mixed>[]
*/
public function getFederatedCalendarsForUser(string $principalUri): array {
$federatedCalendars = $this->federatedCalendarMapper->findByPrincipalUri($principalUri);
return array_map(
static fn (FederatedCalendarEntity $entity) => $entity->toCalendarInfo(),
$federatedCalendars,
);
}
public function getFederatedCalendarByUri(string $principalUri, string $uri): ?array {
$federatedCalendar = $this->federatedCalendarMapper->findByUri($principalUri, $uri);
return $federatedCalendar?->toCalendarInfo();
}
}
+6 -10
View File
@@ -64,10 +64,6 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable
return $this->calendarInfo['uri'];
}
protected function getCalendarType(): int {
return CalDavBackend::CALENDAR_TYPE_CALENDAR;
}
/**
* {@inheritdoc}
* @throws Forbidden
@@ -201,7 +197,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable
$this->getOwner() . '/calendar-proxy-read',
$this->getOwner() . '/calendar-proxy-write',
parent::getOwner(),
'principals/system/public',
'principals/system/public'
];
/** @var list<array{privilege: string, principal: string, protected: bool}> $acl */
$acl = array_filter($acl, function (array $rule) use ($allowedPrincipals): bool {
@@ -251,7 +247,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable
}
public function getChild($name) {
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name, $this->getCalendarType());
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
if (!$obj) {
throw new NotFound('Calendar object not found');
@@ -267,7 +263,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable
}
public function getChildren() {
$objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id'], $this->getCalendarType());
$objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
$children = [];
foreach ($objs as $obj) {
if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) {
@@ -280,7 +276,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable
}
public function getMultipleChildren(array $paths) {
$objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths, $this->getCalendarType());
$objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths);
$children = [];
foreach ($objs as $obj) {
if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) {
@@ -293,7 +289,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable
}
public function childExists($name) {
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name, $this->getCalendarType());
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
if (!$obj) {
return false;
}
@@ -305,7 +301,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable
}
public function calendarQuery(array $filters) {
$uris = $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters, $this->getCalendarType());
$uris = $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters);
if ($this->isShared()) {
return array_filter($uris, function ($uri) {
return $this->childExists($uri);
+2 -23
View File
@@ -8,7 +8,6 @@
namespace OCA\DAV\CalDAV;
use OCA\DAV\AppInfo\PluginManager;
use OCA\DAV\CalDAV\Federation\FederatedCalendarFactory;
use OCA\DAV\CalDAV\Integration\ExternalCalendar;
use OCA\DAV\CalDAV\Integration\ICalendarProvider;
use OCA\DAV\CalDAV\Trashbin\TrashbinHome;
@@ -38,14 +37,12 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome {
/** @var PluginManager */
private $pluginManager;
private ?array $cachedChildren = null;
public function __construct(
BackendInterface $caldavBackend,
array $principalInfo,
private LoggerInterface $logger,
private FederatedCalendarFactory $federatedCalendarFactory,
private bool $returnCachedSubscriptions,
) {
parent::__construct($caldavBackend, $principalInfo);
@@ -105,15 +102,6 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome {
if ($this->caldavBackend instanceof CalDavBackend) {
$objects[] = new TrashbinHome($this->caldavBackend, $this->principalInfo);
$federatedCalendars = $this->caldavBackend->getFederatedCalendarsForUser(
$this->principalInfo['uri'],
);
foreach ($federatedCalendars as $federatedCalendarInfo) {
$objects[] = $this->federatedCalendarFactory->createFederatedCalendar(
$federatedCalendarInfo,
);
}
}
// If the backend supports subscriptions, we'll add those as well,
@@ -159,22 +147,13 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome {
return new TrashbinHome($this->caldavBackend, $this->principalInfo);
}
// Only check if the methods are available
// Calendar - this covers all "regular" calendars, but not shared
// only check if the method is available
if ($this->caldavBackend instanceof CalDavBackend) {
// Calendar - this covers all "regular" calendars, but not shared
$calendar = $this->caldavBackend->getCalendarByUri($this->principalInfo['uri'], $name);
if (!empty($calendar)) {
return new Calendar($this->caldavBackend, $calendar, $this->l10n, $this->config, $this->logger);
}
// Federated calendar
$federatedCalendar = $this->caldavBackend->getFederatedCalendarByUri(
$this->principalInfo['uri'],
$name,
);
if ($federatedCalendar !== null) {
return $this->federatedCalendarFactory->createFederatedCalendar($federatedCalendar);
}
}
// Fallback to cover shared calendars
+49 -97
View File
@@ -11,7 +11,6 @@ namespace OCA\DAV\CalDAV;
use Generator;
use OCA\DAV\CalDAV\Auth\CustomPrincipalPlugin;
use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
use OCA\DAV\Connector\Sabre\Server;
use OCP\Calendar\CalendarExportOptions;
use OCP\Calendar\Exceptions\CalendarException;
use OCP\Calendar\ICalendarExport;
@@ -24,12 +23,11 @@ use OCP\Constants;
use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp;
use Sabre\DAV\Exception\Conflict;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Component\VEvent;
use Sabre\VObject\Component\VTimeZone;
use Sabre\VObject\ITip\Message;
use Sabre\VObject\ParseException;
use Sabre\VObject\Property;
use Sabre\VObject\Reader;
use function Sabre\Uri\split as uriSplit;
class CalendarImpl implements ICreateFromString, IHandleImipMessage, ICalendarIsWritable, ICalendarIsShared, ICalendarExport, ICalendarIsEnabled {
@@ -41,9 +39,6 @@ class CalendarImpl implements ICreateFromString, IHandleImipMessage, ICalendarIs
) {
}
private const DAV_PROPERTY_USER_ADDRESS = '{http://sabredav.org/ns}email-address';
private const DAV_PROPERTY_USER_ADDRESSES = '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set';
/**
* @return string defining the technical unique key
* @since 13.0.0
@@ -59,14 +54,6 @@ class CalendarImpl implements ICreateFromString, IHandleImipMessage, ICalendarIs
return $this->calendarInfo['uri'];
}
/**
* @return string the principal URI of the calendar owner
* @since 32.0.0
*/
public function getPrincipalUri(): string {
return $this->calendarInfo['principaluri'];
}
/**
* In comparison to getKey() this function returns a human readable (maybe translated) name
* @since 13.0.0
@@ -174,7 +161,7 @@ class CalendarImpl implements ICreateFromString, IHandleImipMessage, ICalendarIs
private function createFromStringInServer(
string $name,
string $calendarData,
Server $server,
\OCA\DAV\Connector\Sabre\Server $server,
): void {
/** @var CustomPrincipalPlugin $plugin */
$plugin = $server->getPlugin('auth');
@@ -222,93 +209,58 @@ class CalendarImpl implements ICreateFromString, IHandleImipMessage, ICalendarIs
* @throws CalendarException
*/
public function handleIMipMessage(string $name, string $calendarData): void {
try {
/** @var VCalendar $vObject|null */
$vObject = Reader::read($calendarData);
} catch (ParseException $e) {
throw new CalendarException('iMip message could not be processed because an error occurred while parsing the iMip message', 0, $e);
}
// validate the iMip message
if (!isset($vObject->METHOD)) {
throw new CalendarException('iMip message contains no valid method');
}
if (!isset($vObject->VEVENT)) {
throw new CalendarException('iMip message contains no event');
}
if (!isset($vObject->VEVENT->UID)) {
throw new CalendarException('iMip message event dose not contain a UID');
}
if (!isset($vObject->VEVENT->ORGANIZER)) {
throw new CalendarException('iMip message event dose not contain an organizer');
}
if (!isset($vObject->VEVENT->ATTENDEE)) {
throw new CalendarException('iMip message event dose not contain an attendee');
}
if (empty($this->calendarInfo['uri'])) {
throw new CalendarException('Could not write to calendar as URI parameter is missing');
}
// construct dav server
$server = $this->getInvitationResponseServer();
/** @var CustomPrincipalPlugin $authPlugin */
$authPlugin = $server->getServer()->getPlugin('auth');
/** @var CustomPrincipalPlugin $plugin */
$plugin = $server->getServer()->getPlugin('auth');
// we're working around the previous implementation
// that only allowed the public system principal to be used
// so set the custom principal here
$authPlugin->setCurrentPrincipal($this->calendar->getPrincipalURI());
// retrieve all users addresses
$userProperties = $server->getServer()->getProperties($this->calendar->getPrincipalURI(), [ self::DAV_PROPERTY_USER_ADDRESS, self::DAV_PROPERTY_USER_ADDRESSES ]);
$userAddress = 'mailto:' . ($userProperties[self::DAV_PROPERTY_USER_ADDRESS] ?? null);
$userAddresses = $userProperties[self::DAV_PROPERTY_USER_ADDRESSES]->getHrefs() ?? [];
$userAddresses = array_map('strtolower', array_map('urldecode', $userAddresses));
// validate the method, recipient and sender
$imipMethod = strtoupper($vObject->METHOD->getValue());
if (in_array($imipMethod, ['REPLY', 'REFRESH'], true)) {
// extract sender (REPLY and REFRESH method should only have one attendee)
$sender = strtolower($vObject->VEVENT->ATTENDEE->getValue());
// extract and verify the recipient
$recipient = strtolower($vObject->VEVENT->ORGANIZER->getValue());
if (!in_array($recipient, $userAddresses, true)) {
throw new CalendarException('iMip message dose not contain an organizer that matches the user');
}
// if the recipient address is not the same as the user address this means an alias was used
// the iTip broker uses the users primary email address during processing
if ($userAddress !== $recipient) {
$recipient = $userAddress;
}
} elseif (in_array($imipMethod, ['PUBLISH', 'REQUEST', 'ADD', 'CANCEL'], true)) {
// extract sender
$sender = strtolower($vObject->VEVENT->ORGANIZER->getValue());
// extract and verify the recipient
foreach ($vObject->VEVENT->ATTENDEE as $attendee) {
$recipient = strtolower($attendee->getValue());
if (in_array($recipient, $userAddresses, true)) {
break;
}
$recipient = null;
}
if ($recipient === null) {
throw new CalendarException('iMip message dose not contain an attendee that matches the user');
}
// if the recipient address is not the same as the user address this means an alias was used
// the iTip broker uses the users primary email address during processing
if ($userAddress !== $recipient) {
$recipient = $userAddress;
}
} else {
throw new CalendarException('iMip message contains a method that is not supported: ' . $imipMethod);
}
// generate the iTip message
$iTip = new Message();
$iTip->method = $imipMethod;
$iTip->sender = $sender;
$iTip->recipient = $recipient;
$iTip->component = 'VEVENT';
$iTip->uid = $vObject->VEVENT->UID->getValue();
$iTip->sequence = isset($vObject->VEVENT->SEQUENCE) ? (int)$vObject->VEVENT->SEQUENCE->getValue() : 1;
$iTip->message = $vObject;
$plugin->setCurrentPrincipal($this->calendar->getPrincipalURI());
$server->server->emit('schedule', [$iTip]);
if (empty($this->calendarInfo['uri'])) {
throw new CalendarException('Could not write to calendar as URI parameter is missing');
}
// Force calendar change URI
/** @var Schedule\Plugin $schedulingPlugin */
$schedulingPlugin = $server->getServer()->getPlugin('caldav-schedule');
// Let sabre handle the rest
$iTipMessage = new Message();
/** @var VCalendar $vObject */
$vObject = Reader::read($calendarData);
/** @var VEvent $vEvent */
$vEvent = $vObject->{'VEVENT'};
if ($vObject->{'METHOD'} === null) {
throw new CalendarException('No Method provided for scheduling data. Could not process message');
}
if (!isset($vEvent->{'ORGANIZER'}) || !isset($vEvent->{'ATTENDEE'})) {
throw new CalendarException('Could not process scheduling data, neccessary data missing from ICAL');
}
$organizer = $vEvent->{'ORGANIZER'}->getValue();
$attendee = $vEvent->{'ATTENDEE'}->getValue();
$iTipMessage->method = $vObject->{'METHOD'}->getValue();
if ($iTipMessage->method === 'REQUEST') {
$iTipMessage->sender = $organizer;
$iTipMessage->recipient = $attendee;
} elseif ($iTipMessage->method === 'REPLY') {
if ($server->isExternalAttendee($vEvent->{'ATTENDEE'}->getValue())) {
$iTipMessage->recipient = $organizer;
} else {
$iTipMessage->recipient = $attendee;
}
$iTipMessage->sender = $attendee;
} elseif ($iTipMessage->method === 'CANCEL') {
$iTipMessage->recipient = $attendee;
$iTipMessage->sender = $organizer;
}
$iTipMessage->uid = isset($vEvent->{'UID'}) ? $vEvent->{'UID'}->getValue() : '';
$iTipMessage->component = 'VEVENT';
$iTipMessage->sequence = isset($vEvent->{'SEQUENCE'}) ? (int)$vEvent->{'SEQUENCE'}->getValue() : 0;
$iTipMessage->message = $vObject;
$server->server->emit('schedule', [$iTipMessage]);
}
public function getInvitationResponseServer(): InvitationResponseServer {
+1 -6
View File
@@ -53,8 +53,7 @@ class CalendarObject extends \Sabre\CalDAV\CalendarObject {
}
// shows as busy if event is declared confidential
if ($this->objectData['classification'] === CalDavBackend::CLASSIFICATION_CONFIDENTIAL
&& ($this->isPublic() || !$this->canWrite())) {
if ($this->objectData['classification'] === CalDavBackend::CLASSIFICATION_CONFIDENTIAL) {
$this->createConfidentialObject($vObject);
}
@@ -136,10 +135,6 @@ class CalendarObject extends \Sabre\CalDAV\CalendarObject {
return true;
}
private function isPublic(): bool {
return $this->calendarInfo['{http://owncloud.org/ns}public'] ?? false;
}
public function getCalendarId(): int {
return (int)$this->objectData['calendarid'];
}
+1 -23
View File
@@ -8,7 +8,6 @@ declare(strict_types=1);
*/
namespace OCA\DAV\CalDAV;
use OCA\DAV\CalDAV\Federation\FederatedCalendarImpl;
use OCA\DAV\Db\Property;
use OCA\DAV\Db\PropertyMapper;
use OCP\Calendar\ICalendarProvider;
@@ -28,24 +27,17 @@ class CalendarProvider implements ICalendarProvider {
}
public function getCalendars(string $principalUri, array $calendarUris = []): array {
/** @var array{uri: string, principaluri: string}[] $calendarInfos */
$calendarInfos = $this->calDavBackend->getCalendarsForUser($principalUri) ?? [];
/** @var array{uri: string, principaluri: string}[] $federatedCalendarInfos */
$federatedCalendarInfos = $this->calDavBackend->getFederatedCalendarsForUser($principalUri);
if (!empty($calendarUris)) {
$calendarInfos = array_filter($calendarInfos, function ($calendar) use ($calendarUris) {
return in_array($calendar['uri'], $calendarUris);
});
$federatedCalendarInfos = array_filter($federatedCalendarInfos, function ($federatedCalendar) use ($calendarUris) {
return in_array($federatedCalendar['uri'], $calendarUris);
});
}
$additionalProperties = $this->getAdditionalPropertiesForCalendars($calendarInfos);
$iCalendars = [];
foreach ($calendarInfos as $calendarInfo) {
$user = str_replace('principals/users/', '', $calendarInfo['principaluri']);
$path = 'calendars/' . $user . '/' . $calendarInfo['uri'];
@@ -59,20 +51,6 @@ class CalendarProvider implements ICalendarProvider {
$this->calDavBackend,
);
}
$additionalFederatedProps = $this->getAdditionalPropertiesForCalendars(
$federatedCalendarInfos,
);
foreach ($federatedCalendarInfos as $calendarInfo) {
$user = str_replace('principals/users/', '', $calendarInfo['principaluri']);
$path = 'calendars/' . $user . '/' . $calendarInfo['uri'];
if (isset($additionalFederatedProps[$path])) {
$calendarInfo = array_merge($calendarInfo, $additionalProperties[$path]);
}
$iCalendars[] = new FederatedCalendarImpl($calendarInfo, $this->calDavBackend);
}
return $iCalendars;
}
-47
View File
@@ -7,15 +7,8 @@
*/
namespace OCA\DAV\CalDAV;
use OCA\DAV\CalDAV\Federation\FederatedCalendarFactory;
use OCA\DAV\CalDAV\Federation\RemoteUserCalendarHome;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\DAV\DAV\RemoteUserPrincipalBackend;
use OCP\IConfig;
use OCP\IL10N;
use Psr\Log\LoggerInterface;
use Sabre\CalDAV\Backend;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAVACL\PrincipalBackend;
class CalendarRoot extends \Sabre\CalDAV\CalendarRoot {
@@ -26,30 +19,15 @@ class CalendarRoot extends \Sabre\CalDAV\CalendarRoot {
Backend\BackendInterface $caldavBackend,
$principalPrefix,
private LoggerInterface $logger,
private IL10N $l10n,
private IConfig $config,
private FederatedCalendarFactory $federatedCalendarFactory,
) {
parent::__construct($principalBackend, $caldavBackend, $principalPrefix);
}
public function getChildForPrincipal(array $principal) {
[$prefix] = \Sabre\Uri\split($principal['uri']);
if ($prefix === RemoteUserPrincipalBackend::PRINCIPAL_PREFIX) {
return new RemoteUserCalendarHome(
$this->caldavBackend,
$principal,
$this->l10n,
$this->config,
$this->logger,
);
}
return new CalendarHome(
$this->caldavBackend,
$principal,
$this->logger,
$this->federatedCalendarFactory,
array_key_exists($principal['uri'], $this->returnCachedSubscriptions)
);
}
@@ -62,35 +40,10 @@ class CalendarRoot extends \Sabre\CalDAV\CalendarRoot {
return $parts[1];
}
if ($this->principalPrefix === RemoteUserPrincipalBackend::PRINCIPAL_PREFIX) {
return 'remote-calendars';
}
return parent::getName();
}
public function enableReturnCachedSubscriptions(string $principalUri): void {
$this->returnCachedSubscriptions['principals/users/' . $principalUri] = true;
}
public function childExists($name) {
if (!($this->principalBackend instanceof Principal)) {
return parent::childExists($name);
}
// Fetch the most shallow version of the principal just to determine if it exists
$principalInfo = $this->principalBackend->getPrincipalPropertiesByPath(
$this->principalPrefix . '/' . $name,
[],
);
if ($principalInfo === null) {
return false;
}
try {
return $this->getChildForPrincipal($principalInfo) !== null;
} catch (NotFound $e) {
return false;
}
}
}
+1 -2
View File
@@ -13,7 +13,6 @@ use OCA\DAV\AppInfo\PluginManager;
use OCA\DAV\CalDAV\Auth\CustomPrincipalPlugin;
use OCA\DAV\CalDAV\Auth\PublicPrincipalPlugin;
use OCA\DAV\CalDAV\Publishing\PublishPlugin;
use OCA\DAV\CalDAV\Schedule\IMipPlugin;
use OCA\DAV\Connector\Sabre\AnonymousOptionsPlugin;
use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin;
use OCA\DAV\Connector\Sabre\CachingTree;
@@ -95,7 +94,7 @@ class EmbeddedCalDavServer {
Server::get(IURLGenerator::class)
));
if ($appConfig->getValueString('dav', 'sendInvitations', 'yes') === 'yes') {
$this->server->addPlugin(Server::get(IMipPlugin::class));
$this->server->addPlugin(Server::get(\OCA\DAV\CalDAV\Schedule\IMipPlugin::class));
}
// collection preload plugin
@@ -1,23 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCP\AppFramework\Services\IAppConfig;
class CalendarFederationConfig {
public function __construct(
private readonly IAppConfig $appConfig,
) {
}
public function isFederationEnabled(): bool {
return $this->appConfig->getAppValueBool('enableCalendarFederation', true);
}
}
@@ -1,68 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCP\Federation\ICloudFederationFactory;
use OCP\Federation\ICloudFederationProviderManager;
use OCP\Federation\ICloudId;
use OCP\Http\Client\IResponse;
use OCP\IURLGenerator;
use OCP\OCM\Exceptions\OCMProviderException;
class CalendarFederationNotifier {
public const NOTIFICATION_SYNC_CALENDAR = 'SYNC_CALENDAR';
public const PROP_SYNC_CALENDAR_SHARE_WITH = 'shareWith';
public const PROP_SYNC_CALENDAR_CALENDAR_URL = 'calendarUrl';
public function __construct(
private readonly ICloudFederationFactory $federationFactory,
private readonly ICloudFederationProviderManager $federationManager,
private readonly IURLGenerator $url,
) {
}
/**
* Notify a remote server to sync a calendar.
*
* @param ICloudId $shareWith The cloud id of the remote sharee.
* @return IResponse
*
* @throws OCMProviderException If sending the notification fails.
*/
public function notifySyncCalendar(
ICloudId $shareWith,
string $calendarOwner,
string $calendarName,
string $sharedSecret,
): IResponse {
$sharedWithEncoded = base64_encode($shareWith->getId());
$relativeCalendarUrl = "remote-calendars/$sharedWithEncoded/{$calendarName}_shared_by_$calendarOwner";
$calendarUrl = $this->url->linkTo('', 'remote.php') . "/dav/$relativeCalendarUrl";
$calendarUrl = $this->url->getAbsoluteURL($calendarUrl);
$notification = $this->federationFactory->getCloudFederationNotification();
$notification->setMessage(
self::NOTIFICATION_SYNC_CALENDAR,
CalendarFederationProvider::CALENDAR_RESOURCE,
CalendarFederationProvider::PROVIDER_ID,
[
'sharedSecret' => $sharedSecret,
self::PROP_SYNC_CALENDAR_SHARE_WITH => $shareWith->getId(),
self::PROP_SYNC_CALENDAR_CALENDAR_URL => $calendarUrl,
],
);
return $this->federationManager->sendCloudNotification(
$shareWith->getRemote(),
$notification,
);
}
}
@@ -1,203 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCA\DAV\BackgroundJob\FederatedCalendarSyncJob;
use OCA\DAV\CalDAV\Federation\Protocol\CalendarFederationProtocolV1;
use OCA\DAV\CalDAV\Federation\Protocol\ICalendarFederationProtocol;
use OCA\DAV\DAV\Sharing\Backend as DavSharingBackend;
use OCP\AppFramework\Http;
use OCP\BackgroundJob\IJobList;
use OCP\Constants;
use OCP\Federation\Exceptions\BadRequestException;
use OCP\Federation\Exceptions\ProviderCouldNotAddShareException;
use OCP\Federation\ICloudFederationProvider;
use OCP\Federation\ICloudFederationShare;
use OCP\Federation\ICloudIdManager;
use OCP\Share\Exceptions\ShareNotFound;
use Psr\Log\LoggerInterface;
class CalendarFederationProvider implements ICloudFederationProvider {
public const PROVIDER_ID = 'calendar';
public const CALENDAR_RESOURCE = 'calendar';
public const USER_SHARE_TYPE = 'user';
public function __construct(
private readonly LoggerInterface $logger,
private readonly FederatedCalendarMapper $federatedCalendarMapper,
private readonly CalendarFederationConfig $calendarFederationConfig,
private readonly IJobList $jobList,
private readonly ICloudIdManager $cloudIdManager,
) {
}
public function getShareType(): string {
return self::PROVIDER_ID;
}
public function shareReceived(ICloudFederationShare $share): string {
if (!$this->calendarFederationConfig->isFederationEnabled()) {
$this->logger->debug('Received a federation invite but federation is disabled');
throw new ProviderCouldNotAddShareException(
'Server does not support calendar federation',
'',
Http::STATUS_SERVICE_UNAVAILABLE,
);
}
if (!in_array($share->getShareType(), $this->getSupportedShareTypes(), true)) {
$this->logger->debug('Received a federation invite for invalid share type');
throw new ProviderCouldNotAddShareException(
'Support for sharing with non-users not implemented yet',
'',
Http::STATUS_NOT_IMPLEMENTED,
);
// TODO: Implement group shares
}
$rawProtocol = $share->getProtocol();
switch ($rawProtocol[ICalendarFederationProtocol::PROP_VERSION]) {
case CalendarFederationProtocolV1::VERSION:
try {
$protocol = CalendarFederationProtocolV1::parse($rawProtocol);
} catch (Protocol\CalendarProtocolParseException $e) {
throw new ProviderCouldNotAddShareException(
'Invalid protocol data (v1)',
'',
Http::STATUS_BAD_REQUEST,
);
}
$calendarUrl = $protocol->getUrl();
$displayName = $protocol->getDisplayName();
$color = $protocol->getColor();
$access = $protocol->getAccess();
$components = $protocol->getComponents();
break;
default:
throw new ProviderCouldNotAddShareException(
'Unknown protocol version',
'',
Http::STATUS_BAD_REQUEST,
);
}
if (!$calendarUrl || !$displayName) {
throw new ProviderCouldNotAddShareException(
'Incomplete protocol data',
'',
Http::STATUS_BAD_REQUEST,
);
}
// TODO: implement read-write sharing
$permissions = match ($access) {
DavSharingBackend::ACCESS_READ => Constants::PERMISSION_READ,
default => throw new ProviderCouldNotAddShareException(
"Unsupported access value: $access",
'',
Http::STATUS_BAD_REQUEST,
),
};
// The calendar uri is the local name of the calendar. As such it must not contain slashes.
// Just use the hashed url for simplicity here.
// Example: calendars/foo-bar-user/<calendar-uri>
$calendarUri = hash('md5', $calendarUrl);
$sharedWithPrincipal = 'principals/users/' . $share->getShareWith();
// Delete existing incoming federated share first
$this->federatedCalendarMapper->deleteByUri($sharedWithPrincipal, $calendarUri);
$calendar = new FederatedCalendarEntity();
$calendar->setPrincipaluri($sharedWithPrincipal);
$calendar->setUri($calendarUri);
$calendar->setRemoteUrl($calendarUrl);
$calendar->setDisplayName($displayName);
$calendar->setColor($color);
$calendar->setToken($share->getShareSecret());
$calendar->setSharedBy($share->getSharedBy());
$calendar->setSharedByDisplayName($share->getSharedByDisplayName());
$calendar->setPermissions($permissions);
$calendar->setComponents($components);
$calendar = $this->federatedCalendarMapper->insert($calendar);
$this->jobList->add(FederatedCalendarSyncJob::class, [
FederatedCalendarSyncJob::ARGUMENT_ID => $calendar->getId(),
]);
return (string)$calendar->getId();
}
public function notificationReceived(
$notificationType,
$providerId,
array $notification,
): array {
if ($providerId !== self::PROVIDER_ID) {
throw new BadRequestException(['providerId']);
}
switch ($notificationType) {
case CalendarFederationNotifier::NOTIFICATION_SYNC_CALENDAR:
return $this->handleSyncCalendarNotification($notification);
default:
return [];
}
}
/**
* @return string[]
*/
public function getSupportedShareTypes(): array {
return [self::USER_SHARE_TYPE];
}
/**
* @throws BadRequestException If notification props are missing.
* @throws ShareNotFound If the notification is not related to a known share.
*/
private function handleSyncCalendarNotification(array $notification): array {
$sharedSecret = $notification['sharedSecret'];
$shareWithRaw = $notification[CalendarFederationNotifier::PROP_SYNC_CALENDAR_SHARE_WITH] ?? null;
$calendarUrl = $notification[CalendarFederationNotifier::PROP_SYNC_CALENDAR_CALENDAR_URL] ?? null;
if ($shareWithRaw === null || $shareWithRaw === '') {
throw new BadRequestException([CalendarFederationNotifier::PROP_SYNC_CALENDAR_SHARE_WITH]);
}
if ($calendarUrl === null || $calendarUrl === '') {
throw new BadRequestException([CalendarFederationNotifier::PROP_SYNC_CALENDAR_CALENDAR_URL]);
}
try {
$shareWith = $this->cloudIdManager->resolveCloudId($shareWithRaw);
} catch (\InvalidArgumentException $e) {
throw new ShareNotFound('Invalid sharee cloud id');
}
$calendars = $this->federatedCalendarMapper->findByRemoteUrl(
$calendarUrl,
'principals/users/' . $shareWith->getUser(),
$sharedSecret,
);
if (empty($calendars)) {
throw new ShareNotFound('Calendar is not shared with the sharee');
}
foreach ($calendars as $calendar) {
$this->jobList->add(FederatedCalendarSyncJob::class, [
FederatedCalendarSyncJob::ARGUMENT_ID => $calendar->getId(),
]);
}
return [];
}
}
@@ -1,38 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Calendar;
use OCP\IConfig;
use OCP\IL10N;
use Psr\Log\LoggerInterface;
use Sabre\CalDAV\Backend;
class FederatedCalendar extends Calendar {
public function __construct(
Backend\BackendInterface $caldavBackend,
$calendarInfo,
IL10N $l10n,
IConfig $config,
LoggerInterface $logger,
private readonly FederatedCalendarMapper $federatedCalendarMapper,
) {
parent::__construct($caldavBackend, $calendarInfo, $l10n, $config, $logger);
}
public function delete() {
$this->federatedCalendarMapper->deleteById($this->getResourceId());
}
protected function getCalendarType(): int {
return CalDavBackend::CALENDAR_TYPE_FEDERATED;
}
}
@@ -1,81 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCA\DAV\DAV\RemoteUserPrincipalBackend;
use OCA\DAV\DAV\Sharing\SharingMapper;
use OCP\Defaults;
use Sabre\DAV\Auth\Backend\BackendInterface;
use Sabre\HTTP\Auth\Basic as BasicAuth;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
class FederatedCalendarAuth implements BackendInterface {
private readonly string $realm;
public function __construct(
private readonly SharingMapper $sharingMapper,
) {
$defaults = new Defaults();
$this->realm = $defaults->getName();
}
/**
* @return string|null A principal uri if the given combination of user and pass is valid and null otherwise.
*/
private function validateUserPass(
string $requestPath,
string $username,
string $password,
): ?string {
$remoteUserPrincipalUri = RemoteUserPrincipalBackend::PRINCIPAL_PREFIX . "/$username";
[, $remoteUserPrincipalId] = \Sabre\Uri\split($remoteUserPrincipalUri);
$rows = $this->sharingMapper->getSharedCalendarsForRemoteUser(
$remoteUserPrincipalUri,
$password,
);
// Is the requested calendar actually shared with the remote user?
foreach ($rows as $row) {
$ownerPrincipalUri = $row['principaluri'];
[, $ownerUserId] = \Sabre\Uri\split($ownerPrincipalUri);
$shareUri = $row['uri'] . '_shared_by_' . $ownerUserId;
if (str_starts_with($requestPath, "remote-calendars/$remoteUserPrincipalId/$shareUri")) {
// Yes? -> return early
return $remoteUserPrincipalUri;
}
}
return null;
}
public function check(RequestInterface $request, ResponseInterface $response): array {
if (!str_starts_with($request->getPath(), 'remote-calendars/')) {
return [false, 'This request is not for a federated calendar'];
}
$auth = new BasicAuth($this->realm, $request, $response);
$userpass = $auth->getCredentials();
if ($userpass === null || count($userpass) !== 2) {
return [false, "No 'Authorization: Basic' header found. Either the client didn't send one, or the server is misconfigured"];
}
$principal = $this->validateUserPass($request->getPath(), $userpass[0], $userpass[1]);
if ($principal === null) {
return [false, 'Username or password was incorrect'];
}
return [true, $principal];
}
public function challenge(RequestInterface $request, ResponseInterface $response): void {
// No special challenge is needed here
}
}
@@ -1,101 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCA\DAV\DAV\RemoteUserPrincipalBackend;
use OCP\AppFramework\Db\Entity;
use OCP\DB\Types;
use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
/**
* @method string getPrincipaluri()
* @method void setPrincipaluri(string $principaluri)
* @method string getUri()
* @method void setUri(string $uri)
* @method string getDisplayName()
* @method void setDisplayName(string $displayName)
* @method string|null getColor()
* @method void setColor(string|null $color)
* @method int getPermissions()
* @method void setPermissions(int $permissions)
* @method int getSyncToken()
* @method void setSyncToken(int $syncToken)
* @method string getRemoteUrl()
* @method void setRemoteUrl(string $remoteUrl)
* @method string getToken()
* @method void setToken(string $token)
* @method int|null getLastSync()
* @method void setLastSync(int|null $lastSync)
* @method string getSharedBy()
* @method void setSharedBy(string $sharedBy)
* @method string getSharedByDisplayName()
* @method void setSharedByDisplayName(string $sharedByDisplayName)
* @method string getComponents()
* @method void setComponents(string $components)
*/
class FederatedCalendarEntity extends Entity {
protected string $principaluri = '';
protected string $uri = '';
protected string $displayName = '';
protected ?string $color = null;
protected int $permissions = 0;
protected int $syncToken = 0;
protected string $remoteUrl = '';
protected string $token = '';
protected ?int $lastSync = null;
protected string $sharedBy = '';
protected string $sharedByDisplayName = '';
protected string $components = '';
public function __construct() {
$this->addType('principaluri', Types::STRING);
$this->addType('uri', Types::STRING);
$this->addType('color', Types::STRING);
$this->addType('displayName', Types::STRING);
$this->addType('permissions', Types::INTEGER);
$this->addType('syncToken', Types::INTEGER);
$this->addType('remoteUrl', Types::STRING);
$this->addType('token', Types::STRING);
$this->addType('lastSync', Types::INTEGER);
$this->addType('sharedBy', Types::STRING);
$this->addType('sharedByDisplayName', Types::STRING);
$this->addType('components', Types::STRING);
}
public function getSyncTokenForSabre(): string {
return 'http://sabre.io/ns/sync/' . $this->getSyncToken();
}
public function getSharedByPrincipal(): string {
return RemoteUserPrincipalBackend::PRINCIPAL_PREFIX . '/' . base64_encode($this->getSharedBy());
}
public function getSupportedCalendarComponentSet(): SupportedCalendarComponentSet {
$components = explode(',', $this->getComponents());
return new SupportedCalendarComponentSet($components);
}
public function toCalendarInfo(): array {
return [
'id' => $this->getId(),
'uri' => $this->getUri(),
'principaluri' => $this->getPrincipaluri(),
'federated' => 1,
'{DAV:}displayname' => $this->getDisplayName(),
'{http://sabredav.org/ns}sync-token' => $this->getSyncToken(),
'{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => $this->getSyncTokenForSabre(),
'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => $this->getSupportedCalendarComponentSet(),
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->getSharedByPrincipal(),
// TODO: implement read-write sharing
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => 1
];
}
}
@@ -1,42 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCA\DAV\AppInfo\Application;
use OCA\DAV\CalDAV\CalDavBackend;
use OCP\IConfig;
use OCP\IL10N;
use OCP\L10N\IFactory as IL10NFactory;
use Psr\Log\LoggerInterface;
class FederatedCalendarFactory {
private readonly IL10N $l10n;
public function __construct(
private readonly CalDavBackend $caldavBackend,
private readonly IConfig $config,
private readonly LoggerInterface $logger,
private readonly FederatedCalendarMapper $federatedCalendarMapper,
IL10NFactory $l10nFactory,
) {
$this->l10n = $l10nFactory->get(Application::APP_ID);
}
public function createFederatedCalendar(array $calendarInfo): FederatedCalendar {
return new FederatedCalendar(
$this->caldavBackend,
$calendarInfo,
$this->l10n,
$this->config,
$this->logger,
$this->federatedCalendarMapper,
);
}
}
@@ -1,73 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCA\DAV\CalDAV\CalDavBackend;
use OCP\Calendar\ICalendar;
use OCP\Calendar\ICalendarIsEnabled;
use OCP\Calendar\ICalendarIsShared;
use OCP\Calendar\ICalendarIsWritable;
use OCP\Constants;
class FederatedCalendarImpl implements ICalendar, ICalendarIsShared, ICalendarIsWritable, ICalendarIsEnabled {
public function __construct(
private readonly array $calendarInfo,
private readonly CalDavBackend $calDavBackend,
) {
}
public function getKey(): string {
return (string)$this->calendarInfo['id'];
}
public function getUri(): string {
return $this->calendarInfo['uri'];
}
public function getDisplayName(): ?string {
return $this->calendarInfo['{DAV:}displayname'];
}
public function getDisplayColor(): ?string {
return $this->calendarInfo['{http://apple.com/ns/ical/}calendar-color'];
}
public function search(string $pattern, array $searchProperties = [], array $options = [], ?int $limit = null, ?int $offset = null): array {
return $this->calDavBackend->search(
$this->calendarInfo,
$pattern,
$searchProperties,
$options,
$limit,
$offset,
);
}
public function getPermissions(): int {
// TODO: implement read-write sharing
return Constants::PERMISSION_READ;
}
public function isDeleted(): bool {
return false;
}
public function isShared(): bool {
return true;
}
public function isWritable(): bool {
return false;
}
public function isEnabled(): bool {
return $this->calendarInfo['{http://owncloud.org/ns}calendar-enabled'] ?? true;
}
}
@@ -1,209 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\AppFramework\Db\QBMapper;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
/** @template-extends QBMapper<FederatedCalendarEntity> */
class FederatedCalendarMapper extends QBMapper {
public const TABLE_NAME = 'calendars_federated';
public function __construct(
IDBConnection $db,
private readonly ITimeFactory $time,
) {
parent::__construct($db, self::TABLE_NAME, FederatedCalendarEntity::class);
}
/**
* @throws DoesNotExistException If there is no federated calendar with the given id.
*/
public function find(int $id): FederatedCalendarEntity {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from(self::TABLE_NAME)
->where($qb->expr()->eq(
'id',
$qb->createNamedParameter($id, IQueryBuilder::PARAM_INT),
IQueryBuilder::PARAM_INT,
));
return $this->findEntity($qb);
}
/**
* @return FederatedCalendarEntity[]
*/
public function findByPrincipalUri(string $principalUri): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from(self::TABLE_NAME)
->where($qb->expr()->eq(
'principaluri',
$qb->createNamedParameter($principalUri, IQueryBuilder::PARAM_STR),
IQueryBuilder::PARAM_STR,
));
return $this->findEntities($qb);
}
public function findByUri(string $principalUri, string $uri): ?FederatedCalendarEntity {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from(self::TABLE_NAME)
->where($qb->expr()->eq(
'principaluri',
$qb->createNamedParameter($principalUri, IQueryBuilder::PARAM_STR),
IQueryBuilder::PARAM_STR,
))
->andWhere($qb->expr()->eq(
'uri',
$qb->createNamedParameter($uri, IQueryBuilder::PARAM_STR),
IQueryBuilder::PARAM_STR,
));
try {
return $this->findEntity($qb);
} catch (DoesNotExistException $e) {
return null;
} catch (MultipleObjectsReturnedException $e) {
// Should never happen
return null;
}
}
/**
* @return FederatedCalendarEntity[]
*/
public function findUnsyncedSinceBefore(int $beforeTimestamp): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from(self::TABLE_NAME)
->where($qb->expr()->lt(
'last_sync',
$qb->createNamedParameter($beforeTimestamp, IQueryBuilder::PARAM_INT),
IQueryBuilder::PARAM_INT,
))
// Omit unsynced calendars for now as they are synced by a separate job
->andWhere($qb->expr()->isNotNull('last_sync'));
return $this->findEntities($qb);
}
public function deleteById(int $id): void {
$qb = $this->db->getQueryBuilder();
$qb->delete(self::TABLE_NAME)
->where($qb->expr()->eq(
'id',
$qb->createNamedParameter($id, IQueryBuilder::PARAM_INT),
IQueryBuilder::PARAM_INT,
));
$qb->executeStatement();
}
public function updateSyncTime(int $id): void {
$now = $this->time->getTime();
$qb = $this->db->getQueryBuilder();
$qb->update(self::TABLE_NAME)
->set('last_sync', $qb->createNamedParameter($now, IQueryBuilder::PARAM_INT))
->where($qb->expr()->eq(
'id',
$qb->createNamedParameter($id, IQueryBuilder::PARAM_INT),
IQueryBuilder::PARAM_INT,
));
$qb->executeStatement();
}
public function updateSyncTokenAndTime(int $id, int $syncToken): void {
$now = $this->time->getTime();
$qb = $this->db->getQueryBuilder();
$qb->update(self::TABLE_NAME)
->set('sync_token', $qb->createNamedParameter($syncToken, IQueryBuilder::PARAM_INT))
->set('last_sync', $qb->createNamedParameter($now, IQueryBuilder::PARAM_INT))
->where($qb->expr()->eq(
'id',
$qb->createNamedParameter($id, IQueryBuilder::PARAM_INT),
IQueryBuilder::PARAM_INT,
));
$qb->executeStatement();
}
/**
* @return \Generator<mixed, FederatedCalendarEntity>
*/
public function findAll(): \Generator {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from(self::TABLE_NAME);
$result = $qb->executeQuery();
while ($row = $result->fetch()) {
yield $this->mapRowToEntity($row);
}
$result->closeCursor();
}
public function countAll(): int {
$qb = $this->db->getQueryBuilder();
$qb->select($qb->func()->count('*'))
->from(self::TABLE_NAME);
$result = $qb->executeQuery();
$count = (int)$result->fetchOne();
$result->closeCursor();
return $count;
}
public function deleteByUri(string $principalUri, string $uri): void {
$qb = $this->db->getQueryBuilder();
$qb->delete(self::TABLE_NAME)
->where($qb->expr()->eq(
'principaluri',
$qb->createNamedParameter($principalUri, IQueryBuilder::PARAM_STR),
IQueryBuilder::PARAM_STR,
))
->andWhere($qb->expr()->eq(
'uri',
$qb->createNamedParameter($uri, IQueryBuilder::PARAM_STR),
IQueryBuilder::PARAM_STR,
));
$qb->executeStatement();
}
/**
* @return FederatedCalendarEntity[]
*/
public function findByRemoteUrl(string $remoteUrl, string $principalUri, string $token): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from(self::TABLE_NAME)
->where($qb->expr()->eq(
'remote_url',
$qb->createNamedParameter($remoteUrl, IQueryBuilder::PARAM_STR),
IQueryBuilder::PARAM_STR,
))
->andWhere($qb->expr()->eq(
'principaluri',
$qb->createNamedParameter($principalUri, IQueryBuilder::PARAM_STR),
IQueryBuilder::PARAM_STR,
))
->andWhere($qb->expr()->eq(
'token',
$qb->createNamedParameter($token, IQueryBuilder::PARAM_STR),
IQueryBuilder::PARAM_STR,
));
return $this->findEntities($qb);
}
}
@@ -1,76 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCA\DAV\CalDAV\SyncService as CalDavSyncService;
use OCP\Federation\ICloudIdManager;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Log\LoggerInterface;
class FederatedCalendarSyncService {
private const SYNC_TOKEN_PREFIX = 'http://sabre.io/ns/sync/';
public function __construct(
private readonly FederatedCalendarMapper $federatedCalendarMapper,
private readonly LoggerInterface $logger,
private readonly CalDavSyncService $syncService,
private readonly ICloudIdManager $cloudIdManager,
) {
}
/**
* @return int Downloaded event count (created or updated).
*
* @throws ClientExceptionInterface If syncing the calendar fails.
*/
public function syncOne(FederatedCalendarEntity $calendar): int {
[,, $sharedWith] = explode('/', $calendar->getPrincipaluri());
$calDavUser = $this->cloudIdManager->getCloudId($sharedWith, null)->getId();
$remoteUrl = $calendar->getRemoteUrl();
$syncToken = $calendar->getSyncTokenForSabre();
// Need to encode the cloud id as it might contain a colon which is not allowed in basic
// auth according to RFC 7617
$calDavUser = base64_encode($calDavUser);
$syncResponse = $this->syncService->syncRemoteCalendar(
$remoteUrl,
$calDavUser,
$calendar->getToken(),
$syncToken,
$calendar,
);
$newSyncToken = $syncResponse->getSyncToken();
// Check sync token format and extract the actual sync token integer
$matches = [];
if (!preg_match('/^http:\/\/sabre\.io\/ns\/sync\/([0-9]+)$/', $newSyncToken, $matches)) {
$this->logger->error("Failed to sync federated calendar at $remoteUrl: New sync token has unexpected format: $newSyncToken", [
'calendar' => $calendar->toCalendarInfo(),
'newSyncToken' => $newSyncToken,
]);
return 0;
}
$newSyncToken = (int)$matches[1];
if ($newSyncToken !== $calendar->getSyncToken()) {
$this->federatedCalendarMapper->updateSyncTokenAndTime(
$calendar->getId(),
$newSyncToken,
);
} else {
$this->logger->debug("Sync Token for $remoteUrl unchanged from previous sync");
$this->federatedCalendarMapper->updateSyncTime($calendar->getId());
}
return $syncResponse->getDownloadedEvents();
}
}
@@ -1,180 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCA\DAV\CalDAV\Federation\Protocol\CalendarFederationProtocolV1;
use OCA\DAV\DAV\RemoteUserPrincipalBackend;
use OCA\DAV\DAV\Sharing\IShareable;
use OCA\DAV\DAV\Sharing\SharingMapper;
use OCP\AppFramework\Http;
use OCP\Federation\ICloudFederationFactory;
use OCP\Federation\ICloudFederationProviderManager;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\OCM\Exceptions\OCMProviderException;
use OCP\Security\ISecureRandom;
use Psr\Log\LoggerInterface;
use Sabre\CalDAV\Calendar;
// TODO: Convert this to an abstract service like the addressbook/calendar sharing services once we
// support addressbook federation as well.
class FederationSharingService {
public function __construct(
private readonly ICloudFederationProviderManager $federationManager,
private readonly ICloudFederationFactory $federationFactory,
private readonly IUserManager $userManager,
private readonly IURLGenerator $url,
private readonly LoggerInterface $logger,
private readonly ISecureRandom $random,
private readonly SharingMapper $sharingMapper,
) {
}
/**
* Decode a (base64) encoded remote user principal and return the remote user's cloud id. Will
* return null if the given principal is not belonging to a remote user (or has an invalid
* format).
*
* The remote user/cloud id needs to be encoded as it might contain slashes.
*/
private function decodeRemoteUserPrincipal(string $principal): ?string {
// Expected format: principals/remote-users/abcdef123
[$prefix, $collection, $encodedId] = explode('/', $principal);
if ($prefix !== 'principals' || $collection !== 'remote-users') {
return null;
}
$decodedId = base64_decode($encodedId);
if (!is_string($decodedId)) {
return null;
}
return $decodedId;
}
/**
* Send a calendar share to a remote instance and create a federated share locally if it is
* accepted.
*
* @param IShareable $shareable The calendar to be shared.
* @param string $principal The principal to share with (should be a remote user principal).
* @param int $access The access level. The remote serve might reject it.
*/
public function shareWith(IShareable $shareable, string $principal, int $access): void {
$baseError = 'Failed to create federated calendar share: ';
// 1. Validate share data
$shareWith = $this->decodeRemoteUserPrincipal($principal);
if ($shareWith === null) {
$this->logger->error($baseError . 'Principal of sharee is not belonging to a remote user', [
'shareable' => $shareable->getName(),
'encodedShareWith' => $principal,
]);
return;
}
[,, $ownerUid] = explode('/', $shareable->getOwner());
$owner = $this->userManager->get($ownerUid);
if ($owner === null) {
$this->logger->error($baseError . 'Shareable is not owned by a user on this server', [
'shareable' => $shareable->getName(),
'shareWith' => $shareWith,
]);
return;
}
// Need a calendar instance to extract properties for the protocol
$calendar = $shareable;
if (!($calendar instanceof Calendar)) {
$this->logger->error($baseError . 'Shareable is not a calendar', [
'shareable' => $shareable->getName(),
'owner' => $owner,
'shareWith' => $shareWith,
]);
return;
}
$getProp = static fn (string $prop) => $calendar->getProperties([$prop])[$prop] ?? null;
$displayName = $getProp('{DAV:}displayname') ?? '';
$token = $this->random->generate(32);
$share = $this->federationFactory->getCloudFederationShare(
$shareWith,
$shareable->getName(),
$displayName,
CalendarFederationProvider::PROVIDER_ID,
// Resharing is not possible so the owner is always the sharer
$owner->getCloudId(),
$owner->getDisplayName(),
$owner->getCloudId(),
$owner->getDisplayName(),
$token,
CalendarFederationProvider::USER_SHARE_TYPE,
CalendarFederationProvider::CALENDAR_RESOURCE,
);
// 2. Send share to federated instance
$shareWithEncoded = base64_encode($shareWith);
$relativeCalendarUrl = "remote-calendars/$shareWithEncoded/" . $calendar->getName() . '_shared_by_' . $ownerUid;
$calendarUrl = $this->url->linkTo('', 'remote.php') . "/dav/$relativeCalendarUrl";
$calendarUrl = $this->url->getAbsoluteURL($calendarUrl);
$protocol = new CalendarFederationProtocolV1();
$protocol->setUrl($calendarUrl);
$protocol->setDisplayName($displayName);
$protocol->setColor($getProp('{http://apple.com/ns/ical/}calendar-color'));
$protocol->setAccess($access);
$protocol->setComponents(implode(',', $getProp(
'{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set')?->getValue() ?? [],
));
$share->setProtocol([
// Preserve original protocol contents
...$share->getProtocol(),
...$protocol->toProtocol(),
]);
try {
$response = $this->federationManager->sendCloudShare($share);
} catch (OCMProviderException $e) {
$this->logger->error($baseError . $e->getMessage(), [
'exception' => $e,
'owner' => $owner->getUID(),
'calendar' => $shareable->getName(),
'shareWith' => $shareWith,
]);
return;
}
if ($response->getStatusCode() !== Http::STATUS_CREATED) {
$this->logger->error($baseError . 'Server replied with code ' . $response->getStatusCode(), [
'responseBody' => $response->getBody(),
'owner' => $owner->getUID(),
'calendar' => $shareable->getName(),
'shareWith' => $shareWith,
]);
return;
}
// 3. Create a local DAV share to track the token for authentication
$shareWithPrincipalUri = RemoteUserPrincipalBackend::PRINCIPAL_PREFIX . '/' . $shareWithEncoded;
$this->sharingMapper->deleteShare(
$shareable->getResourceId(),
'calendar',
$shareWithPrincipalUri,
);
$this->sharingMapper->shareWithToken(
$shareable->getResourceId(),
'calendar',
$access,
$shareWithPrincipalUri,
$token,
);
}
}
@@ -1,125 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation\Protocol;
class CalendarFederationProtocolV1 implements ICalendarFederationProtocol {
public const VERSION = 'v1';
public const PROP_URL = 'url';
public const PROP_DISPLAY_NAME = 'displayName';
public const PROP_COLOR = 'color';
public const PROP_ACCESS = 'access';
public const PROP_COMPONENTS = 'components';
private string $url = '';
private string $displayName = '';
private ?string $color = null;
private int $access = 0;
private string $components = '';
/**
* @throws CalendarProtocolParseException If parsing the raw protocol array fails.
*/
public static function parse(array $rawProtocol): self {
if ($rawProtocol[self::PROP_VERSION] !== self::VERSION) {
throw new CalendarProtocolParseException('Unknown protocol version');
}
$url = $rawProtocol[self::PROP_URL] ?? null;
if (!is_string($url)) {
throw new CalendarProtocolParseException('URL is missing or not a string');
}
$displayName = $rawProtocol[self::PROP_DISPLAY_NAME] ?? null;
if (!is_string($displayName)) {
throw new CalendarProtocolParseException('Display name is missing or not a string');
}
$color = $rawProtocol[self::PROP_COLOR] ?? null;
if (!is_string($color) && $color !== null) {
throw new CalendarProtocolParseException('Color is set but not a string');
}
$access = $rawProtocol[self::PROP_ACCESS] ?? null;
if (!is_int($access)) {
throw new CalendarProtocolParseException('Access is missing or not an integer');
}
$components = $rawProtocol[self::PROP_COMPONENTS] ?? null;
if (!is_string($components)) {
throw new CalendarProtocolParseException('Supported calendar components are missing or not a string');
}
$protocol = new self();
$protocol->setUrl($url);
$protocol->setDisplayName($displayName);
$protocol->setColor($color);
$protocol->setAccess($access);
$protocol->setComponents($components);
return $protocol;
}
#[\Override]
public function toProtocol(): array {
return [
self::PROP_VERSION => $this->getVersion(),
self::PROP_URL => $this->getUrl(),
self::PROP_DISPLAY_NAME => $this->getDisplayName(),
self::PROP_COLOR => $this->getColor(),
self::PROP_ACCESS => $this->getAccess(),
self::PROP_COMPONENTS => $this->getComponents(),
];
}
#[\Override]
public function getVersion(): string {
return self::VERSION;
}
public function getUrl(): string {
return $this->url;
}
public function setUrl(string $url): void {
$this->url = $url;
}
public function getDisplayName(): string {
return $this->displayName;
}
public function setDisplayName(string $displayName): void {
$this->displayName = $displayName;
}
public function getColor(): ?string {
return $this->color;
}
public function setColor(?string $color): void {
$this->color = $color;
}
public function getAccess(): int {
return $this->access;
}
public function setAccess(int $access): void {
$this->access = $access;
}
public function getComponents(): string {
return $this->components;
}
public function setComponents(string $components): void {
$this->components = $components;
}
}
@@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation\Protocol;
class CalendarProtocolParseException extends \Exception {
}
@@ -1,25 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation\Protocol;
interface ICalendarFederationProtocol {
public const PROP_VERSION = 'version';
/**
* Get the version of this protocol implementation.
*/
public function getVersion(): string;
/**
* Convert the protocol to an associative array to be sent to a remote instance.
* The resulting array still needs to be merged with the base protocol from the share!
*/
public function toProtocol(): array;
}
@@ -1,67 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Federation;
use OCA\DAV\CalDAV\Calendar;
use OCP\IConfig;
use OCP\IL10N;
use Psr\Log\LoggerInterface;
use Sabre\CalDAV\Backend;
use Sabre\CalDAV\CalendarHome;
use Sabre\DAV\Exception\NotFound;
class RemoteUserCalendarHome extends CalendarHome {
public function __construct(
Backend\BackendInterface $caldavBackend,
$principalInfo,
private readonly IL10N $l10n,
private readonly IConfig $config,
private readonly LoggerInterface $logger,
) {
parent::__construct($caldavBackend, $principalInfo);
}
public function getChild($name) {
// Remote users can only have incoming shared calendars so we can skip the rest of a regular
// calendar home
foreach ($this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']) as $calendar) {
if ($calendar['uri'] === $name) {
return new Calendar(
$this->caldavBackend,
$calendar,
$this->l10n,
$this->config,
$this->logger,
);
}
}
throw new NotFound("Node with name $name could not be found");
}
public function getChildren(): array {
$objects = [];
// Remote users can only have incoming shared calendars so we can skip the rest of a regular
// calendar home
$calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
foreach ($calendars as $calendar) {
$objects[] = new Calendar(
$this->caldavBackend,
$calendar,
$this->l10n,
$this->config,
$this->logger,
);
}
return $objects;
}
}
@@ -1,334 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Import;
use Exception;
use Generator;
use InvalidArgumentException;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\CalendarImpl;
use OCP\Calendar\CalendarImportOptions;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Node;
use Sabre\VObject\Reader;
use Sabre\VObject\UUIDUtil;
/**
* Calendar Import Service
*/
class ImportService {
/** @var resource */
private $source;
public function __construct(
private CalDavBackend $backend,
) {
}
/**
* Executes import with appropriate object generator based on format
*
* @param resource $source
*
* @return array<string,array<string,string|array<string>>>
*
* @throws \InvalidArgumentException
*/
public function import($source, CalendarImpl $calendar, CalendarImportOptions $options): array {
if (!is_resource($source)) {
throw new InvalidArgumentException('Invalid import source must be a file resource');
}
$this->source = $source;
switch ($options->getFormat()) {
case 'ical':
return $this->importProcess($calendar, $options, $this->importText(...));
break;
case 'jcal':
return $this->importProcess($calendar, $options, $this->importJson(...));
break;
case 'xcal':
return $this->importProcess($calendar, $options, $this->importXml(...));
break;
default:
throw new InvalidArgumentException('Invalid import format');
}
}
/**
* Generates object stream from a text formatted source (ical)
*
* @return Generator<\Sabre\VObject\Component\VCalendar>
*/
private function importText(): Generator {
$importer = new TextImporter($this->source);
$structure = $importer->structure();
$sObjectPrefix = $importer::OBJECT_PREFIX;
$sObjectSuffix = $importer::OBJECT_SUFFIX;
// calendar properties
foreach ($structure['VCALENDAR'] as $entry) {
if (!str_ends_with($entry, "\n") || !str_ends_with($entry, "\r\n")) {
$sObjectPrefix .= PHP_EOL;
}
}
// calendar time zones
$timezones = [];
foreach ($structure['VTIMEZONE'] as $tid => $collection) {
$instance = $collection[0];
$sObjectContents = $importer->extract((int)$instance[2], (int)$instance[3]);
$vObject = Reader::read($sObjectPrefix . $sObjectContents . $sObjectSuffix);
$timezones[$tid] = clone $vObject->VTIMEZONE;
}
// calendar components
// for each component type, construct a full calendar object with all components
// that match the same UID and appropriate time zones that are used in the components
foreach (['VEVENT', 'VTODO', 'VJOURNAL'] as $type) {
foreach ($structure[$type] as $cid => $instances) {
/** @var array<int,VCalendar> $instances */
// extract all instances of component and unserialize to object
$sObjectContents = '';
foreach ($instances as $instance) {
$sObjectContents .= $importer->extract($instance[2], $instance[3]);
}
/** @var VCalendar $vObject */
$vObject = Reader::read($sObjectPrefix . $sObjectContents . $sObjectSuffix);
// add time zones to object
foreach ($this->findTimeZones($vObject) as $zone) {
if (isset($timezones[$zone])) {
$vObject->add(clone $timezones[$zone]);
}
}
yield $vObject;
}
}
}
/**
* Generates object stream from a xml formatted source (xcal)
*
* @return Generator<\Sabre\VObject\Component\VCalendar>
*/
private function importXml(): Generator {
$importer = new XmlImporter($this->source);
$structure = $importer->structure();
$sObjectPrefix = $importer::OBJECT_PREFIX;
$sObjectSuffix = $importer::OBJECT_SUFFIX;
// calendar time zones
$timezones = [];
foreach ($structure['VTIMEZONE'] as $tid => $collection) {
$instance = $collection[0];
$sObjectContents = $importer->extract((int)$instance[2], (int)$instance[3]);
$vObject = Reader::readXml($sObjectPrefix . $sObjectContents . $sObjectSuffix);
$timezones[$tid] = clone $vObject->VTIMEZONE;
}
// calendar components
// for each component type, construct a full calendar object with all components
// that match the same UID and appropriate time zones that are used in the components
foreach (['VEVENT', 'VTODO', 'VJOURNAL'] as $type) {
foreach ($structure[$type] as $cid => $instances) {
/** @var array<int,VCalendar> $instances */
// extract all instances of component and unserialize to object
$sObjectContents = '';
foreach ($instances as $instance) {
$sObjectContents .= $importer->extract($instance[2], $instance[3]);
}
/** @var VCalendar $vObject */
$vObject = Reader::readXml($sObjectPrefix . $sObjectContents . $sObjectSuffix);
// add time zones to object
foreach ($this->findTimeZones($vObject) as $zone) {
if (isset($timezones[$zone])) {
$vObject->add(clone $timezones[$zone]);
}
}
yield $vObject;
}
}
}
/**
* Generates object stream from a json formatted source (jcal)
*
* @return Generator<\Sabre\VObject\Component\VCalendar>
*/
private function importJson(): Generator {
/** @var VCALENDAR $importer */
$importer = Reader::readJson($this->source);
// calendar time zones
$timezones = [];
foreach ($importer->VTIMEZONE as $timezone) {
$tzid = $timezone->TZID?->getValue();
if ($tzid !== null) {
$timezones[$tzid] = clone $timezone;
}
}
// calendar components
foreach ($importer->getBaseComponents() as $base) {
$vObject = new VCalendar;
$vObject->VERSION = clone $importer->VERSION;
$vObject->PRODID = clone $importer->PRODID;
// extract all instances of component
foreach ($importer->getByUID($base->UID->getValue()) as $instance) {
$vObject->add(clone $instance);
}
// add time zones to object
foreach ($this->findTimeZones($vObject) as $zone) {
if (isset($timezones[$zone])) {
$vObject->add(clone $timezones[$zone]);
}
}
yield $vObject;
}
}
/**
* Searches through all component properties looking for defined timezones
*
* @return array<string>
*/
private function findTimeZones(VCalendar $vObject): array {
$timezones = [];
foreach ($vObject->getComponents() as $vComponent) {
if ($vComponent->name !== 'VTIMEZONE') {
foreach (['DTSTART', 'DTEND', 'DUE', 'RDATE', 'EXDATE'] as $property) {
if (isset($vComponent->$property?->parameters['TZID'])) {
$tid = $vComponent->$property->parameters['TZID']->getValue();
$timezones[$tid] = true;
}
}
}
}
return array_keys($timezones);
}
/**
* Import objects
*
* @since 32.0.0
*
* @param CalendarImportOptions $options
* @param callable $generator<CalendarImportOptions>: Generator<\Sabre\VObject\Component\VCalendar>
*
* @return array<string,array<string,string|array<string>>>
*/
public function importProcess(CalendarImpl $calendar, CalendarImportOptions $options, callable $generator): array {
$calendarId = $calendar->getKey();
$calendarUri = $calendar->getUri();
$principalUri = $calendar->getPrincipalUri();
$outcome = [];
foreach ($generator() as $vObject) {
$components = $vObject->getBaseComponents();
// determine if the object has no base component types
if (count($components) === 0) {
$errorMessage = 'One or more objects discovered with no base component types';
if ($options->getErrors() === $options::ERROR_FAIL) {
throw new InvalidArgumentException('Error importing calendar data: ' . $errorMessage);
}
$outcome['nbct'] = ['outcome' => 'error', 'errors' => [$errorMessage]];
continue;
}
// determine if the object has more than one base component type
// object can have multiple base components with the same uid
// but we need to make sure they are of the same type
if (count($components) > 1) {
$type = $components[0]->name;
foreach ($components as $entry) {
if ($type !== $entry->name) {
$errorMessage = 'One or more objects discovered with multiple base component types';
if ($options->getErrors() === $options::ERROR_FAIL) {
throw new InvalidArgumentException('Error importing calendar data: ' . $errorMessage);
}
$outcome['mbct'] = ['outcome' => 'error', 'errors' => [$errorMessage]];
continue 2;
}
}
}
// determine if the object has a uid
if (!isset($components[0]->UID)) {
$errorMessage = 'One or more objects discovered without a UID';
if ($options->getErrors() === $options::ERROR_FAIL) {
throw new InvalidArgumentException('Error importing calendar data: ' . $errorMessage);
}
$outcome['noid'] = ['outcome' => 'error', 'errors' => [$errorMessage]];
continue;
}
$uid = $components[0]->UID->getValue();
// validate object
if ($options->getValidate() !== $options::VALIDATE_NONE) {
$issues = $this->componentValidate($vObject, true, 3);
if ($options->getValidate() === $options::VALIDATE_SKIP && $issues !== []) {
$outcome[$uid] = ['outcome' => 'error', 'errors' => $issues];
continue;
} elseif ($options->getValidate() === $options::VALIDATE_FAIL && $issues !== []) {
throw new InvalidArgumentException('Error importing calendar data: UID <' . $uid . '> - ' . $issues[0]);
}
}
// create or update object in the data store
$objectId = $this->backend->getCalendarObjectByUID($principalUri, $uid, $calendarUri);
$objectData = $vObject->serialize();
try {
if ($objectId === null) {
$objectId = UUIDUtil::getUUID();
$this->backend->createCalendarObject(
$calendarId,
$objectId,
$objectData
);
$outcome[$uid] = ['outcome' => 'created'];
} else {
[$cid, $oid] = explode('/', $objectId);
if ($options->getSupersede()) {
$this->backend->updateCalendarObject(
$calendarId,
$oid,
$objectData
);
$outcome[$uid] = ['outcome' => 'updated'];
} else {
$outcome[$uid] = ['outcome' => 'exists'];
}
}
} catch (Exception $e) {
$errorMessage = $e->getMessage();
if ($options->getErrors() === $options::ERROR_FAIL) {
throw new Exception('Error importing calendar data: UID <' . $uid . '> - ' . $errorMessage, 0, $e);
}
$outcome[$uid] = ['outcome' => 'error', 'errors' => [$errorMessage]];
}
}
return $outcome;
}
/**
* Validate a component
*
* @param VCalendar $vObject
* @param bool $repair attempt to repair the component
* @param int $level minimum level of issues to return
* @return list<mixed>
*/
private function componentValidate(VCalendar $vObject, bool $repair, int $level): array {
// validate component(S)
$issues = $vObject->validate(Node::PROFILE_CALDAV);
// attempt to repair
if ($repair && count($issues) > 0) {
$issues = $vObject->validate(Node::REPAIR);
}
// filter out messages based on level
$result = [];
foreach ($issues as $key => $issue) {
if (isset($issue['level']) && $issue['level'] >= $level) {
$result[] = $issue['message'];
}
}
return $result;
}
}
-156
View File
@@ -1,156 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Import;
use Exception;
class TextImporter {
public const OBJECT_PREFIX = 'BEGIN:VCALENDAR' . PHP_EOL;
public const OBJECT_SUFFIX = PHP_EOL . 'END:VCALENDAR';
private const COMPONENT_TYPES = ['VEVENT', 'VTODO', 'VJOURNAL', 'VTIMEZONE'];
private bool $analyzed = false;
private array $structure = ['VCALENDAR' => [], 'VEVENT' => [], 'VTODO' => [], 'VJOURNAL' => [], 'VTIMEZONE' => []];
/**
* @param resource $source
*/
public function __construct(
private $source,
) {
// Ensure that source is a stream resource
if (!is_resource($source) || get_resource_type($source) !== 'stream') {
throw new Exception('Source must be a stream resource');
}
}
/**
* Analyzes the source data and creates a structure of components
*/
private function analyze() {
$componentStart = null;
$componentEnd = null;
$componentId = null;
$componentType = null;
$tagName = null;
$tagValue = null;
// iterate through the source data line by line
fseek($this->source, 0);
while (!feof($this->source)) {
$data = fgets($this->source);
// skip empty lines
if ($data === false || empty(trim($data))) {
continue;
}
// lines with whitespace at the beginning are continuations of the previous line
if (ctype_space($data[0]) === false) {
// detect the line TAG
// detect the first occurrence of ':' or ';'
$colonPos = strpos($data, ':');
$semicolonPos = strpos($data, ';');
if ($colonPos !== false && $semicolonPos !== false) {
$splitPosition = min($colonPos, $semicolonPos);
} elseif ($colonPos !== false) {
$splitPosition = $colonPos;
} elseif ($semicolonPos !== false) {
$splitPosition = $semicolonPos;
} else {
continue;
}
$tagName = strtoupper(trim(substr($data, 0, $splitPosition)));
$tagValue = trim(substr($data, $splitPosition + 1));
$tagContinuation = false;
} else {
$tagContinuation = true;
$tagValue .= trim($data);
}
if ($tagContinuation === false) {
// check line for component start, remember the position and determine the type
if ($tagName === 'BEGIN' && in_array($tagValue, self::COMPONENT_TYPES, true)) {
$componentStart = ftell($this->source) - strlen($data);
$componentType = $tagValue;
}
// check line for component end, remember the position
if ($tagName === 'END' && $componentType === $tagValue) {
$componentEnd = ftell($this->source);
}
// check line for component id
if ($componentStart !== null && ($tagName === 'UID' || $tagName === 'TZID')) {
$componentId = $tagValue;
}
} else {
// check line for component id
if ($componentStart !== null && ($tagName === 'UID' || $tagName === 'TZID')) {
$componentId = $tagValue;
}
}
// any line(s) not inside a component are VCALENDAR properties
if ($componentStart === null) {
if ($tagName !== 'BEGIN' && $tagName !== 'END' && $tagValue === 'VCALENDAR') {
$components['VCALENDAR'][] = $data;
}
}
// if component start and end are found, add the component to the structure
if ($componentStart !== null && $componentEnd !== null) {
if ($componentId !== null) {
$this->structure[$componentType][$componentId][] = [
$componentType,
$componentId,
$componentStart,
$componentEnd
];
} else {
$this->structure[$componentType][] = [
$componentType,
$componentId,
$componentStart,
$componentEnd
];
}
$componentId = null;
$componentType = null;
$componentStart = null;
$componentEnd = null;
}
}
}
/**
* Returns the analyzed structure of the source data
* the analyzed structure is a collection of components organized by type,
* each entry is a collection of instances
* [
* 'VEVENT' => [
* '7456f141-b478-4cb9-8efc-1427ba0d6839' => [
* ['VEVENT', '7456f141-b478-4cb9-8efc-1427ba0d6839', 0, 100 ],
* ['VEVENT', '7456f141-b478-4cb9-8efc-1427ba0d6839', 100, 200 ]
* ]
* ]
* ]
*/
public function structure(): array {
if (!$this->analyzed) {
$this->analyze();
}
return $this->structure;
}
/**
* Extracts a string chuck from the source data
*
* @param int $start starting byte position
* @param int $end ending byte position
*/
public function extract(int $start, int $end): string {
fseek($this->source, $start);
return fread($this->source, $end - $start);
}
}
-173
View File
@@ -1,173 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CalDAV\Import;
use Exception;
use XMLParser;
class XmlImporter {
public const OBJECT_PREFIX = '<?xml version="1.0" encoding="UTF-8"?><icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"><vcalendar><components>';
public const OBJECT_SUFFIX = '</components></vcalendar></icalendar>';
private const COMPONENT_TYPES = ['VEVENT', 'VTODO', 'VJOURNAL', 'VTIMEZONE'];
private bool $analyzed = false;
private array $structure = ['VCALENDAR' => [], 'VEVENT' => [], 'VTODO' => [], 'VJOURNAL' => [], 'VTIMEZONE' => []];
private int $praseLevel = 0;
private array $prasePath = [];
private ?int $componentStart = null;
private ?int $componentEnd = null;
private int $componentLevel = 0;
private ?string $componentId = null;
private ?string $componentType = null;
private bool $componentIdProperty = false;
/**
* @param resource $source
*/
public function __construct(
private $source,
) {
// Ensure that source is a stream resource
if (!is_resource($source) || get_resource_type($source) !== 'stream') {
throw new Exception('Source must be a stream resource');
}
}
/**
* Analyzes the source data and creates a structure of components
*/
private function analyze() {
$this->praseLevel = 0;
$this->prasePath = [];
$this->componentStart = null;
$this->componentEnd = null;
$this->componentLevel = 0;
$this->componentId = null;
$this->componentType = null;
$this->componentIdProperty = false;
// Create the parser and assign tag handlers
$parser = xml_parser_create();
xml_set_object($parser, $this);
xml_set_element_handler($parser, $this->tagStart(...), $this->tagEnd(...));
xml_set_default_handler($parser, $this->tagContents(...));
// iterate through the source data chuck by chunk to trigger the handlers
@fseek($this->source, 0);
while ($chunk = fread($this->source, 4096)) {
if (!xml_parse($parser, $chunk, feof($this->source))) {
throw new Exception(
xml_error_string(xml_get_error_code($parser))
. ' At line: '
. xml_get_current_line_number($parser)
);
}
}
//Free up the parser
xml_parser_free($parser);
}
/**
* Handles start of tag events from the parser for all tags
*/
private function tagStart(XMLParser $parser, string $tag, array $attributes): void {
// add the tag to the path tracker and increment depth the level
$this->praseLevel++;
$this->prasePath[$this->praseLevel] = $tag;
// determine if the tag is a component type and remember the byte position
if (in_array($tag, self::COMPONENT_TYPES, true)) {
$this->componentStart = xml_get_current_byte_index($parser) - (strlen($tag) + 1);
$this->componentType = $tag;
$this->componentLevel = $this->praseLevel;
}
// determine if the tag is a sub tag of the component and an id property
if ($this->componentStart !== null
&& ($this->componentLevel + 2) === $this->praseLevel
&& ($tag === 'UID' || $tag === 'TZID')
) {
$this->componentIdProperty = true;
}
}
/**
* Handles end of tag events from the parser for all tags
*/
private function tagEnd(XMLParser $parser, string $tag): void {
// if the end tag matched the component type or the component id property
// then add the component to the structure
if ($tag === 'UID' || $tag === 'TZID') {
$this->componentIdProperty = false;
} elseif ($this->componentType === $tag) {
$this->componentEnd = xml_get_current_byte_index($parser);
if ($this->componentId !== null) {
$this->structure[$this->componentType][$this->componentId][] = [
$this->componentType,
$this->componentId,
$this->componentStart,
$this->componentEnd,
implode('/', $this->prasePath)
];
} else {
$this->structure[$this->componentType][] = [
$this->componentType,
$this->componentId,
$this->componentStart,
$this->componentEnd,
implode('/', $this->prasePath)
];
}
$this->componentStart = null;
$this->componentEnd = null;
$this->componentId = null;
$this->componentType = null;
$this->componentIdProperty = false;
}
// remove the tag from the path tacker and depth the level
unset($this->prasePath[$this->praseLevel]);
$this->praseLevel--;
}
/**
* Handles tag contents events from the parser for all tags
*/
private function tagContents(XMLParser $parser, string $data): void {
if ($this->componentIdProperty) {
$this->componentId = $data;
}
}
/**
* Returns the analyzed structure of the source data
* the analyzed structure is a collection of components organized by type,
* each entry is a collection of instances
* [
* 'VEVENT' => [
* '7456f141-b478-4cb9-8efc-1427ba0d6839' => [
* ['VEVENT', '7456f141-b478-4cb9-8efc-1427ba0d6839', 0, 100 ],
* ['VEVENT', '7456f141-b478-4cb9-8efc-1427ba0d6839', 100, 200 ]
* ]
* ]
* ]
*/
public function structure(): array {
if (!$this->analyzed) {
$this->analyze();
}
return $this->structure;
}
/**
* Extracts a string chuck from the source data
*
* @param int $start starting byte position
* @param int $end ending byte position
*/
public function extract(int $start, int $end): string {
fseek($this->source, $start);
return fread($this->source, $end - $start);
}
}
@@ -6,9 +6,7 @@
*/
namespace OCA\DAV\CalDAV\Publishing;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Calendar;
use OCA\DAV\CalDAV\CalendarHome;
use OCA\DAV\CalDAV\Publishing\Xml\Publisher;
use OCP\AppFramework\Http;
use OCP\IConfig;
@@ -93,20 +91,6 @@ class PublishPlugin extends ServerPlugin {
}
public function propFind(PropFind $propFind, INode $node) {
if ($node instanceof CalendarHome && $propFind->getDepth() === 1) {
$backend = $node->getCalDAVBackend();
if ($backend instanceof CalDavBackend) {
$calendars = array_filter(
$node->getChildren(),
static fn ($child) => $child instanceof Calendar,
);
$resourceIds = array_map(
static fn (Calendar $calendar) => $calendar->getResourceId(),
$calendars,
);
$backend->preloadPublishStatuses($resourceIds);
}
}
if ($node instanceof Calendar) {
$propFind->handle('{' . self::NS_CALENDARSERVER . '}publish-url', function () use ($node) {
if ($node->getPublishStatus()) {

Some files were not shown because too many files have changed in this diff Show More