Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion lib/Conversion/ConversionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,18 @@ public function convertFile(File $file, string $targetMimeType): mixed {
}
}

return $this->remoteService->convertFileTo($file, $targetFileExtension);
return $this->remoteService->convertFileTo(
$file,
$targetFileExtension,
conversionOptions: ['lang' => $this->getConversionLanguage()]
);
}

private function getConversionLanguage(): string {
$locale = $this->l10n->getLocaleCode();
$language = $locale !== '' ? $locale : $this->l10n->getLanguageCode();

return str_replace('_', '-', $language);
}

private function getMimeProvidersFor(array $inputMimeTypes, string $outputMimeType): array {
Expand Down
22 changes: 18 additions & 4 deletions lib/Service/RemoteService.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,28 @@ public function fetchTargetThumbnail(File $file, string $target): ?string {
}

/**
* @param array<string, string> $conversionOptions
* @return resource|string
*/
public function convertFileTo(File $file, string $format, int $timeout = RemoteOptionsService::REMOTE_TIMEOUT_DEFAULT) {
public function convertFileTo(
File $file,
string $format,
int $timeout = RemoteOptionsService::REMOTE_TIMEOUT_DEFAULT,
array $conversionOptions = [],
) {
$fileName = $file->getStorage()->getLocalFile($file->getInternalPath());
$stream = fopen($fileName, 'rb');

if ($stream === false) {
throw new Exception('Failed to open stream');
}

return $this->convertTo($file->getName(), $stream, $format, [], $timeout);
return $this->convertTo($file->getName(), $stream, $format, $conversionOptions, $timeout);
}

/**
* @param resource $stream
* @param array<string, string>|null $conversionOptions
* @return resource|string
*/
public function convertTo(string $filename, $stream, string $format, ?array $conversionOptions = [], int $timeout = RemoteOptionsService::REMOTE_TIMEOUT_DEFAULT) {
Expand All @@ -86,13 +93,20 @@ public function convertTo(string $filename, $stream, string $format, ?array $con
}

$options['multipart'] = [
array_merge([
[
'name' => $filename,
'filename' => $filename,
'contents' => $stream
], $conversionOptions),
],
];

foreach ($conversionOptions ?? [] as $name => $contents) {
$options['multipart'][] = [
'name' => (string)$name,
'contents' => $contents,
];
}

try {
$response = $client->post($this->appConfig->getCollaboraUrlInternal() . '/cool/convert-to/' . $format, $options);
$body = $response->getBody();
Expand Down
70 changes: 70 additions & 0 deletions tests/lib/Conversion/ConversionProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace Tests\Richdocuments\Conversion;

use OCA\Richdocuments\Conversion\ConversionProvider;
use OCA\Richdocuments\Service\RemoteOptionsService;
use OCA\Richdocuments\Service\RemoteService;
use OCA\Richdocuments\Service\SecureViewService;
use OCP\Files\File;
use OCP\IL10N;
use OCP\L10N\IFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;

class ConversionProviderTest extends TestCase {
private RemoteService&MockObject $remoteService;
private LoggerInterface&MockObject $logger;
private IFactory&MockObject $l10nFactory;
private IL10N&MockObject $l10n;
private SecureViewService&MockObject $secureViewService;
private ConversionProvider $provider;

protected function setUp(): void {
parent::setUp();

$this->remoteService = $this->createMock(RemoteService::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->l10nFactory = $this->createMock(IFactory::class);
$this->l10n = $this->createMock(IL10N::class);
$this->secureViewService = $this->createMock(SecureViewService::class);

$this->l10n->method('t')->willReturnCallback(static fn (string $text): string => $text);
$this->l10nFactory->method('get')
->with('richdocuments')
->willReturn($this->l10n);

$this->provider = new ConversionProvider(
$this->remoteService,
$this->logger,
$this->l10nFactory,
$this->secureViewService,
);
}

public function testConvertFilePassesCurrentLocaleToCollabora(): void {
$file = $this->createMock(File::class);

$this->l10n->expects($this->once())
->method('getLocaleCode')
->willReturn('de_DE');
$this->secureViewService->method('isEnabled')
->willReturn(false);
$this->remoteService->expects($this->once())
->method('convertFileTo')
->with($file, 'pdf', RemoteOptionsService::REMOTE_TIMEOUT_DEFAULT, ['lang' => 'de-DE'])
->willReturn('pdf-content');

$result = $this->provider->convertFile($file, 'application/pdf');

$this->assertSame('pdf-content', $result);
}
}
87 changes: 87 additions & 0 deletions tests/lib/Service/RemoteServiceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace Tests\Richdocuments\Service;

use OCA\Richdocuments\AppConfig;
use OCA\Richdocuments\Service\CapabilitiesService;
use OCA\Richdocuments\Service\RemoteService;
use OCP\Http\Client\IClient;
use OCP\Http\Client\IClientService;
use OCP\Http\Client\IResponse;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;

class RemoteServiceTest extends TestCase {
private AppConfig&MockObject $appConfig;
private IClientService&MockObject $clientService;
private IClient&MockObject $client;
private CapabilitiesService&MockObject $capabilitiesService;
private LoggerInterface&MockObject $logger;
private RemoteService $service;

protected function setUp(): void {
parent::setUp();

$this->appConfig = $this->createMock(AppConfig::class);
$this->clientService = $this->createMock(IClientService::class);
$this->client = $this->createMock(IClient::class);
$this->capabilitiesService = $this->createMock(CapabilitiesService::class);
$this->logger = $this->createMock(LoggerInterface::class);

$this->clientService->method('newClient')
->willReturn($this->client);

$this->service = new RemoteService(
$this->appConfig,
$this->clientService,
$this->capabilitiesService,
$this->logger,
);
}

public function testConvertToSendsConversionOptionsAsMultipartFields(): void {
$stream = fopen('php://memory', 'r+');
$response = $this->createMock(IResponse::class);

$this->appConfig->method('getCollaboraUrlInternal')
->willReturn('http://cool.example');
$this->appConfig->method('getDisableCertificateValidation')
->willReturn(false);
$response->method('getBody')
->willReturn('converted-content');

$this->client->expects($this->once())
->method('post')
->with(
'http://cool.example/cool/convert-to/pdf',
$this->callback(function (array $options) use ($stream): bool {
$this->assertSame([
[
'name' => 'document.xlsx',
'filename' => 'document.xlsx',
'contents' => $stream,
],
[
'name' => 'lang',
'contents' => 'de-DE',
],
], $options['multipart']);

return true;
})
)
->willReturn($response);

$result = $this->service->convertTo('document.xlsx', $stream, 'pdf', ['lang' => 'de-DE']);

$this->assertSame('converted-content', $result);
}
}