Merge pull request #2 from moqui/test-upstream
Test upstream
Showing
9 changed files
with
438 additions
and
138 deletions
... | @@ -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 | ... | ... |
build.gradle
deleted
100644 → 0
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"/> | ... | ... |
lib/README
deleted
100644 → 0
... | @@ -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 | 114 | ||
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 | 117 | ||
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 | 120 | ||
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 && !htmlLocation"> | 57 | <if condition="!grapesLocation && !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 && htmlLocation"> | 127 | <else-if condition="grapesLocation && 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 && !grapesLocation"> | 229 | <if condition="!htmlLocation && !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> | ... | ... |
-
Please register or sign in to post a comment