mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
fix(n8n Form Node): Resolve expressions in HTML fields (#13755)
This commit is contained in:
@@ -336,15 +336,7 @@ export class Form extends Node {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fields = (context.getNodeParameter('formFields.values', []) as FormFieldsParameter).map(
|
fields = context.getNodeParameter('formFields.values', []) as FormFieldsParameter;
|
||||||
(field) => {
|
|
||||||
if (field.fieldType === 'hiddenField') {
|
|
||||||
field.fieldLabel = field.fieldName as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
return field;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const method = context.getRequestObject().method;
|
const method = context.getRequestObject().method;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
type IWebhookResponseData,
|
type IWebhookResponseData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { renderForm, sanitizeHtml } from './utils';
|
import { renderForm } from './utils';
|
||||||
|
|
||||||
export const renderFormNode = async (
|
export const renderFormNode = async (
|
||||||
context: IWebhookFunctions,
|
context: IWebhookFunctions,
|
||||||
@@ -42,12 +42,6 @@ export const renderFormNode = async (
|
|||||||
) as string) || 'Submit';
|
) as string) || 'Submit';
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const field of fields) {
|
|
||||||
if (field.fieldType === 'html') {
|
|
||||||
field.html = sanitizeHtml(field.html as string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const appendAttribution = context.evaluateExpression(
|
const appendAttribution = context.evaluateExpression(
|
||||||
`{{ $('${trigger?.name}').params.options?.appendAttribution === false ? false : true }}`,
|
`{{ $('${trigger?.name}').params.options?.appendAttribution === false ? false : true }}`,
|
||||||
) as boolean;
|
) as boolean;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { type Response } from 'express';
|
import { type Response } from 'express';
|
||||||
|
import type { MockProxy } from 'jest-mock-extended';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import {
|
import {
|
||||||
type FormFieldsParameter,
|
type FormFieldsParameter,
|
||||||
@@ -9,10 +10,19 @@ import {
|
|||||||
import { renderFormNode } from '../formNodeUtils';
|
import { renderFormNode } from '../formNodeUtils';
|
||||||
|
|
||||||
describe('formNodeUtils', () => {
|
describe('formNodeUtils', () => {
|
||||||
|
let webhookFunctions: MockProxy<IWebhookFunctions>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
webhookFunctions = mock<IWebhookFunctions>();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('should sanitize custom html', async () => {
|
it('should sanitize custom html', async () => {
|
||||||
const executeFunctions = mock<IWebhookFunctions>();
|
webhookFunctions.getNode.mockReturnValue({ typeVersion: 2.1 } as any);
|
||||||
executeFunctions.getNode.mockReturnValue({ typeVersion: 2.1 } as any);
|
webhookFunctions.getNodeParameter.calledWith('options').mockReturnValue({
|
||||||
executeFunctions.getNodeParameter.calledWith('options').mockReturnValue({
|
|
||||||
formTitle: 'Test Title',
|
formTitle: 'Test Title',
|
||||||
formDescription: 'Test Description',
|
formDescription: 'Test Description',
|
||||||
buttonLabel: 'Test Button Label',
|
buttonLabel: 'Test Button Label',
|
||||||
@@ -47,12 +57,12 @@ describe('formNodeUtils', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
executeFunctions.getNodeParameter.calledWith('formFields.values').mockReturnValue(formFields);
|
webhookFunctions.getNodeParameter.calledWith('formFields.values').mockReturnValue(formFields);
|
||||||
|
|
||||||
const responseMock = mock<Response>({ render: mockRender } as any);
|
const responseMock = mock<Response>({ render: mockRender } as any);
|
||||||
const triggerMock = mock<NodeTypeAndVersion>({ name: 'triggerName' } as any);
|
const triggerMock = mock<NodeTypeAndVersion>({ name: 'triggerName' } as any);
|
||||||
|
|
||||||
await renderFormNode(executeFunctions, responseMock, triggerMock, formFields, 'test');
|
await renderFormNode(webhookFunctions, responseMock, triggerMock, formFields, 'test');
|
||||||
|
|
||||||
expect(mockRender).toHaveBeenCalledWith('form-trigger', {
|
expect(mockRender).toHaveBeenCalledWith('form-trigger', {
|
||||||
appendAttribution: true,
|
appendAttribution: true,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
isFormConnected,
|
isFormConnected,
|
||||||
sanitizeHtml,
|
sanitizeHtml,
|
||||||
validateResponseModeConfiguration,
|
validateResponseModeConfiguration,
|
||||||
|
prepareFormFields,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
|
|
||||||
describe('FormTrigger, parseFormDescription', () => {
|
describe('FormTrigger, parseFormDescription', () => {
|
||||||
@@ -994,4 +995,40 @@ describe('validateResponseModeConfiguration', () => {
|
|||||||
|
|
||||||
expect(() => validateResponseModeConfiguration(webhookFunctions)).not.toThrow();
|
expect(() => validateResponseModeConfiguration(webhookFunctions)).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('prepareFormFields', () => {
|
||||||
|
it('should resolve expressions in html fields', async () => {
|
||||||
|
webhookFunctions.evaluateExpression.mockImplementation((expression) => {
|
||||||
|
if (expression === '{{ $json.formMode }}') {
|
||||||
|
return 'Title';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = prepareFormFields(webhookFunctions, [
|
||||||
|
{
|
||||||
|
fieldLabel: 'Custom HTML',
|
||||||
|
fieldType: 'html',
|
||||||
|
elementName: 'test',
|
||||||
|
html: '<h1>{{ $json.formMode }}</h1>',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(result[0].html).toBe('<h1>Title</h1>');
|
||||||
|
});
|
||||||
|
it('should prepare hiddenField', async () => {
|
||||||
|
const result = prepareFormFields(webhookFunctions, [
|
||||||
|
{
|
||||||
|
fieldLabel: '',
|
||||||
|
fieldName: 'test',
|
||||||
|
fieldType: 'hiddenField',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(result[0]).toEqual({
|
||||||
|
fieldLabel: 'test',
|
||||||
|
fieldName: 'test',
|
||||||
|
fieldType: 'hiddenField',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -72,6 +72,28 @@ export function sanitizeHtml(text: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const prepareFormFields = (context: IWebhookFunctions, fields: FormFieldsParameter) => {
|
||||||
|
return fields.map((field) => {
|
||||||
|
if (field.fieldType === 'html') {
|
||||||
|
let { html } = field;
|
||||||
|
|
||||||
|
if (!html) return field;
|
||||||
|
|
||||||
|
for (const resolvable of getResolvables(html)) {
|
||||||
|
html = html.replace(resolvable, context.evaluateExpression(resolvable) as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
field.html = sanitizeHtml(html as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.fieldType === 'hiddenField') {
|
||||||
|
field.fieldLabel = field.fieldName as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return field;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export function sanitizeCustomCss(css: string | undefined): string | undefined {
|
export function sanitizeCustomCss(css: string | undefined): string | undefined {
|
||||||
if (!css) return undefined;
|
if (!css) return undefined;
|
||||||
|
|
||||||
@@ -411,6 +433,8 @@ export function renderForm({
|
|||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formFields = prepareFormFields(context, formFields);
|
||||||
|
|
||||||
const data = prepareFormData({
|
const data = prepareFormData({
|
||||||
formTitle,
|
formTitle,
|
||||||
formDescription,
|
formDescription,
|
||||||
@@ -476,17 +500,8 @@ export async function formWebhook(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mode = context.getMode() === 'manual' ? 'test' : 'production';
|
const mode = context.getMode() === 'manual' ? 'test' : 'production';
|
||||||
const formFields = (context.getNodeParameter('formFields.values', []) as FormFieldsParameter).map(
|
const formFields = context.getNodeParameter('formFields.values', []) as FormFieldsParameter;
|
||||||
(field) => {
|
|
||||||
if (field.fieldType === 'html') {
|
|
||||||
field.html = sanitizeHtml(field.html as string);
|
|
||||||
}
|
|
||||||
if (field.fieldType === 'hiddenField') {
|
|
||||||
field.fieldLabel = field.fieldName as string;
|
|
||||||
}
|
|
||||||
return field;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const method = context.getRequestObject().method;
|
const method = context.getRequestObject().method;
|
||||||
|
|
||||||
validateResponseModeConfiguration(context);
|
validateResponseModeConfiguration(context);
|
||||||
|
|||||||
Reference in New Issue
Block a user