MjmlServices.xml 20.7 KB
<?xml version="1.0" encoding="UTF-8"?>
<!--
This software is in the public domain under CC0 1.0 Universal plus a 
Grant of Patent License.

To the extent possible under law, the author(s) have dedicated all
copyright and related and neighboring rights to this software to the
public domain worldwide. This software is distributed without any
warranty.

You should have received a copy of the CC0 Public Domain Dedication
along with this software (see the LICENSE.md file). If not, see
<http://creativecommons.org/publicdomain/zero/1.0/>.
-->
<services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/service-definition-3.xsd">

    <service verb="get" noun="GlobalGrapeVarList">
        <out-parameters>
            <parameter name="baseLinkUrl"/>
            <parameter name="currentYear"/>
        </out-parameters>
        <actions>
            <set field="baseLinkUrl" from="!'production'.equals(System.getProperty('instance_purpose')) ? 'http://localhost:8080' : ec.web.getWebappRootUrl(true,true)" />
            <set field="currentYear" from="ec.user.nowTimestamp.format('yyyy')"/>
        </actions>
    </service>

    <service verb="load" noun="GrapeJs">
        <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>
        <in-parameters>
            <parameter name="grapesLocation"/>
            <parameter name="htmlLocation"/>
            <parameter name="emailTemplateId" required="true"/>
        </in-parameters>
        <out-parameters>
            <parameter name="grapesLocation"/>
            <parameter name="grapesVersionName"/>
            <parameter name="grapesPublishedVersionName"/>
            <parameter name="htmlLocation"/>
            <parameter name="htmlVersionName"/>
            <parameter name="htmlPublishedVersionName"/>
            <parameter name="data"/>
            <parameter name="emailTemplateId"/>
            <parameter name="moquiVars"/>
        </out-parameters>
        <actions>
            <if condition="grapesLocation == 'null'"><set field="grapesLocation" from="null"/></if>
            <if condition="htmlLocation == 'null'"><set field="htmlLocation" from="null"/></if>
            <entity-find-one entity-name="moqui.basic.email.EmailTemplate" value-field="emailTemplate" auto-field-map="[emailTemplateId:emailTemplateId]"/>
            <if condition="!emailTemplate"><return error="true" message="Resource not found"/></if>
            <set field="grapesLocation" from="emailTemplate?.grapesLocation"/>
            <set field="grapesPublishedVersionName" from="emailTemplate?.grapesPublishedVersionName"/>
            <set field="htmlLocation" from="emailTemplate?.htmlLocation"/>
            <set field="htmlPublishedVersionName" from="emailTemplate?.htmlPublishedVersionName"/>

            <set field="grapesJsResource" from="ec.resource.getLocationReference('dbresource://grapesjs/project')"/>
            <if condition="!grapesLocation &amp;&amp; !htmlLocation">
                <then>
                    <set field="htmlFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.html')"/>
                    <set field="grapesFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.json')"/>

                    <service-call name="mjml.MjmlServices.get#GlobalGrapeVarList" out-map="context"/>
                    <set field="dataRaw" from="ec.resource.getLocationReference('dbresource://grapesjs/template/default.json').getText()"/>
                    <set field="htmlLocation" from="grapesJsResource.location + '/' + htmlFile?.dbResourceId + '.html'"/>
                    <set field="grapesLocation" from="grapesJsResource.location + '/' + grapesFile?.dbResourceId + '.json'"/>
                    <script><![CDATA[
                        htmlFile.move(htmlLocation)
                        grapesFile.putText(dataRaw)
                        grapesFile.move(grapesLocation)
                        ]]></script>
                    <set field="htmlVersionName" from="htmlFile?.getCurrentVersion()?.getVersionName()"/>
                    <set field="grapesVersionName" from="grapesFile?.getCurrentVersion()?.getVersionName()"/>
                    <service-call name="update#moqui.basic.email.EmailTemplate" in-map="[emailTemplateId:emailTemplateId,grapesLocation:grapesLocation,htmlLocation:htmlLocation]"/>

                    <service-call name="mjml.MjmlServices.get#GlobalGrapeVarList" out-map="globalGrapeVarList"/>
                    <script><![CDATA[
                        context.putAll(globalGrapeVarList)

                        context.varList = globalGrapeVarList*.key.collect { '\\\$\\{'+it+'\\}' }
                        context.replaceVarRegexPattern = context.varList.join('|')
                        if (context.replaceVarRegexPattern) {
                            context.dataPre = context.dataRaw.replaceAll(context.replaceVarRegexPattern) { match ->
                                return "${match}"
                            }
                        }

                        context.allVarsRegexPattern = /\$\{[a-zA-Z_]\w*\}/
                        context.errorVarList = context.dataRaw.findAll(context.allVarsRegexPattern).unique().collect { it - '${' - '}' } - globalGrapeVarList*.key
                        context.errorTemplateRegexPattern = errorVarList.collect { '\\\$\\{'+it+'\\}' }.join('|')
                        if (context.errorTemplateRegexPattern) {
                            context.dataPre = context.dataPre.replaceAll(context.errorTemplateRegexPattern) { match ->
                                def cleanMatch = match - '${' - '}'
                                return "\${" + cleanMatch + "!'\$\\{" + cleanMatch + "}'}"
                            }
                        }
                        ]]></script>
<!--                    <log level="warn" message="dataPre is ${dataPre}"/>-->
                    <script><![CDATA[
                        String location = "template.ftl";

                        freemarker.template.Template newTemplate;
                        Reader templateReader = null;

                        try {
                            templateReader = new StringReader(context.dataPre);
                            // Use the getFtlConfiguration method from ec.resource.templateRenderers.ftl
                            newTemplate = new freemarker.template.Template(location, templateReader, ec.resource.templateRenderers.ftl.getFtlConfiguration());
                        } catch (Exception e) {
                            throw new org.moqui.BaseArtifactException("Error while initializing template at " + location, e);
                        } finally {
                            if (templateReader != null) {
                                try { templateReader.close(); }
                                catch (Exception e) { logger.error("Error closing template reader", e); }
                            }
                        }
                        StringWriter sw = new StringWriter()
                        try {
                            newTemplate.createProcessingEnvironment(ec.contextStack, sw).process();
                        } catch (Exception e) { throw new org.moqui.BaseArtifactException("Error rendering template at " + location, e); }
                        context.data = sw.toString();
                        ]]></script>
<!--                    <log level="warn" message="data is ${data}"/>-->
                    <set field="moquiVars" from="globalGrapeVarList"/>
                </then>
                <else-if condition="grapesLocation &amp;&amp; htmlLocation">
                    <set field="grapesFile" from="ec.resource.getLocationReference(grapesLocation)"/>
                    <!-- TODO: Is this a strong enough check to prevent unauthorized access? -->
                    <if condition="!grapesFile || grapesFile.parent?.location != grapesJsResource.location">
                        <return error="true" message="Resource not found"/>
                    </if>
                    <set field="grapesVersionName" from="grapesFile?.getCurrentVersion()?.getVersionName()"/>
                    <set field="htmlVersionName" from="ec.resource.getLocationReference(htmlLocation)?.getCurrentVersion()?.getVersionName()"/>

                    <service-call name="mjml.MjmlServices.get#GlobalGrapeVarList" out-map="globalGrapeVarList"/>
                    <set field="dataRaw" from="grapesFile.getText()"/>
                    <script><![CDATA[
                        context.putAll(globalGrapeVarList)

                        context.varList = globalGrapeVarList*.key.collect { '\\\$\\{'+it+'\\}' }
                        context.replaceVarRegexPattern = context.varList.join('|')
                        if (context.replaceVarRegexPattern) {
                            context.dataPre = context.dataRaw.replaceAll(context.replaceVarRegexPattern) { match ->
                                return "${match}"
                            }
                        }

                        context.allVarsRegexPattern = /\$\{[a-zA-Z_]\w*\}/
                        context.errorVarList = context.dataRaw.findAll(context.allVarsRegexPattern).unique().collect { it - '${' - '}' } - globalGrapeVarList*.key
                        context.errorTemplateRegexPattern = errorVarList.collect { '\\\$\\{'+it+'\\}' }.join('|')
                        if (context.errorTemplateRegexPattern) {
                            context.dataPre = context.dataPre.replaceAll(context.errorTemplateRegexPattern) { match ->
                                def cleanMatch = match - '${' - '}'
                                return "\${" + cleanMatch + "!'\$\\{" + cleanMatch + "}'}"
                            }
                        }
                        ]]></script>
<!--                    <log level="warn" message="dataPre is ${dataPre}"/>-->
                    <script><![CDATA[
                        String location = "template.ftl";

                        freemarker.template.Template newTemplate;
                        Reader templateReader = null;

                        try {
                            templateReader = new StringReader(context.dataPre);
                            // Use the getFtlConfiguration method from ec.resource.templateRenderers.ftl
                            newTemplate = new freemarker.template.Template(location, templateReader, ec.resource.templateRenderers.ftl.getFtlConfiguration());
                        } catch (Exception e) {
                            throw new org.moqui.BaseArtifactException("Error while initializing template at " + location, e);
                        } finally {
                            if (templateReader != null) {
                                try { templateReader.close(); }
                                catch (Exception e) { logger.error("Error closing template reader", e); }
                            }
                        }
                        StringWriter sw = new StringWriter()
                        try {
                            newTemplate.createProcessingEnvironment(ec.contextStack, sw).process();
                        } catch (Exception e) { throw new org.moqui.BaseArtifactException("Error rendering template at " + location, e); }
                        context.data = sw.toString();
                        ]]></script>
<!--                    <log level="warn" message="data is ${data}"/>-->
                    <set field="moquiVars" from="globalGrapeVarList"/>
                </else-if>
                <else>
                    <return error="true" message="Resource not found"/>
                </else>
            </if>
        </actions>
    </service>
    <service verb="store" noun="GrapeJs" authenticate="anonymous-all">
        <description>Store 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>
        <in-parameters>
            <parameter name="htmlLocation"/>
            <parameter name="grapesLocation"/>
            <parameter name="emailTemplateId" required="true"/>
            <parameter name="data" required="true"/>
            <parameter name="moquiVars" required="true"/>
            <parameter name="html" allow-html="any" required="true"/>
            <parameter name="publish" default="false" type="Boolean" required="true"/>
        </in-parameters>
        <out-parameters>
            <parameter name="htmlLocation"/>
            <parameter name="htmlVersionName"/>
            <parameter name="htmlPublishedVersionName"/>
            <parameter name="grapesLocation"/>
            <parameter name="grapesVersionName"/>
            <parameter name="grapesPublishedVersionName"/>
        </out-parameters>
        <actions>
            <if condition="htmlLocation == 'null'"><set field="htmlLocation" from="null"/></if>
            <if condition="grapesLocation == 'null'"><set field="grapesLocation" from="null"/></if>
            <entity-find-one entity-name="moqui.basic.email.EmailTemplate" value-field="emailTemplate" auto-field-map="[emailTemplateId:emailTemplateId]"/>
            <if condition="!emailTemplate"><return error="true" message="Resource not found"/></if>
            <set field="grapesLocation" from="emailTemplate?.grapesLocation"/>
            <set field="grapesPublishedVersionName" from="emailTemplate?.grapesPublishedVersionName"/>
            <set field="htmlLocation" from="emailTemplate?.htmlLocation"/>
            <set field="htmlPublishedVersionName" from="emailTemplate?.htmlPublishedVersionName"/>
            <set field="dataMap" from="new groovy.json.JsonSlurper().parseText(ec.web.secureRequestParameters._requestBodyText).data"/>
<!--            <log level="warn" message="dataMap is ${dataMap}"/>-->
            <set field="moquiVars" from="new groovy.json.JsonSlurper().parseText(ec.web.secureRequestParameters._requestBodyText).moquiVars"/>
            <set field="data" from="groovy.json.JsonOutput.toJson(dataMap)"/>

            <set field="grapesJsResource" from="ec.resource.getLocationReference('dbresource://grapesjs/project')"/>
            <if condition="!htmlLocation &amp;&amp; !grapesLocation">
                <then>
<!--                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
                    <set field="htmlFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.html')"/>
                    <set field="grapesFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.json')"/>

                    <set field="htmlLocation" from="grapesJsResource.location + '/' + htmlFile?.dbResourceId + '.html'"/>
                    <set field="grapesLocation" from="grapesJsResource.location + '/' + grapesFile?.dbResourceId + '.json'"/>
                    <if condition="ec.resource.getLocationReference(htmlLocation).parent?.location != grapesJsResource.location">
                        <return error="true" message="Resource not found"/>
                    </if>
                    <if condition="ec.resource.getLocationReference(grapesLocation).parent?.location != grapesJsResource.location">
                        <return error="true" message="Resource not found"/>
                    </if>
                    <script><![CDATA[
                        htmlFile.putText(html)
                        htmlFile.move(htmlLocation)
                        grapesFile.putText(data)
                        grapesFile.move(grapesLocation)
                        ]]></script>-->
                    <return error="true" message="Resource not found"/>
                </then>
                <else-if condition="grapesLocation">
                    <set field="grapesFile" from="ec.resource.getLocationReference(grapesLocation)"/>
                    <if condition="!grapesFile || grapesFile.parent?.location != grapesJsResource.location">
                        <return error="true" message="Resource not found"/>
                    </if>
                    <script><![CDATA[
                        context.replaceMoquiVarRegexPattern = context.moquiVars*.value.join('|')

                        if (context.replaceMoquiVarRegexPattern) {
                            context.dataPre = data.replaceAll(context.replaceMoquiVarRegexPattern) { match ->
                                def output = moquiVars.find { it.value == match }?.key
                                return '${' + output + '}'
                            }
                        }
                    ]]></script>
                    <script>grapesFile.putText(dataPre)</script>
                    <set field="grapesVersionName" from="grapesFile?.getCurrentVersion()?.getVersionName()"/>

                    <set field="htmlFile" from="null"/>
                    <if condition="htmlLocation"><then>
                        <set field="htmlFile" from="ec.resource.getLocationReference(htmlLocation)"/>
                    </then><else>
                        <entity-find entity-name="moqui.basic.email.EmailTemplate" list="emailTemplateList">
                            <econdition field-name="grapesLocation"/>
                            <order-by field-name="-lastUpdatedStamp"/></entity-find>
                        <set field="emailTemplate" from="emailTemplateList.getFirst()"/>
                        <if condition="emailTemplateList.size() == 1 &amp;&amp; emailTemplate.htmlLocation"><then>
                            <set field="htmlFile" from="ec.resource.getLocationReference(emailTemplate.htmlLocation)"/>
                        </then><else>
                            <set field="htmlFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.html')"/>
                            <set field="htmlLocation" from="grapesJsResource.location + '/' + htmlFile?.dbResourceId + '.html'"/>
                            <script><![CDATA[htmlFile.move(htmlLocation)]]></script>

                            <service-call name="update#moqui.basic.email.EmailTemplate" in-map="[emailTemplateId:emailTemplateId,htmlLocation:htmlLocation]"/>
                        </else></if>
                    </else></if>
                    <if condition="!htmlFile || htmlFile.parent?.location != grapesJsResource.location">
                        <return error="true" message="Resource not found"/>
                    </if>
                    <script><![CDATA[htmlFile.putText(html)]]></script>
                    <set field="htmlVersionName" from="htmlFile?.getCurrentVersion()?.getVersionName()"/>
                    <if condition="publish">
                        <service-call name="update#moqui.basic.email.EmailTemplate" in-map="[emailTemplateId:emailTemplateId,
                            htmlPublishedVersionName:htmlVersionName,grapesPublishedVersionName:grapesVersionName]"/>
                        <set field="htmlPublishedVersionName" from="htmlVersionName"/>
                        <set field="grapesPublishedVersionName" from="grapesVersionName"/>
                    </if>
                </else-if>
                <else>
                    <return error="true" message="Resource not found"/>
                </else>
            </if>
        </actions>
    </service>
    <service verb="get" noun="AfterMjmlStore">
        <description>Get the data after storing the MJML data</description>
        <in-parameters>
            <parameter name="emailTemplateId" required="true"/>
        </in-parameters>
        <out-parameters>
            <parameter name="htmlLocation"/>
            <parameter name="htmlVersionName"/>
            <parameter name="htmlPublishedVersionName"/>
            <parameter name="grapesLocation"/>
            <parameter name="grapesVersionName"/>
            <parameter name="grapesPublishedVersionName"/>
        </out-parameters>
        <actions>
            <entity-find-one entity-name="moqui.basic.email.EmailTemplate" value-field="emailTemplate" auto-field-map="[emailTemplateId:emailTemplateId]"/>
            <if condition="!emailTemplate"><return error="true" message="Resource not found"/></if>
            <set field="grapesLocation" from="emailTemplate?.grapesLocation"/>
            <set field="grapesVersionName" from="ec.resource.getLocationReference(grapesLocation)?.getCurrentVersion()?.getVersionName()"/>
            <set field="grapesPublishedVersionName" from="emailTemplate?.grapesPublishedVersionName"/>
            <set field="htmlLocation" from="emailTemplate?.htmlLocation"/>
            <set field="htmlVersionName" from="ec.resource.getLocationReference(htmlLocation)?.getCurrentVersion()?.getVersionName()"/>
            <set field="htmlPublishedVersionName" from="emailTemplate?.htmlPublishedVersionName"/>
        </actions>
    </service>

</services>