summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <213943+nickvergessen@users.noreply.github.com>2023-03-15 12:11:45 +0100
committerGitHub <noreply@github.com>2023-03-15 12:11:45 +0100
commit259530ed5a860a67e1b2a8c52452869d2403c947 (patch)
tree2fc4128049b52746ea56f97cbc44e77ccc80be1b
parent15d7fb35f448794c88675ecb90b889e39f88c956 (diff)
parent6f8d75bd24334d23f2fd6133b7d3cca663520575 (diff)
downloadnextcloud-server-259530ed5a860a67e1b2a8c52452869d2403c947.tar.gz
nextcloud-server-259530ed5a860a67e1b2a8c52452869d2403c947.zip
Merge pull request #37218 from nextcloud/backport/36852/stable26
[stable26] fix(user_status): Fix the user status automation on the day availablity rules are adjusted
-rw-r--r--apps/dav/lib/BackgroundJob/UserStatusAutomation.php17
-rw-r--r--apps/dav/tests/unit/BackgroundJob/UserStatusAutomationTest.php204
2 files changed, 217 insertions, 4 deletions
diff --git a/apps/dav/lib/BackgroundJob/UserStatusAutomation.php b/apps/dav/lib/BackgroundJob/UserStatusAutomation.php
index 8edc28c9a69..43fccbf233e 100644
--- a/apps/dav/lib/BackgroundJob/UserStatusAutomation.php
+++ b/apps/dav/lib/BackgroundJob/UserStatusAutomation.php
@@ -92,7 +92,7 @@ class UserStatusAutomation extends TimedJob {
$isCurrentlyAvailable = false;
$nextPotentialToggles = [];
- $now = new \DateTime('now');
+ $now = $this->time->getDateTime();
$lastMidnight = (clone $now)->setTime(0, 0);
$vObject = Reader::read($property);
@@ -105,9 +105,16 @@ class UserStatusAutomation extends TimedJob {
foreach ($availables as $available) {
/** @var Available $available */
if ($available->name === 'AVAILABLE') {
- /** @var \DateTimeInterface $effectiveStart */
- /** @var \DateTimeInterface $effectiveEnd */
- [$effectiveStart, $effectiveEnd] = $available->getEffectiveStartEnd();
+ /** @var \DateTimeImmutable $originalStart */
+ /** @var \DateTimeImmutable $originalEnd */
+ [$originalStart, $originalEnd] = $available->getEffectiveStartEnd();
+
+ // Little shenanigans to fix the automation on the day the rules were adjusted
+ // Otherwise the $originalStart would match rules for Thursdays on a Friday, etc.
+ // So we simply wind back a week and then fastForward to the next occurrence
+ // since today's midnight, which then also accounts for the week days.
+ $effectiveStart = \DateTime::createFromImmutable($originalStart)->sub(new \DateInterval('P7D'));
+ $effectiveEnd = \DateTime::createFromImmutable($originalEnd)->sub(new \DateInterval('P7D'));
try {
$it = new RRuleIterator((string) $available->RRULE, $effectiveStart);
@@ -150,8 +157,10 @@ class UserStatusAutomation extends TimedJob {
$this->setLastRunToNextToggleTime($userId, $nextAutomaticToggle - 1);
if ($isCurrentlyAvailable) {
+ $this->logger->debug('User is currently available, reverting DND status if applicable');
$this->manager->revertUserStatus($userId, IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND);
} else {
+ $this->logger->debug('User is currently NOT available, reverting call status if applicable and then setting DND');
// The DND status automation is more important than the "Away - In call" so we also restore that one if it exists.
$this->manager->revertUserStatus($userId, IUserStatus::MESSAGE_CALL, IUserStatus::AWAY);
$this->manager->setUserStatus($userId, IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND, true);
diff --git a/apps/dav/tests/unit/BackgroundJob/UserStatusAutomationTest.php b/apps/dav/tests/unit/BackgroundJob/UserStatusAutomationTest.php
new file mode 100644
index 00000000000..59438c7cd28
--- /dev/null
+++ b/apps/dav/tests/unit/BackgroundJob/UserStatusAutomationTest.php
@@ -0,0 +1,204 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\DAV\Tests\unit\BackgroundJob;
+
+use OCA\DAV\BackgroundJob\UserStatusAutomation;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\IJobList;
+use OCP\IConfig;
+use OCP\UserStatus\IManager;
+use OCP\UserStatus\IUserStatus;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class UserStatusAutomationTest extends TestCase {
+
+ protected MockObject|ITimeFactory $time;
+ protected MockObject|IJobList $jobList;
+ protected MockObject|LoggerInterface $logger;
+ protected MockObject|IManager $statusManager;
+ protected MockObject|IConfig $config;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->time = $this->createMock(ITimeFactory::class);
+ $this->jobList = $this->createMock(IJobList::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->statusManager = $this->createMock(IManager::class);
+ $this->config = $this->createMock(IConfig::class);
+
+ }
+
+ protected function getAutomationMock(array $methods): MockObject|UserStatusAutomation {
+ if (empty($methods)) {
+ return new UserStatusAutomation(
+ $this->time,
+ \OC::$server->getDatabaseConnection(),
+ $this->jobList,
+ $this->logger,
+ $this->statusManager,
+ $this->config,
+ );
+ }
+
+ return $this->getMockBuilder(UserStatusAutomation::class)
+ ->setConstructorArgs([
+ $this->time,
+ \OC::$server->getDatabaseConnection(),
+ $this->jobList,
+ $this->logger,
+ $this->statusManager,
+ $this->config,
+ ])
+ ->setMethods($methods)
+ ->getMock();
+ }
+
+ public function dataRun(): array {
+ return [
+ ['20230217', '2023-02-24 10:49:36.613834', true],
+ ['20230224', '2023-02-24 10:49:36.613834', true],
+ ['20230217', '2023-02-24 13:58:24.479357', false],
+ ['20230224', '2023-02-24 13:58:24.479357', false],
+ ];
+ }
+
+ /**
+ * @dataProvider dataRun
+ */
+ public function testRun(string $ruleDay, string $currentTime, bool $isAvailable): void {
+ $this->config->method('getUserValue')
+ ->with('user', 'dav', 'user_status_automation', 'no')
+ ->willReturn('yes');
+
+ $this->time->method('getDateTime')
+ ->willReturn(new \DateTime($currentTime, new \DateTimeZone('UTC')));
+
+ $automation = $this->getAutomationMock(['getAvailabilityFromPropertiesTable']);
+ $automation->method('getAvailabilityFromPropertiesTable')
+ ->with('user')
+ ->willReturn('BEGIN:VCALENDAR
+PRODID:Nextcloud DAV app
+BEGIN:VTIMEZONE
+TZID:Europe/Berlin
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VAVAILABILITY
+BEGIN:AVAILABLE
+DTSTART;TZID=Europe/Berlin:' . $ruleDay . 'T090000
+DTEND;TZID=Europe/Berlin:' . $ruleDay . 'T170000
+UID:3e6feeec-8e00-4265-b822-b73174e8b39f
+RRULE:FREQ=WEEKLY;BYDAY=TH
+END:AVAILABLE
+BEGIN:AVAILABLE
+DTSTART;TZID=Europe/Berlin:' . $ruleDay . 'T090000
+DTEND;TZID=Europe/Berlin:' . $ruleDay . 'T120000
+UID:8a634e99-07cf-443b-b480-005a0e1db323
+RRULE:FREQ=WEEKLY;BYDAY=FR
+END:AVAILABLE
+END:VAVAILABILITY
+END:VCALENDAR');
+
+ if ($isAvailable) {
+ $this->statusManager->expects($this->once())
+ ->method('revertUserStatus')
+ ->with('user', IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND);
+ } else {
+ $this->statusManager->expects($this->once())
+ ->method('revertUserStatus')
+ ->with('user', IUserStatus::MESSAGE_CALL, IUserStatus::AWAY);
+ $this->statusManager->expects($this->once())
+ ->method('setUserStatus')
+ ->with('user', IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND, true);
+ }
+
+ self::invokePrivate($automation, 'run', [['userId' => 'user']]);
+ }
+
+ public function testRunNoMoreAvailabilityDefined(): void {
+ $this->config->method('getUserValue')
+ ->with('user', 'dav', 'user_status_automation', 'no')
+ ->willReturn('yes');
+
+ $this->time->method('getDateTime')
+ ->willReturn(new \DateTime('2023-02-24 13:58:24.479357', new \DateTimeZone('UTC')));
+
+ $automation = $this->getAutomationMock(['getAvailabilityFromPropertiesTable']);
+ $automation->method('getAvailabilityFromPropertiesTable')
+ ->with('user')
+ ->willReturn('BEGIN:VCALENDAR
+PRODID:Nextcloud DAV app
+BEGIN:VTIMEZONE
+TZID:Europe/Berlin
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VAVAILABILITY
+END:VAVAILABILITY
+END:VCALENDAR');
+
+ $this->statusManager->expects($this->once())
+ ->method('revertUserStatus')
+ ->with('user', IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND);
+
+ $this->jobList->expects($this->once())
+ ->method('remove')
+ ->with(UserStatusAutomation::class, ['userId' => 'user']);
+
+ self::invokePrivate($automation, 'run', [['userId' => 'user']]);
+ }
+}