]> source.dussan.org Git - nextcloud-server.git/commitdiff
feat(user status): automate user status for events 40615/head
authorAnna Larch <anna@nextcloud.com>
Mon, 25 Sep 2023 12:47:02 +0000 (14:47 +0200)
committerAnna Larch <anna@nextcloud.com>
Thu, 9 Nov 2023 15:20:19 +0000 (16:20 +0100)
and automatically set a user status to free or busy depending on their calendar
transparency, event status and availability settings

Signed-off-by: Anna Larch <anna@nextcloud.com>
17 files changed:
apps/dav/composer/composer/autoload_classmap.php
apps/dav/composer/composer/autoload_static.php
apps/dav/lib/CalDAV/CalendarImpl.php
apps/dav/lib/CalDAV/FreeBusy/FreeBusyGenerator.php [new file with mode: 0644]
apps/dav/lib/CalDAV/Schedule/Plugin.php
apps/dav/lib/CalDAV/Status/Status.php [new file with mode: 0644]
apps/dav/lib/CalDAV/Status/StatusService.php [new file with mode: 0644]
apps/dav/tests/unit/CalDAV/Status/StatusServiceTest.php [new file with mode: 0644]
apps/user_status/lib/Db/UserStatusMapper.php
apps/user_status/lib/Listener/UserLiveStatusListener.php
apps/user_status/lib/Service/PredefinedStatusService.php
apps/user_status/lib/Service/StatusService.php
apps/user_status/src/mixins/OnlineStatusMixin.js
apps/user_status/tests/Unit/Service/StatusServiceTest.php
dist/user_status-menu.js
dist/user_status-menu.js.map
lib/public/UserStatus/IUserStatus.php

index e0c3e20dc6bd072846528327eb91d71a952dd93b..396b94601bbf49e05bc03435e6086cef2a7f7333 100644 (file)
@@ -55,6 +55,7 @@ return array(
     'OCA\\DAV\\CalDAV\\CalendarProvider' => $baseDir . '/../lib/CalDAV/CalendarProvider.php',
     'OCA\\DAV\\CalDAV\\CalendarRoot' => $baseDir . '/../lib/CalDAV/CalendarRoot.php',
     'OCA\\DAV\\CalDAV\\EventComparisonService' => $baseDir . '/../lib/CalDAV/EventComparisonService.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\\Integration\\ExternalCalendar' => $baseDir . '/../lib/CalDAV/Integration/ExternalCalendar.php',
@@ -97,6 +98,8 @@ return array(
     'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\PropFilter' => $baseDir . '/../lib/CalDAV/Search/Xml/Filter/PropFilter.php',
     'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\SearchTermFilter' => $baseDir . '/../lib/CalDAV/Search/Xml/Filter/SearchTermFilter.php',
     'OCA\\DAV\\CalDAV\\Search\\Xml\\Request\\CalendarSearchReport' => $baseDir . '/../lib/CalDAV/Search/Xml/Request/CalendarSearchReport.php',
+    'OCA\\DAV\\CalDAV\\Status\\Status' => $baseDir . '/../lib/CalDAV/Status/Status.php',
+    'OCA\\DAV\\CalDAV\\Status\\StatusService' => $baseDir . '/../lib/CalDAV/Status/StatusService.php',
     'OCA\\DAV\\CalDAV\\Trashbin\\DeletedCalendarObject' => $baseDir . '/../lib/CalDAV/Trashbin/DeletedCalendarObject.php',
     'OCA\\DAV\\CalDAV\\Trashbin\\DeletedCalendarObjectsCollection' => $baseDir . '/../lib/CalDAV/Trashbin/DeletedCalendarObjectsCollection.php',
     'OCA\\DAV\\CalDAV\\Trashbin\\Plugin' => $baseDir . '/../lib/CalDAV/Trashbin/Plugin.php',
index 9292731af985dc19fb3653a1807fa40f2d08606b..f02d39bd11b006b09e344d97b84573e7617a285e 100644 (file)
@@ -70,6 +70,7 @@ class ComposerStaticInitDAV
         'OCA\\DAV\\CalDAV\\CalendarProvider' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarProvider.php',
         'OCA\\DAV\\CalDAV\\CalendarRoot' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarRoot.php',
         'OCA\\DAV\\CalDAV\\EventComparisonService' => __DIR__ . '/..' . '/../lib/CalDAV/EventComparisonService.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\\Integration\\ExternalCalendar' => __DIR__ . '/..' . '/../lib/CalDAV/Integration/ExternalCalendar.php',
@@ -112,6 +113,8 @@ class ComposerStaticInitDAV
         'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\PropFilter' => __DIR__ . '/..' . '/../lib/CalDAV/Search/Xml/Filter/PropFilter.php',
         'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\SearchTermFilter' => __DIR__ . '/..' . '/../lib/CalDAV/Search/Xml/Filter/SearchTermFilter.php',
         'OCA\\DAV\\CalDAV\\Search\\Xml\\Request\\CalendarSearchReport' => __DIR__ . '/..' . '/../lib/CalDAV/Search/Xml/Request/CalendarSearchReport.php',
+        'OCA\\DAV\\CalDAV\\Status\\Status' => __DIR__ . '/..' . '/../lib/CalDAV/Status/Status.php',
+        'OCA\\DAV\\CalDAV\\Status\\StatusService' => __DIR__ . '/..' . '/../lib/CalDAV/Status/StatusService.php',
         'OCA\\DAV\\CalDAV\\Trashbin\\DeletedCalendarObject' => __DIR__ . '/..' . '/../lib/CalDAV/Trashbin/DeletedCalendarObject.php',
         'OCA\\DAV\\CalDAV\\Trashbin\\DeletedCalendarObjectsCollection' => __DIR__ . '/..' . '/../lib/CalDAV/Trashbin/DeletedCalendarObjectsCollection.php',
         'OCA\\DAV\\CalDAV\\Trashbin\\Plugin' => __DIR__ . '/..' . '/../lib/CalDAV/Trashbin/Plugin.php',
index cc57aa36469328134d0c7d1efb7408d3fc43ad02..de20c9ac3aefba192861439811a0b365ff15f43b 100644 (file)
@@ -33,11 +33,15 @@ use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
 use OCP\Calendar\Exceptions\CalendarException;
 use OCP\Calendar\ICreateFromString;
 use OCP\Calendar\IHandleImipMessage;
+use OCP\Calendar\ISchedulingInformation;
 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\Property;
 use Sabre\VObject\Reader;
 use function Sabre\Uri\split as uriSplit;
 
@@ -86,6 +90,29 @@ class CalendarImpl implements ICreateFromString, IHandleImipMessage {
                return $this->calendarInfo['{http://apple.com/ns/ical/}calendar-color'];
        }
 
+       public function getSchedulingTransparency(): ?ScheduleCalendarTransp {
+               return $this->calendarInfo['{' . \OCA\DAV\CalDAV\Schedule\Plugin::NS_CALDAV . '}schedule-calendar-transp'];
+       }
+
+       public function getSchedulingTimezone(): ?VTimeZone {
+               $tzProp = '{' . \OCA\DAV\CalDAV\Schedule\Plugin::NS_CALDAV . '}calendar-timezone';
+               if (!isset($this->calendarInfo[$tzProp])) {
+                       return null;
+               }
+               // This property contains a VCALENDAR with a single VTIMEZONE
+               /** @var string $timezoneProp */
+               $timezoneProp = $this->calendarInfo[$tzProp];
+               /** @var VCalendar $vobj */
+               $vobj = Reader::read($timezoneProp);
+               $components = $vobj->getComponents();
+               if(empty($components)) {
+                       return null;
+               }
+               /** @var VTimeZone $vtimezone */
+               $vtimezone = $components[0];
+               return $vtimezone;
+       }
+
        /**
         * @param string $pattern which should match within the $searchProperties
         * @param array $searchProperties defines the properties within the query pattern should match
diff --git a/apps/dav/lib/CalDAV/FreeBusy/FreeBusyGenerator.php b/apps/dav/lib/CalDAV/FreeBusy/FreeBusyGenerator.php
new file mode 100644 (file)
index 0000000..29daca4
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+declare(strict_types=1);
+/*
+ * *
+ *  *
+ *  * @copyright 2023 Anna Larch <anna.larch@gmx.net>
+ *  *
+ *  * @author Anna Larch <anna.larch@gmx.net>
+ *  *
+ *  * This library is free software; you can redistribute it and/or
+ *  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ *  * License as published by the Free Software Foundation; either
+ *  * version 3 of the License, or any later version.
+ *  *
+ *  * This library 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 library.  If not, see <http://www.gnu.org/licenses/>.
+ *  *
+ *
+ */
+
+namespace OCA\DAV\CalDAV\FreeBusy;
+
+use DateTimeInterface;
+use DateTimeZone;
+use Sabre\VObject\Component\VCalendar;
+
+/**
+ * @psalm-suppress PropertyNotSetInConstructor
+ */
+class FreeBusyGenerator extends \Sabre\VObject\FreeBusyGenerator {
+
+       public function __construct() {
+               parent::__construct();
+       }
+
+       public function getVCalendar(): VCalendar {
+               return new VCalendar();
+       }
+}
index 2845ccdf6c213ea5953e67901e28b0d025873dd1..16acc72d9886ccb37166ca2191902ac182a7ca37 100644 (file)
@@ -36,6 +36,7 @@ use OCA\DAV\CalDAV\CalendarHome;
 use OCP\IConfig;
 use Psr\Log\LoggerInterface;
 use Sabre\CalDAV\ICalendar;
+use Sabre\CalDAV\Schedule\IOutbox;
 use Sabre\DAV\INode;
 use Sabre\DAV\IProperties;
 use Sabre\DAV\PropFind;
@@ -44,6 +45,7 @@ use Sabre\DAV\Xml\Property\LocalHref;
 use Sabre\DAVACL\IPrincipal;
 use Sabre\HTTP\RequestInterface;
 use Sabre\HTTP\ResponseInterface;
+use Sabre\VObject;
 use Sabre\VObject\Component;
 use Sabre\VObject\Component\VCalendar;
 use Sabre\VObject\Component\VEvent;
diff --git a/apps/dav/lib/CalDAV/Status/Status.php b/apps/dav/lib/CalDAV/Status/Status.php
new file mode 100644 (file)
index 0000000..8857d0f
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/*
+ * *
+ *  * Dav App
+ *  *
+ *  * @copyright 2023 Anna Larch <anna.larch@gmx.net>
+ *  *
+ *  * @author Anna Larch <anna.larch@gmx.net>
+ *  *
+ *  * This library is free software; you can redistribute it and/or
+ *  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ *  * License as published by the Free Software Foundation; either
+ *  * version 3 of the License, or any later version.
+ *  *
+ *  * This library 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 library.  If not, see <http://www.gnu.org/licenses/>.
+ *  *
+ *
+ */
+
+namespace OCA\DAV\CalDAV\Status;
+
+class Status {
+
+       public function __construct(private string $status = '', private ?string $message = null, private ?string $customMessage = null){}
+
+       public function getStatus(): string {
+               return $this->status;
+       }
+
+       public function setStatus(string $status): void {
+               $this->status = $status;
+       }
+
+       public function getMessage(): ?string {
+               return $this->message;
+       }
+
+       public function setMessage(?string $message): void {
+               $this->message = $message;
+       }
+
+       public function getCustomMessage(): ?string {
+               return $this->customMessage;
+       }
+
+       public function setCustomMessage(?string $customMessage): void {
+               $this->customMessage = $customMessage;
+       }
+
+
+}
diff --git a/apps/dav/lib/CalDAV/Status/StatusService.php b/apps/dav/lib/CalDAV/Status/StatusService.php
new file mode 100644 (file)
index 0000000..92554f8
--- /dev/null
@@ -0,0 +1,236 @@
+<?php
+/*
+ * *
+ *  * Dav App
+ *  *
+ *  * @copyright 2023 Anna Larch <anna.larch@gmx.net>
+ *  *
+ *  * @author Anna Larch <anna.larch@gmx.net>
+ *  *
+ *  * This library is free software; you can redistribute it and/or
+ *  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ *  * License as published by the Free Software Foundation; either
+ *  * version 3 of the License, or any later version.
+ *  *
+ *  * This library 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 library.  If not, see <http://www.gnu.org/licenses/>.
+ *  *
+ *
+ */
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2023 Anna Larch <anna.larch@gmx.net>
+ *
+ * @author Anna Larch <anna.larch@gmx.net>
+ *
+ * @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\Status;
+
+use DateTimeZone;
+use OC\Calendar\CalendarQuery;
+use OCA\DAV\CalDAV\CalendarImpl;
+use OCA\DAV\CalDAV\FreeBusy\FreeBusyGenerator;
+use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
+use OCA\DAV\CalDAV\IUser;
+use OCA\DAV\CalDAV\Schedule\Plugin as SchedulePlugin;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Calendar\IManager;
+use OCP\Calendar\ISchedulingInformation;
+use OCP\IL10N;
+use OCP\IUser as User;
+use OCP\UserStatus\IUserStatus;
+use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp;
+use Sabre\DAV\Exception\NotAuthenticated;
+use Sabre\DAVACL\Exception\NeedPrivileges;
+use Sabre\DAVACL\Plugin as AclPlugin;
+use Sabre\VObject\Component;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject\Parameter;
+use Sabre\VObject\Property;
+use Sabre\VObject\Reader;
+
+class StatusService {
+       public function __construct(private ITimeFactory $timeFactory,
+                                                               private IManager $calendarManager,
+                                                               private InvitationResponseServer $server,
+                                                               private IL10N $l10n,
+                                                               private FreeBusyGenerator $generator){}
+
+       public function processCalendarAvailability(User $user, ?string $availability): ?Status {
+               $userId = $user->getUID();
+               $email = $user->getEMailAddress();
+               if($email === null) {
+                       return null;
+               }
+
+               $server = $this->server->getServer();
+
+               /** @var SchedulePlugin $schedulingPlugin */
+               $schedulingPlugin = $server->getPlugin('caldav-schedule');
+               $caldavNS = '{'.$schedulingPlugin::NS_CALDAV.'}';
+
+               /** @var AclPlugin $aclPlugin */
+               $aclPlugin = $server->getPlugin('acl');
+               if ('mailto:' === substr($email, 0, 7)) {
+                       $email = substr($email, 7);
+               }
+
+               $result = $aclPlugin->principalSearch(
+                       ['{http://sabredav.org/ns}email-address' => $email],
+                       [
+                               '{DAV:}principal-URL',
+                               $caldavNS.'calendar-home-set',
+                               $caldavNS.'schedule-inbox-URL',
+                               '{http://sabredav.org/ns}email-address',
+                       ]
+               );
+
+               if (!count($result) || !isset($result[0][200][$caldavNS.'schedule-inbox-URL'])) {
+                       return null;
+               }
+
+               $inboxUrl = $result[0][200][$caldavNS.'schedule-inbox-URL']->getHref();
+
+               // Do we have permission?
+               try {
+                       $aclPlugin->checkPrivileges($inboxUrl, $caldavNS.'schedule-query-freebusy');
+               } catch (NeedPrivileges | NotAuthenticated $exception) {
+                       return null;
+               }
+
+               $now = $this->timeFactory->now();
+               $calendarTimeZone = $now->getTimezone();
+               $calendars = $this->calendarManager->getCalendarsForPrincipal('principals/users/' . $userId);
+               if(empty($calendars)) {
+                       return null;
+               }
+
+               $query = $this->calendarManager->newQuery('principals/users/' . $userId);
+               foreach ($calendars as $calendarObject) {
+                       // We can only work with a calendar if it exposes its scheduling information
+                       if (!$calendarObject instanceof CalendarImpl) {
+                               continue;
+                       }
+
+                       $sct = $calendarObject->getSchedulingTransparency();
+                       if ($sct !== null && ScheduleCalendarTransp::TRANSPARENT == strtolower($sct->getValue())) {
+                               // If a calendar is marked as 'transparent', it means we must
+                               // ignore it for free-busy purposes.
+                               continue;
+                       }
+
+                       /** @var Component\VTimeZone|null $ctz */
+                       $ctz = $calendarObject->getSchedulingTimezone();
+                       if ($ctz !== null) {
+                               $calendarTimeZone = $ctz->getTimeZone();
+                       }
+                       $query->addSearchCalendar($calendarObject->getUri());
+               }
+
+               $calendarEvents = [];
+               $dtStart = $now;
+               $dtEnd = \DateTimeImmutable::createFromMutable($this->timeFactory->getDateTime('+10 minutes'));
+
+               // Only query the calendars when there's any to search
+               if($query instanceof CalendarQuery && !empty($query->getCalendarUris())) {
+                       // Query the next hour
+                       $query->setTimerangeStart($dtStart);
+                       $query->setTimerangeEnd($dtEnd);
+                       $calendarEvents = $this->calendarManager->searchForPrincipal($query);
+               }
+
+               // @todo we can cache that
+               if(empty($availability) && empty($calendarEvents)) {
+                       // No availability settings and no calendar events, we can stop here
+                       return null;
+               }
+
+               $calendar = $this->generator->getVCalendar();
+               foreach ($calendarEvents as $calendarEvent) {
+                       $vEvent = new VEvent($calendar, 'VEVENT');
+                       foreach($calendarEvent['objects'] as $component) {
+                               foreach ($component as $key =>  $value) {
+                                       $vEvent->add($key, $value[0]);
+                               }
+                       }
+                       $calendar->add($vEvent);
+               }
+
+               $calendar->METHOD = 'REQUEST';
+
+               $this->generator->setObjects($calendar);
+               $this->generator->setTimeRange($dtStart, $dtEnd);
+               $this->generator->setTimeZone($calendarTimeZone);
+
+               if (!empty($availability)) {
+                       $this->generator->setVAvailability(
+                               Reader::read(
+                                       $availability
+                               )
+                       );
+               }
+               // Generate the intersection of VAVILABILITY and all VEVENTS in all calendars
+               $result = $this->generator->getResult();
+
+               if (!isset($result->VFREEBUSY)) {
+                       return null;
+               }
+
+               /** @var Component $freeBusyComponent */
+               $freeBusyComponent = $result->VFREEBUSY;
+               $freeBusyProperties = $freeBusyComponent->select('FREEBUSY');
+               // If there is no FreeBusy property, the time-range is empty and available
+               // so set the status to online as otherwise we will never recover from a BUSY status
+               if (count($freeBusyProperties) === 0) {
+                       return new Status(IUserStatus::ONLINE);
+               }
+
+               /** @var Property $freeBusyProperty */
+               $freeBusyProperty = $freeBusyProperties[0];
+               if (!$freeBusyProperty->offsetExists('FBTYPE')) {
+                       // If there is no FBTYPE, it means it's busy from a regular event
+                       return new Status(IUserStatus::BUSY, IUserStatus::MESSAGE_CALENDAR_BUSY);
+               }
+
+               // If we can't deal with the FBTYPE (custom properties are a possibility)
+               // we should ignore it and leave the current status
+               $fbTypeParameter = $freeBusyProperty->offsetGet('FBTYPE');
+               if (!($fbTypeParameter instanceof Parameter)) {
+                       return null;
+               }
+               $fbType = $fbTypeParameter->getValue();
+               switch ($fbType) {
+                       case 'BUSY':
+                               return new Status(IUserStatus::BUSY, IUserStatus::MESSAGE_CALENDAR_BUSY, $this->l10n->t('In a meeting'));
+                       case 'BUSY-UNAVAILABLE':
+                               return new Status(IUserStatus::AWAY, IUserStatus::MESSAGE_AVAILABILITY);
+                       case 'BUSY-TENTATIVE':
+                               return new Status(IUserStatus::AWAY, IUserStatus::MESSAGE_CALENDAR_BUSY_TENTATIVE);
+                       default:
+                               return null;
+               }
+       }
+}
diff --git a/apps/dav/tests/unit/CalDAV/Status/StatusServiceTest.php b/apps/dav/tests/unit/CalDAV/Status/StatusServiceTest.php
new file mode 100644 (file)
index 0000000..450eac9
--- /dev/null
@@ -0,0 +1,1508 @@
+<?php
+/**
+ * @copyright 2023 Anna Larch <anna.larch@gmx.net>
+ *
+ * @author Anna Larch <anna.larch@gmx.net>
+ *
+ * @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\Status;
+
+use OC\Calendar\CalendarQuery;
+use OCA\DAV\CalDAV\CalendarImpl;
+use OCA\DAV\CalDAV\FreeBusy\FreeBusyGenerator;
+use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
+use OCA\DAV\CalDAV\Schedule\Plugin;
+use OCA\DAV\CalDAV\Status\Status;
+use OCA\DAV\CalDAV\Status\StatusService;
+use OCA\DAV\Connector\Sabre\Server;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Calendar\IManager;
+use OCP\IL10N;
+use OCP\IUser;
+use OCP\UserStatus\IUserStatus;
+use PHPUnit\Framework\MockObject\MockObject;
+use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp;
+use Sabre\DAV\Exception\NotAuthenticated;
+use Sabre\DAV\Xml\Property\LocalHref;
+use Sabre\DAVACL\Exception\NeedPrivileges;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Component\VTimeZone;
+use Sabre\VObject\Document;
+use Sabre\VObject\Reader;
+use Test\TestCase;
+
+class StatusServiceTest extends TestCase {
+       private ITimeFactory|MockObject $timeFactory;
+       private IManager|MockObject $calendarManager;
+       private InvitationResponseServer|MockObject $server;
+       private IL10N|MockObject $l10n;
+       private FreeBusyGenerator|MockObject $generator;
+
+       protected function setUp(): void {
+               parent::setUp();
+
+               $this->timeFactory = $this->createMock(ITimeFactory::class);
+               $this->calendarManager = $this->createMock(IManager::class);
+               $this->server = $this->createMock(InvitationResponseServer::class);
+               $this->l10n = $this->createMock(IL10N::class);
+               $this->generator = $this->createMock(FreeBusyGenerator::class);
+
+               $this->service = new StatusService($this->timeFactory,
+                       $this->calendarManager,
+                       $this->server,
+                       $this->l10n,
+                       $this->generator);
+       }
+
+       public function testNoEmail(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => null,
+               ]);
+               $availability = '';
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn(null);
+               $this->server->expects(self::never())
+                       ->method('getServer');
+               $this->timeFactory->expects(self::never())
+                       ->method('now');
+               $this->timeFactory->expects(self::never())
+                       ->method('getDateTime');
+               $this->calendarManager->expects(self::never())
+                       ->method('getCalendarsForPrincipal');
+               $this->calendarManager->expects(self::never())
+                       ->method('newQuery');
+               $this->calendarManager->expects(self::never())
+                       ->method('searchForPrincipal');
+               $this->generator->expects(self::never())
+                       ->method('getVCalendar');
+               $this->generator->expects(self::never())
+                       ->method('setObjects');
+               $this->generator->expects(self::never())
+                       ->method('setTimeRange');
+               $this->generator->expects(self::never())
+                       ->method('setTimeZone');
+               $this->generator->expects(self::never())
+                       ->method('setVAvailability');
+               $this->generator->expects(self::never())
+                       ->method('getResult');
+
+               $status = $this->service->processCalendarAvailability($user, $availability);
+               $this->assertNull($status);
+       }
+
+       public function testNoAcl(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $availability = '';
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with([ '{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn([]);
+               $aclPlugin->expects(self::never())
+                       ->method('checkPrivileges');
+               $this->timeFactory->expects(self::never())
+                       ->method('now');
+               $this->timeFactory->expects(self::never())
+                       ->method('getDateTime');
+               $this->calendarManager->expects(self::never())
+                       ->method('getCalendarsForPrincipal');
+               $this->calendarManager->expects(self::never())
+                       ->method('newQuery');
+               $this->calendarManager->expects(self::never())
+                       ->method('searchForPrincipal');
+               $this->generator->expects(self::never())
+                       ->method('getVCalendar');
+               $this->generator->expects(self::never())
+                       ->method('setObjects');
+               $this->generator->expects(self::never())
+                       ->method('setTimeRange');
+               $this->generator->expects(self::never())
+                       ->method('setTimeZone');
+               $this->generator->expects(self::never())
+                       ->method('setVAvailability');
+               $this->generator->expects(self::never())
+                       ->method('getResult');
+
+               $status = $this->service->processCalendarAvailability($user, $availability);
+               $this->assertNull($status);
+       }
+
+       public function testNoInbox(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $availability = '';
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with([ '{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn([]);
+               $aclPlugin->expects(self::never())
+                       ->method('checkPrivileges');
+               $this->timeFactory->expects(self::never())
+                       ->method('now');
+               $this->timeFactory->expects(self::never())
+                       ->method('getDateTime');
+               $this->calendarManager->expects(self::never())
+                       ->method('getCalendarsForPrincipal');
+               $this->calendarManager->expects(self::never())
+                       ->method('newQuery');
+               $this->calendarManager->expects(self::never())
+                       ->method('searchForPrincipal');
+               $this->generator->expects(self::never())
+                       ->method('getVCalendar');
+               $this->generator->expects(self::never())
+                       ->method('setObjects');
+               $this->generator->expects(self::never())
+                       ->method('setTimeRange');
+               $this->generator->expects(self::never())
+                       ->method('setTimeZone');
+               $this->generator->expects(self::never())
+                       ->method('setVAvailability');
+               $this->generator->expects(self::never())
+                       ->method('getResult');
+
+               $status = $this->service->processCalendarAvailability($user, $availability);
+               $this->assertNull($status);
+       }
+
+       public function testNoPrivilegesAcl(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $availability = '';
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $principal = 'principals/users/admin';
+               $calendarHome = $this->createMock(LocalHref::class);
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with([ '{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willThrowException(new NeedPrivileges($principal, ['{DAV:}all']));
+               $this->timeFactory->expects(self::never())
+                       ->method('now');
+               $this->timeFactory->expects(self::never())
+                       ->method('getDateTime');
+               $this->calendarManager->expects(self::never())
+                       ->method('getCalendarsForPrincipal');
+               $this->calendarManager->expects(self::never())
+                       ->method('newQuery');
+               $this->calendarManager->expects(self::never())
+                       ->method('searchForPrincipal');
+               $this->generator->expects(self::never())
+                       ->method('getVCalendar');
+               $this->generator->expects(self::never())
+                       ->method('setObjects');
+               $this->generator->expects(self::never())
+                       ->method('setTimeRange');
+               $this->generator->expects(self::never())
+                       ->method('setTimeZone');
+               $this->generator->expects(self::never())
+                       ->method('setVAvailability');
+               $this->generator->expects(self::never())
+                       ->method('getResult');
+
+               $status = $this->service->processCalendarAvailability($user, $availability);
+               $this->assertNull($status);
+       }
+
+       public function testNotAuthenticated(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $availability = '';
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $calendarHome = $this->createMock(LocalHref::class);
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with([ '{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willThrowException(new NotAuthenticated());
+               $this->timeFactory->expects(self::never())
+                       ->method('now');
+               $this->timeFactory->expects(self::never())
+                       ->method('getDateTime');
+               $this->calendarManager->expects(self::never())
+                       ->method('getCalendarsForPrincipal');
+               $this->calendarManager->expects(self::never())
+                       ->method('newQuery');
+               $this->calendarManager->expects(self::never())
+                       ->method('searchForPrincipal');
+               $this->generator->expects(self::never())
+                       ->method('getVCalendar');
+               $this->generator->expects(self::never())
+                       ->method('setObjects');
+               $this->generator->expects(self::never())
+                       ->method('setTimeRange');
+               $this->generator->expects(self::never())
+                       ->method('setTimeZone');
+               $this->generator->expects(self::never())
+                       ->method('setVAvailability');
+               $this->generator->expects(self::never())
+                       ->method('getResult');
+
+               $status = $this->service->processCalendarAvailability($user, $availability);
+               $this->assertNull($status);
+       }
+
+       public function testNoCalendars(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $availability = '';
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $calendarHome = $this->createMock(LocalHref::class);
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+               $now = new \DateTimeImmutable('1970-1-1', new \DateTimeZone('UTC'));
+               $principal = 'principals/users/admin';
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with([ '{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willReturn(true);
+               $this->timeFactory->expects(self::once())
+                       ->method('now')
+                       ->willReturn($now);
+               $this->calendarManager->expects(self::once())
+                       ->method('getCalendarsForPrincipal')
+                       ->with($principal)
+                       ->willReturn([]);
+               $this->timeFactory->expects(self::never())
+                       ->method('getDateTime');
+               $this->calendarManager->expects(self::never())
+                       ->method('newQuery');
+               $this->calendarManager->expects(self::never())
+                       ->method('searchForPrincipal');
+               $this->generator->expects(self::never())
+                       ->method('getVCalendar');
+               $this->generator->expects(self::never())
+                       ->method('setObjects');
+               $this->generator->expects(self::never())
+                       ->method('setTimeRange');
+               $this->generator->expects(self::never())
+                       ->method('setTimeZone');
+               $this->generator->expects(self::never())
+                       ->method('setVAvailability');
+               $this->generator->expects(self::never())
+                       ->method('getResult');
+
+               $status = $this->service->processCalendarAvailability($user, $availability);
+               $this->assertNull($status);
+       }
+
+       public function testEmptyAvailabilityAndNoSearchCalendars(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $availability = '';
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $calendarHome = $this->createMock(LocalHref::class);
+               $now = new \DateTimeImmutable('1970-1-1', new \DateTimeZone('UTC'));
+               $inTenMinutes = new \DateTime('1970-1-1 01:00');
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+               $principal = 'principals/users/admin';
+               $calendar = $this->createMock(CalendarImpl::class);
+               $query = $this->createMock(CalendarQuery::class);
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with([ '{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willReturn(true);
+               $this->timeFactory->expects(self::once())
+                       ->method('now')
+                       ->willReturn($now);
+               $this->calendarManager->expects(self::once())
+                       ->method('getCalendarsForPrincipal')
+                       ->with($principal)
+                       ->willReturn([$calendar]);
+               $this->calendarManager->expects(self::once())
+                       ->method('newQuery')
+                       ->with($principal)
+                       ->willReturn($query);
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTransparency')
+                       ->willReturn(new ScheduleCalendarTransp('transparent'));
+               $this->timeFactory->expects(self::once())
+                       ->method('getDateTime')
+                       ->with('+10 minutes')
+                       ->willReturn($inTenMinutes);
+               $this->calendarManager->expects(self::never())
+                       ->method('searchForPrincipal');
+               $this->generator->expects(self::never())
+                       ->method('getVCalendar');
+               $this->generator->expects(self::never())
+                       ->method('setObjects');
+               $this->generator->expects(self::never())
+                       ->method('setTimeRange');
+               $this->generator->expects(self::never())
+                       ->method('setTimeZone');
+               $this->generator->expects(self::never())
+                       ->method('setVAvailability');
+               $this->generator->expects(self::never())
+                       ->method('getResult');
+
+               $status = $this->service->processCalendarAvailability($user, $availability);
+               $this->assertNull($status);
+       }
+
+       public function testEmptyAvailabilityAndSearchCalendarsNoResults(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $availability = '';
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $calendarHome = $this->createMock(LocalHref::class);
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+               $now = new \DateTimeImmutable('1970-1-1 00:00', new \DateTimeZone('UTC'));
+               $inTenMinutes = new \DateTime('1970-1-1 01:00');
+               $immutableInTenMinutes = \DateTimeImmutable::createFromMutable($inTenMinutes);
+               $principal = 'principals/users/admin';
+               $query = $this->createMock(CalendarQuery::class);
+               $timezone = new \DateTimeZone('UTC');
+               $timezoneObj = $this->createMock(VTimeZone::class);
+               $calendar = $this->createMock(CalendarImpl::class);
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with(['{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willReturn(true);
+               $this->timeFactory->expects(self::once())
+                       ->method('now')
+                       ->willReturn($now);
+               $this->calendarManager->expects(self::once())
+                       ->method('getCalendarsForPrincipal')
+                       ->with($principal)
+                       ->willReturn([$calendar]);
+               $this->calendarManager->expects(self::once())
+                       ->method('newQuery')
+                       ->with($principal)
+                       ->willReturn($query);
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTransparency')
+                       ->willReturn(new ScheduleCalendarTransp('opaque'));
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTimezone')
+                       ->willReturn($timezoneObj);
+               $timezoneObj->expects(self::once())
+                       ->method('getTimeZone')
+                       ->willReturn($timezone);
+               $calendar->expects(self::once())
+                       ->method('getUri');
+               $query->expects(self::once())
+                       ->method('addSearchCalendar');
+               $query->expects(self::once())
+                       ->method('getCalendarUris')
+                       ->willReturn([$calendar]);
+               $this->timeFactory->expects(self::once())
+                       ->method('getDateTime')
+                       ->with('+10 minutes')
+                       ->willReturn($inTenMinutes);
+               $query->expects(self::once())
+                       ->method('setTimerangeStart')
+                       ->with($now);
+               $query->expects(self::once())
+                       ->method('setTimerangeEnd')
+                       ->with($immutableInTenMinutes);
+               $this->calendarManager->expects(self::once())
+                       ->method('searchForPrincipal')
+                       ->with($query)
+                       ->willReturn([]);
+               $this->generator->expects(self::never())
+                       ->method('getVCalendar');
+               $this->generator->expects(self::never())
+                       ->method('setObjects');
+               $this->generator->expects(self::never())
+                       ->method('setTimeRange');
+               $this->generator->expects(self::never())
+                       ->method('setTimeZone');
+               $this->generator->expects(self::never())
+                       ->method('setVAvailability');
+               $this->generator->expects(self::never())
+                       ->method('getResult');
+
+               $status = $this->service->processCalendarAvailability($user, $availability);
+               $this->assertNull($status);
+       }
+
+       public function testAvailabilityAndSearchCalendarsNoResults(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $calendarHome = $this->createMock(LocalHref::class);
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+               $now = new \DateTimeImmutable('1970-1-1 00:00', new \DateTimeZone('UTC'));
+               $inTenMinutes = new \DateTime('1970-1-1 01:00');
+               $immutableInTenMinutes = \DateTimeImmutable::createFromMutable($inTenMinutes);
+               $principal = 'principals/users/admin';
+               $query = $this->createMock(CalendarQuery::class);
+               $timezone = new \DateTimeZone('UTC');
+               $timezoneObj = $this->createMock(VTimeZone::class);
+               $calendar = $this->createMock(CalendarImpl::class);
+               $vCalendar = $this->createMock(VCalendar::class);
+               $availability = $this->getVAvailability();
+               $result = Reader::read('BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject 4.5.3//EN
+               CALSCALE:GREGORIAN
+METHOD:REQUEST
+END:VCALENDAR');
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with(['{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willReturn(true);
+               $this->timeFactory->expects(self::once())
+                       ->method('now')
+                       ->willReturn($now);
+               $this->calendarManager->expects(self::once())
+                       ->method('getCalendarsForPrincipal')
+                       ->with($principal)
+                       ->willReturn([$calendar]);
+               $this->calendarManager->expects(self::once())
+                       ->method('newQuery')
+                       ->with($principal)
+                       ->willReturn($query);
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTransparency')
+                       ->willReturn(new ScheduleCalendarTransp('opaque'));
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTimezone')
+                       ->willReturn($timezoneObj);
+               $timezoneObj->expects(self::once())
+                       ->method('getTimeZone')
+                       ->willReturn($timezone);
+               $calendar->expects(self::once())
+                       ->method('getUri');
+               $query->expects(self::once())
+                       ->method('addSearchCalendar');
+               $query->expects(self::once())
+                       ->method('getCalendarUris')
+                       ->willReturn([$calendar]);
+               $this->timeFactory->expects(self::once())
+                       ->method('getDateTime')
+                       ->with('+10 minutes')
+                       ->willReturn($inTenMinutes);
+               $query->expects(self::once())
+                       ->method('setTimerangeStart')
+                       ->with($now);
+               $query->expects(self::once())
+                       ->method('setTimerangeEnd')
+                       ->with($immutableInTenMinutes);
+               $this->calendarManager->expects(self::once())
+                       ->method('searchForPrincipal')
+                       ->with($query)
+                       ->willReturn([]);
+               $this->generator->expects(self::once())
+                       ->method('getVCalendar')
+                       ->willReturn($vCalendar);
+               $vCalendar->expects(self::never())
+                       ->method('add');
+               $this->generator->expects(self::once())
+                       ->method('setObjects')
+                       ->with($vCalendar);
+               $this->generator->expects(self::once())
+                       ->method('setTimeRange')
+                       ->with($now, $immutableInTenMinutes);
+               $this->generator->expects(self::once())
+                       ->method('setTimeZone')
+                       ->with($timezone);
+               $this->generator->expects(self::once())
+                       ->method('setVAvailability')
+                       ->with($availability);
+               $this->generator->expects(self::once())
+                       ->method('getResult')
+                       ->willReturn($result);
+
+               $status = $this->service->processCalendarAvailability($user, $availability->serialize());
+               $this->assertNull($status);
+       }
+
+       public function testAvailabilityAndSearchCalendarsStatusOnline(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $calendarHome = $this->createMock(LocalHref::class);
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+               $now = new \DateTimeImmutable('1970-1-1 00:00', new \DateTimeZone('UTC'));
+               $inTenMinutes = new \DateTime('1970-1-1 01:00');
+               $immutableInTenMinutes = \DateTimeImmutable::createFromMutable($inTenMinutes);
+               $principal = 'principals/users/admin';
+               $query = $this->createMock(CalendarQuery::class);
+               $timezone = new \DateTimeZone('UTC');
+               $timezoneObj = $this->createMock(VTimeZone::class);
+               $calendar = $this->createMock(CalendarImpl::class);
+               $vCalendar = $this->createMock(VCalendar::class);
+               $availability = $this->getVAvailability();
+               $result = Reader::read('BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject 4.5.3//EN
+               CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VFREEBUSY
+DTSTART:19700101T000000Z
+DTEND:19700101T003600Z
+DTSTAMP:19700101T000200Z
+END:VFREEBUSY
+END:VCALENDAR');
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with(['{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willReturn(true);
+               $this->timeFactory->expects(self::once())
+                       ->method('now')
+                       ->willReturn($now);
+               $this->calendarManager->expects(self::once())
+                       ->method('getCalendarsForPrincipal')
+                       ->with($principal)
+                       ->willReturn([$calendar]);
+               $this->calendarManager->expects(self::once())
+                       ->method('newQuery')
+                       ->with($principal)
+                       ->willReturn($query);
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTransparency')
+                       ->willReturn(new ScheduleCalendarTransp('opaque'));
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTimezone')
+                       ->willReturn($timezoneObj);
+               $timezoneObj->expects(self::once())
+                       ->method('getTimeZone')
+                       ->willReturn($timezone);
+               $calendar->expects(self::once())
+                       ->method('getUri');
+               $query->expects(self::once())
+                       ->method('addSearchCalendar');
+               $query->expects(self::once())
+                       ->method('getCalendarUris')
+                       ->willReturn([$calendar]);
+               $this->timeFactory->expects(self::once())
+                       ->method('getDateTime')
+                       ->with('+10 minutes')
+                       ->willReturn($inTenMinutes);
+               $query->expects(self::once())
+                       ->method('setTimerangeStart')
+                       ->with($now);
+               $query->expects(self::once())
+                       ->method('setTimerangeEnd')
+                       ->with($immutableInTenMinutes);
+               $this->calendarManager->expects(self::once())
+                       ->method('searchForPrincipal')
+                       ->with($query)
+                       ->willReturn([]);
+               $this->generator->expects(self::once())
+                       ->method('getVCalendar')
+                       ->willReturn($vCalendar);
+               $vCalendar->expects(self::never())
+                       ->method('add');
+               $this->generator->expects(self::once())
+                       ->method('setObjects')
+                       ->with($vCalendar);
+               $this->generator->expects(self::once())
+                       ->method('setTimeRange')
+                       ->with($now, $immutableInTenMinutes);
+               $this->generator->expects(self::once())
+                       ->method('setTimeZone')
+                       ->with($timezone);
+               $this->generator->expects(self::once())
+                       ->method('setVAvailability')
+                       ->with($availability);
+               $this->generator->expects(self::once())
+                       ->method('getResult')
+                       ->willReturn($result);
+
+               $status = $this->service->processCalendarAvailability($user, $availability->serialize());
+               $this->assertEquals(new Status(IUserStatus::ONLINE), $status);
+       }
+
+       public function testAvailabilityAndSearchCalendarsStatusBusyNoFBType(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $calendarHome = $this->createMock(LocalHref::class);
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+               $now = new \DateTimeImmutable('1970-1-1 00:00', new \DateTimeZone('UTC'));
+               $inTenMinutes = new \DateTime('1970-1-1 01:00');
+               $immutableInTenMinutes = \DateTimeImmutable::createFromMutable($inTenMinutes);
+               $principal = 'principals/users/admin';
+               $query = $this->createMock(CalendarQuery::class);
+               $timezone = new \DateTimeZone('UTC');
+               $timezoneObj = $this->createMock(VTimeZone::class);
+               $calendar = $this->createMock(CalendarImpl::class);
+               $vCalendar = $this->createMock(VCalendar::class);
+               $availability = $this->getVAvailability();
+               $result = Reader::read('BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject 4.5.3//EN
+               CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VFREEBUSY
+DTSTART:19700101T000000Z
+DTEND:19700101T003600Z
+DTSTAMP:19700101T000200Z
+FREEBUSY:19700101T000000Z/19700101T003600Z
+END:VFREEBUSY
+END:VCALENDAR');
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with(['{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willReturn(true);
+               $this->timeFactory->expects(self::once())
+                       ->method('now')
+                       ->willReturn($now);
+               $this->calendarManager->expects(self::once())
+                       ->method('getCalendarsForPrincipal')
+                       ->with($principal)
+                       ->willReturn([$calendar]);
+               $this->calendarManager->expects(self::once())
+                       ->method('newQuery')
+                       ->with($principal)
+                       ->willReturn($query);
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTransparency')
+                       ->willReturn(new ScheduleCalendarTransp('opaque'));
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTimezone')
+                       ->willReturn($timezoneObj);
+               $timezoneObj->expects(self::once())
+                       ->method('getTimeZone')
+                       ->willReturn($timezone);
+               $calendar->expects(self::once())
+                       ->method('getUri');
+               $query->expects(self::once())
+                       ->method('addSearchCalendar');
+               $query->expects(self::once())
+                       ->method('getCalendarUris')
+                       ->willReturn([$calendar]);
+               $this->timeFactory->expects(self::once())
+                       ->method('getDateTime')
+                       ->with('+10 minutes')
+                       ->willReturn($inTenMinutes);
+               $query->expects(self::once())
+                       ->method('setTimerangeStart')
+                       ->with($now);
+               $query->expects(self::once())
+                       ->method('setTimerangeEnd')
+                       ->with($immutableInTenMinutes);
+               $this->calendarManager->expects(self::once())
+                       ->method('searchForPrincipal')
+                       ->with($query)
+                       ->willReturn([]);
+               $this->generator->expects(self::once())
+                       ->method('getVCalendar')
+                       ->willReturn($vCalendar);
+               $vCalendar->expects(self::never())
+                       ->method('add');
+               $this->generator->expects(self::once())
+                       ->method('setObjects')
+                       ->with($vCalendar);
+               $this->generator->expects(self::once())
+                       ->method('setTimeRange')
+                       ->with($now, $immutableInTenMinutes);
+               $this->generator->expects(self::once())
+                       ->method('setTimeZone')
+                       ->with($timezone);
+               $this->generator->expects(self::once())
+                       ->method('setVAvailability')
+                       ->with($availability);
+               $this->generator->expects(self::once())
+                       ->method('getResult')
+                       ->willReturn($result);
+
+               $status = $this->service->processCalendarAvailability($user, $availability->serialize());
+               $this->assertEquals(new Status(IUserStatus::BUSY, IUserStatus::MESSAGE_CALENDAR_BUSY), $status);
+       }
+
+       public function testAvailabilityAndSearchCalendarsStatusBusy(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $calendarHome = $this->createMock(LocalHref::class);
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+               $now = new \DateTimeImmutable('1970-1-1 00:00', new \DateTimeZone('UTC'));
+               $inTenMinutes = new \DateTime('1970-1-1 01:00');
+               $immutableInTenMinutes = \DateTimeImmutable::createFromMutable($inTenMinutes);
+               $principal = 'principals/users/admin';
+               $query = $this->createMock(CalendarQuery::class);
+               $timezone = new \DateTimeZone('UTC');
+               $timezoneObj = $this->createMock(VTimeZone::class);
+               $calendar = $this->createMock(CalendarImpl::class);
+               $vCalendar = $this->createMock(VCalendar::class);
+               $availability = $this->getVAvailability();
+               $result = Reader::read('BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject 4.5.3//EN
+               CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VFREEBUSY
+DTSTART:19700101T000000Z
+DTEND:19700101T003600Z
+DTSTAMP:19700101T000200Z
+FREEBUSY;FBTYPE=BUSY:19700101T000000Z/19700101T003600Z
+END:VFREEBUSY
+END:VCALENDAR');
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with(['{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willReturn(true);
+               $this->timeFactory->expects(self::once())
+                       ->method('now')
+                       ->willReturn($now);
+               $this->calendarManager->expects(self::once())
+                       ->method('getCalendarsForPrincipal')
+                       ->with($principal)
+                       ->willReturn([$calendar]);
+               $this->calendarManager->expects(self::once())
+                       ->method('newQuery')
+                       ->with($principal)
+                       ->willReturn($query);
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTransparency')
+                       ->willReturn(new ScheduleCalendarTransp('opaque'));
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTimezone')
+                       ->willReturn($timezoneObj);
+               $timezoneObj->expects(self::once())
+                       ->method('getTimeZone')
+                       ->willReturn($timezone);
+               $calendar->expects(self::once())
+                       ->method('getUri');
+               $query->expects(self::once())
+                       ->method('addSearchCalendar');
+               $query->expects(self::once())
+                       ->method('getCalendarUris')
+                       ->willReturn([$calendar]);
+               $this->timeFactory->expects(self::once())
+                       ->method('getDateTime')
+                       ->with('+10 minutes')
+                       ->willReturn($inTenMinutes);
+               $query->expects(self::once())
+                       ->method('setTimerangeStart')
+                       ->with($now);
+               $query->expects(self::once())
+                       ->method('setTimerangeEnd')
+                       ->with($immutableInTenMinutes);
+               $this->calendarManager->expects(self::once())
+                       ->method('searchForPrincipal')
+                       ->with($query)
+                       ->willReturn([]);
+               $this->generator->expects(self::once())
+                       ->method('getVCalendar')
+                       ->willReturn($vCalendar);
+               $vCalendar->expects(self::never())
+                       ->method('add');
+               $this->generator->expects(self::once())
+                       ->method('setObjects')
+                       ->with($vCalendar);
+               $this->generator->expects(self::once())
+                       ->method('setTimeRange')
+                       ->with($now, $immutableInTenMinutes);
+               $this->generator->expects(self::once())
+                       ->method('setTimeZone')
+                       ->with($timezone);
+               $this->generator->expects(self::once())
+                       ->method('setVAvailability')
+                       ->with($availability);
+               $this->generator->expects(self::once())
+                       ->method('getResult')
+                       ->willReturn($result);
+               $this->l10n->expects(self::once())
+                       ->method('t')
+                       ->with('In a meeting')
+                       ->willReturn('In a meeting');
+
+               $status = $this->service->processCalendarAvailability($user, $availability->serialize());
+               $this->assertEquals(new Status(IUserStatus::BUSY, IUserStatus::MESSAGE_CALENDAR_BUSY, 'In a meeting'), $status);
+       }
+
+       public function testAvailabilityAndSearchCalendarsStatusBusyUnavailable(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $calendarHome = $this->createMock(LocalHref::class);
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+               $now = new \DateTimeImmutable('1970-1-1 00:00', new \DateTimeZone('UTC'));
+               $inTenMinutes = new \DateTime('1970-1-1 01:00');
+               $immutableInTenMinutes = \DateTimeImmutable::createFromMutable($inTenMinutes);
+               $principal = 'principals/users/admin';
+               $query = $this->createMock(CalendarQuery::class);
+               $timezone = new \DateTimeZone('UTC');
+               $timezoneObj = $this->createMock(VTimeZone::class);
+               $calendar = $this->createMock(CalendarImpl::class);
+               $vCalendar = $this->createMock(VCalendar::class);
+               $availability = $this->getVAvailability();
+               $result = Reader::read('BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject 4.5.3//EN
+               CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VFREEBUSY
+DTSTART:19700101T000000Z
+DTEND:19700101T003600Z
+DTSTAMP:19700101T000200Z
+FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:19700101T000000Z/19700101T003600Z
+END:VFREEBUSY
+END:VCALENDAR');
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with(['{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willReturn(true);
+               $this->timeFactory->expects(self::once())
+                       ->method('now')
+                       ->willReturn($now);
+               $this->calendarManager->expects(self::once())
+                       ->method('getCalendarsForPrincipal')
+                       ->with($principal)
+                       ->willReturn([$calendar]);
+               $this->calendarManager->expects(self::once())
+                       ->method('newQuery')
+                       ->with($principal)
+                       ->willReturn($query);
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTransparency')
+                       ->willReturn(new ScheduleCalendarTransp('opaque'));
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTimezone')
+                       ->willReturn($timezoneObj);
+               $timezoneObj->expects(self::once())
+                       ->method('getTimeZone')
+                       ->willReturn($timezone);
+               $calendar->expects(self::once())
+                       ->method('getUri');
+               $query->expects(self::once())
+                       ->method('addSearchCalendar');
+               $query->expects(self::once())
+                       ->method('getCalendarUris')
+                       ->willReturn([$calendar]);
+               $this->timeFactory->expects(self::once())
+                       ->method('getDateTime')
+                       ->with('+10 minutes')
+                       ->willReturn($inTenMinutes);
+               $query->expects(self::once())
+                       ->method('setTimerangeStart')
+                       ->with($now);
+               $query->expects(self::once())
+                       ->method('setTimerangeEnd')
+                       ->with($immutableInTenMinutes);
+               $this->calendarManager->expects(self::once())
+                       ->method('searchForPrincipal')
+                       ->with($query)
+                       ->willReturn([]);
+               $this->generator->expects(self::once())
+                       ->method('getVCalendar')
+                       ->willReturn($vCalendar);
+               $vCalendar->expects(self::never())
+                       ->method('add');
+               $this->generator->expects(self::once())
+                       ->method('setObjects')
+                       ->with($vCalendar);
+               $this->generator->expects(self::once())
+                       ->method('setTimeRange')
+                       ->with($now, $immutableInTenMinutes);
+               $this->generator->expects(self::once())
+                       ->method('setTimeZone')
+                       ->with($timezone);
+               $this->generator->expects(self::once())
+                       ->method('setVAvailability')
+                       ->with($availability);
+               $this->generator->expects(self::once())
+                       ->method('getResult')
+                       ->willReturn($result);
+               $this->l10n->expects(self::never())
+                       ->method('t');
+               $status = $this->service->processCalendarAvailability($user, $availability->serialize());
+               $this->assertEquals(new Status(IUserStatus::AWAY, IUserStatus::MESSAGE_AVAILABILITY), $status);
+       }
+
+       public function testAvailabilityAndSearchCalendarsStatusBusyTentative(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $calendarHome = $this->createMock(LocalHref::class);
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+               $now = new \DateTimeImmutable('1970-1-1 00:00', new \DateTimeZone('UTC'));
+               $inTenMinutes = new \DateTime('1970-1-1 01:00');
+               $immutableInTenMinutes = \DateTimeImmutable::createFromMutable($inTenMinutes);
+               $principal = 'principals/users/admin';
+               $query = $this->createMock(CalendarQuery::class);
+               $timezone = new \DateTimeZone('UTC');
+               $timezoneObj = $this->createMock(VTimeZone::class);
+               $calendar = $this->createMock(CalendarImpl::class);
+               $vCalendar = $this->createMock(VCalendar::class);
+               $availability = $this->getVAvailability();
+               $result = Reader::read('BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject 4.5.3//EN
+               CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VFREEBUSY
+DTSTART:19700101T000000Z
+DTEND:19700101T003600Z
+DTSTAMP:19700101T000200Z
+FREEBUSY;FBTYPE=BUSY-TENTATIVE:19700101T000000Z/19700101T003600Z
+END:VFREEBUSY
+END:VCALENDAR');
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with(['{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willReturn(true);
+               $this->timeFactory->expects(self::once())
+                       ->method('now')
+                       ->willReturn($now);
+               $this->calendarManager->expects(self::once())
+                       ->method('getCalendarsForPrincipal')
+                       ->with($principal)
+                       ->willReturn([$calendar]);
+               $this->calendarManager->expects(self::once())
+                       ->method('newQuery')
+                       ->with($principal)
+                       ->willReturn($query);
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTransparency')
+                       ->willReturn(new ScheduleCalendarTransp('opaque'));
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTimezone')
+                       ->willReturn($timezoneObj);
+               $timezoneObj->expects(self::once())
+                       ->method('getTimeZone')
+                       ->willReturn($timezone);
+               $calendar->expects(self::once())
+                       ->method('getUri');
+               $query->expects(self::once())
+                       ->method('addSearchCalendar');
+               $query->expects(self::once())
+                       ->method('getCalendarUris')
+                       ->willReturn([$calendar]);
+               $this->timeFactory->expects(self::once())
+                       ->method('getDateTime')
+                       ->with('+10 minutes')
+                       ->willReturn($inTenMinutes);
+               $query->expects(self::once())
+                       ->method('setTimerangeStart')
+                       ->with($now);
+               $query->expects(self::once())
+                       ->method('setTimerangeEnd')
+                       ->with($immutableInTenMinutes);
+               $this->calendarManager->expects(self::once())
+                       ->method('searchForPrincipal')
+                       ->with($query)
+                       ->willReturn([]);
+               $this->generator->expects(self::once())
+                       ->method('getVCalendar')
+                       ->willReturn($vCalendar);
+               $vCalendar->expects(self::never())
+                       ->method('add');
+               $this->generator->expects(self::once())
+                       ->method('setObjects')
+                       ->with($vCalendar);
+               $this->generator->expects(self::once())
+                       ->method('setTimeRange')
+                       ->with($now, $immutableInTenMinutes);
+               $this->generator->expects(self::once())
+                       ->method('setTimeZone')
+                       ->with($timezone);
+               $this->generator->expects(self::once())
+                       ->method('setVAvailability')
+                       ->with($availability);
+               $this->generator->expects(self::once())
+                       ->method('getResult')
+                       ->willReturn($result);
+               $this->l10n->expects(self::never())
+                       ->method('t');
+               $status = $this->service->processCalendarAvailability($user, $availability->serialize());
+               $this->assertEquals(new Status(IUserStatus::AWAY, IUserStatus::MESSAGE_CALENDAR_BUSY_TENTATIVE), $status);
+       }
+
+       public function testAvailabilityAndSearchCalendarsStatusBusyUnknownProperty(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $server = $this->createMock(Server::class);
+               $schedulingPlugin = $this->createMock(Plugin::class);
+               $aclPlugin = $this->createMock(\Sabre\DAVACL\Plugin::class);
+               $calendarHome = $this->createMock(LocalHref::class);
+               $acl = [[200 => ['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL' => $calendarHome]]];
+               $now = new \DateTimeImmutable('1970-1-1 00:00', new \DateTimeZone('UTC'));
+               $inTenMinutes = new \DateTime('1970-1-1 01:00');
+               $immutableInTenMinutes = \DateTimeImmutable::createFromMutable($inTenMinutes);
+               $principal = 'principals/users/admin';
+               $query = $this->createMock(CalendarQuery::class);
+               $timezone = new \DateTimeZone('UTC');
+               $timezoneObj = $this->createMock(VTimeZone::class);
+               $calendar = $this->createMock(CalendarImpl::class);
+               $vCalendar = $this->createMock(VCalendar::class);
+               $availability = $this->getVAvailability();
+               $result = Reader::read('BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject 4.5.3//EN
+               CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VFREEBUSY
+DTSTART:19700101T000000Z
+DTEND:19700101T003600Z
+DTSTAMP:19700101T000200Z
+FREEBUSY;FBTYPE=X-MEETING:19700101T000000Z/19700101T003600Z
+END:VFREEBUSY
+END:VCALENDAR');
+
+               $user->expects(self::once())
+                       ->method('getUID')
+                       ->willReturn('admin');
+               $user->expects(self::once())
+                       ->method('getEMailAddress')
+                       ->willReturn('test@test.com');
+               $this->server->expects(self::once())
+                       ->method('getServer')
+                       ->willReturn($server);
+               $server->expects(self::exactly(2))
+                       ->method('getPlugin')
+                       ->withConsecutive(
+                               ['caldav-schedule'],
+                               ['acl'],
+                       )->willReturnOnConsecutiveCalls($schedulingPlugin, $aclPlugin);
+               $aclPlugin->expects(self::once())
+                       ->method('principalSearch')
+                       ->with(['{http://sabredav.org/ns}email-address' => 'test@test.com'])
+                       ->willReturn($acl);
+               $calendarHome->expects(self::once())
+                       ->method('getHref')
+                       ->willReturn('calendars/admin/inbox/');
+               $aclPlugin->expects(self::once())
+                       ->method('checkPrivileges')
+                       ->willReturn(true);
+               $this->timeFactory->expects(self::once())
+                       ->method('now')
+                       ->willReturn($now);
+               $this->calendarManager->expects(self::once())
+                       ->method('getCalendarsForPrincipal')
+                       ->with($principal)
+                       ->willReturn([$calendar]);
+               $this->calendarManager->expects(self::once())
+                       ->method('newQuery')
+                       ->with($principal)
+                       ->willReturn($query);
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTransparency')
+                       ->willReturn(new ScheduleCalendarTransp('opaque'));
+               $calendar->expects(self::once())
+                       ->method('getSchedulingTimezone')
+                       ->willReturn($timezoneObj);
+               $timezoneObj->expects(self::once())
+                       ->method('getTimeZone')
+                       ->willReturn($timezone);
+               $calendar->expects(self::once())
+                       ->method('getUri');
+               $query->expects(self::once())
+                       ->method('addSearchCalendar');
+               $query->expects(self::once())
+                       ->method('getCalendarUris')
+                       ->willReturn([$calendar]);
+               $this->timeFactory->expects(self::once())
+                       ->method('getDateTime')
+                       ->with('+10 minutes')
+                       ->willReturn($inTenMinutes);
+               $query->expects(self::once())
+                       ->method('setTimerangeStart')
+                       ->with($now);
+               $query->expects(self::once())
+                       ->method('setTimerangeEnd')
+                       ->with($immutableInTenMinutes);
+               $this->calendarManager->expects(self::once())
+                       ->method('searchForPrincipal')
+                       ->with($query)
+                       ->willReturn([]);
+               $this->generator->expects(self::once())
+                       ->method('getVCalendar')
+                       ->willReturn($vCalendar);
+               $vCalendar->expects(self::never())
+                       ->method('add');
+               $this->generator->expects(self::once())
+                       ->method('setObjects')
+                       ->with($vCalendar);
+               $this->generator->expects(self::once())
+                       ->method('setTimeRange')
+                       ->with($now, $immutableInTenMinutes);
+               $this->generator->expects(self::once())
+                       ->method('setTimeZone')
+                       ->with($timezone);
+               $this->generator->expects(self::once())
+                       ->method('setVAvailability')
+                       ->with($availability);
+               $this->generator->expects(self::once())
+                       ->method('getResult')
+                       ->willReturn($result);
+               $this->l10n->expects(self::never())
+                       ->method('t');
+               $status = $this->service->processCalendarAvailability($user, $availability->serialize());
+               $this->assertNull($status);
+       }
+
+       private function getVAvailability(): Document {
+               return Reader::read('BEGIN:VCALENDAR
+PRODID:Nextcloud DAV app
+BEGIN:VTIMEZONE
+TZID:Europe/Vienna
+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/Vienna:20231025T000000
+DTEND;TZID=Europe/Vienna:20231025T235900
+UID:d866782e-e003-4906-9ece-303f270a2c6b
+RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SA,SU
+END:AVAILABLE
+END:VAVAILABILITY
+END:VCALENDAR');
+       }
+}
index 3621c1bfa9666c7db248ce67dcdbce1d0735a749..c8ffcca5ad90cb39323b76da697707bfef25b04b 100644 (file)
@@ -26,6 +26,7 @@ declare(strict_types=1);
 
 namespace OCA\UserStatus\Db;
 
+use Sabre\CalDAV\Schedule\Plugin;
 use OCP\AppFramework\Db\QBMapper;
 use OCP\DB\QueryBuilder\IQueryBuilder;
 use OCP\IDBConnection;
@@ -210,4 +211,23 @@ class UserStatusMapper extends QBMapper {
 
                $qb->executeStatement();
        }
+
+       public function getAvailabilityFromPropertiesTable(string $userId): ?string {
+               $propertyPath = 'calendars/' . $userId . '/inbox';
+               $propertyName = '{' . Plugin::NS_CALDAV . '}calendar-availability';
+
+               $query = $this->db->getQueryBuilder();
+               $query->select('propertyvalue')
+                       ->from('properties')
+                       ->where($query->expr()->eq('userid', $query->createNamedParameter($userId)))
+                       ->andWhere($query->expr()->eq('propertypath', $query->createNamedParameter($propertyPath)))
+                       ->andWhere($query->expr()->eq('propertyname', $query->createNamedParameter($propertyName)))
+                       ->setMaxResults(1);
+
+               $result = $query->executeQuery();
+               $property = $result->fetchOne();
+               $result->closeCursor();
+
+               return ($property === false ? null : $property);
+       }
 }
index 3e05efa7118616a80e8673989fc089590117c3ab..687e01fc3a787c4cab59760398e8f35cb6e03b14 100644 (file)
@@ -74,7 +74,7 @@ class UserLiveStatusListener implements IEventListener {
                        $userStatus->setIsUserDefined(false);
                }
 
-               // If the status is user-defined and one of the persistent statuses, we
+               // If the status is user-defined and one of the persistent status, we
                // will not override it.
                if ($userStatus->getIsUserDefined() &&
                        \in_array($userStatus->getStatus(), StatusService::PERSISTENT_STATUSES, true)) {
index 7d2f985c1689560a44dc1d38f16d304a03c95a57..516680ba6835544c7d63db122139b4157f1b8c8d 100644 (file)
@@ -202,6 +202,8 @@ class PredefinedStatusService {
                        self::REMOTE_WORK,
                        IUserStatus::MESSAGE_CALL,
                        IUserStatus::MESSAGE_AVAILABILITY,
+                       IUserStatus::MESSAGE_CALENDAR_BUSY,
+                       IUserStatus::MESSAGE_CALENDAR_BUSY_TENTATIVE,
                ], true);
        }
 }
index f9ae769a5a9d5739a0435e004c348e0f2e39f5c1..003c9aa849a45214b67ae4ee473a88b521948dcd 100644 (file)
@@ -7,6 +7,7 @@ declare(strict_types=1);
  *
  * @author Georg Ehrke <oc.list@georgehrke.com>
  * @author Joas Schilling <coding@schilljs.com>
+ * @author Anna Larch <anna.larch@gmx.net>
  *
  * @license GNU AGPL version 3 or any later version
  *
@@ -26,6 +27,8 @@ declare(strict_types=1);
  */
 namespace OCA\UserStatus\Service;
 
+use OCA\DAV\CalDAV\Status\Status as CalendarStatus;
+use OCA\DAV\CalDAV\Status\StatusService as CalendarStatusService;
 use OCA\UserStatus\Db\UserStatus;
 use OCA\UserStatus\Db\UserStatusMapper;
 use OCA\UserStatus\Exception\InvalidClearAtException;
@@ -35,10 +38,13 @@ use OCA\UserStatus\Exception\InvalidStatusTypeException;
 use OCA\UserStatus\Exception\StatusMessageTooLongException;
 use OCP\AppFramework\Db\DoesNotExistException;
 use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Calendar\ISchedulingInformation;
 use OCP\DB\Exception;
 use OCP\IConfig;
 use OCP\IEmojiHelper;
+use OCP\IUserManager;
 use OCP\UserStatus\IUserStatus;
+use function in_array;
 
 /**
  * Class StatusService
@@ -46,26 +52,9 @@ use OCP\UserStatus\IUserStatus;
  * @package OCA\UserStatus\Service
  */
 class StatusService {
-
-       /** @var UserStatusMapper */
-       private $mapper;
-
-       /** @var ITimeFactory */
-       private $timeFactory;
-
-       /** @var PredefinedStatusService */
-       private $predefinedStatusService;
-
-       private IEmojiHelper $emojiHelper;
-
-       /** @var bool */
-       private $shareeEnumeration;
-
-       /** @var bool */
-       private $shareeEnumerationInGroupOnly;
-
-       /** @var bool */
-       private $shareeEnumerationPhone;
+       private bool $shareeEnumeration;
+       private bool $shareeEnumerationInGroupOnly;
+       private bool $shareeEnumerationPhone;
 
        /**
         * List of priorities ordered by their priority
@@ -74,6 +63,7 @@ class StatusService {
                IUserStatus::ONLINE,
                IUserStatus::AWAY,
                IUserStatus::DND,
+               IUserStatus::BUSY,
                IUserStatus::INVISIBLE,
                IUserStatus::OFFLINE,
        ];
@@ -84,6 +74,7 @@ class StatusService {
         */
        public const PERSISTENT_STATUSES = [
                IUserStatus::AWAY,
+               IUserStatus::BUSY,
                IUserStatus::DND,
                IUserStatus::INVISIBLE,
        ];
@@ -94,18 +85,16 @@ class StatusService {
        /** @var int */
        public const MAXIMUM_MESSAGE_LENGTH = 80;
 
-       public function __construct(UserStatusMapper $mapper,
-                                                               ITimeFactory $timeFactory,
-                                                               PredefinedStatusService $defaultStatusService,
-                                                               IEmojiHelper $emojiHelper,
-                                                               IConfig $config) {
-               $this->mapper = $mapper;
-               $this->timeFactory = $timeFactory;
-               $this->predefinedStatusService = $defaultStatusService;
-               $this->emojiHelper = $emojiHelper;
-               $this->shareeEnumeration = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
-               $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
-               $this->shareeEnumerationPhone = $this->shareeEnumeration && $config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
+       public function __construct(private UserStatusMapper $mapper,
+                                                               private ITimeFactory $timeFactory,
+                                                               private PredefinedStatusService $predefinedStatusService,
+                                                               private IEmojiHelper $emojiHelper,
+                                                               private IConfig $config,
+                                                               private IUserManager $userManager,
+                                                               private CalendarStatusService $calendarStatusService) {
+               $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
+               $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
+               $this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
        }
 
        /**
@@ -149,8 +138,37 @@ class StatusService {
         * @return UserStatus
         * @throws DoesNotExistException
         */
-       public function findByUserId(string $userId):UserStatus {
-               return $this->processStatus($this->mapper->findByUserId($userId));
+       public function findByUserId(string $userId): UserStatus {
+               $userStatus = $this->mapper->findByUserId($userId);
+               // If the status is user-defined and one of the persistent status, we
+               // will not override it.
+               if ($userStatus->getIsUserDefined() && \in_array($userStatus->getStatus(), StatusService::PERSISTENT_STATUSES, true)) {
+                       return $this->processStatus($userStatus);
+               }
+
+               $calendarStatus = $this->getCalendarStatus($userId);
+               // We found no status from the calendar, proceed with the existing status
+               if($calendarStatus === null) {
+                       return $this->processStatus($userStatus);
+               }
+
+               // if we have the same status result for the calendar and the current status,
+               // and a custom message to boot, we leave the existing status alone
+               // as to not overwrite a custom message / emoji
+               if($userStatus->getIsUserDefined()
+                       && $calendarStatus->getStatus() === $userStatus->getStatus()
+                       && !empty($userStatus->getCustomMessage())) {
+                       return $this->processStatus($userStatus);
+               }
+
+               // If the new status is null, there's already an identical status in place
+               $newUserStatus = $this->setUserStatus($userId,
+                       $calendarStatus->getStatus(),
+                       $calendarStatus->getMessage() ?? IUserStatus::MESSAGE_AVAILABILITY,
+                       true,
+                       $calendarStatus->getCustomMessage() ?? '');
+
+               return $newUserStatus === null ? $this->processStatus($userStatus) : $this->processStatus($newUserStatus);
        }
 
        /**
@@ -183,9 +201,12 @@ class StatusService {
                }
 
                // Check if status-type is valid
-               if (!\in_array($status, self::PRIORITY_ORDERED_STATUSES, true)) {
+               if (!in_array($status, self::PRIORITY_ORDERED_STATUSES, true)) {
                        throw new InvalidStatusTypeException('Status-type "' . $status . '" is not supported');
                }
+
+
+
                if ($statusTimestamp === null) {
                        $statusTimestamp = $this->timeFactory->getTime();
                }
@@ -255,11 +276,12 @@ class StatusService {
         * @throws InvalidMessageIdException
         */
        public function setUserStatus(string $userId,
-                                                                                string $status,
-                                                                                string $messageId,
-                                                                                bool $createBackup): void {
+                                                                       string $status,
+                                                                       string $messageId,
+                                                                       bool $createBackup,
+                                                                       string $customMessage = null): ?UserStatus {
                // Check if status-type is valid
-               if (!\in_array($status, self::PRIORITY_ORDERED_STATUSES, true)) {
+               if (!in_array($status, self::PRIORITY_ORDERED_STATUSES, true)) {
                        throw new InvalidStatusTypeException('Status-type "' . $status . '" is not supported');
                }
 
@@ -269,7 +291,7 @@ class StatusService {
 
                if ($createBackup) {
                        if ($this->backupCurrentStatus($userId) === false) {
-                               return; // Already a status set automatically => abort.
+                               return null; // Already a status set automatically => abort.
                        }
 
                        // If we just created the backup
@@ -290,15 +312,14 @@ class StatusService {
                $userStatus->setIsBackup(false);
                $userStatus->setMessageId($messageId);
                $userStatus->setCustomIcon(null);
-               $userStatus->setCustomMessage(null);
+               $userStatus->setCustomMessage($customMessage);
                $userStatus->setClearAt(null);
                $userStatus->setStatusMessageTimestamp($this->timeFactory->now()->getTimestamp());
 
                if ($userStatus->getId() !== null) {
-                       $this->mapper->update($userStatus);
-                       return;
+                       return $this->mapper->update($userStatus);
                }
-               $this->mapper->insert($userStatus);
+               return $this->mapper->insert($userStatus);
        }
 
        /**
@@ -561,4 +582,35 @@ class StatusService {
                // For users that matched restore the previous status
                $this->mapper->restoreBackupStatuses($restoreIds);
        }
+
+       /**
+        * Calculate a users' status according to their availabilit settings and their calendar
+        * events
+        *
+        * There are 4 predefined types of FBTYPE - 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE',
+        * but 'X-' properties are possible
+        *
+        * @link https://icalendar.org/iCalendar-RFC-5545/3-2-9-free-busy-time-type.html
+        *
+        * The status will be changed for types
+        *  - 'BUSY'
+        *  - 'BUSY-UNAVAILABLE' (ex.: when a VAVILABILITY setting is in effect)
+        *  - 'BUSY-TENTATIVE' (ex.: an event has been accepted tentatively)
+        * and all FREEBUSY components without a type (implicitly a 'BUSY' status)
+        *
+        * 'X-' properties are not handled for now
+        *
+        * @param string $userId
+        * @return CalendarStatus|null
+        */
+       public function getCalendarStatus(string $userId): ?CalendarStatus {
+               $user = $this->userManager->get($userId);
+               if ($user === null) {
+                       return null;
+               }
+
+               $availability = $this->mapper->getAvailabilityFromPropertiesTable($userId);
+
+               return $this->calendarStatusService->processCalendarAvailability($user, $availability);
+       }
 }
index d1e3a9111fae70b8291d20a5b38074625fd4d158..e9746669f0983ff5f6f249b224cce7b8f38e2fff 100644 (file)
@@ -52,6 +52,7 @@ export default {
                                        return this.$t('user_status', 'Online')
 
                                case 'away':
+                               case 'busy':
                                        return this.$t('user_status', 'Away')
 
                                case 'dnd':
@@ -79,6 +80,7 @@ export default {
                                return 'icon-user-status-online'
 
                        case 'away':
+                       case 'busy':
                                return 'icon-user-status-away'
 
                        case 'dnd':
index 413cecce5954fe166767801e6e4b7170836a7576..bd150cd4258f9a98052ea1dcb67e72a9ccc7d633 100644 (file)
@@ -28,6 +28,9 @@ namespace OCA\UserStatus\Tests\Service;
 
 use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
 use OC\DB\Exceptions\DbalException;
+use OC\User\User;
+use OCA\DAV\CalDAV\Status\Status;
+use OCA\DAV\CalDAV\Status\StatusService as CalendarStatusService;
 use OCA\UserStatus\Db\UserStatus;
 use OCA\UserStatus\Db\UserStatusMapper;
 use OCA\UserStatus\Exception\InvalidClearAtException;
@@ -40,30 +43,40 @@ use OCA\UserStatus\Service\StatusService;
 use OCP\AppFramework\Db\DoesNotExistException;
 use OCP\AppFramework\Utility\ITimeFactory;
 use OCP\DB\Exception;
+use OCP\EventDispatcher\IEventDispatcher;
 use OCP\IConfig;
 use OCP\IEmojiHelper;
+use OCP\IUser;
+use OCP\IUserBackend;
+use OCP\IUserManager;
 use OCP\UserStatus\IUserStatus;
+use PHPUnit\Framework\MockObject\MockObject;
 use Test\TestCase;
 
 class StatusServiceTest extends TestCase {
 
-       /** @var UserStatusMapper|\PHPUnit\Framework\MockObject\MockObject */
+       /** @var UserStatusMapper|MockObject */
        private $mapper;
 
-       /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
+       /** @var ITimeFactory|MockObject */
        private $timeFactory;
 
-       /** @var PredefinedStatusService|\PHPUnit\Framework\MockObject\MockObject */
+       /** @var PredefinedStatusService|MockObject */
        private $predefinedStatusService;
 
-       /** @var IEmojiHelper|\PHPUnit\Framework\MockObject\MockObject */
+       /** @var IEmojiHelper|MockObject */
        private $emojiHelper;
 
-       /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+       /** @var IConfig|MockObject */
        private $config;
 
-       /** @var StatusService */
-       private $service;
+       /** @var IUserManager|MockObject  */
+       private $userManager;
+
+       /** @var CalendarStatusService|MockObject  */
+       private $calendarStatusService;
+
+       private StatusService $service;
 
        protected function setUp(): void {
                parent::setUp();
@@ -72,6 +85,8 @@ class StatusServiceTest extends TestCase {
                $this->timeFactory = $this->createMock(ITimeFactory::class);
                $this->predefinedStatusService = $this->createMock(PredefinedStatusService::class);
                $this->emojiHelper = $this->createMock(IEmojiHelper::class);
+               $this->userManager = $this->createMock(IUserManager::class);
+               $this->calendarStatusService = $this->createMock(CalendarStatusService::class);
 
                $this->config = $this->createMock(IConfig::class);
 
@@ -85,7 +100,10 @@ class StatusServiceTest extends TestCase {
                        $this->timeFactory,
                        $this->predefinedStatusService,
                        $this->emojiHelper,
-                       $this->config);
+                       $this->config,
+                       $this->userManager,
+                       $this->calendarStatusService,
+               );
        }
 
        public function testFindAll(): void {
@@ -139,7 +157,10 @@ class StatusServiceTest extends TestCase {
                        $this->timeFactory,
                        $this->predefinedStatusService,
                        $this->emojiHelper,
-                       $this->config);
+                       $this->config,
+                       $this->userManager,
+                       $this->calendarStatusService,
+               );
 
                $this->assertEquals([], $this->service->findAllRecentStatusChanges(20, 50));
 
@@ -156,21 +177,14 @@ class StatusServiceTest extends TestCase {
                        $this->timeFactory,
                        $this->predefinedStatusService,
                        $this->emojiHelper,
-                       $this->config);
+                       $this->config,
+                       $this->userManager,
+                       $this->calendarStatusService,
+               );
 
                $this->assertEquals([], $this->service->findAllRecentStatusChanges(20, 50));
        }
 
-       public function testFindByUserId(): void {
-               $status = $this->createMock(UserStatus::class);
-               $this->mapper->expects($this->once())
-                       ->method('findByUserId')
-                       ->with('john.doe')
-                       ->willReturn($status);
-
-               $this->assertEquals($status, $this->service->findByUserId('john.doe'));
-       }
-
        public function testFindByUserIdDoesNotExist(): void {
                $this->mapper->expects($this->once())
                        ->method('findByUserId')
@@ -825,4 +839,295 @@ class StatusServiceTest extends TestCase {
 
                $this->service->revertMultipleUserStatus(['john', 'nobackup', 'backuponly', 'nobackupanddnd'], 'call');
        }
+
+       public function testCalendarAvailabilityNoUser(): void {
+               $userId = 'admin';
+
+               $this->userManager->expects(self::once())
+                       ->method('get')
+                       ->with($userId)
+                       ->willReturn(null);
+               $this->mapper->expects(self::never())
+                       ->method('getAvailabilityFromPropertiesTable');
+               $this->calendarStatusService->expects(self::never())
+                       ->method('processCalendarAvailability');
+
+               $this->service->getCalendarStatus($userId);
+       }
+
+       public function testCalendarAvailabilityNoVavailablility(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+
+               $this->userManager->expects(self::once())
+                       ->method('get')
+                       ->with($user->getUID())
+                       ->willReturn($user);
+               $this->mapper->expects(self::once())
+                       ->method('getAvailabilityFromPropertiesTable')
+                       ->willReturn('');
+               $this->calendarStatusService->expects(self::once())
+                       ->method('processCalendarAvailability')
+                       ->with($user, '')
+                       ->willReturn(null);
+
+               $this->service->getCalendarStatus($user->getUID());
+       }
+
+       public function testCalendarAvailabilityVavailablilityAvailable(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+
+               $vavailability = <<<EOF
+BEGIN:VCALENDAR
+PRODID:Nextcloud DAV app
+BEGIN:VTIMEZONE
+TZID:Europe/Vienna
+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/Vienna:20231025T000000
+DTEND;TZID=Europe/Vienna:20231025T235900
+UID:d866782e-e003-4906-9ece-303f270a2c6b
+RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SA,SU
+END:AVAILABLE
+END:VAVAILABILITY
+END:VCALENDAR
+EOF;
+               $status = new Status(IUserStatus::AWAY);
+               $this->userManager->expects(self::once())
+                       ->method('get')
+                       ->with($user->getUID())
+                       ->willReturn($user);
+               $this->mapper->expects(self::once())
+                       ->method('getAvailabilityFromPropertiesTable')
+                       ->willReturn($vavailability);
+               $this->calendarStatusService->expects(self::once())
+                       ->method('processCalendarAvailability')
+                       ->with($user, $vavailability)
+                       ->willReturn($status);
+
+               $this->service->getCalendarStatus($user->getUID());
+       }
+
+       public function testCalendarAvailabilityVavailablilityUpdate(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $calDavStatus = new Status(IUserStatus::BUSY, 'meeting', 'In a meeting');
+               $vavailability = <<<EOF
+BEGIN:VCALENDAR
+PRODID:Nextcloud DAV app
+BEGIN:VTIMEZONE
+TZID:Europe/Vienna
+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/Vienna:20231025T000000
+DTEND;TZID=Europe/Vienna:20231025T235900
+UID:d866782e-e003-4906-9ece-303f270a2c6b
+RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SA,SU
+END:AVAILABLE
+END:VAVAILABILITY
+END:VCALENDAR
+EOF;
+               $this->userManager->expects(self::once())
+                       ->method('get')
+                       ->with($user->getUID())
+                       ->willReturn($user);
+               $this->mapper->expects(self::once())
+                       ->method('getAvailabilityFromPropertiesTable')
+                       ->willReturn($vavailability);
+               $this->calendarStatusService->expects(self::once())
+                       ->method('processCalendarAvailability')
+                       ->with($user, $vavailability)
+                       ->willReturn($calDavStatus);
+
+               $this->service->getCalendarStatus($user->getUID());
+       }
+
+       public function testFindByUserIdUserDefinedAndPersistent(): void {
+               $status = new UserStatus();
+               $status->setIsUserDefined(true);
+               $status->setStatus(IUserStatus::DND);
+
+               $this->mapper->expects($this->once())
+                       ->method('findByUserId')
+                       ->with('admin')
+                       ->willReturn($status);
+               $this->mapper->expects(self::never())
+                       ->method('getAvailabilityFromPropertiesTable');
+               $this->calendarStatusService->expects(self::never())
+                       ->method('processCalendarAvailability');
+
+               $this->assertEquals($status, $this->service->findByUserId('admin'));
+       }
+
+       public function testFindByUserIdUserDefinedNoCalStatus(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $status = new UserStatus();
+               $status->setIsUserDefined(true);
+               $status->setStatus(IUserStatus::ONLINE);
+
+               $this->mapper->expects($this->once())
+                       ->method('findByUserId')
+                       ->with($user->getUID())
+                       ->willReturn($status);
+               $this->userManager->expects(self::once())
+                       ->method('get')
+                       ->willReturn($user);
+               $this->mapper->expects(self::once())
+                       ->method('getAvailabilityFromPropertiesTable')
+                       ->willReturn('');
+               $this->calendarStatusService->expects(self::once())
+                       ->method('processCalendarAvailability')
+                       ->with($user, '')
+                       ->willReturn(null);
+
+               $this->assertEquals($status, $this->service->findByUserId('admin'));
+       }
+
+       public function testFindByUserIdUserDefinedCalStatusIdentical(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $calDavStatus = new Status(IUserStatus::ONLINE);
+               $userStatus = new UserStatus();
+               $userStatus->setStatus(IUserStatus::ONLINE);
+               $userStatus->setIsUserDefined(true);
+               $userStatus->setCustomMessage('Test');
+
+               $this->mapper->expects(self::once())
+                       ->method('findByUserId')
+                       ->with($user->getUID())
+                       ->willReturn($userStatus);
+               $this->userManager->expects(self::once())
+                       ->method('get')
+                       ->willReturn($user);
+               $this->mapper->expects(self::once())
+                       ->method('getAvailabilityFromPropertiesTable')
+                       ->willReturn('');
+               $this->calendarStatusService->expects(self::once())
+                       ->method('processCalendarAvailability')
+                       ->with($user, '')
+                       ->willReturn($calDavStatus);
+
+               $this->assertEquals($userStatus, $this->service->findByUserId('admin'));
+       }
+
+       public function testFindByUserIdUserDefinedCalStatusUpdate(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $calDavStatus = new Status(IUserStatus::BUSY, 'meeting', 'In a meeting');
+
+               $oldStatus = new UserStatus();
+               $oldStatus->setId(42);
+               $oldStatus->setUserId($user->getUID());
+               $oldStatus->setStatus(IUserStatus::ONLINE);
+               $oldStatus->setStatusTimestamp(0);
+               $oldStatus->setIsUserDefined(true);
+
+               $expected = new UserStatus();
+               $expected->setUserId($user->getUID());
+               $expected->setStatus(IUserStatus::BUSY);
+               $expected->setStatusTimestamp(0);
+               $expected->setIsUserDefined(true);
+               $expected->setIsBackup(false);
+
+               $this->mapper->expects(self::once())
+                       ->method('findByUserId')
+                       ->with($user->getUID())
+                       ->willReturn($oldStatus);
+               $this->userManager->expects(self::once())
+                       ->method('get')
+                       ->willReturn($user);
+               $this->mapper->expects(self::once())
+                       ->method('getAvailabilityFromPropertiesTable')
+                       ->willReturn('');
+               $this->mapper->expects(self::once())
+                       ->method('createBackupStatus')
+                       ->with($user->getUID())
+                       ->willReturn(true);
+               $this->calendarStatusService->expects(self::once())
+                       ->method('processCalendarAvailability')
+                       ->with($user, '')
+                       ->willReturn($calDavStatus);
+               $this->predefinedStatusService->expects(self::once())
+                       ->method('isValidId')
+                       ->with($calDavStatus->getMessage())
+                       ->willReturn(true);
+               $this->mapper->expects(self::once())
+                       ->method('insert')
+                       ->willReturn($expected);
+
+               $actual = $this->service->findByUserId('admin');
+               $this->assertEquals($expected->getStatus(), $actual->getStatus());
+               $this->assertEquals($expected->getCustomMessage(), $actual->getCustomMessage());
+       }
+
+       public function testFindByUserIdSystemDefined(): void {
+               $user = $this->createConfiguredMock(IUser::class, [
+                       'getUID' => 'admin',
+                       'getEMailAddress' => 'test@test.com',
+               ]);
+               $status = new UserStatus();
+               $status->setIsUserDefined(false);
+               $status->setStatus(IUserStatus::ONLINE);
+
+               $this->mapper->expects($this->once())
+                       ->method('findByUserId')
+                       ->with($user->getUID())
+                       ->willReturn($status);
+               $this->userManager->expects(self::once())
+                       ->method('get')
+                       ->willReturn($user);
+               $this->mapper->expects(self::once())
+                       ->method('getAvailabilityFromPropertiesTable')
+                       ->willReturn('');
+               $this->calendarStatusService->expects(self::once())
+                       ->method('processCalendarAvailability')
+                       ->with($user, '')
+                       ->willReturn(null);
+
+               $this->assertEquals($status, $this->service->findByUserId('admin'));
+       }
 }
index 764f37ac4c507c2fdac24895d00042d700fd7802..3ab422bec0b9b9ff1b7f5e14411fdd006593daa0 100644 (file)
@@ -1,3 +1,3 @@
 /*! For license information please see user_status-menu.js.LICENSE.txt */
-(()=>{var s,e,a,r={86419:(s,e,a)=>{"use strict";var r=a(20144),n=a(77958),o=a(69183),u=a(62642),i=a(20296),c=a.n(i),d=a(93664),l=a(79753),m=a(84387),p=a(25108);const g={name:"UserStatus",components:{NcButton:u.Z,SetStatusModal:()=>Promise.all([a.e(7874),a.e(8299)]).then(a.bind(a,21124))},mixins:[m.Z],props:{inline:{type:Boolean,default:!1}},data:()=>({heartbeatInterval:null,isAway:!1,isModalOpen:!1,mouseMoveListener:null,setAwayTimeout:null}),mounted(){this.$store.dispatch("loadStatusFromInitialState"),OC.config.session_keepalive&&(this.heartbeatInterval=setInterval(this._backgroundHeartbeat.bind(this),3e5),this.setAwayTimeout=()=>{this.isAway=!0},this.mouseMoveListener=c()((()=>{const s=this.isAway;this.isAway=!1,clearTimeout(this.setAwayTimeout),setTimeout(this.setAwayTimeout,12e4),s&&this._backgroundHeartbeat()}),2e3,!0),window.addEventListener("mousemove",this.mouseMoveListener,{capture:!0,passive:!0}),this._backgroundHeartbeat()),(0,o.Ld)("user_status:status.updated",this.handleUserStatusUpdated)},beforeDestroy(){window.removeEventListener("mouseMove",this.mouseMoveListener),clearInterval(this.heartbeatInterval),(0,o.r1)("user_status:status.updated",this.handleUserStatusUpdated)},methods:{openModal(){this.isModalOpen=!0},closeModal(){this.isModalOpen=!1},async _backgroundHeartbeat(){try{const s=await(async s=>{const t=(0,l.generateOcsUrl)("apps/user_status/api/v1/heartbeat?format=json");return(await d.Z.put(t,{status:s?"away":"online"})).data.ocs.data})(this.isAway);s?.userId?this.$store.dispatch("setStatusFromHeartbeat",s):await this.$store.dispatch("reFetchStatusFromServer")}catch(s){p.debug("Failed sending heartbeat, got: "+s.response?.status)}},handleUserStatusUpdated(s){OC.getCurrentUser().uid===s.userId&&this.$store.dispatch("setStatusFromObject",{status:s.status,icon:s.icon,message:s.message})}}};var h=a(93379),f=a.n(h),j=a(7795),v=a.n(j),b=a(90569),A=a.n(b),y=a(3565),w=a.n(y),k=a(19216),S=a.n(k),C=a(44589),I=a.n(C),_=a(34882),O={};O.styleTagTransform=I(),O.setAttributes=w(),O.insert=A().bind(null,"head"),O.domAPI=v(),O.insertStyleElement=S(),f()(_.Z,O),_.Z&&_.Z.locals&&_.Z.locals;const M=(0,a(51900).Z)(g,(function(){var s=this,t=s._self._c;return t(s.inline?"div":"li",{tag:"component"},[s.inline?t("NcButton",{attrs:{icon:s.statusIcon},on:{click:function(t){return t.stopPropagation(),s.openModal.apply(null,arguments)}},scopedSlots:s._u([{key:"icon",fn:function(){return[t("span",{staticClass:"user-status-icon",class:s.statusIcon,attrs:{"aria-hidden":"true"}})]},proxy:!0}])},[s._v("\n\t\t"+s._s(s.visibleMessage)+"\n\t")]):t("button",{staticClass:"user-status-menu-item",on:{click:function(t){return t.stopPropagation(),s.openModal.apply(null,arguments)}}},[t("span",{staticClass:"user-status-icon",class:s.statusIcon,attrs:{"aria-hidden":"true"}}),s._v("\n\t\t"+s._s(s.visibleMessage)+"\n\t")]),s._v(" "),s.isModalOpen?t("SetStatusModal",{on:{close:s.closeModal}}):s._e()],1)}),[],!1,null,"798bb70c",null).exports;var x=a(20629);const U={state:{predefinedStatuses:[]},mutations:{addPredefinedStatus(s,t){s.predefinedStatuses=[...s.predefinedStatuses,t]}},getters:{statusesHaveLoaded:s=>s.predefinedStatuses.length>0},actions:{async loadAllPredefinedStatuses(s){let{state:t,commit:e}=s;if(t.predefinedStatuses.length>0)return;const a=await(async()=>{const s=(0,l.generateOcsUrl)("apps/user_status/api/v1/predefined_statuses?format=json");return(await d.Z.get(s)).data.ocs.data})();for(const s of a)e("addPredefinedStatus",s)}}};var z=a(43554),P=a(64039),T=a(80351),F=a.n(T);const D=s=>{if(null===s)return null;const t=(0,P.n)();if("period"===s.type)return t.setSeconds(t.getSeconds()+s.time),Math.floor(t.getTime()/1e3);if("end-of"===s.type)switch(s.time){case"day":case"week":return Number(F()(t).endOf(s.time).format("X"))}return"_time"===s.type?s.time:null},E={state:{status:null,statusIsUserDefined:null,message:null,icon:null,clearAt:null,messageIsPredefined:null,messageId:null},mutations:{setStatus(s,t){let{statusType:e}=t;s.status=e,s.statusIsUserDefined=!0},setPredefinedMessage(s,t){let{messageId:e,clearAt:a,message:r,icon:n}=t;s.messageId=e,s.messageIsPredefined=!0,s.message=r,s.icon=n,s.clearAt=a},setCustomMessage(s,t){let{message:e,icon:a,clearAt:r}=t;s.messageId=null,s.messageIsPredefined=!1,s.message=e,s.icon=a,s.clearAt=r},clearMessage(s){s.messageId=null,s.messageIsPredefined=!1,s.message=null,s.icon=null,s.clearAt=null},loadStatusFromServer(s,t){let{status:e,statusIsUserDefined:a,message:r,icon:n,clearAt:o,messageIsPredefined:u,messageId:i}=t;s.status=e,s.message=r,s.icon=n,void 0!==a&&(s.statusIsUserDefined=a),void 0!==o&&(s.clearAt=o),void 0!==u&&(s.messageIsPredefined=u),void 0!==i&&(s.messageId=i)}},getters:{},actions:{async setStatus(s,t){let{commit:e,state:a}=s,{statusType:r}=t;await(async s=>{const t=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status/status");await d.Z.put(t,{statusType:s})})(r),e("setStatus",{statusType:r}),(0,o.j8)("user_status:status.updated",{status:a.status,message:a.message,icon:a.icon,clearAt:a.clearAt,userId:(0,n.ts)()?.uid})},async setStatusFromObject(s,t){let{commit:e,state:a}=s;e("loadStatusFromServer",t)},async setPredefinedMessage(s,t){let{commit:e,rootState:a,state:r}=s,{messageId:u,clearAt:i}=t;const c=D(i);await async function(s){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const e=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status/message/predefined?format=json");await d.Z.put(e,{messageId:s,clearAt:t})}(u,c);const m=a.predefinedStatuses.predefinedStatuses.find((s=>s.id===u)),{message:p,icon:g}=m;e("setPredefinedMessage",{messageId:u,clearAt:c,message:p,icon:g}),(0,o.j8)("user_status:status.updated",{status:r.status,message:r.message,icon:r.icon,clearAt:r.clearAt,userId:(0,n.ts)()?.uid})},async setCustomMessage(s,t){let{commit:e,state:a}=s,{message:r,icon:u,clearAt:i}=t;const c=D(i);await async function(s){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const a=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status/message/custom?format=json");await d.Z.put(a,{message:s,statusIcon:t,clearAt:e})}(r,u,c),e("setCustomMessage",{message:r,icon:u,clearAt:c}),(0,o.j8)("user_status:status.updated",{status:a.status,message:a.message,icon:a.icon,clearAt:a.clearAt,userId:(0,n.ts)()?.uid})},async clearMessage(s){let{commit:t,state:e}=s;await(async()=>{const s=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status/message?format=json");await d.Z.delete(s)})(),t("clearMessage"),(0,o.j8)("user_status:status.updated",{status:e.status,message:e.message,icon:e.icon,clearAt:e.clearAt,userId:(0,n.ts)()?.uid})},async reFetchStatusFromServer(s){let{commit:t}=s;t("loadStatusFromServer",await(async()=>{const s=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status");return(await d.Z.get(s)).data.ocs.data})())},async setStatusFromHeartbeat(s,t){let{commit:e}=s;e("loadStatusFromServer",t)},loadStatusFromInitialState(s){let{commit:t}=s;t("loadStatusFromServer",(0,z.j)("user_status","status"))}}},Z={state:{status:null,statusIsUserDefined:null,message:null,icon:null,clearAt:null,messageIsPredefined:null,messageId:null},mutations:{loadBackupStatusFromServer(s,t){let{status:e,statusIsUserDefined:a,message:r,icon:n,clearAt:o,messageIsPredefined:u,messageId:i}=t;s.status=e,s.message=r,s.icon=n,void 0!==a&&(s.statusIsUserDefined=a),void 0!==o&&(s.clearAt=o),void 0!==u&&(s.messageIsPredefined=u),void 0!==i&&(s.messageId=i)}},getters:{},actions:{async fetchBackupFromServer(s){let{commit:t}=s;try{t("loadBackupStatusFromServer",await(async s=>{const t=(0,l.generateOcsUrl)("apps/user_status/api/v1/statuses/{userId}",{userId:"_"+s});return(await d.Z.get(t)).data.ocs.data})((0,n.ts)()?.uid))}catch(s){}},async revertBackupFromServer(s,t){let{commit:e}=s,{messageId:a}=t;const r=await(async s=>{const t=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status/revert/{messageId}",{messageId:s});return(await d.Z.delete(t)).data.ocs.data})(a);r&&(e("loadBackupStatusFromServer",{}),e("loadStatusFromServer",r),(0,o.j8)("user_status:status.updated",{status:r.status,message:r.message,icon:r.icon,clearAt:r.clearAt,userId:(0,n.ts)()?.uid}))}}};r.default.use(x.ZP);const $=new x.yh({modules:{predefinedStatuses:U,userStatus:E,userBackupStatus:Z},strict:!0});a.nc=btoa((0,n.IH)()),r.default.prototype.t=t,r.default.prototype.$t=t;const B=()=>{const s=document.getElementById("user_status-menu-entry");new r.default({el:s,render:s=>s(M),store:$})};document.getElementById("user_status-menu-entry")?B():(0,o.Ld)("core:user-menu:mounted",B),document.addEventListener("DOMContentLoaded",(function(){OCA.Dashboard&&OCA.Dashboard.registerStatus("status",(s=>new(r.default.extend(M))({propsData:{inline:!0},store:$}).$mount(s)))}))},84387:(s,t,e)=>{"use strict";e.d(t,{Z:()=>o});var a=e(20629),r=e(64024),n=e(25108);const o={computed:{...(0,a.rn)({statusType:s=>s.userStatus.status,statusIsUserDefined:s=>s.userStatus.statusIsUserDefined,customIcon:s=>s.userStatus.icon,customMessage:s=>s.userStatus.message}),visibleMessage(){if(this.customIcon&&this.customMessage)return`${this.customIcon} ${this.customMessage}`;if(this.customMessage)return this.customMessage;if(this.statusIsUserDefined)switch(this.statusType){case"online":return this.$t("user_status","Online");case"away":return this.$t("user_status","Away");case"dnd":return this.$t("user_status","Do not disturb");case"invisible":return this.$t("user_status","Invisible");case"offline":return this.$t("user_status","Offline")}return this.$t("user_status","Set status")},statusIcon(){switch(this.statusType){case"online":return"icon-user-status-online";case"away":return"icon-user-status-away";case"dnd":return"icon-user-status-dnd";case"invisible":case"offline":return"icon-user-status-invisible"}return""}},methods:{async changeStatus(s){try{await this.$store.dispatch("setStatus",{statusType:s})}catch(s){(0,r.x2)(this.$t("user_status","There was an error saving the new status")),n.debug(s)}}}}},64039:(s,t,e)=>{"use strict";e.d(t,{n:()=>a});const a=()=>new Date},34882:(s,t,e)=>{"use strict";e.d(t,{Z:()=>u});var a=e(87537),r=e.n(a),n=e(23645),o=e.n(n)()(r());o.push([s.id,".user-status-menu-item[data-v-798bb70c]{width:auto;min-width:44px;height:44px;margin:0;border:0;border-radius:var(--border-radius-pill);background-color:var(--color-main-background-blur);font-size:inherit;font-weight:normal;-webkit-backdrop-filter:var(--background-blur);backdrop-filter:var(--background-blur)}.user-status-menu-item[data-v-798bb70c]:active,.user-status-menu-item[data-v-798bb70c]:hover,.user-status-menu-item[data-v-798bb70c]:focus-visible{background-color:var(--color-background-hover)}.user-status-menu-item[data-v-798bb70c]:focus-visible{box-shadow:0 0 0 4px var(--color-main-background) !important;outline:2px solid var(--color-main-text) !important}.user-status-icon[data-v-798bb70c]{width:16px;height:16px;margin-right:10px;opacity:1 !important;background-size:16px;vertical-align:middle !important}","",{version:3,sources:["webpack://./apps/user_status/src/UserStatus.vue"],names:[],mappings:"AACA,wCACC,UAAA,CACA,cAAA,CACA,WAAA,CACA,QAAA,CACA,QAAA,CACA,uCAAA,CACA,kDAAA,CACA,iBAAA,CACA,kBAAA,CAEA,8CAAA,CACA,sCAAA,CAEA,mJAGC,8CAAA,CAED,sDACC,4DAAA,CACA,mDAAA,CAIF,mCACC,UAAA,CACA,WAAA,CACA,iBAAA,CACA,oBAAA,CACA,oBAAA,CACA,gCAAA",sourcesContent:["\n.user-status-menu-item {\n\twidth: auto;\n\tmin-width: 44px;\n\theight: 44px;\n\tmargin: 0;\n\tborder: 0;\n\tborder-radius: var(--border-radius-pill);\n\tbackground-color: var(--color-main-background-blur);\n\tfont-size: inherit;\n\tfont-weight: normal;\n\n\t-webkit-backdrop-filter: var(--background-blur);\n\tbackdrop-filter: var(--background-blur);\n\n\t&:active,\n\t&:hover,\n\t&:focus-visible {\n\t\tbackground-color: var(--color-background-hover);\n\t}\n\t&:focus-visible {\n\t\tbox-shadow: 0 0 0 4px var(--color-main-background) !important;\n\t\toutline: 2px solid var(--color-main-text) !important;\n\t}\n}\n\n.user-status-icon {\n\twidth: 16px;\n\theight: 16px;\n\tmargin-right: 10px;\n\topacity: 1 !important;\n\tbackground-size: 16px;\n\tvertical-align: middle !important;\n}\n"],sourceRoot:""}]);const u=o},46700:(s,t,e)=>{var a={"./af":42786,"./af.js":42786,"./ar":30867,"./ar-dz":14130,"./ar-dz.js":14130,"./ar-kw":96135,"./ar-kw.js":96135,"./ar-ly":56440,"./ar-ly.js":56440,"./ar-ma":47702,"./ar-ma.js":47702,"./ar-sa":16040,"./ar-sa.js":16040,"./ar-tn":37100,"./ar-tn.js":37100,"./ar.js":30867,"./az":31083,"./az.js":31083,"./be":9808,"./be.js":9808,"./bg":68338,"./bg.js":68338,"./bm":67438,"./bm.js":67438,"./bn":8905,"./bn-bd":76225,"./bn-bd.js":76225,"./bn.js":8905,"./bo":11560,"./bo.js":11560,"./br":1278,"./br.js":1278,"./bs":80622,"./bs.js":80622,"./ca":2468,"./ca.js":2468,"./cs":5822,"./cs.js":5822,"./cv":50877,"./cv.js":50877,"./cy":47373,"./cy.js":47373,"./da":24780,"./da.js":24780,"./de":59740,"./de-at":60217,"./de-at.js":60217,"./de-ch":60894,"./de-ch.js":60894,"./de.js":59740,"./dv":5300,"./dv.js":5300,"./el":50837,"./el.js":50837,"./en-au":78348,"./en-au.js":78348,"./en-ca":77925,"./en-ca.js":77925,"./en-gb":22243,"./en-gb.js":22243,"./en-ie":46436,"./en-ie.js":46436,"./en-il":47207,"./en-il.js":47207,"./en-in":44175,"./en-in.js":44175,"./en-nz":76319,"./en-nz.js":76319,"./en-sg":31662,"./en-sg.js":31662,"./eo":92915,"./eo.js":92915,"./es":55655,"./es-do":55251,"./es-do.js":55251,"./es-mx":96112,"./es-mx.js":96112,"./es-us":71146,"./es-us.js":71146,"./es.js":55655,"./et":5603,"./et.js":5603,"./eu":77763,"./eu.js":77763,"./fa":76959,"./fa.js":76959,"./fi":11897,"./fi.js":11897,"./fil":42549,"./fil.js":42549,"./fo":94694,"./fo.js":94694,"./fr":94470,"./fr-ca":63049,"./fr-ca.js":63049,"./fr-ch":52330,"./fr-ch.js":52330,"./fr.js":94470,"./fy":5044,"./fy.js":5044,"./ga":29295,"./ga.js":29295,"./gd":2101,"./gd.js":2101,"./gl":38794,"./gl.js":38794,"./gom-deva":27884,"./gom-deva.js":27884,"./gom-latn":23168,"./gom-latn.js":23168,"./gu":95349,"./gu.js":95349,"./he":24206,"./he.js":24206,"./hi":30094,"./hi.js":30094,"./hr":30316,"./hr.js":30316,"./hu":22138,"./hu.js":22138,"./hy-am":11423,"./hy-am.js":11423,"./id":29218,"./id.js":29218,"./is":90135,"./is.js":90135,"./it":90626,"./it-ch":10150,"./it-ch.js":10150,"./it.js":90626,"./ja":39183,"./ja.js":39183,"./jv":24286,"./jv.js":24286,"./ka":12105,"./ka.js":12105,"./kk":47772,"./kk.js":47772,"./km":18758,"./km.js":18758,"./kn":79282,"./kn.js":79282,"./ko":33730,"./ko.js":33730,"./ku":1408,"./ku.js":1408,"./ky":33291,"./ky.js":33291,"./lb":36841,"./lb.js":36841,"./lo":55466,"./lo.js":55466,"./lt":57010,"./lt.js":57010,"./lv":37595,"./lv.js":37595,"./me":39861,"./me.js":39861,"./mi":35493,"./mi.js":35493,"./mk":95966,"./mk.js":95966,"./ml":87341,"./ml.js":87341,"./mn":5115,"./mn.js":5115,"./mr":10370,"./mr.js":10370,"./ms":9847,"./ms-my":41237,"./ms-my.js":41237,"./ms.js":9847,"./mt":72126,"./mt.js":72126,"./my":56165,"./my.js":56165,"./nb":64924,"./nb.js":64924,"./ne":16744,"./ne.js":16744,"./nl":93901,"./nl-be":59814,"./nl-be.js":59814,"./nl.js":93901,"./nn":83877,"./nn.js":83877,"./oc-lnc":92135,"./oc-lnc.js":92135,"./pa-in":15858,"./pa-in.js":15858,"./pl":64495,"./pl.js":64495,"./pt":89520,"./pt-br":57971,"./pt-br.js":57971,"./pt.js":89520,"./ro":96459,"./ro.js":96459,"./ru":21793,"./ru.js":21793,"./sd":40950,"./sd.js":40950,"./se":10490,"./se.js":10490,"./si":90124,"./si.js":90124,"./sk":64249,"./sk.js":64249,"./sl":14985,"./sl.js":14985,"./sq":51104,"./sq.js":51104,"./sr":49131,"./sr-cyrl":79915,"./sr-cyrl.js":79915,"./sr.js":49131,"./ss":85893,"./ss.js":85893,"./sv":98760,"./sv.js":98760,"./sw":91172,"./sw.js":91172,"./ta":27333,"./ta.js":27333,"./te":23110,"./te.js":23110,"./tet":52095,"./tet.js":52095,"./tg":27321,"./tg.js":27321,"./th":9041,"./th.js":9041,"./tk":19005,"./tk.js":19005,"./tl-ph":75768,"./tl-ph.js":75768,"./tlh":89444,"./tlh.js":89444,"./tr":72397,"./tr.js":72397,"./tzl":28254,"./tzl.js":28254,"./tzm":51106,"./tzm-latn":30699,"./tzm-latn.js":30699,"./tzm.js":51106,"./ug-cn":9288,"./ug-cn.js":9288,"./uk":67691,"./uk.js":67691,"./ur":13795,"./ur.js":13795,"./uz":6791,"./uz-latn":60588,"./uz-latn.js":60588,"./uz.js":6791,"./vi":65666,"./vi.js":65666,"./x-pseudo":14378,"./x-pseudo.js":14378,"./yo":75805,"./yo.js":75805,"./zh-cn":83839,"./zh-cn.js":83839,"./zh-hk":55726,"./zh-hk.js":55726,"./zh-mo":99807,"./zh-mo.js":99807,"./zh-tw":74152,"./zh-tw.js":74152};function r(s){var t=n(s);return e(t)}function n(s){if(!e.o(a,s)){var t=new Error("Cannot find module '"+s+"'");throw t.code="MODULE_NOT_FOUND",t}return a[s]}r.keys=function(){return Object.keys(a)},r.resolve=n,s.exports=r,r.id=46700}},n={};function o(s){var t=n[s];if(void 0!==t)return t.exports;var e=n[s]={id:s,loaded:!1,exports:{}};return r[s].call(e.exports,e,e.exports,o),e.loaded=!0,e.exports}o.m=r,s=[],o.O=(t,e,a,r)=>{if(!e){var n=1/0;for(d=0;d<s.length;d++){e=s[d][0],a=s[d][1],r=s[d][2];for(var u=!0,i=0;i<e.length;i++)(!1&r||n>=r)&&Object.keys(o.O).every((s=>o.O[s](e[i])))?e.splice(i--,1):(u=!1,r<n&&(n=r));if(u){s.splice(d--,1);var c=a();void 0!==c&&(t=c)}}return t}r=r||0;for(var d=s.length;d>0&&s[d-1][2]>r;d--)s[d]=s[d-1];s[d]=[e,a,r]},o.n=s=>{var t=s&&s.__esModule?()=>s.default:()=>s;return o.d(t,{a:t}),t},o.d=(s,t)=>{for(var e in t)o.o(t,e)&&!o.o(s,e)&&Object.defineProperty(s,e,{enumerable:!0,get:t[e]})},o.f={},o.e=s=>Promise.all(Object.keys(o.f).reduce(((t,e)=>(o.f[e](s,t),t)),[])),o.u=s=>(8299===s?"user-status-modal":s)+"-"+s+".js?v="+{3240:"29c327d1e4e42959b82f",3998:"a49373c9d79e30e60f7b",8299:"7006122e6c76e3cf7057",9064:"f6243754beec9d78de45"}[s],o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(s){if("object"==typeof window)return window}}(),o.o=(s,t)=>Object.prototype.hasOwnProperty.call(s,t),e={},a="nextcloud:",o.l=(s,t,r,n)=>{if(e[s])e[s].push(t);else{var u,i;if(void 0!==r)for(var c=document.getElementsByTagName("script"),d=0;d<c.length;d++){var l=c[d];if(l.getAttribute("src")==s||l.getAttribute("data-webpack")==a+r){u=l;break}}u||(i=!0,(u=document.createElement("script")).charset="utf-8",u.timeout=120,o.nc&&u.setAttribute("nonce",o.nc),u.setAttribute("data-webpack",a+r),u.src=s),e[s]=[t];var m=(t,a)=>{u.onerror=u.onload=null,clearTimeout(p);var r=e[s];if(delete e[s],u.parentNode&&u.parentNode.removeChild(u),r&&r.forEach((s=>s(a))),t)return t(a)},p=setTimeout(m.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=m.bind(null,u.onerror),u.onload=m.bind(null,u.onload),i&&document.head.appendChild(u)}},o.r=s=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})},o.nmd=s=>(s.paths=[],s.children||(s.children=[]),s),o.j=2613,(()=>{var s;o.g.importScripts&&(s=o.g.location+"");var t=o.g.document;if(!s&&t&&(t.currentScript&&(s=t.currentScript.src),!s)){var e=t.getElementsByTagName("script");if(e.length)for(var a=e.length-1;a>-1&&!s;)s=e[a--].src}if(!s)throw new Error("Automatic publicPath is not supported in this browser");s=s.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),o.p=s})(),(()=>{o.b=document.baseURI||self.location.href;var s={2613:0};o.f.j=(t,e)=>{var a=o.o(s,t)?s[t]:void 0;if(0!==a)if(a)e.push(a[2]);else{var r=new Promise(((e,r)=>a=s[t]=[e,r]));e.push(a[2]=r);var n=o.p+o.u(t),u=new Error;o.l(n,(e=>{if(o.o(s,t)&&(0!==(a=s[t])&&(s[t]=void 0),a)){var r=e&&("load"===e.type?"missing":e.type),n=e&&e.target&&e.target.src;u.message="Loading chunk "+t+" failed.\n("+r+": "+n+")",u.name="ChunkLoadError",u.type=r,u.request=n,a[1](u)}}),"chunk-"+t,t)}},o.O.j=t=>0===s[t];var t=(t,e)=>{var a,r,n=e[0],u=e[1],i=e[2],c=0;if(n.some((t=>0!==s[t]))){for(a in u)o.o(u,a)&&(o.m[a]=u[a]);if(i)var d=i(o)}for(t&&t(e);c<n.length;c++)r=n[c],o.o(s,r)&&s[r]&&s[r][0](),s[r]=0;return o.O(d)},e=self.webpackChunknextcloud=self.webpackChunknextcloud||[];e.forEach(t.bind(null,0)),e.push=t.bind(null,e.push.bind(e))})(),o.nc=void 0;var u=o.O(void 0,[7874],(()=>o(86419)));u=o.O(u)})();
-//# sourceMappingURL=user_status-menu.js.map?v=0f34f844efa006f14fc6
\ No newline at end of file
+(()=>{var s,e,a,r={86419:(s,e,a)=>{"use strict";var r=a(20144),n=a(77958),o=a(69183),u=a(62642),i=a(20296),c=a.n(i),d=a(93664),l=a(79753),m=a(84387),p=a(25108);const g={name:"UserStatus",components:{NcButton:u.Z,SetStatusModal:()=>Promise.all([a.e(7874),a.e(8299)]).then(a.bind(a,21124))},mixins:[m.Z],props:{inline:{type:Boolean,default:!1}},data:()=>({heartbeatInterval:null,isAway:!1,isModalOpen:!1,mouseMoveListener:null,setAwayTimeout:null}),mounted(){this.$store.dispatch("loadStatusFromInitialState"),OC.config.session_keepalive&&(this.heartbeatInterval=setInterval(this._backgroundHeartbeat.bind(this),3e5),this.setAwayTimeout=()=>{this.isAway=!0},this.mouseMoveListener=c()((()=>{const s=this.isAway;this.isAway=!1,clearTimeout(this.setAwayTimeout),setTimeout(this.setAwayTimeout,12e4),s&&this._backgroundHeartbeat()}),2e3,!0),window.addEventListener("mousemove",this.mouseMoveListener,{capture:!0,passive:!0}),this._backgroundHeartbeat()),(0,o.Ld)("user_status:status.updated",this.handleUserStatusUpdated)},beforeDestroy(){window.removeEventListener("mouseMove",this.mouseMoveListener),clearInterval(this.heartbeatInterval),(0,o.r1)("user_status:status.updated",this.handleUserStatusUpdated)},methods:{openModal(){this.isModalOpen=!0},closeModal(){this.isModalOpen=!1},async _backgroundHeartbeat(){try{const s=await(async s=>{const t=(0,l.generateOcsUrl)("apps/user_status/api/v1/heartbeat?format=json");return(await d.Z.put(t,{status:s?"away":"online"})).data.ocs.data})(this.isAway);s?.userId?this.$store.dispatch("setStatusFromHeartbeat",s):await this.$store.dispatch("reFetchStatusFromServer")}catch(s){p.debug("Failed sending heartbeat, got: "+s.response?.status)}},handleUserStatusUpdated(s){OC.getCurrentUser().uid===s.userId&&this.$store.dispatch("setStatusFromObject",{status:s.status,icon:s.icon,message:s.message})}}};var h=a(93379),f=a.n(h),j=a(7795),v=a.n(j),b=a(90569),A=a.n(b),y=a(3565),w=a.n(y),k=a(19216),S=a.n(k),C=a(44589),I=a.n(C),_=a(34882),O={};O.styleTagTransform=I(),O.setAttributes=w(),O.insert=A().bind(null,"head"),O.domAPI=v(),O.insertStyleElement=S(),f()(_.Z,O),_.Z&&_.Z.locals&&_.Z.locals;const M=(0,a(51900).Z)(g,(function(){var s=this,t=s._self._c;return t(s.inline?"div":"li",{tag:"component"},[s.inline?t("NcButton",{attrs:{icon:s.statusIcon},on:{click:function(t){return t.stopPropagation(),s.openModal.apply(null,arguments)}},scopedSlots:s._u([{key:"icon",fn:function(){return[t("span",{staticClass:"user-status-icon",class:s.statusIcon,attrs:{"aria-hidden":"true"}})]},proxy:!0}])},[s._v("\n\t\t"+s._s(s.visibleMessage)+"\n\t")]):t("button",{staticClass:"user-status-menu-item",on:{click:function(t){return t.stopPropagation(),s.openModal.apply(null,arguments)}}},[t("span",{staticClass:"user-status-icon",class:s.statusIcon,attrs:{"aria-hidden":"true"}}),s._v("\n\t\t"+s._s(s.visibleMessage)+"\n\t")]),s._v(" "),s.isModalOpen?t("SetStatusModal",{on:{close:s.closeModal}}):s._e()],1)}),[],!1,null,"798bb70c",null).exports;var x=a(20629);const U={state:{predefinedStatuses:[]},mutations:{addPredefinedStatus(s,t){s.predefinedStatuses=[...s.predefinedStatuses,t]}},getters:{statusesHaveLoaded:s=>s.predefinedStatuses.length>0},actions:{async loadAllPredefinedStatuses(s){let{state:t,commit:e}=s;if(t.predefinedStatuses.length>0)return;const a=await(async()=>{const s=(0,l.generateOcsUrl)("apps/user_status/api/v1/predefined_statuses?format=json");return(await d.Z.get(s)).data.ocs.data})();for(const s of a)e("addPredefinedStatus",s)}}};var z=a(43554),P=a(64039),T=a(80351),F=a.n(T);const D=s=>{if(null===s)return null;const t=(0,P.n)();if("period"===s.type)return t.setSeconds(t.getSeconds()+s.time),Math.floor(t.getTime()/1e3);if("end-of"===s.type)switch(s.time){case"day":case"week":return Number(F()(t).endOf(s.time).format("X"))}return"_time"===s.type?s.time:null},E={state:{status:null,statusIsUserDefined:null,message:null,icon:null,clearAt:null,messageIsPredefined:null,messageId:null},mutations:{setStatus(s,t){let{statusType:e}=t;s.status=e,s.statusIsUserDefined=!0},setPredefinedMessage(s,t){let{messageId:e,clearAt:a,message:r,icon:n}=t;s.messageId=e,s.messageIsPredefined=!0,s.message=r,s.icon=n,s.clearAt=a},setCustomMessage(s,t){let{message:e,icon:a,clearAt:r}=t;s.messageId=null,s.messageIsPredefined=!1,s.message=e,s.icon=a,s.clearAt=r},clearMessage(s){s.messageId=null,s.messageIsPredefined=!1,s.message=null,s.icon=null,s.clearAt=null},loadStatusFromServer(s,t){let{status:e,statusIsUserDefined:a,message:r,icon:n,clearAt:o,messageIsPredefined:u,messageId:i}=t;s.status=e,s.message=r,s.icon=n,void 0!==a&&(s.statusIsUserDefined=a),void 0!==o&&(s.clearAt=o),void 0!==u&&(s.messageIsPredefined=u),void 0!==i&&(s.messageId=i)}},getters:{},actions:{async setStatus(s,t){let{commit:e,state:a}=s,{statusType:r}=t;await(async s=>{const t=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status/status");await d.Z.put(t,{statusType:s})})(r),e("setStatus",{statusType:r}),(0,o.j8)("user_status:status.updated",{status:a.status,message:a.message,icon:a.icon,clearAt:a.clearAt,userId:(0,n.ts)()?.uid})},async setStatusFromObject(s,t){let{commit:e,state:a}=s;e("loadStatusFromServer",t)},async setPredefinedMessage(s,t){let{commit:e,rootState:a,state:r}=s,{messageId:u,clearAt:i}=t;const c=D(i);await async function(s){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const e=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status/message/predefined?format=json");await d.Z.put(e,{messageId:s,clearAt:t})}(u,c);const m=a.predefinedStatuses.predefinedStatuses.find((s=>s.id===u)),{message:p,icon:g}=m;e("setPredefinedMessage",{messageId:u,clearAt:c,message:p,icon:g}),(0,o.j8)("user_status:status.updated",{status:r.status,message:r.message,icon:r.icon,clearAt:r.clearAt,userId:(0,n.ts)()?.uid})},async setCustomMessage(s,t){let{commit:e,state:a}=s,{message:r,icon:u,clearAt:i}=t;const c=D(i);await async function(s){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const a=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status/message/custom?format=json");await d.Z.put(a,{message:s,statusIcon:t,clearAt:e})}(r,u,c),e("setCustomMessage",{message:r,icon:u,clearAt:c}),(0,o.j8)("user_status:status.updated",{status:a.status,message:a.message,icon:a.icon,clearAt:a.clearAt,userId:(0,n.ts)()?.uid})},async clearMessage(s){let{commit:t,state:e}=s;await(async()=>{const s=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status/message?format=json");await d.Z.delete(s)})(),t("clearMessage"),(0,o.j8)("user_status:status.updated",{status:e.status,message:e.message,icon:e.icon,clearAt:e.clearAt,userId:(0,n.ts)()?.uid})},async reFetchStatusFromServer(s){let{commit:t}=s;t("loadStatusFromServer",await(async()=>{const s=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status");return(await d.Z.get(s)).data.ocs.data})())},async setStatusFromHeartbeat(s,t){let{commit:e}=s;e("loadStatusFromServer",t)},loadStatusFromInitialState(s){let{commit:t}=s;t("loadStatusFromServer",(0,z.j)("user_status","status"))}}},Z={state:{status:null,statusIsUserDefined:null,message:null,icon:null,clearAt:null,messageIsPredefined:null,messageId:null},mutations:{loadBackupStatusFromServer(s,t){let{status:e,statusIsUserDefined:a,message:r,icon:n,clearAt:o,messageIsPredefined:u,messageId:i}=t;s.status=e,s.message=r,s.icon=n,void 0!==a&&(s.statusIsUserDefined=a),void 0!==o&&(s.clearAt=o),void 0!==u&&(s.messageIsPredefined=u),void 0!==i&&(s.messageId=i)}},getters:{},actions:{async fetchBackupFromServer(s){let{commit:t}=s;try{t("loadBackupStatusFromServer",await(async s=>{const t=(0,l.generateOcsUrl)("apps/user_status/api/v1/statuses/{userId}",{userId:"_"+s});return(await d.Z.get(t)).data.ocs.data})((0,n.ts)()?.uid))}catch(s){}},async revertBackupFromServer(s,t){let{commit:e}=s,{messageId:a}=t;const r=await(async s=>{const t=(0,l.generateOcsUrl)("apps/user_status/api/v1/user_status/revert/{messageId}",{messageId:s});return(await d.Z.delete(t)).data.ocs.data})(a);r&&(e("loadBackupStatusFromServer",{}),e("loadStatusFromServer",r),(0,o.j8)("user_status:status.updated",{status:r.status,message:r.message,icon:r.icon,clearAt:r.clearAt,userId:(0,n.ts)()?.uid}))}}};r.default.use(x.ZP);const $=new x.yh({modules:{predefinedStatuses:U,userStatus:E,userBackupStatus:Z},strict:!0});a.nc=btoa((0,n.IH)()),r.default.prototype.t=t,r.default.prototype.$t=t;const B=()=>{const s=document.getElementById("user_status-menu-entry");new r.default({el:s,render:s=>s(M),store:$})};document.getElementById("user_status-menu-entry")?B():(0,o.Ld)("core:user-menu:mounted",B),document.addEventListener("DOMContentLoaded",(function(){OCA.Dashboard&&OCA.Dashboard.registerStatus("status",(s=>new(r.default.extend(M))({propsData:{inline:!0},store:$}).$mount(s)))}))},84387:(s,t,e)=>{"use strict";e.d(t,{Z:()=>o});var a=e(20629),r=e(64024),n=e(25108);const o={computed:{...(0,a.rn)({statusType:s=>s.userStatus.status,statusIsUserDefined:s=>s.userStatus.statusIsUserDefined,customIcon:s=>s.userStatus.icon,customMessage:s=>s.userStatus.message}),visibleMessage(){if(this.customIcon&&this.customMessage)return`${this.customIcon} ${this.customMessage}`;if(this.customMessage)return this.customMessage;if(this.statusIsUserDefined)switch(this.statusType){case"online":return this.$t("user_status","Online");case"away":case"busy":return this.$t("user_status","Away");case"dnd":return this.$t("user_status","Do not disturb");case"invisible":return this.$t("user_status","Invisible");case"offline":return this.$t("user_status","Offline")}return this.$t("user_status","Set status")},statusIcon(){switch(this.statusType){case"online":return"icon-user-status-online";case"away":case"busy":return"icon-user-status-away";case"dnd":return"icon-user-status-dnd";case"invisible":case"offline":return"icon-user-status-invisible"}return""}},methods:{async changeStatus(s){try{await this.$store.dispatch("setStatus",{statusType:s})}catch(s){(0,r.x2)(this.$t("user_status","There was an error saving the new status")),n.debug(s)}}}}},64039:(s,t,e)=>{"use strict";e.d(t,{n:()=>a});const a=()=>new Date},34882:(s,t,e)=>{"use strict";e.d(t,{Z:()=>u});var a=e(87537),r=e.n(a),n=e(23645),o=e.n(n)()(r());o.push([s.id,".user-status-menu-item[data-v-798bb70c]{width:auto;min-width:44px;height:44px;margin:0;border:0;border-radius:var(--border-radius-pill);background-color:var(--color-main-background-blur);font-size:inherit;font-weight:normal;-webkit-backdrop-filter:var(--background-blur);backdrop-filter:var(--background-blur)}.user-status-menu-item[data-v-798bb70c]:active,.user-status-menu-item[data-v-798bb70c]:hover,.user-status-menu-item[data-v-798bb70c]:focus-visible{background-color:var(--color-background-hover)}.user-status-menu-item[data-v-798bb70c]:focus-visible{box-shadow:0 0 0 4px var(--color-main-background) !important;outline:2px solid var(--color-main-text) !important}.user-status-icon[data-v-798bb70c]{width:16px;height:16px;margin-right:10px;opacity:1 !important;background-size:16px;vertical-align:middle !important}","",{version:3,sources:["webpack://./apps/user_status/src/UserStatus.vue"],names:[],mappings:"AACA,wCACC,UAAA,CACA,cAAA,CACA,WAAA,CACA,QAAA,CACA,QAAA,CACA,uCAAA,CACA,kDAAA,CACA,iBAAA,CACA,kBAAA,CAEA,8CAAA,CACA,sCAAA,CAEA,mJAGC,8CAAA,CAED,sDACC,4DAAA,CACA,mDAAA,CAIF,mCACC,UAAA,CACA,WAAA,CACA,iBAAA,CACA,oBAAA,CACA,oBAAA,CACA,gCAAA",sourcesContent:["\n.user-status-menu-item {\n\twidth: auto;\n\tmin-width: 44px;\n\theight: 44px;\n\tmargin: 0;\n\tborder: 0;\n\tborder-radius: var(--border-radius-pill);\n\tbackground-color: var(--color-main-background-blur);\n\tfont-size: inherit;\n\tfont-weight: normal;\n\n\t-webkit-backdrop-filter: var(--background-blur);\n\tbackdrop-filter: var(--background-blur);\n\n\t&:active,\n\t&:hover,\n\t&:focus-visible {\n\t\tbackground-color: var(--color-background-hover);\n\t}\n\t&:focus-visible {\n\t\tbox-shadow: 0 0 0 4px var(--color-main-background) !important;\n\t\toutline: 2px solid var(--color-main-text) !important;\n\t}\n}\n\n.user-status-icon {\n\twidth: 16px;\n\theight: 16px;\n\tmargin-right: 10px;\n\topacity: 1 !important;\n\tbackground-size: 16px;\n\tvertical-align: middle !important;\n}\n"],sourceRoot:""}]);const u=o},46700:(s,t,e)=>{var a={"./af":42786,"./af.js":42786,"./ar":30867,"./ar-dz":14130,"./ar-dz.js":14130,"./ar-kw":96135,"./ar-kw.js":96135,"./ar-ly":56440,"./ar-ly.js":56440,"./ar-ma":47702,"./ar-ma.js":47702,"./ar-sa":16040,"./ar-sa.js":16040,"./ar-tn":37100,"./ar-tn.js":37100,"./ar.js":30867,"./az":31083,"./az.js":31083,"./be":9808,"./be.js":9808,"./bg":68338,"./bg.js":68338,"./bm":67438,"./bm.js":67438,"./bn":8905,"./bn-bd":76225,"./bn-bd.js":76225,"./bn.js":8905,"./bo":11560,"./bo.js":11560,"./br":1278,"./br.js":1278,"./bs":80622,"./bs.js":80622,"./ca":2468,"./ca.js":2468,"./cs":5822,"./cs.js":5822,"./cv":50877,"./cv.js":50877,"./cy":47373,"./cy.js":47373,"./da":24780,"./da.js":24780,"./de":59740,"./de-at":60217,"./de-at.js":60217,"./de-ch":60894,"./de-ch.js":60894,"./de.js":59740,"./dv":5300,"./dv.js":5300,"./el":50837,"./el.js":50837,"./en-au":78348,"./en-au.js":78348,"./en-ca":77925,"./en-ca.js":77925,"./en-gb":22243,"./en-gb.js":22243,"./en-ie":46436,"./en-ie.js":46436,"./en-il":47207,"./en-il.js":47207,"./en-in":44175,"./en-in.js":44175,"./en-nz":76319,"./en-nz.js":76319,"./en-sg":31662,"./en-sg.js":31662,"./eo":92915,"./eo.js":92915,"./es":55655,"./es-do":55251,"./es-do.js":55251,"./es-mx":96112,"./es-mx.js":96112,"./es-us":71146,"./es-us.js":71146,"./es.js":55655,"./et":5603,"./et.js":5603,"./eu":77763,"./eu.js":77763,"./fa":76959,"./fa.js":76959,"./fi":11897,"./fi.js":11897,"./fil":42549,"./fil.js":42549,"./fo":94694,"./fo.js":94694,"./fr":94470,"./fr-ca":63049,"./fr-ca.js":63049,"./fr-ch":52330,"./fr-ch.js":52330,"./fr.js":94470,"./fy":5044,"./fy.js":5044,"./ga":29295,"./ga.js":29295,"./gd":2101,"./gd.js":2101,"./gl":38794,"./gl.js":38794,"./gom-deva":27884,"./gom-deva.js":27884,"./gom-latn":23168,"./gom-latn.js":23168,"./gu":95349,"./gu.js":95349,"./he":24206,"./he.js":24206,"./hi":30094,"./hi.js":30094,"./hr":30316,"./hr.js":30316,"./hu":22138,"./hu.js":22138,"./hy-am":11423,"./hy-am.js":11423,"./id":29218,"./id.js":29218,"./is":90135,"./is.js":90135,"./it":90626,"./it-ch":10150,"./it-ch.js":10150,"./it.js":90626,"./ja":39183,"./ja.js":39183,"./jv":24286,"./jv.js":24286,"./ka":12105,"./ka.js":12105,"./kk":47772,"./kk.js":47772,"./km":18758,"./km.js":18758,"./kn":79282,"./kn.js":79282,"./ko":33730,"./ko.js":33730,"./ku":1408,"./ku.js":1408,"./ky":33291,"./ky.js":33291,"./lb":36841,"./lb.js":36841,"./lo":55466,"./lo.js":55466,"./lt":57010,"./lt.js":57010,"./lv":37595,"./lv.js":37595,"./me":39861,"./me.js":39861,"./mi":35493,"./mi.js":35493,"./mk":95966,"./mk.js":95966,"./ml":87341,"./ml.js":87341,"./mn":5115,"./mn.js":5115,"./mr":10370,"./mr.js":10370,"./ms":9847,"./ms-my":41237,"./ms-my.js":41237,"./ms.js":9847,"./mt":72126,"./mt.js":72126,"./my":56165,"./my.js":56165,"./nb":64924,"./nb.js":64924,"./ne":16744,"./ne.js":16744,"./nl":93901,"./nl-be":59814,"./nl-be.js":59814,"./nl.js":93901,"./nn":83877,"./nn.js":83877,"./oc-lnc":92135,"./oc-lnc.js":92135,"./pa-in":15858,"./pa-in.js":15858,"./pl":64495,"./pl.js":64495,"./pt":89520,"./pt-br":57971,"./pt-br.js":57971,"./pt.js":89520,"./ro":96459,"./ro.js":96459,"./ru":21793,"./ru.js":21793,"./sd":40950,"./sd.js":40950,"./se":10490,"./se.js":10490,"./si":90124,"./si.js":90124,"./sk":64249,"./sk.js":64249,"./sl":14985,"./sl.js":14985,"./sq":51104,"./sq.js":51104,"./sr":49131,"./sr-cyrl":79915,"./sr-cyrl.js":79915,"./sr.js":49131,"./ss":85893,"./ss.js":85893,"./sv":98760,"./sv.js":98760,"./sw":91172,"./sw.js":91172,"./ta":27333,"./ta.js":27333,"./te":23110,"./te.js":23110,"./tet":52095,"./tet.js":52095,"./tg":27321,"./tg.js":27321,"./th":9041,"./th.js":9041,"./tk":19005,"./tk.js":19005,"./tl-ph":75768,"./tl-ph.js":75768,"./tlh":89444,"./tlh.js":89444,"./tr":72397,"./tr.js":72397,"./tzl":28254,"./tzl.js":28254,"./tzm":51106,"./tzm-latn":30699,"./tzm-latn.js":30699,"./tzm.js":51106,"./ug-cn":9288,"./ug-cn.js":9288,"./uk":67691,"./uk.js":67691,"./ur":13795,"./ur.js":13795,"./uz":6791,"./uz-latn":60588,"./uz-latn.js":60588,"./uz.js":6791,"./vi":65666,"./vi.js":65666,"./x-pseudo":14378,"./x-pseudo.js":14378,"./yo":75805,"./yo.js":75805,"./zh-cn":83839,"./zh-cn.js":83839,"./zh-hk":55726,"./zh-hk.js":55726,"./zh-mo":99807,"./zh-mo.js":99807,"./zh-tw":74152,"./zh-tw.js":74152};function r(s){var t=n(s);return e(t)}function n(s){if(!e.o(a,s)){var t=new Error("Cannot find module '"+s+"'");throw t.code="MODULE_NOT_FOUND",t}return a[s]}r.keys=function(){return Object.keys(a)},r.resolve=n,s.exports=r,r.id=46700}},n={};function o(s){var t=n[s];if(void 0!==t)return t.exports;var e=n[s]={id:s,loaded:!1,exports:{}};return r[s].call(e.exports,e,e.exports,o),e.loaded=!0,e.exports}o.m=r,s=[],o.O=(t,e,a,r)=>{if(!e){var n=1/0;for(d=0;d<s.length;d++){e=s[d][0],a=s[d][1],r=s[d][2];for(var u=!0,i=0;i<e.length;i++)(!1&r||n>=r)&&Object.keys(o.O).every((s=>o.O[s](e[i])))?e.splice(i--,1):(u=!1,r<n&&(n=r));if(u){s.splice(d--,1);var c=a();void 0!==c&&(t=c)}}return t}r=r||0;for(var d=s.length;d>0&&s[d-1][2]>r;d--)s[d]=s[d-1];s[d]=[e,a,r]},o.n=s=>{var t=s&&s.__esModule?()=>s.default:()=>s;return o.d(t,{a:t}),t},o.d=(s,t)=>{for(var e in t)o.o(t,e)&&!o.o(s,e)&&Object.defineProperty(s,e,{enumerable:!0,get:t[e]})},o.f={},o.e=s=>Promise.all(Object.keys(o.f).reduce(((t,e)=>(o.f[e](s,t),t)),[])),o.u=s=>(8299===s?"user-status-modal":s)+"-"+s+".js?v="+{3240:"29c327d1e4e42959b82f",3998:"a49373c9d79e30e60f7b",8299:"7006122e6c76e3cf7057",9064:"f6243754beec9d78de45"}[s],o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(s){if("object"==typeof window)return window}}(),o.o=(s,t)=>Object.prototype.hasOwnProperty.call(s,t),e={},a="nextcloud:",o.l=(s,t,r,n)=>{if(e[s])e[s].push(t);else{var u,i;if(void 0!==r)for(var c=document.getElementsByTagName("script"),d=0;d<c.length;d++){var l=c[d];if(l.getAttribute("src")==s||l.getAttribute("data-webpack")==a+r){u=l;break}}u||(i=!0,(u=document.createElement("script")).charset="utf-8",u.timeout=120,o.nc&&u.setAttribute("nonce",o.nc),u.setAttribute("data-webpack",a+r),u.src=s),e[s]=[t];var m=(t,a)=>{u.onerror=u.onload=null,clearTimeout(p);var r=e[s];if(delete e[s],u.parentNode&&u.parentNode.removeChild(u),r&&r.forEach((s=>s(a))),t)return t(a)},p=setTimeout(m.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=m.bind(null,u.onerror),u.onload=m.bind(null,u.onload),i&&document.head.appendChild(u)}},o.r=s=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})},o.nmd=s=>(s.paths=[],s.children||(s.children=[]),s),o.j=2613,(()=>{var s;o.g.importScripts&&(s=o.g.location+"");var t=o.g.document;if(!s&&t&&(t.currentScript&&(s=t.currentScript.src),!s)){var e=t.getElementsByTagName("script");if(e.length)for(var a=e.length-1;a>-1&&!s;)s=e[a--].src}if(!s)throw new Error("Automatic publicPath is not supported in this browser");s=s.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),o.p=s})(),(()=>{o.b=document.baseURI||self.location.href;var s={2613:0};o.f.j=(t,e)=>{var a=o.o(s,t)?s[t]:void 0;if(0!==a)if(a)e.push(a[2]);else{var r=new Promise(((e,r)=>a=s[t]=[e,r]));e.push(a[2]=r);var n=o.p+o.u(t),u=new Error;o.l(n,(e=>{if(o.o(s,t)&&(0!==(a=s[t])&&(s[t]=void 0),a)){var r=e&&("load"===e.type?"missing":e.type),n=e&&e.target&&e.target.src;u.message="Loading chunk "+t+" failed.\n("+r+": "+n+")",u.name="ChunkLoadError",u.type=r,u.request=n,a[1](u)}}),"chunk-"+t,t)}},o.O.j=t=>0===s[t];var t=(t,e)=>{var a,r,n=e[0],u=e[1],i=e[2],c=0;if(n.some((t=>0!==s[t]))){for(a in u)o.o(u,a)&&(o.m[a]=u[a]);if(i)var d=i(o)}for(t&&t(e);c<n.length;c++)r=n[c],o.o(s,r)&&s[r]&&s[r][0](),s[r]=0;return o.O(d)},e=self.webpackChunknextcloud=self.webpackChunknextcloud||[];e.forEach(t.bind(null,0)),e.push=t.bind(null,e.push.bind(e))})(),o.nc=void 0;var u=o.O(void 0,[7874],(()=>o(86419)));u=o.O(u)})();
+//# sourceMappingURL=user_status-menu.js.map?v=dc4dcdbcfe4a541313fd
\ No newline at end of file
index 9a8933d93bab736f6e792f43f6dad36aff89fd39..41d30e2855fa54f99ad627dbdb420861165740da 100644 (file)
@@ -1 +1 @@
-{"version":3,"file":"user_status-menu.js?v=0f34f844efa006f14fc6","mappings":"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","sources":["webpack:///nextcloud/webpack/runtime/chunk loaded","webpack:///nextcloud/webpack/runtime/load script","webpack:///nextcloud/apps/user_status/src/UserStatus.vue","webpack:///nextcloud/apps/user_status/src/UserStatus.vue?vue&type=script&lang=js","webpack:///nextcloud/apps/user_status/src/services/heartbeatService.js","webpack://nextcloud/./apps/user_status/src/UserStatus.vue?be7c","webpack://nextcloud/./apps/user_status/src/UserStatus.vue?d74a","webpack://nextcloud/./apps/user_status/src/UserStatus.vue?e2b3","webpack:///nextcloud/apps/user_status/src/services/predefinedStatusService.js","webpack:///nextcloud/apps/user_status/src/store/predefinedStatuses.js","webpack:///nextcloud/apps/user_status/src/services/clearAtService.js","webpack:///nextcloud/apps/user_status/src/store/userStatus.js","webpack:///nextcloud/apps/user_status/src/services/statusService.js","webpack:///nextcloud/apps/user_status/src/store/userBackupStatus.js","webpack:///nextcloud/apps/user_status/src/store/index.js","webpack:///nextcloud/apps/user_status/src/menu.js","webpack:///nextcloud/apps/user_status/src/mixins/OnlineStatusMixin.js","webpack:///nextcloud/apps/user_status/src/services/dateService.js","webpack:///nextcloud/apps/user_status/src/UserStatus.vue?vue&type=style&index=0&id=798bb70c&prod&lang=scss&scoped=true","webpack:///nextcloud/node_modules/moment/locale|sync|/^\\.\\/.*$","webpack:///nextcloud/webpack/bootstrap","webpack:///nextcloud/webpack/runtime/compat get default export","webpack:///nextcloud/webpack/runtime/define property getters","webpack:///nextcloud/webpack/runtime/ensure chunk","webpack:///nextcloud/webpack/runtime/get javascript chunk filename","webpack:///nextcloud/webpack/runtime/global","webpack:///nextcloud/webpack/runtime/hasOwnProperty shorthand","webpack:///nextcloud/webpack/runtime/make namespace object","webpack:///nextcloud/webpack/runtime/node module decorator","webpack:///nextcloud/webpack/runtime/runtimeId","webpack:///nextcloud/webpack/runtime/publicPath","webpack:///nextcloud/webpack/runtime/jsonp chunk loading","webpack:///nextcloud/webpack/runtime/nonce","webpack:///nextcloud/webpack/startup"],"sourcesContent":["var deferred = [];\n__webpack_require__.O = (result, chunkIds, fn, priority) => {\n\tif(chunkIds) {\n\t\tpriority = priority || 0;\n\t\tfor(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1];\n\t\tdeferred[i] = [chunkIds, fn, priority];\n\t\treturn;\n\t}\n\tvar notFulfilled = Infinity;\n\tfor (var i = 0; i < deferred.length; i++) {\n\t\tvar chunkIds = deferred[i][0];\n\t\tvar fn = deferred[i][1];\n\t\tvar priority = deferred[i][2];\n\t\tvar fulfilled = true;\n\t\tfor (var j = 0; j < chunkIds.length; j++) {\n\t\t\tif ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) {\n\t\t\t\tchunkIds.splice(j--, 1);\n\t\t\t} else {\n\t\t\t\tfulfilled = false;\n\t\t\t\tif(priority < notFulfilled) notFulfilled = priority;\n\t\t\t}\n\t\t}\n\t\tif(fulfilled) {\n\t\t\tdeferred.splice(i--, 1)\n\t\t\tvar r = fn();\n\t\t\tif (r !== undefined) result = r;\n\t\t}\n\t}\n\treturn result;\n};","var inProgress = {};\nvar dataWebpackPrefix = \"nextcloud:\";\n// loadScript function to load a script via script tag\n__webpack_require__.l = (url, done, key, chunkId) => {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url || s.getAttribute(\"data-webpack\") == dataWebpackPrefix + key) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\n\t\tscript.charset = 'utf-8';\n\t\tscript.timeout = 120;\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\t\tscript.setAttribute(\"data-webpack\", dataWebpackPrefix + key);\n\n\t\tscript.src = url;\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = (prev, event) => {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach((fn) => (fn(event)));\n\t\tif(prev) return prev(event);\n\t}\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","<!--\n  - @copyright Copyright (c) 2020 Georg Ehrke <oc.list@georgehrke.com>\n  - @author Georg Ehrke <oc.list@georgehrke.com>\n  -\n  - @license GNU AGPL version 3 or any later version\n  -\n  - This program is free software: you can redistribute it and/or modify\n  - it under the terms of the GNU Affero General Public License as\n  - published by the Free Software Foundation, either version 3 of the\n  - License, or (at your option) any later version.\n  -\n  - This program is distributed in the hope that it will be useful,\n  - but WITHOUT ANY WARRANTY; without even the implied warranty of\n  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n  - GNU Affero General Public License for more details.\n  -\n  - You should have received a copy of the GNU Affero General Public License\n  - along with this program. If not, see <http://www.gnu.org/licenses/>.\n  -\n  -->\n\n<template>\n\t<component :is=\"inline ? 'div' : 'li'\">\n\t\t<!-- User Status = Status modal toggle -->\n\t\t<button v-if=\"!inline\"\n\t\t\tclass=\"user-status-menu-item\"\n\t\t\t@click.stop=\"openModal\">\n\t\t\t<span aria-hidden=\"true\" :class=\"statusIcon\" class=\"user-status-icon\" />\n\t\t\t{{ visibleMessage }}\n\t\t</button>\n\n\t\t<!-- Dashboard Status -->\n\t\t<NcButton v-else\n\t\t\t:icon=\"statusIcon\"\n\t\t\t@click.stop=\"openModal\">\n\t\t\t<template #icon>\n\t\t\t\t<span aria-hidden=\"true\" :class=\"statusIcon\" class=\"user-status-icon\" />\n\t\t\t</template>\n\t\t\t{{ visibleMessage }}\n\t\t</NcButton>\n\n\t\t<!-- Status management modal -->\n\t\t<SetStatusModal v-if=\"isModalOpen\" @close=\"closeModal\" />\n\t</component>\n</template>\n\n<script>\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport NcButton from '@nextcloud/vue/dist/Components/NcButton.js'\nimport debounce from 'debounce'\n\nimport { sendHeartbeat } from './services/heartbeatService.js'\nimport OnlineStatusMixin from './mixins/OnlineStatusMixin.js'\n\nexport default {\n\tname: 'UserStatus',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tSetStatusModal: () => import(/* webpackChunkName: 'user-status-modal' */'./components/SetStatusModal.vue'),\n\t},\n\tmixins: [OnlineStatusMixin],\n\n\tprops: {\n\t\t/**\n\t\t * Whether the component should be rendered as a Dashboard Status or a User Menu Entries\n\t\t * true = Dashboard Status\n\t\t * false = User Menu Entries\n\t\t */\n\t\tinline: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\theartbeatInterval: null,\n\t\t\tisAway: false,\n\t\t\tisModalOpen: false,\n\t\t\tmouseMoveListener: null,\n\t\t\tsetAwayTimeout: null,\n\t\t}\n\t},\n\n\t/**\n\t * Loads the current user's status from initial state\n\t * and stores it in Vuex\n\t */\n\tmounted() {\n\t\tthis.$store.dispatch('loadStatusFromInitialState')\n\n\t\tif (OC.config.session_keepalive) {\n\t\t\t// Send the latest status to the server every 5 minutes\n\t\t\tthis.heartbeatInterval = setInterval(this._backgroundHeartbeat.bind(this), 1000 * 60 * 5)\n\t\t\tthis.setAwayTimeout = () => {\n\t\t\t\tthis.isAway = true\n\t\t\t}\n\t\t\t// Catch mouse movements, but debounce to once every 30 seconds\n\t\t\tthis.mouseMoveListener = debounce(() => {\n\t\t\t\tconst wasAway = this.isAway\n\t\t\t\tthis.isAway = false\n\t\t\t\t// Reset the two minute counter\n\t\t\t\tclearTimeout(this.setAwayTimeout)\n\t\t\t\t// If the user did not move the mouse within two minutes,\n\t\t\t\t// mark them as away\n\t\t\t\tsetTimeout(this.setAwayTimeout, 1000 * 60 * 2)\n\n\t\t\t\tif (wasAway) {\n\t\t\t\t\tthis._backgroundHeartbeat()\n\t\t\t\t}\n\t\t\t}, 1000 * 2, true)\n\t\t\twindow.addEventListener('mousemove', this.mouseMoveListener, {\n\t\t\t\tcapture: true,\n\t\t\t\tpassive: true,\n\t\t\t})\n\n\t\t\tthis._backgroundHeartbeat()\n\t\t}\n\t\tsubscribe('user_status:status.updated', this.handleUserStatusUpdated)\n\t},\n\n\t/**\n\t * Some housekeeping before destroying the component\n\t */\n\tbeforeDestroy() {\n\t\twindow.removeEventListener('mouseMove', this.mouseMoveListener)\n\t\tclearInterval(this.heartbeatInterval)\n\t\tunsubscribe('user_status:status.updated', this.handleUserStatusUpdated)\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Opens the modal to set a custom status\n\t\t */\n\t\topenModal() {\n\t\t\tthis.isModalOpen = true\n\t\t},\n\t\t/**\n\t\t * Closes the modal\n\t\t */\n\t\tcloseModal() {\n\t\t\tthis.isModalOpen = false\n\t\t},\n\n\t\t/**\n\t\t * Sends the status heartbeat to the server\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @private\n\t\t */\n\t\tasync _backgroundHeartbeat() {\n\t\t\ttry {\n\t\t\t\tconst status = await sendHeartbeat(this.isAway)\n\t\t\t\tif (status?.userId) {\n\t\t\t\t\tthis.$store.dispatch('setStatusFromHeartbeat', status)\n\t\t\t\t} else {\n\t\t\t\t\tawait this.$store.dispatch('reFetchStatusFromServer')\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.debug('Failed sending heartbeat, got: ' + error.response?.status)\n\t\t\t}\n\t\t},\n\t\thandleUserStatusUpdated(state) {\n\t\t\tif (OC.getCurrentUser().uid === state.userId) {\n\t\t\t\tthis.$store.dispatch('setStatusFromObject', {\n\t\t\t\t\tstatus: state.status,\n\t\t\t\t\ticon: state.icon,\n\t\t\t\t\tmessage: state.message,\n\t\t\t\t})\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.user-status-menu-item {\n\twidth: auto;\n\tmin-width: 44px;\n\theight: 44px;\n\tmargin: 0;\n\tborder: 0;\n\tborder-radius: var(--border-radius-pill);\n\tbackground-color: var(--color-main-background-blur);\n\tfont-size: inherit;\n\tfont-weight: normal;\n\n\t-webkit-backdrop-filter: var(--background-blur);\n\tbackdrop-filter: var(--background-blur);\n\n\t&:active,\n\t&:hover,\n\t&:focus-visible {\n\t\tbackground-color: var(--color-background-hover);\n\t}\n\t&:focus-visible {\n\t\tbox-shadow: 0 0 0 4px var(--color-main-background) !important;\n\t\toutline: 2px solid var(--color-main-text) !important;\n\t}\n}\n\n.user-status-icon {\n\twidth: 16px;\n\theight: 16px;\n\tmargin-right: 10px;\n\topacity: 1 !important;\n\tbackground-size: 16px;\n\tvertical-align: middle !important;\n}\n</style>\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./UserStatus.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./UserStatus.vue?vue&type=script&lang=js\"","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport HttpClient from '@nextcloud/axios'\nimport { generateOcsUrl } from '@nextcloud/router'\n\n/**\n * Sends a heartbeat\n *\n * @param {boolean} isAway Whether or not the user is active\n * @return {Promise<void>}\n */\nconst sendHeartbeat = async (isAway) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/heartbeat?format=json')\n\tconst response = await HttpClient.put(url, {\n\t\tstatus: isAway ? 'away' : 'online',\n\t})\n\treturn response.data.ocs.data\n}\n\nexport {\n\tsendHeartbeat,\n}\n","\n      import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n      import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n      import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n      import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n      import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n      import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n      import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/sass-loader/dist/cjs.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./UserStatus.vue?vue&type=style&index=0&id=798bb70c&prod&lang=scss&scoped=true\";\n      \n      \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\n\n      options.insert = insertFn.bind(null, \"head\");\n    \noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/sass-loader/dist/cjs.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./UserStatus.vue?vue&type=style&index=0&id=798bb70c&prod&lang=scss&scoped=true\";\n       export default content && content.locals ? content.locals : undefined;\n","import { render, staticRenderFns } from \"./UserStatus.vue?vue&type=template&id=798bb70c&scoped=true\"\nimport script from \"./UserStatus.vue?vue&type=script&lang=js\"\nexport * from \"./UserStatus.vue?vue&type=script&lang=js\"\nimport style0 from \"./UserStatus.vue?vue&type=style&index=0&id=798bb70c&prod&lang=scss&scoped=true\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n  script,\n  render,\n  staticRenderFns,\n  false,\n  null,\n  \"798bb70c\",\n  null\n  \n)\n\nexport default component.exports","var render = function render(){var _vm=this,_c=_vm._self._c;return _c(_vm.inline ? 'div' : 'li',{tag:\"component\"},[(!_vm.inline)?_c('button',{staticClass:\"user-status-menu-item\",on:{\"click\":function($event){$event.stopPropagation();return _vm.openModal.apply(null, arguments)}}},[_c('span',{staticClass:\"user-status-icon\",class:_vm.statusIcon,attrs:{\"aria-hidden\":\"true\"}}),_vm._v(\"\\n\\t\\t\"+_vm._s(_vm.visibleMessage)+\"\\n\\t\")]):_c('NcButton',{attrs:{\"icon\":_vm.statusIcon},on:{\"click\":function($event){$event.stopPropagation();return _vm.openModal.apply(null, arguments)}},scopedSlots:_vm._u([{key:\"icon\",fn:function(){return [_c('span',{staticClass:\"user-status-icon\",class:_vm.statusIcon,attrs:{\"aria-hidden\":\"true\"}})]},proxy:true}])},[_vm._v(\"\\n\\t\\t\"+_vm._s(_vm.visibleMessage)+\"\\n\\t\")]),_vm._v(\" \"),(_vm.isModalOpen)?_c('SetStatusModal',{on:{\"close\":_vm.closeModal}}):_vm._e()],1)\n}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport HttpClient from '@nextcloud/axios'\nimport { generateOcsUrl } from '@nextcloud/router'\n\n/**\n * Fetches all predefined statuses from the server\n *\n * @return {Promise<void>}\n */\nconst fetchAllPredefinedStatuses = async () => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/predefined_statuses?format=json')\n\tconst response = await HttpClient.get(url)\n\n\treturn response.data.ocs.data\n}\n\nexport {\n\tfetchAllPredefinedStatuses,\n}\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport { fetchAllPredefinedStatuses } from '../services/predefinedStatusService.js'\n\nconst state = {\n\tpredefinedStatuses: [],\n}\n\nconst mutations = {\n\n\t/**\n\t * Adds a predefined status to the state\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} status The status to add\n\t */\n\taddPredefinedStatus(state, status) {\n\t\tstate.predefinedStatuses = [...state.predefinedStatuses, status]\n\t},\n}\n\nconst getters = {\n\tstatusesHaveLoaded(state) {\n\t\treturn state.predefinedStatuses.length > 0\n\t},\n}\n\nconst actions = {\n\n\t/**\n\t * Loads all predefined statuses from the server\n\t *\n\t * @param {object} vuex The Vuex components\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state -\n\t */\n\tasync loadAllPredefinedStatuses({ state, commit }) {\n\t\tif (state.predefinedStatuses.length > 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst statuses = await fetchAllPredefinedStatuses()\n\t\tfor (const status of statuses) {\n\t\t\tcommit('addPredefinedStatus', status)\n\t\t}\n\t},\n\n}\n\nexport default { state, mutations, getters, actions }\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport {\n\tdateFactory,\n} from './dateService.js'\nimport moment from '@nextcloud/moment'\n\n/**\n * Calculates the actual clearAt timestamp\n *\n * @param {object | null} clearAt The clear-at config\n * @return {number | null}\n */\nconst getTimestampForClearAt = (clearAt) => {\n\tif (clearAt === null) {\n\t\treturn null\n\t}\n\n\tconst date = dateFactory()\n\n\tif (clearAt.type === 'period') {\n\t\tdate.setSeconds(date.getSeconds() + clearAt.time)\n\t\treturn Math.floor(date.getTime() / 1000)\n\t}\n\tif (clearAt.type === 'end-of') {\n\t\tswitch (clearAt.time) {\n\t\tcase 'day':\n\t\tcase 'week':\n\t\t\treturn Number(moment(date).endOf(clearAt.time).format('X'))\n\t\t}\n\t}\n\t// This is not an officially supported type\n\t// but only used internally to show the remaining time\n\t// in the Set Status Modal\n\tif (clearAt.type === '_time') {\n\t\treturn clearAt.time\n\t}\n\n\treturn null\n}\n\nexport {\n\tgetTimestampForClearAt,\n}\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport {\n\tfetchCurrentStatus,\n\tsetStatus,\n\tsetPredefinedMessage,\n\tsetCustomMessage,\n\tclearMessage,\n} from '../services/statusService.js'\nimport { loadState } from '@nextcloud/initial-state'\nimport { getCurrentUser } from '@nextcloud/auth'\nimport { getTimestampForClearAt } from '../services/clearAtService.js'\nimport { emit } from '@nextcloud/event-bus'\n\nconst state = {\n\t// Status (online / away / dnd / invisible / offline)\n\tstatus: null,\n\t// Whether the status is user-defined\n\tstatusIsUserDefined: null,\n\t// A custom message set by the user\n\tmessage: null,\n\t// The icon selected by the user\n\ticon: null,\n\t// When to automatically clean the status\n\tclearAt: null,\n\t// Whether the message is predefined\n\t// (and can automatically be translated by Nextcloud)\n\tmessageIsPredefined: null,\n\t// The id of the message in case it's predefined\n\tmessageId: null,\n}\n\nconst mutations = {\n\n\t/**\n\t * Sets a new status\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} data The destructuring object\n\t * @param {string} data.statusType The new status type\n\t */\n\tsetStatus(state, { statusType }) {\n\t\tstate.status = statusType\n\t\tstate.statusIsUserDefined = true\n\t},\n\n\t/**\n\t * Sets a message using a predefined message\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} data The destructuring object\n\t * @param {string} data.messageId The messageId\n\t * @param {number | null} data.clearAt When to automatically clear the status\n\t * @param {string} data.message The message\n\t * @param {string} data.icon The icon\n\t */\n\tsetPredefinedMessage(state, { messageId, clearAt, message, icon }) {\n\t\tstate.messageId = messageId\n\t\tstate.messageIsPredefined = true\n\n\t\tstate.message = message\n\t\tstate.icon = icon\n\t\tstate.clearAt = clearAt\n\t},\n\n\t/**\n\t * Sets a custom message\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} data The destructuring object\n\t * @param {string} data.message The message\n\t * @param {string} data.icon The icon\n\t * @param {number} data.clearAt When to automatically clear the status\n\t */\n\tsetCustomMessage(state, { message, icon, clearAt }) {\n\t\tstate.messageId = null\n\t\tstate.messageIsPredefined = false\n\n\t\tstate.message = message\n\t\tstate.icon = icon\n\t\tstate.clearAt = clearAt\n\t},\n\n\t/**\n\t * Clears the status\n\t *\n\t * @param {object} state The Vuex state\n\t */\n\tclearMessage(state) {\n\t\tstate.messageId = null\n\t\tstate.messageIsPredefined = false\n\n\t\tstate.message = null\n\t\tstate.icon = null\n\t\tstate.clearAt = null\n\t},\n\n\t/**\n\t * Loads the status from initial state\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} data The destructuring object\n\t * @param {string} data.status The status type\n\t * @param {boolean} data.statusIsUserDefined Whether or not this status is user-defined\n\t * @param {string} data.message The message\n\t * @param {string} data.icon The icon\n\t * @param {number} data.clearAt When to automatically clear the status\n\t * @param {boolean} data.messageIsPredefined Whether or not the message is predefined\n\t * @param {string} data.messageId The id of the predefined message\n\t */\n\tloadStatusFromServer(state, { status, statusIsUserDefined, message, icon, clearAt, messageIsPredefined, messageId }) {\n\t\tstate.status = status\n\t\tstate.message = message\n\t\tstate.icon = icon\n\n\t\t// Don't overwrite certain values if the refreshing comes in via short updates\n\t\t// E.g. from talk participant list which only has the status, message and icon\n\t\tif (typeof statusIsUserDefined !== 'undefined') {\n\t\t\tstate.statusIsUserDefined = statusIsUserDefined\n\t\t}\n\t\tif (typeof clearAt !== 'undefined') {\n\t\t\tstate.clearAt = clearAt\n\t\t}\n\t\tif (typeof messageIsPredefined !== 'undefined') {\n\t\t\tstate.messageIsPredefined = messageIsPredefined\n\t\t}\n\t\tif (typeof messageId !== 'undefined') {\n\t\t\tstate.messageId = messageId\n\t\t}\n\t},\n}\n\nconst getters = {}\n\nconst actions = {\n\n\t/**\n\t * Sets a new status\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state The Vuex state object\n\t * @param {object} data The data destructuring object\n\t * @param {string} data.statusType The new status type\n\t * @return {Promise<void>}\n\t */\n\tasync setStatus({ commit, state }, { statusType }) {\n\t\tawait setStatus(statusType)\n\t\tcommit('setStatus', { statusType })\n\t\temit('user_status:status.updated', {\n\t\t\tstatus: state.status,\n\t\t\tmessage: state.message,\n\t\t\ticon: state.icon,\n\t\t\tclearAt: state.clearAt,\n\t\t\tuserId: getCurrentUser()?.uid,\n\t\t})\n\t},\n\n\t/**\n\t * Update status from 'user_status:status.updated' update.\n\t * This doesn't trigger another 'user_status:status.updated'\n\t * event.\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state The Vuex state object\n\t * @param {string} status The new status\n\t * @return {Promise<void>}\n\t */\n\tasync setStatusFromObject({ commit, state }, status) {\n\t\tcommit('loadStatusFromServer', status)\n\t},\n\n\t/**\n\t * Sets a message using a predefined message\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state The Vuex state object\n\t * @param {object} vuex.rootState The Vuex root state\n\t * @param {object} data The data destructuring object\n\t * @param {string} data.messageId The messageId\n\t * @param {object | null} data.clearAt When to automatically clear the status\n\t * @return {Promise<void>}\n\t */\n\tasync setPredefinedMessage({ commit, rootState, state }, { messageId, clearAt }) {\n\t\tconst resolvedClearAt = getTimestampForClearAt(clearAt)\n\n\t\tawait setPredefinedMessage(messageId, resolvedClearAt)\n\t\tconst status = rootState.predefinedStatuses.predefinedStatuses.find((status) => status.id === messageId)\n\t\tconst { message, icon } = status\n\n\t\tcommit('setPredefinedMessage', { messageId, clearAt: resolvedClearAt, message, icon })\n\t\temit('user_status:status.updated', {\n\t\t\tstatus: state.status,\n\t\t\tmessage: state.message,\n\t\t\ticon: state.icon,\n\t\t\tclearAt: state.clearAt,\n\t\t\tuserId: getCurrentUser()?.uid,\n\t\t})\n\t},\n\n\t/**\n\t * Sets a custom message\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state The Vuex state object\n\t * @param {object} data The data destructuring object\n\t * @param {string} data.message The message\n\t * @param {string} data.icon The icon\n\t * @param {object | null} data.clearAt When to automatically clear the status\n\t * @return {Promise<void>}\n\t */\n\tasync setCustomMessage({ commit, state }, { message, icon, clearAt }) {\n\t\tconst resolvedClearAt = getTimestampForClearAt(clearAt)\n\n\t\tawait setCustomMessage(message, icon, resolvedClearAt)\n\t\tcommit('setCustomMessage', { message, icon, clearAt: resolvedClearAt })\n\t\temit('user_status:status.updated', {\n\t\t\tstatus: state.status,\n\t\t\tmessage: state.message,\n\t\t\ticon: state.icon,\n\t\t\tclearAt: state.clearAt,\n\t\t\tuserId: getCurrentUser()?.uid,\n\t\t})\n\t},\n\n\t/**\n\t * Clears the status\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state The Vuex state object\n\t * @return {Promise<void>}\n\t */\n\tasync clearMessage({ commit, state }) {\n\t\tawait clearMessage()\n\t\tcommit('clearMessage')\n\t\temit('user_status:status.updated', {\n\t\t\tstatus: state.status,\n\t\t\tmessage: state.message,\n\t\t\ticon: state.icon,\n\t\t\tclearAt: state.clearAt,\n\t\t\tuserId: getCurrentUser()?.uid,\n\t\t})\n\t},\n\n\t/**\n\t * Re-fetches the status from the server\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @return {Promise<void>}\n\t */\n\tasync reFetchStatusFromServer({ commit }) {\n\t\tconst status = await fetchCurrentStatus()\n\t\tcommit('loadStatusFromServer', status)\n\t},\n\n\t/**\n\t * Stores the status we got in the reply of the heartbeat\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} status The data destructuring object\n\t * @param {string} status.status The status type\n\t * @param {boolean} status.statusIsUserDefined Whether or not this status is user-defined\n\t * @param {string} status.message The message\n\t * @param {string} status.icon The icon\n\t * @param {number} status.clearAt When to automatically clear the status\n\t * @param {boolean} status.messageIsPredefined Whether or not the message is predefined\n\t * @param {string} status.messageId The id of the predefined message\n\t * @return {Promise<void>}\n\t */\n\tasync setStatusFromHeartbeat({ commit }, status) {\n\t\tcommit('loadStatusFromServer', status)\n\t},\n\n\t/**\n\t * Loads the server from the initial state\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t */\n\tloadStatusFromInitialState({ commit }) {\n\t\tconst status = loadState('user_status', 'status')\n\t\tcommit('loadStatusFromServer', status)\n\t},\n}\n\nexport default { state, mutations, getters, actions }\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport HttpClient from '@nextcloud/axios'\nimport { generateOcsUrl } from '@nextcloud/router'\n\n/**\n * Fetches the current user-status\n *\n * @return {Promise<object>}\n */\nconst fetchCurrentStatus = async () => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status')\n\tconst response = await HttpClient.get(url)\n\n\treturn response.data.ocs.data\n}\n\n/**\n * Fetches the current user-status\n *\n * @param {string} userId\n * @return {Promise<object>}\n */\nconst fetchBackupStatus = async (userId) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/statuses/{userId}', { userId: '_' + userId })\n\tconst response = await HttpClient.get(url)\n\n\treturn response.data.ocs.data\n}\n\n/**\n * Sets the status\n *\n * @param {string} statusType The status (online / away / dnd / invisible)\n * @return {Promise<void>}\n */\nconst setStatus = async (statusType) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status/status')\n\tawait HttpClient.put(url, {\n\t\tstatusType,\n\t})\n}\n\n/**\n * Sets a message based on our predefined statuses\n *\n * @param {string} messageId The id of the message, taken from predefined status service\n * @param {number | null} clearAt When to automatically clean the status\n * @return {Promise<void>}\n */\nconst setPredefinedMessage = async (messageId, clearAt = null) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status/message/predefined?format=json')\n\tawait HttpClient.put(url, {\n\t\tmessageId,\n\t\tclearAt,\n\t})\n}\n\n/**\n * Sets a custom message\n *\n * @param {string} message The user-defined message\n * @param {string | null} statusIcon The user-defined icon\n * @param {number | null} clearAt When to automatically clean the status\n * @return {Promise<void>}\n */\nconst setCustomMessage = async (message, statusIcon = null, clearAt = null) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status/message/custom?format=json')\n\tawait HttpClient.put(url, {\n\t\tmessage,\n\t\tstatusIcon,\n\t\tclearAt,\n\t})\n}\n\n/**\n * Clears the current status of the user\n *\n * @return {Promise<void>}\n */\nconst clearMessage = async () => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status/message?format=json')\n\tawait HttpClient.delete(url)\n}\n\n/**\n * Revert the automated status\n *\n * @param {string} messageId\n * @return {Promise<object>}\n */\nconst revertToBackupStatus = async (messageId) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status/revert/{messageId}', { messageId })\n\tconst response = await HttpClient.delete(url)\n\n\treturn response.data.ocs.data\n}\n\nexport {\n\tfetchCurrentStatus,\n\tfetchBackupStatus,\n\tsetStatus,\n\tsetCustomMessage,\n\tsetPredefinedMessage,\n\tclearMessage,\n\trevertToBackupStatus,\n}\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n * @author Joas Schilling <coding@schilljs.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport {\n\tfetchBackupStatus,\n\trevertToBackupStatus,\n} from '../services/statusService.js'\nimport { getCurrentUser } from '@nextcloud/auth'\nimport { emit } from '@nextcloud/event-bus'\n\nconst state = {\n\t// Status (online / away / dnd / invisible / offline)\n\tstatus: null,\n\t// Whether the status is user-defined\n\tstatusIsUserDefined: null,\n\t// A custom message set by the user\n\tmessage: null,\n\t// The icon selected by the user\n\ticon: null,\n\t// When to automatically clean the status\n\tclearAt: null,\n\t// Whether the message is predefined\n\t// (and can automatically be translated by Nextcloud)\n\tmessageIsPredefined: null,\n\t// The id of the message in case it's predefined\n\tmessageId: null,\n}\n\nconst mutations = {\n\t/**\n\t * Loads the status from initial state\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} data The destructuring object\n\t * @param {string} data.status The status type\n\t * @param {boolean} data.statusIsUserDefined Whether or not this status is user-defined\n\t * @param {string} data.message The message\n\t * @param {string} data.icon The icon\n\t * @param {number} data.clearAt When to automatically clear the status\n\t * @param {boolean} data.messageIsPredefined Whether or not the message is predefined\n\t * @param {string} data.messageId The id of the predefined message\n\t */\n\tloadBackupStatusFromServer(state, { status, statusIsUserDefined, message, icon, clearAt, messageIsPredefined, messageId }) {\n\t\tstate.status = status\n\t\tstate.message = message\n\t\tstate.icon = icon\n\n\t\t// Don't overwrite certain values if the refreshing comes in via short updates\n\t\t// E.g. from talk participant list which only has the status, message and icon\n\t\tif (typeof statusIsUserDefined !== 'undefined') {\n\t\t\tstate.statusIsUserDefined = statusIsUserDefined\n\t\t}\n\t\tif (typeof clearAt !== 'undefined') {\n\t\t\tstate.clearAt = clearAt\n\t\t}\n\t\tif (typeof messageIsPredefined !== 'undefined') {\n\t\t\tstate.messageIsPredefined = messageIsPredefined\n\t\t}\n\t\tif (typeof messageId !== 'undefined') {\n\t\t\tstate.messageId = messageId\n\t\t}\n\t},\n}\n\nconst getters = {}\n\nconst actions = {\n\t/**\n\t * Re-fetches the status from the server\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @return {Promise<void>}\n\t */\n\tasync fetchBackupFromServer({ commit }) {\n\t\ttry {\n\t\t\tconst status = await fetchBackupStatus(getCurrentUser()?.uid)\n\t\t\tcommit('loadBackupStatusFromServer', status)\n\t\t} catch (e) {\n\t\t\t// Ignore missing user backup status\n\t\t}\n\t},\n\n\tasync revertBackupFromServer({ commit }, { messageId }) {\n\t\tconst status = await revertToBackupStatus(messageId)\n\t\tif (status) {\n\t\t\tcommit('loadBackupStatusFromServer', {})\n\t\t\tcommit('loadStatusFromServer', status)\n\t\t\temit('user_status:status.updated', {\n\t\t\t\tstatus: status.status,\n\t\t\t\tmessage: status.message,\n\t\t\t\ticon: status.icon,\n\t\t\t\tclearAt: status.clearAt,\n\t\t\t\tuserId: getCurrentUser()?.uid,\n\t\t\t})\n\t\t}\n\t},\n}\n\nexport default { state, mutations, getters, actions }\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport Vue from 'vue'\nimport Vuex, { Store } from 'vuex'\nimport predefinedStatuses from './predefinedStatuses.js'\nimport userStatus from './userStatus.js'\nimport userBackupStatus from './userBackupStatus.js'\n\nVue.use(Vuex)\n\nexport default new Store({\n\tmodules: {\n\t\tpredefinedStatuses,\n\t\tuserStatus,\n\t\tuserBackupStatus,\n\t},\n\tstrict: true,\n})\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n * @author John Molakvoæ <skjnldsv@protonmail.com>\n * @author Julius Härtl <jus@bitgrid.net>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport Vue from 'vue'\nimport { getRequestToken } from '@nextcloud/auth'\nimport { subscribe } from '@nextcloud/event-bus'\n\nimport UserStatus from './UserStatus.vue'\n\nimport store from './store/index.js'\n\n// eslint-disable-next-line camelcase\n__webpack_nonce__ = btoa(getRequestToken())\n\nVue.prototype.t = t\nVue.prototype.$t = t\n\nconst mountPoint = document.getElementById('user_status-menu-entry')\n\nconst mountMenuEntry = () => {\n\tconst mountPoint = document.getElementById('user_status-menu-entry')\n\t// eslint-disable-next-line no-new\n\tnew Vue({\n\t\tel: mountPoint,\n\t\trender: h => h(UserStatus),\n\t\tstore,\n\t})\n}\n\nif (mountPoint) {\n\tmountMenuEntry()\n} else {\n\tsubscribe('core:user-menu:mounted', mountMenuEntry)\n}\n\n// Register dashboard status\ndocument.addEventListener('DOMContentLoaded', function() {\n\tif (!OCA.Dashboard) {\n\t\treturn\n\t}\n\n\tOCA.Dashboard.registerStatus('status', (el) => {\n\t\tconst Dashboard = Vue.extend(UserStatus)\n\t\treturn new Dashboard({\n\t\t\tpropsData: {\n\t\t\t\tinline: true,\n\t\t\t},\n\t\t\tstore,\n\t\t}).$mount(el)\n\t})\n})\n","/**\n * @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>\n *\n * @author John Molakvoæ <skjnldsv@protonmail.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport { mapState } from 'vuex'\nimport { showError } from '@nextcloud/dialogs'\n\nexport default {\n\tcomputed: {\n\t\t...mapState({\n\t\t\tstatusType: state => state.userStatus.status,\n\t\t\tstatusIsUserDefined: state => state.userStatus.statusIsUserDefined,\n\t\t\tcustomIcon: state => state.userStatus.icon,\n\t\t\tcustomMessage: state => state.userStatus.message,\n\t\t}),\n\n\t\t/**\n\t\t * The message displayed in the top right corner\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tvisibleMessage() {\n\t\t\tif (this.customIcon && this.customMessage) {\n\t\t\t\treturn `${this.customIcon} ${this.customMessage}`\n\t\t\t}\n\n\t\t\tif (this.customMessage) {\n\t\t\t\treturn this.customMessage\n\t\t\t}\n\n\t\t\tif (this.statusIsUserDefined) {\n\t\t\t\tswitch (this.statusType) {\n\t\t\t\tcase 'online':\n\t\t\t\t\treturn this.$t('user_status', 'Online')\n\n\t\t\t\tcase 'away':\n\t\t\t\t\treturn this.$t('user_status', 'Away')\n\n\t\t\t\tcase 'dnd':\n\t\t\t\t\treturn this.$t('user_status', 'Do not disturb')\n\n\t\t\t\tcase 'invisible':\n\t\t\t\t\treturn this.$t('user_status', 'Invisible')\n\n\t\t\t\tcase 'offline':\n\t\t\t\t\treturn this.$t('user_status', 'Offline')\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.$t('user_status', 'Set status')\n\t\t},\n\n\t\t/**\n\t\t * The status indicator icon\n\t\t *\n\t\t * @return {string | null}\n\t\t */\n\t\tstatusIcon() {\n\t\t\tswitch (this.statusType) {\n\t\t\tcase 'online':\n\t\t\t\treturn 'icon-user-status-online'\n\n\t\t\tcase 'away':\n\t\t\t\treturn 'icon-user-status-away'\n\n\t\t\tcase 'dnd':\n\t\t\t\treturn 'icon-user-status-dnd'\n\n\t\t\tcase 'invisible':\n\t\t\tcase 'offline':\n\t\t\t\treturn 'icon-user-status-invisible'\n\t\t\t}\n\n\t\t\treturn ''\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Changes the user-status\n\t\t *\n\t\t * @param {string} statusType (online / away / dnd / invisible)\n\t\t */\n\t\tasync changeStatus(statusType) {\n\t\t\ttry {\n\t\t\t\tawait this.$store.dispatch('setStatus', { statusType })\n\t\t\t} catch (err) {\n\t\t\t\tshowError(this.$t('user_status', 'There was an error saving the new status'))\n\t\t\t\tconsole.debug(err)\n\t\t\t}\n\t\t},\n\t},\n}\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nconst dateFactory = () => {\n\treturn new Date()\n}\n\nexport {\n\tdateFactory,\n}\n","// Imports\nimport ___CSS_LOADER_API_SOURCEMAP_IMPORT___ from \"../../../node_modules/css-loader/dist/runtime/sourceMaps.js\";\nimport ___CSS_LOADER_API_IMPORT___ from \"../../../node_modules/css-loader/dist/runtime/api.js\";\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `.user-status-menu-item[data-v-798bb70c]{width:auto;min-width:44px;height:44px;margin:0;border:0;border-radius:var(--border-radius-pill);background-color:var(--color-main-background-blur);font-size:inherit;font-weight:normal;-webkit-backdrop-filter:var(--background-blur);backdrop-filter:var(--background-blur)}.user-status-menu-item[data-v-798bb70c]:active,.user-status-menu-item[data-v-798bb70c]:hover,.user-status-menu-item[data-v-798bb70c]:focus-visible{background-color:var(--color-background-hover)}.user-status-menu-item[data-v-798bb70c]:focus-visible{box-shadow:0 0 0 4px var(--color-main-background) !important;outline:2px solid var(--color-main-text) !important}.user-status-icon[data-v-798bb70c]{width:16px;height:16px;margin-right:10px;opacity:1 !important;background-size:16px;vertical-align:middle !important}`, \"\",{\"version\":3,\"sources\":[\"webpack://./apps/user_status/src/UserStatus.vue\"],\"names\":[],\"mappings\":\"AACA,wCACC,UAAA,CACA,cAAA,CACA,WAAA,CACA,QAAA,CACA,QAAA,CACA,uCAAA,CACA,kDAAA,CACA,iBAAA,CACA,kBAAA,CAEA,8CAAA,CACA,sCAAA,CAEA,mJAGC,8CAAA,CAED,sDACC,4DAAA,CACA,mDAAA,CAIF,mCACC,UAAA,CACA,WAAA,CACA,iBAAA,CACA,oBAAA,CACA,oBAAA,CACA,gCAAA\",\"sourcesContent\":[\"\\n.user-status-menu-item {\\n\\twidth: auto;\\n\\tmin-width: 44px;\\n\\theight: 44px;\\n\\tmargin: 0;\\n\\tborder: 0;\\n\\tborder-radius: var(--border-radius-pill);\\n\\tbackground-color: var(--color-main-background-blur);\\n\\tfont-size: inherit;\\n\\tfont-weight: normal;\\n\\n\\t-webkit-backdrop-filter: var(--background-blur);\\n\\tbackdrop-filter: var(--background-blur);\\n\\n\\t&:active,\\n\\t&:hover,\\n\\t&:focus-visible {\\n\\t\\tbackground-color: var(--color-background-hover);\\n\\t}\\n\\t&:focus-visible {\\n\\t\\tbox-shadow: 0 0 0 4px var(--color-main-background) !important;\\n\\t\\toutline: 2px solid var(--color-main-text) !important;\\n\\t}\\n}\\n\\n.user-status-icon {\\n\\twidth: 16px;\\n\\theight: 16px;\\n\\tmargin-right: 10px;\\n\\topacity: 1 !important;\\n\\tbackground-size: 16px;\\n\\tvertical-align: middle !important;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\nexport default ___CSS_LOADER_EXPORT___;\n","var map = {\n\t\"./af\": 42786,\n\t\"./af.js\": 42786,\n\t\"./ar\": 30867,\n\t\"./ar-dz\": 14130,\n\t\"./ar-dz.js\": 14130,\n\t\"./ar-kw\": 96135,\n\t\"./ar-kw.js\": 96135,\n\t\"./ar-ly\": 56440,\n\t\"./ar-ly.js\": 56440,\n\t\"./ar-ma\": 47702,\n\t\"./ar-ma.js\": 47702,\n\t\"./ar-sa\": 16040,\n\t\"./ar-sa.js\": 16040,\n\t\"./ar-tn\": 37100,\n\t\"./ar-tn.js\": 37100,\n\t\"./ar.js\": 30867,\n\t\"./az\": 31083,\n\t\"./az.js\": 31083,\n\t\"./be\": 9808,\n\t\"./be.js\": 9808,\n\t\"./bg\": 68338,\n\t\"./bg.js\": 68338,\n\t\"./bm\": 67438,\n\t\"./bm.js\": 67438,\n\t\"./bn\": 8905,\n\t\"./bn-bd\": 76225,\n\t\"./bn-bd.js\": 76225,\n\t\"./bn.js\": 8905,\n\t\"./bo\": 11560,\n\t\"./bo.js\": 11560,\n\t\"./br\": 1278,\n\t\"./br.js\": 1278,\n\t\"./bs\": 80622,\n\t\"./bs.js\": 80622,\n\t\"./ca\": 2468,\n\t\"./ca.js\": 2468,\n\t\"./cs\": 5822,\n\t\"./cs.js\": 5822,\n\t\"./cv\": 50877,\n\t\"./cv.js\": 50877,\n\t\"./cy\": 47373,\n\t\"./cy.js\": 47373,\n\t\"./da\": 24780,\n\t\"./da.js\": 24780,\n\t\"./de\": 59740,\n\t\"./de-at\": 60217,\n\t\"./de-at.js\": 60217,\n\t\"./de-ch\": 60894,\n\t\"./de-ch.js\": 60894,\n\t\"./de.js\": 59740,\n\t\"./dv\": 5300,\n\t\"./dv.js\": 5300,\n\t\"./el\": 50837,\n\t\"./el.js\": 50837,\n\t\"./en-au\": 78348,\n\t\"./en-au.js\": 78348,\n\t\"./en-ca\": 77925,\n\t\"./en-ca.js\": 77925,\n\t\"./en-gb\": 22243,\n\t\"./en-gb.js\": 22243,\n\t\"./en-ie\": 46436,\n\t\"./en-ie.js\": 46436,\n\t\"./en-il\": 47207,\n\t\"./en-il.js\": 47207,\n\t\"./en-in\": 44175,\n\t\"./en-in.js\": 44175,\n\t\"./en-nz\": 76319,\n\t\"./en-nz.js\": 76319,\n\t\"./en-sg\": 31662,\n\t\"./en-sg.js\": 31662,\n\t\"./eo\": 92915,\n\t\"./eo.js\": 92915,\n\t\"./es\": 55655,\n\t\"./es-do\": 55251,\n\t\"./es-do.js\": 55251,\n\t\"./es-mx\": 96112,\n\t\"./es-mx.js\": 96112,\n\t\"./es-us\": 71146,\n\t\"./es-us.js\": 71146,\n\t\"./es.js\": 55655,\n\t\"./et\": 5603,\n\t\"./et.js\": 5603,\n\t\"./eu\": 77763,\n\t\"./eu.js\": 77763,\n\t\"./fa\": 76959,\n\t\"./fa.js\": 76959,\n\t\"./fi\": 11897,\n\t\"./fi.js\": 11897,\n\t\"./fil\": 42549,\n\t\"./fil.js\": 42549,\n\t\"./fo\": 94694,\n\t\"./fo.js\": 94694,\n\t\"./fr\": 94470,\n\t\"./fr-ca\": 63049,\n\t\"./fr-ca.js\": 63049,\n\t\"./fr-ch\": 52330,\n\t\"./fr-ch.js\": 52330,\n\t\"./fr.js\": 94470,\n\t\"./fy\": 5044,\n\t\"./fy.js\": 5044,\n\t\"./ga\": 29295,\n\t\"./ga.js\": 29295,\n\t\"./gd\": 2101,\n\t\"./gd.js\": 2101,\n\t\"./gl\": 38794,\n\t\"./gl.js\": 38794,\n\t\"./gom-deva\": 27884,\n\t\"./gom-deva.js\": 27884,\n\t\"./gom-latn\": 23168,\n\t\"./gom-latn.js\": 23168,\n\t\"./gu\": 95349,\n\t\"./gu.js\": 95349,\n\t\"./he\": 24206,\n\t\"./he.js\": 24206,\n\t\"./hi\": 30094,\n\t\"./hi.js\": 30094,\n\t\"./hr\": 30316,\n\t\"./hr.js\": 30316,\n\t\"./hu\": 22138,\n\t\"./hu.js\": 22138,\n\t\"./hy-am\": 11423,\n\t\"./hy-am.js\": 11423,\n\t\"./id\": 29218,\n\t\"./id.js\": 29218,\n\t\"./is\": 90135,\n\t\"./is.js\": 90135,\n\t\"./it\": 90626,\n\t\"./it-ch\": 10150,\n\t\"./it-ch.js\": 10150,\n\t\"./it.js\": 90626,\n\t\"./ja\": 39183,\n\t\"./ja.js\": 39183,\n\t\"./jv\": 24286,\n\t\"./jv.js\": 24286,\n\t\"./ka\": 12105,\n\t\"./ka.js\": 12105,\n\t\"./kk\": 47772,\n\t\"./kk.js\": 47772,\n\t\"./km\": 18758,\n\t\"./km.js\": 18758,\n\t\"./kn\": 79282,\n\t\"./kn.js\": 79282,\n\t\"./ko\": 33730,\n\t\"./ko.js\": 33730,\n\t\"./ku\": 1408,\n\t\"./ku.js\": 1408,\n\t\"./ky\": 33291,\n\t\"./ky.js\": 33291,\n\t\"./lb\": 36841,\n\t\"./lb.js\": 36841,\n\t\"./lo\": 55466,\n\t\"./lo.js\": 55466,\n\t\"./lt\": 57010,\n\t\"./lt.js\": 57010,\n\t\"./lv\": 37595,\n\t\"./lv.js\": 37595,\n\t\"./me\": 39861,\n\t\"./me.js\": 39861,\n\t\"./mi\": 35493,\n\t\"./mi.js\": 35493,\n\t\"./mk\": 95966,\n\t\"./mk.js\": 95966,\n\t\"./ml\": 87341,\n\t\"./ml.js\": 87341,\n\t\"./mn\": 5115,\n\t\"./mn.js\": 5115,\n\t\"./mr\": 10370,\n\t\"./mr.js\": 10370,\n\t\"./ms\": 9847,\n\t\"./ms-my\": 41237,\n\t\"./ms-my.js\": 41237,\n\t\"./ms.js\": 9847,\n\t\"./mt\": 72126,\n\t\"./mt.js\": 72126,\n\t\"./my\": 56165,\n\t\"./my.js\": 56165,\n\t\"./nb\": 64924,\n\t\"./nb.js\": 64924,\n\t\"./ne\": 16744,\n\t\"./ne.js\": 16744,\n\t\"./nl\": 93901,\n\t\"./nl-be\": 59814,\n\t\"./nl-be.js\": 59814,\n\t\"./nl.js\": 93901,\n\t\"./nn\": 83877,\n\t\"./nn.js\": 83877,\n\t\"./oc-lnc\": 92135,\n\t\"./oc-lnc.js\": 92135,\n\t\"./pa-in\": 15858,\n\t\"./pa-in.js\": 15858,\n\t\"./pl\": 64495,\n\t\"./pl.js\": 64495,\n\t\"./pt\": 89520,\n\t\"./pt-br\": 57971,\n\t\"./pt-br.js\": 57971,\n\t\"./pt.js\": 89520,\n\t\"./ro\": 96459,\n\t\"./ro.js\": 96459,\n\t\"./ru\": 21793,\n\t\"./ru.js\": 21793,\n\t\"./sd\": 40950,\n\t\"./sd.js\": 40950,\n\t\"./se\": 10490,\n\t\"./se.js\": 10490,\n\t\"./si\": 90124,\n\t\"./si.js\": 90124,\n\t\"./sk\": 64249,\n\t\"./sk.js\": 64249,\n\t\"./sl\": 14985,\n\t\"./sl.js\": 14985,\n\t\"./sq\": 51104,\n\t\"./sq.js\": 51104,\n\t\"./sr\": 49131,\n\t\"./sr-cyrl\": 79915,\n\t\"./sr-cyrl.js\": 79915,\n\t\"./sr.js\": 49131,\n\t\"./ss\": 85893,\n\t\"./ss.js\": 85893,\n\t\"./sv\": 98760,\n\t\"./sv.js\": 98760,\n\t\"./sw\": 91172,\n\t\"./sw.js\": 91172,\n\t\"./ta\": 27333,\n\t\"./ta.js\": 27333,\n\t\"./te\": 23110,\n\t\"./te.js\": 23110,\n\t\"./tet\": 52095,\n\t\"./tet.js\": 52095,\n\t\"./tg\": 27321,\n\t\"./tg.js\": 27321,\n\t\"./th\": 9041,\n\t\"./th.js\": 9041,\n\t\"./tk\": 19005,\n\t\"./tk.js\": 19005,\n\t\"./tl-ph\": 75768,\n\t\"./tl-ph.js\": 75768,\n\t\"./tlh\": 89444,\n\t\"./tlh.js\": 89444,\n\t\"./tr\": 72397,\n\t\"./tr.js\": 72397,\n\t\"./tzl\": 28254,\n\t\"./tzl.js\": 28254,\n\t\"./tzm\": 51106,\n\t\"./tzm-latn\": 30699,\n\t\"./tzm-latn.js\": 30699,\n\t\"./tzm.js\": 51106,\n\t\"./ug-cn\": 9288,\n\t\"./ug-cn.js\": 9288,\n\t\"./uk\": 67691,\n\t\"./uk.js\": 67691,\n\t\"./ur\": 13795,\n\t\"./ur.js\": 13795,\n\t\"./uz\": 6791,\n\t\"./uz-latn\": 60588,\n\t\"./uz-latn.js\": 60588,\n\t\"./uz.js\": 6791,\n\t\"./vi\": 65666,\n\t\"./vi.js\": 65666,\n\t\"./x-pseudo\": 14378,\n\t\"./x-pseudo.js\": 14378,\n\t\"./yo\": 75805,\n\t\"./yo.js\": 75805,\n\t\"./zh-cn\": 83839,\n\t\"./zh-cn.js\": 83839,\n\t\"./zh-hk\": 55726,\n\t\"./zh-hk.js\": 55726,\n\t\"./zh-mo\": 99807,\n\t\"./zh-mo.js\": 99807,\n\t\"./zh-tw\": 74152,\n\t\"./zh-tw.js\": 74152\n};\n\n\nfunction webpackContext(req) {\n\tvar id = webpackContextResolve(req);\n\treturn __webpack_require__(id);\n}\nfunction webpackContextResolve(req) {\n\tif(!__webpack_require__.o(map, req)) {\n\t\tvar e = new Error(\"Cannot find module '\" + req + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\treturn map[req];\n}\nwebpackContext.keys = function webpackContextKeys() {\n\treturn Object.keys(map);\n};\nwebpackContext.resolve = webpackContextResolve;\nmodule.exports = webpackContext;\nwebpackContext.id = 46700;","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\tid: moduleId,\n\t\tloaded: false,\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Flag the module as loaded\n\tmodule.loaded = true;\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = (chunkId) => {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = (chunkId) => {\n\t// return url for filenames based on template\n\treturn \"\" + (chunkId === 8299 ? \"user-status-modal\" : chunkId) + \"-\" + chunkId + \".js?v=\" + {\"3240\":\"29c327d1e4e42959b82f\",\"3998\":\"a49373c9d79e30e60f7b\",\"8299\":\"7006122e6c76e3cf7057\",\"9064\":\"f6243754beec9d78de45\"}[chunkId] + \"\";\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","__webpack_require__.nmd = (module) => {\n\tmodule.paths = [];\n\tif (!module.children) module.children = [];\n\treturn module;\n};","__webpack_require__.j = 2613;","var scriptUrl;\nif (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + \"\";\nvar document = __webpack_require__.g.document;\nif (!scriptUrl && document) {\n\tif (document.currentScript)\n\t\tscriptUrl = document.currentScript.src;\n\tif (!scriptUrl) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tif(scripts.length) {\n\t\t\tvar i = scripts.length - 1;\n\t\t\twhile (i > -1 && !scriptUrl) scriptUrl = scripts[i--].src;\n\t\t}\n\t}\n}\n// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration\n// or pass an empty string (\"\") and set the __webpack_public_path__ variable from your code to use your own logic.\nif (!scriptUrl) throw new Error(\"Automatic publicPath is not supported in this browser\");\nscriptUrl = scriptUrl.replace(/#.*$/, \"\").replace(/\\?.*$/, \"\").replace(/\\/[^\\/]+$/, \"/\");\n__webpack_require__.p = scriptUrl;","__webpack_require__.b = document.baseURI || self.location.href;\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t2613: 0\n};\n\n__webpack_require__.f.j = (chunkId, promises) => {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(true) { // all chunks have JS\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = (event) => {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n__webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0);\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = (parentChunkLoadingFunction, data) => {\n\tvar chunkIds = data[0];\n\tvar moreModules = data[1];\n\tvar runtime = data[2];\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some((id) => (installedChunks[id] !== 0))) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\treturn __webpack_require__.O(result);\n}\n\nvar chunkLoadingGlobal = self[\"webpackChunknextcloud\"] = self[\"webpackChunknextcloud\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","__webpack_require__.nc = undefined;","// startup\n// Load entry module and return exports\n// This entry module depends on other loaded chunks and execution need to be delayed\nvar __webpack_exports__ = __webpack_require__.O(undefined, [7874], () => (__webpack_require__(86419)))\n__webpack_exports__ = __webpack_require__.O(__webpack_exports__);\n"],"names":["deferred","inProgress","dataWebpackPrefix","name","components","NcButton","SetStatusModal","mixins","OnlineStatusMixin","props","inline","type","Boolean","default","data","heartbeatInterval","isAway","isModalOpen","mouseMoveListener","setAwayTimeout","mounted","$store","dispatch","OC","config","session_keepalive","setInterval","_backgroundHeartbeat","bind","debounce","wasAway","clearTimeout","setTimeout","window","addEventListener","capture","passive","subscribe","handleUserStatusUpdated","beforeDestroy","removeEventListener","clearInterval","unsubscribe","methods","openModal","closeModal","status","async","url","generateOcsUrl","HttpClient","put","ocs","sendHeartbeat","userId","error","console","debug","response","state","getCurrentUser","uid","icon","message","options","styleTagTransform","setAttributes","insert","domAPI","insertStyleElement","locals","_vm","this","_c","_self","tag","attrs","statusIcon","on","$event","stopPropagation","apply","arguments","scopedSlots","_u","key","fn","staticClass","class","proxy","_v","_s","visibleMessage","_e","predefinedStatuses","mutations","addPredefinedStatus","getters","statusesHaveLoaded","length","actions","loadAllPredefinedStatuses","_ref","commit","statuses","get","fetchAllPredefinedStatuses","getTimestampForClearAt","clearAt","date","dateFactory","setSeconds","getSeconds","time","Math","floor","getTime","Number","moment","endOf","format","statusIsUserDefined","messageIsPredefined","messageId","setStatus","statusType","setPredefinedMessage","_ref2","setCustomMessage","_ref3","clearMessage","loadStatusFromServer","_ref4","_ref5","_ref6","emit","setStatusFromObject","_ref7","_ref8","_ref9","rootState","resolvedClearAt","undefined","find","id","_ref10","_ref11","_ref12","delete","reFetchStatusFromServer","_ref13","fetchCurrentStatus","setStatusFromHeartbeat","_ref14","loadStatusFromInitialState","_ref15","loadState","loadBackupStatusFromServer","fetchBackupFromServer","fetchBackupStatus","e","revertBackupFromServer","revertToBackupStatus","Vue","use","Vuex","Store","modules","userStatus","userBackupStatus","strict","__webpack_nonce__","btoa","getRequestToken","prototype","t","$t","mountMenuEntry","mountPoint","document","getElementById","el","render","h","UserStatus","store","OCA","Dashboard","registerStatus","extend","propsData","$mount","computed","mapState","customIcon","customMessage","changeStatus","err","showError","Date","___CSS_LOADER_EXPORT___","push","module","map","webpackContext","req","webpackContextResolve","__webpack_require__","o","Error","code","keys","Object","resolve","exports","__webpack_module_cache__","moduleId","cachedModule","loaded","__webpack_modules__","call","m","O","result","chunkIds","priority","notFulfilled","Infinity","i","fulfilled","j","every","splice","r","n","getter","__esModule","d","a","definition","defineProperty","enumerable","f","chunkId","Promise","all","reduce","promises","u","g","globalThis","Function","obj","prop","hasOwnProperty","l","done","script","needAttach","scripts","getElementsByTagName","s","getAttribute","createElement","charset","timeout","nc","setAttribute","src","onScriptComplete","prev","event","onerror","onload","doneFns","parentNode","removeChild","forEach","target","head","appendChild","Symbol","toStringTag","value","nmd","paths","children","scriptUrl","importScripts","location","currentScript","replace","p","b","baseURI","self","href","installedChunks","installedChunkData","promise","reject","errorType","realSrc","request","webpackJsonpCallback","parentChunkLoadingFunction","moreModules","runtime","some","chunkLoadingGlobal","__webpack_exports__"],"sourceRoot":""}
\ No newline at end of file
+{"version":3,"file":"user_status-menu.js?v=dc4dcdbcfe4a541313fd","mappings":"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","sources":["webpack:///nextcloud/webpack/runtime/chunk loaded","webpack:///nextcloud/webpack/runtime/load script","webpack:///nextcloud/apps/user_status/src/UserStatus.vue","webpack:///nextcloud/apps/user_status/src/UserStatus.vue?vue&type=script&lang=js","webpack:///nextcloud/apps/user_status/src/services/heartbeatService.js","webpack://nextcloud/./apps/user_status/src/UserStatus.vue?be7c","webpack://nextcloud/./apps/user_status/src/UserStatus.vue?d74a","webpack://nextcloud/./apps/user_status/src/UserStatus.vue?e2b3","webpack:///nextcloud/apps/user_status/src/services/predefinedStatusService.js","webpack:///nextcloud/apps/user_status/src/store/predefinedStatuses.js","webpack:///nextcloud/apps/user_status/src/services/clearAtService.js","webpack:///nextcloud/apps/user_status/src/store/userStatus.js","webpack:///nextcloud/apps/user_status/src/services/statusService.js","webpack:///nextcloud/apps/user_status/src/store/userBackupStatus.js","webpack:///nextcloud/apps/user_status/src/store/index.js","webpack:///nextcloud/apps/user_status/src/menu.js","webpack:///nextcloud/apps/user_status/src/mixins/OnlineStatusMixin.js","webpack:///nextcloud/apps/user_status/src/services/dateService.js","webpack:///nextcloud/apps/user_status/src/UserStatus.vue?vue&type=style&index=0&id=798bb70c&prod&lang=scss&scoped=true","webpack:///nextcloud/node_modules/moment/locale|sync|/^\\.\\/.*$","webpack:///nextcloud/webpack/bootstrap","webpack:///nextcloud/webpack/runtime/compat get default export","webpack:///nextcloud/webpack/runtime/define property getters","webpack:///nextcloud/webpack/runtime/ensure chunk","webpack:///nextcloud/webpack/runtime/get javascript chunk filename","webpack:///nextcloud/webpack/runtime/global","webpack:///nextcloud/webpack/runtime/hasOwnProperty shorthand","webpack:///nextcloud/webpack/runtime/make namespace object","webpack:///nextcloud/webpack/runtime/node module decorator","webpack:///nextcloud/webpack/runtime/runtimeId","webpack:///nextcloud/webpack/runtime/publicPath","webpack:///nextcloud/webpack/runtime/jsonp chunk loading","webpack:///nextcloud/webpack/runtime/nonce","webpack:///nextcloud/webpack/startup"],"sourcesContent":["var deferred = [];\n__webpack_require__.O = (result, chunkIds, fn, priority) => {\n\tif(chunkIds) {\n\t\tpriority = priority || 0;\n\t\tfor(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1];\n\t\tdeferred[i] = [chunkIds, fn, priority];\n\t\treturn;\n\t}\n\tvar notFulfilled = Infinity;\n\tfor (var i = 0; i < deferred.length; i++) {\n\t\tvar chunkIds = deferred[i][0];\n\t\tvar fn = deferred[i][1];\n\t\tvar priority = deferred[i][2];\n\t\tvar fulfilled = true;\n\t\tfor (var j = 0; j < chunkIds.length; j++) {\n\t\t\tif ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) {\n\t\t\t\tchunkIds.splice(j--, 1);\n\t\t\t} else {\n\t\t\t\tfulfilled = false;\n\t\t\t\tif(priority < notFulfilled) notFulfilled = priority;\n\t\t\t}\n\t\t}\n\t\tif(fulfilled) {\n\t\t\tdeferred.splice(i--, 1)\n\t\t\tvar r = fn();\n\t\t\tif (r !== undefined) result = r;\n\t\t}\n\t}\n\treturn result;\n};","var inProgress = {};\nvar dataWebpackPrefix = \"nextcloud:\";\n// loadScript function to load a script via script tag\n__webpack_require__.l = (url, done, key, chunkId) => {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url || s.getAttribute(\"data-webpack\") == dataWebpackPrefix + key) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\n\t\tscript.charset = 'utf-8';\n\t\tscript.timeout = 120;\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\t\tscript.setAttribute(\"data-webpack\", dataWebpackPrefix + key);\n\n\t\tscript.src = url;\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = (prev, event) => {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach((fn) => (fn(event)));\n\t\tif(prev) return prev(event);\n\t}\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","<!--\n  - @copyright Copyright (c) 2020 Georg Ehrke <oc.list@georgehrke.com>\n  - @author Georg Ehrke <oc.list@georgehrke.com>\n  -\n  - @license GNU AGPL version 3 or any later version\n  -\n  - This program is free software: you can redistribute it and/or modify\n  - it under the terms of the GNU Affero General Public License as\n  - published by the Free Software Foundation, either version 3 of the\n  - License, or (at your option) any later version.\n  -\n  - This program is distributed in the hope that it will be useful,\n  - but WITHOUT ANY WARRANTY; without even the implied warranty of\n  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n  - GNU Affero General Public License for more details.\n  -\n  - You should have received a copy of the GNU Affero General Public License\n  - along with this program. If not, see <http://www.gnu.org/licenses/>.\n  -\n  -->\n\n<template>\n\t<component :is=\"inline ? 'div' : 'li'\">\n\t\t<!-- User Status = Status modal toggle -->\n\t\t<button v-if=\"!inline\"\n\t\t\tclass=\"user-status-menu-item\"\n\t\t\t@click.stop=\"openModal\">\n\t\t\t<span aria-hidden=\"true\" :class=\"statusIcon\" class=\"user-status-icon\" />\n\t\t\t{{ visibleMessage }}\n\t\t</button>\n\n\t\t<!-- Dashboard Status -->\n\t\t<NcButton v-else\n\t\t\t:icon=\"statusIcon\"\n\t\t\t@click.stop=\"openModal\">\n\t\t\t<template #icon>\n\t\t\t\t<span aria-hidden=\"true\" :class=\"statusIcon\" class=\"user-status-icon\" />\n\t\t\t</template>\n\t\t\t{{ visibleMessage }}\n\t\t</NcButton>\n\n\t\t<!-- Status management modal -->\n\t\t<SetStatusModal v-if=\"isModalOpen\" @close=\"closeModal\" />\n\t</component>\n</template>\n\n<script>\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport NcButton from '@nextcloud/vue/dist/Components/NcButton.js'\nimport debounce from 'debounce'\n\nimport { sendHeartbeat } from './services/heartbeatService.js'\nimport OnlineStatusMixin from './mixins/OnlineStatusMixin.js'\n\nexport default {\n\tname: 'UserStatus',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tSetStatusModal: () => import(/* webpackChunkName: 'user-status-modal' */'./components/SetStatusModal.vue'),\n\t},\n\tmixins: [OnlineStatusMixin],\n\n\tprops: {\n\t\t/**\n\t\t * Whether the component should be rendered as a Dashboard Status or a User Menu Entries\n\t\t * true = Dashboard Status\n\t\t * false = User Menu Entries\n\t\t */\n\t\tinline: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\theartbeatInterval: null,\n\t\t\tisAway: false,\n\t\t\tisModalOpen: false,\n\t\t\tmouseMoveListener: null,\n\t\t\tsetAwayTimeout: null,\n\t\t}\n\t},\n\n\t/**\n\t * Loads the current user's status from initial state\n\t * and stores it in Vuex\n\t */\n\tmounted() {\n\t\tthis.$store.dispatch('loadStatusFromInitialState')\n\n\t\tif (OC.config.session_keepalive) {\n\t\t\t// Send the latest status to the server every 5 minutes\n\t\t\tthis.heartbeatInterval = setInterval(this._backgroundHeartbeat.bind(this), 1000 * 60 * 5)\n\t\t\tthis.setAwayTimeout = () => {\n\t\t\t\tthis.isAway = true\n\t\t\t}\n\t\t\t// Catch mouse movements, but debounce to once every 30 seconds\n\t\t\tthis.mouseMoveListener = debounce(() => {\n\t\t\t\tconst wasAway = this.isAway\n\t\t\t\tthis.isAway = false\n\t\t\t\t// Reset the two minute counter\n\t\t\t\tclearTimeout(this.setAwayTimeout)\n\t\t\t\t// If the user did not move the mouse within two minutes,\n\t\t\t\t// mark them as away\n\t\t\t\tsetTimeout(this.setAwayTimeout, 1000 * 60 * 2)\n\n\t\t\t\tif (wasAway) {\n\t\t\t\t\tthis._backgroundHeartbeat()\n\t\t\t\t}\n\t\t\t}, 1000 * 2, true)\n\t\t\twindow.addEventListener('mousemove', this.mouseMoveListener, {\n\t\t\t\tcapture: true,\n\t\t\t\tpassive: true,\n\t\t\t})\n\n\t\t\tthis._backgroundHeartbeat()\n\t\t}\n\t\tsubscribe('user_status:status.updated', this.handleUserStatusUpdated)\n\t},\n\n\t/**\n\t * Some housekeeping before destroying the component\n\t */\n\tbeforeDestroy() {\n\t\twindow.removeEventListener('mouseMove', this.mouseMoveListener)\n\t\tclearInterval(this.heartbeatInterval)\n\t\tunsubscribe('user_status:status.updated', this.handleUserStatusUpdated)\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Opens the modal to set a custom status\n\t\t */\n\t\topenModal() {\n\t\t\tthis.isModalOpen = true\n\t\t},\n\t\t/**\n\t\t * Closes the modal\n\t\t */\n\t\tcloseModal() {\n\t\t\tthis.isModalOpen = false\n\t\t},\n\n\t\t/**\n\t\t * Sends the status heartbeat to the server\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @private\n\t\t */\n\t\tasync _backgroundHeartbeat() {\n\t\t\ttry {\n\t\t\t\tconst status = await sendHeartbeat(this.isAway)\n\t\t\t\tif (status?.userId) {\n\t\t\t\t\tthis.$store.dispatch('setStatusFromHeartbeat', status)\n\t\t\t\t} else {\n\t\t\t\t\tawait this.$store.dispatch('reFetchStatusFromServer')\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.debug('Failed sending heartbeat, got: ' + error.response?.status)\n\t\t\t}\n\t\t},\n\t\thandleUserStatusUpdated(state) {\n\t\t\tif (OC.getCurrentUser().uid === state.userId) {\n\t\t\t\tthis.$store.dispatch('setStatusFromObject', {\n\t\t\t\t\tstatus: state.status,\n\t\t\t\t\ticon: state.icon,\n\t\t\t\t\tmessage: state.message,\n\t\t\t\t})\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.user-status-menu-item {\n\twidth: auto;\n\tmin-width: 44px;\n\theight: 44px;\n\tmargin: 0;\n\tborder: 0;\n\tborder-radius: var(--border-radius-pill);\n\tbackground-color: var(--color-main-background-blur);\n\tfont-size: inherit;\n\tfont-weight: normal;\n\n\t-webkit-backdrop-filter: var(--background-blur);\n\tbackdrop-filter: var(--background-blur);\n\n\t&:active,\n\t&:hover,\n\t&:focus-visible {\n\t\tbackground-color: var(--color-background-hover);\n\t}\n\t&:focus-visible {\n\t\tbox-shadow: 0 0 0 4px var(--color-main-background) !important;\n\t\toutline: 2px solid var(--color-main-text) !important;\n\t}\n}\n\n.user-status-icon {\n\twidth: 16px;\n\theight: 16px;\n\tmargin-right: 10px;\n\topacity: 1 !important;\n\tbackground-size: 16px;\n\tvertical-align: middle !important;\n}\n</style>\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./UserStatus.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./UserStatus.vue?vue&type=script&lang=js\"","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport HttpClient from '@nextcloud/axios'\nimport { generateOcsUrl } from '@nextcloud/router'\n\n/**\n * Sends a heartbeat\n *\n * @param {boolean} isAway Whether or not the user is active\n * @return {Promise<void>}\n */\nconst sendHeartbeat = async (isAway) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/heartbeat?format=json')\n\tconst response = await HttpClient.put(url, {\n\t\tstatus: isAway ? 'away' : 'online',\n\t})\n\treturn response.data.ocs.data\n}\n\nexport {\n\tsendHeartbeat,\n}\n","\n      import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n      import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n      import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n      import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n      import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n      import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n      import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/sass-loader/dist/cjs.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./UserStatus.vue?vue&type=style&index=0&id=798bb70c&prod&lang=scss&scoped=true\";\n      \n      \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\n\n      options.insert = insertFn.bind(null, \"head\");\n    \noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/sass-loader/dist/cjs.js!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./UserStatus.vue?vue&type=style&index=0&id=798bb70c&prod&lang=scss&scoped=true\";\n       export default content && content.locals ? content.locals : undefined;\n","import { render, staticRenderFns } from \"./UserStatus.vue?vue&type=template&id=798bb70c&scoped=true\"\nimport script from \"./UserStatus.vue?vue&type=script&lang=js\"\nexport * from \"./UserStatus.vue?vue&type=script&lang=js\"\nimport style0 from \"./UserStatus.vue?vue&type=style&index=0&id=798bb70c&prod&lang=scss&scoped=true\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n  script,\n  render,\n  staticRenderFns,\n  false,\n  null,\n  \"798bb70c\",\n  null\n  \n)\n\nexport default component.exports","var render = function render(){var _vm=this,_c=_vm._self._c;return _c(_vm.inline ? 'div' : 'li',{tag:\"component\"},[(!_vm.inline)?_c('button',{staticClass:\"user-status-menu-item\",on:{\"click\":function($event){$event.stopPropagation();return _vm.openModal.apply(null, arguments)}}},[_c('span',{staticClass:\"user-status-icon\",class:_vm.statusIcon,attrs:{\"aria-hidden\":\"true\"}}),_vm._v(\"\\n\\t\\t\"+_vm._s(_vm.visibleMessage)+\"\\n\\t\")]):_c('NcButton',{attrs:{\"icon\":_vm.statusIcon},on:{\"click\":function($event){$event.stopPropagation();return _vm.openModal.apply(null, arguments)}},scopedSlots:_vm._u([{key:\"icon\",fn:function(){return [_c('span',{staticClass:\"user-status-icon\",class:_vm.statusIcon,attrs:{\"aria-hidden\":\"true\"}})]},proxy:true}])},[_vm._v(\"\\n\\t\\t\"+_vm._s(_vm.visibleMessage)+\"\\n\\t\")]),_vm._v(\" \"),(_vm.isModalOpen)?_c('SetStatusModal',{on:{\"close\":_vm.closeModal}}):_vm._e()],1)\n}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport HttpClient from '@nextcloud/axios'\nimport { generateOcsUrl } from '@nextcloud/router'\n\n/**\n * Fetches all predefined statuses from the server\n *\n * @return {Promise<void>}\n */\nconst fetchAllPredefinedStatuses = async () => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/predefined_statuses?format=json')\n\tconst response = await HttpClient.get(url)\n\n\treturn response.data.ocs.data\n}\n\nexport {\n\tfetchAllPredefinedStatuses,\n}\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport { fetchAllPredefinedStatuses } from '../services/predefinedStatusService.js'\n\nconst state = {\n\tpredefinedStatuses: [],\n}\n\nconst mutations = {\n\n\t/**\n\t * Adds a predefined status to the state\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} status The status to add\n\t */\n\taddPredefinedStatus(state, status) {\n\t\tstate.predefinedStatuses = [...state.predefinedStatuses, status]\n\t},\n}\n\nconst getters = {\n\tstatusesHaveLoaded(state) {\n\t\treturn state.predefinedStatuses.length > 0\n\t},\n}\n\nconst actions = {\n\n\t/**\n\t * Loads all predefined statuses from the server\n\t *\n\t * @param {object} vuex The Vuex components\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state -\n\t */\n\tasync loadAllPredefinedStatuses({ state, commit }) {\n\t\tif (state.predefinedStatuses.length > 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst statuses = await fetchAllPredefinedStatuses()\n\t\tfor (const status of statuses) {\n\t\t\tcommit('addPredefinedStatus', status)\n\t\t}\n\t},\n\n}\n\nexport default { state, mutations, getters, actions }\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport {\n\tdateFactory,\n} from './dateService.js'\nimport moment from '@nextcloud/moment'\n\n/**\n * Calculates the actual clearAt timestamp\n *\n * @param {object | null} clearAt The clear-at config\n * @return {number | null}\n */\nconst getTimestampForClearAt = (clearAt) => {\n\tif (clearAt === null) {\n\t\treturn null\n\t}\n\n\tconst date = dateFactory()\n\n\tif (clearAt.type === 'period') {\n\t\tdate.setSeconds(date.getSeconds() + clearAt.time)\n\t\treturn Math.floor(date.getTime() / 1000)\n\t}\n\tif (clearAt.type === 'end-of') {\n\t\tswitch (clearAt.time) {\n\t\tcase 'day':\n\t\tcase 'week':\n\t\t\treturn Number(moment(date).endOf(clearAt.time).format('X'))\n\t\t}\n\t}\n\t// This is not an officially supported type\n\t// but only used internally to show the remaining time\n\t// in the Set Status Modal\n\tif (clearAt.type === '_time') {\n\t\treturn clearAt.time\n\t}\n\n\treturn null\n}\n\nexport {\n\tgetTimestampForClearAt,\n}\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport {\n\tfetchCurrentStatus,\n\tsetStatus,\n\tsetPredefinedMessage,\n\tsetCustomMessage,\n\tclearMessage,\n} from '../services/statusService.js'\nimport { loadState } from '@nextcloud/initial-state'\nimport { getCurrentUser } from '@nextcloud/auth'\nimport { getTimestampForClearAt } from '../services/clearAtService.js'\nimport { emit } from '@nextcloud/event-bus'\n\nconst state = {\n\t// Status (online / away / dnd / invisible / offline)\n\tstatus: null,\n\t// Whether the status is user-defined\n\tstatusIsUserDefined: null,\n\t// A custom message set by the user\n\tmessage: null,\n\t// The icon selected by the user\n\ticon: null,\n\t// When to automatically clean the status\n\tclearAt: null,\n\t// Whether the message is predefined\n\t// (and can automatically be translated by Nextcloud)\n\tmessageIsPredefined: null,\n\t// The id of the message in case it's predefined\n\tmessageId: null,\n}\n\nconst mutations = {\n\n\t/**\n\t * Sets a new status\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} data The destructuring object\n\t * @param {string} data.statusType The new status type\n\t */\n\tsetStatus(state, { statusType }) {\n\t\tstate.status = statusType\n\t\tstate.statusIsUserDefined = true\n\t},\n\n\t/**\n\t * Sets a message using a predefined message\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} data The destructuring object\n\t * @param {string} data.messageId The messageId\n\t * @param {number | null} data.clearAt When to automatically clear the status\n\t * @param {string} data.message The message\n\t * @param {string} data.icon The icon\n\t */\n\tsetPredefinedMessage(state, { messageId, clearAt, message, icon }) {\n\t\tstate.messageId = messageId\n\t\tstate.messageIsPredefined = true\n\n\t\tstate.message = message\n\t\tstate.icon = icon\n\t\tstate.clearAt = clearAt\n\t},\n\n\t/**\n\t * Sets a custom message\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} data The destructuring object\n\t * @param {string} data.message The message\n\t * @param {string} data.icon The icon\n\t * @param {number} data.clearAt When to automatically clear the status\n\t */\n\tsetCustomMessage(state, { message, icon, clearAt }) {\n\t\tstate.messageId = null\n\t\tstate.messageIsPredefined = false\n\n\t\tstate.message = message\n\t\tstate.icon = icon\n\t\tstate.clearAt = clearAt\n\t},\n\n\t/**\n\t * Clears the status\n\t *\n\t * @param {object} state The Vuex state\n\t */\n\tclearMessage(state) {\n\t\tstate.messageId = null\n\t\tstate.messageIsPredefined = false\n\n\t\tstate.message = null\n\t\tstate.icon = null\n\t\tstate.clearAt = null\n\t},\n\n\t/**\n\t * Loads the status from initial state\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} data The destructuring object\n\t * @param {string} data.status The status type\n\t * @param {boolean} data.statusIsUserDefined Whether or not this status is user-defined\n\t * @param {string} data.message The message\n\t * @param {string} data.icon The icon\n\t * @param {number} data.clearAt When to automatically clear the status\n\t * @param {boolean} data.messageIsPredefined Whether or not the message is predefined\n\t * @param {string} data.messageId The id of the predefined message\n\t */\n\tloadStatusFromServer(state, { status, statusIsUserDefined, message, icon, clearAt, messageIsPredefined, messageId }) {\n\t\tstate.status = status\n\t\tstate.message = message\n\t\tstate.icon = icon\n\n\t\t// Don't overwrite certain values if the refreshing comes in via short updates\n\t\t// E.g. from talk participant list which only has the status, message and icon\n\t\tif (typeof statusIsUserDefined !== 'undefined') {\n\t\t\tstate.statusIsUserDefined = statusIsUserDefined\n\t\t}\n\t\tif (typeof clearAt !== 'undefined') {\n\t\t\tstate.clearAt = clearAt\n\t\t}\n\t\tif (typeof messageIsPredefined !== 'undefined') {\n\t\t\tstate.messageIsPredefined = messageIsPredefined\n\t\t}\n\t\tif (typeof messageId !== 'undefined') {\n\t\t\tstate.messageId = messageId\n\t\t}\n\t},\n}\n\nconst getters = {}\n\nconst actions = {\n\n\t/**\n\t * Sets a new status\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state The Vuex state object\n\t * @param {object} data The data destructuring object\n\t * @param {string} data.statusType The new status type\n\t * @return {Promise<void>}\n\t */\n\tasync setStatus({ commit, state }, { statusType }) {\n\t\tawait setStatus(statusType)\n\t\tcommit('setStatus', { statusType })\n\t\temit('user_status:status.updated', {\n\t\t\tstatus: state.status,\n\t\t\tmessage: state.message,\n\t\t\ticon: state.icon,\n\t\t\tclearAt: state.clearAt,\n\t\t\tuserId: getCurrentUser()?.uid,\n\t\t})\n\t},\n\n\t/**\n\t * Update status from 'user_status:status.updated' update.\n\t * This doesn't trigger another 'user_status:status.updated'\n\t * event.\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state The Vuex state object\n\t * @param {string} status The new status\n\t * @return {Promise<void>}\n\t */\n\tasync setStatusFromObject({ commit, state }, status) {\n\t\tcommit('loadStatusFromServer', status)\n\t},\n\n\t/**\n\t * Sets a message using a predefined message\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state The Vuex state object\n\t * @param {object} vuex.rootState The Vuex root state\n\t * @param {object} data The data destructuring object\n\t * @param {string} data.messageId The messageId\n\t * @param {object | null} data.clearAt When to automatically clear the status\n\t * @return {Promise<void>}\n\t */\n\tasync setPredefinedMessage({ commit, rootState, state }, { messageId, clearAt }) {\n\t\tconst resolvedClearAt = getTimestampForClearAt(clearAt)\n\n\t\tawait setPredefinedMessage(messageId, resolvedClearAt)\n\t\tconst status = rootState.predefinedStatuses.predefinedStatuses.find((status) => status.id === messageId)\n\t\tconst { message, icon } = status\n\n\t\tcommit('setPredefinedMessage', { messageId, clearAt: resolvedClearAt, message, icon })\n\t\temit('user_status:status.updated', {\n\t\t\tstatus: state.status,\n\t\t\tmessage: state.message,\n\t\t\ticon: state.icon,\n\t\t\tclearAt: state.clearAt,\n\t\t\tuserId: getCurrentUser()?.uid,\n\t\t})\n\t},\n\n\t/**\n\t * Sets a custom message\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state The Vuex state object\n\t * @param {object} data The data destructuring object\n\t * @param {string} data.message The message\n\t * @param {string} data.icon The icon\n\t * @param {object | null} data.clearAt When to automatically clear the status\n\t * @return {Promise<void>}\n\t */\n\tasync setCustomMessage({ commit, state }, { message, icon, clearAt }) {\n\t\tconst resolvedClearAt = getTimestampForClearAt(clearAt)\n\n\t\tawait setCustomMessage(message, icon, resolvedClearAt)\n\t\tcommit('setCustomMessage', { message, icon, clearAt: resolvedClearAt })\n\t\temit('user_status:status.updated', {\n\t\t\tstatus: state.status,\n\t\t\tmessage: state.message,\n\t\t\ticon: state.icon,\n\t\t\tclearAt: state.clearAt,\n\t\t\tuserId: getCurrentUser()?.uid,\n\t\t})\n\t},\n\n\t/**\n\t * Clears the status\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} vuex.state The Vuex state object\n\t * @return {Promise<void>}\n\t */\n\tasync clearMessage({ commit, state }) {\n\t\tawait clearMessage()\n\t\tcommit('clearMessage')\n\t\temit('user_status:status.updated', {\n\t\t\tstatus: state.status,\n\t\t\tmessage: state.message,\n\t\t\ticon: state.icon,\n\t\t\tclearAt: state.clearAt,\n\t\t\tuserId: getCurrentUser()?.uid,\n\t\t})\n\t},\n\n\t/**\n\t * Re-fetches the status from the server\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @return {Promise<void>}\n\t */\n\tasync reFetchStatusFromServer({ commit }) {\n\t\tconst status = await fetchCurrentStatus()\n\t\tcommit('loadStatusFromServer', status)\n\t},\n\n\t/**\n\t * Stores the status we got in the reply of the heartbeat\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @param {object} status The data destructuring object\n\t * @param {string} status.status The status type\n\t * @param {boolean} status.statusIsUserDefined Whether or not this status is user-defined\n\t * @param {string} status.message The message\n\t * @param {string} status.icon The icon\n\t * @param {number} status.clearAt When to automatically clear the status\n\t * @param {boolean} status.messageIsPredefined Whether or not the message is predefined\n\t * @param {string} status.messageId The id of the predefined message\n\t * @return {Promise<void>}\n\t */\n\tasync setStatusFromHeartbeat({ commit }, status) {\n\t\tcommit('loadStatusFromServer', status)\n\t},\n\n\t/**\n\t * Loads the server from the initial state\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t */\n\tloadStatusFromInitialState({ commit }) {\n\t\tconst status = loadState('user_status', 'status')\n\t\tcommit('loadStatusFromServer', status)\n\t},\n}\n\nexport default { state, mutations, getters, actions }\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport HttpClient from '@nextcloud/axios'\nimport { generateOcsUrl } from '@nextcloud/router'\n\n/**\n * Fetches the current user-status\n *\n * @return {Promise<object>}\n */\nconst fetchCurrentStatus = async () => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status')\n\tconst response = await HttpClient.get(url)\n\n\treturn response.data.ocs.data\n}\n\n/**\n * Fetches the current user-status\n *\n * @param {string} userId\n * @return {Promise<object>}\n */\nconst fetchBackupStatus = async (userId) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/statuses/{userId}', { userId: '_' + userId })\n\tconst response = await HttpClient.get(url)\n\n\treturn response.data.ocs.data\n}\n\n/**\n * Sets the status\n *\n * @param {string} statusType The status (online / away / dnd / invisible)\n * @return {Promise<void>}\n */\nconst setStatus = async (statusType) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status/status')\n\tawait HttpClient.put(url, {\n\t\tstatusType,\n\t})\n}\n\n/**\n * Sets a message based on our predefined statuses\n *\n * @param {string} messageId The id of the message, taken from predefined status service\n * @param {number | null} clearAt When to automatically clean the status\n * @return {Promise<void>}\n */\nconst setPredefinedMessage = async (messageId, clearAt = null) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status/message/predefined?format=json')\n\tawait HttpClient.put(url, {\n\t\tmessageId,\n\t\tclearAt,\n\t})\n}\n\n/**\n * Sets a custom message\n *\n * @param {string} message The user-defined message\n * @param {string | null} statusIcon The user-defined icon\n * @param {number | null} clearAt When to automatically clean the status\n * @return {Promise<void>}\n */\nconst setCustomMessage = async (message, statusIcon = null, clearAt = null) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status/message/custom?format=json')\n\tawait HttpClient.put(url, {\n\t\tmessage,\n\t\tstatusIcon,\n\t\tclearAt,\n\t})\n}\n\n/**\n * Clears the current status of the user\n *\n * @return {Promise<void>}\n */\nconst clearMessage = async () => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status/message?format=json')\n\tawait HttpClient.delete(url)\n}\n\n/**\n * Revert the automated status\n *\n * @param {string} messageId\n * @return {Promise<object>}\n */\nconst revertToBackupStatus = async (messageId) => {\n\tconst url = generateOcsUrl('apps/user_status/api/v1/user_status/revert/{messageId}', { messageId })\n\tconst response = await HttpClient.delete(url)\n\n\treturn response.data.ocs.data\n}\n\nexport {\n\tfetchCurrentStatus,\n\tfetchBackupStatus,\n\tsetStatus,\n\tsetCustomMessage,\n\tsetPredefinedMessage,\n\tclearMessage,\n\trevertToBackupStatus,\n}\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n * @author Joas Schilling <coding@schilljs.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport {\n\tfetchBackupStatus,\n\trevertToBackupStatus,\n} from '../services/statusService.js'\nimport { getCurrentUser } from '@nextcloud/auth'\nimport { emit } from '@nextcloud/event-bus'\n\nconst state = {\n\t// Status (online / away / dnd / invisible / offline)\n\tstatus: null,\n\t// Whether the status is user-defined\n\tstatusIsUserDefined: null,\n\t// A custom message set by the user\n\tmessage: null,\n\t// The icon selected by the user\n\ticon: null,\n\t// When to automatically clean the status\n\tclearAt: null,\n\t// Whether the message is predefined\n\t// (and can automatically be translated by Nextcloud)\n\tmessageIsPredefined: null,\n\t// The id of the message in case it's predefined\n\tmessageId: null,\n}\n\nconst mutations = {\n\t/**\n\t * Loads the status from initial state\n\t *\n\t * @param {object} state The Vuex state\n\t * @param {object} data The destructuring object\n\t * @param {string} data.status The status type\n\t * @param {boolean} data.statusIsUserDefined Whether or not this status is user-defined\n\t * @param {string} data.message The message\n\t * @param {string} data.icon The icon\n\t * @param {number} data.clearAt When to automatically clear the status\n\t * @param {boolean} data.messageIsPredefined Whether or not the message is predefined\n\t * @param {string} data.messageId The id of the predefined message\n\t */\n\tloadBackupStatusFromServer(state, { status, statusIsUserDefined, message, icon, clearAt, messageIsPredefined, messageId }) {\n\t\tstate.status = status\n\t\tstate.message = message\n\t\tstate.icon = icon\n\n\t\t// Don't overwrite certain values if the refreshing comes in via short updates\n\t\t// E.g. from talk participant list which only has the status, message and icon\n\t\tif (typeof statusIsUserDefined !== 'undefined') {\n\t\t\tstate.statusIsUserDefined = statusIsUserDefined\n\t\t}\n\t\tif (typeof clearAt !== 'undefined') {\n\t\t\tstate.clearAt = clearAt\n\t\t}\n\t\tif (typeof messageIsPredefined !== 'undefined') {\n\t\t\tstate.messageIsPredefined = messageIsPredefined\n\t\t}\n\t\tif (typeof messageId !== 'undefined') {\n\t\t\tstate.messageId = messageId\n\t\t}\n\t},\n}\n\nconst getters = {}\n\nconst actions = {\n\t/**\n\t * Re-fetches the status from the server\n\t *\n\t * @param {object} vuex The Vuex destructuring object\n\t * @param {Function} vuex.commit The Vuex commit function\n\t * @return {Promise<void>}\n\t */\n\tasync fetchBackupFromServer({ commit }) {\n\t\ttry {\n\t\t\tconst status = await fetchBackupStatus(getCurrentUser()?.uid)\n\t\t\tcommit('loadBackupStatusFromServer', status)\n\t\t} catch (e) {\n\t\t\t// Ignore missing user backup status\n\t\t}\n\t},\n\n\tasync revertBackupFromServer({ commit }, { messageId }) {\n\t\tconst status = await revertToBackupStatus(messageId)\n\t\tif (status) {\n\t\t\tcommit('loadBackupStatusFromServer', {})\n\t\t\tcommit('loadStatusFromServer', status)\n\t\t\temit('user_status:status.updated', {\n\t\t\t\tstatus: status.status,\n\t\t\t\tmessage: status.message,\n\t\t\t\ticon: status.icon,\n\t\t\t\tclearAt: status.clearAt,\n\t\t\t\tuserId: getCurrentUser()?.uid,\n\t\t\t})\n\t\t}\n\t},\n}\n\nexport default { state, mutations, getters, actions }\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport Vue from 'vue'\nimport Vuex, { Store } from 'vuex'\nimport predefinedStatuses from './predefinedStatuses.js'\nimport userStatus from './userStatus.js'\nimport userBackupStatus from './userBackupStatus.js'\n\nVue.use(Vuex)\n\nexport default new Store({\n\tmodules: {\n\t\tpredefinedStatuses,\n\t\tuserStatus,\n\t\tuserBackupStatus,\n\t},\n\tstrict: true,\n})\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n * @author John Molakvoæ <skjnldsv@protonmail.com>\n * @author Julius Härtl <jus@bitgrid.net>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport Vue from 'vue'\nimport { getRequestToken } from '@nextcloud/auth'\nimport { subscribe } from '@nextcloud/event-bus'\n\nimport UserStatus from './UserStatus.vue'\n\nimport store from './store/index.js'\n\n// eslint-disable-next-line camelcase\n__webpack_nonce__ = btoa(getRequestToken())\n\nVue.prototype.t = t\nVue.prototype.$t = t\n\nconst mountPoint = document.getElementById('user_status-menu-entry')\n\nconst mountMenuEntry = () => {\n\tconst mountPoint = document.getElementById('user_status-menu-entry')\n\t// eslint-disable-next-line no-new\n\tnew Vue({\n\t\tel: mountPoint,\n\t\trender: h => h(UserStatus),\n\t\tstore,\n\t})\n}\n\nif (mountPoint) {\n\tmountMenuEntry()\n} else {\n\tsubscribe('core:user-menu:mounted', mountMenuEntry)\n}\n\n// Register dashboard status\ndocument.addEventListener('DOMContentLoaded', function() {\n\tif (!OCA.Dashboard) {\n\t\treturn\n\t}\n\n\tOCA.Dashboard.registerStatus('status', (el) => {\n\t\tconst Dashboard = Vue.extend(UserStatus)\n\t\treturn new Dashboard({\n\t\t\tpropsData: {\n\t\t\t\tinline: true,\n\t\t\t},\n\t\t\tstore,\n\t\t}).$mount(el)\n\t})\n})\n","/**\n * @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>\n *\n * @author John Molakvoæ <skjnldsv@protonmail.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport { mapState } from 'vuex'\nimport { showError } from '@nextcloud/dialogs'\n\nexport default {\n\tcomputed: {\n\t\t...mapState({\n\t\t\tstatusType: state => state.userStatus.status,\n\t\t\tstatusIsUserDefined: state => state.userStatus.statusIsUserDefined,\n\t\t\tcustomIcon: state => state.userStatus.icon,\n\t\t\tcustomMessage: state => state.userStatus.message,\n\t\t}),\n\n\t\t/**\n\t\t * The message displayed in the top right corner\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tvisibleMessage() {\n\t\t\tif (this.customIcon && this.customMessage) {\n\t\t\t\treturn `${this.customIcon} ${this.customMessage}`\n\t\t\t}\n\n\t\t\tif (this.customMessage) {\n\t\t\t\treturn this.customMessage\n\t\t\t}\n\n\t\t\tif (this.statusIsUserDefined) {\n\t\t\t\tswitch (this.statusType) {\n\t\t\t\tcase 'online':\n\t\t\t\t\treturn this.$t('user_status', 'Online')\n\n\t\t\t\tcase 'away':\n\t\t\t\tcase 'busy':\n\t\t\t\t\treturn this.$t('user_status', 'Away')\n\n\t\t\t\tcase 'dnd':\n\t\t\t\t\treturn this.$t('user_status', 'Do not disturb')\n\n\t\t\t\tcase 'invisible':\n\t\t\t\t\treturn this.$t('user_status', 'Invisible')\n\n\t\t\t\tcase 'offline':\n\t\t\t\t\treturn this.$t('user_status', 'Offline')\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.$t('user_status', 'Set status')\n\t\t},\n\n\t\t/**\n\t\t * The status indicator icon\n\t\t *\n\t\t * @return {string | null}\n\t\t */\n\t\tstatusIcon() {\n\t\t\tswitch (this.statusType) {\n\t\t\tcase 'online':\n\t\t\t\treturn 'icon-user-status-online'\n\n\t\t\tcase 'away':\n\t\t\tcase 'busy':\n\t\t\t\treturn 'icon-user-status-away'\n\n\t\t\tcase 'dnd':\n\t\t\t\treturn 'icon-user-status-dnd'\n\n\t\t\tcase 'invisible':\n\t\t\tcase 'offline':\n\t\t\t\treturn 'icon-user-status-invisible'\n\t\t\t}\n\n\t\t\treturn ''\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Changes the user-status\n\t\t *\n\t\t * @param {string} statusType (online / away / dnd / invisible)\n\t\t */\n\t\tasync changeStatus(statusType) {\n\t\t\ttry {\n\t\t\t\tawait this.$store.dispatch('setStatus', { statusType })\n\t\t\t} catch (err) {\n\t\t\t\tshowError(this.$t('user_status', 'There was an error saving the new status'))\n\t\t\t\tconsole.debug(err)\n\t\t\t}\n\t\t},\n\t},\n}\n","/**\n * @copyright Copyright (c) 2020 Georg Ehrke\n *\n * @author Georg Ehrke <oc.list@georgehrke.com>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nconst dateFactory = () => {\n\treturn new Date()\n}\n\nexport {\n\tdateFactory,\n}\n","// Imports\nimport ___CSS_LOADER_API_SOURCEMAP_IMPORT___ from \"../../../node_modules/css-loader/dist/runtime/sourceMaps.js\";\nimport ___CSS_LOADER_API_IMPORT___ from \"../../../node_modules/css-loader/dist/runtime/api.js\";\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `.user-status-menu-item[data-v-798bb70c]{width:auto;min-width:44px;height:44px;margin:0;border:0;border-radius:var(--border-radius-pill);background-color:var(--color-main-background-blur);font-size:inherit;font-weight:normal;-webkit-backdrop-filter:var(--background-blur);backdrop-filter:var(--background-blur)}.user-status-menu-item[data-v-798bb70c]:active,.user-status-menu-item[data-v-798bb70c]:hover,.user-status-menu-item[data-v-798bb70c]:focus-visible{background-color:var(--color-background-hover)}.user-status-menu-item[data-v-798bb70c]:focus-visible{box-shadow:0 0 0 4px var(--color-main-background) !important;outline:2px solid var(--color-main-text) !important}.user-status-icon[data-v-798bb70c]{width:16px;height:16px;margin-right:10px;opacity:1 !important;background-size:16px;vertical-align:middle !important}`, \"\",{\"version\":3,\"sources\":[\"webpack://./apps/user_status/src/UserStatus.vue\"],\"names\":[],\"mappings\":\"AACA,wCACC,UAAA,CACA,cAAA,CACA,WAAA,CACA,QAAA,CACA,QAAA,CACA,uCAAA,CACA,kDAAA,CACA,iBAAA,CACA,kBAAA,CAEA,8CAAA,CACA,sCAAA,CAEA,mJAGC,8CAAA,CAED,sDACC,4DAAA,CACA,mDAAA,CAIF,mCACC,UAAA,CACA,WAAA,CACA,iBAAA,CACA,oBAAA,CACA,oBAAA,CACA,gCAAA\",\"sourcesContent\":[\"\\n.user-status-menu-item {\\n\\twidth: auto;\\n\\tmin-width: 44px;\\n\\theight: 44px;\\n\\tmargin: 0;\\n\\tborder: 0;\\n\\tborder-radius: var(--border-radius-pill);\\n\\tbackground-color: var(--color-main-background-blur);\\n\\tfont-size: inherit;\\n\\tfont-weight: normal;\\n\\n\\t-webkit-backdrop-filter: var(--background-blur);\\n\\tbackdrop-filter: var(--background-blur);\\n\\n\\t&:active,\\n\\t&:hover,\\n\\t&:focus-visible {\\n\\t\\tbackground-color: var(--color-background-hover);\\n\\t}\\n\\t&:focus-visible {\\n\\t\\tbox-shadow: 0 0 0 4px var(--color-main-background) !important;\\n\\t\\toutline: 2px solid var(--color-main-text) !important;\\n\\t}\\n}\\n\\n.user-status-icon {\\n\\twidth: 16px;\\n\\theight: 16px;\\n\\tmargin-right: 10px;\\n\\topacity: 1 !important;\\n\\tbackground-size: 16px;\\n\\tvertical-align: middle !important;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\nexport default ___CSS_LOADER_EXPORT___;\n","var map = {\n\t\"./af\": 42786,\n\t\"./af.js\": 42786,\n\t\"./ar\": 30867,\n\t\"./ar-dz\": 14130,\n\t\"./ar-dz.js\": 14130,\n\t\"./ar-kw\": 96135,\n\t\"./ar-kw.js\": 96135,\n\t\"./ar-ly\": 56440,\n\t\"./ar-ly.js\": 56440,\n\t\"./ar-ma\": 47702,\n\t\"./ar-ma.js\": 47702,\n\t\"./ar-sa\": 16040,\n\t\"./ar-sa.js\": 16040,\n\t\"./ar-tn\": 37100,\n\t\"./ar-tn.js\": 37100,\n\t\"./ar.js\": 30867,\n\t\"./az\": 31083,\n\t\"./az.js\": 31083,\n\t\"./be\": 9808,\n\t\"./be.js\": 9808,\n\t\"./bg\": 68338,\n\t\"./bg.js\": 68338,\n\t\"./bm\": 67438,\n\t\"./bm.js\": 67438,\n\t\"./bn\": 8905,\n\t\"./bn-bd\": 76225,\n\t\"./bn-bd.js\": 76225,\n\t\"./bn.js\": 8905,\n\t\"./bo\": 11560,\n\t\"./bo.js\": 11560,\n\t\"./br\": 1278,\n\t\"./br.js\": 1278,\n\t\"./bs\": 80622,\n\t\"./bs.js\": 80622,\n\t\"./ca\": 2468,\n\t\"./ca.js\": 2468,\n\t\"./cs\": 5822,\n\t\"./cs.js\": 5822,\n\t\"./cv\": 50877,\n\t\"./cv.js\": 50877,\n\t\"./cy\": 47373,\n\t\"./cy.js\": 47373,\n\t\"./da\": 24780,\n\t\"./da.js\": 24780,\n\t\"./de\": 59740,\n\t\"./de-at\": 60217,\n\t\"./de-at.js\": 60217,\n\t\"./de-ch\": 60894,\n\t\"./de-ch.js\": 60894,\n\t\"./de.js\": 59740,\n\t\"./dv\": 5300,\n\t\"./dv.js\": 5300,\n\t\"./el\": 50837,\n\t\"./el.js\": 50837,\n\t\"./en-au\": 78348,\n\t\"./en-au.js\": 78348,\n\t\"./en-ca\": 77925,\n\t\"./en-ca.js\": 77925,\n\t\"./en-gb\": 22243,\n\t\"./en-gb.js\": 22243,\n\t\"./en-ie\": 46436,\n\t\"./en-ie.js\": 46436,\n\t\"./en-il\": 47207,\n\t\"./en-il.js\": 47207,\n\t\"./en-in\": 44175,\n\t\"./en-in.js\": 44175,\n\t\"./en-nz\": 76319,\n\t\"./en-nz.js\": 76319,\n\t\"./en-sg\": 31662,\n\t\"./en-sg.js\": 31662,\n\t\"./eo\": 92915,\n\t\"./eo.js\": 92915,\n\t\"./es\": 55655,\n\t\"./es-do\": 55251,\n\t\"./es-do.js\": 55251,\n\t\"./es-mx\": 96112,\n\t\"./es-mx.js\": 96112,\n\t\"./es-us\": 71146,\n\t\"./es-us.js\": 71146,\n\t\"./es.js\": 55655,\n\t\"./et\": 5603,\n\t\"./et.js\": 5603,\n\t\"./eu\": 77763,\n\t\"./eu.js\": 77763,\n\t\"./fa\": 76959,\n\t\"./fa.js\": 76959,\n\t\"./fi\": 11897,\n\t\"./fi.js\": 11897,\n\t\"./fil\": 42549,\n\t\"./fil.js\": 42549,\n\t\"./fo\": 94694,\n\t\"./fo.js\": 94694,\n\t\"./fr\": 94470,\n\t\"./fr-ca\": 63049,\n\t\"./fr-ca.js\": 63049,\n\t\"./fr-ch\": 52330,\n\t\"./fr-ch.js\": 52330,\n\t\"./fr.js\": 94470,\n\t\"./fy\": 5044,\n\t\"./fy.js\": 5044,\n\t\"./ga\": 29295,\n\t\"./ga.js\": 29295,\n\t\"./gd\": 2101,\n\t\"./gd.js\": 2101,\n\t\"./gl\": 38794,\n\t\"./gl.js\": 38794,\n\t\"./gom-deva\": 27884,\n\t\"./gom-deva.js\": 27884,\n\t\"./gom-latn\": 23168,\n\t\"./gom-latn.js\": 23168,\n\t\"./gu\": 95349,\n\t\"./gu.js\": 95349,\n\t\"./he\": 24206,\n\t\"./he.js\": 24206,\n\t\"./hi\": 30094,\n\t\"./hi.js\": 30094,\n\t\"./hr\": 30316,\n\t\"./hr.js\": 30316,\n\t\"./hu\": 22138,\n\t\"./hu.js\": 22138,\n\t\"./hy-am\": 11423,\n\t\"./hy-am.js\": 11423,\n\t\"./id\": 29218,\n\t\"./id.js\": 29218,\n\t\"./is\": 90135,\n\t\"./is.js\": 90135,\n\t\"./it\": 90626,\n\t\"./it-ch\": 10150,\n\t\"./it-ch.js\": 10150,\n\t\"./it.js\": 90626,\n\t\"./ja\": 39183,\n\t\"./ja.js\": 39183,\n\t\"./jv\": 24286,\n\t\"./jv.js\": 24286,\n\t\"./ka\": 12105,\n\t\"./ka.js\": 12105,\n\t\"./kk\": 47772,\n\t\"./kk.js\": 47772,\n\t\"./km\": 18758,\n\t\"./km.js\": 18758,\n\t\"./kn\": 79282,\n\t\"./kn.js\": 79282,\n\t\"./ko\": 33730,\n\t\"./ko.js\": 33730,\n\t\"./ku\": 1408,\n\t\"./ku.js\": 1408,\n\t\"./ky\": 33291,\n\t\"./ky.js\": 33291,\n\t\"./lb\": 36841,\n\t\"./lb.js\": 36841,\n\t\"./lo\": 55466,\n\t\"./lo.js\": 55466,\n\t\"./lt\": 57010,\n\t\"./lt.js\": 57010,\n\t\"./lv\": 37595,\n\t\"./lv.js\": 37595,\n\t\"./me\": 39861,\n\t\"./me.js\": 39861,\n\t\"./mi\": 35493,\n\t\"./mi.js\": 35493,\n\t\"./mk\": 95966,\n\t\"./mk.js\": 95966,\n\t\"./ml\": 87341,\n\t\"./ml.js\": 87341,\n\t\"./mn\": 5115,\n\t\"./mn.js\": 5115,\n\t\"./mr\": 10370,\n\t\"./mr.js\": 10370,\n\t\"./ms\": 9847,\n\t\"./ms-my\": 41237,\n\t\"./ms-my.js\": 41237,\n\t\"./ms.js\": 9847,\n\t\"./mt\": 72126,\n\t\"./mt.js\": 72126,\n\t\"./my\": 56165,\n\t\"./my.js\": 56165,\n\t\"./nb\": 64924,\n\t\"./nb.js\": 64924,\n\t\"./ne\": 16744,\n\t\"./ne.js\": 16744,\n\t\"./nl\": 93901,\n\t\"./nl-be\": 59814,\n\t\"./nl-be.js\": 59814,\n\t\"./nl.js\": 93901,\n\t\"./nn\": 83877,\n\t\"./nn.js\": 83877,\n\t\"./oc-lnc\": 92135,\n\t\"./oc-lnc.js\": 92135,\n\t\"./pa-in\": 15858,\n\t\"./pa-in.js\": 15858,\n\t\"./pl\": 64495,\n\t\"./pl.js\": 64495,\n\t\"./pt\": 89520,\n\t\"./pt-br\": 57971,\n\t\"./pt-br.js\": 57971,\n\t\"./pt.js\": 89520,\n\t\"./ro\": 96459,\n\t\"./ro.js\": 96459,\n\t\"./ru\": 21793,\n\t\"./ru.js\": 21793,\n\t\"./sd\": 40950,\n\t\"./sd.js\": 40950,\n\t\"./se\": 10490,\n\t\"./se.js\": 10490,\n\t\"./si\": 90124,\n\t\"./si.js\": 90124,\n\t\"./sk\": 64249,\n\t\"./sk.js\": 64249,\n\t\"./sl\": 14985,\n\t\"./sl.js\": 14985,\n\t\"./sq\": 51104,\n\t\"./sq.js\": 51104,\n\t\"./sr\": 49131,\n\t\"./sr-cyrl\": 79915,\n\t\"./sr-cyrl.js\": 79915,\n\t\"./sr.js\": 49131,\n\t\"./ss\": 85893,\n\t\"./ss.js\": 85893,\n\t\"./sv\": 98760,\n\t\"./sv.js\": 98760,\n\t\"./sw\": 91172,\n\t\"./sw.js\": 91172,\n\t\"./ta\": 27333,\n\t\"./ta.js\": 27333,\n\t\"./te\": 23110,\n\t\"./te.js\": 23110,\n\t\"./tet\": 52095,\n\t\"./tet.js\": 52095,\n\t\"./tg\": 27321,\n\t\"./tg.js\": 27321,\n\t\"./th\": 9041,\n\t\"./th.js\": 9041,\n\t\"./tk\": 19005,\n\t\"./tk.js\": 19005,\n\t\"./tl-ph\": 75768,\n\t\"./tl-ph.js\": 75768,\n\t\"./tlh\": 89444,\n\t\"./tlh.js\": 89444,\n\t\"./tr\": 72397,\n\t\"./tr.js\": 72397,\n\t\"./tzl\": 28254,\n\t\"./tzl.js\": 28254,\n\t\"./tzm\": 51106,\n\t\"./tzm-latn\": 30699,\n\t\"./tzm-latn.js\": 30699,\n\t\"./tzm.js\": 51106,\n\t\"./ug-cn\": 9288,\n\t\"./ug-cn.js\": 9288,\n\t\"./uk\": 67691,\n\t\"./uk.js\": 67691,\n\t\"./ur\": 13795,\n\t\"./ur.js\": 13795,\n\t\"./uz\": 6791,\n\t\"./uz-latn\": 60588,\n\t\"./uz-latn.js\": 60588,\n\t\"./uz.js\": 6791,\n\t\"./vi\": 65666,\n\t\"./vi.js\": 65666,\n\t\"./x-pseudo\": 14378,\n\t\"./x-pseudo.js\": 14378,\n\t\"./yo\": 75805,\n\t\"./yo.js\": 75805,\n\t\"./zh-cn\": 83839,\n\t\"./zh-cn.js\": 83839,\n\t\"./zh-hk\": 55726,\n\t\"./zh-hk.js\": 55726,\n\t\"./zh-mo\": 99807,\n\t\"./zh-mo.js\": 99807,\n\t\"./zh-tw\": 74152,\n\t\"./zh-tw.js\": 74152\n};\n\n\nfunction webpackContext(req) {\n\tvar id = webpackContextResolve(req);\n\treturn __webpack_require__(id);\n}\nfunction webpackContextResolve(req) {\n\tif(!__webpack_require__.o(map, req)) {\n\t\tvar e = new Error(\"Cannot find module '\" + req + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\treturn map[req];\n}\nwebpackContext.keys = function webpackContextKeys() {\n\treturn Object.keys(map);\n};\nwebpackContext.resolve = webpackContextResolve;\nmodule.exports = webpackContext;\nwebpackContext.id = 46700;","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\tid: moduleId,\n\t\tloaded: false,\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Flag the module as loaded\n\tmodule.loaded = true;\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = (chunkId) => {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = (chunkId) => {\n\t// return url for filenames based on template\n\treturn \"\" + (chunkId === 8299 ? \"user-status-modal\" : chunkId) + \"-\" + chunkId + \".js?v=\" + {\"3240\":\"29c327d1e4e42959b82f\",\"3998\":\"a49373c9d79e30e60f7b\",\"8299\":\"7006122e6c76e3cf7057\",\"9064\":\"f6243754beec9d78de45\"}[chunkId] + \"\";\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","__webpack_require__.nmd = (module) => {\n\tmodule.paths = [];\n\tif (!module.children) module.children = [];\n\treturn module;\n};","__webpack_require__.j = 2613;","var scriptUrl;\nif (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + \"\";\nvar document = __webpack_require__.g.document;\nif (!scriptUrl && document) {\n\tif (document.currentScript)\n\t\tscriptUrl = document.currentScript.src;\n\tif (!scriptUrl) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tif(scripts.length) {\n\t\t\tvar i = scripts.length - 1;\n\t\t\twhile (i > -1 && !scriptUrl) scriptUrl = scripts[i--].src;\n\t\t}\n\t}\n}\n// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration\n// or pass an empty string (\"\") and set the __webpack_public_path__ variable from your code to use your own logic.\nif (!scriptUrl) throw new Error(\"Automatic publicPath is not supported in this browser\");\nscriptUrl = scriptUrl.replace(/#.*$/, \"\").replace(/\\?.*$/, \"\").replace(/\\/[^\\/]+$/, \"/\");\n__webpack_require__.p = scriptUrl;","__webpack_require__.b = document.baseURI || self.location.href;\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t2613: 0\n};\n\n__webpack_require__.f.j = (chunkId, promises) => {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(true) { // all chunks have JS\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = (event) => {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n__webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0);\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = (parentChunkLoadingFunction, data) => {\n\tvar chunkIds = data[0];\n\tvar moreModules = data[1];\n\tvar runtime = data[2];\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some((id) => (installedChunks[id] !== 0))) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\treturn __webpack_require__.O(result);\n}\n\nvar chunkLoadingGlobal = self[\"webpackChunknextcloud\"] = self[\"webpackChunknextcloud\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","__webpack_require__.nc = undefined;","// startup\n// Load entry module and return exports\n// This entry module depends on other loaded chunks and execution need to be delayed\nvar __webpack_exports__ = __webpack_require__.O(undefined, [7874], () => (__webpack_require__(86419)))\n__webpack_exports__ = __webpack_require__.O(__webpack_exports__);\n"],"names":["deferred","inProgress","dataWebpackPrefix","name","components","NcButton","SetStatusModal","mixins","OnlineStatusMixin","props","inline","type","Boolean","default","data","heartbeatInterval","isAway","isModalOpen","mouseMoveListener","setAwayTimeout","mounted","$store","dispatch","OC","config","session_keepalive","setInterval","_backgroundHeartbeat","bind","debounce","wasAway","clearTimeout","setTimeout","window","addEventListener","capture","passive","subscribe","handleUserStatusUpdated","beforeDestroy","removeEventListener","clearInterval","unsubscribe","methods","openModal","closeModal","status","async","url","generateOcsUrl","HttpClient","put","ocs","sendHeartbeat","userId","error","console","debug","response","state","getCurrentUser","uid","icon","message","options","styleTagTransform","setAttributes","insert","domAPI","insertStyleElement","locals","_vm","this","_c","_self","tag","attrs","statusIcon","on","$event","stopPropagation","apply","arguments","scopedSlots","_u","key","fn","staticClass","class","proxy","_v","_s","visibleMessage","_e","predefinedStatuses","mutations","addPredefinedStatus","getters","statusesHaveLoaded","length","actions","loadAllPredefinedStatuses","_ref","commit","statuses","get","fetchAllPredefinedStatuses","getTimestampForClearAt","clearAt","date","dateFactory","setSeconds","getSeconds","time","Math","floor","getTime","Number","moment","endOf","format","statusIsUserDefined","messageIsPredefined","messageId","setStatus","statusType","setPredefinedMessage","_ref2","setCustomMessage","_ref3","clearMessage","loadStatusFromServer","_ref4","_ref5","_ref6","emit","setStatusFromObject","_ref7","_ref8","_ref9","rootState","resolvedClearAt","undefined","find","id","_ref10","_ref11","_ref12","delete","reFetchStatusFromServer","_ref13","fetchCurrentStatus","setStatusFromHeartbeat","_ref14","loadStatusFromInitialState","_ref15","loadState","loadBackupStatusFromServer","fetchBackupFromServer","fetchBackupStatus","e","revertBackupFromServer","revertToBackupStatus","Vue","use","Vuex","Store","modules","userStatus","userBackupStatus","strict","__webpack_nonce__","btoa","getRequestToken","prototype","t","$t","mountMenuEntry","mountPoint","document","getElementById","el","render","h","UserStatus","store","OCA","Dashboard","registerStatus","extend","propsData","$mount","computed","mapState","customIcon","customMessage","changeStatus","err","showError","Date","___CSS_LOADER_EXPORT___","push","module","map","webpackContext","req","webpackContextResolve","__webpack_require__","o","Error","code","keys","Object","resolve","exports","__webpack_module_cache__","moduleId","cachedModule","loaded","__webpack_modules__","call","m","O","result","chunkIds","priority","notFulfilled","Infinity","i","fulfilled","j","every","splice","r","n","getter","__esModule","d","a","definition","defineProperty","enumerable","f","chunkId","Promise","all","reduce","promises","u","g","globalThis","Function","obj","prop","hasOwnProperty","l","done","script","needAttach","scripts","getElementsByTagName","s","getAttribute","createElement","charset","timeout","nc","setAttribute","src","onScriptComplete","prev","event","onerror","onload","doneFns","parentNode","removeChild","forEach","target","head","appendChild","Symbol","toStringTag","value","nmd","paths","children","scriptUrl","importScripts","location","currentScript","replace","p","b","baseURI","self","href","installedChunks","installedChunkData","promise","reject","errorType","realSrc","request","webpackJsonpCallback","parentChunkLoadingFunction","moreModules","runtime","some","chunkLoadingGlobal","__webpack_exports__"],"sourceRoot":""}
\ No newline at end of file
index 74c54cc9da2ba830e0268c481a8b847719aaffe8..c96d07d298b16741b6e02e0a5e977138d8b2c308 100644 (file)
@@ -51,6 +51,12 @@ interface IUserStatus {
         */
        public const DND = 'dnd';
 
+       /**
+        * @var string
+        * @since 28.0.0
+        */
+       public const BUSY = 'busy';
+
        /**
         * @var string
         * @since 20.0.0
@@ -75,6 +81,18 @@ interface IUserStatus {
         */
        public const MESSAGE_AVAILABILITY = 'availability';
 
+       /**
+        * @var string
+        * @since 28.0.0
+        */
+       public const MESSAGE_CALENDAR_BUSY = 'meeting';
+
+       /**
+        * @var string
+        * @since 28.0.0
+        */
+       public const MESSAGE_CALENDAR_BUSY_TENTATIVE = 'busy-tentative';
+
        /**
         * Get the user this status is connected to
         *