@useinsider/guido 3.19.0-beta.c8d2be8 → 3.19.0-beta.cd75ff0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/@types/config/schemas.js +102 -87
- package/dist/components/Guido.vue.js +4 -4
- package/dist/components/Guido.vue2.js +83 -75
- package/dist/components/organisms/AutoSaveController.vue2.js +10 -8
- package/dist/components/organisms/base/AuthErrorModal.vue.js +18 -0
- package/dist/components/organisms/base/AuthErrorModal.vue2.js +42 -0
- package/dist/components/organisms/header/EditorActions.vue.js +2 -2
- package/dist/components/organisms/header/EditorActions.vue2.js +83 -48
- package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
- package/dist/components/organisms/header/LeftSlot.vue2.js +16 -12
- package/dist/components/organisms/validation/ValidationErrorDrawer.vue.js +20 -0
- package/dist/components/organisms/validation/ValidationErrorDrawer.vue2.js +50 -0
- package/dist/components/wrappers/WpDrawer.vue.js +9 -7
- package/dist/components/wrappers/WpDrawer.vue2.js +1 -1
- package/dist/components/wrappers/WpModal.vue.js +1 -1
- package/dist/components/wrappers/WpModal.vue2.js +4 -3
- package/dist/composables/useActionsApi.js +52 -47
- package/dist/composables/useAuthError.js +52 -0
- package/dist/composables/useAutoSave.js +44 -42
- package/dist/composables/useCodeEditorApi.js +23 -9
- package/dist/composables/useHtmlCompiler.js +37 -26
- package/dist/composables/useHtmlValidator.js +2 -0
- package/dist/composables/useHttp.js +48 -49
- package/dist/composables/useLegacyStrategyConversion.js +23 -19
- package/dist/composables/useSave.js +92 -45
- package/dist/composables/useSaveValidation.js +284 -0
- package/dist/composables/useSaveValidationRollout.js +20 -0
- package/dist/composables/useStripo.js +24 -20
- package/dist/composables/useToaster.js +17 -15
- package/dist/composables/useTranslations.js +3 -2
- package/dist/composables/useVersionHistoryApi.js +33 -24
- package/dist/composables/validators/useCouponBlockValidator.js +24 -21
- package/dist/composables/validators/useLiquidValidator.js +15 -15
- package/dist/composables/validators/useUnsubscribeBlockValidator.js +34 -27
- package/dist/config/migrator/index.js +14 -13
- package/dist/config/migrator/recommendationPartnerMigrator.js +50 -0
- package/dist/enums/extensions/recommendationBlock.js +30 -29
- package/dist/enums/save-validation.js +4 -0
- package/dist/extensions/Blocks/Recommendation/block.js +15 -14
- package/dist/extensions/Blocks/Recommendation/controls/main/index.js +45 -38
- package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +72 -49
- package/dist/extensions/Blocks/Recommendation/store/recommendation.js +30 -29
- package/dist/extensions/Blocks/Recommendation/utils/partnerScope.js +25 -0
- package/dist/guido.css +1 -1
- package/dist/services/settingsApi.js +11 -0
- package/dist/src/@types/config/schemas.d.ts +20 -0
- package/dist/src/@types/save-validation.d.ts +19 -0
- package/dist/src/components/Guido.vue.d.ts +17 -1
- package/dist/src/components/organisms/base/AuthErrorModal.vue.d.ts +2 -0
- package/dist/src/components/organisms/header/EditorActions.vue.d.ts +17 -1
- package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +17 -1
- package/dist/src/components/organisms/header/RightSlot.vue.d.ts +17 -1
- package/dist/src/components/organisms/validation/ValidationErrorDrawer.vue.d.ts +2 -0
- package/dist/src/components/wrappers/WpDrawer.vue.d.ts +4 -3
- package/dist/src/components/wrappers/WpModal.vue.d.ts +5 -1
- package/dist/src/composables/useAuthError.d.ts +12 -0
- package/dist/src/composables/useAutoSave.d.ts +4 -1
- package/dist/src/composables/useConfig.d.ts +4 -0
- package/dist/src/composables/useHtmlValidator.d.ts +21 -0
- package/dist/src/composables/useHttp.d.ts +2 -0
- package/dist/src/composables/useSave.d.ts +6 -1
- package/dist/src/composables/useSaveValidation.d.ts +6 -0
- package/dist/src/composables/useSaveValidationRollout.d.ts +4 -0
- package/dist/src/composables/validators/useCouponBlockValidator.d.ts +3 -0
- package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +10 -0
- package/dist/src/config/migrator/index.d.ts +2 -1
- package/dist/src/config/migrator/recommendationPartnerMigrator.d.ts +19 -0
- package/dist/src/enums/extensions/recommendationBlock.d.ts +1 -1
- package/dist/src/enums/save-validation.d.ts +27 -0
- package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +6 -0
- package/dist/src/extensions/Blocks/Recommendation/utils/partnerScope.d.ts +33 -0
- package/dist/src/services/settingsApi.d.ts +6 -0
- package/dist/src/stores/config.d.ts +52 -0
- package/dist/src/stores/save-validation.d.ts +22 -0
- package/dist/src/stores/setup-settings.d.ts +4 -0
- package/dist/src/utils/stripoEditorApi.d.ts +4 -0
- package/dist/src/utils/templatePreparation.d.ts +30 -0
- package/dist/static/styles/components/recommendation-strategy.css.js +9 -0
- package/dist/stores/config.js +84 -67
- package/dist/stores/save-validation.js +30 -0
- package/dist/stores/setup-settings.js +10 -0
- package/dist/stores/unsubscribe.js +3 -3
- package/dist/utils/stripoEditorApi.js +11 -0
- package/dist/utils/templatePreparation.js +39 -36
- package/package.json +2 -1
|
@@ -1,99 +1,98 @@
|
|
|
1
|
-
import { getCsrfToken as
|
|
2
|
-
import { ref as
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { getCsrfToken as D } from "../utils/genericUtil.js";
|
|
2
|
+
import { ref as T, computed as w } from "vue";
|
|
3
|
+
import { isPanelOrigin as j, reportAuthError as X } from "./useAuthError.js";
|
|
4
|
+
const k = [401, 403, 419], _ = (g = {}) => {
|
|
5
|
+
const f = T(!1), n = T(null), c = T(null), b = w(() => f.value), y = w(() => n.value !== null), E = {
|
|
5
6
|
baseURL: "",
|
|
6
7
|
timeout: 1e4,
|
|
7
8
|
retry: 0,
|
|
8
9
|
retryDelay: 1e3,
|
|
9
10
|
headers: {
|
|
10
11
|
"Content-Type": "application/json",
|
|
11
|
-
"X-CSRF-TOKEN":
|
|
12
|
+
"X-CSRF-TOKEN": D(),
|
|
12
13
|
Accept: "application/json",
|
|
13
|
-
//
|
|
14
|
-
// fetch gets a 302 to the login page and never sees an auth status.
|
|
14
|
+
// Without this a guest fetch gets a 302 to the login page, never an auth status.
|
|
15
15
|
"X-Requested-With": "XMLHttpRequest"
|
|
16
16
|
},
|
|
17
17
|
...g
|
|
18
18
|
}, x = (e, r) => {
|
|
19
19
|
if (e.startsWith("http://") || e.startsWith("https://"))
|
|
20
20
|
return e;
|
|
21
|
-
const t = r ||
|
|
21
|
+
const t = r || E.baseURL || "";
|
|
22
22
|
return t.endsWith("/") && e.startsWith("/") ? `${t}${e.slice(1)}` : t.endsWith("/") || e.startsWith("/") ? `${t}${e}` : `${t}/${e}`;
|
|
23
|
-
},
|
|
23
|
+
}, R = (e) => new Promise((r, t) => {
|
|
24
24
|
setTimeout(() => {
|
|
25
25
|
t(new Error("Request timeout"));
|
|
26
26
|
}, e);
|
|
27
|
-
}),
|
|
27
|
+
}), P = (e) => new Promise((r) => {
|
|
28
28
|
setTimeout(r, e);
|
|
29
29
|
}), i = async (e, r, t, W = {}) => {
|
|
30
|
-
n.value = null,
|
|
31
|
-
const a = { ...
|
|
30
|
+
n.value = null, f.value = !0, c.value = new AbortController();
|
|
31
|
+
const a = { ...E, ...W }, p = x(r, a.baseURL), d = {
|
|
32
32
|
method: e,
|
|
33
33
|
headers: { ...a.headers },
|
|
34
|
-
signal:
|
|
34
|
+
signal: c.value.signal,
|
|
35
35
|
...a
|
|
36
36
|
};
|
|
37
|
-
t && ["POST", "PUT", "PATCH"].includes(e) && (t instanceof FormData ? (delete
|
|
38
|
-
const
|
|
39
|
-
if (window.isSessionExpired)
|
|
40
|
-
|
|
37
|
+
t && ["POST", "PUT", "PATCH"].includes(e) && (t instanceof FormData ? (delete d.headers["Content-Type"], d.body = t) : d.body = JSON.stringify(t));
|
|
38
|
+
const v = async (m = 0) => {
|
|
39
|
+
if (window.isSessionExpired) {
|
|
40
|
+
n.value = { message: "Session expired", authReported: !0 };
|
|
41
|
+
const o = new Error("Session expired");
|
|
42
|
+
throw o.authReported = !0, o;
|
|
43
|
+
}
|
|
41
44
|
try {
|
|
42
|
-
const o = [fetch(
|
|
43
|
-
a.timeout && o.push(
|
|
45
|
+
const o = [fetch(p, d)];
|
|
46
|
+
a.timeout && o.push(R(a.timeout));
|
|
44
47
|
const s = await Promise.race(o);
|
|
45
48
|
if (!s.ok) {
|
|
46
|
-
const
|
|
47
|
-
throw
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}))).catch(() => {
|
|
54
|
-
}), f;
|
|
49
|
+
const H = `HTTP Error: ${s.status} ${s.statusText}`, l = new Error(H);
|
|
50
|
+
throw l.status = s.status, l.statusText = s.statusText, l.response = s, l.authReported = k.includes(s.status) && j(p), l.authReported && Promise.resolve().then(() => s.clone().text().catch(() => "")).then((O) => X(
|
|
51
|
+
s.status,
|
|
52
|
+
O,
|
|
53
|
+
new URL(p, window.location.href).href
|
|
54
|
+
)).catch(() => {
|
|
55
|
+
}), l;
|
|
55
56
|
}
|
|
56
|
-
let
|
|
57
|
-
const
|
|
58
|
-
return
|
|
59
|
-
data:
|
|
57
|
+
let h;
|
|
58
|
+
const u = s.headers.get("content-type");
|
|
59
|
+
return u != null && u.includes("application/json") ? h = await s.json() : u != null && u.includes("text/") ? h = await s.text() : h = await s.blob(), {
|
|
60
|
+
data: h,
|
|
60
61
|
status: s.status,
|
|
61
62
|
statusText: s.statusText,
|
|
62
63
|
headers: s.headers
|
|
63
64
|
};
|
|
64
65
|
} catch (o) {
|
|
65
66
|
const s = o instanceof Error && o.name === "AbortError";
|
|
66
|
-
if (
|
|
67
|
-
return await
|
|
67
|
+
if (m < (a.retry || 0) && !s)
|
|
68
|
+
return await P(a.retryDelay || 1e3), v(m + 1);
|
|
68
69
|
throw o instanceof Error ? o.name === "AbortError" ? n.value = { message: "Request was cancelled" } : n.value = { message: o.message } : n.value = o, o;
|
|
69
70
|
}
|
|
70
71
|
};
|
|
71
72
|
try {
|
|
72
|
-
return await
|
|
73
|
+
return await v();
|
|
73
74
|
} finally {
|
|
74
|
-
|
|
75
|
+
f.value = !1, c.value = null;
|
|
75
76
|
}
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
},
|
|
77
|
+
}, A = (e, r) => i("GET", e, void 0, r), S = (e, r, t) => i("POST", e, r, t), U = (e, r, t) => i("PUT", e, r, t), C = (e, r, t) => i("PATCH", e, r, t), $ = (e, r) => i("DELETE", e, void 0, r), q = (e) => {
|
|
78
|
+
c.value && c.value.abort(e);
|
|
79
|
+
}, L = () => {
|
|
79
80
|
n.value = null;
|
|
80
81
|
};
|
|
81
82
|
return {
|
|
82
|
-
// State
|
|
83
83
|
loading: b,
|
|
84
84
|
error: w(() => n.value),
|
|
85
85
|
hasError: y,
|
|
86
|
-
// Methods
|
|
87
86
|
request: i,
|
|
88
|
-
get:
|
|
89
|
-
post:
|
|
90
|
-
put:
|
|
91
|
-
patch:
|
|
92
|
-
delete:
|
|
93
|
-
cancel:
|
|
94
|
-
clearError:
|
|
87
|
+
get: A,
|
|
88
|
+
post: S,
|
|
89
|
+
put: U,
|
|
90
|
+
patch: C,
|
|
91
|
+
delete: $,
|
|
92
|
+
cancel: q,
|
|
93
|
+
clearError: L
|
|
95
94
|
};
|
|
96
95
|
};
|
|
97
96
|
export {
|
|
98
|
-
|
|
97
|
+
_ as useHttp
|
|
99
98
|
};
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { useRecommendation as
|
|
2
|
-
import { useToaster as
|
|
1
|
+
import { useRecommendation as C } from "./useRecommendation.js";
|
|
2
|
+
import { useToaster as I } from "./useToaster.js";
|
|
3
3
|
import { useTranslations as v } from "./useTranslations.js";
|
|
4
4
|
import { ToasterTypeOptions as g } from "../enums/toaster.js";
|
|
5
5
|
import "../extensions/Blocks/Recommendation/constants/selectors.js";
|
|
6
6
|
import { CURRENT_CONFIG_VERSION as L } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
|
|
7
7
|
import { useRecommendationExtensionStore as b } from "../extensions/Blocks/Recommendation/store/recommendation.js";
|
|
8
|
-
import { resolveMigrationTarget as
|
|
9
|
-
import { commitBlockChangeOutsidePanel as
|
|
10
|
-
import { useConfigStore as
|
|
8
|
+
import { resolveMigrationTarget as k, buildLegacyMigrationPayload as R } from "../extensions/Blocks/Recommendation/utils/legacyStrategyMigration.js";
|
|
9
|
+
import { commitBlockChangeOutsidePanel as T } from "../extensions/Blocks/Recommendation/utils/nodeConfigWriter.js";
|
|
10
|
+
import { useConfigStore as h } from "../stores/config.js";
|
|
11
11
|
const F = () => {
|
|
12
|
-
const e = b(), r =
|
|
12
|
+
const e = b(), r = h(), { buildCampaignUrl: m } = C(), { showToaster: c } = I(), a = v();
|
|
13
13
|
return { convertLegacyStrategy: async () => {
|
|
14
14
|
const t = e.currentRecommendationId;
|
|
15
15
|
if (t === null || !e.isLegacyStrategyBlock || e.legacyConversionLoading)
|
|
16
16
|
return "";
|
|
17
|
-
const { strategy: d, productIds: y, size: l, strategyId: p, nodeConfigVersion: u } = e.recommendationConfigs, f = { strategyId: p, size: l, nodeConfigVersion: u }, s =
|
|
17
|
+
const { strategy: d, productIds: y, size: l, strategyId: p, nodeConfigVersion: u } = e.recommendationConfigs, f = { strategyId: p, size: l, nodeConfigVersion: u }, s = k({
|
|
18
18
|
campaignId: r.campaignId,
|
|
19
19
|
templateLibraryId: r.templateLibraryId
|
|
20
|
-
}), S =
|
|
20
|
+
}), S = R({
|
|
21
21
|
blockId: t,
|
|
22
22
|
url: m(String(t)),
|
|
23
23
|
campaignId: r.campaignId,
|
|
@@ -29,30 +29,34 @@ const F = () => {
|
|
|
29
29
|
let o;
|
|
30
30
|
try {
|
|
31
31
|
o = await e.convertLegacyStrategy(S, s);
|
|
32
|
-
} catch (
|
|
33
|
-
return console.error("[useLegacyStrategyConversion] migration failed",
|
|
32
|
+
} catch (i) {
|
|
33
|
+
return console.error("[useLegacyStrategyConversion] migration failed", i), c({
|
|
34
34
|
type: g.Error,
|
|
35
|
-
message:
|
|
35
|
+
message: a("email-editor.legacy-strategy-migration-failed"),
|
|
36
|
+
actionButton: {
|
|
37
|
+
text: a("ds-steps.create-recommendation-strategy"),
|
|
38
|
+
onClick: () => e.openStrategyCreation()
|
|
39
|
+
}
|
|
36
40
|
}), "";
|
|
37
41
|
}
|
|
38
42
|
try {
|
|
39
43
|
await e.fetchStrategyProductsForBlock(t);
|
|
40
|
-
} catch (
|
|
41
|
-
console.error("[useLegacyStrategyConversion] product refetch failed",
|
|
44
|
+
} catch (i) {
|
|
45
|
+
console.error("[useLegacyStrategyConversion] product refetch failed", i);
|
|
42
46
|
}
|
|
43
|
-
const
|
|
44
|
-
return
|
|
47
|
+
const n = e.blockStates[t];
|
|
48
|
+
return n ? T(
|
|
45
49
|
t,
|
|
46
50
|
{
|
|
47
51
|
strategyId: o,
|
|
48
|
-
size:
|
|
52
|
+
size: n.recommendationConfigs.size,
|
|
49
53
|
configVersion: L
|
|
50
54
|
},
|
|
51
|
-
|
|
55
|
+
n.recommendationProducts,
|
|
52
56
|
"Converted legacy recommendation strategy"
|
|
53
|
-
) ? (e.currentRecommendationId === t && e.openLegacyStrategyUpdatedModal(), o) : (console.error("[useLegacyStrategyConversion] node write failed", t), e.revertLegacyConversion(t, f),
|
|
57
|
+
) ? (e.currentRecommendationId === t && e.openLegacyStrategyUpdatedModal(), o) : (console.error("[useLegacyStrategyConversion] node write failed", t), e.revertLegacyConversion(t, f), c({
|
|
54
58
|
type: g.Error,
|
|
55
|
-
message:
|
|
59
|
+
message: a("email-editor.legacy-strategy-update-failed")
|
|
56
60
|
}), "") : o;
|
|
57
61
|
} };
|
|
58
62
|
};
|
|
@@ -1,55 +1,102 @@
|
|
|
1
|
-
import { useActionsApi as
|
|
1
|
+
import { useActionsApi as R } from "./useActionsApi.js";
|
|
2
2
|
import { useConfig as q } from "./useConfig.js";
|
|
3
|
-
import { useEditorLock as
|
|
4
|
-
import { useSaveStart as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return;
|
|
28
|
-
if (
|
|
3
|
+
import { useEditorLock as O } from "./useEditorLock.js";
|
|
4
|
+
import { useSaveStart as D, useSaveComplete as F } from "./useGuidoActions.js";
|
|
5
|
+
import { useSaveValidation as j } from "./useSaveValidation.js";
|
|
6
|
+
import { useSaveValidationRollout as z } from "./useSaveValidationRollout.js";
|
|
7
|
+
import { useSyncModuleExtractor as G } from "./useSyncModuleExtractor.js";
|
|
8
|
+
import { ToasterTypeOptions as I } from "../enums/toaster.js";
|
|
9
|
+
import { useStripoApi as J } from "../services/stripoApi.js";
|
|
10
|
+
import { useConfigStore as K } from "../stores/config.js";
|
|
11
|
+
import { useDynamicContentStore as N } from "../stores/dynamic-content.js";
|
|
12
|
+
import { useSaveValidationStore as Q } from "../stores/save-validation.js";
|
|
13
|
+
import { useTemplatePreparation as X, getHtmlReferencedUnsubscribePages as Y } from "../utils/templatePreparation.js";
|
|
14
|
+
import { useAllowlist as Z } from "./useAllowlist.js";
|
|
15
|
+
import { useHtmlValidator as _ } from "./useHtmlValidator.js";
|
|
16
|
+
import { useToaster as $ } from "./useToaster.js";
|
|
17
|
+
import { useCouponBlockValidator as tt } from "./validators/useCouponBlockValidator.js";
|
|
18
|
+
import { useLiquidValidator as et } from "./validators/useLiquidValidator.js";
|
|
19
|
+
import { useSyncModuleBlockValidator as ot } from "./validators/useSyncModuleBlockValidator.js";
|
|
20
|
+
import { useUnsubscribeBlockValidator as st } from "./validators/useUnsubscribeBlockValidator.js";
|
|
21
|
+
const Bt = () => {
|
|
22
|
+
const r = D(), p = F(), { validateHtml: v } = _(), { validateLiquidSyntax: S } = et(), { validateCouponBlockTags: y } = tt(), { validateUnsubscribeBlockUniqueness: w, validateUnsubscribeBlockHasTemplate: E } = st(), { validateSyncModuleBlocks: n } = ot(), { callbacks: a, isFeatureEnabled: m } = q(), { extractSyncModuleData: b } = G(), { setSyncModuleUnsubscriptionPages: g } = J(), { editorSave: H, getTemplateData: V } = R(), { showToaster: x } = $(), { getBlockedDomains: k, resolveToast: C } = Z(), { prepareTemplateDetails: d } = X(), { collectSaveValidationErrors: B } = j(), { isEnabled: h } = z(), T = Q(), P = N(), f = K(), { runExclusive: l } = O(), c = async (t, e, o) => {
|
|
23
|
+
if (!m("allowlistEnabled"))
|
|
24
|
+
return !1;
|
|
25
|
+
const i = await k(t, e);
|
|
26
|
+
if (!i.length)
|
|
27
|
+
return !1;
|
|
28
|
+
if (!o) {
|
|
29
|
+
const s = await C(i);
|
|
30
|
+
s != null && s.message && x({
|
|
31
|
+
type: I.Warning,
|
|
32
|
+
message: s.message,
|
|
33
|
+
actionButton: s.actionButton
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return !0;
|
|
37
|
+
}, L = async (t) => !y(t.compiledHtml) || !w(t.compiledHtml) || !E(t.compiledHtml) ? !1 : m("liquidSyntax") ? S(t.compiledHtml) : v(t.compiledHtml, t.dynamicContentList, !0), u = async (t, e) => {
|
|
38
|
+
if (!await H())
|
|
29
39
|
return;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
const { unsubscribePayload: i, stripoModules: s } = b(t.rawHtml);
|
|
41
|
+
if (await g(i), t.modules = s, !e.suppress) {
|
|
42
|
+
const W = e.blankCompiled ? { compiledHtml: "", ampHtml: "" } : {};
|
|
43
|
+
p({ ...t, ...W, silent: e.silent });
|
|
44
|
+
}
|
|
45
|
+
return t;
|
|
46
|
+
}, U = async (t, e) => {
|
|
47
|
+
var i;
|
|
48
|
+
r();
|
|
49
|
+
const o = await d();
|
|
50
|
+
if (n(o.rawHtml, e)) {
|
|
51
|
+
if (h.value && !e) {
|
|
52
|
+
const s = await B(
|
|
53
|
+
o.compiledHtml,
|
|
54
|
+
o.dynamicContentList
|
|
55
|
+
);
|
|
56
|
+
if (s.length) {
|
|
57
|
+
T.open(s, "save");
|
|
58
|
+
return;
|
|
40
59
|
}
|
|
60
|
+
} else if (!await L(o))
|
|
41
61
|
return;
|
|
42
|
-
|
|
62
|
+
if (!((i = a.value) != null && i.externalValidation && !await a.value.externalValidation(o)) && !await c(o.compiledHtml, o.css, e))
|
|
63
|
+
return u(o, { silent: e, suppress: t });
|
|
43
64
|
}
|
|
44
|
-
|
|
65
|
+
}, A = async () => {
|
|
66
|
+
var e;
|
|
67
|
+
if (!f.allowSaveWithErrors)
|
|
68
|
+
return;
|
|
69
|
+
r();
|
|
70
|
+
const t = await d();
|
|
71
|
+
if (n(t.rawHtml, !1) && !((e = a.value) != null && e.externalValidation && !await a.value.externalValidation(t)) && !await c(t.compiledHtml, t.css, !1))
|
|
72
|
+
return u(t, { silent: !1, blankCompiled: !0 });
|
|
73
|
+
}, M = async () => {
|
|
74
|
+
if (!f.allowSaveWithErrors)
|
|
45
75
|
return;
|
|
46
|
-
|
|
47
|
-
|
|
76
|
+
r();
|
|
77
|
+
const { html: t, css: e } = await V();
|
|
78
|
+
if (!n(t, !0) || await c(t, e, !0))
|
|
79
|
+
return;
|
|
80
|
+
const o = Y(t), i = {
|
|
81
|
+
dynamicContentList: P.getSelectedDynamicContentList,
|
|
82
|
+
compiledHtml: "",
|
|
83
|
+
rawHtml: t,
|
|
84
|
+
css: e,
|
|
85
|
+
ampHtml: "",
|
|
86
|
+
ampErrors: [],
|
|
87
|
+
modules: [],
|
|
88
|
+
recommendation: { campaignUrls: {}, configs: {} },
|
|
89
|
+
unsubscribe: {
|
|
90
|
+
status: o.length > 0,
|
|
91
|
+
config: o
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
return u(i, { silent: !0 });
|
|
48
95
|
};
|
|
49
|
-
return { save: (
|
|
50
|
-
() =>
|
|
51
|
-
) };
|
|
96
|
+
return { save: (t = !1, e = !1) => l(
|
|
97
|
+
() => U(t, e)
|
|
98
|
+
), saveWithErrors: () => l(A), saveRawOnly: () => l(M) };
|
|
52
99
|
};
|
|
53
100
|
export {
|
|
54
|
-
|
|
101
|
+
Bt as useSave
|
|
55
102
|
};
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { getIgnoredRecommendationBlockIds as B } from "../config/compiler/utils/recommendationIgnoreUtils.js";
|
|
2
|
+
import { CanNotMakeAnyChangesForRunningKey as k, DISPLAY_CONDITIONS_REGEX as Y, DISPLAY_CONDITIONS_EXCEPTIONS_REGEX as x } from "../enums/html-validator.js";
|
|
3
|
+
import { SaveValidationRule as o } from "../enums/save-validation.js";
|
|
4
|
+
import { ToasterTypeOptions as r } from "../enums/toaster.js";
|
|
5
|
+
import { BLOCK_ROOT_SELECTOR as M } from "../extensions/Blocks/Checkbox/constants/selectors.js";
|
|
6
|
+
import { itemsBlockDynamicVariables as U } from "../extensions/Blocks/Items/enums/productEnums.js";
|
|
7
|
+
import { BLOCK_ROOT_SELECTOR as P } from "../extensions/Blocks/RadioButton/constants/selectors.js";
|
|
8
|
+
import { useRecommendationExtensionStore as W } from "../extensions/Blocks/Recommendation/store/recommendation.js";
|
|
9
|
+
import { getInvalidFields as G, STRATEGY_FIELD_KEY as q, RecommendationAssignStrategyKey as H, RecommendationRequiredFieldsKey as V } from "../extensions/Blocks/Recommendation/validation/requiredFields.js";
|
|
10
|
+
import { useConfigStore as X } from "../stores/config.js";
|
|
11
|
+
import { useDynamicContentStore as j } from "../stores/dynamic-content.js";
|
|
12
|
+
import { useRecommendationStore as z } from "../stores/recommendation.js";
|
|
13
|
+
import { base64EncodeWithSpecialChars as T } from "../utils/base64.js";
|
|
14
|
+
import { escapeHtml as $ } from "../utils/htmlEscape.js";
|
|
15
|
+
import { useTemplatePreparation as Q } from "../utils/templatePreparation.js";
|
|
16
|
+
import { useActionsApi as Z } from "./useActionsApi.js";
|
|
17
|
+
import { useConfig as J } from "./useConfig.js";
|
|
18
|
+
import { useHtmlCompiler as ee } from "./useHtmlCompiler.js";
|
|
19
|
+
import { buildPartnerAttributeNameSet as te, getInvalidDynamicContentTags as ne, getRecommendationBlockIds as ie, findInvalidCustomFields as oe } from "./useHtmlValidator.js";
|
|
20
|
+
import { useHttp as re } from "./useHttp.js";
|
|
21
|
+
import { useTranslations as se } from "./useTranslations.js";
|
|
22
|
+
import { findInvalidCouponBlocks as ae, COUPON_TAG_MODIFIED_KEY as ce } from "./validators/useCouponBlockValidator.js";
|
|
23
|
+
import { findDuplicateUnsubPageTypes as le, countUnsubBlocksWithoutPage as me, EMPTY_BLOCK_KEY as ue } from "./validators/useUnsubscribeBlockValidator.js";
|
|
24
|
+
const de = "newsletter.html-content-is-empty", pe = "custom-fields.missing-closing-braces", ge = "custom-fields.missing-opening-braces", I = "custom-fields.invalid-custom-fields", fe = "newsletter.display-conditions-invalid-syntax", ye = "custom-conditions.wrong-equality-operators", Ee = "custom-conditions.missing-if-endif-tag", Ie = "custom-conditions.no-space-after-braces", Se = "custom-conditions.no-braces-inside-if-tag", _e = "newsletter.fill-all-necessary-fields", Ce = "newsletter.invalid-image-type", he = "unsubscribe-templates.select-checkbox-groups", ke = "unsubscribe-templates.select-radio-button-groups", Te = "newsletter.invalid-url-link-for-toaster", F = "journey-builder.liquid-validation-failed", Fe = "/newsletter/template-library/check-template-html-body", Re = /src="[^"]*\.(svg|pst)"/gm, O = /({%(.*?)%})/g, Oe = ["if", "endif", "else", "elif", "now"], Le = (t) => !Oe.includes(t.toLowerCase()), Ne = (t) => ["if", "endif"].includes(t.toLowerCase());
|
|
25
|
+
function be(t) {
|
|
26
|
+
return ae(t).map((i) => ({
|
|
27
|
+
rule: o.CouponTagModified,
|
|
28
|
+
type: r.Warning,
|
|
29
|
+
key: ce,
|
|
30
|
+
detail: i
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
function Ae(t) {
|
|
34
|
+
return le(t).map((i) => ({
|
|
35
|
+
rule: o.DuplicateUnsubBlock,
|
|
36
|
+
type: r.Error,
|
|
37
|
+
key: i.messageKey
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
function ve(t) {
|
|
41
|
+
return Array.from({ length: me(t) }, () => ({
|
|
42
|
+
rule: o.UnsubBlockMissingTemplate,
|
|
43
|
+
type: r.Warning,
|
|
44
|
+
key: ue
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
function De(t, i) {
|
|
48
|
+
const n = t.match(O);
|
|
49
|
+
if (!n)
|
|
50
|
+
return [];
|
|
51
|
+
const l = [];
|
|
52
|
+
return n.forEach((u) => {
|
|
53
|
+
const c = u.slice(2, -2).trim().match(/(".*?"|[^"\s]+)(?=\s*|\s*$)/g);
|
|
54
|
+
if (c && c.length > 0) {
|
|
55
|
+
const [d] = c;
|
|
56
|
+
Le(d) && !i.includes(d) && l.push({
|
|
57
|
+
rule: o.CustomFieldScript,
|
|
58
|
+
type: r.Warning,
|
|
59
|
+
key: I,
|
|
60
|
+
detail: d
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}), l;
|
|
64
|
+
}
|
|
65
|
+
function Ke(t) {
|
|
66
|
+
return t.length > 0 ? [] : [{ rule: o.EmptyHtml, type: r.Warning, key: de }];
|
|
67
|
+
}
|
|
68
|
+
function we(t) {
|
|
69
|
+
const i = (t.match(/{/gm) || []).length, n = (t.match(/}/gm) || []).length;
|
|
70
|
+
return i > n ? [{
|
|
71
|
+
rule: o.MismatchedBraces,
|
|
72
|
+
type: r.Warning,
|
|
73
|
+
key: pe
|
|
74
|
+
}] : i < n ? [{
|
|
75
|
+
rule: o.MismatchedBraces,
|
|
76
|
+
type: r.Warning,
|
|
77
|
+
key: ge
|
|
78
|
+
}] : [];
|
|
79
|
+
}
|
|
80
|
+
function Be(t) {
|
|
81
|
+
return ne(t).map((i) => ({
|
|
82
|
+
rule: o.InvalidDynamicTag,
|
|
83
|
+
type: r.Warning,
|
|
84
|
+
key: I,
|
|
85
|
+
detail: i
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
function Ye(t, i, n, l) {
|
|
89
|
+
const u = ie(t);
|
|
90
|
+
return oe(t, i, u, n, l).map((c) => ({
|
|
91
|
+
rule: o.InvalidCustomField,
|
|
92
|
+
type: r.Error,
|
|
93
|
+
key: I,
|
|
94
|
+
detail: c
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
function xe(t) {
|
|
98
|
+
const i = t.match(/{%(.*?)%}/g), n = [], l = [];
|
|
99
|
+
if (i && i.forEach((u) => {
|
|
100
|
+
const c = u.match(Y), d = u.match(x), E = (c == null ? void 0 : c.join("")) || "";
|
|
101
|
+
(!c || u !== E) && !d && l.push({
|
|
102
|
+
rule: o.ConditionsSyntax,
|
|
103
|
+
type: r.Error,
|
|
104
|
+
key: fe,
|
|
105
|
+
detail: u
|
|
106
|
+
}), c && c.forEach((g) => {
|
|
107
|
+
g.trim() === "=" && l.push({
|
|
108
|
+
rule: o.WrongEqualityOperator,
|
|
109
|
+
type: r.Error,
|
|
110
|
+
key: ye
|
|
111
|
+
}), (g.match(/^[a-zA-Z]*$/g) ?? []).forEach((f) => {
|
|
112
|
+
Ne(f) && n.push(f);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}), n.length) {
|
|
116
|
+
const u = n.filter((d) => d === "if").length, c = n.filter((d) => d === "endif").length;
|
|
117
|
+
u !== c && l.push({
|
|
118
|
+
rule: o.MissingIfEndif,
|
|
119
|
+
type: r.Error,
|
|
120
|
+
key: Ee
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return l;
|
|
124
|
+
}
|
|
125
|
+
function Me(t) {
|
|
126
|
+
const i = (t.match(/{% /gm) || []).length, n = (t.match(/ %}/gm) || []).length;
|
|
127
|
+
return i === n ? [] : [{
|
|
128
|
+
rule: o.ConditionTagSpacing,
|
|
129
|
+
type: r.Warning,
|
|
130
|
+
key: Ie
|
|
131
|
+
}];
|
|
132
|
+
}
|
|
133
|
+
function Ue(t) {
|
|
134
|
+
return (t.match(O) || []).filter((n) => n.includes("if")).map((n) => (n.match(/{{.*}}/gm) || []).length).reduce((n, l) => n + l, 0) === 0 ? [] : [{
|
|
135
|
+
rule: o.BracesInsideIfTag,
|
|
136
|
+
type: r.Warning,
|
|
137
|
+
key: Se
|
|
138
|
+
}];
|
|
139
|
+
}
|
|
140
|
+
function Pe(t) {
|
|
141
|
+
return (t.match(Re) ?? []).map((i) => ({
|
|
142
|
+
rule: o.InvalidImageType,
|
|
143
|
+
type: r.Error,
|
|
144
|
+
key: Ce,
|
|
145
|
+
detail: i
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
function R(t, i, n, l) {
|
|
149
|
+
const u = new DOMParser().parseFromString(t, "text/html");
|
|
150
|
+
return Array.from(u.querySelectorAll(i)).filter((c) => {
|
|
151
|
+
var d;
|
|
152
|
+
return !((d = c.id) != null && d.trim());
|
|
153
|
+
}).map(() => ({ rule: n, type: r.Error, key: l }));
|
|
154
|
+
}
|
|
155
|
+
const dt = () => {
|
|
156
|
+
const { config: t, isFeatureEnabled: i } = J(), n = se(), { post: l } = re(), { getCompiledEmail: u, getTemplateData: c } = Z(), { compileHtml: d } = ee(), { hydrateRecommendationStore: E } = Q(), g = j(), f = z(), y = W(), L = X(), N = (e) => ({
|
|
157
|
+
rule: e.rule,
|
|
158
|
+
type: e.type,
|
|
159
|
+
detail: e.detail,
|
|
160
|
+
message: "key" in e ? n(e.key) : $(e.message)
|
|
161
|
+
}), b = async (e) => {
|
|
162
|
+
if (e.size === 0)
|
|
163
|
+
return /* @__PURE__ */ new Set();
|
|
164
|
+
try {
|
|
165
|
+
await y.fetchRecommendationFilters();
|
|
166
|
+
} catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
return te(y.filterList);
|
|
170
|
+
}, A = async (e) => {
|
|
171
|
+
try {
|
|
172
|
+
const s = await l(Fe, { html: T(e) }), { status: a, message: m } = s.data, p = [];
|
|
173
|
+
return a || p.push({
|
|
174
|
+
rule: o.InvalidLinks,
|
|
175
|
+
type: r.Error,
|
|
176
|
+
// status===undefined means a bare message instead of a boolean; surface it,
|
|
177
|
+
// but fall back to the canned key when it's missing so a malformed body
|
|
178
|
+
// never renders a literal 'undefined' row (SD-147430).
|
|
179
|
+
...a === void 0 && m ? { message: m } : { key: Te }
|
|
180
|
+
}), m === n(k) && p.push({
|
|
181
|
+
rule: o.LinkCheckInProgress,
|
|
182
|
+
type: r.Error,
|
|
183
|
+
key: k
|
|
184
|
+
}), p;
|
|
185
|
+
} catch (s) {
|
|
186
|
+
return console.warn("Link-check collector failed; skipping the link rule.", s), [];
|
|
187
|
+
}
|
|
188
|
+
}, v = async (e) => {
|
|
189
|
+
try {
|
|
190
|
+
const s = await l("/newsletter/contents/validate-syntax", [{
|
|
191
|
+
identifier: "default",
|
|
192
|
+
syntax: "liquid",
|
|
193
|
+
contents: {
|
|
194
|
+
subject: null,
|
|
195
|
+
preheader: null,
|
|
196
|
+
html: T(e),
|
|
197
|
+
ampHtml: null
|
|
198
|
+
}
|
|
199
|
+
}]);
|
|
200
|
+
if (!Array.isArray(s.data)) {
|
|
201
|
+
const m = s.data;
|
|
202
|
+
return [{
|
|
203
|
+
rule: o.LiquidSyntax,
|
|
204
|
+
type: r.Warning,
|
|
205
|
+
message: m.message ?? n(F)
|
|
206
|
+
}];
|
|
207
|
+
}
|
|
208
|
+
const [a] = s.data;
|
|
209
|
+
return Object.values((a == null ? void 0 : a.errors) ?? {}).map((m) => ({
|
|
210
|
+
rule: o.LiquidSyntax,
|
|
211
|
+
type: r.Warning,
|
|
212
|
+
message: m.replace(/^line \d+:\s*/, "")
|
|
213
|
+
}));
|
|
214
|
+
} catch (s) {
|
|
215
|
+
if (s != null && s.authReported)
|
|
216
|
+
throw s;
|
|
217
|
+
return [{
|
|
218
|
+
rule: o.LiquidSyntax,
|
|
219
|
+
type: r.Error,
|
|
220
|
+
message: n(F)
|
|
221
|
+
}];
|
|
222
|
+
}
|
|
223
|
+
}, D = () => {
|
|
224
|
+
const e = Object.values(y.blockStates).map((a) => G(a.recommendationConfigs, y)).filter((a) => a.length > 0).map((a) => {
|
|
225
|
+
const m = a.every((p) => p === q);
|
|
226
|
+
return {
|
|
227
|
+
rule: o.RecommendationRequiredFields,
|
|
228
|
+
type: m ? r.Warning : r.Error,
|
|
229
|
+
key: m ? H : V
|
|
230
|
+
};
|
|
231
|
+
}), s = (f.recommendationConfigs ?? []).filter((a) => a.filters.some((m) => m.value === "")).map(() => ({
|
|
232
|
+
rule: o.RecommendationFilters,
|
|
233
|
+
type: r.Error,
|
|
234
|
+
key: _e
|
|
235
|
+
}));
|
|
236
|
+
return [...e, ...s];
|
|
237
|
+
}, K = async (e, s) => {
|
|
238
|
+
var C, h;
|
|
239
|
+
const a = [
|
|
240
|
+
...s.map((w) => w.value),
|
|
241
|
+
...U,
|
|
242
|
+
...((h = (C = t.value) == null ? void 0 : C.template) == null ? void 0 : h.customFieldAttributes) ?? []
|
|
243
|
+
], m = B(e), p = await b(m), { isTransactional: _ } = L;
|
|
244
|
+
return [
|
|
245
|
+
...await A(e),
|
|
246
|
+
..._ ? [] : De(e, a),
|
|
247
|
+
...Ke(e),
|
|
248
|
+
...we(e),
|
|
249
|
+
...Be(e),
|
|
250
|
+
..._ ? [] : Ye(e, a, m, p),
|
|
251
|
+
...xe(e),
|
|
252
|
+
...Me(e),
|
|
253
|
+
...Ue(e),
|
|
254
|
+
...D(),
|
|
255
|
+
...Pe(e),
|
|
256
|
+
...R(
|
|
257
|
+
e,
|
|
258
|
+
M,
|
|
259
|
+
o.CheckboxUnsubGroup,
|
|
260
|
+
he
|
|
261
|
+
),
|
|
262
|
+
...R(
|
|
263
|
+
e,
|
|
264
|
+
P,
|
|
265
|
+
o.RadioButtonUnsubGroup,
|
|
266
|
+
ke
|
|
267
|
+
)
|
|
268
|
+
];
|
|
269
|
+
}, S = async (e, s) => [
|
|
270
|
+
...be(e),
|
|
271
|
+
...Ae(e),
|
|
272
|
+
...ve(e),
|
|
273
|
+
...i("liquidSyntax") ? await v(e) : await K(e, s)
|
|
274
|
+
].map(N);
|
|
275
|
+
return { collectSaveValidationErrors: S, validateCurrentTemplate: async () => {
|
|
276
|
+
const { html: e } = await c();
|
|
277
|
+
await E(e);
|
|
278
|
+
const { html: s } = await u({ minimize: !0, resetDataSavedFlag: !1 }), { compiledHtml: a } = d(s);
|
|
279
|
+
return S(a, g.getSelectedDynamicContentList);
|
|
280
|
+
} };
|
|
281
|
+
};
|
|
282
|
+
export {
|
|
283
|
+
dt as useSaveValidation
|
|
284
|
+
};
|