@elementor/editor-canvas 4.4.0-1144 → 4.4.0-1145
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/dist/index.js +68 -50
- package/dist/index.mjs +68 -50
- package/package.json +20 -20
- package/src/form-structure/__tests__/{honeypot-hint.test.ts → form-atom-added-hints.test.ts} +34 -3
- package/src/form-structure/__tests__/{prevent-duplicate-honeypot-commands.test.ts → prevent-duplicate-form-atoms-commands.test.ts} +68 -3
- package/src/form-structure/form-atom-added-hints.ts +53 -0
- package/src/form-structure/prevent-duplicate-form-atoms-commands.ts +97 -0
- package/src/form-structure/utils.ts +2 -0
- package/src/init.tsx +4 -4
- package/src/form-structure/honeypot-hint.ts +0 -29
- package/src/form-structure/prevent-duplicate-honeypot-commands.ts +0 -87
package/dist/index.js
CHANGED
|
@@ -1799,6 +1799,7 @@ var import_editor_elements4 = require("@elementor/editor-elements");
|
|
|
1799
1799
|
var FORM_ELEMENT_TYPE = "e-form";
|
|
1800
1800
|
var FORM_HIDDEN_FIELD_ELEMENT_TYPE = "e-form-hidden";
|
|
1801
1801
|
var FORM_HONEYPOT_ELEMENT_TYPE = "e-form-honeypot";
|
|
1802
|
+
var FORM_RECAPTCHA_SCORE_ELEMENT_TYPE = "e-form-recaptcha-score";
|
|
1802
1803
|
var FORM_FIELD_ELEMENT_TYPES = /* @__PURE__ */ new Set([
|
|
1803
1804
|
"e-form-input",
|
|
1804
1805
|
"e-form-textarea",
|
|
@@ -1811,7 +1812,8 @@ var FORM_FIELD_ELEMENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
1811
1812
|
"e-form-date-picker",
|
|
1812
1813
|
"e-form-time-picker",
|
|
1813
1814
|
FORM_HIDDEN_FIELD_ELEMENT_TYPE,
|
|
1814
|
-
FORM_HONEYPOT_ELEMENT_TYPE
|
|
1815
|
+
FORM_HONEYPOT_ELEMENT_TYPE,
|
|
1816
|
+
FORM_RECAPTCHA_SCORE_ELEMENT_TYPE
|
|
1815
1817
|
]);
|
|
1816
1818
|
function getArgsElementType(args) {
|
|
1817
1819
|
return args.model?.widgetType || args.model?.elType;
|
|
@@ -1947,112 +1949,128 @@ function handleBlockedFormField() {
|
|
|
1947
1949
|
(0, import_editor_notifications.notify)(FORM_FIELDS_OUTSIDE_ALERT);
|
|
1948
1950
|
}
|
|
1949
1951
|
|
|
1950
|
-
// src/form-structure/
|
|
1952
|
+
// src/form-structure/form-atom-added-hints.ts
|
|
1951
1953
|
var import_editor_notifications2 = require("@elementor/editor-notifications");
|
|
1952
1954
|
var import_editor_v1_adapters12 = require("@elementor/editor-v1-adapters");
|
|
1953
1955
|
var import_i18n2 = require("@wordpress/i18n");
|
|
1954
1956
|
var MOVE_COMMAND = "document/elements/move";
|
|
1957
|
+
var FORM_ATOM_HINTS = {
|
|
1958
|
+
[FORM_HONEYPOT_ELEMENT_TYPE]: {
|
|
1959
|
+
idPrefix: "honeypot-added",
|
|
1960
|
+
getMessage: () => (0, import_i18n2.__)("Honeypot added - invisible on your form. Reorder it in the Structure panel.", "elementor")
|
|
1961
|
+
},
|
|
1962
|
+
[FORM_RECAPTCHA_SCORE_ELEMENT_TYPE]: {
|
|
1963
|
+
idPrefix: "recaptcha-score-added",
|
|
1964
|
+
getMessage: () => (0, import_i18n2.__)("Score-based reCAPTCHA added - You can adjust its settings from the Structure panel.", "elementor")
|
|
1965
|
+
}
|
|
1966
|
+
};
|
|
1955
1967
|
var notificationCounter = 0;
|
|
1956
|
-
function
|
|
1957
|
-
return {
|
|
1958
|
-
type: "default",
|
|
1959
|
-
message: (0, import_i18n2.__)("Hidden field added - invisible on your form. Manage it in the Structure panel.", "elementor"),
|
|
1960
|
-
id: `hidden-field-added-${++notificationCounter}`
|
|
1961
|
-
};
|
|
1962
|
-
}
|
|
1963
|
-
function initHiddenFieldHint() {
|
|
1968
|
+
function initFormAtomAddedHints() {
|
|
1964
1969
|
(0, import_editor_v1_adapters12.registerDataHook)("after", "document/elements/create", (args, _result, options) => {
|
|
1965
1970
|
if (options?.commandsCurrentTrace?.includes(MOVE_COMMAND)) {
|
|
1966
1971
|
return;
|
|
1967
1972
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1973
|
+
const elementType = getArgsElementType(args);
|
|
1974
|
+
const hint = elementType ? FORM_ATOM_HINTS[elementType] : void 0;
|
|
1975
|
+
if (!hint) {
|
|
1976
|
+
return;
|
|
1970
1977
|
}
|
|
1978
|
+
(0, import_editor_notifications2.notify)({
|
|
1979
|
+
type: "default",
|
|
1980
|
+
message: hint.getMessage(),
|
|
1981
|
+
id: `${hint.idPrefix}-${++notificationCounter}`
|
|
1982
|
+
});
|
|
1971
1983
|
});
|
|
1972
1984
|
}
|
|
1973
1985
|
|
|
1974
|
-
// src/form-structure/
|
|
1986
|
+
// src/form-structure/hidden-field-hint.ts
|
|
1975
1987
|
var import_editor_notifications3 = require("@elementor/editor-notifications");
|
|
1976
1988
|
var import_editor_v1_adapters13 = require("@elementor/editor-v1-adapters");
|
|
1977
1989
|
var import_i18n3 = require("@wordpress/i18n");
|
|
1978
1990
|
var MOVE_COMMAND2 = "document/elements/move";
|
|
1979
1991
|
var notificationCounter2 = 0;
|
|
1980
|
-
function
|
|
1992
|
+
function createHiddenFieldNotification() {
|
|
1981
1993
|
return {
|
|
1982
1994
|
type: "default",
|
|
1983
|
-
message: (0, import_i18n3.__)("
|
|
1984
|
-
id: `
|
|
1995
|
+
message: (0, import_i18n3.__)("Hidden field added - invisible on your form. Manage it in the Structure panel.", "elementor"),
|
|
1996
|
+
id: `hidden-field-added-${++notificationCounter2}`
|
|
1985
1997
|
};
|
|
1986
1998
|
}
|
|
1987
|
-
function
|
|
1999
|
+
function initHiddenFieldHint() {
|
|
1988
2000
|
(0, import_editor_v1_adapters13.registerDataHook)("after", "document/elements/create", (args, _result, options) => {
|
|
1989
2001
|
if (options?.commandsCurrentTrace?.includes(MOVE_COMMAND2)) {
|
|
1990
2002
|
return;
|
|
1991
2003
|
}
|
|
1992
|
-
if (getArgsElementType(args) ===
|
|
1993
|
-
(0, import_editor_notifications3.notify)(
|
|
2004
|
+
if (getArgsElementType(args) === FORM_HIDDEN_FIELD_ELEMENT_TYPE) {
|
|
2005
|
+
(0, import_editor_notifications3.notify)(createHiddenFieldNotification());
|
|
1994
2006
|
}
|
|
1995
2007
|
});
|
|
1996
2008
|
}
|
|
1997
2009
|
|
|
1998
|
-
// src/form-structure/prevent-duplicate-
|
|
2010
|
+
// src/form-structure/prevent-duplicate-form-atoms-commands.ts
|
|
1999
2011
|
var import_editor_elements5 = require("@elementor/editor-elements");
|
|
2000
2012
|
var import_editor_notifications4 = require("@elementor/editor-notifications");
|
|
2001
2013
|
var import_editor_v1_adapters14 = require("@elementor/editor-v1-adapters");
|
|
2002
2014
|
var import_i18n4 = require("@wordpress/i18n");
|
|
2003
2015
|
var MOVE_COMMAND3 = "document/elements/move";
|
|
2004
|
-
var
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2016
|
+
var SINGLE_INSTANCE_FORM_ATOM_ALERTS = {
|
|
2017
|
+
[FORM_HONEYPOT_ELEMENT_TYPE]: {
|
|
2018
|
+
type: "default",
|
|
2019
|
+
message: (0, import_i18n4.__)("Only one Honeypot can be added per form.", "elementor"),
|
|
2020
|
+
id: "duplicate-honeypot-blocked"
|
|
2021
|
+
},
|
|
2022
|
+
[FORM_RECAPTCHA_SCORE_ELEMENT_TYPE]: {
|
|
2023
|
+
type: "default",
|
|
2024
|
+
message: (0, import_i18n4.__)("Only one Score-based reCAPTCHA can be added per form.", "elementor"),
|
|
2025
|
+
id: "duplicate-recaptcha-score-blocked"
|
|
2026
|
+
}
|
|
2008
2027
|
};
|
|
2009
|
-
|
|
2028
|
+
var SINGLE_INSTANCE_FORM_ATOM_TYPES = Object.keys(SINGLE_INSTANCE_FORM_ATOM_ALERTS);
|
|
2029
|
+
function initDuplicateFormAtomsPrevention() {
|
|
2010
2030
|
(0, import_editor_v1_adapters14.registerDataHook)("dependency", "document/elements/create", (args, options) => {
|
|
2011
|
-
return !
|
|
2031
|
+
return !blockDuplicateCreate(args, options);
|
|
2012
2032
|
});
|
|
2013
2033
|
(0, import_editor_v1_adapters14.blockCommand)({
|
|
2014
2034
|
command: "document/elements/move",
|
|
2015
|
-
condition:
|
|
2035
|
+
condition: blockDuplicateMove
|
|
2016
2036
|
});
|
|
2017
2037
|
}
|
|
2018
|
-
function
|
|
2038
|
+
function blockDuplicateCreate(args, options) {
|
|
2019
2039
|
if (options?.commandsCurrentTrace?.includes(MOVE_COMMAND3)) {
|
|
2020
2040
|
return false;
|
|
2021
2041
|
}
|
|
2022
|
-
if (!hasModelElementType(args.model, FORM_HONEYPOT_ELEMENT_TYPE)) {
|
|
2023
|
-
return false;
|
|
2024
|
-
}
|
|
2025
2042
|
const containers = args.containers ?? [args.container];
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2043
|
+
const blockedType = SINGLE_INSTANCE_FORM_ATOM_TYPES.find(
|
|
2044
|
+
(type) => hasModelElementType(args.model, type) && containers.some((container) => formHasElementType(getFormContainer(container), type))
|
|
2045
|
+
);
|
|
2046
|
+
if (!blockedType) {
|
|
2047
|
+
return false;
|
|
2029
2048
|
}
|
|
2030
|
-
|
|
2049
|
+
(0, import_editor_notifications4.notify)(SINGLE_INSTANCE_FORM_ATOM_ALERTS[blockedType]);
|
|
2050
|
+
return true;
|
|
2031
2051
|
}
|
|
2032
|
-
function
|
|
2052
|
+
function blockDuplicateMove(args) {
|
|
2033
2053
|
const { containers = [args.container], target } = args;
|
|
2034
2054
|
const movedContainers = containers.filter((container) => !!container);
|
|
2035
|
-
if (!movedContainers.some((container) => hasElementType(container, FORM_HONEYPOT_ELEMENT_TYPE))) {
|
|
2036
|
-
return false;
|
|
2037
|
-
}
|
|
2038
2055
|
const movedElements = new Set(movedContainers.flatMap((container) => (0, import_editor_elements5.getAllDescendants)(container)));
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2056
|
+
const targetForm = getFormContainer(target);
|
|
2057
|
+
const blockedType = SINGLE_INSTANCE_FORM_ATOM_TYPES.find(
|
|
2058
|
+
(type) => movedContainers.some((container) => hasElementType(container, type)) && formHasElementType(targetForm, type, movedElements)
|
|
2059
|
+
);
|
|
2060
|
+
if (!blockedType) {
|
|
2061
|
+
return false;
|
|
2042
2062
|
}
|
|
2043
|
-
|
|
2063
|
+
(0, import_editor_notifications4.notify)(SINGLE_INSTANCE_FORM_ATOM_ALERTS[blockedType]);
|
|
2064
|
+
return true;
|
|
2044
2065
|
}
|
|
2045
|
-
function
|
|
2066
|
+
function formHasElementType(form, type, excluded = /* @__PURE__ */ new Set()) {
|
|
2046
2067
|
if (!form) {
|
|
2047
2068
|
return false;
|
|
2048
2069
|
}
|
|
2049
2070
|
return (0, import_editor_elements5.getAllDescendants)(form).some(
|
|
2050
|
-
(element) => !excluded.has(element) && getElementType(element) ===
|
|
2071
|
+
(element) => !excluded.has(element) && getElementType(element) === type
|
|
2051
2072
|
);
|
|
2052
2073
|
}
|
|
2053
|
-
function handleBlockedHoneypot() {
|
|
2054
|
-
(0, import_editor_notifications4.notify)(DUPLICATE_HONEYPOT_ALERT);
|
|
2055
|
-
}
|
|
2056
2074
|
|
|
2057
2075
|
// src/form-structure/prevent-form-nesting-commands.ts
|
|
2058
2076
|
var import_editor_notifications5 = require("@elementor/editor-notifications");
|
|
@@ -6087,8 +6105,8 @@ function init() {
|
|
|
6087
6105
|
initFormNestingPrevention();
|
|
6088
6106
|
initFormAncestorEnforcement();
|
|
6089
6107
|
initHiddenFieldHint();
|
|
6090
|
-
|
|
6091
|
-
|
|
6108
|
+
initDuplicateFormAtomsPrevention();
|
|
6109
|
+
initFormAtomAddedHints();
|
|
6092
6110
|
initViewReplacements();
|
|
6093
6111
|
initLegacyViews();
|
|
6094
6112
|
initSettingsTransformers();
|
package/dist/index.mjs
CHANGED
|
@@ -1751,6 +1751,7 @@ import { getAllDescendants } from "@elementor/editor-elements";
|
|
|
1751
1751
|
var FORM_ELEMENT_TYPE = "e-form";
|
|
1752
1752
|
var FORM_HIDDEN_FIELD_ELEMENT_TYPE = "e-form-hidden";
|
|
1753
1753
|
var FORM_HONEYPOT_ELEMENT_TYPE = "e-form-honeypot";
|
|
1754
|
+
var FORM_RECAPTCHA_SCORE_ELEMENT_TYPE = "e-form-recaptcha-score";
|
|
1754
1755
|
var FORM_FIELD_ELEMENT_TYPES = /* @__PURE__ */ new Set([
|
|
1755
1756
|
"e-form-input",
|
|
1756
1757
|
"e-form-textarea",
|
|
@@ -1763,7 +1764,8 @@ var FORM_FIELD_ELEMENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
1763
1764
|
"e-form-date-picker",
|
|
1764
1765
|
"e-form-time-picker",
|
|
1765
1766
|
FORM_HIDDEN_FIELD_ELEMENT_TYPE,
|
|
1766
|
-
FORM_HONEYPOT_ELEMENT_TYPE
|
|
1767
|
+
FORM_HONEYPOT_ELEMENT_TYPE,
|
|
1768
|
+
FORM_RECAPTCHA_SCORE_ELEMENT_TYPE
|
|
1767
1769
|
]);
|
|
1768
1770
|
function getArgsElementType(args) {
|
|
1769
1771
|
return args.model?.widgetType || args.model?.elType;
|
|
@@ -1899,112 +1901,128 @@ function handleBlockedFormField() {
|
|
|
1899
1901
|
notify(FORM_FIELDS_OUTSIDE_ALERT);
|
|
1900
1902
|
}
|
|
1901
1903
|
|
|
1902
|
-
// src/form-structure/
|
|
1904
|
+
// src/form-structure/form-atom-added-hints.ts
|
|
1903
1905
|
import { notify as notify2 } from "@elementor/editor-notifications";
|
|
1904
1906
|
import { registerDataHook as registerDataHook3 } from "@elementor/editor-v1-adapters";
|
|
1905
1907
|
import { __ as __2 } from "@wordpress/i18n";
|
|
1906
1908
|
var MOVE_COMMAND = "document/elements/move";
|
|
1909
|
+
var FORM_ATOM_HINTS = {
|
|
1910
|
+
[FORM_HONEYPOT_ELEMENT_TYPE]: {
|
|
1911
|
+
idPrefix: "honeypot-added",
|
|
1912
|
+
getMessage: () => __2("Honeypot added - invisible on your form. Reorder it in the Structure panel.", "elementor")
|
|
1913
|
+
},
|
|
1914
|
+
[FORM_RECAPTCHA_SCORE_ELEMENT_TYPE]: {
|
|
1915
|
+
idPrefix: "recaptcha-score-added",
|
|
1916
|
+
getMessage: () => __2("Score-based reCAPTCHA added - You can adjust its settings from the Structure panel.", "elementor")
|
|
1917
|
+
}
|
|
1918
|
+
};
|
|
1907
1919
|
var notificationCounter = 0;
|
|
1908
|
-
function
|
|
1909
|
-
return {
|
|
1910
|
-
type: "default",
|
|
1911
|
-
message: __2("Hidden field added - invisible on your form. Manage it in the Structure panel.", "elementor"),
|
|
1912
|
-
id: `hidden-field-added-${++notificationCounter}`
|
|
1913
|
-
};
|
|
1914
|
-
}
|
|
1915
|
-
function initHiddenFieldHint() {
|
|
1920
|
+
function initFormAtomAddedHints() {
|
|
1916
1921
|
registerDataHook3("after", "document/elements/create", (args, _result, options) => {
|
|
1917
1922
|
if (options?.commandsCurrentTrace?.includes(MOVE_COMMAND)) {
|
|
1918
1923
|
return;
|
|
1919
1924
|
}
|
|
1920
|
-
|
|
1921
|
-
|
|
1925
|
+
const elementType = getArgsElementType(args);
|
|
1926
|
+
const hint = elementType ? FORM_ATOM_HINTS[elementType] : void 0;
|
|
1927
|
+
if (!hint) {
|
|
1928
|
+
return;
|
|
1922
1929
|
}
|
|
1930
|
+
notify2({
|
|
1931
|
+
type: "default",
|
|
1932
|
+
message: hint.getMessage(),
|
|
1933
|
+
id: `${hint.idPrefix}-${++notificationCounter}`
|
|
1934
|
+
});
|
|
1923
1935
|
});
|
|
1924
1936
|
}
|
|
1925
1937
|
|
|
1926
|
-
// src/form-structure/
|
|
1938
|
+
// src/form-structure/hidden-field-hint.ts
|
|
1927
1939
|
import { notify as notify3 } from "@elementor/editor-notifications";
|
|
1928
1940
|
import { registerDataHook as registerDataHook4 } from "@elementor/editor-v1-adapters";
|
|
1929
1941
|
import { __ as __3 } from "@wordpress/i18n";
|
|
1930
1942
|
var MOVE_COMMAND2 = "document/elements/move";
|
|
1931
1943
|
var notificationCounter2 = 0;
|
|
1932
|
-
function
|
|
1944
|
+
function createHiddenFieldNotification() {
|
|
1933
1945
|
return {
|
|
1934
1946
|
type: "default",
|
|
1935
|
-
message: __3("
|
|
1936
|
-
id: `
|
|
1947
|
+
message: __3("Hidden field added - invisible on your form. Manage it in the Structure panel.", "elementor"),
|
|
1948
|
+
id: `hidden-field-added-${++notificationCounter2}`
|
|
1937
1949
|
};
|
|
1938
1950
|
}
|
|
1939
|
-
function
|
|
1951
|
+
function initHiddenFieldHint() {
|
|
1940
1952
|
registerDataHook4("after", "document/elements/create", (args, _result, options) => {
|
|
1941
1953
|
if (options?.commandsCurrentTrace?.includes(MOVE_COMMAND2)) {
|
|
1942
1954
|
return;
|
|
1943
1955
|
}
|
|
1944
|
-
if (getArgsElementType(args) ===
|
|
1945
|
-
notify3(
|
|
1956
|
+
if (getArgsElementType(args) === FORM_HIDDEN_FIELD_ELEMENT_TYPE) {
|
|
1957
|
+
notify3(createHiddenFieldNotification());
|
|
1946
1958
|
}
|
|
1947
1959
|
});
|
|
1948
1960
|
}
|
|
1949
1961
|
|
|
1950
|
-
// src/form-structure/prevent-duplicate-
|
|
1962
|
+
// src/form-structure/prevent-duplicate-form-atoms-commands.ts
|
|
1951
1963
|
import { getAllDescendants as getAllDescendants2 } from "@elementor/editor-elements";
|
|
1952
1964
|
import { notify as notify4 } from "@elementor/editor-notifications";
|
|
1953
1965
|
import { blockCommand as blockCommand2, registerDataHook as registerDataHook5 } from "@elementor/editor-v1-adapters";
|
|
1954
1966
|
import { __ as __4 } from "@wordpress/i18n";
|
|
1955
1967
|
var MOVE_COMMAND3 = "document/elements/move";
|
|
1956
|
-
var
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1968
|
+
var SINGLE_INSTANCE_FORM_ATOM_ALERTS = {
|
|
1969
|
+
[FORM_HONEYPOT_ELEMENT_TYPE]: {
|
|
1970
|
+
type: "default",
|
|
1971
|
+
message: __4("Only one Honeypot can be added per form.", "elementor"),
|
|
1972
|
+
id: "duplicate-honeypot-blocked"
|
|
1973
|
+
},
|
|
1974
|
+
[FORM_RECAPTCHA_SCORE_ELEMENT_TYPE]: {
|
|
1975
|
+
type: "default",
|
|
1976
|
+
message: __4("Only one Score-based reCAPTCHA can be added per form.", "elementor"),
|
|
1977
|
+
id: "duplicate-recaptcha-score-blocked"
|
|
1978
|
+
}
|
|
1960
1979
|
};
|
|
1961
|
-
|
|
1980
|
+
var SINGLE_INSTANCE_FORM_ATOM_TYPES = Object.keys(SINGLE_INSTANCE_FORM_ATOM_ALERTS);
|
|
1981
|
+
function initDuplicateFormAtomsPrevention() {
|
|
1962
1982
|
registerDataHook5("dependency", "document/elements/create", (args, options) => {
|
|
1963
|
-
return !
|
|
1983
|
+
return !blockDuplicateCreate(args, options);
|
|
1964
1984
|
});
|
|
1965
1985
|
blockCommand2({
|
|
1966
1986
|
command: "document/elements/move",
|
|
1967
|
-
condition:
|
|
1987
|
+
condition: blockDuplicateMove
|
|
1968
1988
|
});
|
|
1969
1989
|
}
|
|
1970
|
-
function
|
|
1990
|
+
function blockDuplicateCreate(args, options) {
|
|
1971
1991
|
if (options?.commandsCurrentTrace?.includes(MOVE_COMMAND3)) {
|
|
1972
1992
|
return false;
|
|
1973
1993
|
}
|
|
1974
|
-
if (!hasModelElementType(args.model, FORM_HONEYPOT_ELEMENT_TYPE)) {
|
|
1975
|
-
return false;
|
|
1976
|
-
}
|
|
1977
1994
|
const containers = args.containers ?? [args.container];
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1995
|
+
const blockedType = SINGLE_INSTANCE_FORM_ATOM_TYPES.find(
|
|
1996
|
+
(type) => hasModelElementType(args.model, type) && containers.some((container) => formHasElementType(getFormContainer(container), type))
|
|
1997
|
+
);
|
|
1998
|
+
if (!blockedType) {
|
|
1999
|
+
return false;
|
|
1981
2000
|
}
|
|
1982
|
-
|
|
2001
|
+
notify4(SINGLE_INSTANCE_FORM_ATOM_ALERTS[blockedType]);
|
|
2002
|
+
return true;
|
|
1983
2003
|
}
|
|
1984
|
-
function
|
|
2004
|
+
function blockDuplicateMove(args) {
|
|
1985
2005
|
const { containers = [args.container], target } = args;
|
|
1986
2006
|
const movedContainers = containers.filter((container) => !!container);
|
|
1987
|
-
if (!movedContainers.some((container) => hasElementType(container, FORM_HONEYPOT_ELEMENT_TYPE))) {
|
|
1988
|
-
return false;
|
|
1989
|
-
}
|
|
1990
2007
|
const movedElements = new Set(movedContainers.flatMap((container) => getAllDescendants2(container)));
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2008
|
+
const targetForm = getFormContainer(target);
|
|
2009
|
+
const blockedType = SINGLE_INSTANCE_FORM_ATOM_TYPES.find(
|
|
2010
|
+
(type) => movedContainers.some((container) => hasElementType(container, type)) && formHasElementType(targetForm, type, movedElements)
|
|
2011
|
+
);
|
|
2012
|
+
if (!blockedType) {
|
|
2013
|
+
return false;
|
|
1994
2014
|
}
|
|
1995
|
-
|
|
2015
|
+
notify4(SINGLE_INSTANCE_FORM_ATOM_ALERTS[blockedType]);
|
|
2016
|
+
return true;
|
|
1996
2017
|
}
|
|
1997
|
-
function
|
|
2018
|
+
function formHasElementType(form, type, excluded = /* @__PURE__ */ new Set()) {
|
|
1998
2019
|
if (!form) {
|
|
1999
2020
|
return false;
|
|
2000
2021
|
}
|
|
2001
2022
|
return getAllDescendants2(form).some(
|
|
2002
|
-
(element) => !excluded.has(element) && getElementType(element) ===
|
|
2023
|
+
(element) => !excluded.has(element) && getElementType(element) === type
|
|
2003
2024
|
);
|
|
2004
2025
|
}
|
|
2005
|
-
function handleBlockedHoneypot() {
|
|
2006
|
-
notify4(DUPLICATE_HONEYPOT_ALERT);
|
|
2007
|
-
}
|
|
2008
2026
|
|
|
2009
2027
|
// src/form-structure/prevent-form-nesting-commands.ts
|
|
2010
2028
|
import { notify as notify5 } from "@elementor/editor-notifications";
|
|
@@ -6085,8 +6103,8 @@ function init() {
|
|
|
6085
6103
|
initFormNestingPrevention();
|
|
6086
6104
|
initFormAncestorEnforcement();
|
|
6087
6105
|
initHiddenFieldHint();
|
|
6088
|
-
|
|
6089
|
-
|
|
6106
|
+
initDuplicateFormAtomsPrevention();
|
|
6107
|
+
initFormAtomAddedHints();
|
|
6090
6108
|
initViewReplacements();
|
|
6091
6109
|
initLegacyViews();
|
|
6092
6110
|
initSettingsTransformers();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-canvas",
|
|
3
3
|
"description": "Elementor Editor Canvas",
|
|
4
|
-
"version": "4.4.0-
|
|
4
|
+
"version": "4.4.0-1145",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -37,26 +37,26 @@
|
|
|
37
37
|
"react-dom": "^18.3.1"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@elementor/editor": "4.4.0-
|
|
41
|
-
"@elementor/editor-controls": "4.4.0-
|
|
42
|
-
"@elementor/editor-documents": "4.4.0-
|
|
43
|
-
"@elementor/editor-elements": "4.4.0-
|
|
44
|
-
"@elementor/editor-interactions": "4.4.0-
|
|
45
|
-
"@elementor/editor-mcp": "4.4.0-
|
|
46
|
-
"@elementor/editor-notifications": "4.4.0-
|
|
47
|
-
"@elementor/editor-props": "4.4.0-
|
|
48
|
-
"@elementor/editor-responsive": "4.4.0-
|
|
49
|
-
"@elementor/editor-styles": "4.4.0-
|
|
50
|
-
"@elementor/editor-styles-repository": "4.4.0-
|
|
51
|
-
"@elementor/editor-ui": "4.4.0-
|
|
52
|
-
"@elementor/editor-v1-adapters": "4.4.0-
|
|
53
|
-
"@elementor/events": "4.4.0-
|
|
54
|
-
"@elementor/http-client": "4.4.0-
|
|
55
|
-
"@elementor/schema": "4.4.0-
|
|
56
|
-
"@elementor/twing": "4.4.0-
|
|
40
|
+
"@elementor/editor": "4.4.0-1145",
|
|
41
|
+
"@elementor/editor-controls": "4.4.0-1145",
|
|
42
|
+
"@elementor/editor-documents": "4.4.0-1145",
|
|
43
|
+
"@elementor/editor-elements": "4.4.0-1145",
|
|
44
|
+
"@elementor/editor-interactions": "4.4.0-1145",
|
|
45
|
+
"@elementor/editor-mcp": "4.4.0-1145",
|
|
46
|
+
"@elementor/editor-notifications": "4.4.0-1145",
|
|
47
|
+
"@elementor/editor-props": "4.4.0-1145",
|
|
48
|
+
"@elementor/editor-responsive": "4.4.0-1145",
|
|
49
|
+
"@elementor/editor-styles": "4.4.0-1145",
|
|
50
|
+
"@elementor/editor-styles-repository": "4.4.0-1145",
|
|
51
|
+
"@elementor/editor-ui": "4.4.0-1145",
|
|
52
|
+
"@elementor/editor-v1-adapters": "4.4.0-1145",
|
|
53
|
+
"@elementor/events": "4.4.0-1145",
|
|
54
|
+
"@elementor/http-client": "4.4.0-1145",
|
|
55
|
+
"@elementor/schema": "4.4.0-1145",
|
|
56
|
+
"@elementor/twing": "4.4.0-1145",
|
|
57
57
|
"@elementor/ui": "1.41.4",
|
|
58
|
-
"@elementor/utils": "4.4.0-
|
|
59
|
-
"@elementor/wp-media": "4.4.0-
|
|
58
|
+
"@elementor/utils": "4.4.0-1145",
|
|
59
|
+
"@elementor/wp-media": "4.4.0-1145",
|
|
60
60
|
"@floating-ui/react": "^0.27.5",
|
|
61
61
|
"@wordpress/i18n": "^5.13.0",
|
|
62
62
|
"dompurify": "^3.2.6"
|
package/src/form-structure/__tests__/{honeypot-hint.test.ts → form-atom-added-hints.test.ts}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHooksRegistry, setupHooksRegistry } from 'test-utils';
|
|
2
2
|
import { notify } from '@elementor/editor-notifications';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { initFormAtomAddedHints } from '../form-atom-added-hints';
|
|
5
5
|
|
|
6
6
|
jest.mock( '@elementor/editor-notifications', () => ( {
|
|
7
7
|
notify: jest.fn(),
|
|
@@ -10,14 +10,45 @@ jest.mock( '@elementor/editor-notifications', () => ( {
|
|
|
10
10
|
const CREATE_COMMAND = 'document/elements/create';
|
|
11
11
|
const MOVE_COMMAND = 'document/elements/move';
|
|
12
12
|
const HONEYPOT_TYPE = 'e-form-honeypot';
|
|
13
|
+
const RECAPTCHA_SCORE_TYPE = 'e-form-recaptcha-score';
|
|
13
14
|
|
|
14
|
-
describe( '
|
|
15
|
+
describe( 'form atom added hints', () => {
|
|
15
16
|
const hooksRegistry = createHooksRegistry();
|
|
16
17
|
|
|
17
18
|
beforeEach( () => {
|
|
18
19
|
jest.mocked( notify ).mockClear();
|
|
19
20
|
setupHooksRegistry( hooksRegistry );
|
|
20
|
-
|
|
21
|
+
initFormAtomAddedHints();
|
|
22
|
+
} );
|
|
23
|
+
|
|
24
|
+
it( 'should notify when a reCAPTCHA Score is added', () => {
|
|
25
|
+
// Arrange
|
|
26
|
+
const hook = hooksRegistry.getByCommand( CREATE_COMMAND );
|
|
27
|
+
|
|
28
|
+
// Act
|
|
29
|
+
hook?.apply( { model: { widgetType: RECAPTCHA_SCORE_TYPE } } );
|
|
30
|
+
|
|
31
|
+
// Assert
|
|
32
|
+
expect( notify ).toHaveBeenCalledWith(
|
|
33
|
+
expect.objectContaining( {
|
|
34
|
+
message: 'Score-based reCAPTCHA added - You can adjust its settings from the Structure panel.',
|
|
35
|
+
} )
|
|
36
|
+
);
|
|
37
|
+
} );
|
|
38
|
+
|
|
39
|
+
it( 'should notify again when a reCAPTCHA Score is removed and added once more', () => {
|
|
40
|
+
// Arrange
|
|
41
|
+
const hook = hooksRegistry.getByCommand( CREATE_COMMAND );
|
|
42
|
+
|
|
43
|
+
// Act
|
|
44
|
+
hook?.apply( { model: { widgetType: RECAPTCHA_SCORE_TYPE } } );
|
|
45
|
+
hook?.apply( { model: { widgetType: RECAPTCHA_SCORE_TYPE } } );
|
|
46
|
+
|
|
47
|
+
// Assert
|
|
48
|
+
expect( notify ).toHaveBeenCalledTimes( 2 );
|
|
49
|
+
expect( jest.mocked( notify ).mock.calls[ 0 ][ 0 ].id ).not.toBe(
|
|
50
|
+
jest.mocked( notify ).mock.calls[ 1 ][ 0 ].id
|
|
51
|
+
);
|
|
21
52
|
} );
|
|
22
53
|
|
|
23
54
|
it( 'should notify when a honeypot is added', () => {
|
|
@@ -2,7 +2,7 @@ import { createHooksRegistry, setupHooksRegistry } from 'test-utils';
|
|
|
2
2
|
import { type V1Element } from '@elementor/editor-elements';
|
|
3
3
|
import { notify } from '@elementor/editor-notifications';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { initDuplicateFormAtomsPrevention } from '../prevent-duplicate-form-atoms-commands';
|
|
6
6
|
|
|
7
7
|
jest.mock( '@elementor/editor-notifications', () => ( {
|
|
8
8
|
notify: jest.fn(),
|
|
@@ -11,6 +11,7 @@ jest.mock( '@elementor/editor-notifications', () => ( {
|
|
|
11
11
|
const CREATE_COMMAND = 'document/elements/create';
|
|
12
12
|
const MOVE_COMMAND = 'document/elements/move';
|
|
13
13
|
const HONEYPOT_TYPE = 'e-form-honeypot';
|
|
14
|
+
const RECAPTCHA_SCORE_TYPE = 'e-form-recaptcha-score';
|
|
14
15
|
|
|
15
16
|
function createElement( type: string, children: V1Element[] = [] ): V1Element {
|
|
16
17
|
const element = {
|
|
@@ -26,7 +27,7 @@ function createElement( type: string, children: V1Element[] = [] ): V1Element {
|
|
|
26
27
|
return element;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
describe( 'prevent duplicate
|
|
30
|
+
describe( 'prevent duplicate form atoms', () => {
|
|
30
31
|
const hooksRegistry = createHooksRegistry();
|
|
31
32
|
|
|
32
33
|
const isAllowed = ( command: string, args: unknown ) => hooksRegistry.getByCommand( command )?.apply( args );
|
|
@@ -34,7 +35,7 @@ describe( 'prevent duplicate honeypot', () => {
|
|
|
34
35
|
beforeEach( () => {
|
|
35
36
|
jest.mocked( notify ).mockClear();
|
|
36
37
|
setupHooksRegistry( hooksRegistry );
|
|
37
|
-
|
|
38
|
+
initDuplicateFormAtomsPrevention();
|
|
38
39
|
} );
|
|
39
40
|
|
|
40
41
|
it( 'should allow adding the first honeypot to a form', () => {
|
|
@@ -130,4 +131,68 @@ describe( 'prevent duplicate honeypot', () => {
|
|
|
130
131
|
expect( result ).toBe( false );
|
|
131
132
|
expect( notify ).toHaveBeenCalledTimes( 1 );
|
|
132
133
|
} );
|
|
134
|
+
|
|
135
|
+
it( 'should block a second reCAPTCHA Score in the same form and show its toast', () => {
|
|
136
|
+
// Arrange
|
|
137
|
+
const flexbox = createElement( 'e-flexbox' );
|
|
138
|
+
createElement( 'e-form', [ createElement( RECAPTCHA_SCORE_TYPE ), flexbox ] );
|
|
139
|
+
|
|
140
|
+
// Act
|
|
141
|
+
const result = isAllowed( CREATE_COMMAND, {
|
|
142
|
+
container: flexbox,
|
|
143
|
+
model: { widgetType: RECAPTCHA_SCORE_TYPE },
|
|
144
|
+
} );
|
|
145
|
+
|
|
146
|
+
// Assert
|
|
147
|
+
expect( result ).toBe( false );
|
|
148
|
+
expect( notify ).toHaveBeenCalledWith(
|
|
149
|
+
expect.objectContaining( { message: 'Only one Score-based reCAPTCHA can be added per form.' } )
|
|
150
|
+
);
|
|
151
|
+
} );
|
|
152
|
+
|
|
153
|
+
it( 'should allow a reCAPTCHA Score in a form that already has a honeypot', () => {
|
|
154
|
+
// Arrange
|
|
155
|
+
const form = createElement( 'e-form', [ createElement( HONEYPOT_TYPE ) ] );
|
|
156
|
+
|
|
157
|
+
// Act
|
|
158
|
+
const result = isAllowed( CREATE_COMMAND, { container: form, model: { widgetType: RECAPTCHA_SCORE_TYPE } } );
|
|
159
|
+
|
|
160
|
+
// Assert
|
|
161
|
+
expect( result ).toBe( true );
|
|
162
|
+
expect( notify ).not.toHaveBeenCalled();
|
|
163
|
+
} );
|
|
164
|
+
|
|
165
|
+
it( 'should allow a reCAPTCHA Score in a second form on the same page', () => {
|
|
166
|
+
// Arrange
|
|
167
|
+
const secondForm = createElement( 'e-form' );
|
|
168
|
+
createElement( 'document', [
|
|
169
|
+
createElement( 'e-form', [ createElement( RECAPTCHA_SCORE_TYPE ) ] ),
|
|
170
|
+
secondForm,
|
|
171
|
+
] );
|
|
172
|
+
|
|
173
|
+
// Act
|
|
174
|
+
const result = isAllowed( CREATE_COMMAND, {
|
|
175
|
+
container: secondForm,
|
|
176
|
+
model: { widgetType: RECAPTCHA_SCORE_TYPE },
|
|
177
|
+
} );
|
|
178
|
+
|
|
179
|
+
// Assert
|
|
180
|
+
expect( result ).toBe( true );
|
|
181
|
+
} );
|
|
182
|
+
|
|
183
|
+
it( 'should block moving a reCAPTCHA Score into another form that already has one', () => {
|
|
184
|
+
// Arrange
|
|
185
|
+
const recaptcha = createElement( RECAPTCHA_SCORE_TYPE );
|
|
186
|
+
const targetForm = createElement( 'e-form', [ createElement( RECAPTCHA_SCORE_TYPE ) ] );
|
|
187
|
+
createElement( 'document', [ createElement( 'e-form', [ recaptcha ] ), targetForm ] );
|
|
188
|
+
|
|
189
|
+
// Act
|
|
190
|
+
const result = isAllowed( MOVE_COMMAND, { containers: [ recaptcha ], target: targetForm } );
|
|
191
|
+
|
|
192
|
+
// Assert
|
|
193
|
+
expect( result ).toBe( false );
|
|
194
|
+
expect( notify ).toHaveBeenCalledWith(
|
|
195
|
+
expect.objectContaining( { message: 'Only one Score-based reCAPTCHA can be added per form.' } )
|
|
196
|
+
);
|
|
197
|
+
} );
|
|
133
198
|
} );
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { notify } from '@elementor/editor-notifications';
|
|
2
|
+
import { type HookOptions, registerDataHook } from '@elementor/editor-v1-adapters';
|
|
3
|
+
import { __ } from '@wordpress/i18n';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
type CreateArgs,
|
|
7
|
+
FORM_HONEYPOT_ELEMENT_TYPE,
|
|
8
|
+
FORM_RECAPTCHA_SCORE_ELEMENT_TYPE,
|
|
9
|
+
getArgsElementType,
|
|
10
|
+
} from './utils';
|
|
11
|
+
|
|
12
|
+
const MOVE_COMMAND = 'document/elements/move';
|
|
13
|
+
|
|
14
|
+
type FormAtomHint = {
|
|
15
|
+
idPrefix: string;
|
|
16
|
+
getMessage: () => string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const FORM_ATOM_HINTS: Record< string, FormAtomHint > = {
|
|
20
|
+
[ FORM_HONEYPOT_ELEMENT_TYPE ]: {
|
|
21
|
+
idPrefix: 'honeypot-added',
|
|
22
|
+
getMessage: () =>
|
|
23
|
+
__( 'Honeypot added - invisible on your form. Reorder it in the Structure panel.', 'elementor' ),
|
|
24
|
+
},
|
|
25
|
+
[ FORM_RECAPTCHA_SCORE_ELEMENT_TYPE ]: {
|
|
26
|
+
idPrefix: 'recaptcha-score-added',
|
|
27
|
+
getMessage: () =>
|
|
28
|
+
__( 'Score-based reCAPTCHA added - You can adjust its settings from the Structure panel.', 'elementor' ),
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
let notificationCounter = 0;
|
|
33
|
+
|
|
34
|
+
export function initFormAtomAddedHints() {
|
|
35
|
+
registerDataHook( 'after', 'document/elements/create', ( args: CreateArgs, _result, options?: HookOptions ) => {
|
|
36
|
+
if ( options?.commandsCurrentTrace?.includes( MOVE_COMMAND ) ) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const elementType = getArgsElementType( args );
|
|
41
|
+
const hint = elementType ? FORM_ATOM_HINTS[ elementType ] : undefined;
|
|
42
|
+
|
|
43
|
+
if ( ! hint ) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
notify( {
|
|
48
|
+
type: 'default',
|
|
49
|
+
message: hint.getMessage(),
|
|
50
|
+
id: `${ hint.idPrefix }-${ ++notificationCounter }`,
|
|
51
|
+
} );
|
|
52
|
+
} );
|
|
53
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
FORM_RECAPTCHA_SCORE_ELEMENT_TYPE,
|
|
10
|
+
getElementType,
|
|
11
|
+
getFormContainer,
|
|
12
|
+
hasElementType,
|
|
13
|
+
hasModelElementType,
|
|
14
|
+
type MoveArgs,
|
|
15
|
+
} from './utils';
|
|
16
|
+
|
|
17
|
+
const MOVE_COMMAND = 'document/elements/move';
|
|
18
|
+
|
|
19
|
+
const SINGLE_INSTANCE_FORM_ATOM_ALERTS: Record< string, NotificationData > = {
|
|
20
|
+
[ FORM_HONEYPOT_ELEMENT_TYPE ]: {
|
|
21
|
+
type: 'default',
|
|
22
|
+
message: __( 'Only one Honeypot can be added per form.', 'elementor' ),
|
|
23
|
+
id: 'duplicate-honeypot-blocked',
|
|
24
|
+
},
|
|
25
|
+
[ FORM_RECAPTCHA_SCORE_ELEMENT_TYPE ]: {
|
|
26
|
+
type: 'default',
|
|
27
|
+
message: __( 'Only one Score-based reCAPTCHA can be added per form.', 'elementor' ),
|
|
28
|
+
id: 'duplicate-recaptcha-score-blocked',
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const SINGLE_INSTANCE_FORM_ATOM_TYPES = Object.keys( SINGLE_INSTANCE_FORM_ATOM_ALERTS );
|
|
33
|
+
|
|
34
|
+
export function initDuplicateFormAtomsPrevention() {
|
|
35
|
+
registerDataHook( 'dependency', 'document/elements/create', ( args: CreateArgs, options?: HookOptions ) => {
|
|
36
|
+
return ! blockDuplicateCreate( args, options );
|
|
37
|
+
} );
|
|
38
|
+
|
|
39
|
+
blockCommand( {
|
|
40
|
+
command: 'document/elements/move',
|
|
41
|
+
condition: blockDuplicateMove,
|
|
42
|
+
} );
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function blockDuplicateCreate( args: CreateArgs, options?: HookOptions ): boolean {
|
|
46
|
+
// Move re-creates the moved element at the target, so it is handled by the move condition.
|
|
47
|
+
if ( options?.commandsCurrentTrace?.includes( MOVE_COMMAND ) ) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const containers = args.containers ?? [ args.container ];
|
|
52
|
+
|
|
53
|
+
const blockedType = SINGLE_INSTANCE_FORM_ATOM_TYPES.find(
|
|
54
|
+
( type ) =>
|
|
55
|
+
hasModelElementType( args.model, type ) &&
|
|
56
|
+
containers.some( ( container ) => formHasElementType( getFormContainer( container ), type ) )
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if ( ! blockedType ) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
notify( SINGLE_INSTANCE_FORM_ATOM_ALERTS[ blockedType ] );
|
|
64
|
+
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function blockDuplicateMove( args: MoveArgs ): boolean {
|
|
69
|
+
const { containers = [ args.container ], target } = args;
|
|
70
|
+
const movedContainers = containers.filter( ( container ): container is V1Element => !! container );
|
|
71
|
+
const movedElements = new Set( movedContainers.flatMap( ( container ) => getAllDescendants( container ) ) );
|
|
72
|
+
const targetForm = getFormContainer( target );
|
|
73
|
+
|
|
74
|
+
const blockedType = SINGLE_INSTANCE_FORM_ATOM_TYPES.find(
|
|
75
|
+
( type ) =>
|
|
76
|
+
movedContainers.some( ( container ) => hasElementType( container, type ) ) &&
|
|
77
|
+
formHasElementType( targetForm, type, movedElements )
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if ( ! blockedType ) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
notify( SINGLE_INSTANCE_FORM_ATOM_ALERTS[ blockedType ] );
|
|
85
|
+
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function formHasElementType( form: V1Element | undefined, type: string, excluded: Set< V1Element > = new Set() ) {
|
|
90
|
+
if ( ! form ) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return getAllDescendants( form ).some(
|
|
95
|
+
( element ) => ! excluded.has( element ) && getElementType( element ) === type
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -38,6 +38,7 @@ export type StorageContent = {
|
|
|
38
38
|
export const FORM_ELEMENT_TYPE = 'e-form';
|
|
39
39
|
export const FORM_HIDDEN_FIELD_ELEMENT_TYPE = 'e-form-hidden';
|
|
40
40
|
export const FORM_HONEYPOT_ELEMENT_TYPE = 'e-form-honeypot';
|
|
41
|
+
export const FORM_RECAPTCHA_SCORE_ELEMENT_TYPE = 'e-form-recaptcha-score';
|
|
41
42
|
export const FORM_FIELD_ELEMENT_TYPES = new Set( [
|
|
42
43
|
'e-form-input',
|
|
43
44
|
'e-form-textarea',
|
|
@@ -51,6 +52,7 @@ export const FORM_FIELD_ELEMENT_TYPES = new Set( [
|
|
|
51
52
|
'e-form-time-picker',
|
|
52
53
|
FORM_HIDDEN_FIELD_ELEMENT_TYPE,
|
|
53
54
|
FORM_HONEYPOT_ELEMENT_TYPE,
|
|
55
|
+
FORM_RECAPTCHA_SCORE_ELEMENT_TYPE,
|
|
54
56
|
] );
|
|
55
57
|
|
|
56
58
|
export function getArgsElementType( args: CreateArgs ): string | undefined {
|
package/src/init.tsx
CHANGED
|
@@ -6,9 +6,9 @@ import { ElementsOverlays } from './components/elements-overlays';
|
|
|
6
6
|
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
|
+
import { initFormAtomAddedHints } from './form-structure/form-atom-added-hints';
|
|
9
10
|
import { initHiddenFieldHint } from './form-structure/hidden-field-hint';
|
|
10
|
-
import {
|
|
11
|
-
import { initDuplicateHoneypotPrevention } from './form-structure/prevent-duplicate-honeypot-commands';
|
|
11
|
+
import { initDuplicateFormAtomsPrevention } from './form-structure/prevent-duplicate-form-atoms-commands';
|
|
12
12
|
import { initFormNestingPrevention } from './form-structure/prevent-form-nesting-commands';
|
|
13
13
|
import { initSettingsTransformers } from './init-settings-transformers';
|
|
14
14
|
import { initStyleTransformers } from './init-style-transformers';
|
|
@@ -29,8 +29,8 @@ export function init() {
|
|
|
29
29
|
initFormNestingPrevention();
|
|
30
30
|
initFormAncestorEnforcement();
|
|
31
31
|
initHiddenFieldHint();
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
initDuplicateFormAtomsPrevention();
|
|
33
|
+
initFormAtomAddedHints();
|
|
34
34
|
|
|
35
35
|
initViewReplacements();
|
|
36
36
|
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { type NotificationData, notify } from '@elementor/editor-notifications';
|
|
2
|
-
import { type HookOptions, registerDataHook } from '@elementor/editor-v1-adapters';
|
|
3
|
-
import { __ } from '@wordpress/i18n';
|
|
4
|
-
|
|
5
|
-
import { type CreateArgs, FORM_HONEYPOT_ELEMENT_TYPE, getArgsElementType } from './utils';
|
|
6
|
-
|
|
7
|
-
const MOVE_COMMAND = 'document/elements/move';
|
|
8
|
-
|
|
9
|
-
let notificationCounter = 0;
|
|
10
|
-
|
|
11
|
-
function createHoneypotNotification(): NotificationData {
|
|
12
|
-
return {
|
|
13
|
-
type: 'default',
|
|
14
|
-
message: __( 'Honeypot added - invisible on your form. Reorder it in the Structure panel.', 'elementor' ),
|
|
15
|
-
id: `honeypot-added-${ ++notificationCounter }`,
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function initHoneypotHint() {
|
|
20
|
-
registerDataHook( 'after', 'document/elements/create', ( args: CreateArgs, _result, options?: HookOptions ) => {
|
|
21
|
-
if ( options?.commandsCurrentTrace?.includes( MOVE_COMMAND ) ) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if ( getArgsElementType( args ) === FORM_HONEYPOT_ELEMENT_TYPE ) {
|
|
26
|
-
notify( createHoneypotNotification() );
|
|
27
|
-
}
|
|
28
|
-
} );
|
|
29
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
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
|
-
}
|