diff options
author | fenn-cs <fenn25.fn@gmail.com> | 2024-04-22 09:37:56 +0100 |
---|---|---|
committer | fenn-cs <fenn25.fn@gmail.com> | 2024-04-22 16:46:38 +0100 |
commit | 990d2446ebc0ff0ed7ae3bdbce3fbd924904de67 (patch) | |
tree | 92008ee0b4ae01d9d45cfadb3fd7fb3d57fd7cfe | |
parent | ea1b0a035630609cb43e3c773fb7e5d0ecab34c9 (diff) | |
download | nextcloud-server-44319-fix-fed-share-user-avatars.tar.gz nextcloud-server-44319-fix-fed-share-user-avatars.zip |
fix(contactsMenu): Attach user cloud to each contact entry44319-fix-fed-share-user-avatars
The contact entry should carry cloud information, this can help to fix various issues including:
- Displaying correct avatar links for federated users
This can also lead to UI improvements in the frontend where, it's federated contacts are shown with
a marker or indicator on the UI.
Signed-off-by: fenn-cs <fenn25.fn@gmail.com>
-rw-r--r-- | core/src/components/UnifiedSearch/SearchableList.vue | 5 | ||||
-rw-r--r-- | core/src/services/UnifiedSearchService.js | 1 | ||||
-rw-r--r-- | core/src/views/UnifiedSearchModal.vue | 3 | ||||
-rw-r--r-- | lib/private/Contacts/ContactsMenu/ContactsStore.php | 29 | ||||
-rw-r--r-- | lib/private/Contacts/ContactsMenu/Entry.php | 71 | ||||
-rw-r--r-- | lib/private/Federation/CloudId.php | 12 | ||||
-rw-r--r-- | lib/private/URLGenerator.php | 13 |
7 files changed, 96 insertions, 38 deletions
diff --git a/core/src/components/UnifiedSearch/SearchableList.vue b/core/src/components/UnifiedSearch/SearchableList.vue index 33f45d06266..cd5117c0884 100644 --- a/core/src/components/UnifiedSearch/SearchableList.vue +++ b/core/src/components/UnifiedSearch/SearchableList.vue @@ -46,7 +46,8 @@ :wide="true" @click="itemSelected(element)"> <template #icon> - <NcAvatar :user="element.user" :show-user-status="false" :hide-favorite="false" /> + <NcAvatar v-if="element.isUser" :user="element.user" :show-user-status="false" :hide-favorite="false" /> + <NcAvatar v-else :url="element.avatar" :show-user-status="false" :hide-favorite="false" /> </template> {{ element.displayName }} </NcButton> @@ -117,7 +118,6 @@ export default { }) }, }, - methods: { clearSearch() { this.searchTerm = '' @@ -128,6 +128,7 @@ export default { this.opened = false }, searchTermChanged(term) { + console.debug('Users (search)', this.filteredList) // WIP, would remove this.$emit('search-term-change', term) }, }, diff --git a/core/src/services/UnifiedSearchService.js b/core/src/services/UnifiedSearchService.js index 54cd19b6a92..01760cfd5ca 100644 --- a/core/src/services/UnifiedSearchService.js +++ b/core/src/services/UnifiedSearchService.js @@ -116,6 +116,7 @@ export async function getContacts({ searchTerm }) { id: authenticatedUser.uid, fullName: authenticatedUser.displayName, emailAddresses: [], + isUser: true, } contacts.unshift(authenticatedUser) return contacts diff --git a/core/src/views/UnifiedSearchModal.vue b/core/src/views/UnifiedSearchModal.vue index 365a7818509..9fbcf7ad059 100644 --- a/core/src/views/UnifiedSearchModal.vue +++ b/core/src/views/UnifiedSearchModal.vue @@ -383,6 +383,7 @@ export default { }, mapContacts(contacts) { return contacts.map(contact => { + console.debug('CONTACT VIEW', contact) // WIP would remove return { // id: contact.id, // name: '', @@ -391,6 +392,8 @@ export default { subname: contact.emailAddresses[0] ? contact.emailAddresses[0] : '', icon: '', user: contact.id, + isUser: contact.isUser, + avatar: contact.avatar, } }) }, diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php index 3b39cc869a7..4c1b544a1c0 100644 --- a/lib/private/Contacts/ContactsMenu/ContactsStore.php +++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php @@ -31,6 +31,7 @@ namespace OC\Contacts\ContactsMenu; +use OC\Federation\CloudId; use OC\KnownUser\KnownUserService; use OC\Profile\ProfileManager; use OCA\UserStatus\Db\UserStatus; @@ -44,6 +45,7 @@ use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\L10N\IFactory as IL10NFactory; +use Psr\Log\LoggerInterface; use function array_column; use function array_fill_keys; use function array_filter; @@ -62,6 +64,7 @@ class ContactsStore implements IContactsStore { private IGroupManager $groupManager, private KnownUserService $knownUserService, private IL10NFactory $l10nFactory, + private LoggerInterface $logger, ) { } @@ -351,19 +354,35 @@ class ContactsStore implements IContactsStore { private function contactArrayToEntry(array $contact): Entry { $entry = new Entry(); - if (!empty($contact['UID'])) { $uid = $contact['UID']; $entry->setId($uid); $entry->setProperty('isUser', false); + $username = ''; + $remoteServer = ''; + + if (isset($contact['CLOUD']) && is_array($contact['CLOUD']) && isset($contact['CLOUD'][0])) { + preg_match('/^(.*?)@(https?:\/\/.*?)$/', $contact['CLOUD'][0], $matches); + if (count($matches) === 3) { + $username = $matches[1]; + $remoteServer = $matches[2]; + $cloud = new CloudId($entry->getId(), $username, $remoteServer); + $entry->setCloudId($cloud); + $this->logger->warning('Set address cloud: ' . json_encode(['username' => $username, 'server' => $remoteServer])); + } else { + $this->logger->warning('Unable to process contact remote server: ' . $contact['CLOUD'][0]); + } + } else { + $this->logger->warning('Invalid remote server data'); + } // overloaded usage so leaving as-is for now if (isset($contact['isLocalSystemBook'])) { $avatar = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $uid, 'size' => 64]); $entry->setProperty('isUser', true); - } elseif (!empty($contact['FN'])) { - $avatar = $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => str_replace('/', ' ', $contact['FN']), 'size' => 64]); + } elseif ($username != '') { + $avatar = $this->urlGenerator->linkToRemoteRouteAbsolute($remoteServer, 'core.avatar.getAvatar', ['userId' => str_replace('/', ' ', $username), 'size' => 64]); } else { - $avatar = $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => str_replace('/', ' ', $uid), 'size' => 64]); + $avatar = $this->urlGenerator->linkToRemoteRouteAbsolute($remoteServer, 'core.avatar.getAvatar', ['userId' => str_replace('/', ' ', $uid), 'size' => 64]); } $entry->setAvatar($avatar); } @@ -374,7 +393,7 @@ class ContactsStore implements IContactsStore { $avatarPrefix = "VALUE=uri:"; if (!empty($contact['PHOTO']) && str_starts_with($contact['PHOTO'], $avatarPrefix)) { - $entry->setAvatar(substr($contact['PHOTO'], strlen($avatarPrefix))); + //$entry->setAvatar(substr($contact['PHOTO'], strlen($avatarPrefix))); } if (!empty($contact['EMAIL'])) { diff --git a/lib/private/Contacts/ContactsMenu/Entry.php b/lib/private/Contacts/ContactsMenu/Entry.php index 41fb88b1528..1951501db5f 100644 --- a/lib/private/Contacts/ContactsMenu/Entry.php +++ b/lib/private/Contacts/ContactsMenu/Entry.php @@ -27,6 +27,7 @@ declare(strict_types=1); namespace OC\Contacts\ContactsMenu; +use OC\Federation\CloudId; use OCP\Contacts\ContactsMenu\IAction; use OCP\Contacts\ContactsMenu\IEntry; use function array_merge; @@ -34,34 +35,32 @@ use function array_merge; class Entry implements IEntry { public const PROPERTY_STATUS_MESSAGE_TIMESTAMP = 'statusMessageTimestamp'; - /** @var string|int|null */ - private $id = null; - - private string $fullName = ''; - - /** @var string[] */ - private array $emailAddresses = []; - - private ?string $avatar = null; - - private ?string $profileTitle = null; - - private ?string $profileUrl = null; - - /** @var IAction[] */ - private array $actions = []; - - private array $properties = []; + public function __construct( + private ?string $id = null, + private string $fullName = '', + private array $emailAddresses = [], + private ?string $avatar = null, + private ?string $profileTitle = null, + private ?string $profileUrl = null, + private array $actions = [], + private array $properties = [], + private ?string $status = null, + private ?string $statusMessage = null, + private ?int $statusMessageTimestamp = null, + private ?string $statusIcon = null, + private ?CloudId $cloud = null + ) { + } - private ?string $status = null; - private ?string $statusMessage = null; - private ?int $statusMessageTimestamp = null; - private ?string $statusIcon = null; public function setId(string $id): void { $this->id = $id; } + public function getId(): string { + return $this->id; + } + public function setFullName(string $displayName): void { $this->fullName = $displayName; } @@ -163,8 +162,25 @@ class Entry implements IEntry { return $this->properties[$key]; } + + public function getStatusMessage(): ?string { + return $this->statusMessage; + } + + public function getStatusMessageTimestamp(): ?int { + return $this->statusMessageTimestamp; + } + + public function setCloudId(CloudId $cloudId) { + $this->cloud = $cloudId; + } + + public function getCloud(): CloudId { + return $this->cloud; + } + /** - * @return array{id: int|string|null, fullName: string, avatar: string|null, topAction: mixed, actions: array, lastMessage: '', emailAddresses: string[], profileTitle: string|null, profileUrl: string|null, status: string|null, statusMessage: null|string, statusMessageTimestamp: null|int, statusIcon: null|string, isUser: bool, uid: mixed} + * @return array{id: int|string|null, fullName: string, avatar: string|null, topAction: mixed, actions: array, lastMessage: '', emailAddresses: string[], profileTitle: string|null, profileUrl: string|null, status: string|null, statusMessage: null|string, statusMessageTimestamp: null|int, statusIcon: null|string, isUser: bool, uid: mixed, cloud: mixed} */ public function jsonSerialize(): array { $topAction = !empty($this->actions) ? $this->actions[0]->jsonSerialize() : null; @@ -188,14 +204,7 @@ class Entry implements IEntry { 'statusIcon' => $this->statusIcon, 'isUser' => $this->getProperty('isUser') === true, 'uid' => $this->getProperty('UID'), + 'cloud' => $this->cloud, ]; } - - public function getStatusMessage(): ?string { - return $this->statusMessage; - } - - public function getStatusMessageTimestamp(): ?int { - return $this->statusMessageTimestamp; - } } diff --git a/lib/private/Federation/CloudId.php b/lib/private/Federation/CloudId.php index 50e974831a6..ae2d453044b 100644 --- a/lib/private/Federation/CloudId.php +++ b/lib/private/Federation/CloudId.php @@ -88,4 +88,16 @@ class CloudId implements ICloudId { public function getRemote(): string { return $this->remote; } + + /** + * @return array{id: string, user: string, remote: string, displayName: string|null} + */ + public function jsonSerialize(): array { + return [ + 'id' => $this->id, + 'user' => $this->user, + 'remote' => $this->remote, + 'displayName' => $this->displayName, + ]; + } } diff --git a/lib/private/URLGenerator.php b/lib/private/URLGenerator.php index 4701f3af6a9..46368876659 100644 --- a/lib/private/URLGenerator.php +++ b/lib/private/URLGenerator.php @@ -115,6 +115,19 @@ class URLGenerator implements IURLGenerator { return $this->getAbsoluteURL($this->linkToRoute($routeName, $arguments)); } + + public function linkToRemoteRouteAbsolute(string $remote, $routeName, array $arguments = []): string { + return $this->formatAsUrl($remote, $this->linkToRoute($routeName, $arguments)); + } + + private function formatAsUrl(string $baseUrl, string $restUrl): ?string { + $baseUrl = trim($baseUrl); + if (empty($baseUrl) || !filter_var(preg_match("~^(?:f|ht)tps?://~i", $baseUrl) ? $baseUrl : "http://$baseUrl", FILTER_VALIDATE_URL)) { + return null; + } + return filter_var($baseUrl . $restUrl, FILTER_VALIDATE_URL) ? $baseUrl . $restUrl : null; + } + public function linkToOCSRouteAbsolute(string $routeName, array $arguments = []): string { // Returns `/subfolder/index.php/ocsapp/…` with `'htaccess.IgnoreFrontController' => false` in config.php // And `/subfolder/ocsapp/…` with `'htaccess.IgnoreFrontController' => true` in config.php |