diff --git a/src/bp/core/misc/templating.ts b/src/bp/core/misc/templating.ts new file mode 100644 index 00000000000..40fa5123193 --- /dev/null +++ b/src/bp/core/misc/templating.ts @@ -0,0 +1,15 @@ +import _ from 'lodash' +import Mustache from 'mustache' + +export function renderRecursive(template: string, context: any): string { + let i = 0 + while (i < 3 && containsTemplate(template)) { + template = Mustache.render(template, context) + i++ + } + return template +} + +function containsTemplate(value: string) { + return _.isString(value) && value.indexOf('{{') < value.indexOf('}}') +} diff --git a/src/bp/core/services/cms.ts b/src/bp/core/services/cms.ts index 0d468e10776..0ccafe31207 100644 --- a/src/bp/core/services/cms.ts +++ b/src/bp/core/services/cms.ts @@ -1,6 +1,7 @@ import { IO, Logger } from 'botpress/sdk' import { ContentElement, ContentType, SearchParams } from 'botpress/sdk' import { KnexExtension } from 'common/knex' +import { renderRecursive } from 'core/misc/templating' import { inject, injectable, tagged } from 'inversify' import Knex from 'knex' import _ from 'lodash' @@ -431,7 +432,7 @@ export class CMSService implements IDisposeOnExit { throw new Error(`Content element "${contentId}" not found`) } - _.set(content, 'previewPath', Mustache.render(content.previewText, args)) + _.set(content, 'previewPath', renderRecursive(content.previewText, args)) const text = _.get(content.formData, 'text') const variations = _.get(content.formData, 'variations') diff --git a/src/bp/core/services/dialog/instruction/strategy.ts b/src/bp/core/services/dialog/instruction/strategy.ts index bebef401463..506b2c234eb 100644 --- a/src/bp/core/services/dialog/instruction/strategy.ts +++ b/src/bp/core/services/dialog/instruction/strategy.ts @@ -3,10 +3,10 @@ import { CMSService } from 'core/services/cms' import { EventEngine } from 'core/services/middleware/event-engine' import { inject, injectable, tagged } from 'inversify' import _ from 'lodash' -import Mustache from 'mustache' import { NodeVM } from 'vm2' import { container } from '../../../app.inversify' +import { renderRecursive } from '../../../misc/templating' import { TYPES } from '../../../types' import ActionService from '../../action/action-service' import { VmRunner } from '../../action/vm' @@ -109,16 +109,7 @@ export class ActionStrategy implements InstructionStrategy { const view = { event } - args = _.mapValues(args, value => { - if (this.containsTemplate(value)) { - const output = Mustache.render(value, view) - if (this.containsTemplate(output)) { - return Mustache.render(output, view) - } - return output - } - return value - }) + args = _.mapValues(args, value => renderRecursive(value, args)) const hasAction = await this.actionService.forBot(botId).hasAction(actionName) if (!hasAction) { @@ -128,10 +119,6 @@ export class ActionStrategy implements InstructionStrategy { await this.actionService.forBot(botId).runAction(actionName, event, args) return ProcessingResult.none() } - - private containsTemplate(value: string) { - return _.isString(value) && value.indexOf('{{') < value.indexOf('}}') - } } @injectable() diff --git a/src/bp/ui-studio/src/web/views/FlowBuilder/common/action.jsx b/src/bp/ui-studio/src/web/views/FlowBuilder/common/action.jsx index e56e57fb135..91865650f8c 100755 --- a/src/bp/ui-studio/src/web/views/FlowBuilder/common/action.jsx +++ b/src/bp/ui-studio/src/web/views/FlowBuilder/common/action.jsx @@ -80,7 +80,7 @@ class ActionItem extends Component { const stripDots = str => str.replace(/\./g, '--dot--') const restoreDots = str => str.replace(/--dot--/g, '.') - const htmlTpl = textContent.replace(/{{([a-z$@0-9. _-]*?)}}/gi, x => { + const htmlTpl = textContent.replace(/{{([a-z$@0-9. '"\[\]_-]*?)}}/gi, x => { const name = stripDots(x.replace(/{|}/g, '')) vars[name] = '' + x + '' return '{' + stripDots(x) + '}'