Skip to content

Commit

Permalink
Merge pull request #3 from jreyesr/feature/options
Browse files Browse the repository at this point in the history
Add Options settings (closes #2)
  • Loading branch information
jreyesr authored Aug 26, 2023
2 parents 522f395 + 66eebcf commit d6f5b36
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 9 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## v1.1.0 [2023-08-25]

* Add the ability to set [render options](https://carbone.io/api-reference.html#options). Thanks, [@mrtnblv](https://github.com/mrtnblv)!

## v1.0.1 [2023-05-30]

* Add docs suggesting alternative for PDF rendering when running N8N on Docker (i.e. Gotenberg)

## v1.0.0 [2023-50-27]

* Initial release
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes

Must receive an input item with both `$json` and `$binary` keys. The `$json` key may be used to compose the "context", which will be provided to the templating engine. The `$binary` key should contain a DOCX document that contains a valid Carbone template.

This operation can take "advanced options", which are passed directly to Carbone's rendering engine. See [Carbone's docs](https://carbone.io/api-reference.html#options) for information about each option. They appear in the Options dropdown, at the bottom of the Render operation:

![a screenshot of the advanced options](images/image.png)

### Convert to PDF

> **NOTE:** This operation requires LibreOffice to be installed. If using the native NPM install, you should install LibreOffice system-wide. If using the Docker images, this operation doesn't seem to work :(
Expand Down Expand Up @@ -170,3 +174,27 @@ By default, this configuration will override the incoming file with the response
1. In the new Put Output in Field textfield that appears, set it to a name that is different to the name of the input file (e.g., if the input file is in `data`, set it to `data_pdf` or something)

![](./images/gotenberg_no_owrite.png)

## Development

More information [here](https://docs.n8n.io/integrations/creating-nodes/test/run-node-locally/).

You must have a local (non-Docker) installation of N8N.

1. Clone this repo
1. `npm i`
1. Make changes as required
1. `npm run build`
1. `npm link`
1. Go to N8N's install dir (`~/.n8n/nodes/` on Linux), then run `npm link n8n-nodes-carbonejs`
1. `n8n start`. If you need to start the N8N instance on another port, `N8N_PORT=5679 n8n start`
1. There's no need to visit the web UI to install the node: it's already installed since it lives in the correct directory
1. After making changes in the code and rebuilding, you'll need to stop N8N (Ctrl+C) and restart it (`n8n start`)
1. For faster changes, instead of rebuilding the code each time, run `npm run dev`. This will start the TypeScript compiler in watch mode, which will recompile the code on every change. You'll still need to restart N8N manually, though.

### Releasing changes

- [ ] Bump the version in `package.json`. We use [SemVer](https://semver.org/).
- [ ] Add an entry to the top of `CHANGELOG.md` describing the changes.
- [ ] Push changes, open a PR and merge it to master branch (if developing on another branch)
- [ ] Create a release. This will kick off the CI which will build and publish the package on NPM
Binary file added images/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 69 additions & 3 deletions nodes/CarboneNode/CarboneNode.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from 'n8n-workflow';
import type { Readable } from 'stream';
import { BINARY_ENCODING } from 'n8n-workflow';
import { convertDocumentToPdf, isWordDocument, renderDocument } from './CarboneUtils';
import { convertDocumentToPdf, isWordDocument, renderDocument, buildOptions } from './CarboneUtils';

const nodeOperations: INodePropertyOptions[] = [
{
Expand Down Expand Up @@ -60,6 +60,69 @@ const nodeOperationOptions: INodeProperties[] = [
},
];

const nodeOptions: INodeProperties[] = [
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
options: [
{
displayName: 'Timezone',
name: 'timezone',
type: 'string',
default: 'Europe/Paris',
description:
'Convert document dates to a timezone. The date must be chained with the `:formatD` formatter. See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List, in the column "TZ identifier"',
},
{
displayName: 'Locale',
name: 'lang',
type: 'string',
default: 'en',
description:
'Locale of the generated document, it will used for translation `{t()}`, formatting numbers with `:formatN`, and currencies `:formatC`. See https://github.com/carboneio/carbone/blob/master/formatters/_locale.js.',
},
{
displayName: 'Complement',
name: 'complement',
type: 'json',
default: '{}',
description: 'Extra data accessible in the template with {c.} instead of {d.}',
},
{
displayName: 'Alias',
name: 'variableStr',
type: 'string',
default: '',
placeholder: 'e.g. {#def = d.id}', // eslint-disable-line n8n-nodes-base/node-param-placeholder-miscased-id
description: 'Predefined alias. See https://carbone.io/documentation.html#alias.',
},
{
displayName: 'Enums',
name: 'enum',
type: 'json',
default: '',
placeholder: 'e.g. {"ORDER_STATUS": ["open", "close"]}',
description: 'Object with enumerations, use it in reports with `convEnum` formatters',
},
{
displayName: 'Translations',
name: 'translations',
type: 'json',
default: '',
placeholder: 'e.g. {"es-es": {"one": "uno"}}',
description:
'When the report is generated, all text between `{t( )}` is replaced with the corresponding translation. The `lang` option is required to select the correct translation. See https://carbone.io/documentation.html#translations',
},
],
displayOptions: {
show: { operation: ['render'] },
},
},
];

export class CarboneNode implements INodeType {
description: INodeTypeDescription = {
displayName: 'Carbone',
Expand All @@ -84,7 +147,8 @@ export class CarboneNode implements INodeType {
default: 'render',
},
{
displayName: 'This operation requires LibreOffice to be installed! If using Docker, see <a href="https://www.npmjs.com/package/n8n-nodes-carbonejs#a-workaround-for-converting-docx-files-to-pdf-on-docker" target="_blank">this link</a> for a suggested alternative.',
displayName:
'This operation requires LibreOffice to be installed! If using Docker, see <a href="https://www.npmjs.com/package/n8n-nodes-carbonejs#a-workaround-for-converting-docx-files-to-pdf-on-docker" target="_blank">this link</a> for a suggested alternative.',
name: 'notice',
type: 'notice',
default: '',
Expand All @@ -93,6 +157,7 @@ export class CarboneNode implements INodeType {
},
},
...nodeOperationOptions,
...nodeOptions,
],
};

Expand Down Expand Up @@ -130,7 +195,8 @@ export class CarboneNode implements INodeType {
fileContent = Buffer.from(binaryData.data, BINARY_ENCODING);
}

const rendered = await renderDocument(fileContent, context);
const options = buildOptions(this, itemIndex);
const rendered = await renderDocument(fileContent, context, options);

item.json = context; // Overwrite the item's JSON data with the used context

Expand Down
30 changes: 27 additions & 3 deletions nodes/CarboneNode/CarboneUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import path from 'path';
import carbone from 'carbone';

import type { Readable } from 'stream';
import { IBinaryData } from 'n8n-workflow';
import { IBinaryData, IExecuteFunctions } from 'n8n-workflow';

// These two functions come straight from https://advancedweb.hu/secure-tempfiles-in-nodejs-without-dependencies/#solution,
// plus typing. These should be safe (from pesky hackers and race conditions), and require no third-party dependencies
Expand All @@ -24,15 +24,32 @@ const withTempDir = async <T>(fn: (dirPath: string) => T): Promise<T> => {
const isWordDocument = (data: IBinaryData) =>
data.mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';

const buildOptions = (node: IExecuteFunctions, index: number): object => {
const additionalFields = node.getNodeParameter('options', index);
// console.debug(additionalFields);

let options: any = {};
if(additionalFields.timezone) options.timezone = additionalFields.timezone;
if(additionalFields.lang) options.lang = additionalFields.lang;
if(additionalFields.variableStr) options.variableStr = additionalFields.variableStr;
if(additionalFields.complement) options.complement = JSON.parse(additionalFields.complement as string);
if(additionalFields.enum) options.enum = JSON.parse(additionalFields.enum as string);
if(additionalFields.translations) options.translations = JSON.parse(additionalFields.translations as string);

// console.debug(options)
return options;
};

const renderDocument = async (
document: Buffer | Readable,
context: any,
options: object,
): Promise<Buffer | Readable> => {
return withTempFile(async (file) => {
await fs.writeFile(file, document); // Save the template to temp dir, since Carbone needs to read from disk

return await new Promise((resolve, reject) => {
carbone.render(file, context, {}, function (err, result) {
carbone.render(file, context, options, function (err, result) {
if (err) {
reject(err);
}
Expand Down Expand Up @@ -65,4 +82,11 @@ const convertDocumentToPdf = async (document: Buffer): Promise<Buffer> => {
});
};

export { withTempFile, withTempDir, isWordDocument, renderDocument, convertDocumentToPdf };
export {
withTempFile,
withTempDir,
isWordDocument,
buildOptions,
renderDocument,
convertDocumentToPdf,
};
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "n8n-nodes-carbonejs",
"version": "1.0.1",
"version": "1.1.0",
"description": "A Carbone JS node that renders Word templates on n8n.io",
"keywords": [
"n8n-community-node-package"
Expand Down

0 comments on commit d6f5b36

Please sign in to comment.