UNPKG

n8n-nodes-base

Version:

Base nodes of n8n

387 lines 14.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Form = exports.formFieldsProperties = void 0; const n8n_workflow_1 = require("n8n-workflow"); const cssVariables_1 = require("./cssVariables"); const formCompletionUtils_1 = require("./utils/formCompletionUtils"); const formNodeUtils_1 = require("./utils/formNodeUtils"); const utils_1 = require("./utils/utils"); const configureWaitTillDate_util_1 = require("../../utils/sendAndWait/configureWaitTillDate.util"); const descriptions_1 = require("../../utils/sendAndWait/descriptions"); const common_descriptions_1 = require("../Form/common.descriptions"); const waitTimeProperties = [ { displayName: 'Limit Wait Time', name: 'limitWaitTime', type: 'boolean', default: false, description: 'Whether to limit the time this node should wait for a user response before execution resumes', }, ...(0, n8n_workflow_1.updateDisplayOptions)({ show: { limitWaitTime: [true], }, }, descriptions_1.limitWaitTimeProperties), ]; exports.formFieldsProperties = [ { displayName: 'Define Form', name: 'defineForm', type: 'options', noDataExpression: true, options: [ { name: 'Using Fields Below', value: 'fields', }, { name: 'Using JSON', value: 'json', }, ], default: 'fields', }, { displayName: 'Form Fields', name: 'jsonOutput', type: 'json', typeOptions: { rows: 5, }, default: '[\n {\n "fieldLabel": "Name",\n "placeholder": "enter your name",\n "requiredField": true\n },\n {\n "fieldLabel": "Age",\n "fieldType": "number",\n "placeholder": "enter your age"\n },\n {\n "fieldLabel": "Email",\n "fieldType": "email",\n "requiredField": true\n },\n {\n "fieldLabel": "Textarea",\n "fieldType": "textarea"\n },\n {\n "fieldLabel": "Dropdown Options",\n "fieldType": "dropdown",\n "fieldOptions": {\n "values": [\n {\n "option": "option 1"\n },\n {\n "option": "option 2"\n }\n ]\n },\n "requiredField": true\n },\n {\n "fieldLabel": "Checkboxes",\n "fieldType": "checkbox",\n "fieldOptions": {\n "values": [\n {\n "option": "option 1"\n },\n {\n "option": "option 2"\n }\n ]\n }\n },\n {\n "fieldLabel": "Radio",\n "fieldType": "radio",\n "fieldOptions": {\n "values": [\n {\n "option": "option 1"\n },\n {\n "option": "option 2"\n }\n ]\n }\n },\n {\n "fieldLabel": "Email",\n "fieldType": "email",\n "placeholder": "me@mail.con"\n },\n {\n "fieldLabel": "File",\n "fieldType": "file",\n "multipleFiles": true,\n "acceptFileTypes": ".jpg, .png"\n },\n {\n "fieldLabel": "Number",\n "fieldType": "number"\n },\n {\n "fieldLabel": "Password",\n "fieldType": "password"\n }\n]\n', validateType: 'form-fields', ignoreValidationDuringExecution: true, hint: '<a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.form/" target="_blank">See docs</a> for field syntax', displayOptions: { show: { defineForm: ['json'], }, }, }, { ...common_descriptions_1.formFields, displayOptions: { show: { '@version': [{ _cnd: { lt: 2.5 } }], defineForm: ['fields'] }, }, }, { ...common_descriptions_1.formFieldsDynamic, displayOptions: { show: { '@version': [{ _cnd: { gte: 2.5 } }], defineForm: ['fields'] }, }, }, ]; const pageProperties = (0, n8n_workflow_1.updateDisplayOptions)({ show: { operation: ['page'], }, }, [ ...exports.formFieldsProperties, ...waitTimeProperties, { displayName: 'Options', name: 'options', type: 'collection', placeholder: 'Add option', default: {}, options: [ { ...common_descriptions_1.formTitle, required: false }, common_descriptions_1.formDescription, { displayName: 'Button Label', name: 'buttonLabel', type: 'string', default: 'Submit', }, { displayName: 'Custom Form Styling', name: 'customCss', type: 'string', typeOptions: { rows: 10, editor: 'cssEditor', }, default: cssVariables_1.cssVariables.trim(), description: 'Override default styling of the public form interface with CSS', }, ], }, ]); const completionProperties = (0, n8n_workflow_1.updateDisplayOptions)({ show: { operation: ['completion'], }, }, [ { // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased displayName: 'On n8n Form Submission', name: 'respondWith', type: 'options', default: 'text', options: [ { name: 'Show Completion Screen', value: 'text', description: 'Show a response text to the user', }, { name: 'Redirect to URL', value: 'redirect', description: 'Redirect the user to a URL', }, { name: 'Show Text', value: 'showText', description: 'Display simple text or HTML', }, { name: 'Return Binary File', value: 'returnBinary', description: 'Return incoming binary file', }, ], }, { displayName: 'URL', name: 'redirectUrl', validateType: 'url', type: 'string', default: '', required: true, displayOptions: { show: { respondWith: ['redirect'], }, }, }, { displayName: 'Completion Title', name: 'completionTitle', type: 'string', default: '', required: true, displayOptions: { show: { respondWith: ['text', 'returnBinary'], }, }, }, { displayName: 'Completion Message', name: 'completionMessage', type: 'string', default: '', typeOptions: { rows: 2, }, displayOptions: { show: { respondWith: ['text', 'returnBinary'], }, }, }, { displayName: 'Text', name: 'responseText', type: 'string', displayOptions: { show: { respondWith: ['showText'], }, }, typeOptions: { rows: 2, }, default: '', placeholder: 'e.g. Thanks for filling the form', description: 'The text to display on the page. Use HTML to show a customized web page.', }, { displayName: 'Input Data Field Name', name: 'inputDataFieldName', type: 'string', displayOptions: { show: { respondWith: ['returnBinary'], }, }, default: 'data', placeholder: 'e.g. data', description: 'Find the name of input field containing the binary data to return in the Input panel on the left, in the Binary tab', hint: 'The name of the input field containing the binary file data to be returned', }, ...waitTimeProperties, { displayName: 'Options', name: 'options', type: 'collection', placeholder: 'Add option', default: {}, options: [ { ...common_descriptions_1.formTitle, required: false, displayName: 'Completion Page Title' }, { displayName: 'Custom Form Styling', name: 'customCss', type: 'string', typeOptions: { rows: 10, editor: 'cssEditor', }, default: cssVariables_1.cssVariables.trim(), description: 'Override default styling of the public form interface with CSS', }, ], displayOptions: { show: { respondWith: ['text', 'returnBinary', 'redirect'], }, }, }, ]); class Form extends n8n_workflow_1.Node { nodeInputData = []; description = { displayName: 'n8n Form', name: 'form', icon: 'file:form.svg', group: ['input'], // since trigger and node are sharing descriptions and logic we need to sync the versions // and keep them aligned in both nodes version: [1, 2.3, 2.4, 2.5], description: 'Generate webforms in n8n and pass their responses to the workflow', defaults: { name: 'Form', }, builderHint: { relatedNodes: [ { nodeType: 'n8n-nodes-base.formTrigger', relationHint: 'Creates additional pages/steps after the trigger', }, ], }, inputs: [n8n_workflow_1.NodeConnectionTypes.Main], outputs: [n8n_workflow_1.NodeConnectionTypes.Main], waitingNodeTooltip: '=Execution will continue when form is submitted on <a href="{{ $execution.resumeFormUrl }}" target="_blank">{{ $execution.resumeFormUrl }}</a>', webhooks: [ { name: 'default', httpMethod: 'GET', responseMode: 'onReceived', path: '', restartWebhook: true, isFullPath: true, nodeType: 'form', }, { name: 'default', httpMethod: 'POST', responseMode: 'responseNode', path: '', restartWebhook: true, isFullPath: true, nodeType: 'form', }, ], properties: [ { displayName: 'An n8n Form Trigger node must be set up before this node', name: 'triggerNotice', type: 'notice', default: '', }, { displayName: 'Page Type', name: 'operation', type: 'options', default: 'page', noDataExpression: true, options: [ { name: 'Next Form Page', value: 'page', }, { name: 'Form Ending', value: 'completion', }, ], }, ...pageProperties, ...completionProperties, ], }; async webhook(context) { const res = context.getResponseObject(); const operation = context.getNodeParameter('operation', ''); const trigger = (0, formNodeUtils_1.getFormTriggerNode)(context); const mode = context.evaluateExpression(`{{ $('${trigger.name}').first().json.formMode }}`); const defineForm = context.getNodeParameter('defineForm', false); let fields = []; if (defineForm === 'json') { try { const jsonOutput = context.getNodeParameter('jsonOutput', '', { rawExpressions: true, }); fields = (0, n8n_workflow_1.tryToParseJsonToFormFields)((0, utils_1.resolveRawData)(context, jsonOutput)); } catch (error) { throw new n8n_workflow_1.NodeOperationError(context.getNode(), error.message, { description: error.message, type: mode === 'test' ? 'manual-form-test' : undefined, }); } } else { fields = context.getNodeParameter('formFields.values', []); } const method = context.getRequestObject().method; if (operation === 'completion' && method === 'GET') { return await (0, formCompletionUtils_1.renderFormCompletion)(context, res, trigger); } if (operation === 'completion' && method === 'POST') { return { workflowData: [context.evaluateExpression('{{ $input.all() }}')], }; } if (method === 'GET') { return await (0, formNodeUtils_1.renderFormNode)(context, res, trigger, fields, mode); } let useWorkflowTimezone = context.evaluateExpression(`{{ $('${trigger.name}').params.options?.useWorkflowTimezone }}`); if (useWorkflowTimezone === undefined && trigger?.typeVersion > 2) { useWorkflowTimezone = true; } const returnItem = await (0, utils_1.prepareFormReturnItem)(context, fields, mode, useWorkflowTimezone); return { webhookResponse: { status: 200 }, workflowData: [[returnItem]], }; } async execute(context) { const operation = context.getNodeParameter('operation', 0); if (operation === 'completion') { this.nodeInputData = context.getInputData(); } const parentNodes = context.getParentNodes(context.getNode().name); const hasFormTrigger = parentNodes.some((node) => node.type === n8n_workflow_1.FORM_TRIGGER_NODE_TYPE); if (!hasFormTrigger) { throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Form Trigger node must be set before this node'); } const childNodes = context.getChildNodes(context.getNode().name); const hasNextPage = childNodes.some((node) => node.type === n8n_workflow_1.FORM_NODE_TYPE); if (operation === 'completion' && hasNextPage) { throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Completion has to be the last Form node in the workflow'); } const waitTill = (0, configureWaitTillDate_util_1.configureWaitTillDate)(context, 'root'); await context.putExecutionToWait(waitTill); context.sendResponse({ headers: { location: context.evaluateExpression('{{ $execution.resumeFormUrl }}', 0), }, statusCode: 307, }); return [context.getInputData()]; } } exports.Form = Form; //# sourceMappingURL=Form.node.js.map