76e7c5a5 by Acetousk Committed by GitHub

Merge pull request #2 from moqui/test-upstream

Test upstream
2 parents 76e88ff2 3db488ac
...@@ -11,3 +11,4 @@ To install run (with moqui-framework): ...@@ -11,3 +11,4 @@ To install run (with moqui-framework):
11 ### Setup 11 ### Setup
12 12
13 - Have all email templates setup as a ProductStoreEmail to EmailTemplate see: the ProductStore's Emails page 13 - Have all email templates setup as a ProductStoreEmail to EmailTemplate see: the ProductStore's Emails page
14
......
1 /*
2 * This software is in the public domain under CC0 1.0 Universal plus a
3 * Grant of Patent License.
4 *
5 * To the extent possible under law, the author(s) have dedicated all
6 * copyright and related and neighboring rights to this software to the
7 * public domain worldwide. This software is distributed without any
8 * warranty.
9 *
10 * You should have received a copy of the CC0 Public Domain Dedication
11 * along with this software (see the LICENSE.md file). If not, see
12 * <http://creativecommons.org/publicdomain/zero/1.0/>.
13 */
14
15 apply plugin: 'groovy'
16
17 version = '3.0.0'
18 // sourceCompatibility = '1.8'
19 def moquiDir = file(projectDir.absolutePath + '/../../..')
20 def frameworkDir = file(moquiDir.absolutePath + '/framework')
21
22 repositories {
23 flatDir name: 'localLib', dirs: frameworkDir.absolutePath + '/lib'
24 mavenCentral()
25 }
26
27 // Log4J has annotation processors, disable to avoid warning
28 tasks.withType(JavaCompile) { options.compilerArgs << "-proc:none" }
29 tasks.withType(GroovyCompile) { options.compilerArgs << "-proc:none" }
30
31 dependencies {
32 implementation project(':framework')
33 testImplementation project(':framework').configurations.testImplementation.allDependencies
34 }
35
36 // by default the Java plugin runs test on build, change to not do that (only run test if explicit task)
37 // no longer workds as of gradle 4.8 or possibly earlier, use clear() instead: check.dependsOn.remove(test)
38 check.dependsOn.clear()
39
40 jar {
41 destinationDirectory = file(projectDir.absolutePath + '/lib')
42 // this is required to change from the default that includes the path to this module (ie 'runtime/component/mjml')
43 archiveBaseName = 'moqui-mjml'
44 }
45 task cleanLib(type: Delete) { delete file(jar.archivePath) }
46 clean.dependsOn cleanLib
47
48 test {
49 useJUnitPlatform()
50 testLogging { events "passed", "skipped", "failed" }
51 testLogging.showStandardStreams = true; testLogging.showExceptions = true
52 maxParallelForks 1
53
54 dependsOn cleanTest
55
56 systemProperty 'moqui.runtime', moquiDir.absolutePath + '/runtime'
57 systemProperty 'moqui.conf', 'conf/MoquiDevConf.xml'
58 systemProperty 'moqui.init.static', 'true'
59 maxHeapSize = "512M"
60
61 classpath += files(sourceSets.main.output.classesDirs)
62 // filter out classpath entries that don't exist (gradle adds a bunch of these), or ElasticSearch JarHell will blow up
63 classpath = classpath.filter { it.exists() }
64
65 beforeTest { descriptor ->
66 logger.lifecycle("Running test: ${descriptor}")
67 }
68 }
...@@ -13,6 +13,7 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -13,6 +13,7 @@ along with this software (see the LICENSE.md file). If not, see
13 <http://creativecommons.org/publicdomain/zero/1.0/>. 13 <http://creativecommons.org/publicdomain/zero/1.0/>.
14 --> 14 -->
15 <entity-facade-xml type="seed"> 15 <entity-facade-xml type="seed">
16 <!-- ========= Screen Theme Resource for GrapesJs / mjml -->
16 <moqui.basic.Enumeration description="Mjml Theme Type" enumId="STT_MJML" enumTypeId="ScreenThemeType"/> 17 <moqui.basic.Enumeration description="Mjml Theme Type" enumId="STT_MJML" enumTypeId="ScreenThemeType"/>
17 <moqui.screen.ScreenTheme screenThemeId="MJML_DEFAULT" screenThemeTypeEnumId="STT_MJML" 18 <moqui.screen.ScreenTheme screenThemeId="MJML_DEFAULT" screenThemeTypeEnumId="STT_MJML"
18 description="Mjml Default Theme"/> 19 description="Mjml Default Theme"/>
...@@ -26,13 +27,20 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -26,13 +27,20 @@ along with this software (see the LICENSE.md file). If not, see
26 <moqui.screen.ScreenThemeResource screenThemeId="MJML_DEFAULT" sequenceNum="110" resourceTypeEnumId="STRT_HEADER_TITLE" 27 <moqui.screen.ScreenThemeResource screenThemeId="MJML_DEFAULT" sequenceNum="110" resourceTypeEnumId="STRT_HEADER_TITLE"
27 resourceValue="Email Editor"/> 28 resourceValue="Email Editor"/>
28 29
30 <!-- ========= DB Resource for storing project and template files -->
29 <moqui.resource.DbResource resourceId="GRAPESJS" filename="grapesjs" isFile="N"/> 31 <moqui.resource.DbResource resourceId="GRAPESJS" filename="grapesjs" isFile="N"/>
30 <moqui.resource.DbResource resourceId="GRAPESJS_PROJECT" filename="project" isFile="N" parentResourceId="GRAPESJS"/> 32 <moqui.resource.DbResource resourceId="GRAPESJS_PROJECT" filename="project" isFile="N" parentResourceId="GRAPESJS"/>
31 <moqui.resource.DbResource resourceId="GRAPESJS_TEMPLATE" filename="template" isFile="N" parentResourceId="GRAPESJS"/> 33 <moqui.resource.DbResource resourceId="GRAPESJS_TEMPLATE" filename="template" isFile="N" parentResourceId="GRAPESJS"/>
32 34
33 <dbResources resourceId="GRAPESJS_TEMPLATE_DEFAULT" filename="default.json" isFile="Y" parentResourceId="GRAPESJS_TEMPLATE"> 35 <dbResources resourceId="GRAPESJS_TEMPLATE_DEFAULT" filename="default.json" isFile="Y" parentResourceId="GRAPESJS_TEMPLATE">
34 <file rootVersionName="01" mimeType="text/json" versionName="01"> 36 <file rootVersionName="01" mimeType="text/json" versionName="01">
35 <fileData><![CDATA[eyJhc3NldHMiOltdLCJzdHlsZXMiOlt7InNlbGVjdG9ycyI6W10sInNlbGVjdG9yc0FkZCI6IiNvdXRsb29rIGEiLCJzdHlsZSI6eyJwYWRkaW5nLXRvcCI6IjBweCIsInBhZGRpbmctcmlnaHQiOiIwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjBweCIsInBhZGRpbmctbGVmdCI6IjBweCJ9fSx7InNlbGVjdG9ycyI6W10sInNlbGVjdG9yc0FkZCI6ImJvZHkiLCJzdHlsZSI6eyJtYXJnaW4tdG9wIjoiMHB4IiwibWFyZ2luLXJpZ2h0IjoiMHB4IiwibWFyZ2luLWJvdHRvbSI6IjBweCIsIm1hcmdpbi1sZWZ0IjoiMHB4IiwicGFkZGluZy10b3AiOiIwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMHB4IiwicGFkZGluZy1ib3R0b20iOiIwcHgiLCJwYWRkaW5nLWxlZnQiOiIwcHgiLCJ0ZXh0LXNpemUtYWRqdXN0IjoiMTAwJSJ9fSx7InNlbGVjdG9ycyI6W10sInNlbGVjdG9yc0FkZCI6InRhYmxlLCB0ZCIsInN0eWxlIjp7ImJvcmRlci1jb2xsYXBzZSI6ImNvbGxhcHNlIn19LHsic2VsZWN0b3JzIjpbXSwic2VsZWN0b3JzQWRkIjoiaW1nIiwic3R5bGUiOnsiYm9yZGVyLXRvcC13aWR0aCI6IjBweCIsImJvcmRlci1yaWdodC13aWR0aCI6IjBweCIsImJvcmRlci1ib3R0b20td2lkdGgiOiIwcHgiLCJib3JkZXItbGVmdC13aWR0aCI6IjBweCIsImJvcmRlci10b3Atc3R5bGUiOiJpbml0aWFsIiwiYm9yZGVyLXJpZ2h0LXN0eWxlIjoiaW5pdGlhbCIsImJvcmRlci1ib3R0b20tc3R5bGUiOiJpbml0aWFsIiwiYm9yZGVyLWxlZnQtc3R5bGUiOiJpbml0aWFsIiwiYm9yZGVyLXRvcC1jb2xvciI6ImluaXRpYWwiLCJib3JkZXItcmlnaHQtY29sb3IiOiJpbml0aWFsIiwiYm9yZGVyLWJvdHRvbS1jb2xvciI6ImluaXRpYWwiLCJib3JkZXItbGVmdC1jb2xvciI6ImluaXRpYWwiLCJib3JkZXItaW1hZ2Utc291cmNlIjoiaW5pdGlhbCIsImJvcmRlci1pbWFnZS1zbGljZSI6ImluaXRpYWwiLCJib3JkZXItaW1hZ2Utd2lkdGgiOiJpbml0aWFsIiwiYm9yZGVyLWltYWdlLW91dHNldCI6ImluaXRpYWwiLCJib3JkZXItaW1hZ2UtcmVwZWF0IjoiaW5pdGlhbCIsImhlaWdodCI6ImF1dG8iLCJsaW5lLWhlaWdodCI6IjEwMCUiLCJvdXRsaW5lLWNvbG9yIjoiaW5pdGlhbCIsIm91dGxpbmUtc3R5bGUiOiJub25lIiwib3V0bGluZS13aWR0aCI6ImluaXRpYWwiLCJ0ZXh0LWRlY29yYXRpb24tbGluZSI6Im5vbmUiLCJ0ZXh0LWRlY29yYXRpb24tdGhpY2tuZXNzIjoiaW5pdGlhbCIsInRleHQtZGVjb3JhdGlvbi1zdHlsZSI6ImluaXRpYWwiLCJ0ZXh0LWRlY29yYXRpb24tY29sb3IiOiJpbml0aWFsIn19LHsic2VsZWN0b3JzIjpbXSwic2VsZWN0b3JzQWRkIjoicCIsInN0eWxlIjp7ImRpc3BsYXkiOiJibG9jayIsIm1hcmdpbi10b3AiOiIxM3B4IiwibWFyZ2luLXJpZ2h0IjoiMHB4IiwibWFyZ2luLWJvdHRvbSI6IjEzcHgiLCJtYXJnaW4tbGVmdCI6IjBweCJ9fSx7InNlbGVjdG9ycyI6WyJtai1jb2x1bW4tcGVyLTEwMCJdLCJzdHlsZSI6eyJ3aWR0aCI6IjEwMCUgIWltcG9ydGFudCIsIm1heC13aWR0aCI6IjEwMCUifSwibWVkaWFUZXh0Ijoib25seSBzY3JlZW4gYW5kIChtaW4td2lkdGg6IDQ4MHB4KSIsImF0UnVsZVR5cGUiOiJtZWRpYSJ9LHsic2VsZWN0b3JzIjpbXSwic2VsZWN0b3JzQWRkIjoiLm1vei10ZXh0LWh0bWwgLm1qLWNvbHVtbi1wZXItMTAwIiwic3R5bGUiOnsid2lkdGgiOiIxMDAlICFpbXBvcnRhbnQiLCJtYXgtd2lkdGgiOiIxMDAlIn19LHsic2VsZWN0b3JzIjpbIm1qLWNvbHVtbi1wZXItNTAiXSwic3R5bGUiOnsid2lkdGgiOiI1MCUgIWltcG9ydGFudCIsIm1heC13aWR0aCI6IjUwJSJ9LCJtZWRpYVRleHQiOiJvbmx5IHNjcmVlbiBhbmQgKG1pbi13aWR0aDogNDgwcHgpIiwiYXRSdWxlVHlwZSI6Im1lZGlhIn0seyJzZWxlY3RvcnMiOltdLCJzZWxlY3RvcnNBZGQiOiIubW96LXRleHQtaHRtbCAubWotY29sdW1uLXBlci01MCIsInN0eWxlIjp7IndpZHRoIjoiNTAlICFpbXBvcnRhbnQiLCJtYXgtd2lkdGgiOiI1MCUifX0seyJzZWxlY3RvcnMiOlsibWotY29sdW1uLXBlci0zMy0zMzMzMzMzMzMzMzMzMzYiXSwic3R5bGUiOnsid2lkdGgiOiIzMy4zMzMzJSAhaW1wb3J0YW50IiwibWF4LXdpZHRoIjoiMzMuMzMzMyUifSwibWVkaWFUZXh0Ijoib25seSBzY3JlZW4gYW5kIChtaW4td2lkdGg6IDQ4MHB4KSIsImF0UnVsZVR5cGUiOiJtZWRpYSJ9LHsic2VsZWN0b3JzIjpbXSwic2VsZWN0b3JzQWRkIjoiLm1vei10ZXh0LWh0bWwgLm1qLWNvbHVtbi1wZXItMzMtMzMzMzMzMzMzMzMzMzM2Iiwic3R5bGUiOnsid2lkdGgiOiIzMy4zMzMzJSAhaW1wb3J0YW50IiwibWF4LXdpZHRoIjoiMzMuMzMzMyUifX1dLCJwYWdlcyI6W3siZnJhbWVzIjpbeyJjb21wb25lbnQiOnsidHlwZSI6IndyYXBwZXIiLCJzdHlsYWJsZSI6WyJiYWNrZ3JvdW5kIiwiYmFja2dyb3VuZC1jb2xvciIsImJhY2tncm91bmQtaW1hZ2UiLCJiYWNrZ3JvdW5kLXJlcGVhdCIsImJhY2tncm91bmQtYXR0YWNobWVudCIsImJhY2tncm91bmQtcG9zaXRpb24iLCJiYWNrZ3JvdW5kLXNpemUiXSwiY29tcG9uZW50cyI6W3sidGFnTmFtZSI6Im1qbWwiLCJ0eXBlIjoibWptbCIsImNvbXBvbmVudHMiOlt7InRhZ05hbWUiOiJtai1oZWFkIiwidHlwZSI6Im1qLWhlYWQiLCJjb21wb25lbnRzIjpbeyJ0YWdOYW1lIjoibWotZm9udCIsInR5cGUiOiJtai1mb250Iiwic3R5bGUiOnsibmFtZSI6IkJhcmxvdyIsImhyZWYiOiJodHRwczovL2ZvbnRzLmdvb2dsZWFwaXMuY29tL2Nzcz9mYW1pbHk9QmFybG93In0sImF0dHJpYnV0ZXMiOnsibmFtZSI6IkJhcmxvdyIsImhyZWYiOiJodHRwczovL2ZvbnRzLmdvb2dsZWFwaXMuY29tL2Nzcz9mYW1pbHk9QmFybG93Iiwic3R5bGUiOiJuYW1lOkJhcmxvdztocmVmOmh0dHBzOi8vZm9udHMuZ29vZ2xlYXBpcy5jb20vY3NzP2ZhbWlseT1CYXJsb3c7In19LHsidGFnTmFtZSI6Im1qLXN0eWxlIiwidHlwZSI6Im1qLXN0eWxlIiwiY29tcG9uZW50cyI6W3sidHlwZSI6InRleHRub2RlIiwiY29udGVudCI6IlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5zbG9nYW4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmQ6ICMwMDA7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgIn1dfV19LHsidGFnTmFtZSI6Im1qLWJvZHkiLCJ0eXBlIjoibWotYm9keSIsInN0eWxlIjp7IndpZHRoIjoiNjAwcHgifSwiYXR0cmlidXRlcyI6eyJ3aWR0aCI6IjYwMHB4Iiwic3R5bGUiOiJ3aWR0aDo2MDBweDsifSwiY29tcG9uZW50cyI6W3sidHlwZSI6ImNvbW1lbnQiLCJjb250ZW50IjoiIENvbXBhbnkgSGVhZGVyICJ9LHsidGFnTmFtZSI6Im1qLXNlY3Rpb24iLCJ0eXBlIjoibWotc2VjdGlvbiIsInN0eWxlIjp7ImJhY2tncm91bmQtY29sb3IiOiIjZjBmMGYwIiwicGFkZGluZy1sZWZ0IjoiMHB4IiwicGFkZGluZy1yaWdodCI6IjBweCIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInRleHQtYWxpZ24iOiJjZW50ZXIifSwiYXR0cmlidXRlcyI6eyJiYWNrZ3JvdW5kLWNvbG9yIjoiI2YwZjBmMCIsInBhZGRpbmctbGVmdCI6IjBweCIsInBhZGRpbmctcmlnaHQiOiIwcHgiLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJ0ZXh0LWFsaWduIjoiY2VudGVyIiwic3R5bGUiOiJiYWNrZ3JvdW5kLWNvbG9yOiNmMGYwZjA7cGFkZGluZy1sZWZ0OjBweDtwYWRkaW5nLXJpZ2h0OjBweDtwYWRkaW5nLXRvcDoxMHB4O3BhZGRpbmctYm90dG9tOjEwcHg7dGV4dC1hbGlnbjpjZW50ZXI7In0sImNvbXBvbmVudHMiOlt7InRhZ05hbWUiOiJtai1jb2x1bW4iLCJ0eXBlIjoibWotY29sdW1uIiwic3R5bGUiOnsiYm9yZGVyIjoiMTBweCBzb2xpZCAjRjQ1RTQzIiwidmVydGljYWwtYWxpZ24iOiJ0b3AifSwiYXR0cmlidXRlcyI6eyJib3JkZXIiOiIxMHB4IHNvbGlkICNGNDVFNDMiLCJ2ZXJ0aWNhbC1hbGlnbiI6InRvcCIsInN0eWxlIjoiYm9yZGVyOjEwcHggc29saWQgI0Y0NUU0Mzt2ZXJ0aWNhbC1hbGlnbjp0b3A7In0sImNvbXBvbmVudHMiOlt7InRhZ05hbWUiOiJtai10ZXh0IiwidHlwZSI6Im1qLXRleHQiLCJzdHlsZSI6eyJmb250LWZhbWlseSI6IkJhcmxvdyIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInBhZGRpbmctcmlnaHQiOiIyNXB4IiwicGFkZGluZy1sZWZ0IjoiMjVweCIsImZvbnQtc2l6ZSI6IjEzcHgiLCJsaW5lLWhlaWdodCI6IjIycHgiLCJhbGlnbiI6ImxlZnQifSwiYXR0cmlidXRlcyI6eyJmb250LWZhbWlseSI6IkJhcmxvdyIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInBhZGRpbmctcmlnaHQiOiIyNXB4IiwicGFkZGluZy1sZWZ0IjoiMjVweCIsImZvbnQtc2l6ZSI6IjEzcHgiLCJsaW5lLWhlaWdodCI6IjIycHgiLCJhbGlnbiI6ImxlZnQiLCJzdHlsZSI6ImZvbnQtZmFtaWx5OkJhcmxvdztwYWRkaW5nLXRvcDoxMHB4O3BhZGRpbmctYm90dG9tOjEwcHg7cGFkZGluZy1yaWdodDoyNXB4O3BhZGRpbmctbGVmdDoyNXB4O2ZvbnQtc2l6ZToxM3B4O2xpbmUtaGVpZ2h0OjIycHg7YWxpZ246bGVmdDsifSwiY29tcG9uZW50cyI6W3sidHlwZSI6InRleHRub2RlIiwiY29udGVudCI6IkEgZmlyc3QgbGluZSBvZiB0ZXh0In1dfSx7InRhZ05hbWUiOiJtai1zcGFjZXIiLCJ0eXBlIjoibWotc3BhY2VyIiwic3R5bGUiOnsiaGVpZ2h0IjoiNTBweCJ9LCJhdHRyaWJ1dGVzIjp7ImhlaWdodCI6IjUwcHgiLCJzdHlsZSI6ImhlaWdodDo1MHB4OyJ9LCJjb21wb25lbnRzIjpbeyJ0eXBlIjoidGV4dG5vZGUiLCJjb250ZW50IjoiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIn1dfV19XX0seyJ0eXBlIjoiY29tbWVudCIsImNvbnRlbnQiOiIgSW1hZ2UgSGVhZGVyICJ9LHsidGFnTmFtZSI6Im1qLXNlY3Rpb24iLCJ0eXBlIjoibWotc2VjdGlvbiIsInN0eWxlIjp7ImJhY2tncm91bmQtdXJsIjoiaHR0cDovLzEuYnAuYmxvZ3Nwb3QuY29tLy1UUHJmaHhiWXBEWS9VaDNSZWZ6azAySS9BQUFBQUFBQUx3OC81c1VKMFVVR1l1dy9zMTYwMC9OZXcrWW9yaytpbitUaGUrMTk2MCdzKy0rNzAncysoMikuanBnIiwiYmFja2dyb3VuZC1zaXplIjoiY292ZXIiLCJiYWNrZ3JvdW5kLXJlcGVhdCI6Im5vLXJlcGVhdCIsInBhZGRpbmctbGVmdCI6IjBweCIsInBhZGRpbmctcmlnaHQiOiIwcHgiLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJ0ZXh0LWFsaWduIjoiY2VudGVyIn0sImF0dHJpYnV0ZXMiOnsiYmFja2dyb3VuZC11cmwiOiJodHRwOi8vMS5icC5ibG9nc3BvdC5jb20vLVRQcmZoeGJZcERZL1VoM1JlZnprMDJJL0FBQUFBQUFBTHc4LzVzVUowVVVHWXV3L3MxNjAwL05ldytZb3JrK2luK1RoZSsxOTYwJ3MrLSs3MCdzKygyKS5qcGciLCJiYWNrZ3JvdW5kLXNpemUiOiJjb3ZlciIsImJhY2tncm91bmQtcmVwZWF0Ijoibm8tcmVwZWF0IiwicGFkZGluZy1sZWZ0IjoiMHB4IiwicGFkZGluZy1yaWdodCI6IjBweCIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInRleHQtYWxpZ24iOiJjZW50ZXIiLCJzdHlsZSI6ImJhY2tncm91bmQtdXJsOmh0dHA6Ly8xLmJwLmJsb2dzcG90LmNvbS8tVFByZmh4YllwRFkvVWgzUmVmemswMkkvQUFBQUFBQUFMdzgvNXNVSjBVVUdZdXcvczE2MDAvTmV3K1lvcmsraW4rVGhlKzE5NjAncystKzcwJ3MrKDIpLmpwZztiYWNrZ3JvdW5kLXNpemU6Y292ZXI7YmFja2dyb3VuZC1yZXBlYXQ6bm8tcmVwZWF0O3BhZGRpbmctbGVmdDowcHg7cGFkZGluZy1yaWdodDowcHg7cGFkZGluZy10b3A6MTBweDtwYWRkaW5nLWJvdHRvbToxMHB4O3RleHQtYWxpZ246Y2VudGVyOyJ9LCJjb21wb25lbnRzIjpbeyJ0YWdOYW1lIjoibWotY29sdW1uIiwidHlwZSI6Im1qLWNvbHVtbiIsInN0eWxlIjp7InZlcnRpY2FsLWFsaWduIjoidG9wIn0sImF0dHJpYnV0ZXMiOnsidmVydGljYWwtYWxpZ24iOiJ0b3AiLCJzdHlsZSI6InZlcnRpY2FsLWFsaWduOnRvcDsifSwiY29tcG9uZW50cyI6W3sidGFnTmFtZSI6Im1qLXRleHQiLCJ0eXBlIjoibWotdGV4dCIsInN0eWxlIjp7ImNzcy1jbGFzcyI6InNsb2dhbiIsImFsaWduIjoiY2VudGVyIiwiY29sb3IiOiIjZmZmIiwiZm9udC1zaXplIjoiNDBweCIsImZvbnQtZmFtaWx5IjoiSGVsdmV0aWNhIE5ldWUiLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMjVweCIsInBhZGRpbmctbGVmdCI6IjI1cHgiLCJsaW5lLWhlaWdodCI6IjIycHgifSwiYXR0cmlidXRlcyI6eyJjc3MtY2xhc3MiOiJzbG9nYW4iLCJhbGlnbiI6ImNlbnRlciIsImNvbG9yIjoiI2ZmZiIsImZvbnQtc2l6ZSI6IjQwcHgiLCJmb250LWZhbWlseSI6IkhlbHZldGljYSBOZXVlIiwicGFkZGluZy10b3AiOiIxMHB4IiwicGFkZGluZy1ib3R0b20iOiIxMHB4IiwicGFkZGluZy1yaWdodCI6IjI1cHgiLCJwYWRkaW5nLWxlZnQiOiIyNXB4IiwibGluZS1oZWlnaHQiOiIyMnB4Iiwic3R5bGUiOiJjc3MtY2xhc3M6c2xvZ2FuO2FsaWduOmNlbnRlcjtjb2xvcjojZmZmO2ZvbnQtc2l6ZTo0MHB4O2ZvbnQtZmFtaWx5OkhlbHZldGljYSBOZXVlO3BhZGRpbmctdG9wOjEwcHg7cGFkZGluZy1ib3R0b206MTBweDtwYWRkaW5nLXJpZ2h0OjI1cHg7cGFkZGluZy1sZWZ0OjI1cHg7bGluZS1oZWlnaHQ6MjJweDsifSwiY29tcG9uZW50cyI6W3sidHlwZSI6InRleHRub2RlIiwiY29udGVudCI6IlNsb2dhbiBoZXJlIn1dfV19XX0seyJ0eXBlIjoiY29tbWVudCIsImNvbnRlbnQiOiIgSW50cm8gdGV4dCAifSx7InRhZ05hbWUiOiJtai13cmFwcGVyIiwidHlwZSI6Im1qLXdyYXBwZXIiLCJzdHlsZSI6eyJiYWNrZ3JvdW5kLWNvbG9yIjoiI2ZmZTlmNyIsInBhZGRpbmciOiIxMHB4In0sImF0dHJpYnV0ZXMiOnsiYmFja2dyb3VuZC1jb2xvciI6IiNmZmU5ZjciLCJwYWRkaW5nIjoiMTBweCIsInN0eWxlIjoiYmFja2dyb3VuZC1jb2xvcjojZmZlOWY3O3BhZGRpbmc6MTBweDsifSwiY29tcG9uZW50cyI6W3sidGFnTmFtZSI6Im1qLXNlY3Rpb24iLCJ0eXBlIjoibWotc2VjdGlvbiIsInN0eWxlIjp7ImJhY2tncm91bmQtY29sb3IiOiIjZWFlZmZhIiwicGFkZGluZy1sZWZ0IjoiMHB4IiwicGFkZGluZy1yaWdodCI6IjBweCIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInRleHQtYWxpZ24iOiJjZW50ZXIifSwiYXR0cmlidXRlcyI6eyJiYWNrZ3JvdW5kLWNvbG9yIjoiI2VhZWZmYSIsInBhZGRpbmctbGVmdCI6IjBweCIsInBhZGRpbmctcmlnaHQiOiIwcHgiLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJ0ZXh0LWFsaWduIjoiY2VudGVyIiwic3R5bGUiOiJiYWNrZ3JvdW5kLWNvbG9yOiNlYWVmZmE7cGFkZGluZy1sZWZ0OjBweDtwYWRkaW5nLXJpZ2h0OjBweDtwYWRkaW5nLXRvcDoxMHB4O3BhZGRpbmctYm90dG9tOjEwcHg7dGV4dC1hbGlnbjpjZW50ZXI7In0sImNvbXBvbmVudHMiOlt7InRhZ05hbWUiOiJtai1ncm91cCIsInR5cGUiOiJtai1ncm91cCIsInN0eWxlIjp7ImJhY2tncm91bmQtY29sb3IiOiIjZmZmYWRkIiwidmVydGljYWwtYWxpZ24iOiJ0b3AifSwiYXR0cmlidXRlcyI6eyJiYWNrZ3JvdW5kLWNvbG9yIjoiI2ZmZmFkZCIsInZlcnRpY2FsLWFsaWduIjoidG9wIiwic3R5bGUiOiJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZhZGQ7dmVydGljYWwtYWxpZ246dG9wOyJ9LCJjb21wb25lbnRzIjpbeyJ0YWdOYW1lIjoibWotY29sdW1uIiwidHlwZSI6Im1qLWNvbHVtbiIsInN0eWxlIjp7InZlcnRpY2FsLWFsaWduIjoidG9wIn0sImF0dHJpYnV0ZXMiOnsidmVydGljYWwtYWxpZ24iOiJ0b3AiLCJzdHlsZSI6InZlcnRpY2FsLWFsaWduOnRvcDsifSwiY29tcG9uZW50cyI6W3sidGFnTmFtZSI6Im1qLXRleHQiLCJ0eXBlIjoibWotdGV4dCIsInN0eWxlIjp7ImZvbnQtc3R5bGUiOiJpdGFsaWMiLCJmb250LXNpemUiOiIyMHB4IiwiZm9udC1mYW1pbHkiOiJIZWx2ZXRpY2EgTmV1ZSIsImNvbG9yIjoiIzYyNjI2MiIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInBhZGRpbmctcmlnaHQiOiIyNXB4IiwicGFkZGluZy1sZWZ0IjoiMjVweCIsImxpbmUtaGVpZ2h0IjoiMjJweCIsImFsaWduIjoibGVmdCJ9LCJhdHRyaWJ1dGVzIjp7ImZvbnQtc3R5bGUiOiJpdGFsaWMiLCJmb250LXNpemUiOiIyMHB4IiwiZm9udC1mYW1pbHkiOiJIZWx2ZXRpY2EgTmV1ZSIsImNvbG9yIjoiIzYyNjI2MiIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInBhZGRpbmctcmlnaHQiOiIyNXB4IiwicGFkZGluZy1sZWZ0IjoiMjVweCIsImxpbmUtaGVpZ2h0IjoiMjJweCIsImFsaWduIjoibGVmdCIsInN0eWxlIjoiZm9udC1zdHlsZTppdGFsaWM7Zm9udC1zaXplOjIwcHg7Zm9udC1mYW1pbHk6SGVsdmV0aWNhIE5ldWU7Y29sb3I6IzYyNjI2MjtwYWRkaW5nLXRvcDoxMHB4O3BhZGRpbmctYm90dG9tOjEwcHg7cGFkZGluZy1yaWdodDoyNXB4O3BhZGRpbmctbGVmdDoyNXB4O2xpbmUtaGVpZ2h0OjIycHg7YWxpZ246bGVmdDsifSwiY29tcG9uZW50cyI6W3sidHlwZSI6InRleHRub2RlIiwiY29udGVudCI6Ik15IEF3ZXNvbWUgVGV4dCJ9XX0seyJ0YWdOYW1lIjoibWotdGV4dCIsInR5cGUiOiJtai10ZXh0Iiwic3R5bGUiOnsiY29sb3IiOiIjNTI1MjUyIiwicGFkZGluZy10b3AiOiIxMHB4IiwicGFkZGluZy1ib3R0b20iOiIxMHB4IiwicGFkZGluZy1yaWdodCI6IjI1cHgiLCJwYWRkaW5nLWxlZnQiOiIyNXB4IiwiZm9udC1zaXplIjoiMTNweCIsImxpbmUtaGVpZ2h0IjoiMjJweCIsImFsaWduIjoibGVmdCJ9LCJhdHRyaWJ1dGVzIjp7ImNvbG9yIjoiIzUyNTI1MiIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInBhZGRpbmctcmlnaHQiOiIyNXB4IiwicGFkZGluZy1sZWZ0IjoiMjVweCIsImZvbnQtc2l6ZSI6IjEzcHgiLCJsaW5lLWhlaWdodCI6IjIycHgiLCJhbGlnbiI6ImxlZnQiLCJzdHlsZSI6ImNvbG9yOiM1MjUyNTI7cGFkZGluZy10b3A6MTBweDtwYWRkaW5nLWJvdHRvbToxMHB4O3BhZGRpbmctcmlnaHQ6MjVweDtwYWRkaW5nLWxlZnQ6MjVweDtmb250LXNpemU6MTNweDtsaW5lLWhlaWdodDoyMnB4O2FsaWduOmxlZnQ7In0sImNvbXBvbmVudHMiOlt7InR5cGUiOiJ0ZXh0bm9kZSIsImNvbnRlbnQiOiJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gUHJvaW4gcnV0cnVtIGVuaW0gZWdldCBtYWduYSBlZmZpY2l0dXIsIGV1IHNlbXBlciBhdWd1ZSBzZW1wZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFsaXF1YW0gZXJhdCB2b2x1dHBhdC4gQ3JhcyBpZCBkdWkgbGVjdHVzLiBWZXN0aWJ1bHVtIHNlZCBmaW5pYnVzIGxlY3R1cywgc2l0IGFtZXQgc3VzY2lwaXQgbmliaC4gUHJvaW4gbmVjXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbW1vZG8gcHVydXMuIFNlZCBlZ2V0IG51bGxhIGVsaXQuIE51bGxhIGFsaXF1ZXQgbW9sbGlzIGZhdWNpYnVzLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ9XX0seyJ0YWdOYW1lIjoibWotYnV0dG9uIiwidHlwZSI6Im1qLWJ1dHRvbiIsInN0eWxlIjp7ImJhY2tncm91bmQtY29sb3IiOiIjRjQ1RTQzIiwiaHJlZiI6IiMiLCJib3JkZXItcmFkaXVzIjoiM3B4IiwiZm9udC1zaXplIjoiMTNweCIsImZvbnQtd2VpZ2h0IjoiNDAwIiwiY29sb3IiOiIjZmZmZmZmIiwidmVydGljYWwtYWxpZ24iOiJtaWRkbGUiLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMjVweCIsInBhZGRpbmctbGVmdCI6IjI1cHgiLCJhbGlnbiI6ImNlbnRlciJ9LCJhdHRyaWJ1dGVzIjp7ImJhY2tncm91bmQtY29sb3IiOiIjRjQ1RTQzIiwiaHJlZiI6IiMiLCJib3JkZXItcmFkaXVzIjoiM3B4IiwiZm9udC1zaXplIjoiMTNweCIsImZvbnQtd2VpZ2h0IjoiNDAwIiwiY29sb3IiOiIjZmZmZmZmIiwidmVydGljYWwtYWxpZ24iOiJtaWRkbGUiLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMjVweCIsInBhZGRpbmctbGVmdCI6IjI1cHgiLCJhbGlnbiI6ImNlbnRlciIsInN0eWxlIjoiYmFja2dyb3VuZC1jb2xvcjojRjQ1RTQzO2hyZWY6Iztib3JkZXItcmFkaXVzOjNweDtmb250LXNpemU6MTNweDtmb250LXdlaWdodDo0MDA7Y29sb3I6I2ZmZmZmZjt2ZXJ0aWNhbC1hbGlnbjptaWRkbGU7cGFkZGluZy10b3A6MTBweDtwYWRkaW5nLWJvdHRvbToxMHB4O3BhZGRpbmctcmlnaHQ6MjVweDtwYWRkaW5nLWxlZnQ6MjVweDthbGlnbjpjZW50ZXI7In0sImNvbXBvbmVudHMiOlt7InR5cGUiOiJ0ZXh0bm9kZSIsImNvbnRlbnQiOiJMZWFybiBtb3JlIn1dfV19XX1dfV19LHsidHlwZSI6ImNvbW1lbnQiLCJjb250ZW50IjoiIFNpZGUgaW1hZ2UgIn0seyJ0YWdOYW1lIjoibWotc2VjdGlvbiIsInR5cGUiOiJtai1zZWN0aW9uIiwic3R5bGUiOnsiYmFja2dyb3VuZC1jb2xvciI6IndoaXRlIiwicGFkZGluZy1sZWZ0IjoiMHB4IiwicGFkZGluZy1yaWdodCI6IjBweCIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInRleHQtYWxpZ24iOiJjZW50ZXIifSwiYXR0cmlidXRlcyI6eyJiYWNrZ3JvdW5kLWNvbG9yIjoid2hpdGUiLCJwYWRkaW5nLWxlZnQiOiIwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMHB4IiwicGFkZGluZy10b3AiOiIxMHB4IiwicGFkZGluZy1ib3R0b20iOiIxMHB4IiwidGV4dC1hbGlnbiI6ImNlbnRlciIsInN0eWxlIjoiYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTtwYWRkaW5nLWxlZnQ6MHB4O3BhZGRpbmctcmlnaHQ6MHB4O3BhZGRpbmctdG9wOjEwcHg7cGFkZGluZy1ib3R0b206MTBweDt0ZXh0LWFsaWduOmNlbnRlcjsifSwiY29tcG9uZW50cyI6W3sidGFnTmFtZSI6Im1qLWNvbHVtbiIsInR5cGUiOiJtai1jb2x1bW4iLCJzdHlsZSI6eyJ2ZXJ0aWNhbC1hbGlnbiI6InRvcCJ9LCJhdHRyaWJ1dGVzIjp7InZlcnRpY2FsLWFsaWduIjoidG9wIiwic3R5bGUiOiJ2ZXJ0aWNhbC1hbGlnbjp0b3A7In0sImNvbXBvbmVudHMiOlt7InRhZ05hbWUiOiJtai1pbWFnZSIsInR5cGUiOiJtai1pbWFnZSIsInN0eWxlIjp7IndpZHRoIjoiMjAwcHgiLCJzcmMiOiJodHRwczovL2Rlc2lnbnNwZWxsLmZpbGVzLndvcmRwcmVzcy5jb20vMjAxMi8wMS9zY2lvbGluby1wYXJpcy1idy5qcGciLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMjVweCIsInBhZGRpbmctbGVmdCI6IjI1cHgiLCJhbGlnbiI6ImNlbnRlciJ9LCJhdHRyaWJ1dGVzIjp7IndpZHRoIjoiMjAwcHgiLCJzcmMiOiJodHRwczovL2Rlc2lnbnNwZWxsLmZpbGVzLndvcmRwcmVzcy5jb20vMjAxMi8wMS9zY2lvbGluby1wYXJpcy1idy5qcGciLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMjVweCIsInBhZGRpbmctbGVmdCI6IjI1cHgiLCJhbGlnbiI6ImNlbnRlciIsInN0eWxlIjoid2lkdGg6MjAwcHg7c3JjOmh0dHBzOi8vZGVzaWduc3BlbGwuZmlsZXMud29yZHByZXNzLmNvbS8yMDEyLzAxL3NjaW9saW5vLXBhcmlzLWJ3LmpwZztwYWRkaW5nLXRvcDoxMHB4O3BhZGRpbmctYm90dG9tOjEwcHg7cGFkZGluZy1yaWdodDoyNXB4O3BhZGRpbmctbGVmdDoyNXB4O2FsaWduOmNlbnRlcjsifSwiY29tcG9uZW50cyI6W3sidHlwZSI6InRleHRub2RlIiwiY29udGVudCI6IlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ9XX1dfSx7InRhZ05hbWUiOiJtai1jb2x1bW4iLCJ0eXBlIjoibWotY29sdW1uIiwic3R5bGUiOnsidmVydGljYWwtYWxpZ24iOiJ0b3AifSwiYXR0cmlidXRlcyI6eyJ2ZXJ0aWNhbC1hbGlnbiI6InRvcCIsInN0eWxlIjoidmVydGljYWwtYWxpZ246dG9wOyJ9LCJjb21wb25lbnRzIjpbeyJ0YWdOYW1lIjoibWotdGV4dCIsInR5cGUiOiJtai10ZXh0Iiwic3R5bGUiOnsiZm9udC1zdHlsZSI6Iml0YWxpYyIsImZvbnQtc2l6ZSI6IjIwcHgiLCJmb250LWZhbWlseSI6IkhlbHZldGljYSBOZXVlIiwiY29sb3IiOiIjNjI2MjYyIiwicGFkZGluZy10b3AiOiIxMHB4IiwicGFkZGluZy1ib3R0b20iOiIxMHB4IiwicGFkZGluZy1yaWdodCI6IjI1cHgiLCJwYWRkaW5nLWxlZnQiOiIyNXB4IiwibGluZS1oZWlnaHQiOiIyMnB4IiwiYWxpZ24iOiJsZWZ0In0sImF0dHJpYnV0ZXMiOnsiZm9udC1zdHlsZSI6Iml0YWxpYyIsImZvbnQtc2l6ZSI6IjIwcHgiLCJmb250LWZhbWlseSI6IkhlbHZldGljYSBOZXVlIiwiY29sb3IiOiIjNjI2MjYyIiwicGFkZGluZy10b3AiOiIxMHB4IiwicGFkZGluZy1ib3R0b20iOiIxMHB4IiwicGFkZGluZy1yaWdodCI6IjI1cHgiLCJwYWRkaW5nLWxlZnQiOiIyNXB4IiwibGluZS1oZWlnaHQiOiIyMnB4IiwiYWxpZ24iOiJsZWZ0Iiwic3R5bGUiOiJmb250LXN0eWxlOml0YWxpYztmb250LXNpemU6MjBweDtmb250LWZhbWlseTpIZWx2ZXRpY2EgTmV1ZTtjb2xvcjojNjI2MjYyO3BhZGRpbmctdG9wOjEwcHg7cGFkZGluZy1ib3R0b206MTBweDtwYWRkaW5nLXJpZ2h0OjI1cHg7cGFkZGluZy1sZWZ0OjI1cHg7bGluZS1oZWlnaHQ6MjJweDthbGlnbjpsZWZ0OyJ9LCJjb21wb25lbnRzIjpbeyJ0eXBlIjoidGV4dG5vZGUiLCJjb250ZW50IjoiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGaW5kIGFtYXppbmcgcGxhY2VzIC4uLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAifV19LHsidGFnTmFtZSI6Im1qLXRleHQiLCJ0eXBlIjoibWotdGV4dCIsInN0eWxlIjp7ImNvbG9yIjoiIzUyNTI1MiIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInBhZGRpbmctcmlnaHQiOiIyNXB4IiwicGFkZGluZy1sZWZ0IjoiMjVweCIsImZvbnQtc2l6ZSI6IjEzcHgiLCJsaW5lLWhlaWdodCI6IjIycHgiLCJhbGlnbiI6ImxlZnQifSwiYXR0cmlidXRlcyI6eyJjb2xvciI6IiM1MjUyNTIiLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMjVweCIsInBhZGRpbmctbGVmdCI6IjI1cHgiLCJmb250LXNpemUiOiIxM3B4IiwibGluZS1oZWlnaHQiOiIyMnB4IiwiYWxpZ24iOiJsZWZ0Iiwic3R5bGUiOiJjb2xvcjojNTI1MjUyO3BhZGRpbmctdG9wOjEwcHg7cGFkZGluZy1ib3R0b206MTBweDtwYWRkaW5nLXJpZ2h0OjI1cHg7cGFkZGluZy1sZWZ0OjI1cHg7Zm9udC1zaXplOjEzcHg7bGluZS1oZWlnaHQ6MjJweDthbGlnbjpsZWZ0OyJ9LCJjb21wb25lbnRzIjpbeyJ0eXBlIjoidGV4dG5vZGUiLCJjb250ZW50IjoiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LiBQcm9pbiBydXRydW0gZW5pbSBlZ2V0IG1hZ25hIGVmZmljaXR1ciwgZXUgc2VtcGVyIGF1Z3VlIHNlbXBlci5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFsaXF1YW0gZXJhdCB2b2x1dHBhdC4gQ3JhcyBpZCBkdWkgbGVjdHVzLiBWZXN0aWJ1bHVtIHNlZCBmaW5pYnVzIGxlY3R1cy4ifV19XX1dfSx7InRhZ05hbWUiOiJtai1zZWN0aW9uIiwidHlwZSI6Im1qLXNlY3Rpb24iLCJzdHlsZSI6eyJwYWRkaW5nLWxlZnQiOiIwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMHB4IiwicGFkZGluZy10b3AiOiIxMHB4IiwicGFkZGluZy1ib3R0b20iOiIxMHB4IiwidGV4dC1hbGlnbiI6ImNlbnRlciJ9LCJhdHRyaWJ1dGVzIjp7InBhZGRpbmctbGVmdCI6IjBweCIsInBhZGRpbmctcmlnaHQiOiIwcHgiLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJ0ZXh0LWFsaWduIjoiY2VudGVyIiwic3R5bGUiOiJwYWRkaW5nLWxlZnQ6MHB4O3BhZGRpbmctcmlnaHQ6MHB4O3BhZGRpbmctdG9wOjEwcHg7cGFkZGluZy1ib3R0b206MTBweDt0ZXh0LWFsaWduOmNlbnRlcjsifSwiY29tcG9uZW50cyI6W3sidGFnTmFtZSI6Im1qLWNvbHVtbiIsInR5cGUiOiJtai1jb2x1bW4iLCJzdHlsZSI6eyJ2ZXJ0aWNhbC1hbGlnbiI6InRvcCJ9LCJhdHRyaWJ1dGVzIjp7InZlcnRpY2FsLWFsaWduIjoidG9wIiwic3R5bGUiOiJ2ZXJ0aWNhbC1hbGlnbjp0b3A7In0sImNvbXBvbmVudHMiOlt7InRhZ05hbWUiOiJtai10ZXh0IiwidHlwZSI6Im1qLXRleHQiLCJzdHlsZSI6eyJmb250LXN0eWxlIjoiaXRhbGljIiwiZm9udC1zaXplIjoiMjBweCIsImZvbnQtZmFtaWx5IjoiSGVsdmV0aWNhIE5ldWUiLCJjb2xvciI6IiM2MjYyNjIiLCJhbGlnbiI6ImNlbnRlciIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInBhZGRpbmctcmlnaHQiOiIyNXB4IiwicGFkZGluZy1sZWZ0IjoiMjVweCIsImxpbmUtaGVpZ2h0IjoiMjJweCJ9LCJhdHRyaWJ1dGVzIjp7ImZvbnQtc3R5bGUiOiJpdGFsaWMiLCJmb250LXNpemUiOiIyMHB4IiwiZm9udC1mYW1pbHkiOiJIZWx2ZXRpY2EgTmV1ZSIsImNvbG9yIjoiIzYyNjI2MiIsImFsaWduIjoiY2VudGVyIiwicGFkZGluZy10b3AiOiIxMHB4IiwicGFkZGluZy1ib3R0b20iOiIxMHB4IiwicGFkZGluZy1yaWdodCI6IjI1cHgiLCJwYWRkaW5nLWxlZnQiOiIyNXB4IiwibGluZS1oZWlnaHQiOiIyMnB4Iiwic3R5bGUiOiJmb250LXN0eWxlOml0YWxpYztmb250LXNpemU6MjBweDtmb250LWZhbWlseTpIZWx2ZXRpY2EgTmV1ZTtjb2xvcjojNjI2MjYyO2FsaWduOmNlbnRlcjtwYWRkaW5nLXRvcDoxMHB4O3BhZGRpbmctYm90dG9tOjEwcHg7cGFkZGluZy1yaWdodDoyNXB4O3BhZGRpbmctbGVmdDoyNXB4O2xpbmUtaGVpZ2h0OjIycHg7In0sImNvbXBvbmVudHMiOlt7InR5cGUiOiJ0ZXh0bm9kZSIsImNvbnRlbnQiOiJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4uLiB3aXRoIHJlYWwtbGlmZSBpbWFnZXNcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIn1dfV19XX0seyJ0YWdOYW1lIjoibWotcmF3IiwidHlwZSI6Im1qLXJhdyIsImNvbXBvbmVudHMiOlt7ImNsYXNzZXMiOlsiY29udGFpbmVyIl0sImNvbXBvbmVudHMiOlt7InR5cGUiOiJpbWFnZSIsInJlc2l6YWJsZSI6eyJyYXRpb0RlZmF1bHQiOjF9LCJjbGFzc2VzIjpbIml0ZW0iXSwiYXR0cmlidXRlcyI6eyJzcmMiOiJodHRwczovL3NvdXJjZS51bnNwbGFzaC5jb20vcmFuZG9tLzIwMHgxNDE/cmFuZG9tIiwiYWx0IjoiRXhhbXBsZSBpbWFnZSJ9fSx7InR5cGUiOiJpbWFnZSIsInJlc2l6YWJsZSI6eyJyYXRpb0RlZmF1bHQiOjF9LCJjbGFzc2VzIjpbIml0ZW0iXSwiYXR0cmlidXRlcyI6eyJzcmMiOiJodHRwczovL3NvdXJjZS51bnNwbGFzaC5jb20vcmFuZG9tLzIwMHgxNDI/cmFuZG9tIiwiYWx0IjoiRXhhbXBsZSBpbWFnZSJ9fSx7InR5cGUiOiJpbWFnZSIsInJlc2l6YWJsZSI6eyJyYXRpb0RlZmF1bHQiOjF9LCJjbGFzc2VzIjpbIml0ZW0iXSwiYXR0cmlidXRlcyI6eyJzcmMiOiJodHRwczovL3NvdXJjZS51bnNwbGFzaC5jb20vcmFuZG9tLzIwMHgxNDM/cmFuZG9tIiwiYWx0IjoiRXhhbXBsZSBpbWFnZSJ9fSx7InR5cGUiOiJpbWFnZSIsInJlc2l6YWJsZSI6eyJyYXRpb0RlZmF1bHQiOjF9LCJjbGFzc2VzIjpbIml0ZW0iXSwiYXR0cmlidXRlcyI6eyJzcmMiOiJodHRwczovL3NvdXJjZS51bnNwbGFzaC5jb20vcmFuZG9tLzIwMHgxNDQ/cmFuZG9tIiwiYWx0IjoiRXhhbXBsZSBpbWFnZSJ9fSx7InR5cGUiOiJpbWFnZSIsInJlc2l6YWJsZSI6eyJyYXRpb0RlZmF1bHQiOjF9LCJjbGFzc2VzIjpbIml0ZW0iXSwiYXR0cmlidXRlcyI6eyJzcmMiOiJodHRwczovL3NvdXJjZS51bnNwbGFzaC5jb20vcmFuZG9tLzIwMHgxNDU/cmFuZG9tIiwiYWx0IjoiRXhhbXBsZSBpbWFnZSJ9fSx7InR5cGUiOiJpbWFnZSIsInJlc2l6YWJsZSI6eyJyYXRpb0RlZmF1bHQiOjF9LCJjbGFzc2VzIjpbIml0ZW0iXSwiYXR0cmlidXRlcyI6eyJzcmMiOiJodHRwczovL3NvdXJjZS51bnNwbGFzaC5jb20vcmFuZG9tLzIwMHgxNDY/cmFuZG9tIiwiYWx0IjoiRXhhbXBsZSBpbWFnZSJ9fV19XX0seyJ0eXBlIjoiY29tbWVudCIsImNvbnRlbnQiOiIgSWNvbnMgIn0seyJ0YWdOYW1lIjoibWotc2VjdGlvbiIsInR5cGUiOiJtai1zZWN0aW9uIiwic3R5bGUiOnsiYmFja2dyb3VuZC1jb2xvciI6IiNmYmZiZmIiLCJwYWRkaW5nLWxlZnQiOiIwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMHB4IiwicGFkZGluZy10b3AiOiIxMHB4IiwicGFkZGluZy1ib3R0b20iOiIxMHB4IiwidGV4dC1hbGlnbiI6ImNlbnRlciJ9LCJhdHRyaWJ1dGVzIjp7ImJhY2tncm91bmQtY29sb3IiOiIjZmJmYmZiIiwicGFkZGluZy1sZWZ0IjoiMHB4IiwicGFkZGluZy1yaWdodCI6IjBweCIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInRleHQtYWxpZ24iOiJjZW50ZXIiLCJzdHlsZSI6ImJhY2tncm91bmQtY29sb3I6I2ZiZmJmYjtwYWRkaW5nLWxlZnQ6MHB4O3BhZGRpbmctcmlnaHQ6MHB4O3BhZGRpbmctdG9wOjEwcHg7cGFkZGluZy1ib3R0b206MTBweDt0ZXh0LWFsaWduOmNlbnRlcjsifSwiY29tcG9uZW50cyI6W3sidGFnTmFtZSI6Im1qLWNvbHVtbiIsInR5cGUiOiJtai1jb2x1bW4iLCJzdHlsZSI6eyJ2ZXJ0aWNhbC1hbGlnbiI6InRvcCJ9LCJhdHRyaWJ1dGVzIjp7InZlcnRpY2FsLWFsaWduIjoidG9wIiwic3R5bGUiOiJ2ZXJ0aWNhbC1hbGlnbjp0b3A7In0sImNvbXBvbmVudHMiOlt7InRhZ05hbWUiOiJtai1pbWFnZSIsInR5cGUiOiJtai1pbWFnZSIsInN0eWxlIjp7IndpZHRoIjoiMTAwcHgiLCJzcmMiOiJodHRwOi8vMTkxbi5tai5hbS9pbWcvMTkxbi8zcy94MGwucG5nIiwicGFkZGluZy10b3AiOiIxMHB4IiwicGFkZGluZy1ib3R0b20iOiIxMHB4IiwicGFkZGluZy1yaWdodCI6IjI1cHgiLCJwYWRkaW5nLWxlZnQiOiIyNXB4IiwiYWxpZ24iOiJjZW50ZXIifSwiYXR0cmlidXRlcyI6eyJ3aWR0aCI6IjEwMHB4Iiwic3JjIjoiaHR0cDovLzE5MW4ubWouYW0vaW1nLzE5MW4vM3MveDBsLnBuZyIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInBhZGRpbmctcmlnaHQiOiIyNXB4IiwicGFkZGluZy1sZWZ0IjoiMjVweCIsImFsaWduIjoiY2VudGVyIiwic3R5bGUiOiJ3aWR0aDoxMDBweDtzcmM6aHR0cDovLzE5MW4ubWouYW0vaW1nLzE5MW4vM3MveDBsLnBuZztwYWRkaW5nLXRvcDoxMHB4O3BhZGRpbmctYm90dG9tOjEwcHg7cGFkZGluZy1yaWdodDoyNXB4O3BhZGRpbmctbGVmdDoyNXB4O2FsaWduOmNlbnRlcjsifSwiY29tcG9uZW50cyI6W3sidHlwZSI6InRleHRub2RlIiwiY29udGVudCI6IlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ9XX1dfSx7InRhZ05hbWUiOiJtai1jb2x1bW4iLCJ0eXBlIjoibWotY29sdW1uIiwic3R5bGUiOnsidmVydGljYWwtYWxpZ24iOiJ0b3AifSwiYXR0cmlidXRlcyI6eyJ2ZXJ0aWNhbC1hbGlnbiI6InRvcCIsInN0eWxlIjoidmVydGljYWwtYWxpZ246dG9wOyJ9LCJjb21wb25lbnRzIjpbeyJ0YWdOYW1lIjoibWotaW1hZ2UiLCJ0eXBlIjoibWotaW1hZ2UiLCJzdHlsZSI6eyJ3aWR0aCI6IjEwMHB4Iiwic3JjIjoiaHR0cDovLzE5MW4ubWouYW0vaW1nLzE5MW4vM3MveDAxLnBuZyIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInBhZGRpbmctcmlnaHQiOiIyNXB4IiwicGFkZGluZy1sZWZ0IjoiMjVweCIsImFsaWduIjoiY2VudGVyIn0sImF0dHJpYnV0ZXMiOnsid2lkdGgiOiIxMDBweCIsInNyYyI6Imh0dHA6Ly8xOTFuLm1qLmFtL2ltZy8xOTFuLzNzL3gwMS5wbmciLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMjVweCIsInBhZGRpbmctbGVmdCI6IjI1cHgiLCJhbGlnbiI6ImNlbnRlciIsInN0eWxlIjoid2lkdGg6MTAwcHg7c3JjOmh0dHA6Ly8xOTFuLm1qLmFtL2ltZy8xOTFuLzNzL3gwMS5wbmc7cGFkZGluZy10b3A6MTBweDtwYWRkaW5nLWJvdHRvbToxMHB4O3BhZGRpbmctcmlnaHQ6MjVweDtwYWRkaW5nLWxlZnQ6MjVweDthbGlnbjpjZW50ZXI7In0sImNvbXBvbmVudHMiOlt7InR5cGUiOiJ0ZXh0bm9kZSIsImNvbnRlbnQiOiJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAifV19XX0seyJ0YWdOYW1lIjoibWotY29sdW1uIiwidHlwZSI6Im1qLWNvbHVtbiIsInN0eWxlIjp7InZlcnRpY2FsLWFsaWduIjoidG9wIn0sImF0dHJpYnV0ZXMiOnsidmVydGljYWwtYWxpZ24iOiJ0b3AiLCJzdHlsZSI6InZlcnRpY2FsLWFsaWduOnRvcDsifSwiY29tcG9uZW50cyI6W3sidGFnTmFtZSI6Im1qLWltYWdlIiwidHlwZSI6Im1qLWltYWdlIiwic3R5bGUiOnsid2lkdGgiOiIxMDBweCIsInNyYyI6Imh0dHA6Ly8xOTFuLm1qLmFtL2ltZy8xOTFuLzNzL3gwcy5wbmciLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMjVweCIsInBhZGRpbmctbGVmdCI6IjI1cHgiLCJhbGlnbiI6ImNlbnRlciJ9LCJhdHRyaWJ1dGVzIjp7IndpZHRoIjoiMTAwcHgiLCJzcmMiOiJodHRwOi8vMTkxbi5tai5hbS9pbWcvMTkxbi8zcy94MHMucG5nIiwicGFkZGluZy10b3AiOiIxMHB4IiwicGFkZGluZy1ib3R0b20iOiIxMHB4IiwicGFkZGluZy1yaWdodCI6IjI1cHgiLCJwYWRkaW5nLWxlZnQiOiIyNXB4IiwiYWxpZ24iOiJjZW50ZXIiLCJzdHlsZSI6IndpZHRoOjEwMHB4O3NyYzpodHRwOi8vMTkxbi5tai5hbS9pbWcvMTkxbi8zcy94MHMucG5nO3BhZGRpbmctdG9wOjEwcHg7cGFkZGluZy1ib3R0b206MTBweDtwYWRkaW5nLXJpZ2h0OjI1cHg7cGFkZGluZy1sZWZ0OjI1cHg7YWxpZ246Y2VudGVyOyJ9LCJjb21wb25lbnRzIjpbeyJ0eXBlIjoidGV4dG5vZGUiLCJjb250ZW50IjoiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIn1dfV19XX0seyJ0eXBlIjoiY29tbWVudCIsImNvbnRlbnQiOiIgRm9vdGVyICJ9LHsidGFnTmFtZSI6Im1qLXNlY3Rpb24iLCJ0eXBlIjoibWotc2VjdGlvbiIsInN0eWxlIjp7ImJhY2tncm91bmQtY29sb3IiOiIjZTdlN2U3IiwicGFkZGluZy1sZWZ0IjoiMHB4IiwicGFkZGluZy1yaWdodCI6IjBweCIsInBhZGRpbmctdG9wIjoiMTBweCIsInBhZGRpbmctYm90dG9tIjoiMTBweCIsInRleHQtYWxpZ24iOiJjZW50ZXIifSwiYXR0cmlidXRlcyI6eyJiYWNrZ3JvdW5kLWNvbG9yIjoiI2U3ZTdlNyIsInBhZGRpbmctbGVmdCI6IjBweCIsInBhZGRpbmctcmlnaHQiOiIwcHgiLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJ0ZXh0LWFsaWduIjoiY2VudGVyIiwic3R5bGUiOiJiYWNrZ3JvdW5kLWNvbG9yOiNlN2U3ZTc7cGFkZGluZy1sZWZ0OjBweDtwYWRkaW5nLXJpZ2h0OjBweDtwYWRkaW5nLXRvcDoxMHB4O3BhZGRpbmctYm90dG9tOjEwcHg7dGV4dC1hbGlnbjpjZW50ZXI7In0sImNvbXBvbmVudHMiOlt7InRhZ05hbWUiOiJtai1jb2x1bW4iLCJ0eXBlIjoibWotY29sdW1uIiwic3R5bGUiOnsidmVydGljYWwtYWxpZ24iOiJ0b3AifSwiYXR0cmlidXRlcyI6eyJ2ZXJ0aWNhbC1hbGlnbiI6InRvcCIsInN0eWxlIjoidmVydGljYWwtYWxpZ246dG9wOyJ9LCJjb21wb25lbnRzIjpbeyJ0YWdOYW1lIjoibWotYnV0dG9uIiwidHlwZSI6Im1qLWJ1dHRvbiIsInN0eWxlIjp7ImhyZWYiOiIjIiwiYmFja2dyb3VuZC1jb2xvciI6IiM0MTQxNDEiLCJib3JkZXItcmFkaXVzIjoiM3B4IiwiZm9udC1zaXplIjoiMTNweCIsImZvbnQtd2VpZ2h0IjoiNDAwIiwiY29sb3IiOiIjZmZmZmZmIiwidmVydGljYWwtYWxpZ24iOiJtaWRkbGUiLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMjVweCIsInBhZGRpbmctbGVmdCI6IjI1cHgiLCJhbGlnbiI6ImNlbnRlciJ9LCJhdHRyaWJ1dGVzIjp7ImhyZWYiOiIjIiwiYmFja2dyb3VuZC1jb2xvciI6IiM0MTQxNDEiLCJib3JkZXItcmFkaXVzIjoiM3B4IiwiZm9udC1zaXplIjoiMTNweCIsImZvbnQtd2VpZ2h0IjoiNDAwIiwiY29sb3IiOiIjZmZmZmZmIiwidmVydGljYWwtYWxpZ24iOiJtaWRkbGUiLCJwYWRkaW5nLXRvcCI6IjEwcHgiLCJwYWRkaW5nLWJvdHRvbSI6IjEwcHgiLCJwYWRkaW5nLXJpZ2h0IjoiMjVweCIsInBhZGRpbmctbGVmdCI6IjI1cHgiLCJhbGlnbiI6ImNlbnRlciIsInN0eWxlIjoiaHJlZjojO2JhY2tncm91bmQtY29sb3I6IzQxNDE0MTtib3JkZXItcmFkaXVzOjNweDtmb250LXNpemU6MTNweDtmb250LXdlaWdodDo0MDA7Y29sb3I6I2ZmZmZmZjt2ZXJ0aWNhbC1hbGlnbjptaWRkbGU7cGFkZGluZy10b3A6MTBweDtwYWRkaW5nLWJvdHRvbToxMHB4O3BhZGRpbmctcmlnaHQ6MjVweDtwYWRkaW5nLWxlZnQ6MjVweDthbGlnbjpjZW50ZXI7In0sImNvbXBvbmVudHMiOlt7InR5cGUiOiJ0ZXh0bm9kZSIsImNvbnRlbnQiOiJIZWxsbyBUaGVyZSEifV19LHsidGFnTmFtZSI6Im1qLXNvY2lhbCIsInR5cGUiOiJtai1zb2NpYWwiLCJzdHlsZSI6eyJmb250LXNpemUiOiIxNXB4IiwiaWNvbi1zaXplIjoiMzBweCIsIm1vZGUiOiJob3Jpem9udGFsIiwiYWxpZ24iOiJjZW50ZXIiLCJsaW5lLWhlaWdodCI6IjIycHgifSwiYXR0cmlidXRlcyI6eyJmb250LXNpemUiOiIxNXB4IiwiaWNvbi1zaXplIjoiMzBweCIsIm1vZGUiOiJob3Jpem9udGFsIiwiYWxpZ24iOiJjZW50ZXIiLCJsaW5lLWhlaWdodCI6IjIycHgiLCJzdHlsZSI6ImZvbnQtc2l6ZToxNXB4O2ljb24tc2l6ZTozMHB4O21vZGU6aG9yaXpvbnRhbDthbGlnbjpjZW50ZXI7bGluZS1oZWlnaHQ6MjJweDsifSwiY29tcG9uZW50cyI6W3sidGFnTmFtZSI6Im1qLXNvY2lhbC1lbGVtZW50IiwidHlwZSI6Im1qLXNvY2lhbC1lbGVtZW50Iiwic3R5bGUiOnsibmFtZSI6ImZhY2Vib29rIiwiaHJlZiI6Imh0dHBzOi8vbWptbC5pby8iLCJhbGlnbiI6ImNlbnRlciIsImZvbnQtc2l6ZSI6IjEzcHgiLCJsaW5lLWhlaWdodCI6IjIycHgiLCJ2ZXJ0aWNhbC1hbGlnbiI6Im1pZGRsZSJ9LCJhdHRyaWJ1dGVzIjp7Im5hbWUiOiJmYWNlYm9vayIsImhyZWYiOiJodHRwczovL21qbWwuaW8vIiwiYWxpZ24iOiJjZW50ZXIiLCJmb250LXNpemUiOiIxM3B4IiwibGluZS1oZWlnaHQiOiIyMnB4IiwidmVydGljYWwtYWxpZ24iOiJtaWRkbGUiLCJzdHlsZSI6Im5hbWU6ZmFjZWJvb2s7aHJlZjpodHRwczovL21qbWwuaW8vO2FsaWduOmNlbnRlcjtmb250LXNpemU6MTNweDtsaW5lLWhlaWdodDoyMnB4O3ZlcnRpY2FsLWFsaWduOm1pZGRsZTsifSwiY29tcG9uZW50cyI6W3sidHlwZSI6InRleHRub2RlIiwiY29udGVudCI6IlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZhY2Vib29rXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAifV19LHsidGFnTmFtZSI6Im1qLXNvY2lhbC1lbGVtZW50IiwidHlwZSI6Im1qLXNvY2lhbC1lbGVtZW50Iiwic3R5bGUiOnsibmFtZSI6Imdvb2dsZSIsImhyZWYiOiJodHRwczovL21qbWwuaW8vIiwiYWxpZ24iOiJjZW50ZXIiLCJmb250LXNpemUiOiIxM3B4IiwibGluZS1oZWlnaHQiOiIyMnB4IiwidmVydGljYWwtYWxpZ24iOiJtaWRkbGUifSwiYXR0cmlidXRlcyI6eyJuYW1lIjoiZ29vZ2xlIiwiaHJlZiI6Imh0dHBzOi8vbWptbC5pby8iLCJhbGlnbiI6ImNlbnRlciIsImZvbnQtc2l6ZSI6IjEzcHgiLCJsaW5lLWhlaWdodCI6IjIycHgiLCJ2ZXJ0aWNhbC1hbGlnbiI6Im1pZGRsZSIsInN0eWxlIjoibmFtZTpnb29nbGU7aHJlZjpodHRwczovL21qbWwuaW8vO2FsaWduOmNlbnRlcjtmb250LXNpemU6MTNweDtsaW5lLWhlaWdodDoyMnB4O3ZlcnRpY2FsLWFsaWduOm1pZGRsZTsifSwiY29tcG9uZW50cyI6W3sidHlwZSI6InRleHRub2RlIiwiY29udGVudCI6IlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdvb2dsZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIn1dfSx7InRhZ05hbWUiOiJtai1zb2NpYWwtZWxlbWVudCIsInR5cGUiOiJtai1zb2NpYWwtZWxlbWVudCIsInN0eWxlIjp7Im5hbWUiOiJ0d2l0dGVyIiwiaHJlZiI6Imh0dHBzOi8vbWptbC5pby8iLCJhbGlnbiI6ImNlbnRlciIsImZvbnQtc2l6ZSI6IjEzcHgiLCJsaW5lLWhlaWdodCI6IjIycHgiLCJ2ZXJ0aWNhbC1hbGlnbiI6Im1pZGRsZSJ9LCJhdHRyaWJ1dGVzIjp7Im5hbWUiOiJ0d2l0dGVyIiwiaHJlZiI6Imh0dHBzOi8vbWptbC5pby8iLCJhbGlnbiI6ImNlbnRlciIsImZvbnQtc2l6ZSI6IjEzcHgiLCJsaW5lLWhlaWdodCI6IjIycHgiLCJ2ZXJ0aWNhbC1hbGlnbiI6Im1pZGRsZSIsInN0eWxlIjoibmFtZTp0d2l0dGVyO2hyZWY6aHR0cHM6Ly9tam1sLmlvLzthbGlnbjpjZW50ZXI7Zm9udC1zaXplOjEzcHg7bGluZS1oZWlnaHQ6MjJweDt2ZXJ0aWNhbC1hbGlnbjptaWRkbGU7In0sImNvbXBvbmVudHMiOlt7InR5cGUiOiJ0ZXh0bm9kZSIsImNvbnRlbnQiOiJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUd2l0dGVyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAifV19XX1dfV19LHsidHlwZSI6ImNvbW1lbnQiLCJjb250ZW50IjoiIEZvb3RlciAifV19XX1dfSwiaWQiOiI3cERSTzdjS1R3V2hXSXJ4In1dLCJpZCI6IjU2czJHNlVkVVhHMlpidngifV19]]></fileData> 37 <fileData><![CDATA[{"assets":[],"styles":[{"selectors":[],"selectorsAdd":"#outlook a","style":{"padding-top":"0px","padding-right":"0px","padding-bottom":"0px","padding-left":"0px"}},{"selectors":[],"selectorsAdd":"body","style":{"margin-top":"0px","margin-right":"0px","margin-bottom":"0px","margin-left":"0px","padding-top":"0px","padding-right":"0px","padding-bottom":"0px","padding-left":"0px","text-size-adjust":"100%"}},{"selectors":[],"selectorsAdd":"table, td","style":{"border-collapse":"collapse"}},{"selectors":[],"selectorsAdd":"img","style":{"border-top-width":"0px","border-right-width":"0px","border-bottom-width":"0px","border-left-width":"0px","border-top-style":"initial","border-right-style":"initial","border-bottom-style":"initial","border-left-style":"initial","border-top-color":"initial","border-right-color":"initial","border-bottom-color":"initial","border-left-color":"initial","border-image-source":"initial","border-image-slice":"initial","border-image-width":"initial","border-image-outset":"initial","border-image-repeat":"initial","height":"auto","line-height":"100%","outline-color":"initial","outline-style":"none","outline-width":"initial","text-decoration-line":"none","text-decoration-thickness":"initial","text-decoration-style":"initial","text-decoration-color":"initial"}},{"selectors":[],"selectorsAdd":"p","style":{"display":"block","margin-top":"13px","margin-right":"0px","margin-bottom":"13px","margin-left":"0px"}},{"selectors":["mj-column-per-100"],"style":{"width":"100% !important","max-width":"100%"},"mediaText":"only screen and (min-width: 480px)","atRuleType":"media"},{"selectors":[],"selectorsAdd":".moz-text-html .mj-column-per-100","style":{"width":"100% !important","max-width":"100%"}},{"selectors":["mj-column-per-50"],"style":{"width":"50% !important","max-width":"50%"},"mediaText":"only screen and (min-width: 480px)","atRuleType":"media"},{"selectors":[],"selectorsAdd":".moz-text-html .mj-column-per-50","style":{"width":"50% !important","max-width":"50%"}},{"selectors":["mj-column-per-33-333333333333336"],"style":{"width":"33.3333% !important","max-width":"33.3333%"},"mediaText":"only screen and (min-width: 480px)","atRuleType":"media"},{"selectors":[],"selectorsAdd":".moz-text-html .mj-column-per-33-333333333333336","style":{"width":"33.3333% !important","max-width":"33.3333%"}}],"pages":[{"frames":[{"component":{"type":"wrapper","stylable":["background","background-color","background-image","background-repeat","background-attachment","background-position","background-size"],"components":[{"tagName":"mjml","type":"mjml","components":[{"tagName":"mj-head","type":"mj-head","components":[{"tagName":"mj-font","type":"mj-font","style":{"name":"Barlow","href":"https://fonts.googleapis.com/css?family=Barlow"},"attributes":{"name":"Barlow","href":"https://fonts.googleapis.com/css?family=Barlow","style":"name:Barlow;href:https://fonts.googleapis.com/css?family=Barlow;"}},{"tagName":"mj-style","type":"mj-style","components":[{"type":"textnode","content":"\n .slogan {\n background: #000;\n }\n "}]}]},{"tagName":"mj-body","type":"mj-body","style":{"width":"600px"},"attributes":{"width":"600px","style":"width:600px;"},"components":[{"type":"comment","content":" Company Header "},{"tagName":"mj-section","type":"mj-section","style":{"background-color":"#f0f0f0","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center"},"attributes":{"background-color":"#f0f0f0","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center","style":"background-color:#f0f0f0;padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;text-align:center;"},"components":[{"tagName":"mj-column","type":"mj-column","style":{"border":"10px solid #F45E43","vertical-align":"top"},"attributes":{"border":"10px solid #F45E43","vertical-align":"top","style":"border:10px solid #F45E43;vertical-align:top;"},"components":[{"tagName":"mj-text","type":"mj-text","style":{"font-family":"Barlow","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","font-size":"13px","line-height":"22px","align":"left"},"attributes":{"font-family":"Barlow","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","font-size":"13px","line-height":"22px","align":"left","style":"font-family:Barlow;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;font-size:13px;line-height:22px;align:left;"},"components":[{"type":"textnode","content":"A first line of text"}]},{"tagName":"mj-spacer","type":"mj-spacer","style":{"height":"50px"},"attributes":{"height":"50px","style":"height:50px;"},"components":[{"type":"textnode","content":"\n "}]}]}]},{"type":"comment","content":" Image Header "},{"tagName":"mj-section","type":"mj-section","style":{"background-url":"http://1.bp.blogspot.com/-TPrfhxbYpDY/Uh3Refzk02I/AAAAAAAALw8/5sUJ0UUGYuw/s1600/New+York+in+The+1960's+-+70's+(2).jpg","background-size":"cover","background-repeat":"no-repeat","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center"},"attributes":{"background-url":"http://1.bp.blogspot.com/-TPrfhxbYpDY/Uh3Refzk02I/AAAAAAAALw8/5sUJ0UUGYuw/s1600/New+York+in+The+1960's+-+70's+(2).jpg","background-size":"cover","background-repeat":"no-repeat","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center","style":"background-url:http://1.bp.blogspot.com/-TPrfhxbYpDY/Uh3Refzk02I/AAAAAAAALw8/5sUJ0UUGYuw/s1600/New+York+in+The+1960's+-+70's+(2).jpg;background-size:cover;background-repeat:no-repeat;padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;text-align:center;"},"components":[{"tagName":"mj-column","type":"mj-column","style":{"vertical-align":"top"},"attributes":{"vertical-align":"top","style":"vertical-align:top;"},"components":[{"tagName":"mj-text","type":"mj-text","style":{"css-class":"slogan","align":"center","color":"#fff","font-size":"40px","font-family":"Helvetica Neue","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","line-height":"22px"},"attributes":{"css-class":"slogan","align":"center","color":"#fff","font-size":"40px","font-family":"Helvetica Neue","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","line-height":"22px","style":"css-class:slogan;align:center;color:#fff;font-size:40px;font-family:Helvetica Neue;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;line-height:22px;"},"components":[{"type":"textnode","content":"Slogan here"}]}]}]},{"type":"comment","content":" Intro text "},{"tagName":"mj-wrapper","type":"mj-wrapper","style":{"background-color":"#ffe9f7","padding":"10px"},"attributes":{"background-color":"#ffe9f7","padding":"10px","style":"background-color:#ffe9f7;padding:10px;"},"components":[{"tagName":"mj-section","type":"mj-section","style":{"background-color":"#eaeffa","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center"},"attributes":{"background-color":"#eaeffa","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center","style":"background-color:#eaeffa;padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;text-align:center;"},"components":[{"tagName":"mj-group","type":"mj-group","style":{"background-color":"#fffadd","vertical-align":"top"},"attributes":{"background-color":"#fffadd","vertical-align":"top","style":"background-color:#fffadd;vertical-align:top;"},"components":[{"tagName":"mj-column","type":"mj-column","style":{"vertical-align":"top"},"attributes":{"vertical-align":"top","style":"vertical-align:top;"},"components":[{"tagName":"mj-text","type":"mj-text","style":{"font-style":"italic","font-size":"20px","font-family":"Helvetica Neue","color":"#626262","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","line-height":"22px","align":"left"},"attributes":{"font-style":"italic","font-size":"20px","font-family":"Helvetica Neue","color":"#626262","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","line-height":"22px","align":"left","style":"font-style:italic;font-size:20px;font-family:Helvetica Neue;color:#626262;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;line-height:22px;align:left;"},"components":[{"type":"textnode","content":"My Awesome Text"}]},{"tagName":"mj-text","type":"mj-text","style":{"color":"#525252","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","font-size":"13px","line-height":"22px","align":"left"},"attributes":{"color":"#525252","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","font-size":"13px","line-height":"22px","align":"left","style":"color:#525252;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;font-size:13px;line-height:22px;align:left;"},"components":[{"type":"textnode","content":"\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin rutrum enim eget magna efficitur, eu semper augue semper.\n Aliquam erat volutpat. Cras id dui lectus. Vestibulum sed finibus lectus, sit amet suscipit nibh. Proin nec\n commodo purus. Sed eget nulla elit. Nulla aliquet mollis faucibus.\n "}]},{"tagName":"mj-button","type":"mj-button","style":{"background-color":"#F45E43","href":"#","border-radius":"3px","font-size":"13px","font-weight":"400","color":"#ffffff","vertical-align":"middle","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center"},"attributes":{"background-color":"#F45E43","href":"#","border-radius":"3px","font-size":"13px","font-weight":"400","color":"#ffffff","vertical-align":"middle","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center","style":"background-color:#F45E43;href:#;border-radius:3px;font-size:13px;font-weight:400;color:#ffffff;vertical-align:middle;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;align:center;"},"components":[{"type":"textnode","content":"Learn more"}]}]}]}]}]},{"type":"comment","content":" Side image "},{"tagName":"mj-section","type":"mj-section","style":{"background-color":"white","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center"},"attributes":{"background-color":"white","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center","style":"background-color:white;padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;text-align:center;"},"components":[{"tagName":"mj-column","type":"mj-column","style":{"vertical-align":"top"},"attributes":{"vertical-align":"top","style":"vertical-align:top;"},"components":[{"tagName":"mj-image","type":"mj-image","style":{"width":"200px","src":"https://designspell.files.wordpress.com/2012/01/sciolino-paris-bw.jpg","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center"},"attributes":{"width":"200px","src":"https://designspell.files.wordpress.com/2012/01/sciolino-paris-bw.jpg","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center","style":"width:200px;src:https://designspell.files.wordpress.com/2012/01/sciolino-paris-bw.jpg;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;align:center;"},"components":[{"type":"textnode","content":"\n "}]}]},{"tagName":"mj-column","type":"mj-column","style":{"vertical-align":"top"},"attributes":{"vertical-align":"top","style":"vertical-align:top;"},"components":[{"tagName":"mj-text","type":"mj-text","style":{"font-style":"italic","font-size":"20px","font-family":"Helvetica Neue","color":"#626262","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","line-height":"22px","align":"left"},"attributes":{"font-style":"italic","font-size":"20px","font-family":"Helvetica Neue","color":"#626262","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","line-height":"22px","align":"left","style":"font-style:italic;font-size:20px;font-family:Helvetica Neue;color:#626262;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;line-height:22px;align:left;"},"components":[{"type":"textnode","content":"\n Find amazing places ...\n "}]},{"tagName":"mj-text","type":"mj-text","style":{"color":"#525252","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","font-size":"13px","line-height":"22px","align":"left"},"attributes":{"color":"#525252","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","font-size":"13px","line-height":"22px","align":"left","style":"color:#525252;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;font-size:13px;line-height:22px;align:left;"},"components":[{"type":"textnode","content":"\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin rutrum enim eget magna efficitur, eu semper augue semper.\n Aliquam erat volutpat. Cras id dui lectus. Vestibulum sed finibus lectus."}]}]}]},{"tagName":"mj-section","type":"mj-section","style":{"padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center"},"attributes":{"padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center","style":"padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;text-align:center;"},"components":[{"tagName":"mj-column","type":"mj-column","style":{"vertical-align":"top"},"attributes":{"vertical-align":"top","style":"vertical-align:top;"},"components":[{"tagName":"mj-text","type":"mj-text","style":{"font-style":"italic","font-size":"20px","font-family":"Helvetica Neue","color":"#626262","align":"center","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","line-height":"22px"},"attributes":{"font-style":"italic","font-size":"20px","font-family":"Helvetica Neue","color":"#626262","align":"center","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","line-height":"22px","style":"font-style:italic;font-size:20px;font-family:Helvetica Neue;color:#626262;align:center;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;line-height:22px;"},"components":[{"type":"textnode","content":"\n ... with real-life images\n "}]}]}]},{"tagName":"mj-raw","type":"mj-raw","components":[{"classes":["container"],"components":[{"type":"image","resizable":{"ratioDefault":1},"classes":["item"],"attributes":{"src":"https://source.unsplash.com/random/200x141?random","alt":"Example image"}},{"type":"image","resizable":{"ratioDefault":1},"classes":["item"],"attributes":{"src":"https://source.unsplash.com/random/200x142?random","alt":"Example image"}},{"type":"image","resizable":{"ratioDefault":1},"classes":["item"],"attributes":{"src":"https://source.unsplash.com/random/200x143?random","alt":"Example image"}},{"type":"image","resizable":{"ratioDefault":1},"classes":["item"],"attributes":{"src":"https://source.unsplash.com/random/200x144?random","alt":"Example image"}},{"type":"image","resizable":{"ratioDefault":1},"classes":["item"],"attributes":{"src":"https://source.unsplash.com/random/200x145?random","alt":"Example image"}},{"type":"image","resizable":{"ratioDefault":1},"classes":["item"],"attributes":{"src":"https://source.unsplash.com/random/200x146?random","alt":"Example image"}}]}]},{"type":"comment","content":" Icons "},{"tagName":"mj-section","type":"mj-section","style":{"background-color":"#fbfbfb","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center"},"attributes":{"background-color":"#fbfbfb","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center","style":"background-color:#fbfbfb;padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;text-align:center;"},"components":[{"tagName":"mj-column","type":"mj-column","style":{"vertical-align":"top"},"attributes":{"vertical-align":"top","style":"vertical-align:top;"},"components":[{"tagName":"mj-image","type":"mj-image","style":{"width":"100px","src":"http://191n.mj.am/img/191n/3s/x0l.png","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center"},"attributes":{"width":"100px","src":"http://191n.mj.am/img/191n/3s/x0l.png","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center","style":"width:100px;src:http://191n.mj.am/img/191n/3s/x0l.png;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;align:center;"},"components":[{"type":"textnode","content":"\n "}]}]},{"tagName":"mj-column","type":"mj-column","style":{"vertical-align":"top"},"attributes":{"vertical-align":"top","style":"vertical-align:top;"},"components":[{"tagName":"mj-image","type":"mj-image","style":{"width":"100px","src":"http://191n.mj.am/img/191n/3s/x01.png","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center"},"attributes":{"width":"100px","src":"http://191n.mj.am/img/191n/3s/x01.png","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center","style":"width:100px;src:http://191n.mj.am/img/191n/3s/x01.png;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;align:center;"},"components":[{"type":"textnode","content":"\n "}]}]},{"tagName":"mj-column","type":"mj-column","style":{"vertical-align":"top"},"attributes":{"vertical-align":"top","style":"vertical-align:top;"},"components":[{"tagName":"mj-image","type":"mj-image","style":{"width":"100px","src":"http://191n.mj.am/img/191n/3s/x0s.png","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center"},"attributes":{"width":"100px","src":"http://191n.mj.am/img/191n/3s/x0s.png","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center","style":"width:100px;src:http://191n.mj.am/img/191n/3s/x0s.png;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;align:center;"},"components":[{"type":"textnode","content":"\n "}]}]}]},{"type":"comment","content":" Footer "},{"tagName":"mj-section","type":"mj-section","style":{"background-color":"#e7e7e7","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center"},"attributes":{"background-color":"#e7e7e7","padding-left":"0px","padding-right":"0px","padding-top":"10px","padding-bottom":"10px","text-align":"center","style":"background-color:#e7e7e7;padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;text-align:center;"},"components":[{"tagName":"mj-column","type":"mj-column","style":{"vertical-align":"top"},"attributes":{"vertical-align":"top","style":"vertical-align:top;"},"components":[{"tagName":"mj-button","type":"mj-button","style":{"href":"#","background-color":"#414141","border-radius":"3px","font-size":"13px","font-weight":"400","color":"#ffffff","vertical-align":"middle","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center"},"attributes":{"href":"#","background-color":"#414141","border-radius":"3px","font-size":"13px","font-weight":"400","color":"#ffffff","vertical-align":"middle","padding-top":"10px","padding-bottom":"10px","padding-right":"25px","padding-left":"25px","align":"center","style":"href:#;background-color:#414141;border-radius:3px;font-size:13px;font-weight:400;color:#ffffff;vertical-align:middle;padding-top:10px;padding-bottom:10px;padding-right:25px;padding-left:25px;align:center;"},"components":[{"type":"textnode","content":"Hello There!"}]},{"tagName":"mj-social","type":"mj-social","style":{"font-size":"15px","icon-size":"30px","mode":"horizontal","align":"center","line-height":"22px"},"attributes":{"font-size":"15px","icon-size":"30px","mode":"horizontal","align":"center","line-height":"22px","style":"font-size:15px;icon-size:30px;mode:horizontal;align:center;line-height:22px;"},"components":[{"tagName":"mj-social-element","type":"mj-social-element","style":{"name":"facebook","href":"https://mjml.io/","align":"center","font-size":"13px","line-height":"22px","vertical-align":"middle"},"attributes":{"name":"facebook","href":"https://mjml.io/","align":"center","font-size":"13px","line-height":"22px","vertical-align":"middle","style":"name:facebook;href:https://mjml.io/;align:center;font-size:13px;line-height:22px;vertical-align:middle;"},"components":[{"type":"textnode","content":"\n Facebook\n "}]},{"tagName":"mj-social-element","type":"mj-social-element","style":{"name":"google","href":"https://mjml.io/","align":"center","font-size":"13px","line-height":"22px","vertical-align":"middle"},"attributes":{"name":"google","href":"https://mjml.io/","align":"center","font-size":"13px","line-height":"22px","vertical-align":"middle","style":"name:google;href:https://mjml.io/;align:center;font-size:13px;line-height:22px;vertical-align:middle;"},"components":[{"type":"textnode","content":"\n Google\n "}]},{"tagName":"mj-social-element","type":"mj-social-element","style":{"name":"twitter","href":"https://mjml.io/","align":"center","font-size":"13px","line-height":"22px","vertical-align":"middle"},"attributes":{"name":"twitter","href":"https://mjml.io/","align":"center","font-size":"13px","line-height":"22px","vertical-align":"middle","style":"name:twitter;href:https://mjml.io/;align:center;font-size:13px;line-height:22px;vertical-align:middle;"},"components":[{"type":"textnode","content":"\n Twitter\n "}]}]}]}]},{"type":"comment","content":" Footer "}]}]}]},"id":"7pDRO7cKTwWhWIrx"}],"id":"56s2G6UdUXG2Zbvx"}]}]]></fileData>
36 </file> 38 </file>
37 </dbResources> 39 </dbResources>
40
41 <!-- ========= Service Jobs -->
42 <moqui.service.job.ServiceJob jobName="remove_ExcessMjmlEmailResourceHistories" description="Remove any excess mjml resource histories for email templates"
43 serviceName="mjml.MjmlServices.remove#ExcessMjmlEmailResourceHistories" cronExpression="0 0 * * * ?" paused="N"
44 transactionTimeout="3600"/>
45
38 </entity-facade-xml> 46 </entity-facade-xml>
......
...@@ -16,7 +16,9 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -16,7 +16,9 @@ along with this software (see the LICENSE.md file). If not, see
16 16
17 <extend-entity entity-name="EmailTemplate" package="moqui.basic.email"> 17 <extend-entity entity-name="EmailTemplate" package="moqui.basic.email">
18 <field name="grapesLocation" type="text-medium"/> 18 <field name="grapesLocation" type="text-medium"/>
19 <field name="grapesPublishedVersionName" type="text-short"/>
19 <field name="htmlLocation" type="text-medium"/> 20 <field name="htmlLocation" type="text-medium"/>
21 <field name="htmlPublishedVersionName" type="text-short"/>
20 </extend-entity> 22 </extend-entity>
21 <view-entity entity-name="ProductStoreEmailDetail" package="mantle.product.store"> 23 <view-entity entity-name="ProductStoreEmailDetail" package="mantle.product.store">
22 <member-entity entity-alias="PSE" entity-name="mantle.product.store.ProductStoreEmail"/> 24 <member-entity entity-alias="PSE" entity-name="mantle.product.store.ProductStoreEmail"/>
......
1 JAR files go in this directory and are picked up automatically by Moqui Framework based on the convention of the directory name 'lib'.
2
3 The build.gradle file uses this directory as the target for the built jar file, and deletes the built jar file as part of the clean task.
4
...@@ -69,17 +69,6 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -69,17 +69,6 @@ along with this software (see the LICENSE.md file). If not, see
69 </mj-column> 69 </mj-column>
70 </mj-section> 70 </mj-section>
71 71
72 <!-- Image Header -->
73 <mj-section background-url="http://1.bp.blogspot.com/-TPrfhxbYpDY/Uh3Refzk02I/AAAAAAAALw8/5sUJ0UUGYuw/s1600/New+York+in+The+1960's+-+70's+(2).jpg"
74 background-size="cover" background-repeat="no-repeat">
75 <mj-column>
76 <mj-text css-class="slogan" align="center" color="#fff" font-size="40px" font-family="Helvetica Neue">Slogan here</mj-text>
77 <mj-button background-color="#F63A4D" href="#">
78 Promotion
79 </mj-button>
80 </mj-column>
81 </mj-section>
82
83 <!-- Intro text --> 72 <!-- Intro text -->
84 <mj-wrapper background-color="#ffe9f7" padding="10px"> 73 <mj-wrapper background-color="#ffe9f7" padding="10px">
85 <mj-section background-color="#eaeffa"> 74 <mj-section background-color="#eaeffa">
...@@ -100,9 +89,6 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -100,9 +89,6 @@ along with this software (see the LICENSE.md file). If not, see
100 <!-- Side image --> 89 <!-- Side image -->
101 <mj-section background-color="white"> 90 <mj-section background-color="white">
102 <mj-column> 91 <mj-column>
103 <mj-image width="200px" src="https://designspell.files.wordpress.com/2012/01/sciolino-paris-bw.jpg" />
104 </mj-column>
105 <mj-column>
106 <mj-text font-style="italic" font-size="20px" font-family="Helvetica Neue" color="#626262"> 92 <mj-text font-style="italic" font-size="20px" font-family="Helvetica Neue" color="#626262">
107 Find amazing places ... 93 Find amazing places ...
108 </mj-text> 94 </mj-text>
...@@ -118,41 +104,19 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -118,41 +104,19 @@ along with this software (see the LICENSE.md file). If not, see
118 </mj-text> 104 </mj-text>
119 </mj-column> 105 </mj-column>
120 </mj-section> 106 </mj-section>
121 <mj-raw>
122 <div class="container">
123 <img class="item" src="https://source.unsplash.com/random/200x141?random" alt="Example image">
124 <img class="item" src="https://source.unsplash.com/random/200x142?random" alt="Example image">
125 <img class="item" src="https://source.unsplash.com/random/200x143?random" alt="Example image">
126 <img class="item" src="https://source.unsplash.com/random/200x144?random" alt="Example image">
127 <img class="item" src="https://source.unsplash.com/random/200x145?random" alt="Example image">
128 <img class="item" src="https://source.unsplash.com/random/200x146?random" alt="Example image">
129 </div>
130 </mj-raw>
131 <!-- Icons -->
132 <mj-section background-color="#fbfbfb">
133 <mj-column>
134 <mj-image width="100px" src="http://191n.mj.am/img/191n/3s/x0l.png" />
135 </mj-column>
136 <mj-column>
137 <mj-image width="100px" src="http://191n.mj.am/img/191n/3s/x01.png" />
138 </mj-column>
139 <mj-column>
140 <mj-image width="100px" src="http://191n.mj.am/img/191n/3s/x0s.png" />
141 </mj-column>
142 </mj-section>
143 107
144 <!-- Footer --> 108 <!-- Footer -->
145 <mj-section background-color="#e7e7e7"> 109 <mj-section background-color="#e7e7e7">
146 <mj-column> 110 <mj-column>
147 <mj-button href="#">Hello There!</mj-button> 111 <mj-button href="#">Hello There!</mj-button>
148 <mj-social font-size="15px" icon-size="30px" mode="horizontal"> 112 <mj-social font-size="15px" icon-size="30px" mode="horizontal">
149 <mj-social-element name="facebook" href="https://mjml.io/"> 113 <mj-social-element name="facebook" href="https://moqui.org/">
150 Facebook 114 Facebook
151 </mj-social-element> 115 </mj-social-element>
152 <mj-social-element name="google" href="https://mjml.io/"> 116 <mj-social-element name="google" href="https://moqui.org/">
153 Google 117 Google
154 </mj-social-element> 118 </mj-social-element>
155 <mj-social-element name="twitter" href="https://mjml.io/"> 119 <mj-social-element name="twitter" href="https://moqui.org/">
156 Twitter 120 Twitter
157 </mj-social-element> 121 </mj-social-element>
158 </mj-social> 122 </mj-social>
...@@ -180,15 +144,82 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -180,15 +144,82 @@ along with this software (see the LICENSE.md file). If not, see
180 } 144 }
181 // console.log('response ', response) 145 // console.log('response ', response)
182 window.htmlLocation = response.htmlLocation; 146 window.htmlLocation = response.htmlLocation;
147 window.htmlVersionName = response.htmlVersionName;
148 window.htmlPublishedVersionName = response.htmlPublishedVersionName;
183 window.grapesLocation = response.grapesLocation; 149 window.grapesLocation = response.grapesLocation;
184 const url = new URL(window.location.href) 150 window.grapesVersionName = response.grapesVersionName;
185 url.searchParams.set('htmlLocation', response.htmlLocation); 151 window.grapesPublishedVersionName = response.grapesPublishedVersionName;
186 url.searchParams.set('grapesLocation', response.grapesLocation); 152 // const url = new URL(window.location.href)
187 window.history.pushState({}, '', url) 153 // url.searchParams.set('htmlLocation', response.htmlLocation);
154 // url.searchParams.set('grapesLocation', response.grapesLocation);
155 // window.history.pushState({}, '', url)
188 156
157 window.moquiVars = response.moquiVars;
158 // console.log('init window.moquiVars ', window.moquiVars)
189 const projectData = JSON.parse(response.data); 159 const projectData = JSON.parse(response.data);
190 // console.log('window.projectData ', window.projectData) 160 // console.log('window.projectData ', window.projectData)
191 161
162 function moquiPlugin(editor) {
163 // Use the API: https://grapesjs.com/docs/api/
164
165 window.moquiPublishText = 'moqui-publish';
166 editor.Commands.add(window.moquiPublishText, () => {
167 const request = new XMLHttpRequest();
168 request.open("POST", "${baseLinkUrl}/rest/s1/moqui-mjml/mjml?emailTemplateId="+window.emailTemplateId, false); // `false` makes the request synchronous
169 request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
170 request.setRequestHeader("X-CSRF-Token", document.getElementById('confMoquiSessionToken').value);
171 request.send(JSON.stringify({ publish:true, data: window.editor.getProjectData(), moquiVars:window.moquiVars, html:window.editor.runCommand('mjml-code-to-html')?.html }));
172 // console.log('request ', request)
173 if (request.status === 200) {
174 const result = JSON.parse(request.responseText);
175 window.htmlLocation = result.htmlLocation;
176 window.htmlVersionName = result.htmlVersionName;
177 window.htmlPublishedVersionName = result.htmlPublishedVersionName;
178 window.grapesLocation = result.grapesLocation;
179 window.grapesVersionName = result.grapesVersionName;
180 window.grapesPublishedVersionName = result.grapesPublishedVersionName;
181 if (window.grapesVersionName !== window.grapesPublishedVersionName && window.htmlVersionName !== window.htmlPublishedVersionName) {
182 editor.Panels.addButton('options', window.moquiPublishButtonSettings);
183 } else {
184 editor.Panels.removeButton('options', window.moquiPublishText);
185 }
186 }
187 });
188 editor.on('storage:end:store', () => {
189 // console.log('Storage store request ended');
190 const request = new XMLHttpRequest();
191 request.open("GET", "${baseLinkUrl}/rest/s1/moqui-mjml/mjml/afterMjmlStore?emailTemplateId="+window.emailTemplateId, false); // `false` makes the request synchronous
192 request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
193 request.setRequestHeader("X-CSRF-Token", document.getElementById('confMoquiSessionToken').value);
194 request.send();
195 if (request.status === 200) {
196 const result = JSON.parse(request.responseText);
197 window.htmlLocation = result.htmlLocation;
198 window.htmlVersionName = result.htmlVersionName;
199 window.htmlPublishedVersionName = result.htmlPublishedVersionName;
200 window.grapesLocation = result.grapesLocation;
201 window.grapesVersionName = result.grapesVersionName;
202 window.grapesPublishedVersionName = result.grapesPublishedVersionName;
203 if (window.grapesVersionName !== window.grapesPublishedVersionName && window.htmlVersionName !== window.htmlPublishedVersionName) {
204 editor.Panels.addButton('options', window.moquiPublishButtonSettings);
205 } else {
206 editor.Panels.removeButton('options', window.moquiPublishText);
207 }
208 }
209 });
210
211 // Add Publish button
212 window.moquiPublishButtonSettings = {
213 id: window.moquiPublishText,
214 command: 'moqui-publish',
215 attributes: { title: 'publish' },
216 label: `<div style="background-color: #f45e43; color:#2c2e35; padding: 0px 10px; border: none; border-radius: 10px; cursor: pointer;">Publish</div>`,
217 };
218 if (window.grapesVersionName !== window.grapesPublishedVersionName && window.htmlVersionName !== window.htmlPublishedVersionName) {
219 editor.Panels.addButton('options', window.moquiPublishButtonSettings);
220 }
221 }
222
192 window.editor = grapesjs.init({ 223 window.editor = grapesjs.init({
193 projectData: projectData, 224 projectData: projectData,
194 height: '100%', 225 height: '100%',
...@@ -215,18 +246,28 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -215,18 +246,28 @@ 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 246 // we have to properly update the body before the store and extract the
216 // project data from the response result. 247 // project data from the response result.
217 onStore: data => { 248 onStore: data => {
218 return { id: window.grapesLocation, data, html:window.editor.runCommand('mjml-code-to-html')?.html } 249 return { id: window.grapesLocation, data, moquiVars:window.moquiVars, html:window.editor.runCommand('mjml-code-to-html')?.html }
219 }, 250 },
220 onLoad: result => { 251 onLoad: result => {
221 if (result.resourceId !== null) { 252 if (result.resourceId !== null) {
222 const url = new URL(window.location.href) 253 const url = new URL(window.location.href)
223 url.searchParams.set('grapesLocation', result.grapesLocation); 254 url.searchParams.set('grapesLocation', result.grapesLocation);
224 url.searchParams.set('htmlLocation', result.htmlLocation); 255 // url.searchParams.set('htmlLocation', result.htmlLocation);
225 url.searchParams.set('emailTemplateId', result.emailTemplateId); 256 // url.searchParams.set('emailTemplateId', result.emailTemplateId);
226 window.history.pushState({}, '', url) 257 window.history.pushState({}, '', url)
227 window.grapesLocation = result.grapesLocation;
228 window.htmlLocation = result.htmlLocation; 258 window.htmlLocation = result.htmlLocation;
259 window.htmlVersionName = result.htmlVersionName;
260 window.htmlPublishedVersionName = result.htmlPublishedVersionName;
261 window.grapesLocation = result.grapesLocation;
262 window.grapesVersionName = result.grapesVersionName;
263 window.grapesPublishedVersionName = result.grapesPublishedVersionName;
229 window.emailTemplateId = result.emailTemplateId; 264 window.emailTemplateId = result.emailTemplateId;
265 window.moquiVars = result.moquiVars;
266 if (window.grapesVersionName !== window.grapesPublishedVersionName && window.htmlVersionName !== window.htmlPublishedVersionName) {
267 editor.Panels.addButton('options', window.moquiPublishButtonSettings);
268 } else {
269 editor.Panels.removeButton('options', window.moquiPublishText);
270 }
230 } 271 }
231 // console.log('onLoad ', result) 272 // console.log('onLoad ', result)
232 return result.data 273 return result.data
...@@ -237,7 +278,7 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -237,7 +278,7 @@ along with this software (see the LICENSE.md file). If not, see
237 fromElement: true, 278 fromElement: true,
238 container: '#gjs', 279 container: '#gjs',
239 280
240 plugins: ['grapesjs-mjml'], 281 plugins: ['grapesjs-mjml',moquiPlugin],
241 pluginsOpts: { 282 pluginsOpts: {
242 'grapesjs-mjml': {} 283 'grapesjs-mjml': {}
243 } 284 }
......
...@@ -17,11 +17,71 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -17,11 +17,71 @@ along with this software (see the LICENSE.md file). If not, see
17 17
18 <actions> 18 <actions>
19 <entity-find-one entity-name="moqui.basic.email.EmailTemplate" value-field="emailTemplate" auto-field-map="[emailTemplateId:emailTemplateId]"/> 19 <entity-find-one entity-name="moqui.basic.email.EmailTemplate" value-field="emailTemplate" auto-field-map="[emailTemplateId:emailTemplateId]"/>
20 <if condition="!emailTemplate"> 20 <if condition="!emailTemplate || !emailTemplate.htmlLocation || !emailTemplate.htmlPublishedVersionName">
21 <return error="true" message="Email Template not found"/> 21 <return error="true" message="Email Template not found"/>
22 </if> 22 </if>
23 <set field="dataPre" from="ec.resource.getLocationReference(emailTemplate.htmlLocation).getText(emailTemplate.htmlPublishedVersionName)"/>
24 <if condition="dataPre == null">
25 <return error="true" message="Email Template does not exist at ${emailTemplate.htmlLocation} with version ${emailTemplate.htmlPublishedVersionName}"/>
26 </if>
27 <set field="doc" from="org.jsoup.Jsoup.parse(dataPre)"/>
28 <script><![CDATA[
29 doc.outputSettings().escapeMode(org.jsoup.nodes.Entities.EscapeMode.xhtml).prettyPrint(false);
30
31 doc.traverse(new org.jsoup.select.NodeVisitor() {
32 public void head(org.jsoup.nodes.Node node, int depth) {
33 if (node instanceof org.jsoup.nodes.TextNode) {
34 def text = node.getWholeText();
35 if(text.trim().length() > 0) {
36 def newText = "";
37 def lines = text.split("\n");
38 for (line in lines) {
39 if (line.startsWith("<br>")) {
40 line = line.replaceFirst(/^<br>/, "");
41 }
42 if (line.endsWith("<br>")) {
43 newText += line;
44 } else {
45 newText += line + "<br>";
46 }
47 }
48 if (newText.startsWith("<br>")) {
49 newText = newText.replaceFirst(/^<br>/, "");
50 }
51 node = org.jsoup.nodes.TextNode.createFromEncoded(newText);
52 }
53 }
54 }
55 public void tail(org.jsoup.nodes.Node node, int depth) {
56 // No action needed on tail
57 }
58 });
59 ]]></script>
60 <set field="dataPre" from="doc.html()"/>
61 <script><![CDATA[
62 String location = emailTemplate?.htmlLocation ?: "template.ftl";
63
64 freemarker.template.Template newTemplate;
65 Reader templateReader = null;
23 66
24 <set field="renderedText" from="ec.resource.template(emailTemplate.htmlLocation, 'ftl')"/> 67 try {
68 templateReader = new StringReader(context.dataPre);
69 // Use the getFtlConfiguration method from ec.resource.templateRenderers.ftl
70 newTemplate = new freemarker.template.Template(location, templateReader, ec.resource.templateRenderers.ftl.getFtlConfiguration());
71 } catch (Exception e) {
72 throw new org.moqui.BaseArtifactException("Error while initializing template at " + location, e);
73 } finally {
74 if (templateReader != null) {
75 try { templateReader.close(); }
76 catch (Exception e) { logger.error("Error closing template reader", e); }
77 }
78 }
79 StringWriter sw = new StringWriter()
80 try {
81 newTemplate.createProcessingEnvironment(ec.contextStack, sw).process();
82 } catch (Exception e) { throw new org.moqui.BaseArtifactException("Error rendering template at " + location, e); }
83 context.renderedText = sw.toString();
84 ]]></script>
25 </actions> 85 </actions>
26 86
27 <widgets> 87 <widgets>
......
...@@ -21,6 +21,9 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -21,6 +21,9 @@ along with this software (see the LICENSE.md file). If not, see
21 <resource name="mjml"> 21 <resource name="mjml">
22 <method type="get"><service name="mjml.MjmlServices.load#GrapeJs"/></method> 22 <method type="get"><service name="mjml.MjmlServices.load#GrapeJs"/></method>
23 <method type="post"><service name="mjml.MjmlServices.store#GrapeJs"/></method> 23 <method type="post"><service name="mjml.MjmlServices.store#GrapeJs"/></method>
24 <resource name="afterMjmlStore">
25 <method type="get"><service name="mjml.MjmlServices.get#AfterMjmlStore"/></method>
26 </resource>
24 </resource> 27 </resource>
25 28
26 </resource> 29 </resource>
......
...@@ -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>
...@@ -23,18 +34,24 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -23,18 +34,24 @@ along with this software (see the LICENSE.md file). If not, see
23 </in-parameters> 34 </in-parameters>
24 <out-parameters> 35 <out-parameters>
25 <parameter name="grapesLocation"/> 36 <parameter name="grapesLocation"/>
37 <parameter name="grapesVersionName"/>
38 <parameter name="grapesPublishedVersionName"/>
26 <parameter name="htmlLocation"/> 39 <parameter name="htmlLocation"/>
40 <parameter name="htmlVersionName"/>
41 <parameter name="htmlPublishedVersionName"/>
27 <parameter name="data"/> 42 <parameter name="data"/>
43 <parameter name="emailTemplateId"/>
44 <parameter name="moquiVars"/>
28 </out-parameters> 45 </out-parameters>
29 <actions> 46 <actions>
30 <if condition="grapesLocation == 'null'"><set field="grapesLocation" from="null"/></if> 47 <if condition="grapesLocation == 'null'"><set field="grapesLocation" from="null"/></if>
31 <if condition="htmlLocation == 'null'"><set field="htmlLocation" from="null"/></if> 48 <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]"/> 49 <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> 50 <if condition="!emailTemplate"><return error="true" message="Resource not found"/></if>
36 <set field="grapesLocation" from="emailTemplate?.grapesLocation"/> 51 <set field="grapesLocation" from="emailTemplate?.grapesLocation"/>
52 <set field="grapesPublishedVersionName" from="emailTemplate?.grapesPublishedVersionName"/>
37 <set field="htmlLocation" from="emailTemplate?.htmlLocation"/> 53 <set field="htmlLocation" from="emailTemplate?.htmlLocation"/>
54 <set field="htmlPublishedVersionName" from="emailTemplate?.htmlPublishedVersionName"/>
38 55
39 <set field="grapesJsResource" from="ec.resource.getLocationReference('dbresource://grapesjs/project')"/> 56 <set field="grapesJsResource" from="ec.resource.getLocationReference('dbresource://grapesjs/project')"/>
40 <if condition="!grapesLocation &amp;&amp; !htmlLocation"> 57 <if condition="!grapesLocation &amp;&amp; !htmlLocation">
...@@ -42,25 +59,132 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -42,25 +59,132 @@ 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')"/> 59 <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')"/> 60 <set field="grapesFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.json')"/>
44 61
45 <set field="data" from="ec.resource.getLocationReference('dbresource://grapesjs/template/default.json').getText()"/> 62 <service-call name="mjml.MjmlServices.get#GlobalGrapeVarList" out-map="context"/>
63 <set field="defaultLocation" value="dbresource://grapesjs/template/default.json"/>
64 <set field="dataRaw" from="ec.resource.getLocationReference(defaultLocation).getText()"/>
65 <if condition="!dataRaw"><log level="error" message="Template not found at ${defaultLocation}. Please load seed data for moqui-mjml."/></if>
46 <set field="htmlLocation" from="grapesJsResource.location + '/' + htmlFile?.dbResourceId + '.html'"/> 66 <set field="htmlLocation" from="grapesJsResource.location + '/' + htmlFile?.dbResourceId + '.html'"/>
47 <set field="grapesLocation" from="grapesJsResource.location + '/' + grapesFile?.dbResourceId + '.json'"/> 67 <set field="grapesLocation" from="grapesJsResource.location + '/' + grapesFile?.dbResourceId + '.json'"/>
48 <script><![CDATA[ 68 <script><![CDATA[
49 htmlFile.move(htmlLocation) 69 htmlFile.move(htmlLocation)
50 grapesFile.putText(data) 70 grapesFile.putText(dataRaw)
51 grapesFile.move(grapesLocation) 71 grapesFile.move(grapesLocation)
52 ]]></script> 72 ]]></script>
53 73 <set field="htmlVersionName" from="htmlFile?.getCurrentVersion()?.getVersionName() ?: htmlFile?.getRootVersion()?.getVersionName() ?: '01'"/>
74 <set field="grapesVersionName" from="grapesFile?.getCurrentVersion()?.getVersionName() ?: grapesFile?.getRootVersion()?.getVersionName() ?: '01'"/>
54 <service-call name="update#moqui.basic.email.EmailTemplate" in-map="[emailTemplateId:emailTemplateId,grapesLocation:grapesLocation,htmlLocation:htmlLocation]"/> 75 <service-call name="update#moqui.basic.email.EmailTemplate" in-map="[emailTemplateId:emailTemplateId,grapesLocation:grapesLocation,htmlLocation:htmlLocation]"/>
76
77 <service-call name="mjml.MjmlServices.get#GlobalGrapeVarList" out-map="globalGrapeVarList"/>
78 <script><![CDATA[
79 context.putAll(globalGrapeVarList)
80
81 context.varList = globalGrapeVarList*.key.collect { '\\\$\\{'+it+'\\}' }
82 context.replaceVarRegexPattern = context.varList.join('|')
83 if (context.replaceVarRegexPattern) {
84 context.dataPre = context.dataRaw.replaceAll(context.replaceVarRegexPattern) { match ->
85 return "${match}"
86 }
87 }
88
89 context.allVarsRegexPattern = /\$\{[a-zA-Z_][a-zA-Z0-9_.]*\}/
90 context.errorVarList = context.dataRaw.findAll(context.allVarsRegexPattern).unique().collect { it - '${' - '}' } - globalGrapeVarList*.key
91 context.errorTemplateRegexPattern = errorVarList.collect { '\\\$\\{'+it+'\\}' }.join('|')
92 if (context.errorTemplateRegexPattern) {
93 context.dataPre = context.dataPre.replaceAll(context.errorTemplateRegexPattern) { match ->
94 def cleanMatch = match - '${' - '}'
95 return "\${" + cleanMatch + "!'\$\\{" + cleanMatch + "}'}"
96 }
97 }
98 ]]></script>
99 <!-- <log level="warn" message="dataPre is ${dataPre}"/>-->
100 <script><![CDATA[
101 String location = defaultLocation;
102
103 freemarker.template.Template newTemplate;
104 Reader templateReader = null;
105
106 try {
107 templateReader = new StringReader(context.dataPre);
108 // Use the getFtlConfiguration method from ec.resource.templateRenderers.ftl
109 newTemplate = new freemarker.template.Template(location, templateReader, ec.resource.templateRenderers.ftl.getFtlConfiguration());
110 } catch (Exception e) {
111 throw new org.moqui.BaseArtifactException("Error while initializing template at " + location, e);
112 } finally {
113 if (templateReader != null) {
114 try { templateReader.close(); }
115 catch (Exception e) { logger.error("Error closing template reader", e); }
116 }
117 }
118 StringWriter sw = new StringWriter()
119 try {
120 newTemplate.createProcessingEnvironment(ec.contextStack, sw).process();
121 } catch (Exception e) { throw new org.moqui.BaseArtifactException("Error rendering template at " + location, e); }
122 context.data = sw.toString();
123 ]]></script>
124 <!-- <log level="warn" message="data is ${data}"/>-->
125 <set field="moquiVars" from="globalGrapeVarList"/>
55 </then> 126 </then>
56 <else-if condition="grapesLocation &amp;&amp; htmlLocation"> 127 <else-if condition="grapesLocation &amp;&amp; htmlLocation">
57 <set field="putDbResource" from="ec.resource.getLocationReference(grapesLocation)"/> 128 <set field="grapesFile" from="ec.resource.getLocationReference(grapesLocation)"/>
58 <!-- TODO: Is this a strong enough check to prevent unauthorized access? --> 129 <!-- TODO: Is this a strong enough check to prevent unauthorized access? -->
59 <if condition="!putDbResource || putDbResource.parent?.location != grapesJsResource.location"> 130 <if condition="!grapesFile || grapesFile.parent?.location != grapesJsResource.location">
60 <return error="true" message="Resource not found"/> 131 <return error="true" message="Resource not found"/>
61 </if> 132 </if>
133 <set field="grapesVersionName" from="grapesFile?.getCurrentVersion()?.getVersionName() ?: grapesFile?.getRootVersion()?.getVersionName() ?: '01'"/>
134 <set field="tempHtmlFile" from="ec.resource.getLocationReference(htmlLocation)"/>
135 <set field="htmlVersionName" from="tempHtmlFile?.getCurrentVersion()?.getVersionName() ?: tempHtmlFile?.getRootVersion()?.getVersionName() ?: '01'"/>
136 <set field="tempHtmlFile" from="null"/>
137
138 <service-call name="mjml.MjmlServices.get#GlobalGrapeVarList" out-map="globalGrapeVarList"/>
139 <set field="dataRaw" from="grapesFile.getText()"/>
140 <script><![CDATA[
141 context.putAll(globalGrapeVarList)
142
143 context.varList = globalGrapeVarList*.key.collect { '\\\$\\{'+it+'\\}' }
144 context.replaceVarRegexPattern = context.varList.join('|')
145 if (context.replaceVarRegexPattern) {
146 context.dataPre = context.dataRaw.replaceAll(context.replaceVarRegexPattern) { match ->
147 return "${match}"
148 }
149 }
150
151 context.allVarsRegexPattern = /\$\{[a-zA-Z_][a-zA-Z0-9_.]*\}/
152 context.errorVarList = context.dataRaw.findAll(context.allVarsRegexPattern).unique().collect { it - '${' - '}' } - globalGrapeVarList*.key
153 context.errorTemplateRegexPattern = errorVarList.collect { '\\\$\\{'+it+'\\}' }.join('|')
154 if (context.errorTemplateRegexPattern) {
155 context.dataPre = context.dataPre.replaceAll(context.errorTemplateRegexPattern) { match ->
156 def cleanMatch = match - '${' - '}'
157 return "\${" + cleanMatch + "!'\$\\{" + cleanMatch + "}'}"
158 }
159 }
160 ]]></script>
161 <!-- <log level="warn" message="dataPre is ${dataPre}"/>-->
162 <script><![CDATA[
163 String location = grapesFile.location;
62 164
63 <set field="data" from="putDbResource.getText()"/> 165 freemarker.template.Template newTemplate;
166 Reader templateReader = null;
167
168 try {
169 templateReader = new StringReader(context.dataPre);
170 // Use the getFtlConfiguration method from ec.resource.templateRenderers.ftl
171 newTemplate = new freemarker.template.Template(location, templateReader, ec.resource.templateRenderers.ftl.getFtlConfiguration());
172 } catch (Exception e) {
173 throw new org.moqui.BaseArtifactException("Error while initializing template at " + location, e);
174 } finally {
175 if (templateReader != null) {
176 try { templateReader.close(); }
177 catch (Exception e) { logger.error("Error closing template reader", e); }
178 }
179 }
180 StringWriter sw = new StringWriter()
181 try {
182 newTemplate.createProcessingEnvironment(ec.contextStack, sw).process();
183 } catch (Exception e) { throw new org.moqui.BaseArtifactException("Error rendering template at " + location, e); }
184 context.data = sw.toString();
185 ]]></script>
186 <!-- <log level="warn" message="data is ${data}"/>-->
187 <set field="moquiVars" from="globalGrapeVarList"/>
64 </else-if> 188 </else-if>
65 <else> 189 <else>
66 <return error="true" message="Resource not found"/> 190 <return error="true" message="Resource not found"/>
...@@ -73,13 +197,19 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -73,13 +197,19 @@ along with this software (see the LICENSE.md file). If not, see
73 <in-parameters> 197 <in-parameters>
74 <parameter name="htmlLocation"/> 198 <parameter name="htmlLocation"/>
75 <parameter name="grapesLocation"/> 199 <parameter name="grapesLocation"/>
76 <parameter name="emailTemplateId"/> 200 <parameter name="emailTemplateId" required="true"/>
77 <parameter name="data"/> 201 <parameter name="data" required="true"/>
78 <parameter name="html" allow-html="any"/> 202 <parameter name="moquiVars" required="true"/>
203 <parameter name="html" allow-html="any" required="true"/>
204 <parameter name="publish" default="false" type="Boolean" required="true"/>
79 </in-parameters> 205 </in-parameters>
80 <out-parameters> 206 <out-parameters>
81 <parameter name="htmlLocation"/> 207 <parameter name="htmlLocation"/>
208 <parameter name="htmlVersionName"/>
209 <parameter name="htmlPublishedVersionName"/>
82 <parameter name="grapesLocation"/> 210 <parameter name="grapesLocation"/>
211 <parameter name="grapesVersionName"/>
212 <parameter name="grapesPublishedVersionName"/>
83 </out-parameters> 213 </out-parameters>
84 <actions> 214 <actions>
85 <if condition="htmlLocation == 'null'"><set field="htmlLocation" from="null"/></if> 215 <if condition="htmlLocation == 'null'"><set field="htmlLocation" from="null"/></if>
...@@ -87,13 +217,18 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -87,13 +217,18 @@ along with this software (see the LICENSE.md file). If not, see
87 <entity-find-one entity-name="moqui.basic.email.EmailTemplate" value-field="emailTemplate" auto-field-map="[emailTemplateId:emailTemplateId]"/> 217 <entity-find-one entity-name="moqui.basic.email.EmailTemplate" value-field="emailTemplate" auto-field-map="[emailTemplateId:emailTemplateId]"/>
88 <if condition="!emailTemplate"><return error="true" message="Resource not found"/></if> 218 <if condition="!emailTemplate"><return error="true" message="Resource not found"/></if>
89 <set field="grapesLocation" from="emailTemplate?.grapesLocation"/> 219 <set field="grapesLocation" from="emailTemplate?.grapesLocation"/>
220 <set field="grapesPublishedVersionName" from="emailTemplate?.grapesPublishedVersionName"/>
90 <set field="htmlLocation" from="emailTemplate?.htmlLocation"/> 221 <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)"/> 222 <set field="htmlPublishedVersionName" from="emailTemplate?.htmlPublishedVersionName"/>
223 <set field="dataMap" from="new groovy.json.JsonSlurper().parseText(ec.web.secureRequestParameters._requestBodyText).data"/>
224 <!-- <log level="warn" message="dataMap is ${dataMap}"/>-->
225 <set field="moquiVars" from="new groovy.json.JsonSlurper().parseText(ec.web.secureRequestParameters._requestBodyText).moquiVars"/>
226 <set field="data" from="groovy.json.JsonOutput.toJson(dataMap)"/>
92 227
93 <set field="grapesJsResource" from="ec.resource.getLocationReference('dbresource://grapesjs/project')"/> 228 <set field="grapesJsResource" from="ec.resource.getLocationReference('dbresource://grapesjs/project')"/>
94 <if condition="!htmlLocation &amp;&amp; !grapesLocation"> 229 <if condition="!htmlLocation &amp;&amp; !grapesLocation">
95 <then> 230 <then>
96 <!-- TODO: This should work, but isn't used anywhere and is untested. 231 <!-- 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')"/> 232 <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')"/> 233 <set field="grapesFile" from="grapesJsResource.makeFile(java.util.UUID.randomUUID().toString()+'.json')"/>
99 234
...@@ -118,7 +253,18 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -118,7 +253,18 @@ along with this software (see the LICENSE.md file). If not, see
118 <if condition="!grapesFile || grapesFile.parent?.location != grapesJsResource.location"> 253 <if condition="!grapesFile || grapesFile.parent?.location != grapesJsResource.location">
119 <return error="true" message="Resource not found"/> 254 <return error="true" message="Resource not found"/>
120 </if> 255 </if>
121 <script>grapesFile.putText(data)</script> 256 <script><![CDATA[
257 context.replaceMoquiVarRegexPattern = context.moquiVars*.value.join('|')
258
259 if (context.replaceMoquiVarRegexPattern) {
260 context.dataPre = data.replaceAll(context.replaceMoquiVarRegexPattern) { match ->
261 def output = moquiVars.find { it.value == match }?.key
262 return '${' + output + '}'
263 }
264 }
265 ]]></script>
266 <script>grapesFile.putText(dataPre)</script>
267 <set field="grapesVersionName" from="grapesFile?.getCurrentVersion()?.getVersionName()"/>
122 268
123 <set field="htmlFile" from="null"/> 269 <set field="htmlFile" from="null"/>
124 <if condition="htmlLocation"><then> 270 <if condition="htmlLocation"><then>
...@@ -141,7 +287,22 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -141,7 +287,22 @@ along with this software (see the LICENSE.md file). If not, see
141 <if condition="!htmlFile || htmlFile.parent?.location != grapesJsResource.location"> 287 <if condition="!htmlFile || htmlFile.parent?.location != grapesJsResource.location">
142 <return error="true" message="Resource not found"/> 288 <return error="true" message="Resource not found"/>
143 </if> 289 </if>
144 <script><![CDATA[htmlFile.putText(html)]]></script> 290 <set field="htmlOut" from="null"/>
291 <script><![CDATA[
292 if (context.replaceMoquiVarRegexPattern) {
293 context.htmlOut = html.replaceAll(context.replaceMoquiVarRegexPattern) { match ->
294 def output = moquiVars.find { it.value == match }?.key
295 return '${' + output + '}'
296 }
297 }
298 htmlFile.putText(htmlOut)]]></script>
299 <set field="htmlVersionName" from="htmlFile?.getCurrentVersion()?.getVersionName()"/>
300 <if condition="publish">
301 <service-call name="update#moqui.basic.email.EmailTemplate" in-map="[emailTemplateId:emailTemplateId,
302 htmlPublishedVersionName:htmlVersionName,grapesPublishedVersionName:grapesVersionName]"/>
303 <set field="htmlPublishedVersionName" from="htmlVersionName"/>
304 <set field="grapesPublishedVersionName" from="grapesVersionName"/>
305 </if>
145 </else-if> 306 </else-if>
146 <else> 307 <else>
147 <return error="true" message="Resource not found"/> 308 <return error="true" message="Resource not found"/>
...@@ -149,6 +310,102 @@ along with this software (see the LICENSE.md file). If not, see ...@@ -149,6 +310,102 @@ along with this software (see the LICENSE.md file). If not, see
149 </if> 310 </if>
150 </actions> 311 </actions>
151 </service> 312 </service>
313 <service verb="get" noun="AfterMjmlStore">
314 <description>Get the data after storing the MJML data</description>
315 <in-parameters>
316 <parameter name="emailTemplateId" required="true"/>
317 </in-parameters>
318 <out-parameters>
319 <parameter name="htmlLocation"/>
320 <parameter name="htmlVersionName"/>
321 <parameter name="htmlPublishedVersionName"/>
322 <parameter name="grapesLocation"/>
323 <parameter name="grapesVersionName"/>
324 <parameter name="grapesPublishedVersionName"/>
325 </out-parameters>
326 <actions>
327 <entity-find-one entity-name="moqui.basic.email.EmailTemplate" value-field="emailTemplate" auto-field-map="[emailTemplateId:emailTemplateId]"/>
328 <if condition="!emailTemplate"><return error="true" message="Resource not found"/></if>
329 <set field="grapesLocation" from="emailTemplate?.grapesLocation"/>
330 <set field="grapesVersionName" from="ec.resource.getLocationReference(grapesLocation)?.getCurrentVersion()?.getVersionName()"/>
331 <set field="grapesPublishedVersionName" from="emailTemplate?.grapesPublishedVersionName"/>
332 <set field="htmlLocation" from="emailTemplate?.htmlLocation"/>
333 <set field="htmlVersionName" from="ec.resource.getLocationReference(htmlLocation)?.getCurrentVersion()?.getVersionName()"/>
334 <set field="htmlPublishedVersionName" from="emailTemplate?.htmlPublishedVersionName"/>
335 </actions>
336 </service>
337
338 <service verb="remove" noun="ExcessMjmlEmailResourceHistories" authenticate="anonymous-all">
339 <in-parameters>
340 <parameter name="runDate" default="Timestamp.from(java.time.Instant.now())" required="true" type="Timestamp"/>
341 </in-parameters>
342 <out-parameters>
343 <parameter name="removedHistoryList"/>
344 </out-parameters>
345 <actions>
346 <entity-find entity-name="moqui.basic.email.EmailTemplate" list="emailTemplateList">
347 <econdition field-name="grapesLocation" operator="is-not-null"/>
348 <econdition field-name="htmlLocation" operator="is-not-null"/>
349 <select-field field-name="emailTemplateId,grapesLocation,htmlLocation,grapesPublishedVersionName,htmlPublishedVersionName"/>
350 <order-by field-name="-lastUpdatedStamp"/>
351 </entity-find>
352 <set field="resourceList" from="[]"/>
353 <iterate list="emailTemplateList" entry="emailTemplate">
354 <set field="grapesFile" from="ec.resource.getLocationReference(emailTemplate.grapesLocation)"/>
355 <if condition="grapesFile">
356 <script><![CDATA[
357 def versionWhitelist = []
358 if (grapesFile?.getCurrentVersion()?.getVersionName()) versionWhitelist.add(grapesFile.getCurrentVersion().getVersionName())
359 if (emailTemplate?.grapesPublishedVersionName) versionWhitelist.add(emailTemplate.grapesPublishedVersionName)
360 resourceList.add([location:grapesFile.location,resourceId:grapesFile.dbResourceId,versionWhitelist:versionWhitelist.size()>0?versionWhitelist:null])]]></script>
361 </if>
362 <set field="htmlFile" from="ec.resource.getLocationReference(emailTemplate.htmlLocation)"/>
363 <if condition="htmlFile">
364 <script><![CDATA[
365 def versionWhitelist = []
366 if (htmlFile?.getCurrentVersion()?.getVersionName()) versionWhitelist.add(htmlFile.getCurrentVersion().getVersionName())
367 if (emailTemplate?.htmlPublishedVersionName) versionWhitelist.add(emailTemplate.htmlPublishedVersionName)
368 resourceList.add([location:htmlFile.location,resourceId:htmlFile.dbResourceId,versionWhitelist:versionWhitelist.size()>0?versionWhitelist:null])]]></script>
369 </if>
370 </iterate>
371
372 <set field="oneHourAgo" from="Timestamp.from(runDate.toInstant().minus(1, java.time.temporal.ChronoUnit.HOURS))"/>
373 <set field="removedHistoryList" from="[]"/>
374
375 <iterate list="resourceList" entry="resource">
376 <entity-find entity-name="moqui.resource.DbResourceFileHistory" list="resourceHistoryPreList">
377 <econdition field-name="resourceId" from="resource.resourceId"/>
378 <econdition field-name="versionDate" operator="greater" from="oneHourAgo"/>
379 <econdition field-name="versionDate" operator="less-equals" from="runDate"/>
380 <select-field field-name="resourceId,versionName,versionDate,previousVersionName,userId"/>
381 <order-by field-name="-versionDate"/>
382 </entity-find>
383 <set field="skippedUserIds" from="[]"/>
384 <set field="resourceHistoryList" from="[]"/>
385 <iterate list="resourceHistoryPreList" entry="resourceHistory">
386 <!-- Add all but the single latest history per userId to resourceHistoryList -->
387 <if condition="resource.versionWhitelist.contains(resourceHistory.versionName) || !skippedUserIds.contains(resourceHistory.userId)">
388 <!-- <log level="warn" message="${resource.resourceId} ${resourceHistory.versionName} userId ${resourceHistory.userId}"/>-->
389 <set field="skippedUserIds" from="skippedUserIds + [resourceHistory.userId]"/><continue/></if>
390 <set field="resourceHistoryList" from="resourceHistoryList + [resourceHistory]"/>
391 </iterate>
392 <iterate list="resourceHistoryList" entry="resourceHistory">
393 <entity-find-one entity-name="moqui.resource.DbResourceFileHistory" value-field="usedResourceHistory" auto-field-map="[resourceId:resource.resourceId,previousVersionName:resourceHistory.versionName]" for-update="true">
394 <select-field field-name="resourceId,versionName,versionDate,previousVersionName,userId"/></entity-find-one>
395 <!-- <log level="warn" message="set versionName ${usedResourceHistory.versionName} of previousVersionName ${usedResourceHistory.previousVersionName} to ${resourceHistory.previousVersionName}"/>-->
396 <if condition="usedResourceHistory">
397 <set field="usedResourceHistory.previousVersionName" from="resourceHistory?.previousVersionName"/>
398
399 <log level="info" message="Removed resource history ${resourceHistory.resourceId} version ${resourceHistory.versionName}"/>
400 <set field="removedHistoryList" from="removedHistoryList + [resourceId:resource.resourceId, versionName:resourceHistory.versionName,versionDate:resourceHistory.versionDate,userId:resourceHistory.userId]"/>
152 401
402 <entity-update value-field="usedResourceHistory"/>
403 <entity-delete value-field="resourceHistory"/>
404 </if>
405 </iterate>
406 </iterate>
407
408 </actions>
409 </service>
153 410
154 </services> 411 </services>
......