Skip to content

Commit

Permalink
Merge pull request #3954 from nextcloud/backport/3919/stable30
Browse files Browse the repository at this point in the history
[stable30] Pass data to field depending on field type
  • Loading branch information
juliushaertl committed Aug 29, 2024
2 parents 68e00de + ea4ff1f commit 458c04f
Show file tree
Hide file tree
Showing 17 changed files with 386 additions and 218 deletions.
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 <jus@bitgrid.net>
* 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

0 comments on commit 458c04f

Please sign in to comment.