Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6f41f6a050 |
@@ -49,12 +49,5 @@ module.exports = {
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.vue'],
|
||||
rules: {
|
||||
'no-irregular-whitespace': 'off',
|
||||
'vue/no-irregular-whitespace': 'error',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
File diff suppressed because one or more lines are too long
@@ -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: {} }),
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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()];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);");
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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'];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user