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

Источник: 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:
2026-07-06 14:07:50 +00:00
commit 01acfa3b40
1716 changed files with 613013 additions and 0 deletions
@@ -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;
}
}
+93
View File
@@ -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;
}
}
}
+56
View File
@@ -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);
}
}
}
+145
View File
@@ -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;
}
}
+90
View File
@@ -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