mirror of https://github.com/nextcloud/server
displayname on federated shares
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
This commit is contained in:
parent
266436b767
commit
d2efd0e03c
|
@ -102,8 +102,9 @@ class Notifier implements INotifier {
|
|||
$notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg')));
|
||||
|
||||
$params = $notification->getSubjectParameters();
|
||||
$displayName = (count($params) > 3) ? $params[3] : '';
|
||||
if ($params[0] !== $params[1] && $params[1] !== null) {
|
||||
$remoteInitiator = $this->createRemoteUser($params[0]);
|
||||
$remoteInitiator = $this->createRemoteUser($params[0], $displayName);
|
||||
$remoteOwner = $this->createRemoteUser($params[1]);
|
||||
$params[3] = $remoteInitiator['name'] . '@' . $remoteInitiator['server'];
|
||||
$params[4] = $remoteOwner['name'] . '@' . $remoteOwner['server'];
|
||||
|
@ -121,7 +122,7 @@ class Notifier implements INotifier {
|
|||
]
|
||||
);
|
||||
} else {
|
||||
$remoteOwner = $this->createRemoteUser($params[0]);
|
||||
$remoteOwner = $this->createRemoteUser($params[0], $displayName);
|
||||
$params[3] = $remoteOwner['name'] . '@' . $remoteOwner['server'];
|
||||
|
||||
$notification->setRichSubject(
|
||||
|
@ -166,19 +167,21 @@ class Notifier implements INotifier {
|
|||
|
||||
/**
|
||||
* @param string $cloudId
|
||||
* @param string $displayName - overwrite display name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function createRemoteUser($cloudId, $displayName = null) {
|
||||
protected function createRemoteUser(string $cloudId, string $displayName = '') {
|
||||
try {
|
||||
$resolvedId = $this->cloudIdManager->resolveCloudId($cloudId);
|
||||
if ($displayName === null) {
|
||||
if ($displayName === '') {
|
||||
$displayName = $this->getDisplayName($resolvedId);
|
||||
}
|
||||
$user = $resolvedId->getUser();
|
||||
$server = $resolvedId->getRemote();
|
||||
} catch (HintException $e) {
|
||||
$user = $cloudId;
|
||||
$displayName = $cloudId;
|
||||
$displayName = ($displayName !== '') ? $displayName : $cloudId;
|
||||
$server = '';
|
||||
}
|
||||
|
||||
|
|
|
@ -55,10 +55,13 @@ use OCP\ILogger;
|
|||
use OCP\IURLGenerator;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Notification\IManager as INotificationManager;
|
||||
use OCP\Server;
|
||||
use OCP\Share\Exceptions\ShareNotFound;
|
||||
use OCP\Share\IManager;
|
||||
use OCP\Share\IShare;
|
||||
use OCP\Util;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class CloudFederationProviderFiles implements ICloudFederationProvider {
|
||||
|
||||
|
@ -250,26 +253,29 @@ class CloudFederationProviderFiles implements ICloudFederationProvider {
|
|||
$this->externalShareManager->addShare($remote, $token, '', $name, $owner, $shareType,false, $shareWith, $remoteId);
|
||||
$shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external');
|
||||
|
||||
// get DisplayName about the owner of the share
|
||||
$ownerDisplayName = $this->getUserDisplayName($ownerFederatedId);
|
||||
|
||||
if ($shareType === IShare::TYPE_USER) {
|
||||
$event = $this->activityManager->generateEvent();
|
||||
$event->setApp('files_sharing')
|
||||
->setType('remote_share')
|
||||
->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')])
|
||||
->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName])
|
||||
->setAffectedUser($shareWith)
|
||||
->setObject('remote_share', $shareId, $name);
|
||||
\OC::$server->getActivityManager()->publish($event);
|
||||
$this->notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name);
|
||||
$this->notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName);
|
||||
} else {
|
||||
$groupMembers = $this->groupManager->get($shareWith)->getUsers();
|
||||
foreach ($groupMembers as $user) {
|
||||
$event = $this->activityManager->generateEvent();
|
||||
$event->setApp('files_sharing')
|
||||
->setType('remote_share')
|
||||
->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')])
|
||||
->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName])
|
||||
->setAffectedUser($user->getUID())
|
||||
->setObject('remote_share', $shareId, $name);
|
||||
\OC::$server->getActivityManager()->publish($event);
|
||||
$this->notifyAboutNewShare($user->getUID(), $shareId, $ownerFederatedId, $sharedByFederatedId, $name);
|
||||
$this->notifyAboutNewShare($user->getUID(), $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName);
|
||||
}
|
||||
}
|
||||
return $shareId;
|
||||
|
@ -335,13 +341,13 @@ class CloudFederationProviderFiles implements ICloudFederationProvider {
|
|||
return $result;
|
||||
}
|
||||
|
||||
private function notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name): void {
|
||||
private function notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $displayName): void {
|
||||
$notification = $this->notificationManager->createNotification();
|
||||
$notification->setApp('files_sharing')
|
||||
->setUser($shareWith)
|
||||
->setDateTime(new \DateTime())
|
||||
->setObject('remote_share', $shareId)
|
||||
->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]);
|
||||
->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/'), $displayName]);
|
||||
|
||||
$declineAction = $notification->createAction();
|
||||
$declineAction->setLabel('decline')
|
||||
|
@ -579,6 +585,8 @@ class CloudFederationProviderFiles implements ICloudFederationProvider {
|
|||
->where($qb->expr()->eq('parent', $qb->createNamedParameter((int)$share['id'])));
|
||||
$qb->execute();
|
||||
|
||||
$ownerDisplayName = $this->getUserDisplayName($owner->getId());
|
||||
|
||||
if ((int)$share['share_type'] === IShare::TYPE_USER) {
|
||||
if ($share['accepted']) {
|
||||
$path = trim($mountpoint, '/');
|
||||
|
@ -594,7 +602,7 @@ class CloudFederationProviderFiles implements ICloudFederationProvider {
|
|||
$event = $this->activityManager->generateEvent();
|
||||
$event->setApp('files_sharing')
|
||||
->setType('remote_share')
|
||||
->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path])
|
||||
->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path, $ownerDisplayName])
|
||||
->setAffectedUser($user)
|
||||
->setObject('remote_share', (int)$share['id'], $path);
|
||||
\OC::$server->getActivityManager()->publish($event);
|
||||
|
@ -824,4 +832,25 @@ class CloudFederationProviderFiles implements ICloudFederationProvider {
|
|||
public function getSupportedShareTypes() {
|
||||
return ['user', 'group'];
|
||||
}
|
||||
|
||||
|
||||
public function getUserDisplayName(string $userId): string {
|
||||
// check if gss is enabled and available
|
||||
if (!$this->appManager->isInstalled('globalsiteselector')
|
||||
|| !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
$slaveService = Server::get(\OCA\GlobalSiteSelector\Service\SlaveService::class);
|
||||
} catch (\Throwable $e) {
|
||||
Server::get(LoggerInterface::class)->error(
|
||||
$e->getMessage(),
|
||||
['exception' => $e]
|
||||
);
|
||||
return '';
|
||||
}
|
||||
|
||||
return $slaveService->getUserDisplayName($this->cloudIdManager->removeProtocolFromUrl($userId), false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,9 +157,11 @@ abstract class Base implements IProvider {
|
|||
|
||||
/**
|
||||
* @param string $uid
|
||||
* @param string $overwriteDisplayName - overwrite display name, only if user is not local
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser($uid) {
|
||||
protected function getUser(string $uid, string $overwriteDisplayName = '') {
|
||||
// First try local user
|
||||
$displayName = $this->userManager->getDisplayName($uid);
|
||||
if ($displayName !== null) {
|
||||
|
@ -176,7 +178,7 @@ abstract class Base implements IProvider {
|
|||
return [
|
||||
'type' => 'user',
|
||||
'id' => $cloudId->getUser(),
|
||||
'name' => $this->getDisplayNameFromAddressBook($cloudId->getDisplayId()),
|
||||
'name' => (($overwriteDisplayName !== '') ? $overwriteDisplayName : $this->getDisplayNameFromAddressBook($cloudId->getDisplayId())),
|
||||
'server' => $cloudId->getRemote(),
|
||||
];
|
||||
}
|
||||
|
@ -185,7 +187,7 @@ abstract class Base implements IProvider {
|
|||
return [
|
||||
'type' => 'user',
|
||||
'id' => $uid,
|
||||
'name' => $uid,
|
||||
'name' => (($overwriteDisplayName !== '') ? $overwriteDisplayName : $uid),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -115,13 +115,14 @@ class RemoteShares extends Base {
|
|||
switch ($subject) {
|
||||
case self::SUBJECT_REMOTE_SHARE_RECEIVED:
|
||||
case self::SUBJECT_REMOTE_SHARE_UNSHARED:
|
||||
$displayName = (count($parameters) > 2) ? $parameters[2] : '';
|
||||
return [
|
||||
'file' => [
|
||||
'type' => 'pending-federated-share',
|
||||
'id' => $parameters[1],
|
||||
'name' => $parameters[1],
|
||||
],
|
||||
'user' => $this->getUser($parameters[0]),
|
||||
'user' => $this->getUser($parameters[0], $displayName)
|
||||
];
|
||||
case self::SUBJECT_REMOTE_SHARE_ACCEPTED:
|
||||
case self::SUBJECT_REMOTE_SHARE_DECLINED:
|
||||
|
|
|
@ -44,12 +44,13 @@ declare(strict_types=1);
|
|||
*/
|
||||
namespace OCA\Files_Sharing\Controller;
|
||||
|
||||
use Exception;
|
||||
use OC\Files\FileInfo;
|
||||
use OC\Files\Storage\Wrapper\Wrapper;
|
||||
use OCA\Files\Helper;
|
||||
use OCA\Files_Sharing\Exceptions\SharingRightsException;
|
||||
use OCA\Files_Sharing\External\Storage;
|
||||
use OCA\Files_Sharing\SharedStorage;
|
||||
use OCA\Files\Helper;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCS\OCSBadRequestException;
|
||||
|
@ -59,9 +60,9 @@ use OCP\AppFramework\OCS\OCSNotFoundException;
|
|||
use OCP\AppFramework\OCSController;
|
||||
use OCP\AppFramework\QueryException;
|
||||
use OCP\Constants;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\InvalidPathException;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\Node;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IConfig;
|
||||
|
@ -74,12 +75,14 @@ use OCP\IURLGenerator;
|
|||
use OCP\IUserManager;
|
||||
use OCP\Lock\ILockingProvider;
|
||||
use OCP\Lock\LockedException;
|
||||
use OCP\Share;
|
||||
use OCP\Server;
|
||||
use OCP\Share\Exceptions\GenericShareException;
|
||||
use OCP\Share\Exceptions\ShareNotFound;
|
||||
use OCP\Share\IManager;
|
||||
use OCP\Share\IShare;
|
||||
use OCP\UserStatus\IManager as IUserStatusManager;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Class Share20OCS
|
||||
|
@ -274,7 +277,11 @@ class ShareAPIController extends OCSController {
|
|||
|
||||
$result['token'] = $share->getToken();
|
||||
$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
|
||||
} elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
|
||||
} elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $this->getCachedFederatedDisplayName($share->getSharedWith());
|
||||
$result['token'] = $share->getToken();
|
||||
} elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
|
||||
$result['token'] = $share->getToken();
|
||||
|
@ -344,7 +351,7 @@ class ShareAPIController extends OCSController {
|
|||
|
||||
/**
|
||||
* Check if one of the users address books knows the exact property, if
|
||||
* yes we return the full name.
|
||||
* not we return the full name.
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $property
|
||||
|
@ -352,11 +359,20 @@ class ShareAPIController extends OCSController {
|
|||
*/
|
||||
private function getDisplayNameFromAddressBook(string $query, string $property): string {
|
||||
// FIXME: If we inject the contacts manager it gets initialized before any address books are registered
|
||||
$result = \OC::$server->getContactsManager()->search($query, [$property], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'strict_search' => true,
|
||||
]);
|
||||
try {
|
||||
$result = \OC::$server->getContactsManager()->search($query, [$property], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'strict_search' => true,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
Server::get(LoggerInterface::class)->error(
|
||||
$e->getMessage(),
|
||||
['exception' => $e]
|
||||
);
|
||||
return $query;
|
||||
}
|
||||
|
||||
foreach ($result as $r) {
|
||||
foreach ($r[$property] as $value) {
|
||||
if ($value === $query && $r['FN']) {
|
||||
|
@ -368,6 +384,102 @@ class ShareAPIController extends OCSController {
|
|||
return $query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $shares
|
||||
* @param array|null $updatedDisplayName
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function fixMissingDisplayName(array $shares, ?array $updatedDisplayName = null): array {
|
||||
$userIds = $updated = [];
|
||||
foreach ($shares as $share) {
|
||||
// share is federated and share have no display name yet
|
||||
if ($share['share_type'] === IShare::TYPE_REMOTE
|
||||
&& ($share['share_with'] ?? '') !== ''
|
||||
&& ($share['share_with_displayname'] ?? '') === '') {
|
||||
$userIds[] = $userId = $share['share_with'];
|
||||
|
||||
if ($updatedDisplayName !== null && array_key_exists($userId, $updatedDisplayName)) {
|
||||
$share['share_with_displayname'] = $updatedDisplayName[$userId];
|
||||
}
|
||||
}
|
||||
|
||||
// prepping userIds with displayName to be updated
|
||||
$updated[] = $share;
|
||||
}
|
||||
|
||||
// if $updatedDisplayName is not null, it means we should have already fixed displayNames of the shares
|
||||
if ($updatedDisplayName !== null) {
|
||||
return $updated;
|
||||
}
|
||||
|
||||
// get displayName for the generated list of userId with no displayName
|
||||
$displayNames = $this->retrieveFederatedDisplayName($userIds);
|
||||
|
||||
// if no displayName are updated, we exit
|
||||
if (empty($displayNames)) {
|
||||
return $updated;
|
||||
}
|
||||
|
||||
// let's fix missing display name and returns all shares
|
||||
return $this->fixMissingDisplayName($shares, $displayNames);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get displayName of a list of userIds from the lookup-server; through the globalsiteselector app.
|
||||
* returns an array with userIds as keys and displayName as values.
|
||||
*
|
||||
* @param array $userIds
|
||||
* @param bool $cacheOnly - do not reach LUS, get data from cache.
|
||||
*
|
||||
* @return array
|
||||
* @throws ContainerExceptionInterface
|
||||
*/
|
||||
private function retrieveFederatedDisplayName(array $userIds, bool $cacheOnly = false): array {
|
||||
// check if gss is enabled and available
|
||||
if (count($userIds) === 0
|
||||
|| !$this->appManager->isInstalled('globalsiteselector')
|
||||
|| !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
$slaveService = Server::get(\OCA\GlobalSiteSelector\Service\SlaveService::class);
|
||||
} catch (\Throwable $e) {
|
||||
Server::get(LoggerInterface::class)->error(
|
||||
$e->getMessage(),
|
||||
['exception' => $e]
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
return $slaveService->getUsersDisplayName($userIds, $cacheOnly);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* retrieve displayName from cache if available (should be used on federated shares)
|
||||
* if not available in cache/lus, try for get from address-book, else returns empty string.
|
||||
*
|
||||
* @param string $userId
|
||||
* @param bool $cacheOnly if true will not reach the lus but will only get data from cache
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getCachedFederatedDisplayName(string $userId, bool $cacheOnly = true): string {
|
||||
$details = $this->retrieveFederatedDisplayName([$userId], $cacheOnly);
|
||||
if (array_key_exists($userId, $details)) {
|
||||
return $details[$userId];
|
||||
}
|
||||
|
||||
$displayName = $this->getDisplayNameFromAddressBook($userId, 'CLOUD');
|
||||
return ($displayName === $userId) ? '' : $displayName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get a specific share by id
|
||||
*
|
||||
|
@ -646,6 +758,8 @@ class ShareAPIController extends OCSController {
|
|||
throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
|
||||
}
|
||||
}
|
||||
|
||||
$share->setSharedWithDisplayName($this->getCachedFederatedDisplayName($shareWith, false));
|
||||
} elseif ($shareType === IShare::TYPE_REMOTE_GROUP) {
|
||||
if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
|
||||
throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$node->getPath(), $shareType]));
|
||||
|
@ -793,7 +907,6 @@ class ShareAPIController extends OCSController {
|
|||
// filter out duplicate shares
|
||||
$known = [];
|
||||
|
||||
|
||||
$formatted = $miniFormatted = [];
|
||||
$resharingRight = false;
|
||||
$known = [];
|
||||
|
@ -957,6 +1070,9 @@ class ShareAPIController extends OCSController {
|
|||
$formatted = $miniFormatted;
|
||||
}
|
||||
|
||||
// fix eventual missing display name from federated shares
|
||||
$formatted = $this->fixMissingDisplayName($formatted);
|
||||
|
||||
if ($includeTags) {
|
||||
$formatted =
|
||||
Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
|
||||
|
|
|
@ -209,11 +209,12 @@ class CloudIdManager implements ICloudIdManager {
|
|||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
private function removeProtocolFromUrl($url) {
|
||||
public function removeProtocolFromUrl(string $url): string {
|
||||
if (str_starts_with($url, 'https://')) {
|
||||
return substr($url, strlen('https://'));
|
||||
} elseif (str_starts_with($url, 'http://')) {
|
||||
return substr($url, strlen('http://'));
|
||||
return substr($url, 8);
|
||||
}
|
||||
if (str_starts_with($url, 'http://')) {
|
||||
return substr($url, 7);
|
||||
}
|
||||
|
||||
return $url;
|
||||
|
|
|
@ -62,4 +62,14 @@ interface ICloudIdManager {
|
|||
* @since 12.0.0
|
||||
*/
|
||||
public function isValidCloudId(string $cloudId): bool;
|
||||
|
||||
/**
|
||||
* remove scheme/protocol from an url
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return string
|
||||
* @since 28.0.0
|
||||
*/
|
||||
public function removeProtocolFromUrl(string $url): string;
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
<errorLevel type="suppress">
|
||||
<referencedClass name="OCA\GroupFolders\Mount\GroupFolderStorage"/>
|
||||
<referencedClass name="OCA\TwoFactorNextcloudNotification\Controller\APIController"/>
|
||||
<referencedClass name="OCA\GlobalSiteSelector\Service\SlaveService"/>
|
||||
</errorLevel>
|
||||
</UndefinedClass>
|
||||
<UndefinedFunction>
|
||||
|
@ -130,6 +131,7 @@
|
|||
<!-- Helper classes for sharing API integration from other apps -->
|
||||
<referencedClass name="OCA\Deck\Sharing\ShareAPIHelper" />
|
||||
<referencedClass name="OCA\Talk\Share\Helper\DeletedShareAPIController" />
|
||||
<referencedClass name="OCA\GlobalSiteSelector\Service\SlaveService"/>
|
||||
</errorLevel>
|
||||
</UndefinedDocblockClass>
|
||||
</issueHandlers>
|
||||
|
|
Loading…
Reference in New Issue