UPSTREAM BASELINE: nextcloud/spreed v22.0.12 (без изменений)
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled
Источник: https://github.com/nextcloud/spreed/archive/refs/tags/v22.0.12.tar.gz С этого коммита ветка официального Nextcloud Talk отрезана (решение владельца 2026-07-06). Все дальнейшие изменения — только наши; версии релизов: 22.0.12-f7.N.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Share\Helper;
|
||||
|
||||
use OCA\Talk\Exceptions\RoomNotFoundException;
|
||||
use OCA\Talk\Manager;
|
||||
use OCP\Share\IShare;
|
||||
|
||||
/**
|
||||
* Helper of OCA\Files_Sharing\Controller\DeletedShareAPIController for room
|
||||
* shares.
|
||||
*
|
||||
* The methods of this class are called from the DeletedShareAPIController to
|
||||
* perform actions or checks specific to room shares.
|
||||
*/
|
||||
class DeletedShareAPIController {
|
||||
|
||||
public function __construct(
|
||||
private string $userId,
|
||||
private Manager $manager,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the specific fields of a room share for OCS output.
|
||||
*
|
||||
* The returned fields override those set by the main
|
||||
* DeletedShareAPIController.
|
||||
*
|
||||
* @param IShare $share
|
||||
* @return array
|
||||
*/
|
||||
public function formatShare(IShare $share): array {
|
||||
$result = [];
|
||||
|
||||
try {
|
||||
$room = $this->manager->getRoomByToken($share->getSharedWith(), $this->userId);
|
||||
} catch (RoomNotFoundException $e) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result['share_with_displayname'] = $room->getDisplayName($this->userId);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Share\Helper;
|
||||
|
||||
use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
|
||||
use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException;
|
||||
use OCP\FilesMetadata\IFilesMetadataManager;
|
||||
use OCP\FilesMetadata\Model\IFilesMetadata;
|
||||
|
||||
class FilesMetadataCache {
|
||||
/** @var array<int, ?array{width: int, height: int, blurhash?: string}> */
|
||||
protected array $filesSizeData = [];
|
||||
|
||||
public function __construct(
|
||||
protected IFilesMetadataManager $filesMetadataManager,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<int> $fileIds
|
||||
*/
|
||||
public function preloadMetadata(array $fileIds): void {
|
||||
$missingFileIds = array_diff($fileIds, array_keys($this->filesSizeData));
|
||||
if (empty($missingFileIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->filesMetadataManager->getMetadataForFiles($missingFileIds);
|
||||
foreach ($data as $fileId => $metadata) {
|
||||
$this->cachePhotosSize($fileId, $metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $fileId
|
||||
* @return array
|
||||
* @psalm-return array{width: int, height: int, blurhash?: string}
|
||||
* @throws FilesMetadataNotFoundException
|
||||
*/
|
||||
public function getImageMetadataForFileId(int $fileId): array {
|
||||
if (!array_key_exists($fileId, $this->filesSizeData)) {
|
||||
try {
|
||||
$this->cachePhotosSize($fileId, $this->filesMetadataManager->getMetadata($fileId, true));
|
||||
} catch (FilesMetadataNotFoundException) {
|
||||
$this->filesSizeData[$fileId] = null;
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->filesSizeData[$fileId];
|
||||
if ($data === null) {
|
||||
throw new FilesMetadataNotFoundException();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function cachePhotosSize(int $fileId, IFilesMetadata $metadata): void {
|
||||
if ($metadata->hasKey('photos-size')) {
|
||||
try {
|
||||
$sizeMetadata = $metadata->getArray('photos-size');
|
||||
} catch (FilesMetadataNotFoundException|FilesMetadataTypeException) {
|
||||
$this->filesSizeData[$fileId] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($sizeMetadata['width'], $sizeMetadata['height'])) {
|
||||
$dimensions = [
|
||||
'width' => $sizeMetadata['width'],
|
||||
'height' => $sizeMetadata['height'],
|
||||
];
|
||||
|
||||
// Retrieve Blurhash from metadata (if present)
|
||||
if ($metadata->hasKey('blurhash')) {
|
||||
$dimensions['blurhash'] = $metadata->getString('blurhash');
|
||||
}
|
||||
|
||||
$this->filesSizeData[$fileId] = $dimensions;
|
||||
} else {
|
||||
$this->filesSizeData[$fileId] = null;
|
||||
}
|
||||
} else {
|
||||
$this->filesSizeData[$fileId] = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Share\Helper;
|
||||
|
||||
use OCA\Talk\Share\RoomShareProvider;
|
||||
use OCP\Share\IShare;
|
||||
|
||||
/**
|
||||
* Instead of doing a single query to get each share and file metadata
|
||||
* we do a grouped query up front so the entries are cached
|
||||
*/
|
||||
class Preloader {
|
||||
public function __construct(
|
||||
protected RoomShareProvider $shareProvider,
|
||||
protected FilesMetadataCache $filesMetadataCache,
|
||||
) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Gather share IDs from the comments and preload share definitions
|
||||
* and files metadata to avoid separate database query for each
|
||||
* individual share/node later on.
|
||||
*
|
||||
* @param IComment[] $comments
|
||||
*/
|
||||
public function preloadShares(array $comments): void {
|
||||
// Scan messages for share IDs
|
||||
$shareIds = [];
|
||||
foreach ($comments as $comment) {
|
||||
$verb = $comment->getVerb();
|
||||
if ($verb === 'object_shared') {
|
||||
$message = $comment->getMessage();
|
||||
$data = json_decode($message, true);
|
||||
if (isset($data['parameters']['share'])) {
|
||||
$shareIds[] = $data['parameters']['share'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($shareIds)) {
|
||||
// Retrieved Share objects will be cached by
|
||||
// the RoomShareProvider and returned from the cache to
|
||||
// the Parser\SystemMessage without additional database queries.
|
||||
$shares = $this->shareProvider->getSharesByIds($shareIds);
|
||||
|
||||
// Preload files metadata as well
|
||||
$fileIds = array_filter(array_map(static fn (IShare $share) => $share->getNodeId(), $shares));
|
||||
$this->filesMetadataCache->preloadMetadata($fileIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Share\Helper;
|
||||
|
||||
use OCA\Talk\Exceptions\ParticipantNotFoundException;
|
||||
use OCA\Talk\Exceptions\RoomNotFoundException;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCP\AppFramework\OCS\OCSNotFoundException;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\Share\IShare;
|
||||
|
||||
/**
|
||||
* Helper of OCA\Files_Sharing\Controller\ShareAPIController for room shares.
|
||||
*
|
||||
* The methods of this class are called from the ShareAPIController to perform
|
||||
* actions or checks specific to room shares.
|
||||
*/
|
||||
class ShareAPIController {
|
||||
|
||||
public function __construct(
|
||||
protected string $userId,
|
||||
protected Manager $manager,
|
||||
protected ParticipantService $participantService,
|
||||
protected ITimeFactory $timeFactory,
|
||||
protected IL10N $l,
|
||||
protected IURLGenerator $urlGenerator,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the specific fields of a room share for OCS output.
|
||||
*
|
||||
* The returned fields override those set by the main ShareAPIController.
|
||||
*
|
||||
* @param IShare $share
|
||||
* @return array
|
||||
*/
|
||||
public function formatShare(IShare $share): array {
|
||||
$result = [];
|
||||
|
||||
try {
|
||||
$room = $this->manager->getRoomByToken($share->getSharedWith(), $this->userId);
|
||||
} catch (RoomNotFoundException $e) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result['share_with_displayname'] = $room->getDisplayName($this->userId);
|
||||
try {
|
||||
$this->participantService->getParticipant($room, $this->userId, false);
|
||||
$result['share_with_link'] = $this->urlGenerator->linkToRouteAbsolute('spreed.Page.showCall', ['token' => $room->getToken()]);
|
||||
} catch (ParticipantNotFoundException $e) {
|
||||
// Removing the conversation token from the leaked data if not a participant.
|
||||
// Adding some unique but reproducable part to the share_with here
|
||||
// so the avatars for conversations are distinguishable
|
||||
$result['share_with'] = 'private_conversation_' . substr(sha1($room->getName() . $room->getId()), 0, 6);
|
||||
$result['share_with_link'] = '';
|
||||
}
|
||||
if ($room->getType() === Room::TYPE_PUBLIC) {
|
||||
$result['token'] = $share->getToken();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the given share to be passed to OC\Share20\Manager::createShare.
|
||||
*
|
||||
* @param IShare $share
|
||||
* @param string $shareWith
|
||||
* @param int $permissions
|
||||
* @param string $expireDate
|
||||
* @throws OCSNotFoundException
|
||||
*/
|
||||
public function createShare(IShare $share, string $shareWith, int $permissions, string $expireDate): void {
|
||||
$share->setSharedWith($shareWith);
|
||||
$share->setPermissions($permissions);
|
||||
|
||||
if ($expireDate !== '') {
|
||||
try {
|
||||
$expireDateTime = $this->parseDate($expireDate);
|
||||
$share->setExpirationDate($expireDateTime);
|
||||
} catch (\Exception $e) {
|
||||
throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the passed date is valid ISO 8601
|
||||
* So YYYY-MM-DD
|
||||
* If not throw an exception
|
||||
*
|
||||
* Copied from \OCA\Files_Sharing\Controller\ShareAPIController::parseDate.
|
||||
*
|
||||
* @param string $expireDate
|
||||
* @return \DateTime
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function parseDate(string $expireDate): \DateTime {
|
||||
try {
|
||||
$date = $this->timeFactory->getDateTime($expireDate);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
|
||||
}
|
||||
|
||||
$date->setTime(0, 0);
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given user can access the given room share or not.
|
||||
*
|
||||
* A user can access a room share only if they are a participant of the room.
|
||||
*
|
||||
* @param IShare $share
|
||||
* @param string $user
|
||||
* @return bool
|
||||
*/
|
||||
public function canAccessShare(IShare $share, string $user): bool {
|
||||
try {
|
||||
$room = $this->manager->getRoomByToken($share->getSharedWith(), $user);
|
||||
} catch (RoomNotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->participantService->getParticipant($room, $user, false);
|
||||
} catch (ParticipantNotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Share;
|
||||
|
||||
use OC\Files\Filesystem;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Events\RoomDeletedEvent;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use OCP\Share\Events\BeforeShareCreatedEvent;
|
||||
use OCP\Share\Events\VerifyMountPointEvent;
|
||||
use OCP\Share\IShare;
|
||||
|
||||
/**
|
||||
* @template-implements IEventListener<Event>
|
||||
*/
|
||||
class Listener implements IEventListener {
|
||||
|
||||
public function __construct(
|
||||
protected Config $config,
|
||||
protected RoomShareProvider $roomShareProvider,
|
||||
) {
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function handle(Event $event): void {
|
||||
match (get_class($event)) {
|
||||
BeforeShareCreatedEvent::class => $this->overwriteShareTarget($event),
|
||||
VerifyMountPointEvent::class => $this->overwriteMountPoint($event),
|
||||
RoomDeletedEvent::class => $this->roomDeletedEvent($event),
|
||||
};
|
||||
}
|
||||
|
||||
protected function overwriteShareTarget(BeforeShareCreatedEvent $event): void {
|
||||
$share = $event->getShare();
|
||||
|
||||
if ($share->getShareType() !== IShare::TYPE_ROOM
|
||||
&& $share->getShareType() !== RoomShareProvider::SHARE_TYPE_USERROOM) {
|
||||
return;
|
||||
}
|
||||
|
||||
$target = RoomShareProvider::TALK_FOLDER_PLACEHOLDER . '/' . $share->getNode()->getName();
|
||||
$target = Filesystem::normalizePath($target);
|
||||
$share->setTarget($target);
|
||||
}
|
||||
|
||||
protected function overwriteMountPoint(VerifyMountPointEvent $event): void {
|
||||
$share = $event->getShare();
|
||||
$view = $event->getView();
|
||||
|
||||
if ($share->getShareType() !== IShare::TYPE_ROOM
|
||||
&& $share->getShareType() !== RoomShareProvider::SHARE_TYPE_USERROOM) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event->getParent() === RoomShareProvider::TALK_FOLDER_PLACEHOLDER) {
|
||||
try {
|
||||
$userId = $view->getOwner('/');
|
||||
} catch (\Exception $e) {
|
||||
// If we fail to get the owner of the view from the cache,
|
||||
// e.g. because the user never logged in but a cron job runs
|
||||
// We fall back to calculating the owner from the root of the view:
|
||||
if (substr_count($view->getRoot(), '/') >= 2) {
|
||||
// /37c09aa0-1b92-4cf6-8c66-86d8cac8c1d0/files
|
||||
[, $userId, ] = explode('/', $view->getRoot(), 3);
|
||||
} else {
|
||||
// Something weird is going on, we can't fall back more
|
||||
// so for now we don't overwrite the share path ¯\_(ツ)_/¯
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$parent = $this->config->getAttachmentFolder($userId);
|
||||
$event->setParent($parent);
|
||||
if (!$event->getView()->is_dir($parent)) {
|
||||
$event->getView()->mkdir($parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function roomDeletedEvent(RoomDeletedEvent $event): void {
|
||||
$this->roomShareProvider->deleteInRoom($event->getRoom()->getToken());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user