@elementor/editor-canvas 4.4.0-1137 → 4.4.0-1139

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.
@@ -0,0 +1,87 @@
1
+ import { getAllDescendants, type V1Element } from '@elementor/editor-elements';
2
+ import { type NotificationData, notify } from '@elementor/editor-notifications';
3
+ import { blockCommand, type HookOptions, registerDataHook } from '@elementor/editor-v1-adapters';
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ import {
7
+ type CreateArgs,
8
+ FORM_HONEYPOT_ELEMENT_TYPE,
9
+ getElementType,
10
+ getFormContainer,
11
+ hasElementType,
12
+ hasModelElementType,
13
+ type MoveArgs,
14
+ } from './utils';
15
+
16
+ const MOVE_COMMAND = 'document/elements/move';
17
+
18
+ const DUPLICATE_HONEYPOT_ALERT: NotificationData = {
19
+ type: 'default',
20
+ message: __( 'Only one Honeypot can be added per form.', 'elementor' ),
21
+ id: 'duplicate-honeypot-blocked',
22
+ };
23
+
24
+ export function initDuplicateHoneypotPrevention() {
25
+ registerDataHook( 'dependency', 'document/elements/create', ( args: CreateArgs, options?: HookOptions ) => {
26
+ return ! blockHoneypotCreate( args, options );
27
+ } );
28
+
29
+ blockCommand( {
30
+ command: 'document/elements/move',
31
+ condition: blockHoneypotMove,
32
+ } );
33
+ }
34
+
35
+ function blockHoneypotCreate( args: CreateArgs, options?: HookOptions ): boolean {
36
+ // Move re-creates the moved element at the target, so it is handled by the move condition.
37
+ if ( options?.commandsCurrentTrace?.includes( MOVE_COMMAND ) ) {
38
+ return false;
39
+ }
40
+
41
+ if ( ! hasModelElementType( args.model, FORM_HONEYPOT_ELEMENT_TYPE ) ) {
42
+ return false;
43
+ }
44
+
45
+ const containers = args.containers ?? [ args.container ];
46
+
47
+ if ( containers.some( ( container ) => formHasHoneypot( getFormContainer( container ) ) ) ) {
48
+ handleBlockedHoneypot();
49
+
50
+ return true;
51
+ }
52
+
53
+ return false;
54
+ }
55
+
56
+ function blockHoneypotMove( args: MoveArgs ): boolean {
57
+ const { containers = [ args.container ], target } = args;
58
+ const movedContainers = containers.filter( ( container ): container is V1Element => !! container );
59
+
60
+ if ( ! movedContainers.some( ( container ) => hasElementType( container, FORM_HONEYPOT_ELEMENT_TYPE ) ) ) {
61
+ return false;
62
+ }
63
+
64
+ const movedElements = new Set( movedContainers.flatMap( ( container ) => getAllDescendants( container ) ) );
65
+
66
+ if ( formHasHoneypot( getFormContainer( target ), movedElements ) ) {
67
+ handleBlockedHoneypot();
68
+
69
+ return true;
70
+ }
71
+
72
+ return false;
73
+ }
74
+
75
+ function formHasHoneypot( form?: V1Element, excluded: Set< V1Element > = new Set() ): boolean {
76
+ if ( ! form ) {
77
+ return false;
78
+ }
79
+
80
+ return getAllDescendants( form ).some(
81
+ ( element ) => ! excluded.has( element ) && getElementType( element ) === FORM_HONEYPOT_ELEMENT_TYPE
82
+ );
83
+ }
84
+
85
+ function handleBlockedHoneypot(): void {
86
+ notify( DUPLICATE_HONEYPOT_ALERT );
87
+ }
@@ -3,6 +3,7 @@ import { getAllDescendants, type V1Element } from '@elementor/editor-elements';
3
3
  type Model = {
4
4
  elType?: string;
5
5
  widgetType?: string;
6
+ elements?: Model[];
6
7
  };
7
8
 
8
9
  export type CreateArgs = {
@@ -36,6 +37,7 @@ export type StorageContent = {
36
37
 
37
38
  export const FORM_ELEMENT_TYPE = 'e-form';
38
39
  export const FORM_HIDDEN_FIELD_ELEMENT_TYPE = 'e-form-hidden';
40
+ export const FORM_HONEYPOT_ELEMENT_TYPE = 'e-form-honeypot';
39
41
  export const FORM_FIELD_ELEMENT_TYPES = new Set( [
40
42
  'e-form-input',
41
43
  'e-form-textarea',
@@ -48,12 +50,39 @@ export const FORM_FIELD_ELEMENT_TYPES = new Set( [
48
50
  'e-form-date-picker',
49
51
  'e-form-time-picker',
50
52
  FORM_HIDDEN_FIELD_ELEMENT_TYPE,
53
+ FORM_HONEYPOT_ELEMENT_TYPE,
51
54
  ] );
52
55
 
53
56
  export function getArgsElementType( args: CreateArgs ): string | undefined {
54
57
  return args.model?.widgetType || args.model?.elType;
55
58
  }
56
59
 
60
+ export function hasModelElementType( model: Model | undefined, type: string ): boolean {
61
+ if ( ! model ) {
62
+ return false;
63
+ }
64
+
65
+ if ( ( model.widgetType || model.elType ) === type ) {
66
+ return true;
67
+ }
68
+
69
+ return ( model.elements ?? [] ).some( ( child ) => hasModelElementType( child, type ) );
70
+ }
71
+
72
+ export function getFormContainer( element?: V1Element ): V1Element | undefined {
73
+ let current = element;
74
+
75
+ while ( current ) {
76
+ if ( getElementType( current ) === FORM_ELEMENT_TYPE ) {
77
+ return current;
78
+ }
79
+
80
+ current = current.parent;
81
+ }
82
+
83
+ return undefined;
84
+ }
85
+
57
86
  export function getElementType( element?: V1Element ): string | undefined {
58
87
  return element?.model.get( 'widgetType' ) || element?.model.get( 'elType' );
59
88
  }
package/src/init.tsx CHANGED
@@ -7,6 +7,8 @@ import { InteractionsRenderer } from './components/interactions-renderer';
7
7
  import { StyleRenderer } from './components/style-renderer';
8
8
  import { initFormAncestorEnforcement } from './form-structure/enforce-form-ancestor-commands';
9
9
  import { initHiddenFieldHint } from './form-structure/hidden-field-hint';
10
+ import { initHoneypotHint } from './form-structure/honeypot-hint';
11
+ import { initDuplicateHoneypotPrevention } from './form-structure/prevent-duplicate-honeypot-commands';
10
12
  import { initFormNestingPrevention } from './form-structure/prevent-form-nesting-commands';
11
13
  import { initSettingsTransformers } from './init-settings-transformers';
12
14
  import { initStyleTransformers } from './init-style-transformers';
@@ -27,6 +29,8 @@ export function init() {
27
29
  initFormNestingPrevention();
28
30
  initFormAncestorEnforcement();
29
31
  initHiddenFieldHint();
32
+ initDuplicateHoneypotPrevention();
33
+ initHoneypotHint();
30
34
 
31
35
  initViewReplacements();
32
36