aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfenn-cs <fenn25.fn@gmail.com>2024-04-22 09:37:56 +0100
committerfenn-cs <fenn25.fn@gmail.com>2024-04-22 16:46:38 +0100
commit990d2446ebc0ff0ed7ae3bdbce3fbd924904de67 (patch)
tree92008ee0b4ae01d9d45cfadb3fd7fb3d57fd7cfe
parentea1b0a035630609cb43e3c773fb7e5d0ecab34c9 (diff)
downloadnextcloud-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.vue5
-rw-r--r--core/src/services/UnifiedSearchService.js1
-rw-r--r--core/src/views/UnifiedSearchModal.vue3
-rw-r--r--lib/private/Contacts/ContactsMenu/ContactsStore.php29
-rw-r--r--lib/private/Contacts/ContactsMenu/Entry.php71
-rw-r--r--lib/private/Federation/CloudId.php12
-rw-r--r--lib/private/URLGenerator.php13
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