Implement variable injection for global variables in the mjml editor for loading…
… and saving, handle parsing regex in groovy, ftl, and mjml, find the correct variables to update, replace non updated variables with a ftl version of the variables that won't error but return the variable expression for saving, and handle other problems
Showing
2 changed files
with
111 additions
and
19 deletions
... | @@ -181,11 +181,13 @@ along with this software (see the LICENSE.md file). If not, see | ... | @@ -181,11 +181,13 @@ along with this software (see the LICENSE.md file). If not, see |
181 | // console.log('response ', response) | 181 | // console.log('response ', response) |
182 | window.htmlLocation = response.htmlLocation; | 182 | window.htmlLocation = response.htmlLocation; |
183 | window.grapesLocation = response.grapesLocation; | 183 | window.grapesLocation = response.grapesLocation; |
184 | const url = new URL(window.location.href) | 184 | // const url = new URL(window.location.href) |
185 | url.searchParams.set('htmlLocation', response.htmlLocation); | 185 | // url.searchParams.set('htmlLocation', response.htmlLocation); |
186 | url.searchParams.set('grapesLocation', response.grapesLocation); | 186 | // url.searchParams.set('grapesLocation', response.grapesLocation); |
187 | window.history.pushState({}, '', url) | 187 | // window.history.pushState({}, '', url) |
188 | 188 | ||
189 | window.moquiVars = response.moquiVars; | ||
190 | // console.log('init window.moquiVars ', window.moquiVars) | ||
189 | const projectData = JSON.parse(response.data); | 191 | const projectData = JSON.parse(response.data); |
190 | // console.log('window.projectData ', window.projectData) | 192 | // console.log('window.projectData ', window.projectData) |
191 | 193 | ||
... | @@ -215,18 +217,20 @@ along with this software (see the LICENSE.md file). If not, see | ... | @@ -215,18 +217,20 @@ along with this software (see the LICENSE.md file). If not, see |
215 | // we have to properly update the body before the store and extract the | 217 | // we have to properly update the body before the store and extract the |
216 | // project data from the response result. | 218 | // project data from the response result. |
217 | onStore: data => { | 219 | onStore: data => { |
218 | return { id: window.grapesLocation, data, html:window.editor.runCommand('mjml-code-to-html')?.html } | 220 | return { id: window.grapesLocation, data, moquiVars:window.moquiVars, html:window.editor.runCommand('mjml-code-to-html')?.html } |
219 | }, | 221 | }, |
220 | onLoad: result => { | 222 | onLoad: result => { |
221 | if (result.resourceId !== null) { | 223 | if (result.resourceId !== null) { |
222 | const url = new URL(window.location.href) | 224 | const url = new URL(window.location.href) |
223 | url.searchParams.set('grapesLocation', result.grapesLocation); | 225 | url.searchParams.set('grapesLocation', result.grapesLocation); |
224 | url.searchParams.set('htmlLocation', result.htmlLocation); | 226 | // url.searchParams.set('htmlLocation', result.htmlLocation); |
225 | url.searchParams.set('emailTemplateId', result.emailTemplateId); | 227 | // url.searchParams.set('emailTemplateId', result.emailTemplateId); |
226 | window.history.pushState({}, '', url) | 228 | window.history.pushState({}, '', url) |
227 | window.grapesLocation = result.grapesLocation; | 229 | window.grapesLocation = result.grapesLocation; |
228 | window.htmlLocation = result.htmlLocation; | 230 | window.htmlLocation = result.htmlLocation; |
229 | window.emailTemplateId = result.emailTemplateId; | 231 | window.emailTemplateId = result.emailTemplateId; |
232 | window.moquiVars = result.moquiVars; | ||
233 | // console.log('onLoad window.moquiVars ', window.moquiVars) | ||
230 | } | 234 | } |
231 | // console.log('onLoad ', result) | 235 | // console.log('onLoad ', result) |
232 | return result.data | 236 | return result.data | ... | ... |
... | @@ -14,6 +14,17 @@ along with this software (see the LICENSE.md file). If not, see | ... | @@ -14,6 +14,17 @@ along with this software (see the LICENSE.md file). If not, see |
14 | --> | 14 | --> |
15 | <services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/service-definition-3.xsd"> | 15 | <services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/service-definition-3.xsd"> |
16 | 16 | ||
17 | <service verb="get" noun="GlobalGrapeVarList"> | ||
18 | <out-parameters> | ||
19 | <parameter name="baseLinkUrl"/> | ||
20 | <parameter name="currentYear"/> | ||
21 | </out-parameters> | ||
22 | <actions> | ||
23 | <set field="baseLinkUrl" from="!'production'.equals(System.getProperty('instance_purpose')) ? 'http://localhost:8080' : ec.web.getWebappRootUrl(true,true)" /> | ||
24 | <set field="currentYear" from="ec.user.nowTimestamp.format('yyyy')"/> | ||
25 | </actions> | ||
26 | </service> | ||
27 | |||
17 | <service verb="load" noun="GrapeJs"> | 28 | <service verb="load" noun="GrapeJs"> |
18 | <description>Load GrapesJs resource. Can be adapted to entity other than EmailTemplate, but must have data for the grapesLocation and htmlLocation to ensure safety of read and write of resources.</description> | 29 | <description>Load GrapesJs resource. Can be adapted to entity other than EmailTemplate, but must have data for the grapesLocation and htmlLocation to ensure safety of read and write of resources.</description> |
19 | <in-parameters> | 30 | <in-parameters> |
... | @@ -25,12 +36,12 @@ along with this software (see the LICENSE.md file). If not, see | ... | @@ -25,12 +36,12 @@ along with this software (see the LICENSE.md file). If not, see |
25 | <parameter name="grapesLocation"/> | 36 | <parameter name="grapesLocation"/> |
26 | <parameter name="htmlLocation"/> | 37 | <parameter name="htmlLocation"/> |
27 | <parameter name="data"/> | 38 | <parameter name="data"/> |
39 | <parameter name="emailTemplateId"/> | ||
40 | <parameter name="moquiVars"/> | ||
28 | </out-parameters> | 41 | </out-parameters> |
29 | <actions> | 42 | <actions> |
30 | <if condition="grapesLocation == 'null'"><set field="grapesLocation" from="null"/></if> | 43 | <if condition="grapesLocation == 'null'"><set field="grapesLocation" from="null"/></if> |
31 | <if condition="htmlLocation == 'null'"><set field="htmlLocation" from="null"/></if> | 44 | <if condition="htmlLocation == 'null'"><set field="htmlLocation" from="null"/></if> |
32 | <!-- <log level="warn" message="resourceId is ${resourceId} resourceId.getClass().getName() ${resourceId.getClass().getName()} resourceId == 'null' ${resourceId == 'null'} resourceId == null ${resourceId == null}"/>--> | ||
33 | <!-- <log level="warn" message="load context.toString() ${context.toString()}"/>--> | ||
34 | <entity-find-one entity-name="moqui.basic.email.EmailTemplate" value-field="emailTemplate" auto-field-map="[emailTemplateId:emailTemplateId]"/> | 45 | <entity-find-one entity-name="moqui.basic.email.EmailTemplate" value-field="emailTemplate" auto-field-map="[emailTemplateId:emailTemplateId]"/> |
35 | <if condition="!emailTemplate"><return error="true" message="Resource not found"/></if> | 46 | <if condition="!emailTemplate"><return error="true" message="Resource not found"/></if> |
36 | <set field="grapesLocation" from="emailTemplate?.grapesLocation"/> | 47 | <set field="grapesLocation" from="emailTemplate?.grapesLocation"/> |
... | @@ -42,25 +53,84 @@ along with this software (see the LICENSE.md file). If not, see | ... | @@ -42,25 +53,84 @@ along with this software (see the LICENSE.md file). If not, see |
42 | <set field="htmlFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.html')"/> | 53 | <set field="htmlFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.html')"/> |
43 | <set field="grapesFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.json')"/> | 54 | <set field="grapesFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.json')"/> |
44 | 55 | ||
45 | <set field="data" from="ec.resource.getLocationReference('dbresource://grapesjs/template/default.json').getText()"/> | 56 | <service-call name="mjml.MjmlServices.get#GlobalGrapeVarList" out-map="context"/> |
57 | <set field="dataRaw" from="ec.resource.getLocationReference('dbresource://grapesjs/template/default.json').getText()"/> | ||
46 | <set field="htmlLocation" from="grapesJsResource.location + '/' + htmlFile?.dbResourceId + '.html'"/> | 58 | <set field="htmlLocation" from="grapesJsResource.location + '/' + htmlFile?.dbResourceId + '.html'"/> |
47 | <set field="grapesLocation" from="grapesJsResource.location + '/' + grapesFile?.dbResourceId + '.json'"/> | 59 | <set field="grapesLocation" from="grapesJsResource.location + '/' + grapesFile?.dbResourceId + '.json'"/> |
48 | <script><![CDATA[ | 60 | <script><![CDATA[ |
49 | htmlFile.move(htmlLocation) | 61 | htmlFile.move(htmlLocation) |
50 | grapesFile.putText(data) | 62 | grapesFile.putText(dataRaw) |
51 | grapesFile.move(grapesLocation) | 63 | grapesFile.move(grapesLocation) |
52 | ]]></script> | 64 | ]]></script> |
53 | |||
54 | <service-call name="update#moqui.basic.email.EmailTemplate" in-map="[emailTemplateId:emailTemplateId,grapesLocation:grapesLocation,htmlLocation:htmlLocation]"/> | 65 | <service-call name="update#moqui.basic.email.EmailTemplate" in-map="[emailTemplateId:emailTemplateId,grapesLocation:grapesLocation,htmlLocation:htmlLocation]"/> |
66 | |||
67 | <service-call name="mjml.MjmlServices.get#GlobalGrapeVarList" out-map="globalGrapeVarList"/> | ||
68 | <script><![CDATA[ | ||
69 | context.putAll(globalGrapeVarList) | ||
70 | |||
71 | context.varList = globalGrapeVarList*.key.collect { '\\\$\\{'+it+'\\}' } | ||
72 | context.replaceVarRegexPattern = context.varList.join('|') | ||
73 | if (context.replaceVarRegexPattern) { | ||
74 | context.dataPre = context.dataRaw.replaceAll(context.replaceVarRegexPattern) { match -> | ||
75 | return "${match}" | ||
76 | } | ||
77 | } | ||
78 | |||
79 | context.allVarsRegexPattern = /\$\{[a-zA-Z_]\w*\}/ | ||
80 | context.errorVarList = context.dataRaw.findAll(context.allVarsRegexPattern).unique().collect { it - '${' - '}' } - globalGrapeVarList*.key | ||
81 | context.errorTemplateRegexPattern = errorVarList.collect { '\\\$\\{'+it+'\\}' }.join('|') | ||
82 | if (context.errorTemplateRegexPattern) { | ||
83 | context.dataPre = context.dataPre.replaceAll(context.errorTemplateRegexPattern) { match -> | ||
84 | def cleanMatch = match - '${' - '}' | ||
85 | return "\${" + cleanMatch + "!'\$\\{" + cleanMatch + "}'}" | ||
86 | } | ||
87 | } | ||
88 | ]]></script> | ||
89 | <!-- <log level="warn" message="dataPre is ${dataPre}"/>--> | ||
90 | <set field="grapesTempFile" from="grapesJsResource.makeFile(grapesFile?.dbResourceId + '.temp.json')"/> | ||
91 | <script>grapesTempFile.putText(dataPre)</script> | ||
92 | <set field="data" from="ec.resource.template(grapesTempFile.location, 'ftl')"/> | ||
93 | <script>grapesTempFile.delete()</script> | ||
94 | <!-- <log level="warn" message="data is ${data}"/>--> | ||
95 | <set field="moquiVars" from="globalGrapeVarList"/> | ||
55 | </then> | 96 | </then> |
56 | <else-if condition="grapesLocation && htmlLocation"> | 97 | <else-if condition="grapesLocation && htmlLocation"> |
57 | <set field="putDbResource" from="ec.resource.getLocationReference(grapesLocation)"/> | 98 | <set field="grapesFile" from="ec.resource.getLocationReference(grapesLocation)"/> |
58 | <!-- TODO: Is this a strong enough check to prevent unauthorized access? --> | 99 | <!-- TODO: Is this a strong enough check to prevent unauthorized access? --> |
59 | <if condition="!putDbResource || putDbResource.parent?.location != grapesJsResource.location"> | 100 | <if condition="!grapesFile || grapesFile.parent?.location != grapesJsResource.location"> |
60 | <return error="true" message="Resource not found"/> | 101 | <return error="true" message="Resource not found"/> |
61 | </if> | 102 | </if> |
62 | 103 | ||
63 | <set field="data" from="putDbResource.getText()"/> | 104 | <service-call name="mjml.MjmlServices.get#GlobalGrapeVarList" out-map="globalGrapeVarList"/> |
105 | <set field="dataRaw" from="grapesFile.getText()"/> | ||
106 | <script><![CDATA[ | ||
107 | context.putAll(globalGrapeVarList) | ||
108 | |||
109 | context.varList = globalGrapeVarList*.key.collect { '\\\$\\{'+it+'\\}' } | ||
110 | context.replaceVarRegexPattern = context.varList.join('|') | ||
111 | if (context.replaceVarRegexPattern) { | ||
112 | context.dataPre = context.dataRaw.replaceAll(context.replaceVarRegexPattern) { match -> | ||
113 | return "${match}" | ||
114 | } | ||
115 | } | ||
116 | |||
117 | context.allVarsRegexPattern = /\$\{[a-zA-Z_]\w*\}/ | ||
118 | context.errorVarList = context.dataRaw.findAll(context.allVarsRegexPattern).unique().collect { it - '${' - '}' } - globalGrapeVarList*.key | ||
119 | context.errorTemplateRegexPattern = errorVarList.collect { '\\\$\\{'+it+'\\}' }.join('|') | ||
120 | if (context.errorTemplateRegexPattern) { | ||
121 | context.dataPre = context.dataPre.replaceAll(context.errorTemplateRegexPattern) { match -> | ||
122 | def cleanMatch = match - '${' - '}' | ||
123 | return "\${" + cleanMatch + "!'\$\\{" + cleanMatch + "}'}" | ||
124 | } | ||
125 | } | ||
126 | ]]></script> | ||
127 | <!-- <log level="warn" message="dataPre is ${dataPre}"/>--> | ||
128 | <set field="grapesTempFile" from="grapesJsResource.makeFile(grapesFile?.dbResourceId + '.temp.json')"/> | ||
129 | <script>grapesTempFile.putText(dataPre)</script> | ||
130 | <set field="data" from="ec.resource.template(grapesTempFile.location, 'ftl')"/> | ||
131 | <script>grapesTempFile.delete()</script> | ||
132 | <!-- <log level="warn" message="data is ${data}"/>--> | ||
133 | <set field="moquiVars" from="globalGrapeVarList"/> | ||
64 | </else-if> | 134 | </else-if> |
65 | <else> | 135 | <else> |
66 | <return error="true" message="Resource not found"/> | 136 | <return error="true" message="Resource not found"/> |
... | @@ -75,6 +145,7 @@ along with this software (see the LICENSE.md file). If not, see | ... | @@ -75,6 +145,7 @@ along with this software (see the LICENSE.md file). If not, see |
75 | <parameter name="grapesLocation"/> | 145 | <parameter name="grapesLocation"/> |
76 | <parameter name="emailTemplateId"/> | 146 | <parameter name="emailTemplateId"/> |
77 | <parameter name="data"/> | 147 | <parameter name="data"/> |
148 | <parameter name="moquiVars"/> | ||
78 | <parameter name="html" allow-html="any"/> | 149 | <parameter name="html" allow-html="any"/> |
79 | </in-parameters> | 150 | </in-parameters> |
80 | <out-parameters> | 151 | <out-parameters> |
... | @@ -88,12 +159,15 @@ along with this software (see the LICENSE.md file). If not, see | ... | @@ -88,12 +159,15 @@ along with this software (see the LICENSE.md file). If not, see |
88 | <if condition="!emailTemplate"><return error="true" message="Resource not found"/></if> | 159 | <if condition="!emailTemplate"><return error="true" message="Resource not found"/></if> |
89 | <set field="grapesLocation" from="emailTemplate?.grapesLocation"/> | 160 | <set field="grapesLocation" from="emailTemplate?.grapesLocation"/> |
90 | <set field="htmlLocation" from="emailTemplate?.htmlLocation"/> | 161 | <set field="htmlLocation" from="emailTemplate?.htmlLocation"/> |
91 | <set field="data" from="groovy.json.JsonOutput.toJson(new groovy.json.JsonSlurper().parseText(ec.web.secureRequestParameters._requestBodyText).data)"/> | 162 | <set field="dataMap" from="new groovy.json.JsonSlurper().parseText(ec.web.secureRequestParameters._requestBodyText).data"/> |
163 | <!-- <log level="warn" message="dataMap is ${dataMap}"/>--> | ||
164 | <set field="moquiVars" from="new groovy.json.JsonSlurper().parseText(ec.web.secureRequestParameters._requestBodyText).moquiVars"/> | ||
165 | <set field="data" from="groovy.json.JsonOutput.toJson(dataMap)"/> | ||
92 | 166 | ||
93 | <set field="grapesJsResource" from="ec.resource.getLocationReference('dbresource://grapesjs/project')"/> | 167 | <set field="grapesJsResource" from="ec.resource.getLocationReference('dbresource://grapesjs/project')"/> |
94 | <if condition="!htmlLocation && !grapesLocation"> | 168 | <if condition="!htmlLocation && !grapesLocation"> |
95 | <then> | 169 | <then> |
96 | <!-- TODO: This should work, but isn't used anywhere and is untested. | 170 | <!-- TODO: This should work as of 2024 May 14, but isn't used anywhere and is untested. There will need to be work done based on the dataMap code |
97 | <set field="htmlFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.html')"/> | 171 | <set field="htmlFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.html')"/> |
98 | <set field="grapesFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.json')"/> | 172 | <set field="grapesFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.json')"/> |
99 | 173 | ||
... | @@ -118,7 +192,21 @@ along with this software (see the LICENSE.md file). If not, see | ... | @@ -118,7 +192,21 @@ along with this software (see the LICENSE.md file). If not, see |
118 | <if condition="!grapesFile || grapesFile.parent?.location != grapesJsResource.location"> | 192 | <if condition="!grapesFile || grapesFile.parent?.location != grapesJsResource.location"> |
119 | <return error="true" message="Resource not found"/> | 193 | <return error="true" message="Resource not found"/> |
120 | </if> | 194 | </if> |
121 | <script>grapesFile.putText(data)</script> | 195 | <script><![CDATA[ |
196 | context.replaceMoquiVarRegexPattern = context.moquiVars*.value.join('|') | ||
197 | |||
198 | if (context.replaceMoquiVarRegexPattern) { | ||
199 | context.dataPre = data.replaceAll(context.replaceMoquiVarRegexPattern) { match -> | ||
200 | def output = moquiVars.find { it.value == match }?.key | ||
201 | return '${' + output + '}' | ||
202 | } | ||
203 | } | ||
204 | ]]></script> | ||
205 | <script>grapesFile.putText(dataPre)</script> | ||
206 | |||
207 | <!-- <set field="dataRaw" from="grapesFile.getText()"/>--> | ||
208 | <!-- <set field="dataRawMap" from="new groovy.json.JsonSlurper().parseText(dataRaw)"/>--> | ||
209 | <!-- <script>grapesFile.putText(data)</script>--> | ||
122 | 210 | ||
123 | <set field="htmlFile" from="null"/> | 211 | <set field="htmlFile" from="null"/> |
124 | <if condition="htmlLocation"><then> | 212 | <if condition="htmlLocation"><then> |
... | @@ -141,7 +229,7 @@ along with this software (see the LICENSE.md file). If not, see | ... | @@ -141,7 +229,7 @@ along with this software (see the LICENSE.md file). If not, see |
141 | <if condition="!htmlFile || htmlFile.parent?.location != grapesJsResource.location"> | 229 | <if condition="!htmlFile || htmlFile.parent?.location != grapesJsResource.location"> |
142 | <return error="true" message="Resource not found"/> | 230 | <return error="true" message="Resource not found"/> |
143 | </if> | 231 | </if> |
144 | <script><![CDATA[htmlFile.putText(html)]]></script> | 232 | <!-- <script><![CDATA[htmlFile.putText(html)]]></script>--> |
145 | </else-if> | 233 | </else-if> |
146 | <else> | 234 | <else> |
147 | <return error="true" message="Resource not found"/> | 235 | <return error="true" message="Resource not found"/> | ... | ... |
-
Please register or sign in to post a comment