cache = $cacheFactory->createLocal('CalendarStatusService'); } public function processCalendarStatus(string $userId): void { $user = $this->userManager->get($userId); if ($user === null) { return; } $availability = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user); if ($availability !== null && $this->availabilityCoordinator->isInEffect($availability)) { $this->logger->debug('An Absence is in effect, skipping calendar status check', ['user' => $userId]); return; } $calendarEvents = $this->cache->get($userId); if ($calendarEvents === null) { $calendarEvents = $this->getCalendarEvents($user); $this->cache->set($userId, $calendarEvents, 300); } if (empty($calendarEvents)) { try { $this->userStatusService->revertUserStatus($userId, IUserStatus::MESSAGE_CALENDAR_BUSY); } catch (Exception $e) { if ($e->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) { // A different process might have written another status // update to the DB while we're processing our stuff. // We cannot safely restore the status as we don't know which one is valid at this point // So let's silently log this one and exit $this->logger->debug('Unique constraint violation for live user status', ['exception' => $e]); return; } } $this->logger->debug('No calendar events found for status check', ['user' => $userId]); return; } try { $currentStatus = $this->userStatusService->findByUserId($userId); // Was the status set by anything other than the calendar automation? $userStatusTimestamp = $currentStatus->getIsUserDefined() && $currentStatus->getMessageId() !== IUserStatus::MESSAGE_CALENDAR_BUSY ? $currentStatus->getStatusTimestamp() : null; } catch (DoesNotExistException) { $userStatusTimestamp = null; $currentStatus = null; } if (($currentStatus !== null && $currentStatus->getMessageId() === IUserStatus::MESSAGE_CALL) || ($currentStatus !== null && $currentStatus->getStatus() === IUserStatus::DND) || ($currentStatus !== null && $currentStatus->getStatus() === IUserStatus::INVISIBLE)) { // We don't overwrite the call status, DND status or Invisible status $this->logger->debug('Higher priority status detected, skipping calendar status change', ['user' => $userId]); return; } // Filter events to see if we have any that apply to the calendar status $applicableEvents = array_filter($calendarEvents, static function (array $calendarEvent) use ($userStatusTimestamp): bool { if (empty($calendarEvent['objects'])) { return false; } $component = $calendarEvent['objects'][0]; if (isset($component['X-NEXTCLOUD-OUT-OF-OFFICE'])) { return false; } if (isset($component['DTSTART']) && $userStatusTimestamp !== null) { /** @var DateTimeImmutable $dateTime */ $dateTime = $component['DTSTART'][0]; if ($dateTime instanceof DateTimeImmutable && $userStatusTimestamp > $dateTime->getTimestamp()) { return false; } } // Ignore events that are transparent if (isset($component['TRANSP']) && strcasecmp($component['TRANSP'][0], 'TRANSPARENT') === 0) { return false; } return true; }); if (empty($applicableEvents)) { try { $this->userStatusService->revertUserStatus($userId, IUserStatus::MESSAGE_CALENDAR_BUSY); } catch (Exception $e) { if ($e->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) { // A different process might have written another status // update to the DB while we're processing our stuff. // We cannot safely restore the status as we don't know which one is valid at this point // So let's silently log this one and exit $this->logger->debug('Unique constraint violation for live user status', ['exception' => $e]); return; } } $this->logger->debug('No status relevant events found, skipping calendar status change', ['user' => $userId]); return; } // Only update the status if it's neccesary otherwise we mess up the timestamp if ($currentStatus === null || $currentStatus->getMessageId() !== IUserStatus::MESSAGE_CALENDAR_BUSY) { // One event that fulfills all status conditions is enough // 1. Not an OOO event // 2. Current user status (that is not a calendar status) was not set after the start of this event // 3. Event is not set to be transparent $count = count($applicableEvents); $this->logger->debug("Found $count applicable event(s), changing user status", ['user' => $userId]); $this->userStatusService->setUserStatus( $userId, IUserStatus::AWAY, IUserStatus::MESSAGE_CALENDAR_BUSY, true ); } } private function getCalendarEvents(User $user): array { $calendars = $this->calendarManager->getCalendarsForPrincipal('principals/users/' . $user->getUID()); if (empty($calendars)) { return []; } $query = $this->calendarManager->newQuery('principals/users/' . $user->getUID()); 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 && strtolower($sct->getValue()) == ScheduleCalendarTransp::TRANSPARENT) { // If a calendar is marked as 'transparent', it means we must // ignore it for free-busy purposes. continue; } $query->addSearchCalendar($calendarObject->getUri()); } $dtStart = DateTimeImmutable::createFromMutable($this->timeFactory->getDateTime()); $dtEnd = DateTimeImmutable::createFromMutable($this->timeFactory->getDateTime('+5 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); return $this->calendarManager->searchForPrincipal($query); } return []; } } tonge/fix/copy_cache_during_copy_operations Nextcloud server, a safe home for all your data: https://github.com/nextcloud/serverwww-data
aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dashboard/l10n/zh_HK.js
blob: 56a9063cfe3a3fff91b5fafc5b157a7c2332d5d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
OC.L10N.register(
    "dashboard",
    {
    "Dashboard" : "儀表板",
    "Dashboard app" : "儀表板應用程式",
    "Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an overview of your upcoming appointments, urgent emails, chat messages, incoming tickets, latest tweets and much more! People can add the widgets they like and change the background to their liking." : "開始您新的一天\n\nNextcloud 儀表板是您一天的起點,為您提供您即將到來的約會概覽、緊急電郵、聊天訊息、新到的工單、最新的推文以及更多!人仕可以新增他們喜愛的小工具與變更他們喜愛的背景。",
    "\"{title} icon\"" : "\"{title} 圖示\"",
    "Customize" : "自訂",
    "Edit widgets" : "編輯小工具",
    "Get more widgets from the App Store" : "從 App Store 取得更多小工具",
    "Weather service" : "天氣服務",
    "For your privacy, the weather data is requested by your Nextcloud server on your behalf so the weather service receives no personal information." : "為了保護您的隱私,Nextcloud 會代您請求氣象資料,因此天氣服務不會收到您的個人資訊。",
    "Weather data from Met.no" : "氣象資訊來自 Met.no",
    "geocoding with Nominatim" : "使用 Nominatim 來進行地理編碼",
    "elevation data from OpenTopoData" : "來自 OpenTopoData 的海拔資料",
    "Weather" : "天氣",
    "Status" : "狀態",
    "Good morning" : "早晨",
    "Good morning, {name}" : "早晨,{name}",
    "Good afternoon" : "午安",
    "Good afternoon, {name}" : "午安,{name}",
    "Good evening" : "晚安",
    "Good evening, {name}" : "晚安,{name}",
    "Hello" : "哈囉",
    "Hello, {name}" : "哈囉,{name}",
    "Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an overview of your upcoming appointments, urgent emails, chat messages, incoming tickets, latest tweets and much more! Users can add the widgets they like and change the background to their liking." : "開始您新的一天\n\nNextcloud 儀表板是您一天的起點,為您提供您即將到來的約會概覽、緊急電郵、聊天訊息、新到的工單、最新的推文以及更多!用戶可以新增他們喜愛的小工具與變更他們喜愛的背景。"
},
"nplurals=1; plural=0;");