Merge pull request #51747 from nextcloud/backport/51744/stable30

[stable30] fix(files_versions): Rely on server mime fallback icons
pull/51717/head
Kate 2025-03-28 12:41:32 +07:00 committed by GitHub
commit 5cc60c7313
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 63 additions and 19 deletions

@ -12,11 +12,13 @@ use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\IPreview;
use OCP\IRequest;
use OCP\IUserSession;
use OCP\Preview\IMimeIconProvider;
class PreviewController extends Controller {
@ -38,7 +40,8 @@ class PreviewController extends Controller {
IRootFolder $rootFolder,
IUserSession $userSession,
IVersionManager $versionManager,
IPreview $previewManager
IPreview $previewManager,
private IMimeIconProvider $mimeIconProvider,
) {
parent::__construct($appName, $request);
@ -55,9 +58,11 @@ class PreviewController extends Controller {
* @param int $x Width of the preview
* @param int $y Height of the preview
* @param string $version Version of the file to get the preview for
* @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, array<empty>, array{}>
* @param bool $mimeFallback Whether to fallback to the mime icon if no preview is available
* @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, array<empty>, array{}>|RedirectResponse<Http::STATUS_SEE_OTHER, array{}>
*
* 200: Preview returned
* 303: Redirect to the mime icon url if mimeFallback is true
* 400: Getting preview is not possible
* 404: Preview not found
*/
@ -67,20 +72,32 @@ class PreviewController extends Controller {
string $file = '',
int $x = 44,
int $y = 44,
string $version = ''
string $version = '',
bool $mimeFallback = false,
) {
if ($file === '' || $version === '' || $x === 0 || $y === 0) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
$versionFile = null;
try {
$user = $this->userSession->getUser();
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
$file = $userFolder->get($file);
$versionFile = $this->versionManager->getVersionFile($user, $file, $version);
$preview = $this->previewManager->getPreview($versionFile, $x, $y, true, IPreview::MODE_FILL, $versionFile->getMimetype());
return new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => $preview->getMimeType()]);
$response = new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => $preview->getMimeType()]);
$response->cacheFor(3600 * 24, false, true);
return $response;
} catch (NotFoundException $e) {
// If we have no preview enabled, we can redirect to the mime icon if any
if ($mimeFallback && $versionFile !== null) {
$url = $this->mimeIconProvider->getMimeIconUrl($versionFile->getMimeType());
if ($url !== null) {
return new RedirectResponse($url);
}
}
return new DataResponse([], Http::STATUS_NOT_FOUND);
} catch (\InvalidArgumentException $e) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);

@ -103,6 +103,19 @@
"type": "string",
"default": ""
}
},
{
"name": "mimeFallback",
"in": "query",
"description": "Whether to fallback to the mime icon if no preview is available",
"schema": {
"type": "integer",
"default": 0,
"enum": [
0,
1
]
}
}
],
"responses": {
@ -132,6 +145,16 @@
"schema": {}
}
}
},
"303": {
"description": "Redirect to the mime icon url if mimeFallback is true",
"headers": {
"Location": {
"schema": {
"type": "string"
}
}
}
}
}
}

@ -10,7 +10,7 @@
<!-- Icon -->
<template #icon>
<div v-if="!(loadPreview || previewLoaded)" class="version__image" />
<img v-else-if="(isCurrent || version.hasPreview) && !previewErrored"
<img v-else-if="version.previewUrl && !previewErrored"
:src="version.previewUrl"
alt=""
decoding="async"

@ -28,7 +28,6 @@ export interface Version {
type: string, // 'file'
mtime: number, // Version creation date as a timestamp
permissions: string, // Only readable: 'R'
hasPreview: boolean, // Whether the version has a preview
previewUrl: string, // Preview URL of the version
url: string, // Download URL of the version
source: string, // The WebDAV endpoint of the ressource
@ -78,12 +77,12 @@ function formatVersion(version: any, fileInfo: any): Version {
let previewUrl = ''
if (mtime === fileInfo.mtime) { // Version is the current one
previewUrl = generateUrl('/core/preview?fileId={fileId}&c={fileEtag}&x=250&y=250&forceIcon=0&a=0', {
previewUrl = generateUrl('/core/preview?fileId={fileId}&c={fileEtag}&x=250&y=250&forceIcon=0&a=0&forceIcon=1&mimeFallback=1', {
fileId: fileInfo.id,
fileEtag: fileInfo.etag,
})
} else {
previewUrl = generateUrl('/apps/files_versions/preview?file={file}&version={fileVersion}', {
previewUrl = generateUrl('/apps/files_versions/preview?file={file}&version={fileVersion}&mimeFallback=1', {
file: joinPaths(fileInfo.path, fileInfo.name),
fileVersion: version.basename,
})
@ -102,7 +101,6 @@ function formatVersion(version: any, fileInfo: any): Version {
type: version.type,
mtime,
permissions: 'R',
hasPreview: version.props['has-preview'] === 1,
previewUrl,
url: joinPaths('/remote.php/dav', version.filename),
source: generateRemoteUrl('dav') + encodePath(version.filename),

@ -274,13 +274,12 @@ export default {
return
}
// Versions previews are too small for our use case, so we override hasPreview and previewUrl
// Versions previews are too small for our use case, so we override previewUrl
// which makes the viewer render the original file.
// We also point to the original filename if the version is the current one.
const versions = this.versions.map(version => ({
...version,
filename: version.mtime === this.fileInfo.mtime ? path.join('files', getCurrentUser()?.uid ?? '', this.fileInfo.path, this.fileInfo.name) : version.filename,
hasPreview: false,
previewUrl: undefined,
}))
@ -291,7 +290,7 @@ export default {
},
compareVersion({ version }) {
const versions = this.versions.map(version => ({ ...version, hasPreview: false, previewUrl: undefined }))
const versions = this.versions.map(version => ({ ...version, previewUrl: undefined }))
OCA.Viewer.compare(this.viewerFileInfo, versions.find(v => v.source === version.source))
},

@ -3,13 +3,13 @@
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files_Versions\Tests\Controller;
use OCA\Files_Versions\Controller\PreviewController;
use OCA\Files_Versions\Versions\IVersionManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IMimeTypeDetector;
@ -20,6 +20,8 @@ use OCP\IPreview;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Preview\IMimeIconProvider;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class PreviewControllerTest extends TestCase {
@ -45,6 +47,8 @@ class PreviewControllerTest extends TestCase {
/** @var IVersionManager|\PHPUnit\Framework\MockObject\MockObject */
private $versionManager;
private IMimeIconProvider&MockObject $mimeIconProvider;
protected function setUp(): void {
parent::setUp();
@ -60,6 +64,7 @@ class PreviewControllerTest extends TestCase {
->method('getUser')
->willReturn($user);
$this->versionManager = $this->createMock(IVersionManager::class);
$this->mimeIconProvider = $this->createMock(IMimeIconProvider::class);
$this->controller = new PreviewController(
'files_versions',
@ -67,7 +72,8 @@ class PreviewControllerTest extends TestCase {
$this->rootFolder,
$this->userSession,
$this->versionManager,
$this->previewManager
$this->previewManager,
$this->mimeIconProvider,
);
}
@ -131,9 +137,10 @@ class PreviewControllerTest extends TestCase {
->willReturn('previewMime');
$res = $this->controller->getPreview('file', 10, 10, '42');
$expected = new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => 'previewMime']);
$this->assertEquals($expected, $res);
$this->assertEquals('previewMime', $res->getHeaders()['Content-Type']);
$this->assertEquals(Http::STATUS_OK, $res->getStatus());
$this->assertEquals($preview, $this->invokePrivate($res, 'file'));
}
public function testVersionNotFound() {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long