Merge pull request #41440 from nextcloud/feat/dav/out-of-office-job
feat(dav): dispatch out-of-office started and ended eventspull/41570/head
commit
eeba809a52
@ -0,0 +1,92 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud> |
||||
* |
||||
* @author Richard Steinmetz <richard@steinmetz.cloud> |
||||
* |
||||
* @license AGPL-3.0-or-later |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU 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 General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCA\DAV\BackgroundJob; |
||||
|
||||
use OCA\DAV\CalDAV\TimezoneService; |
||||
use OCA\DAV\Db\AbsenceMapper; |
||||
use OCP\AppFramework\Db\DoesNotExistException; |
||||
use OCP\AppFramework\Utility\ITimeFactory; |
||||
use OCP\BackgroundJob\QueuedJob; |
||||
use OCP\EventDispatcher\IEventDispatcher; |
||||
use OCP\IUserManager; |
||||
use OCP\User\Events\OutOfOfficeEndedEvent; |
||||
use OCP\User\Events\OutOfOfficeStartedEvent; |
||||
use Psr\Log\LoggerInterface; |
||||
|
||||
class OutOfOfficeEventDispatcherJob extends QueuedJob { |
||||
public const EVENT_START = 'start'; |
||||
public const EVENT_END = 'end'; |
||||
|
||||
public function __construct( |
||||
ITimeFactory $time, |
||||
private AbsenceMapper $absenceMapper, |
||||
private LoggerInterface $logger, |
||||
private IEventDispatcher $eventDispatcher, |
||||
private IUserManager $userManager, |
||||
private TimezoneService $timezoneService, |
||||
) { |
||||
parent::__construct($time); |
||||
} |
||||
|
||||
public function run($argument): void { |
||||
$id = $argument['id']; |
||||
$event = $argument['event']; |
||||
|
||||
try { |
||||
$absence = $this->absenceMapper->findById($id); |
||||
} catch (DoesNotExistException | \OCP\DB\Exception $e) { |
||||
$this->logger->error('Failed to dispatch out-of-office event: ' . $e->getMessage(), [ |
||||
'exception' => $e, |
||||
'argument' => $argument, |
||||
]); |
||||
return; |
||||
} |
||||
|
||||
$userId = $absence->getUserId(); |
||||
$user = $this->userManager->get($userId); |
||||
if ($user === null) { |
||||
$this->logger->error("Failed to dispatch out-of-office event: User $userId does not exist", [ |
||||
'argument' => $argument, |
||||
]); |
||||
return; |
||||
} |
||||
|
||||
$data = $absence->toOutOufOfficeData( |
||||
$user, |
||||
$this->timezoneService->getUserTimezone($userId) ?? $this->timezoneService->getDefaultTimezone(), |
||||
); |
||||
if ($event === self::EVENT_START) { |
||||
$this->eventDispatcher->dispatchTyped(new OutOfOfficeStartedEvent($data)); |
||||
} elseif ($event === self::EVENT_END) { |
||||
$this->eventDispatcher->dispatchTyped(new OutOfOfficeEndedEvent($data)); |
||||
} else { |
||||
$this->logger->error("Invalid out-of-office event: $event", [ |
||||
'argument' => $argument, |
||||
]); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,98 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/* |
||||
* @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @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\CalDAV; |
||||
|
||||
use OCA\DAV\Db\PropertyMapper; |
||||
use OCP\Calendar\ICalendar; |
||||
use OCP\Calendar\IManager; |
||||
use OCP\IConfig; |
||||
use Sabre\VObject\Component\VCalendar; |
||||
use Sabre\VObject\Component\VTimeZone; |
||||
use Sabre\VObject\Reader; |
||||
use function array_reduce; |
||||
|
||||
class TimezoneService { |
||||
|
||||
public function __construct(private IConfig $config, |
||||
private PropertyMapper $propertyMapper, |
||||
private IManager $calendarManager) { |
||||
} |
||||
|
||||
public function getUserTimezone(string $userId): ?string { |
||||
$availabilityPropPath = 'calendars/' . $userId . '/inbox'; |
||||
$availabilityProp = '{' . Plugin::NS_CALDAV . '}calendar-availability'; |
||||
$availabilities = $this->propertyMapper->findPropertyByPathAndName($userId, $availabilityPropPath, $availabilityProp); |
||||
if (!empty($availabilities)) { |
||||
$availability = $availabilities[0]->getPropertyvalue(); |
||||
/** @var VCalendar $vCalendar */ |
||||
$vCalendar = Reader::read($availability); |
||||
/** @var VTimeZone $vTimezone */ |
||||
$vTimezone = $vCalendar->VTIMEZONE; |
||||
// Sabre has a fallback to date_default_timezone_get |
||||
return $vTimezone->getTimeZone()->getName(); |
||||
} |
||||
|
||||
$principal = 'principals/users/' . $userId; |
||||
$uri = $this->config->getUserValue($userId, 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI); |
||||
$calendars = $this->calendarManager->getCalendarsForPrincipal($principal); |
||||
|
||||
/** @var ?VTimeZone $personalCalendarTimezone */ |
||||
$personalCalendarTimezone = array_reduce($calendars, function (?VTimeZone $acc, ICalendar $calendar) use ($uri) { |
||||
if ($acc !== null) { |
||||
return $acc; |
||||
} |
||||
if ($calendar->getUri() === $uri && !$calendar->isDeleted() && $calendar instanceof CalendarImpl) { |
||||
return $calendar->getSchedulingTimezone(); |
||||
} |
||||
return null; |
||||
}); |
||||
if ($personalCalendarTimezone !== null) { |
||||
return $personalCalendarTimezone->getTimeZone()->getName(); |
||||
} |
||||
|
||||
// No timezone in the personalCalendarTimezone calendar or no personalCalendarTimezone calendar |
||||
// Loop through all calendars until we find a timezone. |
||||
/** @var ?VTimeZone $firstTimezone */ |
||||
$firstTimezone = array_reduce($calendars, function (?VTimeZone $acc, ICalendar $calendar) { |
||||
if ($acc !== null) { |
||||
return $acc; |
||||
} |
||||
if (!$calendar->isDeleted() && $calendar instanceof CalendarImpl) { |
||||
return $calendar->getSchedulingTimezone(); |
||||
} |
||||
return null; |
||||
}); |
||||
if ($firstTimezone !== null) { |
||||
return $firstTimezone->getTimeZone()->getName(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public function getDefaultTimezone(): string { |
||||
return $this->config->getSystemValueString('default_timezone', 'UTC'); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,53 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/* |
||||
* @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @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\Db; |
||||
|
||||
use OCP\AppFramework\Db\Entity; |
||||
|
||||
/** |
||||
* @method string getUserid() |
||||
* @method string getPropertypath() |
||||
* @method string getPropertyname() |
||||
* @method string getPropertyvalue() |
||||
*/ |
||||
class Property extends Entity { |
||||
|
||||
/** @var string|null */ |
||||
protected $userid; |
||||
|
||||
/** @var string|null */ |
||||
protected $propertypath; |
||||
|
||||
/** @var string|null */ |
||||
protected $propertyname; |
||||
|
||||
/** @var string|null */ |
||||
protected $propertyvalue; |
||||
|
||||
/** @var int|null */ |
||||
protected $valuetype; |
||||
|
||||
} |
||||
@ -0,0 +1,57 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/* |
||||
* @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @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\Db; |
||||
|
||||
use OCP\AppFramework\Db\QBMapper; |
||||
use OCP\IDBConnection; |
||||
|
||||
/** |
||||
* @template-extends QBMapper<Property> |
||||
*/ |
||||
class PropertyMapper extends QBMapper { |
||||
|
||||
private const TABLE_NAME = 'properties'; |
||||
|
||||
public function __construct(IDBConnection $db) { |
||||
parent::__construct($db, self::TABLE_NAME, Property::class); |
||||
} |
||||
|
||||
/** |
||||
* @return Property[] |
||||
*/ |
||||
public function findPropertyByPathAndName(string $userId, string $path, string $name): array { |
||||
$selectQb = $this->db->getQueryBuilder(); |
||||
$selectQb->select('*') |
||||
->from(self::TABLE_NAME) |
||||
->where( |
||||
$selectQb->expr()->eq('userid', $selectQb->createNamedParameter($userId)), |
||||
$selectQb->expr()->eq('propertypath', $selectQb->createNamedParameter($path)), |
||||
$selectQb->expr()->eq('propertyname', $selectQb->createNamedParameter($name)), |
||||
); |
||||
return $this->findEntities($selectQb); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,55 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/* |
||||
* @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @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\integration\Db; |
||||
|
||||
use OCA\DAV\Db\PropertyMapper; |
||||
use Test\TestCase; |
||||
|
||||
/** |
||||
* @group DB |
||||
*/ |
||||
class PropertyMapperTest extends TestCase { |
||||
|
||||
/** @var PropertyMapper */ |
||||
private PropertyMapper $mapper; |
||||
|
||||
protected function setUp(): void { |
||||
parent::setUp(); |
||||
|
||||
$this->mapper = \OC::$server->get(PropertyMapper::class); |
||||
} |
||||
|
||||
public function testFindNonExistent(): void { |
||||
$props = $this->mapper->findPropertyByPathAndName( |
||||
'userthatdoesnotexist', |
||||
'path/that/does/not/exist/either', |
||||
'nope', |
||||
); |
||||
|
||||
self::assertEmpty($props); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,175 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud> |
||||
* |
||||
* @author Richard Steinmetz <richard@steinmetz.cloud> |
||||
* |
||||
* @license AGPL-3.0-or-later |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU 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 General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU 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\OutOfOfficeEventDispatcherJob; |
||||
use OCA\DAV\CalDAV\TimezoneService; |
||||
use OCA\DAV\Db\Absence; |
||||
use OCA\DAV\Db\AbsenceMapper; |
||||
use OCP\AppFramework\Utility\ITimeFactory; |
||||
use OCP\EventDispatcher\IEventDispatcher; |
||||
use OCP\IUser; |
||||
use OCP\IUserManager; |
||||
use OCP\User\Events\OutOfOfficeEndedEvent; |
||||
use OCP\User\Events\OutOfOfficeStartedEvent; |
||||
use PHPUnit\Framework\MockObject\MockObject; |
||||
use Psr\Log\LoggerInterface; |
||||
use Test\TestCase; |
||||
|
||||
class OutOfOfficeEventDispatcherJobTest extends TestCase { |
||||
private OutOfOfficeEventDispatcherJob $job; |
||||
|
||||
/** @var MockObject|ITimeFactory */ |
||||
private $timeFactory; |
||||
|
||||
/** @var MockObject|AbsenceMapper */ |
||||
private $absenceMapper; |
||||
|
||||
/** @var MockObject|LoggerInterface */ |
||||
private $logger; |
||||
|
||||
/** @var MockObject|IEventDispatcher */ |
||||
private $eventDispatcher; |
||||
|
||||
/** @var MockObject|IUserManager */ |
||||
private $userManager; |
||||
private MockObject|TimezoneService $timezoneService; |
||||
|
||||
protected function setUp(): void { |
||||
parent::setUp(); |
||||
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class); |
||||
$this->absenceMapper = $this->createMock(AbsenceMapper::class); |
||||
$this->logger = $this->createMock(LoggerInterface::class); |
||||
$this->eventDispatcher = $this->createMock(IEventDispatcher::class); |
||||
$this->userManager = $this->createMock(IUserManager::class); |
||||
$this->timezoneService = $this->createMock(TimezoneService::class); |
||||
|
||||
$this->job = new OutOfOfficeEventDispatcherJob( |
||||
$this->timeFactory, |
||||
$this->absenceMapper, |
||||
$this->logger, |
||||
$this->eventDispatcher, |
||||
$this->userManager, |
||||
$this->timezoneService, |
||||
); |
||||
} |
||||
|
||||
public function testDispatchStartEvent() { |
||||
$this->timezoneService->method('getUserTimezone')->with('user')->willReturn('Europe/Berlin'); |
||||
|
||||
$absence = new Absence(); |
||||
$absence->setId(200); |
||||
$absence->setUserId('user'); |
||||
|
||||
$user = $this->createMock(IUser::class); |
||||
$user->method('getUID') |
||||
->willReturn('user'); |
||||
|
||||
$this->absenceMapper->expects(self::once()) |
||||
->method('findById') |
||||
->with(1) |
||||
->willReturn($absence); |
||||
$this->userManager->expects(self::once()) |
||||
->method('get') |
||||
->with('user') |
||||
->willReturn($user); |
||||
$this->eventDispatcher->expects(self::once()) |
||||
->method('dispatchTyped') |
||||
->with(self::callback(static function ($event): bool { |
||||
self::assertInstanceOf(OutOfOfficeStartedEvent::class, $event); |
||||
return true; |
||||
})); |
||||
|
||||
$this->job->run([ |
||||
'id' => 1, |
||||
'event' => OutOfOfficeEventDispatcherJob::EVENT_START, |
||||
]); |
||||
} |
||||
|
||||
public function testDispatchStopEvent() { |
||||
$this->timezoneService->method('getUserTimezone')->with('user')->willReturn('Europe/Berlin'); |
||||
|
||||
$absence = new Absence(); |
||||
$absence->setId(200); |
||||
$absence->setUserId('user'); |
||||
|
||||
$user = $this->createMock(IUser::class); |
||||
$user->method('getUID') |
||||
->willReturn('user'); |
||||
|
||||
$this->absenceMapper->expects(self::once()) |
||||
->method('findById') |
||||
->with(1) |
||||
->willReturn($absence); |
||||
$this->userManager->expects(self::once()) |
||||
->method('get') |
||||
->with('user') |
||||
->willReturn($user); |
||||
$this->eventDispatcher->expects(self::once()) |
||||
->method('dispatchTyped') |
||||
->with(self::callback(static function ($event): bool { |
||||
self::assertInstanceOf(OutOfOfficeEndedEvent::class, $event); |
||||
return true; |
||||
})); |
||||
|
||||
$this->job->run([ |
||||
'id' => 1, |
||||
'event' => OutOfOfficeEventDispatcherJob::EVENT_END, |
||||
]); |
||||
} |
||||
|
||||
public function testDoesntDispatchUnknownEvent() { |
||||
$this->timezoneService->method('getUserTimezone')->with('user')->willReturn('Europe/Berlin'); |
||||
|
||||
$absence = new Absence(); |
||||
$absence->setId(100); |
||||
$absence->setUserId('user'); |
||||
|
||||
$user = $this->createMock(IUser::class); |
||||
$user->method('getUID') |
||||
->willReturn('user'); |
||||
|
||||
$this->absenceMapper->expects(self::once()) |
||||
->method('findById') |
||||
->with(1) |
||||
->willReturn($absence); |
||||
$this->userManager->expects(self::once()) |
||||
->method('get') |
||||
->with('user') |
||||
->willReturn($user); |
||||
$this->eventDispatcher->expects(self::never()) |
||||
->method('dispatchTyped'); |
||||
$this->logger->expects(self::once()) |
||||
->method('error'); |
||||
|
||||
$this->job->run([ |
||||
'id' => 1, |
||||
'event' => 'foobar', |
||||
]); |
||||
} |
||||
} |
||||
@ -0,0 +1,161 @@ |
||||
<?php |
||||
/* |
||||
* @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @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/>. |
||||
*/ |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/* |
||||
* @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
* |
||||
* @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\CalDAV; |
||||
|
||||
use DateTimeZone; |
||||
use OCA\DAV\CalDAV\CalendarImpl; |
||||
use OCA\DAV\CalDAV\TimezoneService; |
||||
use OCA\DAV\Db\Property; |
||||
use OCA\DAV\Db\PropertyMapper; |
||||
use OCP\Calendar\ICalendar; |
||||
use OCP\Calendar\IManager; |
||||
use OCP\IConfig; |
||||
use PHPUnit\Framework\MockObject\MockObject; |
||||
use Sabre\VObject\Component\VTimeZone; |
||||
use Test\TestCase; |
||||
|
||||
class TimezoneServiceTest extends TestCase { |
||||
|
||||
private IConfig|MockObject $config; |
||||
private PropertyMapper|MockObject $propertyMapper; |
||||
private IManager|MockObject $calendarManager; |
||||
private TimezoneService $service; |
||||
|
||||
protected function setUp(): void { |
||||
parent::setUp(); |
||||
|
||||
$this->config = $this->createMock(IConfig::class); |
||||
$this->propertyMapper = $this->createMock(PropertyMapper::class); |
||||
$this->calendarManager = $this->createMock(IManager::class); |
||||
|
||||
$this->service = new TimezoneService( |
||||
$this->config, |
||||
$this->propertyMapper, |
||||
$this->calendarManager, |
||||
); |
||||
} |
||||
|
||||
public function testGetUserTimezoneFromAvailability(): void { |
||||
$property = new Property(); |
||||
$property->setPropertyvalue('BEGIN:VCALENDAR |
||||
PRODID:Nextcloud DAV app |
||||
BEGIN:VTIMEZONE |
||||
TZID:Europe/Vienna |
||||
END:VTIMEZONE |
||||
END:VCALENDAR'); |
||||
$this->propertyMapper->expects(self::once()) |
||||
->method('findPropertyByPathAndName') |
||||
->willReturn([ |
||||
$property, |
||||
]); |
||||
|
||||
$timezone = $this->service->getUserTimezone('test123'); |
||||
|
||||
self::assertNotNull($timezone); |
||||
self::assertEquals('Europe/Vienna', $timezone); |
||||
} |
||||
|
||||
public function testGetUserTimezoneFromPersonalCalendar(): void { |
||||
$this->config->expects(self::once()) |
||||
->method('getUserValue') |
||||
->with('test123', 'dav', 'defaultCalendar') |
||||
->willReturn('personal-1'); |
||||
$other = $this->createMock(ICalendar::class); |
||||
$other->method('getUri')->willReturn('other'); |
||||
$personal = $this->createMock(CalendarImpl::class); |
||||
$personal->method('getUri')->willReturn('personal-1'); |
||||
$tz = new DateTimeZone('Europe/Berlin'); |
||||
$vtz = $this->createMock(VTimeZone::class); |
||||
$vtz->method('getTimeZone')->willReturn($tz); |
||||
$personal->method('getSchedulingTimezone')->willReturn($vtz); |
||||
$this->calendarManager->expects(self::once()) |
||||
->method('getCalendarsForPrincipal') |
||||
->with('principals/users/test123') |
||||
->willReturn([ |
||||
$other, |
||||
$personal, |
||||
]); |
||||
|
||||
$timezone = $this->service->getUserTimezone('test123'); |
||||
|
||||
self::assertNotNull($timezone); |
||||
self::assertEquals('Europe/Berlin', $timezone); |
||||
} |
||||
|
||||
public function testGetUserTimezoneFromAny(): void { |
||||
$this->config->expects(self::once()) |
||||
->method('getUserValue') |
||||
->with('test123', 'dav', 'defaultCalendar') |
||||
->willReturn('personal-1'); |
||||
$other = $this->createMock(ICalendar::class); |
||||
$other->method('getUri')->willReturn('other'); |
||||
$personal = $this->createMock(CalendarImpl::class); |
||||
$personal->method('getUri')->willReturn('personal-2'); |
||||
$tz = new DateTimeZone('Europe/Prague'); |
||||
$vtz = $this->createMock(VTimeZone::class); |
||||
$vtz->method('getTimeZone')->willReturn($tz); |
||||
$personal->method('getSchedulingTimezone')->willReturn($vtz); |
||||
$this->calendarManager->expects(self::once()) |
||||
->method('getCalendarsForPrincipal') |
||||
->with('principals/users/test123') |
||||
->willReturn([ |
||||
$other, |
||||
$personal, |
||||
]); |
||||
|
||||
$timezone = $this->service->getUserTimezone('test123'); |
||||
|
||||
self::assertNotNull($timezone); |
||||
self::assertEquals('Europe/Prague', $timezone); |
||||
} |
||||
|
||||
public function testGetUserTimezoneNoneFound(): void { |
||||
$timezone = $this->service->getUserTimezone('test123'); |
||||
|
||||
self::assertNull($timezone); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,51 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud> |
||||
* |
||||
* @author Richard Steinmetz <richard@steinmetz.cloud> |
||||
* |
||||
* @license AGPL-3.0-or-later |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU 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 General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\User\Events; |
||||
|
||||
use OCP\EventDispatcher\Event; |
||||
use OCP\User\IOutOfOfficeData; |
||||
|
||||
/** |
||||
* Emitted when a user's out-of-office period ended |
||||
* |
||||
* @since 28.0.0 |
||||
*/ |
||||
class OutOfOfficeEndedEvent extends Event { |
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function __construct(private IOutOfOfficeData $data) { |
||||
parent::__construct(); |
||||
} |
||||
|
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function getData(): IOutOfOfficeData { |
||||
return $this->data; |
||||
} |
||||
} |
||||
@ -0,0 +1,51 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud> |
||||
* |
||||
* @author Richard Steinmetz <richard@steinmetz.cloud> |
||||
* |
||||
* @license AGPL-3.0-or-later |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU 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 General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\User\Events; |
||||
|
||||
use OCP\EventDispatcher\Event; |
||||
use OCP\User\IOutOfOfficeData; |
||||
|
||||
/** |
||||
* Emitted when a user's out-of-office period started |
||||
* |
||||
* @since 28.0.0 |
||||
*/ |
||||
class OutOfOfficeStartedEvent extends Event { |
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function __construct(private IOutOfOfficeData $data) { |
||||
parent::__construct(); |
||||
} |
||||
|
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function getData(): IOutOfOfficeData { |
||||
return $this->data; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue