@capillarytech/creatives-library 8.0.255-alpha.4 → 8.0.255
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/assets/Android.png +0 -0
- package/assets/iOS.png +0 -0
- package/constants/unified.js +2 -2
- package/initialReducer.js +2 -0
- package/package.json +1 -1
- package/services/api.js +10 -5
- package/services/tests/api.test.js +34 -0
- package/translations/en.json +3 -4
- package/utils/common.js +5 -6
- package/utils/commonUtils.js +28 -5
- package/utils/tests/commonUtil.test.js +224 -0
- package/utils/tests/transformerUtils.test.js +0 -297
- package/utils/transformTemplateConfig.js +0 -10
- package/utils/transformerUtils.js +0 -40
- package/v2Components/CapDeviceContent/index.js +61 -56
- package/v2Components/CapImageUpload/constants.js +0 -2
- package/v2Components/CapImageUpload/index.js +16 -65
- package/v2Components/CapImageUpload/index.scss +1 -4
- package/v2Components/CapImageUpload/messages.js +1 -5
- package/v2Components/CapTagList/index.js +6 -1
- package/v2Components/CapTagListWithInput/index.js +5 -1
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
- package/v2Components/ErrorInfoNote/constants.js +1 -0
- package/v2Components/ErrorInfoNote/index.js +457 -72
- package/v2Components/ErrorInfoNote/messages.js +36 -6
- package/v2Components/ErrorInfoNote/style.scss +282 -6
- package/v2Components/FormBuilder/tests/index.test.js +13 -4
- package/v2Components/HtmlEditor/HTMLEditor.js +547 -94
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +874 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1441 -133
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
- package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
- package/v2Components/HtmlEditor/_index.lazy.scss +0 -1
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +23 -102
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -140
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +4 -4
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +3 -6
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +22 -43
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +1 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +49 -31
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +50 -34
- package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +6 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +70 -41
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +255 -0
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +364 -0
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
- package/v2Components/HtmlEditor/constants.js +42 -20
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +103 -0
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
- package/v2Components/HtmlEditor/hooks/useValidation.js +189 -53
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +92 -94
- package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +94 -45
- package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +134 -0
- package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
- package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +134 -102
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
- package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
- package/v2Components/HtmlEditor/utils/validationConstants.js +40 -0
- package/v2Components/MobilePushPreviewV2/index.js +32 -7
- package/v2Components/TemplatePreview/_templatePreview.scss +55 -24
- package/v2Components/TemplatePreview/index.js +47 -32
- package/v2Components/TemplatePreview/messages.js +4 -0
- package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
- package/v2Containers/App/constants.js +0 -5
- package/v2Containers/BeeEditor/index.js +172 -90
- package/v2Containers/BeePopupEditor/_beePopupEditor.scss +14 -0
- package/v2Containers/BeePopupEditor/constants.js +10 -0
- package/v2Containers/BeePopupEditor/index.js +194 -0
- package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
- package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +3 -4
- package/v2Containers/CreativesContainer/SlideBoxContent.js +129 -107
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +163 -13
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -2
- package/v2Containers/CreativesContainer/constants.js +1 -3
- package/v2Containers/CreativesContainer/index.js +239 -214
- package/v2Containers/CreativesContainer/messages.js +8 -4
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +0 -210
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -354
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +106 -0
- package/v2Containers/Email/actions.js +7 -0
- package/v2Containers/Email/constants.js +5 -1
- package/v2Containers/Email/index.js +234 -29
- package/v2Containers/Email/messages.js +32 -0
- package/v2Containers/Email/reducer.js +12 -1
- package/v2Containers/Email/sagas.js +61 -7
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
- package/v2Containers/Email/tests/reducer.test.js +46 -0
- package/v2Containers/Email/tests/sagas.test.js +320 -29
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1285 -0
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +211 -21
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +1880 -0
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
- package/v2Containers/EmailWrapper/constants.js +2 -0
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +629 -77
- package/v2Containers/EmailWrapper/index.js +103 -23
- package/v2Containers/EmailWrapper/messages.js +65 -1
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +643 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +594 -77
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
- package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
- package/v2Containers/InApp/actions.js +7 -0
- package/v2Containers/InApp/constants.js +20 -4
- package/v2Containers/InApp/index.js +802 -359
- package/v2Containers/InApp/index.scss +4 -3
- package/v2Containers/InApp/messages.js +7 -3
- package/v2Containers/InApp/reducer.js +21 -3
- package/v2Containers/InApp/sagas.js +29 -9
- package/v2Containers/InApp/selectors.js +25 -5
- package/v2Containers/InApp/tests/index.test.js +154 -50
- package/v2Containers/InApp/tests/reducer.test.js +34 -0
- package/v2Containers/InApp/tests/sagas.test.js +61 -9
- package/v2Containers/InApp/tests/selectors.test.js +612 -0
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +151 -0
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +23 -0
- package/v2Containers/InAppWrapper/constants.js +16 -0
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
- package/v2Containers/InAppWrapper/index.js +148 -0
- package/v2Containers/InAppWrapper/messages.js +49 -0
- package/v2Containers/InappAdvance/index.js +1099 -0
- package/v2Containers/InappAdvance/index.scss +10 -0
- package/v2Containers/InappAdvance/tests/index.test.js +448 -0
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +15 -36
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +8 -8
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +77 -100
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +63 -72
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +190 -250
- package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +12 -16
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +40 -48
- package/v2Containers/TagList/index.js +62 -19
- package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
- package/v2Containers/Templates/_templates.scss +56 -202
- package/v2Containers/Templates/actions.js +1 -2
- package/v2Containers/Templates/constants.js +0 -1
- package/v2Containers/Templates/index.js +123 -278
- package/v2Containers/Templates/messages.js +4 -24
- package/v2Containers/Templates/reducer.js +0 -2
- package/v2Containers/Templates/tests/index.test.js +0 -10
- package/v2Containers/TemplatesV2/index.js +7 -15
- package/v2Containers/TemplatesV2/messages.js +0 -4
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +768 -1272
- package/utils/imageUrlUpload.js +0 -141
- package/v2Components/CapImageUrlUpload/constants.js +0 -26
- package/v2Components/CapImageUrlUpload/index.js +0 -365
- package/v2Components/CapImageUrlUpload/index.scss +0 -35
- package/v2Components/CapImageUrlUpload/messages.js +0 -47
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
- package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
- package/v2Containers/WebPush/Create/components/BrandIconSection.js +0 -108
- package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -172
- package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
- package/v2Containers/WebPush/Create/components/ButtonList.js +0 -145
- package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +0 -164
- package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +0 -463
- package/v2Containers/WebPush/Create/components/FormActions.js +0 -54
- package/v2Containers/WebPush/Create/components/FormActions.test.js +0 -163
- package/v2Containers/WebPush/Create/components/MediaSection.js +0 -142
- package/v2Containers/WebPush/Create/components/MediaSection.test.js +0 -341
- package/v2Containers/WebPush/Create/components/MessageSection.js +0 -103
- package/v2Containers/WebPush/Create/components/MessageSection.test.js +0 -268
- package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +0 -87
- package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +0 -210
- package/v2Containers/WebPush/Create/components/TemplateNameSection.js +0 -54
- package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +0 -143
- package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +0 -86
- package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +0 -16
- package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +0 -41
- package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +0 -54
- package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +0 -37
- package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +0 -21
- package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
- package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
- package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
- package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -78
- package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +0 -138
- package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +0 -406
- package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +0 -30
- package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +0 -151
- package/v2Containers/WebPush/Create/hooks/useImageUpload.js +0 -104
- package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +0 -538
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -122
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -633
- package/v2Containers/WebPush/Create/index.js +0 -1148
- package/v2Containers/WebPush/Create/index.scss +0 -134
- package/v2Containers/WebPush/Create/messages.js +0 -203
- package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -228
- package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -294
- package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -90
- package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -305
- package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -23
- package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -155
- package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
- package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
- package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
- package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
- package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
- package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
- package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -47
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -141
- package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
- package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -68
- package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -61
- package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -99
- package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -733
- package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +0 -571
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -81
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +0 -81
- package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -50
- package/v2Containers/WebPush/Create/preview/constants.js +0 -637
- package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -79
- package/v2Containers/WebPush/Create/preview/preview.scss +0 -351
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -370
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -47
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -207
- package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -153
- package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
- package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -101
- package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -229
- package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
- package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1081
- package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
- package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -1327
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -131
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -112
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -129
- package/v2Containers/WebPush/Create/utils/payloadBuilder.js +0 -96
- package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +0 -396
- package/v2Containers/WebPush/Create/utils/previewUtils.js +0 -89
- package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -115
- package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
- package/v2Containers/WebPush/Create/utils/validation.js +0 -75
- package/v2Containers/WebPush/Create/utils/validation.test.js +0 -283
- package/v2Containers/WebPush/actions.js +0 -60
- package/v2Containers/WebPush/constants.js +0 -132
- package/v2Containers/WebPush/index.js +0 -2
- package/v2Containers/WebPush/reducer.js +0 -104
- package/v2Containers/WebPush/sagas.js +0 -119
- package/v2Containers/WebPush/selectors.js +0 -65
- package/v2Containers/WebPush/tests/reducer.test.js +0 -863
- package/v2Containers/WebPush/tests/sagas.test.js +0 -566
- package/v2Containers/WebPush/tests/selectors.test.js +0 -960
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Validation Hook for HTML Editor
|
|
3
|
-
* Manages real-time validation and error display
|
|
3
|
+
* Manages real-time validation and error display.
|
|
4
|
+
* UI gating: only Rule Group #1 (Input & Sanitization) blocks Save/Update/Preview/Test.
|
|
5
|
+
* All other rules are warnings for backward compatibility with CKEditor legacy templates.
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
useState, useEffect, useCallback, useRef,
|
|
10
|
+
} from 'react';
|
|
7
11
|
import { validateHTML, extractAndValidateCSS } from '../utils/htmlValidator';
|
|
8
12
|
import { sanitizeHTML, isContentSafe, findUnsafeContent } from '../utils/contentSanitizer';
|
|
13
|
+
import { BLOCKING_ERROR_RULE_IDS } from '../constants';
|
|
9
14
|
|
|
10
15
|
/**
|
|
11
16
|
* Custom hook for managing HTML/CSS validation
|
|
@@ -16,12 +21,46 @@ import { sanitizeHTML, isContentSafe, findUnsafeContent } from '../utils/content
|
|
|
16
21
|
* @param {Function} formatValidatorMessage - Message formatter function for validator internationalization
|
|
17
22
|
* @returns {Object} Validation state and methods
|
|
18
23
|
*/
|
|
24
|
+
/**
|
|
25
|
+
* Get line number for a character position in text
|
|
26
|
+
*/
|
|
27
|
+
const getLineNumberFromPosition = (text, position) => {
|
|
28
|
+
if (position === undefined || position < 0) return 1;
|
|
29
|
+
return text.substring(0, position).split('\n').length;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Find line number for a tag or pattern in content
|
|
34
|
+
* Used for API errors to locate where the error occurs
|
|
35
|
+
*/
|
|
36
|
+
const findLineNumberForTag = (content, tagName) => {
|
|
37
|
+
if (!content || !tagName) return null;
|
|
38
|
+
|
|
39
|
+
// Try to find the tag in the content
|
|
40
|
+
// Look for patterns like {{ tagName }}, {{tagName}}, etc.
|
|
41
|
+
const escapedTagName = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
42
|
+
const patterns = [
|
|
43
|
+
new RegExp(`\\{\\{\\s*${escapedTagName}\\s*\\}\\}`, 'g'),
|
|
44
|
+
new RegExp(`\\{%[^%]*${escapedTagName}[^%]*%\\}`, 'g'),
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
for (const pattern of patterns) {
|
|
48
|
+
const match = pattern.exec(content);
|
|
49
|
+
if (match) {
|
|
50
|
+
return getLineNumberFromPosition(content, match.index);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
};
|
|
56
|
+
|
|
19
57
|
export const useValidation = (content, variant = 'email', options = {}, formatSanitizerMessage = null, formatValidatorMessage = null) => {
|
|
20
58
|
const {
|
|
21
59
|
enableRealTime = true,
|
|
22
60
|
debounceMs = 500,
|
|
23
61
|
enableSanitization = true,
|
|
24
|
-
securityLevel = 'standard'
|
|
62
|
+
securityLevel = 'standard',
|
|
63
|
+
apiValidationErrors = null, // API validation errors from validateLiquidTemplateContent
|
|
25
64
|
} = options;
|
|
26
65
|
|
|
27
66
|
// Validation state
|
|
@@ -42,8 +81,8 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
42
81
|
totalErrors: 0,
|
|
43
82
|
totalWarnings: 0,
|
|
44
83
|
totalInfo: 0,
|
|
45
|
-
hasSecurityIssues: false
|
|
46
|
-
}
|
|
84
|
+
hasSecurityIssues: false,
|
|
85
|
+
},
|
|
47
86
|
});
|
|
48
87
|
|
|
49
88
|
// Refs for debouncing
|
|
@@ -55,7 +94,7 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
55
94
|
*/
|
|
56
95
|
const performValidation = useCallback(async (htmlContent) => {
|
|
57
96
|
if (!htmlContent) {
|
|
58
|
-
setValidationState(prev => ({
|
|
97
|
+
setValidationState((prev) => ({
|
|
59
98
|
...prev,
|
|
60
99
|
isValidating: false,
|
|
61
100
|
htmlErrors: [],
|
|
@@ -72,13 +111,13 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
72
111
|
totalErrors: 0,
|
|
73
112
|
totalWarnings: 0,
|
|
74
113
|
totalInfo: 0,
|
|
75
|
-
hasSecurityIssues: false
|
|
76
|
-
}
|
|
114
|
+
hasSecurityIssues: false,
|
|
115
|
+
},
|
|
77
116
|
}));
|
|
78
117
|
return;
|
|
79
118
|
}
|
|
80
119
|
|
|
81
|
-
setValidationState(prev => ({ ...prev, isValidating: true }));
|
|
120
|
+
setValidationState((prev) => ({ ...prev, isValidating: true }));
|
|
82
121
|
|
|
83
122
|
try {
|
|
84
123
|
// 1. HTML Validation
|
|
@@ -91,15 +130,21 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
91
130
|
const isSecure = isContentSafe(htmlContent);
|
|
92
131
|
const securityIssues = isSecure ? [] : findUnsafeContent(htmlContent);
|
|
93
132
|
|
|
94
|
-
// 4. Sanitization (if enabled)
|
|
133
|
+
// 4. Sanitization (if enabled) – Rule Group #1 issues come from here
|
|
95
134
|
let sanitizationResult = null;
|
|
96
135
|
if (enableSanitization) {
|
|
97
136
|
sanitizationResult = sanitizeHTML(htmlContent, variant, securityLevel, formatSanitizerMessage);
|
|
98
137
|
}
|
|
99
138
|
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
const
|
|
139
|
+
const sanitizationWarnings = sanitizationResult?.warnings || [];
|
|
140
|
+
const blockingSanitizerCount = sanitizationWarnings.filter((w) => BLOCKING_ERROR_RULE_IDS.includes(w.rule)).length;
|
|
141
|
+
const protocolSecurityCount = (securityIssues || []).filter((s) => ['JavaScript Protocol', 'Data URL', 'VBScript Protocol'].includes(s?.type)).length;
|
|
142
|
+
|
|
143
|
+
// Summary: totalErrors/totalWarnings are for display; blocking count is for gating
|
|
144
|
+
const totalErrors = htmlValidation.errors.length + cssValidation.errors.length + blockingSanitizerCount + protocolSecurityCount;
|
|
145
|
+
const totalWarnings = htmlValidation.warnings.length + cssValidation.warnings.length
|
|
146
|
+
+ (sanitizationWarnings.length - blockingSanitizerCount)
|
|
147
|
+
+ (securityIssues.length - protocolSecurityCount);
|
|
103
148
|
const totalInfo = htmlValidation.info.length + cssValidation.info.length;
|
|
104
149
|
const hasSecurityIssues = securityIssues.length > 0;
|
|
105
150
|
|
|
@@ -114,20 +159,19 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
114
159
|
cssWarnings: cssValidation.warnings,
|
|
115
160
|
cssInfo: cssValidation.info,
|
|
116
161
|
securityIssues,
|
|
117
|
-
sanitizationWarnings
|
|
162
|
+
sanitizationWarnings,
|
|
118
163
|
isValid: htmlValidation.isValid && cssValidation.isValid,
|
|
119
164
|
isSecure,
|
|
120
165
|
summary: {
|
|
121
166
|
totalErrors,
|
|
122
167
|
totalWarnings,
|
|
123
168
|
totalInfo,
|
|
124
|
-
hasSecurityIssues
|
|
125
|
-
}
|
|
169
|
+
hasSecurityIssues,
|
|
170
|
+
},
|
|
126
171
|
});
|
|
127
|
-
|
|
128
172
|
} catch (error) {
|
|
129
173
|
console.error('Validation error:', error);
|
|
130
|
-
setValidationState(prev => ({
|
|
174
|
+
setValidationState((prev) => ({
|
|
131
175
|
...prev,
|
|
132
176
|
isValidating: false,
|
|
133
177
|
htmlErrors: [{
|
|
@@ -137,13 +181,13 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
137
181
|
column: 1,
|
|
138
182
|
rule: 'validation-error',
|
|
139
183
|
severity: 'error',
|
|
140
|
-
source: 'validator'
|
|
184
|
+
source: 'validator',
|
|
141
185
|
}],
|
|
142
186
|
isValid: false,
|
|
143
187
|
summary: {
|
|
144
188
|
...prev.summary,
|
|
145
|
-
totalErrors: 1
|
|
146
|
-
}
|
|
189
|
+
totalErrors: 1,
|
|
190
|
+
},
|
|
147
191
|
}));
|
|
148
192
|
}
|
|
149
193
|
}, [variant, enableSanitization, securityLevel, formatSanitizerMessage, formatValidatorMessage]);
|
|
@@ -208,8 +252,8 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
208
252
|
totalErrors: 0,
|
|
209
253
|
totalWarnings: 0,
|
|
210
254
|
totalInfo: 0,
|
|
211
|
-
hasSecurityIssues: false
|
|
212
|
-
}
|
|
255
|
+
hasSecurityIssues: false,
|
|
256
|
+
},
|
|
213
257
|
});
|
|
214
258
|
}, []);
|
|
215
259
|
|
|
@@ -223,10 +267,10 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
223
267
|
...validationState.htmlInfo,
|
|
224
268
|
...validationState.cssErrors,
|
|
225
269
|
...validationState.cssWarnings,
|
|
226
|
-
...validationState.cssInfo
|
|
270
|
+
...validationState.cssInfo,
|
|
227
271
|
];
|
|
228
272
|
|
|
229
|
-
return allErrors.filter(error => error.severity === severity);
|
|
273
|
+
return allErrors.filter((error) => error.severity === severity);
|
|
230
274
|
}, [validationState]);
|
|
231
275
|
|
|
232
276
|
/**
|
|
@@ -239,32 +283,111 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
239
283
|
...validationState.htmlInfo,
|
|
240
284
|
...validationState.cssErrors,
|
|
241
285
|
...validationState.cssWarnings,
|
|
242
|
-
...validationState.cssInfo
|
|
286
|
+
...validationState.cssInfo,
|
|
243
287
|
];
|
|
244
288
|
|
|
245
|
-
return allErrors.filter(error => error.source === source);
|
|
289
|
+
return allErrors.filter((error) => error.source === source);
|
|
246
290
|
}, [validationState]);
|
|
247
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Extract line number from error message if present
|
|
294
|
+
* API errors might contain line numbers in messages like "Error at line 5" or "Line 10: error"
|
|
295
|
+
* Also tries to find line number by searching for the problematic tag in content
|
|
296
|
+
*/
|
|
297
|
+
const extractLineNumberFromMessage = useCallback((message) => {
|
|
298
|
+
if (!message || typeof message !== 'string') {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
// Try to match patterns like "line 5", "Line 10", "at line 15", "line: 20", etc.
|
|
302
|
+
const lineMatch = message.match(/\b(?:line|Line|LINE)\s*:?\s*(\d+)\b/i);
|
|
303
|
+
if (lineMatch && lineMatch[1]) {
|
|
304
|
+
return parseInt(lineMatch[1], 10);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Try to extract tag name from error message (e.g., "Unsupported tags: test" -> "test")
|
|
308
|
+
const tagMatch = message.match(/(?:tag|tags|Tag|Tags)[\s:]+([a-zA-Z_][a-zA-Z0-9_.]*)/i);
|
|
309
|
+
if (tagMatch && tagMatch[1] && content) {
|
|
310
|
+
const tagName = tagMatch[1];
|
|
311
|
+
const lineNumber = findLineNumberForTag(content, tagName);
|
|
312
|
+
if (lineNumber) {
|
|
313
|
+
return lineNumber;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return null;
|
|
318
|
+
}, [content]);
|
|
319
|
+
|
|
248
320
|
/**
|
|
249
321
|
* Get all errors and warnings combined
|
|
322
|
+
* Includes both client-side validation errors and API validation errors
|
|
250
323
|
*/
|
|
251
324
|
const getAllIssues = useCallback(() => {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
...validationState.cssErrors,
|
|
257
|
-
...validationState.cssWarnings,
|
|
258
|
-
...validationState.cssInfo,
|
|
259
|
-
...validationState.securityIssues.map(issue => ({
|
|
325
|
+
// API errors (liquid + standard) are blocking – they block Save/Update/Preview/Test
|
|
326
|
+
const apiLiquidErrors = (apiValidationErrors?.liquidErrors || []).map((errorMessage) => {
|
|
327
|
+
const extractedLine = extractLineNumberFromMessage(errorMessage);
|
|
328
|
+
return {
|
|
260
329
|
type: 'error',
|
|
330
|
+
message: errorMessage,
|
|
331
|
+
line: extractedLine,
|
|
332
|
+
column: null,
|
|
333
|
+
rule: 'liquid-api-validation',
|
|
334
|
+
severity: 'error',
|
|
335
|
+
source: 'liquid-validator',
|
|
336
|
+
};
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const apiStandardErrors = (apiValidationErrors?.standardErrors || []).map((errorMessage) => {
|
|
340
|
+
const extractedLine = extractLineNumberFromMessage(errorMessage);
|
|
341
|
+
return {
|
|
342
|
+
type: 'error',
|
|
343
|
+
message: errorMessage,
|
|
344
|
+
line: extractedLine,
|
|
345
|
+
column: null,
|
|
346
|
+
rule: 'standard-api-validation',
|
|
347
|
+
severity: 'error',
|
|
348
|
+
source: 'api-validator',
|
|
349
|
+
};
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// Security: protocol types are Rule Group #1 (blocking); others are warnings
|
|
353
|
+
const PROTOCOL_TYPES = ['JavaScript Protocol', 'Data URL', 'VBScript Protocol'];
|
|
354
|
+
const securityAsIssues = (validationState.securityIssues || []).map((issue) => {
|
|
355
|
+
const isBlocking = PROTOCOL_TYPES.includes(issue?.type);
|
|
356
|
+
return {
|
|
357
|
+
type: isBlocking ? 'error' : 'warning',
|
|
261
358
|
message: `Security issue: ${issue.type}`,
|
|
262
359
|
line: 1,
|
|
263
360
|
column: 1,
|
|
264
|
-
rule: 'security-violation',
|
|
265
|
-
severity: 'error',
|
|
266
|
-
source: 'security'
|
|
267
|
-
}
|
|
361
|
+
rule: isBlocking ? 'sanitizer.dangerousProtocolDetected' : 'security-violation',
|
|
362
|
+
severity: isBlocking ? 'error' : 'warning',
|
|
363
|
+
source: 'security',
|
|
364
|
+
};
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// Sanitization warnings (Rule Group #1 entries have rule set by contentSanitizer)
|
|
368
|
+
const sanitizationAsIssues = (validationState.sanitizationWarnings || []).map((w) => {
|
|
369
|
+
const sev = BLOCKING_ERROR_RULE_IDS.includes(w.rule) ? 'error' : 'warning';
|
|
370
|
+
return {
|
|
371
|
+
...w,
|
|
372
|
+
severity: sev,
|
|
373
|
+
rule: w.rule || 'sanitizer.unknown',
|
|
374
|
+
line: w.line ?? 1,
|
|
375
|
+
column: w.column ?? 1,
|
|
376
|
+
source: w.source || 'sanitizer',
|
|
377
|
+
};
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
const allIssues = [
|
|
381
|
+
...(validationState.htmlErrors || []),
|
|
382
|
+
...(validationState.htmlWarnings || []),
|
|
383
|
+
...(validationState.htmlInfo || []),
|
|
384
|
+
...(validationState.cssErrors || []),
|
|
385
|
+
...(validationState.cssWarnings || []),
|
|
386
|
+
...(validationState.cssInfo || []),
|
|
387
|
+
...securityAsIssues,
|
|
388
|
+
...sanitizationAsIssues,
|
|
389
|
+
...apiLiquidErrors,
|
|
390
|
+
...apiStandardErrors,
|
|
268
391
|
].sort((a, b) => {
|
|
269
392
|
// Sort by severity (error > warning > info) then by line number
|
|
270
393
|
const severityOrder = { error: 0, warning: 1, info: 2 };
|
|
@@ -273,16 +396,22 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
273
396
|
}
|
|
274
397
|
return (a.line || 0) - (b.line || 0);
|
|
275
398
|
});
|
|
276
|
-
|
|
399
|
+
|
|
400
|
+
// Ensure we always return an array
|
|
401
|
+
return Array.isArray(allIssues) ? allIssues : [];
|
|
402
|
+
}, [validationState, apiValidationErrors, extractLineNumberFromMessage]);
|
|
277
403
|
|
|
278
404
|
/**
|
|
279
405
|
* Check if validation is clean (no errors or warnings)
|
|
406
|
+
* Includes API validation errors in the check
|
|
280
407
|
*/
|
|
281
408
|
const isClean = useCallback(() => {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
409
|
+
const hasApiErrors = (apiValidationErrors?.liquidErrors?.length || 0) + (apiValidationErrors?.standardErrors?.length || 0) > 0;
|
|
410
|
+
return validationState.summary.totalErrors === 0
|
|
411
|
+
&& validationState.summary.totalWarnings === 0
|
|
412
|
+
&& !validationState.summary.hasSecurityIssues
|
|
413
|
+
&& !hasApiErrors;
|
|
414
|
+
}, [validationState.summary, apiValidationErrors]);
|
|
286
415
|
|
|
287
416
|
// Effect to validate content when it changes
|
|
288
417
|
useEffect(() => {
|
|
@@ -292,14 +421,19 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
292
421
|
}, [content, validateContent, enableRealTime]);
|
|
293
422
|
|
|
294
423
|
// Cleanup on unmount
|
|
295
|
-
useEffect(() => {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
};
|
|
424
|
+
useEffect(() => () => {
|
|
425
|
+
if (debounceRef.current) {
|
|
426
|
+
clearTimeout(debounceRef.current);
|
|
427
|
+
}
|
|
301
428
|
}, []);
|
|
302
429
|
|
|
430
|
+
const hasApiErrors = (apiValidationErrors?.liquidErrors?.length || 0) + (apiValidationErrors?.standardErrors?.length || 0) > 0;
|
|
431
|
+
|
|
432
|
+
const protocolTypes = ['JavaScript Protocol', 'Data URL', 'VBScript Protocol'];
|
|
433
|
+
// Client-side Liquid validation errors are blocking (genuine syntax errors)
|
|
434
|
+
const hasClientSideLiquidErrors = (validationState.htmlErrors || []).some((e) => e.source === 'liquid-validator' && e.severity === 'error');
|
|
435
|
+
const hasBlockingErrors = (validationState.sanitizationWarnings || []).some((w) => BLOCKING_ERROR_RULE_IDS.includes(w.rule)) || (validationState.securityIssues || []).some((s) => protocolTypes.includes(s?.type)) || hasApiErrors || hasClientSideLiquidErrors;
|
|
436
|
+
|
|
303
437
|
return {
|
|
304
438
|
// Validation state
|
|
305
439
|
...validationState,
|
|
@@ -316,10 +450,12 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
316
450
|
isClean,
|
|
317
451
|
|
|
318
452
|
// Computed properties
|
|
319
|
-
hasErrors: validationState.summary.totalErrors > 0,
|
|
453
|
+
hasErrors: validationState.summary.totalErrors > 0 || hasApiErrors,
|
|
320
454
|
hasWarnings: validationState.summary.totalWarnings > 0,
|
|
321
|
-
hasSecurityIssues: validationState.summary.hasSecurityIssues
|
|
455
|
+
hasSecurityIssues: validationState.summary.hasSecurityIssues,
|
|
456
|
+
/** True only when Rule Group #1 (Input & Sanitization) issues exist. Use for UI gating. */
|
|
457
|
+
hasBlockingErrors,
|
|
322
458
|
};
|
|
323
459
|
};
|
|
324
460
|
|
|
325
|
-
export default useValidation;
|
|
461
|
+
export default useValidation;
|