Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable30] Pass data to field depending on field type #3954

Merged
merged 9 commits into from
Aug 29, 2024
308 changes: 168 additions & 140 deletions composer.lock

Large diffs are not rendered by default.

129 changes: 94 additions & 35 deletions cypress/e2e/templates.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* SPDX-FileCopyrightText: 2023 Julius Härtl <[email protected]>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

describe('Create new office files from templates', function() {

let randUser
Expand All @@ -16,28 +17,20 @@ describe('Create new office files from templates', function() {

it('Create a new file from a user template', function() {
cy.visit('/apps/files')
cy.get('.files-controls .button.new')
.should('be.visible')
.click()

cy.get('.newFileMenu', { timeout: 10000 })
.should('be.visible')
.contains('.menuitem', 'New presentation')
.as('menuitem')
cy.get('.files-list__header div[menu-title="New"] button')
.should('be.visible')
.click()
.as('newFileMenu')

cy.get('@menuitem').find('.filenameform input[type=text]').type('FileFromTemplate')
cy.get('@menuitem').find('.filenameform .icon-confirm').click()
cy.get('@newFileMenu').click()
cy.get('button[role="menuitem"]').contains('New presentation').click()

cy.get('.templates-picker__form')
.as('form')
.should('be.visible')
.contains('.template-picker__label', 'presentation')
.should('be.visible')
.click()
cy.get('input[data-cy-files-new-node-dialog-input=""]').type('FileFromTemplate')
cy.get('button[data-cy-files-new-node-dialog-submit=""]').click()

cy.get('@form').find('.templates-picker__buttons input[type=submit]').click()
cy.get('form.templates-picker__form').as('templatePicker')
cy.get('@templatePicker').contains('presentation').click()
cy.get('@templatePicker').find('input[type="submit"]').click()

cy.waitForViewer()
cy.waitForCollabora()
Expand All @@ -47,34 +40,26 @@ describe('Create new office files from templates', function() {
cy.uploadSystemTemplate()
cy.login(randUser)
cy.visit('/apps/files')
cy.get('.files-controls .button.new')
.should('be.visible')
.click()

cy.get('.newFileMenu', { timeout: 10000 })
.should('be.visible')
.contains('.menuitem', 'New presentation')
.as('menuitem')
cy.get('.files-list__header div[menu-title="New"] button')
.should('be.visible')
.click()
.as('newFileMenu')

cy.get('@menuitem').find('.filenameform input[type=text]').type('FileFromTemplate')
cy.get('@menuitem').find('.filenameform .icon-confirm').click()
cy.get('@newFileMenu').click()
cy.get('button[role="menuitem"]').contains('New presentation').click()

cy.get('.templates-picker__form')
.as('form')
.should('be.visible')
.contains('.template-picker__label', 'systemtemplate')
.should('be.visible')
.click()
cy.get('input[data-cy-files-new-node-dialog-input=""]').type('FileFromSystemTemplate')
cy.get('button[data-cy-files-new-node-dialog-submit=""]').click()

cy.get('@form').find('.templates-picker__buttons input[type=submit]').click()
cy.get('form.templates-picker__form').as('templatePicker')
cy.get('@templatePicker').contains('systemtemplate').click()
cy.get('@templatePicker').find('input[type="submit"]').click()

cy.waitForViewer()
cy.waitForCollabora()
})

it.only('Create a file from a system template as guest', () => {
it('Create a file from a system template as guest', () => {
cy.uploadSystemTemplate()
cy.createFolder(randUser, '/my-share')

Expand Down Expand Up @@ -113,3 +98,77 @@ describe('Create new office files from templates', function() {
})
})
})

describe('Create templates with fields', () => {
let randUser

before(() => {
cy.createRandomUser().then(user => {
randUser = user

cy.login(randUser)
cy.visit('/apps/files')

// Create a templates folder
cy.get('.files-list__header div[menu-title="New"] button')
.should('be.visible')
.as('newFileMenu')

cy.get('@newFileMenu').click()
cy.get('button[role="menuitem"]').contains('Create templates folder').click()

cy.get('button[data-cy-files-new-node-dialog-submit=""]').click()

// Upload the fixtures into the templates folder
cy.uploadFile(randUser, 'templates/document_template_with_fields.odt', 'application/vnd.oasis.opendocument.text', '/Templates/document.odt')
})
})

it('Create a document from a template with fields', () => {
const fields = [
{ type: 'rich-text', alias: 'Name', content: 'Nextcloud' },
{ type: 'rich-text', alias: 'Favorite app', content: 'richdocuments' },
{ type: 'checkbox', alias: 'Uses Nextcloud at home', checked: true },
]

cy.visit('/apps/files')

// Create a new document
cy.get('.files-list__header div[menu-title="New"] button')
.should('be.visible')
.as('newFileMenu')

cy.get('@newFileMenu').click()
cy.get('button[role="menuitem"]').contains('New document').click()

cy.get('input[data-cy-files-new-node-dialog-input=""]').type('FileFromTemplateWithFields')
cy.get('button[data-cy-files-new-node-dialog-submit=""]').click()

// Choose the document template
cy.get('form.templates-picker__form').as('templatePicker')
cy.get('@templatePicker').contains('document').click()
cy.get('@templatePicker').find('input[type="submit"]').click()

// Intercept the POST request to verify the correct fields are submitted
cy.intercept('POST', '**/templates/create', (req) => {
const templateFields = Object.values(req.body.templateFields)

expect(templateFields[0].content).to.equal(fields[0].content)
expect(templateFields[1].content).to.equal(fields[1].content)

req.continue()
}).as('reqFillFields')

cy.submitTemplateFields(fields)

// Wait for the response and collect the file ID of the created file
cy.wait('@reqFillFields').then(({ response }) => {
cy.wrap(response.body.ocs.data.fileid).as('createdFileId')
})

// Test if the fields currently match the values we passed to the template
cy.get('@createdFileId').then(createdFileId => {
cy.verifyTemplateFields(fields, createdFileId)
})
})
})
Binary file not shown.
61 changes: 61 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,64 @@ Cypress.Commands.add('uploadSystemTemplate', () => {
}, { force: true })
cy.get('#richdocuments-templates li').contains('systemtemplate.otp')
})

Cypress.Commands.add('submitTemplateFields', (fields) => {
// Enter test values into the template filler
cy.get('.template-field-modal__content').as('templateFiller')
cy.get('.template-field-modal__buttons').as('templateFillerButtons')

for (const field of fields) {
switch (field.type) {
case 'rich-text':
cy.get('@templateFiller')
.find(`input[placeholder="${field.alias}"]`)
.type(field.content)
break
case 'checkbox':
cy.get('@templateFiller')
.find('span.checkbox-radio-switch__text').contains(field.alias)
.click()
break
default:
expect.fail('Using a field type not yet supported')
break
}
}

// Submit the template fields
cy.get('@templateFillerButtons').find('button[aria-label="Submit button"]').click()
})

Cypress.Commands.add('verifyTemplateFields', (fields, fileId) => {
const apiEndpoint = '/ocs/v2.php/apps/richdocuments/api/v1/template/fields/extract/'

cy.request('/csrftoken')
.then(({ body }) => body.token)
.as('requestToken')

cy.get('@requestToken').then(requesttoken => {
cy.request({
method: 'GET',
url: Cypress.env('baseUrl') + apiEndpoint + fileId + '?format=json',
headers: {
requesttoken,
},
}).then(({ body }) => {
for (const index in body.ocs.data) {
const field = body.ocs.data[index]

switch (field.type) {
case 'rich-text':
expect(field.content).to.equal(fields[index].content)
break
case 'checkbox':
expect(field.checked).to.equal(fields[index].checked)
break
default:
expect.fail('Using a field type not yet supported')
break
}
}
})
})
})
2 changes: 1 addition & 1 deletion lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ public function uploadFontFile(): JSONResponse {
return new JSONResponse($uploadResult);
}
return new JSONResponse(['error' => 'No uploaded file'], Http::STATUS_BAD_REQUEST);
} catch (UploadException | NotPermittedException $e) {
} catch (UploadException|NotPermittedException $e) {
$this->logger->error('Upload error', ['exception' => $e]);
return new JSONResponse(['error' => 'Upload error'], Http::STATUS_BAD_REQUEST);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/Controller/TemplateFieldController.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function extractFields(int $fileId): DataResponse {

return new DataResponse($fields, Http::STATUS_OK);
} catch (\Exception $e) {
return new DataResponse(["Unable to extract fields from given file"], Http::STATUS_INTERNAL_SERVER_ERROR);
return new DataResponse(['Unable to extract fields from given file'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}

Expand All @@ -66,7 +66,7 @@ public function fillFields(int $fileId, array $fields, ?string $destination = nu

return new DataResponse([], Http::STATUS_OK);
} catch (\Exception $e) {
return new DataResponse(["Unable to fill fields into the given file"], Http::STATUS_INTERNAL_SERVER_ERROR);
return new DataResponse(['Unable to fill fields into the given file'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
}
6 changes: 3 additions & 3 deletions lib/Controller/WopiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public function checkFileInfo($fileId, $access_token) {

$share = $this->getShareForWopiToken($wopi);
if ($this->permissionManager->shouldWatermark($file, $wopi->getEditorUid(), $share)) {
$email = $user !== null && !$isPublic ? $user->getEMailAddress() : "";
$email = $user !== null && !$isPublic ? $user->getEMailAddress() : '';
$replacements = [
'userId' => $wopi->getEditorUid(),
'date' => (new \DateTime())->format('Y-m-d H:i:s'),
Expand Down Expand Up @@ -335,7 +335,7 @@ public function getFile($fileId,
}

$fp = $file->fopen('rb');
$rangeStream = fopen("php://temp", "w+b");
$rangeStream = fopen('php://temp', 'w+b');
stream_copy_to_stream($fp, $rangeStream, $length, $offset);
fclose($fp);

Expand Down Expand Up @@ -512,7 +512,7 @@ public function postFile(string $fileId, string $access_token): JSONResponse {
$wopiLock = $this->request->getHeader('X-WOPI-Lock');
[$fileId, , ] = Helper::parseFileId($fileId);
$wopi = $this->wopiMapper->getWopiForToken($access_token);
if ((int) $fileId !== $wopi->getFileid()) {
if ((int)$fileId !== $wopi->getFileid()) {
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
} catch (UnknownTokenException $e) {
Expand Down
2 changes: 1 addition & 1 deletion lib/Db/WopiMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public function getWopiForToken(
* @return int
*/
private function calculateNewTokenExpiry(): int {
return $this->timeFactory->getTime() + (int) $this->appConfig->getAppValue('token_ttl');
return $this->timeFactory->getTime() + (int)$this->appConfig->getAppValue('token_ttl');
}

/**
Expand Down
4 changes: 2 additions & 2 deletions lib/Listener/AddContentSecurityPolicyListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function handle(Event $event): void {

$policy = new EmptyContentSecurityPolicy();
$policy->addAllowedFrameDomain("'self'");
$policy->addAllowedFrameDomain("nc:");
$policy->addAllowedFrameDomain('nc:');

if ($this->capabilitiesService->hasWASMSupport()) {
$policy->allowEvalWasm(true);
Expand All @@ -49,7 +49,7 @@ public function handle(Event $event): void {
}

if ($this->isSettingsPage()) {
$policy->addAllowedConnectDomain("*");
$policy->addAllowedConnectDomain('*');
}

$event->addPolicy($policy);
Expand Down
2 changes: 1 addition & 1 deletion lib/Listener/BeforeGetTemplatesListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function handle(Event $event): void {
}

foreach($event->getTemplates() as $template) {
$templateFileId = $template->jsonSerialize()["fileid"];
$templateFileId = $template->jsonSerialize()['fileid'];
$fields = $this->templateFieldService->extractFields($templateFileId);

$template->setFields($fields);
Expand Down
6 changes: 6 additions & 0 deletions lib/Listener/FileCreatedFromTemplateListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

namespace OCA\Richdocuments\Listener;

use OCA\Richdocuments\Capabilities;
use OCA\Richdocuments\Service\CapabilitiesService;
use OCA\Richdocuments\Service\TemplateFieldService;
use OCA\Richdocuments\TemplateManager;
Expand All @@ -31,6 +32,11 @@ public function handle(Event $event): void {
return;
}

$targetFile = $event->getTarget();
if (!in_array($targetFile->getMimetype(), Capabilities::MIMETYPES) && $targetFile->getMimeType() !== 'application/pdf') {
return;
}

$templateFile = $event->getTemplate();

// Empty template
Expand Down
2 changes: 1 addition & 1 deletion lib/Service/FederationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class FederationService {
private $cache;
/** @var IClientService */
private $clientService;
/** @var LoggerInterface */
/** @var LoggerInterface */
private $logger;
/** @var TrustedServers */
private $trustedServers;
Expand Down
2 changes: 1 addition & 1 deletion lib/Service/FontService.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ private function generateFontOverview(ISimpleFile $fontFile): void {
imagepng($im, $imageFileResource);
imagedestroy($im);
}
} catch (\Exception | \Throwable $e) {
} catch (\Exception|\Throwable $e) {
// do nothing if there was any kind of error during overview generation
// the /apps/richdocuments/settings/fonts/FILE_NAME/overview request will fail with 404
// in the UI and display a fallback message
Expand Down
10 changes: 6 additions & 4 deletions lib/Service/PdfService.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

use mikehaertl\pdftk\Pdf;
use OCP\Files\Node;
use OCP\Files\Template\Field;
use OCP\Files\Template\FieldFactory;
use OCP\Files\Template\FieldType;
use Psr\Log\LoggerInterface;

Expand All @@ -34,12 +34,14 @@ public function extractFields(Node $file): array {
continue;
}

$templateFields[] = new Field(
$templateField = FieldFactory::createField(
(string)$index,
$field['FieldValue'],
$fieldType,
alias: $field['FieldName'],
);
$templateField->setValue($field['FieldValue']);
$templateField->alias = $field['FieldName'];

$templateFields[] = $templateField;
$index++;
}
return $templateFields;
Expand Down
Loading
Loading