diff --git a/lib/CalDAV/Schedule/Plugin.php b/lib/CalDAV/Schedule/Plugin.php index 8577ae9f70..392157da99 100644 --- a/lib/CalDAV/Schedule/Plugin.php +++ b/lib/CalDAV/Schedule/Plugin.php @@ -597,8 +597,9 @@ public function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilege } /** - * This method looks at an old iCalendar object, a new iCalendar object and - * starts sending scheduling messages based on the changes. + * This method looks at an old iCalendar object, a new iCalendar object and: + * - starts sending scheduling messages based on the changes. + * - ensures the description fields are coherent. * * A list of addresses needs to be specified, so the system knows who made * the update, because the behavior may be different based on if it's an @@ -612,6 +613,8 @@ public function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilege */ protected function processICalendarChange($oldObject, VCalendar $newObject, array $addresses, array $ignore = [], &$modified = false) { + $this->ensureDescriptionConsistency($oldObject, $newObject, $modified); + $broker = $this->createITipBroker(); $messages = $broker->parseEvent($newObject, $addresses, $oldObject); @@ -1003,4 +1006,46 @@ protected function createITipBroker(): Broker { return new Broker(); } + + /** + * Ensure the alternate version of the description is removed if only the main one is changed. + * + * @param VCalendar|string|null $oldObject + * @param \Sabre\VObject\Component\VCalendar $vCal + * @param bool $modified a marker to indicate that the original object modified by this process + */ + private function ensureDescriptionConsistency($oldObject, VCalendar $vCal, &$modified) + { + if (!$oldObject) { + return; // No previous version to compare + } + + $xAltDescPropName = 'X-ALT-DESC'; + + // Get presence of description fields + $hasOldDescription = isset($oldObject->VTODO) && isset($oldObject->VTODO->DESCRIPTION); + $hasNewDescription = isset($vCal->VTODO) && isset($vCal->VTODO->DESCRIPTION); + $hasOldXAltDesc = isset($oldObject->VTODO) && isset($oldObject->VTODO->{$xAltDescPropName}); + $hasNewXAltDesc = isset($vCal->VTODO) && isset($vCal->VTODO->{$xAltDescPropName}); + $hasAllDesc = $hasOldDescription && $hasNewDescription && $hasOldXAltDesc && $hasNewXAltDesc; + + // If all description fields are present, then verify consistency + if ($hasAllDesc) { + // Get descriptions + $oldDescription = (string) $oldObject->VTODO->DESCRIPTION; + $newDescription = (string) $vCal->VTODO->DESCRIPTION; + $oldXAltDesc = (string) $oldObject->VTODO->{$xAltDescPropName}; + $newXAltDesc = (string) $vCal->VTODO->{$xAltDescPropName}; + + // Compare descriptions + $isSameDescription = $oldDescription === $newDescription; + $isSameXAltDesc = $oldXAltDesc === $newXAltDesc; + + // If the description changed, but not the alternate one, then delete the latest + if (!$isSameDescription && $isSameXAltDesc) { + unset($vCal->VTODO->{$xAltDescPropName}); + $modified = true; + } + } + } } diff --git a/tests/docker/Dockerfile b/tests/docker/Dockerfile new file mode 100644 index 0000000000..f26fd74cbe --- /dev/null +++ b/tests/docker/Dockerfile @@ -0,0 +1,22 @@ +FROM ubuntu:24.04 + +# Install PHP +RUN apt update -y +RUN apt install ca-certificates -y +ADD php.sources /etc/apt/sources.list.d/ +RUN apt update -y +RUN apt install -y php7.4 php7.4-xml php7.4-mbstring php7.4-curl php7.4-sqlite3 +RUN apt install -y curl zip git + +# Install composer +RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" +RUN php composer-setup.php --version=2.8.3 +RUN mv composer.phar /usr/local/bin/composer +RUN rm composer-setup.php + +# Setup tests +RUN mkdir /src/ +ADD run-tests.sh . +RUN chmod +x run-tests.sh + +ENTRYPOINT ["/run-tests.sh"] diff --git a/tests/docker/README.md b/tests/docker/README.md new file mode 100644 index 0000000000..5481dc0408 --- /dev/null +++ b/tests/docker/README.md @@ -0,0 +1,28 @@ +USAGE +===== + +- Install Docker + +REPO +---- + +- Download the folder **/tests/docker** from the repository +- Goto that folder using a terminal +- Run `docker compose up` +- Run `docker run --rm -ti sabre-dav-unit-tests` + +LOCAL +----- + +- Goto **$local_repo_path/tests/docker** using a terminal +- Run `docker compose up` +- Run either: + - `docker run --rm -ti -v $local_repo_path:/test-dir/ sabre-dav-unit-tests` + - `./run-local.sh` + + +DEV +--- + +- Apply changes to Dockerfile +- Run `docker compose up --build` \ No newline at end of file diff --git a/tests/docker/docker-compose.yml b/tests/docker/docker-compose.yml new file mode 100644 index 0000000000..ee897fa530 --- /dev/null +++ b/tests/docker/docker-compose.yml @@ -0,0 +1,8 @@ +services: + sabre-dav-unit-tests: + entrypoint: bash -c "echo Execute docker run -ti sabre-dav-unit-tests to start" + container_name: sabre-dav-unit-tests + build: + context: ./ + dockerfile: ./Dockerfile + image: sabre-dav-unit-tests:latest \ No newline at end of file diff --git a/tests/docker/php.sources b/tests/docker/php.sources new file mode 100644 index 0000000000..0913c78b91 --- /dev/null +++ b/tests/docker/php.sources @@ -0,0 +1,34 @@ +Types: deb +URIs: https://ppa.launchpadcontent.net/ondrej/php/ubuntu/ +Suites: noble +Components: main +Signed-By: + -----BEGIN PGP PUBLIC KEY BLOCK----- + . + mQINBGYo0vEBEAC0Semxy5I2b8exRUxJfTKkHR4f5uyS0dTd9vYgMI5T3gsa7ypH + HtE+GiZC+T9m/F9h66+XJMxhuNsKRs7T2In5NSeso9H/ytlSTayUaBtCFfRp6y6b + 6ozuRBfqYJGxhjAnIzvNF/Wpp2BvfQm3OrQ7uJJrt5IvzLDC4jPxl/Xs3sTT+Hbk + bkKKprZ3xmy2enuwBaNWR/CUtAz3hbkzL1kGbhX9m3QidFJagVVdDw3aNEwo8ush + djWfF+BajNvpDFYJKBGQbCeagB753Baa5yIN62x+THLnLiKTMDS1e7U0ZDiV9671 + noTbtN5TeZeyfsEmeZ8X60x11JIP3yYHYZT70/DyTYX3WC9yQFyIgVOfRlGklMKI + k3TLMmtq8w5Hz1vovwzV7PzaQnmY+uNP2ZbAP4fJ3iFAj0L+u0i1nOFgTy0Lq058 + O/FjRrQxuceDDCF+9ThspXMw3Puvz8giuBDCdEda84uC7XWMdqgz/maLfFQjAmyP + Ixi1EMxMlHYyZajpR1cdCfrAIQlnQjHSWmyeCFgXPPfRA71aCcJ7oSrDjogW6Ahd + HRkQRKf1FF9BFzycgSQotfR+7CKfPQh1kghufM9W/spARzA709nGZjXJzgEJLQd3 + CDB6dIIxT/0YI36h3Qgfmiiw4twO24MMEqEEPIELz2WJKeWGkdQdcekpxQARAQAB + tB9MYXVuY2hwYWQgUFBBIGZvciBPbmTFmWVqIFN1csO9iQJOBBMBCgA4FiEEuNx+ + U5RmVu+85MHdcdrqq0rUyrYFAmYo0vECGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC + F4AACgkQcdrqq0rUyrYOPQ/+IArA4s1J3op/w7cXek0ieFHWHFDrxPYS+78/LF/J + LoYZw0nIU5Ovr+LzehFMIQU6esgPXwbeCVgwLwat57augAkAYWT0UzH5dE6RKAGr + C2vsHWVfPhQn6UndfzwXc0mTLGQni25aQaZ6k60Dbm/vblejrTQrtAUWoMO3Z1cr + NDGJ3Z9DCxtr2o9gRYUI6HwLHJtobTIeI5xsr5x+GvXiIAVCPa3ZEuRL6jMQfqfS + C43mpuiS1kGgsnQLs2DbN7EFCfiJoNX1QzZu25zg+IS9PXbCJnheZWnH0rwUSb/N + hZPcSefGlNlhr824OfT30v79hQnw59XbsfV270O9jPbD4kttN+OiszbU66zsuiOh + BO46XCckQPqDkBMw56GPFuVrQgGb1thXvn67URJgPyJhwauBWKPNAJ9Ojuo+yVq/ + hdR1VNWThXQbZgaGSWrbjt6FdYtQb9VX88uu5gFDmr180HogHNUDUcqNLLdnjfFs + 4DyJlusQ5I/a7cQ7nlkNgxAmHszwO/mGLBuGljDUYkwZDW9nqP1Q5Q2jMtrhgXvR + 2SOtufvecUbB7+eoRSaOnu7CNMATG6LocFEMzhKUde1uZTfWSqnYEcdqoFJMi46y + qaNxhiNLsQ5OBMbgSp2zCbQxRBdITMVvBR5YjCetUIGEs6T1yQ5wh5Xpoi34ShHn + v38= + =kFlZ + -----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file diff --git a/tests/docker/run-local.sh b/tests/docker/run-local.sh new file mode 100644 index 0000000000..fa62c020ab --- /dev/null +++ b/tests/docker/run-local.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run -ti --rm -v $(realpath "$(dirname $0)/../../"):/test-dir/ sabre-dav-unit-tests diff --git a/tests/docker/run-tests.sh b/tests/docker/run-tests.sh new file mode 100644 index 0000000000..c17c072f9d --- /dev/null +++ b/tests/docker/run-tests.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +function executeTests() +# $1 = Source directory +{ + src="$1" + ( + cd "$src" + composer install + + mkdir -p vendor/sabre/http/tests/www + php -S localhost:8000 -t vendor/sabre/http/tests/www & + + composer phpunit + ) +} + +function downloadRepo() +{ + local repoUrl= + while [ -z "$repoUrl" ] + do + read -p "Please enter public repository URL:" repoUrl + + if [ -z "$repoUrl" ]; then echo "Repository URL is mandatory"; fi + done + + local branchName= + read -p "Enter the branch name [default: master]" branchName + branchName=${branchName:-master} + + echo "Are you sure you want to test with?" + echo "URL: $repoUrl" + echo "Branch: $branchName" + read -p "[y/N]" confirm + + if [ "$confirm" != "Y" ] && [ "$confirm" != "y" ]; then + echo "Cancelling test run" + exit + fi + + git clone -b "$branchName" --single-branch --depth 1 "$repoUrl" /src/ +} + +echo "Starting test container" + +src=/test-dir/ +if [ -d $src ]; then + echo "Using local volume" + git config --global --add safe.directory /test-dir +else + src=/src/ + echo "Using repository" + downloadRepo +fi + +executeTests "$src"