Skip to content

Commit a5dd91b

Browse files
committed
feat(files_sharing): fetch remote avatars for external shares
Signed-off-by: Kent Delante <kent@delante.me> Assisted-by: ClaudeCode:claude-sonnet-5
1 parent 4ad68f0 commit a5dd91b

2 files changed

Lines changed: 111 additions & 0 deletions

File tree

lib/private/Avatar/AvatarManager.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OC\User\Manager;
1414
use OCP\Accounts\IAccountManager;
1515
use OCP\Accounts\PropertyDoesNotExistException;
16+
use OCP\Federation\ICloudIdManager;
1617
use OCP\Files\IAppData;
1718
use OCP\Files\NotFoundException;
1819
use OCP\Files\NotPermittedException;
@@ -55,6 +56,11 @@ public function __construct(
5556
public function getAvatar(string $userId): IAvatar {
5657
$user = $this->userManager->get($userId);
5758
if ($user === null) {
59+
$cloudIdManager = \OCP\Server::get(ICloudIdManager::class);
60+
if ($cloudIdManager->isValidCloudId($userId)) {
61+
return $this->getRemoteAvatar($userId);
62+
}
63+
5864
throw new \Exception('user does not exist');
5965
}
6066

@@ -134,4 +140,13 @@ public function deleteUserAvatar(string $userId): void {
134140
public function getGuestAvatar(string $name): IAvatar {
135141
return new GuestAvatar($name, $this->config, $this->logger);
136142
}
143+
144+
/**
145+
* Returns a RemoteAvatar
146+
*
147+
* @param string $userId The \OCP\Federation\ICloudId of the remote account, e.g. account@example.com
148+
*/
149+
private function getRemoteAvatar(string $userId): IAvatar {
150+
return new RemoteAvatar($userId, $this->config, $this->logger);
151+
}
137152
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-only
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OC\Avatar;
11+
12+
use OCP\Federation\ICloudId;
13+
use OCP\Federation\ICloudIdManager;
14+
use OCP\Files\SimpleFS\InMemoryFile;
15+
use OCP\Files\SimpleFS\ISimpleFile;
16+
use OCP\Http\Client\IClientService;
17+
use OCP\IConfig;
18+
use Psr\Log\LoggerInterface;
19+
20+
class RemoteAvatar extends Avatar {
21+
private ICloudId $cloudId;
22+
23+
public function __construct(
24+
protected string $userId,
25+
protected IConfig $config,
26+
protected LoggerInterface $logger,
27+
) {
28+
parent::__construct($config, $logger);
29+
30+
$cloudIdManager = \OCP\Server::get(ICloudIdManager::class);
31+
$this->cloudId = $cloudIdManager->resolveCloudId($userId);
32+
}
33+
34+
#[\Override]
35+
public function exists(): bool {
36+
return true;
37+
}
38+
39+
#[\Override]
40+
public function getDisplayName(): string {
41+
return $this->cloudId->getDisplayId();
42+
}
43+
44+
/**
45+
* Setting avatars isn't implemented for remote accounts
46+
*/
47+
public function set($data): void {
48+
}
49+
50+
/**
51+
* Removing avatars isn't implemented for remote accounts
52+
*/
53+
public function remove(bool $silent = false): void {
54+
}
55+
56+
#[\Override]
57+
public function getFile(int $size, bool $darkTheme = false): ISimpleFile {
58+
$url = rtrim($this->cloudId->getRemote(), '/') . '/index.php/avatar/' . rawurlencode($this->cloudId->getUser()) . '/' . $size;
59+
if ($darkTheme) {
60+
$url .= '/dark';
61+
}
62+
63+
$clientService = \OCP\Server::get(IClientService::class);
64+
$client = $clientService->newClient();
65+
$response = $client->get($url, [
66+
'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false)
67+
]);
68+
69+
$contentType = $response->getHeader('Content-Type');
70+
if ($contentType !== 'image/png') {
71+
throw new \Exception('Unknown filetype');
72+
}
73+
74+
$avatar = $response->getBody();
75+
if (is_resource($avatar)) {
76+
$avatar = stream_get_contents($avatar);
77+
if ($avatar === false) {
78+
throw new \Exception('Failed to fetch remote avatar');
79+
}
80+
}
81+
82+
return new InMemoryFile('avatar.png', $avatar);
83+
}
84+
85+
/**
86+
* Handling user changes isn't implemented for remote accounts
87+
*/
88+
#[\Override]
89+
public function userChanged(string $feature, $oldValue, $newValue): void {
90+
}
91+
92+
#[\Override]
93+
public function isCustomAvatar(): bool {
94+
return true;
95+
}
96+
}

0 commit comments

Comments
 (0)