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;
}
}