diff --git a/apps/profile/.noopenapi b/apps/profile/.noopenapi deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/apps/profile/composer/composer/autoload_classmap.php b/apps/profile/composer/composer/autoload_classmap.php index 623dce51d5393..d2733b2fd2f8c 100644 --- a/apps/profile/composer/composer/autoload_classmap.php +++ b/apps/profile/composer/composer/autoload_classmap.php @@ -8,6 +8,7 @@ return array( 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'OCA\\Profile\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php', + 'OCA\\Profile\\Controller\\ProfileApiController' => $baseDir . '/../lib/Controller/ProfileApiController.php', 'OCA\\Profile\\Controller\\ProfilePageController' => $baseDir . '/../lib/Controller/ProfilePageController.php', 'OCA\\Profile\\Listener\\LoadAdditionalEntriesListener' => $baseDir . '/../lib/Listener/LoadAdditionalEntriesListener.php', 'OCA\\Profile\\Listener\\ProfilePickerReferenceListener' => $baseDir . '/../lib/Listener/ProfilePickerReferenceListener.php', diff --git a/apps/profile/composer/composer/autoload_static.php b/apps/profile/composer/composer/autoload_static.php index 41efca598c418..472d0115a3017 100644 --- a/apps/profile/composer/composer/autoload_static.php +++ b/apps/profile/composer/composer/autoload_static.php @@ -23,6 +23,7 @@ class ComposerStaticInitProfile public static $classMap = array ( 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'OCA\\Profile\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php', + 'OCA\\Profile\\Controller\\ProfileApiController' => __DIR__ . '/..' . '/../lib/Controller/ProfileApiController.php', 'OCA\\Profile\\Controller\\ProfilePageController' => __DIR__ . '/..' . '/../lib/Controller/ProfilePageController.php', 'OCA\\Profile\\Listener\\LoadAdditionalEntriesListener' => __DIR__ . '/..' . '/../lib/Listener/LoadAdditionalEntriesListener.php', 'OCA\\Profile\\Listener\\ProfilePickerReferenceListener' => __DIR__ . '/..' . '/../lib/Listener/ProfilePickerReferenceListener.php', diff --git a/apps/profile/lib/Controller/ProfileApiController.php b/apps/profile/lib/Controller/ProfileApiController.php new file mode 100644 index 0000000000000..57a64b9cb7ac0 --- /dev/null +++ b/apps/profile/lib/Controller/ProfileApiController.php @@ -0,0 +1,177 @@ +, array{}> + * @throws OCSNotFoundException - The specified user does not exist. + * + * 200: The shared resources between the current user and the specified user. + * 404: The specified user does not exist. + */ + #[NoCSRFRequired] + #[NoAdminRequired] + #[ApiRoute(verb: 'GET', url: '/api/v1/resources/{userId}')] + public function getResources(string $userId): DataResponse { + $user = $this->userManager->get($userId); + if (!$user) { + throw new OCSNotFoundException(); + } + + $files = $this->getSharedNodes($userId); + $events = $this->getSharedCalendarEvents($userId); + + $entries = array_values(array_merge($files, $events)); + return new DataResponse($entries); + } + + /** + * Get all upcoming events shared between a user and the current user. + * + * If the calendar app is disabled for the current user no events will be returned. + * + * @param string $userId - The user ID of the user to get shared events with. + * @return list + */ + private function getSharedCalendarEvents(string $userId) { + if (!$this->appManager->isEnabledForUser('calendar', $this->userSession->getUser())) { + return []; + } + + $mePrincipal = 'principals/users/' . $this->userSession->getUser()->getUID(); + + $query = $this->calendarManager->newQuery($mePrincipal); + $query->setSearchPattern($userId); + $query->addType('VEVENT'); + $query->addSearchProperty(ICalendarQuery::SEARCH_PROPERTY_ATTENDEE); + $query->addSearchProperty(ICalendarQuery::SEARCH_PROPERTY_ORGANIZER); + $now = new \DateTimeImmutable('now'); + $query->setTimerangeStart($now->modify('-1 hour')); + $query->setLimit(9); + + $events = $this->calendarManager->searchForPrincipal($query); + $result = []; + foreach ($events as $event) { + if (isset($event['objects'][0]['STATUS']) && $event['objects'][0]['STATUS'][0] === 'CANCELLED') { + continue; + } + + $end = $event['objects'][0]['DTEND'][0]; + if ($now->diff($end)->invert === 1) { + // already ended, skip + continue; + } + + $calendarUid = $event['objects'][0]['UID'][0]; + if (isset($event['RECURRENCE-ID'])) { + $recurrenceId = $event['RECURRENCE-ID'][0]; + $href = $this->urlGenerator->linkToRouteAbsolute('calendar.object.indexuid.recurrenceId', ['uid' => $calendarUid, 'recurrenceId' => $recurrenceId]); + } else { + $href = $this->urlGenerator->linkToRouteAbsolute('calendar.object.indexuid', ['uid' => $calendarUid]); + } + + $start = \DateTime::createFromImmutable($event['objects'][0]['DTSTART'][0]); + $result[] = [ + 'label' => $event['objects'][0]['SUMMARY'][0], + 'text' => $this->formatter->formatTimeSpan($start, \DateTime::createFromImmutable($now)), + 'href' => $href, + 'img' => $this->urlGenerator->getAbsoluteURL($this->appManager->getAppIcon('calendar')), + ]; + } + return $result; + } + + /** + * @return ProfileSharedResource[] + */ + private function getSharedNodes(string $userId): array { + $outgoingShares = []; + $offset = 0; + while (count($outgoingShares) < 5) { + $shares = $this->shareManager->getSharesBy($userId, IShare::TYPE_USER, limit: 50, offset: $offset); + $outgoingShares = array_merge($outgoingShares, array_filter($shares, fn ($share) => $share->getSharedWith() === $this->userSession->getUser()->getUID())); + $offset += 50; + if (count($shares) < 50) { + break; + } + } + + $incomingShares = []; + $offset = 0; + while (count($incomingShares) < 5) { + $shares = $this->shareManager->getSharesBy($this->userSession->getUser()->getUID(), IShare::TYPE_USER, limit: 50, offset: $offset); + $incomingShares = array_merge($incomingShares, array_filter($shares, fn ($share) => $share->getSharedWith() === $userId)); + $offset += 50; + if (count($shares) < 50) { + break; + } + } + + $shares = array_slice(array_merge($outgoingShares, $incomingShares), 0, 5); + usort($shares, fn ($a, $b) => $a->getNode()->getMTime() <=> $b->getNode()->getMTime()); + $files = []; + foreach ($shares as $share) { + $files[] = [ + 'label' => $share->getNode()->getName(), + 'text' => $this->formatter->formatTimeSpan($share->getNode()->getMTime()), + 'href' => $this->urlGenerator->linkToRouteAbsolute('files.view.index', [ + 'dir' => $share->getNode()->getParent()->getPath(), + 'fileid' => $share->getNode()->getId(), + ]), + 'img' => $this->urlGenerator->linkToRouteAbsolute('core.Preview.getPreviewByFileId', [ + 'fileId' => $share->getNode()->getId(), + 'mimeFallback' => true, + ]), + ]; + } + return $files; + } +} diff --git a/apps/profile/lib/ResponseDefinitions.php b/apps/profile/lib/ResponseDefinitions.php new file mode 100644 index 0000000000000..5af937930f62f --- /dev/null +++ b/apps/profile/lib/ResponseDefinitions.php @@ -0,0 +1,21 @@ +