Skip to content

Commit

Permalink
Merge pull request #3951 from nextcloud/feat/fill-and-convert
Browse files Browse the repository at this point in the history
feat: Add parameter to allow converting files after filling out fields
  • Loading branch information
juliushaertl committed Sep 13, 2024
2 parents ff17c6a + 8899df0 commit 9b10ec7
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 61 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
php-versions: ['8.1']
databases: ['sqlite']
server-versions: ['master']
scenarios: ['wopi', 'direct', 'federation']
scenarios: ['wopi', 'direct', 'federation', 'api']

name: integration-${{ matrix.code-image }}-${{ matrix.scenarios }}-${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}

Expand Down Expand Up @@ -127,7 +127,7 @@ jobs:
php-versions: ['8.1']
databases: ['mysql']
server-versions: ['master']
scenarios: ['wopi', 'direct', 'federation']
scenarios: ['wopi', 'direct', 'federation', 'api']

name: integration-${{ matrix.scenarios }}-${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}

Expand Down Expand Up @@ -206,7 +206,7 @@ jobs:
php-versions: ['8.1']
databases: ['pgsql']
server-versions: ['master']
scenarios: ['wopi', 'direct', 'federation']
scenarios: ['wopi', 'direct', 'federation', 'api']

name: integration-${{ matrix.scenarios }}-${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}

Expand Down Expand Up @@ -287,7 +287,7 @@ jobs:
php-versions: ['8.1']
databases: ['oci']
server-versions: ['master']
scenarios: ['wopi', 'direct', 'federation']
scenarios: ['wopi', 'direct', 'federation', 'api']

name: integration-${{ matrix.scenarios }}-${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}

Expand Down
2 changes: 1 addition & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ SPDX-FileCopyrightText = "2016 Nextcloud contributors"
SPDX-License-Identifier = "AGPL-3.0-or-later"

[[annotations]]
path = ["js/**.js.map", "js/**.js", "js/**.mjs", "js/**.mjs.map", "js/templates/**.handlebars", "emptyTemplates/**", "cypress/fixtures/**", "tests/features/**.feature", "tests/psalm-baseline.xml"]
path = ["js/**.js.map", "js/**.js", "js/**.mjs", "js/**.mjs.map", "js/templates/**.handlebars", "emptyTemplates/**", "cypress/fixtures/**", "tests/data/**", "tests/features/**.feature", "tests/psalm-baseline.xml"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2016 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "AGPL-3.0-or-later"
Expand Down
27 changes: 8 additions & 19 deletions lib/Controller/TemplateFieldController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,19 @@
namespace OCA\Richdocuments\Controller;

use OCA\Richdocuments\Service\TemplateFieldService;
use OCA\Richdocuments\TemplateManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;

class TemplateFieldController extends OCSController {
private TemplateFieldService $templateFieldService;
private TemplateManager $templateManager;

/**
* Template fields controller
*
* @param string $appName,
* @param IRequest $request,
* @param TemplateFieldService $templateFieldService
* @param TemplateManager $templateManager
*/
public function __construct(
string $appName,
IRequest $request,
TemplateFieldService $templateFieldService,
TemplateManager $templateManager
private TemplateFieldService $templateFieldService,
) {
parent::__construct($appName, $request);

$this->templateFieldService = $templateFieldService;
$this->templateManager = $templateManager;
}

/**
Expand All @@ -60,9 +44,14 @@ public function extractFields(int $fileId): DataResponse {
* @return DataResponse
*/
#[NoAdminRequired]
public function fillFields(int $fileId, array $fields, ?string $destination = null): DataResponse {
public function fillFields(int $fileId, array $fields = [], ?string $destination = null, ?string $convert = null): DataResponse {
try {
$this->templateFieldService->fillFields($fileId, $fields, $destination);
$content = $this->templateFieldService->fillFields($fileId, $fields, $destination, $convert);

if ($destination === null) {
echo $content;
die();
}

return new DataResponse([], Http::STATUS_OK);
} catch (\Exception $e) {
Expand Down
47 changes: 11 additions & 36 deletions lib/Preview/Office.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
*/
namespace OCA\Richdocuments\Preview;

use OCA\Richdocuments\AppConfig;
use OCA\Richdocuments\Capabilities;
use OCA\Richdocuments\Service\RemoteOptionsService;
use OCA\Richdocuments\Service\RemoteService;
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\Http\Client\IClientService;
use OCP\IImage;
use OCP\Image;
use OCP\Preview\IProviderV2;
Expand All @@ -20,10 +18,9 @@ abstract class Office implements IProviderV2 {
private array $capabilities;

public function __construct(
private IClientService $clientService,
private AppConfig $config,
Capabilities $capabilities,
private RemoteService $remoteService,
private LoggerInterface $logger,
Capabilities $capabilities,
) {
$this->capabilities = $capabilities->getCapabilities()['richdocuments'] ?? [];
}
Expand All @@ -35,47 +32,25 @@ public function isAvailable(FileInfo $file): bool {
return false;
}

/**
* {@inheritDoc}
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
if ($file->getSize() === 0) {
return null;
}

$useTempFile = $file->isEncrypted() || !$file->getStorage()->isLocal();
if ($useTempFile) {
$fileName = $file->getStorage()->getLocalFile($file->getInternalPath());
$stream = fopen($fileName, 'r');
} else {
$stream = $file->fopen('r');
}

$client = $this->clientService->newClient();
$options = RemoteOptionsService::getDefaultOptions();
// FIXME: can be removed once https://github.com/CollaboraOnline/online/issues/6983 is fixed upstream
$options['expect'] = false;

if ($this->config->getAppValue('richdocuments', 'disable_certificate_verification') === 'yes') {
$options['verify'] = false;
}

$options['multipart'] = [['name' => $file->getName(), 'contents' => $stream]];

try {
$response = $client->post($this->config->getCollaboraUrlInternal() . '/cool/convert-to/png', $options);
$response = $this->remoteService->convertFileTo($file, 'png');
$image = new Image();
$image->loadFromData($response);

if ($image->valid()) {
$image->scaleDownToFit($maxX, $maxY);
return $image;
}
} catch (\Exception $e) {
$this->logger->info('Failed to convert preview: ' . $e->getMessage(), ['exception' => $e]);
return null;
}

$image = new Image();
$image->loadFromData($response->getBody());

if ($image->valid()) {
$image->scaleDownToFit($maxX, $maxY);
return $image;
}
return null;
}
}
43 changes: 43 additions & 0 deletions lib/Service/RemoteService.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,49 @@ public function fetchTargetThumbnail(File $file, string $target): ?string {
return null;
}

/**
* @return resource|string
*/
public function convertFileTo(File $file, string $format) {
$useTempFile = $file->isEncrypted() || !$file->getStorage()->isLocal();
if ($useTempFile) {
$fileName = $file->getStorage()->getLocalFile($file->getInternalPath());
$stream = fopen($fileName, 'r');
} else {
$stream = $file->fopen('r');
}

if ($stream === false) {
throw new Exception('Failed to open stream');
}
return $this->convertTo($file->getName(), $stream, $format);
}

/**
* @param resource $stream
* @return resource|string
*/
public function convertTo(string $filename, $stream, string $format) {
$client = $this->clientService->newClient();
$options = RemoteOptionsService::getDefaultOptions();
// FIXME: can be removed once https://github.com/CollaboraOnline/online/issues/6983 is fixed upstream
$options['expect'] = false;

if ($this->appConfig->getDisableCertificateValidation()) {
$options['verify'] = false;
}

$options['multipart'] = [['name' => $filename, 'contents' => $stream]];

try {
$response = $client->post($this->appConfig->getCollaboraUrlInternal() . '/cool/convert-to/' . $format, $options);
return $response->getBody();
} catch (\Exception $e) {
$this->logger->error('Failed to convert preview: ' . $e->getMessage(), ['exception' => $e]);
throw $e;
}
}

private function getRequestOptionsForFile(File $file, ?string $target = null): array {
$useTempFile = $file->isEncrypted() || !$file->getStorage()->isLocal();
if ($useTempFile) {
Expand Down
13 changes: 12 additions & 1 deletion lib/Service/TemplateFieldService.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use OCP\Files\Template\InvalidFieldTypeException;
use OCP\Http\Client\IClientService;
use OCP\ICacheFactory;
use OCP\ITempManager;
use Psr\Log\LoggerInterface;

class TemplateFieldService {
Expand All @@ -29,6 +30,8 @@ public function __construct(
private IRootFolder $rootFolder,
private LoggerInterface $logger,
private ICacheFactory $cacheFactory,
private RemoteService $remoteService,
private ITempManager $tempManager,
private PdfService $pdfService,
private ?string $userId,
) {
Expand Down Expand Up @@ -134,7 +137,7 @@ public function extractFields(Node|int $file): array {
* @param array<string, array{content: string}> $fields
* @return string|resource
*/
public function fillFields(Node|int $file, array $fields = [], ?string $destination = null) {
public function fillFields(Node|int $file, array $fields = [], ?string $destination = null, ?string $format = null) {
if (!$this->capabilitiesService->hasFormFilling()) {
throw new \RuntimeException('Form filling not supported by the Collabora server');
}
Expand Down Expand Up @@ -187,6 +190,14 @@ public function fillFields(Node|int $file, array $fields = [], ?string $destinat
);

$content = $response->getBody();

if ($format !== null) {
$tmp = $this->tempManager->getTemporaryFile();
file_put_contents($tmp, $content);
$fp = fopen($tmp, 'rb');
$content = $this->remoteService->convertTo($file->getName(), $fp, $format);
}

if ($destination !== null) {
$this->writeToDestination($destination, $content);
}
Expand Down
1 change: 1 addition & 0 deletions tests/config/behat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ default:
- WopiContext
- DirectContext
- FederationContext
- ApiContext


extensions:
Expand Down
Binary file added tests/data/form.odt
Binary file not shown.
43 changes: 43 additions & 0 deletions tests/features/api.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Feature: API

Background:
Given user "user1" exists

Scenario: Extract field values
Given as user "user1"
And User "user1" uploads file "./data/form.odt" to "/form.odt"
Then User "user1" requests the form field data of "/form.odt"
And the response contains the field "Name of the Organizer/Organization"
And the response contains the field "Road closures - Length of road closures (in meters)"

Scenario: Extract field values and fill in field
Given as user "user1"
And User "user1" uploads file "./data/form.odt" to "/form.odt"
Then User "user1" requests the form field data of "/form.odt"
And the response contains the field "Name of the Organizer/Organization"
And the response contains the field "Road closures - Length of road closures (in meters)"
Then User "user1" fills in fields of "/form.odt" with values as "odt" to "/filled.odt"
| ContentControls.ByIndex.19 | 100 |
Then User "user1" requests the form field data of "/filled.odt"
And the response contains the field "Road closures - Length of road closures (in meters)" with "100"
And the resulting file is a "application/vnd.oasis.opendocument.text"

Scenario: Extract field values and fill in field as pdf
Given as user "user1"
And User "user1" uploads file "./data/form.odt" to "/form.odt"
Then User "user1" requests the form field data of "/form.odt"
And the response contains the field "Name of the Organizer/Organization"
And the response contains the field "Road closures - Length of road closures (in meters)"
Then User "user1" fills in fields of "/form.odt" with values as "pdf" to "/filled.pdf"
| ContentControls.ByIndex.19 | 100 |
And the resulting file is a "application/pdf"

Scenario: Extract field values and fill in field as pdf
Given as user "user1"
And User "user1" uploads file "./data/form.odt" to "/form.odt"
Then User "user1" requests the form field data of "/form.odt"
And the response contains the field "Name of the Organizer/Organization"
And the response contains the field "Road closures - Length of road closures (in meters)"
Then User "user1" fills in fields of "/form.odt" with values as "pdf"
| ContentControls.ByIndex.19 | 100 |
And the resulting file is a "application/pdf"
Loading

0 comments on commit 9b10ec7

Please sign in to comment.