diff --git a/package-lock.json b/package-lock.json index 736035f64..9fdd44f9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@eightshift/eightshift-forms", - "version": "1.3.1-alpha", + "version": "1.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -38,25 +38,25 @@ } }, "@babel/compat-data": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", - "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==" + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.0.tgz", + "integrity": "sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw==" }, "@babel/core": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", - "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.0.tgz", + "integrity": "sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ==", "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.13", + "@babel/generator": "^7.19.0", + "@babel/helper-compilation-targets": "^7.19.0", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helpers": "^7.19.0", + "@babel/parser": "^7.19.0", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -97,11 +97,11 @@ } }, "@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", + "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", "requires": { - "@babel/types": "^7.18.13", + "@babel/types": "^7.19.0", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -136,11 +136,11 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz", + "integrity": "sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA==", "requires": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.19.0", "@babel/helper-validator-option": "^7.18.6", "browserslist": "^4.20.2", "semver": "^6.3.0" @@ -154,13 +154,13 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.13.tgz", - "integrity": "sha512-hDvXp+QYxSRL+23mpAlSGxHMDyIGChm0/AwTfTAAK5Ufe40nCsyNdaYCGuK91phn/fVu9kqayImRDkvNAgdrsA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", + "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-member-expression-to-functions": "^7.18.9", "@babel/helper-optimise-call-expression": "^7.18.6", "@babel/helper-replace-supers": "^7.18.9", @@ -168,9 +168,9 @@ } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", - "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "regexpu-core": "^5.1.0" @@ -220,12 +220,12 @@ } }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" } }, "@babel/helper-hoist-variables": { @@ -253,18 +253,18 @@ } }, "@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", "@babel/helper-simple-access": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" } }, "@babel/helper-optimise-call-expression": { @@ -276,9 +276,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==" + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==" }, "@babel/helper-remap-async-to-generator": { "version": "7.18.9", @@ -343,24 +343,24 @@ "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" }, "@babel/helper-wrap-function": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz", - "integrity": "sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", + "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", "requires": { - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.11", - "@babel/types": "^7.18.10" + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" } }, "@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", + "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", "requires": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" } }, "@babel/highlight": { @@ -420,9 +420,9 @@ } }, "@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==" + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz", + "integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -443,12 +443,12 @@ } }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", - "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.0.tgz", + "integrity": "sha512-nhEByMUTx3uZueJ/QkJuSlCfN4FGg+xy+vRsfGQGzSauq5ks2Deid2+05Q3KhfaUjvec1IGhw/Zm3cFm8JigTQ==", "requires": { "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" } @@ -473,15 +473,15 @@ } }, "@babel/plugin-proposal-decorators": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.10.tgz", - "integrity": "sha512-wdGTwWF5QtpTY/gbBtQLAiCnoxfD4qMbN87NYZle1dOZ9Os8Y6zXcKrIaOU8W+TIvFUWVGG9tUgNww3CjXRVVw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.19.0.tgz", + "integrity": "sha512-Bo5nOSjiJccjv00+BrDkmfeBLBi2B0qe8ygj24KdL8VdwtZz+710NCwehF+x/Ng+0mkHx5za2eAofmvVFLF4Fg==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-create-class-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/helper-replace-supers": "^7.18.9", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/plugin-syntax-decorators": "^7.18.6" + "@babel/plugin-syntax-decorators": "^7.19.0" } }, "@babel/plugin-proposal-dynamic-import": { @@ -632,11 +632,11 @@ } }, "@babel/plugin-syntax-decorators": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.18.6.tgz", - "integrity": "sha512-fqyLgjcxf/1yhyZ6A+yo1u9gJ7eleFQod2lkaUsF9DQ7sbbY3Ligym3L0+I2c0WmqNKDpoD9UTb1AKP3qRMOAQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz", + "integrity": "sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-syntax-dynamic-import": { @@ -802,15 +802,16 @@ } }, "@babel/plugin-transform-classes": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", - "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", + "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.19.0", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/helper-replace-supers": "^7.18.9", "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" @@ -866,11 +867,11 @@ } }, "@babel/plugin-transform-flow-strip-types": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.9.tgz", - "integrity": "sha512-+G6rp2zRuOAInY5wcggsx4+QVao1qPM0osC9fTUVlAV3zOrzTCnrMAFVnR6+a3T8wz1wFIH7KhYMcMB3u1n80A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz", + "integrity": "sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==", "requires": { - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/plugin-syntax-flow": "^7.18.6" } }, @@ -930,13 +931,13 @@ } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", - "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz", + "integrity": "sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==", "requires": { "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/helper-validator-identifier": "^7.18.6", "babel-plugin-dynamic-import-node": "^2.3.3" } @@ -951,12 +952,12 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", - "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.0.tgz", + "integrity": "sha512-HDSuqOQzkU//kfGdiHBt71/hkDTApw4U/cMVgKgX7PqfB3LOaK+2GtCEsBu1dL9CkswDm0Gwehht1dCr421ULQ==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-new-target": { @@ -1001,15 +1002,15 @@ } }, "@babel/plugin-transform-react-jsx": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.10.tgz", - "integrity": "sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", + "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/types": "^7.18.10" + "@babel/types": "^7.19.0" } }, "@babel/plugin-transform-react-jsx-development": { @@ -1055,11 +1056,11 @@ } }, "@babel/plugin-transform-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", - "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", "requires": { - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" } }, @@ -1088,12 +1089,12 @@ } }, "@babel/plugin-transform-typescript": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.12.tgz", - "integrity": "sha512-2vjjam0cum0miPkenUbQswKowuxs/NjMwIKEq0zwegRxXk12C9YOF9STXnaUptITOtOJHKHpzvvWYOjbm6tc0w==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.0.tgz", + "integrity": "sha512-DOOIywxPpkQHXijXv+s9MDAyZcLp12oYRl3CMWZ6u7TjSoCBq/KqHR/nNFR3+i2xqheZxoF0H2XyL7B6xeSRuA==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-create-class-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/plugin-syntax-typescript": "^7.18.6" } }, @@ -1115,17 +1116,17 @@ } }, "@babel/preset-env": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", - "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.19.0.tgz", + "integrity": "sha512-1YUju1TAFuzjIQqNM9WsF4U6VbD/8t3wEAlw3LFYuuEr+ywqLRcSXxFKz4DCEj+sN94l/XTDiUXYRrsvMpz9WQ==", "requires": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.19.0", + "@babel/helper-compilation-targets": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/helper-validator-option": "^7.18.6", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-async-generator-functions": "^7.19.0", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-static-block": "^7.18.6", "@babel/plugin-proposal-dynamic-import": "^7.18.6", @@ -1159,9 +1160,9 @@ "@babel/plugin-transform-async-to-generator": "^7.18.6", "@babel/plugin-transform-block-scoped-functions": "^7.18.6", "@babel/plugin-transform-block-scoping": "^7.18.9", - "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-classes": "^7.19.0", "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.13", "@babel/plugin-transform-dotall-regex": "^7.18.6", "@babel/plugin-transform-duplicate-keys": "^7.18.9", "@babel/plugin-transform-exponentiation-operator": "^7.18.6", @@ -1171,9 +1172,9 @@ "@babel/plugin-transform-member-expression-literals": "^7.18.6", "@babel/plugin-transform-modules-amd": "^7.18.6", "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-systemjs": "^7.19.0", "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.0", "@babel/plugin-transform-new-target": "^7.18.6", "@babel/plugin-transform-object-super": "^7.18.6", "@babel/plugin-transform-parameters": "^7.18.8", @@ -1181,14 +1182,14 @@ "@babel/plugin-transform-regenerator": "^7.18.6", "@babel/plugin-transform-reserved-words": "^7.18.6", "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-spread": "^7.19.0", "@babel/plugin-transform-sticky-regex": "^7.18.6", "@babel/plugin-transform-template-literals": "^7.18.9", "@babel/plugin-transform-typeof-symbol": "^7.18.9", "@babel/plugin-transform-unicode-escapes": "^7.18.10", "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.18.10", + "@babel/types": "^7.19.0", "babel-plugin-polyfill-corejs2": "^0.3.2", "babel-plugin-polyfill-corejs3": "^0.5.3", "babel-plugin-polyfill-regenerator": "^0.4.0", @@ -1319,9 +1320,9 @@ } }, "@babel/runtime-corejs3": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz", - "integrity": "sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.19.0.tgz", + "integrity": "sha512-JyXXoCu1N8GLuKc2ii8y5RGma5FMpFeO2nAQIe0Yzrbq+rQnN+sFj47auLblR5ka6aHNGPDgv8G/iI2Grb0ldQ==", "requires": { "core-js-pure": "^3.20.2", "regenerator-runtime": "^0.13.4" @@ -1338,18 +1339,18 @@ } }, "@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.0.tgz", + "integrity": "sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA==", "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", + "@babel/generator": "^7.19.0", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/parser": "^7.19.0", + "@babel/types": "^7.19.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1362,9 +1363,9 @@ } }, "@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", + "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", "requires": { "@babel/helper-string-parser": "^7.18.10", "@babel/helper-validator-identifier": "^7.18.6", @@ -1381,14 +1382,62 @@ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==" }, - "@eightshift/frontend-libs": { + "@dnd-kit/accessibility": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.0.1.tgz", + "integrity": "sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@dnd-kit/core": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.0.5.tgz", + "integrity": "sha512-3nL+Zy5cT+1XwsWdlXIvGIFvbuocMyB4NBxTN74DeBaBqeWdH9JsnKwQv7buZQgAHmAH+eIENfS1ginkvW6bCw==", + "requires": { + "@dnd-kit/accessibility": "^3.0.0", + "@dnd-kit/utilities": "^3.2.0", + "tslib": "^2.0.0" + } + }, + "@dnd-kit/modifiers": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/modifiers/-/modifiers-6.0.0.tgz", + "integrity": "sha512-V3+JSo6/BTcgPRHiNUTSKgqVv/doKXg+T4Z0QvKiiXp+uIyJTUtPkQOBRQApUWi3ApBhnoWljyt/3xxY4fTd0Q==", + "requires": { + "@dnd-kit/utilities": "^3.2.0", + "tslib": "^2.0.0" + } + }, + "@dnd-kit/sortable": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@eightshift/frontend-libs/-/frontend-libs-7.0.1.tgz", - "integrity": "sha512-6m0WXy6fMVpH3L/+i0qkEBfq+77wQx+JZIYAOsG3aeYr1hZTxe4qjAWKZaRkl3Cu5cyoqRalLmr7XO83qpw9Mg==", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-7.0.1.tgz", + "integrity": "sha512-n77qAzJQtMMywu25sJzhz3gsHnDOUlEjTtnRl8A87rWIhnu32zuP+7zmFjwGgvqfXmRufqiHOSlH7JPC/tnJ8Q==", + "requires": { + "@dnd-kit/utilities": "^3.2.0", + "tslib": "^2.0.0" + } + }, + "@dnd-kit/utilities": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.0.tgz", + "integrity": "sha512-h65/pn2IPCCIWwdlR2BMLqRkDxpTEONA+HQW3n765HBijLYGyrnTCLa2YQt8VVjjSQD6EfFlTE6aS2Q/b6nb2g==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@eightshift/frontend-libs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@eightshift/frontend-libs/-/frontend-libs-7.1.0.tgz", + "integrity": "sha512-RYHK+HJ6j18k9Aez/a83YGe4HLApIO0/7vpuhEXOgUcjXJi9WYUZKLXA16dCDp16hE3kTUXcMAxIog+r4M0G/A==", "requires": { "@babel/cli": "^7.18.6", "@babel/eslint-parser": "^7.18.2", "@babel/eslint-plugin": "^7.17.7", + "@dnd-kit/core": "^6.0.5", + "@dnd-kit/modifiers": "^6.0.0", + "@dnd-kit/sortable": "^7.0.1", + "@dnd-kit/utilities": "^3.2.0", "@infinumjs/eslint-config-react-js": "^3.3.1", "@wordpress/api-fetch": "^6.9.0", "@wordpress/dependency-extraction-webpack-plugin": "^3.6.0", @@ -1469,9 +1518,9 @@ } }, "@emotion/cache": { - "version": "11.10.2", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.2.tgz", - "integrity": "sha512-GPR4PovENRvYDbCEnDRecPZYJzWdNMsM+Jn+13MC5uImVNbMyKwzv95DUHy5PDcgfPtKoDtfLU6emF1grrbQDg==", + "version": "11.10.3", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.3.tgz", + "integrity": "sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ==", "requires": { "@emotion/memoize": "^0.8.0", "@emotion/sheet": "^1.2.0", @@ -1491,14 +1540,15 @@ "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" }, "@emotion/react": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.0.tgz", - "integrity": "sha512-K6z9zlHxxBXwN8TcpwBKcEsBsOw4JWCCmR+BeeOWgqp8GIU1yA2Odd41bwdAAr0ssbQrbJbVnndvv7oiv1bZeQ==", + "version": "11.10.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.4.tgz", + "integrity": "sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA==", "requires": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.10.0", "@emotion/cache": "^11.10.0", "@emotion/serialize": "^1.1.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", "@emotion/utils": "^1.2.0", "@emotion/weak-memoize": "^0.3.0", "hoist-non-react-statics": "^3.3.1" @@ -1526,6 +1576,11 @@ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" }, + "@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", + "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==" + }, "@emotion/utils": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", @@ -1537,13 +1592,13 @@ "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" }, "@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.1.tgz", + "integrity": "sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ==", "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.2", + "espree": "^9.4.0", "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -1567,6 +1622,11 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==" }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" + }, "@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", @@ -1911,9 +1971,9 @@ } }, "@types/node": { - "version": "16.11.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.54.tgz", - "integrity": "sha512-ryOpwe15+BtTUxKFfzABjaI/EtXLPBSBEW4B6D5ygWNcORLVKG/1/FC3WwAr5d7t6lCnlVPRsCY0NH680QT+Pg==" + "version": "16.11.57", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.57.tgz", + "integrity": "sha512-diBb5AE2V8h9Fs9zEDtBwSeLvIACng/aAkdZ3ujMV+cGuIQ9Nc/V+wQqurk9HJp8ni5roBxQHW21z/ZYbGDivg==" }, "acorn": { "version": "6.4.2", @@ -2533,11 +2593,6 @@ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" }, - "@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" - }, "@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -2617,9 +2672,9 @@ "integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==" }, "@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" }, "@types/minimist": { "version": "1.2.2", @@ -2662,9 +2717,9 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "@types/react": { - "version": "17.0.48", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.48.tgz", - "integrity": "sha512-zJ6IYlJ8cYYxiJfUaZOQee4lh99mFihBoqkOSEGV+dFi9leROW6+PgstzQ+w3gWTnUfskALtQPGHK6dYmPj+2A==", + "version": "17.0.49", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.49.tgz", + "integrity": "sha512-CCBPMZaPhcKkYUTqFs/hOWqKjPxhTEmnZWjlHHgIMop67DsXywf9B5Os9Hz8KSacjNOgIdnZVJamwl232uxoPg==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -2895,13 +2950,13 @@ "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==" }, "@wordpress/api-fetch": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-6.12.0.tgz", - "integrity": "sha512-5zfpvmAd1Fx+89R+ldBfA+mbHxatmpNWxH9BRdF8tFXIFcJhKLFvGK7NsJuqnTu4DH0pr1tAzQyf+QcF4UkV9A==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-6.13.0.tgz", + "integrity": "sha512-PwVhZI64naytQFmMc2veQYz6jA3DyAPe3cv3L499iKgigNt41fNHZpEz5tgZmpLJA0Avp9Ldy+izCzt9A0PKcQ==", "requires": { "@babel/runtime": "^7.16.0", - "@wordpress/i18n": "^4.15.0", - "@wordpress/url": "^3.16.0" + "@wordpress/i18n": "^4.16.0", + "@wordpress/url": "^3.17.0" } }, "@wordpress/dependency-extraction-webpack-plugin": { @@ -2914,51 +2969,58 @@ } }, "@wordpress/dom-ready": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-3.15.0.tgz", - "integrity": "sha512-sP6M/nBFvN7cK0jnEQmNDwXPyoPSd35e178vE8zWxu070/6YZkCne0sT9MPvW3Q7LA8VZLles6PgXe4Kg1rgXw==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-3.16.0.tgz", + "integrity": "sha512-l4tQ65Y1lNMNypjM8Shi08NsxwS3D/lxFYwOznx+JNZzGU6IU39xHDIzCZYyFOkGvO2NkY7AjyITVnvVNkYY5Q==", "requires": { "@babel/runtime": "^7.16.0" } }, "@wordpress/element": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-4.13.0.tgz", - "integrity": "sha512-oxTEiK7y0bLva9SMbt/xrp90VgDMFcLSOSPz1lS8wSrC+Hy8NyN0v5rku3DdIUf07kYtcOfiQ1jmsmwDWNvodg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-4.14.0.tgz", + "integrity": "sha512-puH6E1sY5HYcU9MTnwpvwmlGqs0prxNd0JuT2/WcHAd/qwDC2wQPB/7fDaffjB0+gIYUt6WBl8kCWkqQ7g6cbQ==", "requires": { "@babel/runtime": "^7.16.0", "@types/react": "^17.0.37", "@types/react-dom": "^17.0.11", - "@wordpress/escape-html": "^2.15.0", + "@wordpress/escape-html": "^2.16.0", "change-case": "^4.1.2", - "is-plain-obj": "^4.1.0", + "is-plain-object": "^5.0.0", "react": "^17.0.2", "react-dom": "^17.0.2" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + } } }, "@wordpress/escape-html": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-2.15.0.tgz", - "integrity": "sha512-eW655uSjCK835/eBt1lgCBtLFfgxSX4MiMTe7Dxo8pqZmP5cwh9zNJuirEnVnaamjAjfIVRel4awNGZebflJeg==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-2.16.0.tgz", + "integrity": "sha512-63SfsnkGTIFBXQWy+vAjlb2PJp9A59R0wsTv4TIS/DBJq6EtAox9GjBbswGzX6l0VksobUcga1FqOua9+i3EyA==", "requires": { "@babel/runtime": "^7.16.0" } }, "@wordpress/hooks": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-3.15.0.tgz", - "integrity": "sha512-w0kFs8xX4C+ofTszaNaggdvs+cuVl4wOCPULncOfXLEWo4MBwUpx82BFTeV5ql44oOF6iEEKHcR75gOOXCXOVQ==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-3.16.0.tgz", + "integrity": "sha512-KpY8KFp2/3TX6lKmffNmdkeaH9c4CN1iJ8SiCufjGgRCnVWmWe/HcEJ5OjhUvBnRkhsLMY7pvlXMU8Mh7nLxyA==", "requires": { "@babel/runtime": "^7.16.0" } }, "@wordpress/i18n": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-4.15.0.tgz", - "integrity": "sha512-nqazvRbtrhuykmZXnMxyumqP6duiCWxf2YAgHCBhoZIpwOmmbu+S7CtYWNx+EHIAa6cSdon6loRACc90Mx6PZg==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-4.16.0.tgz", + "integrity": "sha512-N7BChVVaQpt63e2Wgc0ST+ahUuhSjd6bqHqgIBnxZ4LU3c8tzd/etYjBqSM8RPcI9gSOM32ddlTnJgAxgntKaA==", "requires": { "@babel/runtime": "^7.16.0", - "@wordpress/hooks": "^3.15.0", + "@wordpress/hooks": "^3.16.0", "gettext-parser": "^1.3.1", "lodash": "^4.17.21", "memize": "^1.1.0", @@ -2967,29 +3029,29 @@ } }, "@wordpress/icons": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-9.6.0.tgz", - "integrity": "sha512-vQwoqWzPuYmJZS2h0+0fXRfpBRSA95juq6Pj1EmUJZodlSLX2NTV3EiDsDwj2cRmXR+BLmLBledm8uYo7BsjRA==", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-9.7.0.tgz", + "integrity": "sha512-QW9FHBx4Yof7bnxZA2MzZNwc/eJOtFcGjnAcSdY72uOset9n6vCMVkEYZe5wX5ZKsRSRCcpXkNrDw2vTcWCOSQ==", "requires": { "@babel/runtime": "^7.16.0", - "@wordpress/element": "^4.13.0", - "@wordpress/primitives": "^3.13.0" + "@wordpress/element": "^4.14.0", + "@wordpress/primitives": "^3.14.0" } }, "@wordpress/primitives": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-3.13.0.tgz", - "integrity": "sha512-bNBbK30ZUq24G9ltZWsOPiRB0rP75BDgZTQObXogfeh+nSP7rNPsisYozxJ7FSiX5xhLMpQMOgv294XaV9Syhg==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-3.14.0.tgz", + "integrity": "sha512-rHibruWLgp5aIWqIJEBDozsVK+JWGjy2EIwusINVqPN5BL6ahajEJtOmmUJmYZwUzj5g2MVS6fRdzQUS9oKGRQ==", "requires": { "@babel/runtime": "^7.16.0", - "@wordpress/element": "^4.13.0", + "@wordpress/element": "^4.14.0", "classnames": "^2.3.1" } }, "@wordpress/url": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-3.16.0.tgz", - "integrity": "sha512-5hlT8KfioKrmfqQAHihj2pWqc8oMUFNae3n5/Wlu8H60Btf5h+cBfxr6eiOXPEVX9Ko9NskLjmAqCxxoiNviqg==", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-3.17.0.tgz", + "integrity": "sha512-817geMi/DA5cPXO9tPHJB8g+MtI3xpA0s2/26paAciD5rPa/Y9BgTtfvPiR/pzKk+hQOxjNSw6dwb1bPGTmB1A==", "requires": { "@babel/runtime": "^7.16.0", "remove-accents": "^0.4.2" @@ -4515,14 +4577,14 @@ "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==" }, "core-js": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz", - "integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==" + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.0.tgz", + "integrity": "sha512-CVU1xvJEfJGhyCpBrzzzU1kjCfgsGUxhEvwUV2e/cOedYWHdmluamx+knDnmhqALddMG16fZvIqvs9aijsHHaA==" }, "core-js-compat": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", - "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.0.tgz", + "integrity": "sha512-extKQM0g8/3GjFx9US12FAgx8KJawB7RCQ5y8ipYLbmfzEzmFRWdDjIlxDx82g7ygcNG85qMVUSRyABouELdow==", "requires": { "browserslist": "^4.21.3", "semver": "7.0.0" @@ -4536,9 +4598,9 @@ } }, "core-js-pure": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.24.1.tgz", - "integrity": "sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg==" + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.0.tgz", + "integrity": "sha512-IeHpLwk3uoci37yoI2Laty59+YqH9x5uR65/yiA0ARAJrTrN4YU0rmauLWfvqOuk77SlNJXj2rM6oT/dBD87+A==" }, "core-util-is": { "version": "1.0.3", @@ -4641,9 +4703,9 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" }, "css-declaration-sorter": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", - "integrity": "sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==" }, "css-functions-list": { "version": "3.1.0", @@ -5350,15 +5412,15 @@ } }, "es-abstract": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", - "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.2.tgz", + "integrity": "sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==", "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.1", + "get-intrinsic": "^1.1.2", "get-symbol-description": "^1.0.0", "has": "^1.0.3", "has-property-descriptors": "^1.0.0", @@ -5370,9 +5432,9 @@ "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", + "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.4.3", "string.prototype.trimend": "^1.0.5", "string.prototype.trimstart": "^1.0.5", @@ -5423,13 +5485,14 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "eslint": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz", - "integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.0.tgz", + "integrity": "sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA==", "requires": { - "@eslint/eslintrc": "^1.3.0", + "@eslint/eslintrc": "^1.3.1", "@humanwhocodes/config-array": "^0.10.4", "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@humanwhocodes/module-importer": "^1.0.1", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -5439,7 +5502,7 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.3", + "espree": "^9.4.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -5464,8 +5527,7 @@ "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "dependencies": { "ansi-regex": { @@ -5629,9 +5691,9 @@ } }, "eslint-plugin-react": { - "version": "7.30.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz", - "integrity": "sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==", + "version": "7.31.7", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.7.tgz", + "integrity": "sha512-8NldBTeYp/kQoTV1uT0XF6HcmDqbgZ0lNPkN0wlRw8DJKXEnaWu+oh/6gt3xIhzvQ35wB2Y545fJhIbJSZ2NNw==", "requires": { "array-includes": "^3.1.5", "array.prototype.flatmap": "^1.3.0", @@ -5702,9 +5764,9 @@ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" }, "espree": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", - "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", "requires": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", @@ -6332,9 +6394,9 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, "flow-parser": { - "version": "0.185.1", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.185.1.tgz", - "integrity": "sha512-nbtJZFMGgJVCRBlE/66p7L6IWF+wy6Nbd65sVwyrH7WsnZgeef8m263uxN4xah+8BZwuGndU8HKlt8cHIpTwew==" + "version": "0.186.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.186.0.tgz", + "integrity": "sha512-QaPJczRxNc/yvp3pawws439VZ/vHGq+i1/mZ3bEdSaRy8scPgZgiWklSB6jN7y5NR9sfgL4GGIiBcMXTj3Opqg==" }, "flush-write-stream": { "version": "1.1.1", @@ -7677,9 +7739,9 @@ } }, "is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" }, "is-plain-object": { "version": "2.0.4", @@ -10075,9 +10137,9 @@ } }, "rc-util": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.23.0.tgz", - "integrity": "sha512-lgm6diJ/pLgyfoZY59Vz7sW4mSoQCgozqbBye9IJ7/mb5w5h4T7h+i2JpXAx/UBQxscBZe68q0sP7EW+qfkKUg==", + "version": "5.24.2", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.24.2.tgz", + "integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==", "requires": { "@babel/runtime": "^7.18.3", "react-is": "^16.12.0", @@ -10662,9 +10724,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.54.5", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.5.tgz", - "integrity": "sha512-p7DTOzxkUPa/63FU0R3KApkRHwcVZYC0PLnLm5iyZACyp15qSi32x7zVUhRdABAATmkALqgGrjCJAcWvobmhHw==", + "version": "1.54.8", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.8.tgz", + "integrity": "sha512-ib4JhLRRgbg6QVy6bsv5uJxnJMTS2soVcCp9Y88Extyy13A8vV0G1fAwujOzmNkFQbR3LvedudAMbtuNRPbQww==", "requires": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -11149,28 +11211,16 @@ } }, "socket.io": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz", - "integrity": "sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.2.tgz", + "integrity": "sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==", "requires": { "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.2", "engine.io": "~6.2.0", "socket.io-adapter": "~2.4.0", - "socket.io-parser": "~4.0.4" - }, - "dependencies": { - "socket.io-parser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.5.tgz", - "integrity": "sha512-sNjbT9dX63nqUFIOv95tTVm6elyIU4RvB1m8dOeZt+IgWwcWklFDOdmGcfo3zSiRsnR/3pJkjY5lfoGqEe4Eig==", - "requires": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", - "debug": "~4.3.1" - } - } + "socket.io-parser": "~4.2.0" } }, "socket.io-adapter": { @@ -11179,9 +11229,9 @@ "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==" }, "socket.io-client": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.1.tgz", - "integrity": "sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.2.tgz", + "integrity": "sha512-naqYfFu7CLDiQ1B7AlLhRXKX3gdeaIMfgigwavDzgJoIUYulc1qHH5+2XflTsXTPY7BlPH5rppJyUjhjrKQKLg==", "requires": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", @@ -11876,9 +11926,9 @@ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==" }, "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", "requires": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" @@ -12276,9 +12326,9 @@ } }, "typescript": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", - "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==" + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==" }, "ua-parser-js": { "version": "1.0.2", @@ -12351,13 +12401,6 @@ "is-plain-obj": "^2.0.0", "trough": "^1.0.0", "vfile": "^4.0.0" - }, - "dependencies": { - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" - } } }, "union-value": { diff --git a/package.json b/package.json index 74ccbf341..77c9908b7 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "webpack-cli": "^4.10.0" }, "dependencies": { - "@eightshift/frontend-libs": "^7.0.0", + "@eightshift/frontend-libs": "^7.1.0", "autosize": "^5.0.1", "choices.js": "^10.1.0", "dropzone": "^6.0.0-beta.1" diff --git a/src/AdminMenus/FormGlobalSettingsAdminSubMenu.php b/src/AdminMenus/FormGlobalSettingsAdminSubMenu.php index 2e1d16931..73d03a420 100644 --- a/src/AdminMenus/FormGlobalSettingsAdminSubMenu.php +++ b/src/AdminMenus/FormGlobalSettingsAdminSubMenu.php @@ -13,6 +13,7 @@ use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; use EightshiftForms\Helpers\Helper; use EightshiftForms\Settings\GlobalSettings\SettingsGlobalInterface; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\Settings\SettingsGeneral; use EightshiftFormsVendor\EightshiftLibs\AdminMenus\AbstractAdminSubMenu; @@ -173,13 +174,19 @@ protected function processAttributes($attr): array { $type = isset($_GET['type']) ? \sanitize_text_field(\wp_unslash($_GET['type'])) : SettingsGeneral::SETTINGS_TYPE_KEY; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $settingsSidebarOutput = []; + foreach ($this->settingsGlobal->getSettingsSidebar($type) as $item) { + $sidebarType = $item['type'] ?? SettingsAll::SETTINGS_SIEDBAR_TYPE_GENERAL; + $settingsSidebarOutput[$sidebarType][] = $item; + } + return [ // translators: %s replaces form title name. 'adminSettingsPageTitle' => \esc_html__('Settings', 'eightshift-forms'), 'adminSettingsSubTitle' => \esc_html__('These settings apply to all forms.', 'eightshift-forms'), 'adminSettingsBackLink' => Helper::getListingPageUrl(), 'adminSettingsLink' => Helper::getSettingsGlobalPageUrl(''), - 'adminSettingsSidebar' => $this->settingsGlobal->getSettingsSidebar($type), + 'adminSettingsSidebar' => $settingsSidebarOutput, 'adminSettingsForm' => $this->settingsGlobal->getSettingsForm($type), 'adminSettingsType' => $type, 'adminSettingsIsGlobal' => true, diff --git a/src/AdminMenus/FormSettingsAdminSubMenu.php b/src/AdminMenus/FormSettingsAdminSubMenu.php index 90c34daa2..c93907572 100644 --- a/src/AdminMenus/FormSettingsAdminSubMenu.php +++ b/src/AdminMenus/FormSettingsAdminSubMenu.php @@ -12,6 +12,7 @@ use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; use EightshiftForms\Helpers\Helper; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\Settings\SettingsAllInterface; use EightshiftForms\Settings\Settings\SettingsGeneral; use EightshiftFormsVendor\EightshiftLibs\AdminMenus\AbstractAdminSubMenu; @@ -186,13 +187,19 @@ protected function processAttributes($attr): array $formTitle = \esc_html__('No form title', 'eightshift-forms'); } + $settingsSidebarOutput = []; + foreach ($this->settingsAll->getSettingsSidebar($formId, $type) as $item) { + $sidebarType = $item['type'] ?? SettingsAll::SETTINGS_SIEDBAR_TYPE_GENERAL; + $settingsSidebarOutput[$sidebarType][] = $item; + } + return [ // translators: %s replaces the form name. 'adminSettingsPageTitle' => \sprintf(\esc_html__('Form settings: %s', 'eightshift-forms'), $formTitle), 'adminSettingsBackLink' => Helper::getListingPageUrl(), 'adminSettingsFormEditLink' => Helper::getFormEditPageUrl($formId), 'adminSettingsLink' => Helper::getSettingsPageUrl($formId, ''), - 'adminSettingsSidebar' => $this->settingsAll->getSettingsSidebar($formId, $type), + 'adminSettingsSidebar' => $settingsSidebarOutput, 'adminSettingsForm' => $this->settingsAll->getSettingsForm($formId, $type), 'adminSettingsType' => $type, ]; diff --git a/src/Blocks/components/admin-settings-section/admin-settings-section-admin.scss b/src/Blocks/components/admin-settings-section/admin-settings-section-admin.scss index 134cbba9f..f606c8323 100644 --- a/src/Blocks/components/admin-settings-section/admin-settings-section-admin.scss +++ b/src/Blocks/components/admin-settings-section/admin-settings-section-admin.scss @@ -13,12 +13,26 @@ &__sidebar { background-color: var(--global-colors-es-white); + height: 100vh; + overflow-x: hidden; > * { margin-bottom: var(--global-es-spacing-horizontal); + border-top: 1px solid var(--global-colors-es-ebb); + + &:first-child { + border-top: 0; + } } } + &__sidebar-label { + padding: 1rem 1.1rem 0.5rem; + font-size: 1.1rem; + font-weight: bold; + line-height: 1.2; + } + &__main { border-left: 1px solid var(--global-colors-es-ebb); @@ -87,7 +101,7 @@ } &:hover, - &:focus, { + &:focus { color: var(--global-colors-es-matisse); background-color: var(--global-colors-es-matisse-05); } diff --git a/src/Blocks/components/admin-settings/admin-settings.php b/src/Blocks/components/admin-settings/admin-settings.php index 3ca0ced63..28f044a73 100644 --- a/src/Blocks/components/admin-settings/admin-settings.php +++ b/src/Blocks/components/admin-settings/admin-settings.php @@ -13,6 +13,7 @@ echo Components::outputCssVariablesGlobal(); // phpcs:ignore Eightshift.Security.ComponentsEscape.OutputNotEscaped +$componentName = $manifest['componentName'] ?? ''; $componentClass = $manifest['componentClass'] ?? ''; $sectionClass = $manifestSection['componentClass'] ?? ''; @@ -47,30 +48,19 @@ -
"> -
"> - -
-
+ $adminSettingsSidebar, + 'sectionClass' => $sectionClass, + 'adminSettingsLink' => $adminSettingsLink, + 'adminSettingsType' => $adminSettingsType, + ] + ); + ?>
">
"> diff --git a/src/Blocks/components/admin-settings/partials/sidebar-section.php b/src/Blocks/components/admin-settings/partials/sidebar-section.php new file mode 100644 index 000000000..343e8197b --- /dev/null +++ b/src/Blocks/components/admin-settings/partials/sidebar-section.php @@ -0,0 +1,75 @@ + array_search($key2, $sortOrder, true); +}); + +foreach ($items as $key => $innerItems) { + switch ($key) { + case SettingsAll::SETTINGS_SIEDBAR_TYPE_INTEGRATION: + $sidebarTitle = __('Integrations', 'eightshift-forms'); + break; + case SettingsAll::SETTINGS_SIEDBAR_TYPE_TROUBLESHOOTING: + $sidebarTitle = __('Troubleshooting', 'eightshift-forms'); + break; + case SettingsAll::SETTINGS_SIEDBAR_TYPE_DEVELOP: + $sidebarTitle = __('Develop Mode', 'eightshift-forms'); + break; + default: + $sidebarTitle = __('General', 'eightshift-forms'); + break; + } + ?> + +
"> +
"> +
"> + +
+ +
+
+ + { + if (typeof esFormsLocalization === 'undefined') { + throw 'Your project is missing the global "esFormsLocalization" variable called from the enqueue script.'; + } + const { componentJsClass, componentCacheJsClass, @@ -17,6 +21,7 @@ domReady(() => { formSelector: selector, formSubmitRestApiUrl: esFormsLocalization.formSettingsSubmitRestApiUrl, formIsAdmin: true, + customFormParams: esFormsLocalization.customFormParams, }); form.init(); diff --git a/src/Blocks/components/form/assets/form.js b/src/Blocks/components/form/assets/form.js index ff7f9be06..37c92f328 100644 --- a/src/Blocks/components/form/assets/form.js +++ b/src/Blocks/components/form/assets/form.js @@ -62,15 +62,12 @@ export class Form { this.selectSelector = `${this.fieldSelector} select`; this.fileSelector = `${this.fieldSelector} input[type='file']`; - // State selectors. - this.CLASS_ACTIVE = FORM_SELECTORS.CLASS_ACTIVE; - this.CLASS_FILLED = FORM_SELECTORS.CLASS_FILLED; - this.CLASS_LOADING = FORM_SELECTORS.CLASS_LOADING; - this.CLASS_HAS_ERROR = FORM_SELECTORS.CLASS_HAS_ERROR; - // LocalStorage this.STORAGE_NAME = 'es-storage'; + // Custom fields params. + this.FORM_CUSTOM_FORM_PARAMS = options.customFormParams; + // Settings options. this.formDisableScrollToFieldOnError = options.formDisableScrollToFieldOnError ?? true; this.formDisableScrollToGlobalMessageOnSuccess = options.formDisableScrollToGlobalMessageOnSuccess ?? true; @@ -79,6 +76,7 @@ export class Form { this.hideGlobalMessageTimeout = options.hideGlobalMessageTimeout ?? 6000; this.hideLoadingStateTimeout = options.hideLoadingStateTimeout ?? 600; this.fileCustomRemoveLabel = options.fileCustomRemoveLabel ?? ''; + this.formServerErrorMsg = options.formServerErrorMsg ?? ''; this.captcha = options.captcha ?? ''; this.storageConfig = options.storageConfig ?? ''; @@ -87,14 +85,6 @@ export class Form { this.customTextareas = []; this.customSelects = []; this.customFiles = []; - - // Data attributes. - this.DATA_ATTR_FORM_TYPE = FORM_DATA_ATTRIBUTES.DATA_ATTR_FORM_TYPE; - this.DATA_ATTR_FIELD_ID = FORM_DATA_ATTRIBUTES.DATA_ATTR_FIELD_ID; - this.DATA_ATTR_FORM_POST_ID = FORM_DATA_ATTRIBUTES.DATA_ATTR_FORM_POST_ID; - this.DATA_ATTR_TRACKING_EVENT_NAME = FORM_DATA_ATTRIBUTES.DATA_ATTR_TRACKING_EVENT_NAME; - this.DATA_ATTR_TRACKING = FORM_DATA_ATTRIBUTES.DATA_ATTR_TRACKING; - this.DATA_ATTR_TRACKING_SELECT_LABEL = FORM_DATA_ATTRIBUTES.DATA_ATTR_TRACKING_SELECT_LABEL; } // Init all actions. @@ -122,7 +112,7 @@ export class Form { } // Get form ID. - const formId = element.getAttribute(this.DATA_ATTR_FORM_POST_ID); + const formId = element.getAttribute(FORM_DATA_ATTRIBUTES.DATA_ATTR_FORM_POST_ID); // All fields selectors. const inputs = element.querySelectorAll(this.inputSelector); @@ -197,7 +187,7 @@ export class Form { }, body: JSON.stringify({ token, - formId: element.getAttribute(this.DATA_ATTR_FORM_POST_ID), + formId: element.getAttribute(FORM_DATA_ATTRIBUTES.DATA_ATTR_FORM_POST_ID), }), credentials: 'same-origin', redirect: 'follow', @@ -247,7 +237,7 @@ export class Form { const formData = this.getFormData(element, singleSubmit); - const formType = element.getAttribute(this.DATA_ATTR_FORM_TYPE); + const formType = element.getAttribute(FORM_DATA_ATTRIBUTES.DATA_ATTR_FORM_TYPE); // Populate body data. const body = { @@ -359,6 +349,17 @@ export class Form { // Dispatch event. this.dispatchFormEvent(element, FORM_EVENTS.AFTER_FORM_SUBMIT_END); + }) + .catch(() => { + this.setGlobalMsg(element, this.formServerErrorMsg, 'error'); + + // Remove loader. + this.hideLoader(element); + + // Hide global msg in any case after some time. + setTimeout(() => { + this.hideGlobalMsg(element); + }, parseInt(this.hideGlobalMessageTimeout, 10)); }); }; @@ -371,7 +372,7 @@ export class Form { // Check if we are saving group items in one key. if (groups.length && !singleSubmit) { for (const [key, group] of Object.entries(groups)) { // eslint-disable-line no-unused-vars - const groupId = group.getAttribute(this.DATA_ATTR_FIELD_ID); + const groupId = group.getAttribute(FORM_DATA_ATTRIBUTES.DATA_ATTR_FIELD_ID); const groupInner = group.querySelectorAll(` ${this.groupInnerSelector} input, ${this.groupInnerSelector} select, @@ -409,8 +410,7 @@ export class Form { textarea:not(${this.groupInnerSelector} textarea) `); - const formType = element.getAttribute(this.DATA_ATTR_FORM_TYPE); - const formAction = element.getAttribute('action'); + const formType = element.getAttribute(FORM_DATA_ATTRIBUTES.DATA_ATTR_FORM_TYPE); // If single submit override items and pass only one item to submit. if (singleSubmit) { @@ -483,43 +483,43 @@ export class Form { } // Add form ID field. - formData.append('es-form-post-id', JSON.stringify({ - value: element.getAttribute(this.DATA_ATTR_FORM_POST_ID), + formData.append(this.FORM_CUSTOM_FORM_PARAMS.postId, JSON.stringify({ + value: element.getAttribute(FORM_DATA_ATTRIBUTES.DATA_ATTR_FORM_POST_ID), type: 'hidden', })); // Add form type field. - formData.append('es-form-type', JSON.stringify({ + formData.append(this.FORM_CUSTOM_FORM_PARAMS.type, JSON.stringify({ value: formType, type: 'hidden', })); // Add form action field. - formData.append('action', JSON.stringify({ - value: formAction, + formData.append(this.FORM_CUSTOM_FORM_PARAMS.action, JSON.stringify({ + value: element.getAttribute('action'), type: 'hidden', })); // Add additional options for HubSpot only. if (formType === 'hubspot' && !this.formIsAdmin) { - formData.append('es-form-hubspot-cookie', JSON.stringify({ + formData.append(this.FORM_CUSTOM_FORM_PARAMS.hubspotCookie, JSON.stringify({ value: cookies.getCookie('hubspotutk'), type: 'hidden', })); - formData.append('es-form-hubspot-page-name', JSON.stringify({ + formData.append(this.FORM_CUSTOM_FORM_PARAMS.hubspotPageName, JSON.stringify({ value: document.title, type: 'hidden', })); - formData.append('es-form-hubspot-page-url', JSON.stringify({ + formData.append(this.FORM_CUSTOM_FORM_PARAMS.hubspotPageUrl, JSON.stringify({ value: window.location.href, type: 'hidden', })); } if (singleSubmit && this.formIsAdmin) { - formData.append('es-form-single-submit', JSON.stringify({ + formData.append(this.FORM_CUSTOM_FORM_PARAMS.singleSubmit, JSON.stringify({ value: 'true', type: 'hidden', })); @@ -528,7 +528,7 @@ export class Form { // Set localStorage to hidden field. const storage = this.getLocalStorage(); if (storage) { - formData.append('es-form-storage', JSON.stringify({ + formData.append(this.FORM_CUSTOM_FORM_PARAMS.storage, JSON.stringify({ value: storage, type: 'hidden', })); @@ -543,7 +543,7 @@ export class Form { for (const [key] of Object.entries(fields)) { const item = element.querySelector(`${this.errorSelector}[data-id="${key}"]`); - item?.closest(this.fieldSelector).classList.add(this.CLASS_HAS_ERROR); + item?.closest(this.fieldSelector).classList.add(FORM_SELECTORS.CLASS_HAS_ERROR); if (item !== null) { item.innerHTML = fields[key]; @@ -563,7 +563,7 @@ export class Form { if (this.formResetOnSuccess) { element.reset(); - const formId = element.getAttribute(this.DATA_ATTR_FORM_POST_ID); + const formId = element.getAttribute(FORM_DATA_ATTRIBUTES.DATA_ATTR_FORM_POST_ID); // Unset the choices in the submitted form. if (this.customSelects[formId]) { @@ -582,9 +582,9 @@ export class Form { const fields = element.querySelectorAll(this.fieldSelector); [...fields].forEach((item) => { - item.classList.remove(this.CLASS_FILLED); - item.classList.remove(this.CLASS_ACTIVE); - item.classList.remove(this.CLASS_HAS_ERROR); + item.classList.remove(FORM_SELECTORS.CLASS_FILLED); + item.classList.remove(FORM_SELECTORS.CLASS_ACTIVE); + item.classList.remove(FORM_SELECTORS.CLASS_HAS_ERROR); }); // Remove focus from last input. @@ -603,7 +603,7 @@ export class Form { }); // Reset all error classes on fields. - element.querySelectorAll(`.${this.CLASS_HAS_ERROR}`).forEach((element) => element.classList.remove(this.CLASS_HAS_ERROR)); + element.querySelectorAll(`.${FORM_SELECTORS.CLASS_HAS_ERROR}`).forEach((element) => element.classList.remove(FORM_SELECTORS.CLASS_HAS_ERROR)); this.unsetGlobalMsg(element); }; @@ -612,13 +612,13 @@ export class Form { showLoader = (element) => { const loader = element.querySelector(this.loaderSelector); - element?.classList?.add(this.CLASS_LOADING); + element?.classList?.add(FORM_SELECTORS.CLASS_LOADING); if (!loader) { return; } - loader.classList.add(this.CLASS_ACTIVE); + loader.classList.add(FORM_SELECTORS.CLASS_ACTIVE); }; // Hide loader. @@ -626,13 +626,13 @@ export class Form { const loader = element.querySelector(this.loaderSelector); setTimeout(() => { - element?.classList?.remove(this.CLASS_LOADING); + element?.classList?.remove(FORM_SELECTORS.CLASS_LOADING); if (!loader) { return; } - loader.classList.remove(this.CLASS_ACTIVE); + loader.classList.remove(FORM_SELECTORS.CLASS_ACTIVE); }, parseInt(this.hideLoadingStateTimeout, 10)); }; @@ -648,7 +648,7 @@ export class Form { return; } - messageContainer.classList.add(this.CLASS_ACTIVE); + messageContainer.classList.add(FORM_SELECTORS.CLASS_ACTIVE); messageContainer.dataset.status = status; messageContainer.innerHTML = `${msg}`; @@ -666,7 +666,7 @@ export class Form { return; } - messageContainer.classList.remove(this.CLASS_ACTIVE); + messageContainer.classList.remove(FORM_SELECTORS.CLASS_ACTIVE); messageContainer.dataset.status = ''; messageContainer.innerHTML = ''; } @@ -679,12 +679,12 @@ export class Form { return; } - messageContainer.classList.remove(this.CLASS_ACTIVE); + messageContainer.classList.remove(FORM_SELECTORS.CLASS_ACTIVE); } // Submit GTM event. gtmSubmit(element) { - const eventName = element.getAttribute(this.DATA_ATTR_TRACKING_EVENT_NAME); + const eventName = element.getAttribute(FORM_DATA_ATTRIBUTES.DATA_ATTR_TRACKING_EVENT_NAME); if (eventName) { const gtmData = this.getGtmData(element, eventName); @@ -698,7 +698,7 @@ export class Form { // Build GTM data for the data layer. getGtmData(element, eventName) { - const items = element.querySelectorAll(`[${this.DATA_ATTR_TRACKING}]`); + const items = element.querySelectorAll(`[${FORM_DATA_ATTRIBUTES.DATA_ATTR_TRACKING}]`); const dataTemp = {}; if (!items.length) { @@ -706,7 +706,7 @@ export class Form { } [...items].forEach((item) => { - const tracking = item.getAttribute(this.DATA_ATTR_TRACKING); + const tracking = item.getAttribute(FORM_DATA_ATTRIBUTES.DATA_ATTR_TRACKING); if (tracking) { const {type, checked} = item; @@ -724,7 +724,7 @@ export class Form { } // Check if you have this data attr and if so use select label. - if (item.hasAttribute(this.DATA_ATTR_TRACKING_SELECT_LABEL)) { + if (item.hasAttribute(FORM_DATA_ATTRIBUTES.DATA_ATTR_TRACKING_SELECT_LABEL)) { dataTemp[tracking] = item.selectedOptions[0].label; return; } @@ -866,11 +866,11 @@ export class Form { // On add one file. myDropzone.on("addedfile", (file) => { setTimeout(() => { - file.previewTemplate.classList.add(this.CLASS_ACTIVE); + file.previewTemplate.classList.add(FORM_SELECTORS.CLASS_ACTIVE); }, 200); setTimeout(() => { - file.previewTemplate.classList.add(this.CLASS_FILLED); + file.previewTemplate.classList.add(FORM_SELECTORS.CLASS_FILLED); }, 1200); }); @@ -910,20 +910,20 @@ export class Form { case 'checkbox': case 'radio': if (input.checked) { - input.closest(this.fieldSelector).classList.add(this.CLASS_FILLED); + input.closest(this.fieldSelector).classList.add(FORM_SELECTORS.CLASS_FILLED); } break; case 'select-custom': { const customSelect = input.config.choices; if (customSelect.some((item) => item.selected === true && item.value !== '')) { - input.passedElement.element.closest(this.fieldSelector).classList.add(this.CLASS_FILLED); + input.passedElement.element.closest(this.fieldSelector).classList.add(FORM_SELECTORS.CLASS_FILLED); } break; } default: if (input.value && input.value.length) { - input.closest(this.fieldSelector).classList.add(this.CLASS_FILLED); + input.closest(this.fieldSelector).classList.add(FORM_SELECTORS.CLASS_FILLED); } break; } @@ -931,7 +931,7 @@ export class Form { // On Focus event for regular fields. onFocusEvent = (event) => { - event.target.closest(this.fieldSelector).classList.add(this.CLASS_ACTIVE); + event.target.closest(this.fieldSelector).classList.add(FORM_SELECTORS.CLASS_ACTIVE); }; // On Blur generic method. Check for length of value. @@ -970,10 +970,10 @@ export class Form { } if (condition) { - field.classList.remove(this.CLASS_ACTIVE); - field.classList.add(this.CLASS_FILLED); + field.classList.remove(FORM_SELECTORS.CLASS_ACTIVE); + field.classList.add(FORM_SELECTORS.CLASS_FILLED); } else { - field.classList.remove(this.CLASS_ACTIVE, this.CLASS_FILLED); + field.classList.remove(FORM_SELECTORS.CLASS_ACTIVE, FORM_SELECTORS.CLASS_FILLED); } }; @@ -989,7 +989,7 @@ export class Form { // Regular submit. element.removeEventListener('submit', this.onFormSubmit); - const formId = element.getAttribute(this.DATA_ATTR_FORM_POST_ID); + const formId = element.getAttribute(FORM_DATA_ATTRIBUTES.DATA_ATTR_FORM_POST_ID); const inputs = element.querySelectorAll(this.inputSelector); const textareas = element.querySelectorAll(this.textareaSelector); diff --git a/src/Blocks/components/form/assets/index.js b/src/Blocks/components/form/assets/index.js index 1d287c71a..9bced4315 100644 --- a/src/Blocks/components/form/assets/index.js +++ b/src/Blocks/components/form/assets/index.js @@ -34,8 +34,10 @@ function initAll() { formDisableScrollToGlobalMessageOnSuccess: esFormsLocalization.formDisableScrollToGlobalMessageOnSuccess, formResetOnSuccess: esFormsLocalization.formResetOnSuccess, fileCustomRemoveLabel: esFormsLocalization.fileCustomRemoveLabel, + formServerErrorMsg: esFormsLocalization.formServerErrorMsg, captcha: esFormsLocalization.captcha, storageConfig: esFormsLocalization.storageConfig, + customFormParams: esFormsLocalization.customFormParams, }); // Run forms. @@ -47,11 +49,14 @@ function initAll() { redirectionTimeout: form.redirectionTimeout, hideGlobalMessageTimeout: form.hideGlobalMessageTimeout, captchaSiteKey: esFormsLocalization.captcha, + formServerErrorMsg: esFormsLocalization.formServerErrorMsg, files: form.files, customSelects: form.customSelects, customFiles: form.customFiles, customTextareas: form.customTextareas, storageConfig: form.storageConfig, + customFormParams: form.FORM_CUSTOM_FORM_PARAMS, + storageName: form.STORAGE_NAME, init: () => { form.init(); }, diff --git a/src/Blocks/custom/active-campaign/components/active-campaign-options.js b/src/Blocks/custom/active-campaign/components/active-campaign-options.js index 4e5f189f8..7d360165e 100644 --- a/src/Blocks/custom/active-campaign/components/active-campaign-options.js +++ b/src/Blocks/custom/active-campaign/components/active-campaign-options.js @@ -1,4 +1,4 @@ -/* global esFormsBlocksLocalization */ +/* global esFormsLocalization */ import React from 'react'; import { __ } from '@wordpress/i18n'; @@ -11,7 +11,7 @@ export const ActiveCampaignOptions = ({ postId }) => { settingsPageUrl, } = select(STORE_NAME).getSettings(); - const wpAdminUrl = esFormsBlocksLocalization.wpAdminUrl; + const wpAdminUrl = esFormsLocalization.wpAdminUrl; return ( diff --git a/src/Blocks/custom/form-selector/components/form-selector-editor.js b/src/Blocks/custom/form-selector/components/form-selector-editor.js index ccda859e5..8262039c0 100644 --- a/src/Blocks/custom/form-selector/components/form-selector-editor.js +++ b/src/Blocks/custom/form-selector/components/form-selector-editor.js @@ -27,11 +27,7 @@ export const FormSelectorEditor = ({ attributes, clientId }) => { const hasInnerBlocksCheck = useSelect((select) => { const { innerBlocks } = select('core/block-editor').getBlock(clientId); - if (!innerBlocks.length) { - return false; - } - - return true; + return innerBlocks.length; }); // If parent block has inner blocks set internal state. diff --git a/src/Blocks/custom/forms/components/forms-options.js b/src/Blocks/custom/forms/components/forms-options.js index 64646a229..28e501554 100644 --- a/src/Blocks/custom/forms/components/forms-options.js +++ b/src/Blocks/custom/forms/components/forms-options.js @@ -112,7 +112,7 @@ export const FormsOptions = ({ attributes, setAttributes, preview }) => { isLarge icon={icons.trash} onClick={removeItem} - label={__('Remove', 'eightshift-form')} + label={__('Remove', 'eightshift-forms')} style={{ marginTop: '0.2rem' }} />
@@ -210,7 +210,7 @@ export const FormsOptions = ({ attributes, setAttributes, preview }) => { setIsModalOpen(true); }} > - {__('Geolocation rules', 'eightshift-form')} + {__('Geolocation rules', 'eightshift-forms')} {geoRepeater?.length > 0 && @@ -229,7 +229,7 @@ export const FormsOptions = ({ attributes, setAttributes, preview }) => { {

{__('Geolocation rules allow you to display alternate forms based on the user\'s location.', 'eightshift-forms')}

{__('If no rules are added and the "Show form only if in countries" field is populated, the form will only be shown in these countries. Otherwise, the form is shown everywhere.', 'eightshift-forms')}

{geolocationApi && -

{__('You can find complete list of countries and regions on this', 'eightshift-forms')} {__('link', 'eightshift-form')}.

+

{__('You can find complete list of countries and regions on this', 'eightshift-forms')} {__('link', 'eightshift-forms')}.

}
@@ -248,7 +248,7 @@ export const FormsOptions = ({ attributes, setAttributes, preview }) => { icon={icons.add} onClick={addItem} > - {__('Add rule', 'eightshift-form')} + {__('Add rule', 'eightshift-forms')} {geoRepeater?.length > 0 && @@ -284,7 +284,7 @@ export const FormsOptions = ({ attributes, setAttributes, preview }) => { setAttributes({ formsFormGeolocationAlternatives: prevGeoRepeater }); setIsModalOpen(false); }}> - {__('Cancel', 'eightshift-form')} + {__('Cancel', 'eightshift-forms')}
} diff --git a/src/Blocks/custom/mailchimp/components/mailchimp-options.js b/src/Blocks/custom/mailchimp/components/mailchimp-options.js index d3b77b685..19a97a207 100644 --- a/src/Blocks/custom/mailchimp/components/mailchimp-options.js +++ b/src/Blocks/custom/mailchimp/components/mailchimp-options.js @@ -1,4 +1,4 @@ -/* global esFormsLocalizations */ +/* global esFormsLocalization */ import React from 'react'; import { __ } from '@wordpress/i18n'; @@ -11,7 +11,7 @@ export const MailchimpOptions = ({ postId }) => { settingsPageUrl, } = select(STORE_NAME).getSettings(); - const wpAdminUrl = esFormsLocalizations.wpAdminUrl; + const wpAdminUrl = esFormsLocalization.wpAdminUrl; return ( diff --git a/src/Cache/SettingsCache.php b/src/Cache/SettingsCache.php index 87b6f38ca..21130fb34 100644 --- a/src/Cache/SettingsCache.php +++ b/src/Cache/SettingsCache.php @@ -19,6 +19,7 @@ use EightshiftForms\Integrations\Mailerlite\MailerliteClient; use EightshiftForms\Settings\SettingsHelper; use EightshiftForms\Settings\GlobalSettings\SettingsGlobalDataInterface; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; /** @@ -95,6 +96,7 @@ public function getSettingsSidebar(): array 'label' => \__('Clear cache', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_TROUBLESHOOTING, ]; } diff --git a/src/Enqueue/Admin/EnqueueAdmin.php b/src/Enqueue/Admin/EnqueueAdmin.php index 0a6b5cca8..862983054 100644 --- a/src/Enqueue/Admin/EnqueueAdmin.php +++ b/src/Enqueue/Admin/EnqueueAdmin.php @@ -11,6 +11,7 @@ namespace EightshiftForms\Enqueue\Admin; use EightshiftForms\Config\Config; +use EightshiftForms\Rest\Routes\AbstractBaseRoute; use EightshiftForms\Rest\Routes\CacheDeleteRoute; use EightshiftForms\Rest\Routes\FormSettingsSubmitRoute; use EightshiftFormsVendor\EightshiftLibs\Manifest\ManifestInterface; @@ -76,6 +77,7 @@ protected function getLocalizations(): array return [ 'esFormsLocalization' => [ + 'customFormParams' => AbstractBaseRoute::CUSTOM_FORM_PARAMS, 'formSettingsSubmitRestApiUrl' => $restRoutesPath . FormSettingsSubmitRoute::ROUTE_SLUG, 'clearCacheRestUrl' => $restRoutesPath . CacheDeleteRoute::ROUTE_SLUG, ] diff --git a/src/Enqueue/Blocks/EnqueueBlocks.php b/src/Enqueue/Blocks/EnqueueBlocks.php index 06fdcc036..71fe652e5 100644 --- a/src/Enqueue/Blocks/EnqueueBlocks.php +++ b/src/Enqueue/Blocks/EnqueueBlocks.php @@ -14,10 +14,12 @@ use EightshiftForms\Geolocation\SettingsGeolocation; use EightshiftForms\Hooks\Filters; use EightshiftForms\Hooks\Variables; +use EightshiftForms\Rest\Routes\AbstractBaseRoute; use EightshiftForms\Rest\Routes\GeolocationCountriesRoute; use EightshiftForms\Settings\Settings\SettingsGeneral; use EightshiftForms\Settings\SettingsHelper; use EightshiftForms\Tracking\TrackingInterface; +use EightshiftForms\Troubleshooting\SettingsTroubleshooting; use EightshiftForms\Validation\SettingsCaptcha; use EightshiftForms\Validation\ValidatorInterface; use EightshiftFormsVendor\EightshiftLibs\Enqueue\Blocks\AbstractEnqueueBlocks; @@ -158,7 +160,9 @@ public function getAssetsVersion(): string */ protected function getLocalizations(): array { - $output = []; + $output = [ + 'customFormParams' => AbstractBaseRoute::CUSTOM_FORM_PARAMS, + ]; // Admin part. if (\is_admin()) { @@ -203,33 +207,33 @@ protected function getLocalizations(): array // Frontend part. $restRoutesPath = \rest_url() . Config::getProjectRoutesNamespace() . '/' . Config::getProjectRoutesVersion(); - $hideGlobalMsgTimeoutFilterName = Filters::getBlockFilterName('form', 'hideGlobalMsgTimeout'); - $redirectionTimeoutFilterName = Filters::getBlockFilterName('form', 'redirectionTimeout'); - $previewRemoveLabelFilterName = Filters::getBlockFilterName('file', 'previewRemoveLabel'); - $hideLoadingStateTimeoutFilterName = Filters::getBlockFilterName('form', 'hideLoadingStateTimeout'); - - $output = [ - 'formSubmitRestApiUrl' => $restRoutesPath . '/form-submit', - 'hideGlobalMessageTimeout' => \apply_filters($hideGlobalMsgTimeoutFilterName, 6000), - 'redirectionTimeout' => \apply_filters($redirectionTimeoutFilterName, 300), - 'hideLoadingStateTimeout' => \apply_filters($hideLoadingStateTimeoutFilterName, 600), - 'fileCustomRemoveLabel' => \apply_filters($previewRemoveLabelFilterName, \esc_html__('Remove', 'eightshift-forms')), - 'formDisableScrollToFieldOnError' => $this->isCheckboxOptionChecked( - SettingsGeneral::SETTINGS_GENERAL_DISABLE_SCROLL_TO_FIELD_ON_ERROR, - SettingsGeneral::SETTINGS_GENERAL_DISABLE_SCROLL_KEY - ), - 'formDisableScrollToGlobalMessageOnSuccess' => $this->isCheckboxOptionChecked( - SettingsGeneral::SETTINGS_GENERAL_DISABLE_SCROLL_TO_GLOBAL_MESSAGE_ON_SUCCESS, - SettingsGeneral::SETTINGS_GENERAL_DISABLE_SCROLL_KEY - ), - 'formDisableAutoInit' => $this->isCheckboxOptionChecked( - SettingsGeneral::SETTINGS_GENERAL_DISABLE_AUTOINIT_ENQUEUE_SCRIPT_KEY, - SettingsGeneral::SETTINGS_GENERAL_DISABLE_DEFAULT_ENQUEUE_KEY - ), - 'formResetOnSuccess' => !Variables::isDevelopMode(), - 'captcha' => '', - 'storageConfig' => '', - ]; + $hideGlobalMessageTimeout = Filters::getBlockFilterName('form', 'hideGlobalMsgTimeout'); + $redirectionTimeout = Filters::getBlockFilterName('form', 'redirectionTimeout'); + $hideLoadingStateTimeout = Filters::getBlockFilterName('form', 'hideLoadingStateTimeout'); + $fileCustomRemoveLabel = Filters::getBlockFilterName('file', 'previewRemoveLabel'); + + $output['formSubmitRestApiUrl'] = $restRoutesPath . '/form-submit'; + $output['hideGlobalMessageTimeout'] = \apply_filters($hideGlobalMessageTimeout, 6000); + $output['redirectionTimeout'] = \apply_filters($redirectionTimeout, 300); + $output['hideLoadingStateTimeout'] = \apply_filters($hideLoadingStateTimeout, 600); + $output['fileCustomRemoveLabel'] = \apply_filters($fileCustomRemoveLabel, \esc_html__('Remove', 'eightshift-forms')); + $output['formDisableScrollToFieldOnError'] = $this->isCheckboxOptionChecked( + SettingsGeneral::SETTINGS_GENERAL_DISABLE_SCROLL_TO_FIELD_ON_ERROR, + SettingsGeneral::SETTINGS_GENERAL_DISABLE_SCROLL_KEY + ); + $output['formDisableScrollToGlobalMessageOnSuccess'] = $this->isCheckboxOptionChecked( + SettingsGeneral::SETTINGS_GENERAL_DISABLE_SCROLL_TO_GLOBAL_MESSAGE_ON_SUCCESS, + SettingsGeneral::SETTINGS_GENERAL_DISABLE_SCROLL_KEY + ); + $output['formDisableAutoInit'] = $this->isCheckboxOptionChecked( + SettingsGeneral::SETTINGS_GENERAL_DISABLE_AUTOINIT_ENQUEUE_SCRIPT_KEY, + SettingsGeneral::SETTINGS_GENERAL_DISABLE_DEFAULT_ENQUEUE_KEY + ); + $output['formResetOnSuccess'] = !$this->isCheckboxOptionChecked(SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_SKIP_RESET_KEY, SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_DEBUGGING_KEY); + $output['formServerErrorMsg'] = \esc_html__('A server error occurred while submitting your form. Please try again.', 'eightshift-forms'); + + $output['captcha'] = ''; + $output['storageConfig'] = ''; // Check if Captcha data is set and valid. $isCaptchaSettingsGlobalValid = \apply_filters(SettingsCaptcha::FILTER_SETTINGS_GLOBAL_IS_VALID_NAME, false); diff --git a/src/General/General.php b/src/General/General.php index 53d6dc896..90d63567b 100644 --- a/src/General/General.php +++ b/src/General/General.php @@ -18,6 +18,13 @@ */ class General implements ServiceInterface { + /** + * Default timeout for all http requests. + * + * @var int + */ + public const HTTP_REQUEST_TIMEOUT_DEFAULT = 30; + /** * Register all hooks. * @@ -37,8 +44,10 @@ public function register(): void */ public function getHttpRequestArgs(array $args): array { - $args['timeout'] = 30; + $filterName = Filters::getGeneralSettingsFilterName('httpRequestTimeout'); + + $args['timeout'] = \apply_filters($filterName, self::HTTP_REQUEST_TIMEOUT_DEFAULT); - return \apply_filters(Filters::getGeneralSettingsFilterName('httpRequestArgs'), $args); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase + return $args; } } diff --git a/src/Geolocation/Geolocation.php b/src/Geolocation/Geolocation.php index b024fbdb5..87ddc8ed7 100644 --- a/src/Geolocation/Geolocation.php +++ b/src/Geolocation/Geolocation.php @@ -15,6 +15,7 @@ use EightshiftForms\Hooks\Filters; use EightshiftForms\Hooks\Variables; use EightshiftForms\Settings\SettingsHelper; +use EightshiftForms\Troubleshooting\SettingsTroubleshooting; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; use Throwable; @@ -23,6 +24,9 @@ */ class Geolocation implements ServiceInterface, GeolocationInterface { + /** + * Use general helper trait. + */ use SettingsHelper; /** @@ -111,15 +115,20 @@ public function isUserGeolocated(string $formId, array $defaultLocations, array return $formId; } + $useLogger = $this->isCheckboxOptionChecked(SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_LOG_MODE_KEY, SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_DEBUGGING_KEY); + // Add ability to disable geolocation from external source. (Generaly used for GDPR). $filterName = Filters::getGeolocationFilterName('disable'); if (\has_filter($filterName) && \apply_filters($filterName, null)) { - Helper::logger([ - 'geolocation' => 'Filter disabled active, skip geolocation.', - 'formIdOriginal' => $formId, - 'formIdUsed' => $formId, - 'userLocation' => '', - ]); + if ($useLogger) { + Helper::logger([ + 'geolocation' => 'Disable filter is active, skipping geolocation.', + 'formIdOriginal' => $formId, + 'formIdUsed' => $formId, + 'userLocation' => '', + ]); + } + return $formId; } @@ -155,12 +164,14 @@ function ($geo) use ($userLocation) { // If additional locations match output that new form. if ($matchAdditionalLocations) { - Helper::logger([ - 'geolocation' => 'Locations exists, locations match. Outputing new form.', - 'formIdOriginal' => $formId, - 'formIdUsed' => $matchAdditionalLocations['formId'] ?? '', - 'userLocation' => $userLocation, - ]); + if ($useLogger) { + Helper::logger([ + 'geolocation' => 'Locations exists, locations match. Outputing new form.', + 'formIdOriginal' => $formId, + 'formIdUsed' => $matchAdditionalLocations['formId'] ?? '', + 'userLocation' => $userLocation, + ]); + } return $matchAdditionalLocations['formId'] ?? ''; } } @@ -181,32 +192,38 @@ function ($location) use ($userLocation) { // If default locations match output that new form. if ($matchDefaultLocations) { + if ($useLogger) { + Helper::logger([ + 'geolocation' => 'Locations don\'t match or exist, default location selected. Outputting new form.', + 'formIdOriginal' => $formId, + 'formIdUsed' => $formId, + 'userLocation' => $userLocation, + ]); + } + return $formId; + } + + // If we have set default locations but no match return empty form. + if ($useLogger) { Helper::logger([ - 'geolocation' => 'Locations doesn\'t match or exist, default location match. Outputing new form.', + 'geolocation' => 'Locations don\'t exists, default location doesn\'t match. Outputting nothing.', 'formIdOriginal' => $formId, - 'formIdUsed' => $formId, + 'formIdUsed' => '', 'userLocation' => $userLocation, ]); - return $formId; } + return ''; + } - // If we have set default locations but no match return empty form. + // Final fallback if the user has no locations, no default locations or they didn't match. Just return the current form. + if ($useLogger) { Helper::logger([ - 'geolocation' => 'Locations doesn\'t exists, default location doesn\'t match. Outputing nothing.', + 'geolocation' => 'Final fallback that returns the current form. Outputing the original form.', 'formIdOriginal' => $formId, - 'formIdUsed' => '', + 'formIdUsed' => $formId, 'userLocation' => $userLocation, ]); - return ''; } - - // Final fallback if the user has no locations, no default locations or they didn't match. Just return the current form. - Helper::logger([ - 'geolocation' => 'Final fallback that returns the current form. Outputing the original form.', - 'formIdOriginal' => $formId, - 'formIdUsed' => $formId, - 'userLocation' => $userLocation, - ]); return $formId; } diff --git a/src/Geolocation/SettingsGeolocation.php b/src/Geolocation/SettingsGeolocation.php index 6c99a0358..6007f6b2a 100644 --- a/src/Geolocation/SettingsGeolocation.php +++ b/src/Geolocation/SettingsGeolocation.php @@ -11,6 +11,7 @@ namespace EightshiftForms\Geolocation; use EightshiftForms\Hooks\Filters; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\Settings\SettingsDataInterface; use EightshiftForms\Settings\SettingsHelper; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; @@ -83,6 +84,7 @@ public function getSettingsSidebar(): array 'label' => \__('Geolocation', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_GENERAL, ]; } diff --git a/src/Helpers/Helper.php b/src/Helpers/Helper.php index cc52e4864..14d36a108 100644 --- a/src/Helpers/Helper.php +++ b/src/Helpers/Helper.php @@ -3,7 +3,7 @@ /** * Trait that holds all generic helpers. * - * @package EightshiftLibs\Helpers + * @package EightshiftForms\Helpers */ declare(strict_types=1); @@ -14,7 +14,6 @@ use EightshiftForms\AdminMenus\FormSettingsAdminSubMenu; use EightshiftForms\AdminMenus\FormListingAdminSubMenu; use EightshiftForms\CustomPostType\Forms; -use EightshiftForms\Hooks\Variables; use EightshiftForms\Settings\Settings\SettingsGeneral; /** @@ -197,13 +196,16 @@ public static function getFormNames(string $formId): string */ public static function logger(array $message): void { - if (Variables::isLogMode()) { - $wpContentDir = \defined('WP_CONTENT_DIR') ? \WP_CONTENT_DIR : ''; + $wpContentDir = \defined('WP_CONTENT_DIR') ? \WP_CONTENT_DIR : ''; - if (!empty($wpContentDir)) { - $message['time'] = \gmdate("Y-m-d H:i:s"); - \error_log((string) \wp_json_encode($message) . "\n -------------------------------------", 3, \WP_CONTENT_DIR . '/eightshift-forms-debug.log'); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log + if (!empty($wpContentDir)) { + $message['time'] = \gmdate("Y-m-d H:i:s"); + + if (isset($message['files'])) { + unset($message['files']); } + + \error_log((string) \wp_json_encode($message) . "\n -------------------------------------", 3, \WP_CONTENT_DIR . '/eightshift-forms-debug.log'); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log } } diff --git a/src/Hooks/Filters.php b/src/Hooks/Filters.php index bfad018a4..21fed0067 100644 --- a/src/Hooks/Filters.php +++ b/src/Hooks/Filters.php @@ -31,6 +31,7 @@ use EightshiftForms\Settings\Settings\SettingsGeneral; use EightshiftForms\Settings\Settings\SettingsLocation; use EightshiftForms\Settings\Settings\SettingsTest; +use EightshiftForms\Troubleshooting\SettingsTroubleshooting; use EightshiftForms\Validation\SettingsCaptcha; use EightshiftForms\Validation\SettingsValidation; @@ -123,7 +124,7 @@ class Filters 'global' => SettingsClearbit::FILTER_SETTINGS_GLOBAL_NAME, 'settingsSidebar' => SettingsClearbit::FILTER_SETTINGS_SIDEBAR_NAME, 'fields' => Goodbits::FILTER_FORM_FIELDS_NAME, - 'icon' => '', + 'icon' => '', 'integration' => [ SettingsHubspot::SETTINGS_TYPE_KEY => [ 'use' => SettingsHubspot::SETTINGS_HUBSPOT_USE_CLEARBIT_KEY, @@ -150,6 +151,12 @@ class Filters 'settingsSidebar' => SettingsGeolocation::FILTER_SETTINGS_SIDEBAR_NAME, 'icon' => '', ], + SettingsTroubleshooting::SETTINGS_TYPE_KEY => [ + 'global' => SettingsTroubleshooting::FILTER_SETTINGS_GLOBAL_NAME, + 'settingsSidebar' => SettingsTroubleshooting::FILTER_SETTINGS_SIDEBAR_NAME, + 'valid' => SettingsTroubleshooting::FILTER_SETTINGS_IS_VALID_NAME, + 'icon' => '', + ], SettingsTest::SETTINGS_TYPE_KEY => [ 'global' => SettingsTest::FILTER_SETTINGS_GLOBAL_NAME, 'settingsSidebar' => SettingsTest::FILTER_SETTINGS_SIDEBAR_NAME, @@ -268,7 +275,7 @@ class Filters 'failMimetypeValidationWhenFileNotOnFS' => 'force_mimetype_from_fs', ], 'general' => [ - 'httpRequestArgs' => 'http_request_args', + 'httpRequestTimeout' => 'http_request_timeout', ], ]; diff --git a/src/Hooks/FiltersGeneral.md b/src/Hooks/FiltersGeneral.md index fbd8835ce..b3bbe8b85 100644 --- a/src/Hooks/FiltersGeneral.md +++ b/src/Hooks/FiltersGeneral.md @@ -1,28 +1,24 @@ # Filters General This document will provide you with the code examples for forms filters used in general. -## Change http request arguments -This filter can be used to change CURL timeout for the file upload if you have to upload large files. +## Change http request timeout +This filter can be used to change the cURL timeout for the file upload, useful if you have to upload large files. **Filter name:** -`es_forms_general_http_request_args` +`es_forms_general_http_request_timeout` **Filter example:** ```php -// Return http request args. -add_filter('es_forms_general_http_request_args', [$this, 'getHttpRequestArgs']); +// Return the HTTP request timeout. +add_filter('es_forms_general_http_request_timeout', [$this, 'getHttpRequestTimeout']); /** - * Return http request args. + * Return the HTTP request timeout. * - * @param array $args Arguments from core. - * - * @return array + * @return int */ -public function getHttpRequestArgs(array $args): array +public function getHttpRequestTimeout(): int { - $args['timeout'] = 50; - - return $args; + return 50; } ``` diff --git a/src/Hooks/FiltersTracking.md b/src/Hooks/FiltersTracking.md index aa7c6f464..a68e650a8 100644 --- a/src/Hooks/FiltersTracking.md +++ b/src/Hooks/FiltersTracking.md @@ -40,33 +40,43 @@ This filter provides you with the ability to map you local storage tags got from **Filter example:** ```php // Map Hubspot fields with custom tags from local storage. -\add_filter('es_forms_integration_hubspot_local_storage_map', [$this, 'getIntegrationHubspotLocalStorageMap'], 10, 2); +\add_filter('es_forms_integration_hubspot_local_storage_map', [$this, 'getIntegrationHubspotLocalStorageMap'], 10, 3); /** * Map Hubspot fields with custom tags from local storage. * - * @param array $params Params from Hubspot integration. + * @param array $params Params from Hubspot integration prepared for output. * @param array $storage Data form storage. + * @param array $originalParams Original params with all custom fields. * * @return array */ -public function getIntegrationHubspotLocalStorageMap(array $params, array $storage): array +public function getIntegrationHubspotLocalStorageMap(array $params, array $storage, array $originalParams): array { if (!$storage) { return $params; } - foreach ($params as $key => $param) { - $name = $param['name'] ?? ''; + // Additional tags to allow. + $allowedTags = [ + 'utm_source' => true, + 'utm_content' => true, + 'utm_campaign' => true, + ]; - if (!$name) { - continue; - } + foreach ($storage as $key => $param) { + if (isset($allowedTags[$key]) && isset($originalParams[$key])) { + $name = $originalParams[$key]['name'] ?? ''; - if ($name === 'utm_source' || $name === 'utm_content' || $name === 'utm_campaign') { - if (isset($storage[$name])) { - $params[$key]['value'] = $storage[$name]; + if (!$name) { + continue; } + + $params[] = [ + 'name' => $name, + 'value' => $param, + 'objectTypeId' => $originalParams[$key]['objectTypeId'] ?? '', + ]; } } diff --git a/src/Hooks/Variables.md b/src/Hooks/Variables.md index 9a7954508..898f706ef 100644 --- a/src/Hooks/Variables.md +++ b/src/Hooks/Variables.md @@ -5,28 +5,12 @@ This document will provide you with the code examples for forms global variables ## Set forms to develop mode This variable will set forms to develop mode that will do the following actions: -* disable value removal after the form is successfully submitted. +* output new global settings for testing inputs ```php define('ES_DEVELOP_MODE', true); ``` -## Set forms to skip validation. - -This variable will set forms to skip validation when submitting. This is useful when adding a new integration or testing API responses. - -```php -define('ES_DEVELOP_MODE_SKIP_VALIDATION', true); -``` - -## Set forms to output log. - -This variable will set forms to output log file for all requests and responses. This is useful when adding a new integration or testing API responses. - -```php -define('ES_LOG_MODE', true); -``` - ## Set Hubspot api key This variable will set forms Hubspot api key and you will not be able to change it from the admin. diff --git a/src/Hooks/Variables.php b/src/Hooks/Variables.php index 707b8d521..204d47ef5 100644 --- a/src/Hooks/Variables.php +++ b/src/Hooks/Variables.php @@ -16,7 +16,7 @@ class Variables { /** - * Get forms mode. + * Get forms develop mode, this will output new global settings for testing inputs. * * @return bool */ @@ -25,26 +25,6 @@ public static function isDevelopMode(): bool return \defined('ES_DEVELOP_MODE') ? true : false; } - /** - * Get forms to skip validation used for development of integration. - * - * @return bool - */ - public static function skipFormValidation(): bool - { - return \defined('ES_DEVELOP_MODE_SKIP_VALIDATION') ? true : false; - } - - /** - * Get forms to log out requests/responses. - * - * @return bool - */ - public static function isLogMode(): bool - { - return \defined('ES_LOG_MODE') ? true : false; - } - /** * Get API Key for HubSpot. * diff --git a/src/Integrations/ActiveCampaign/ActiveCampaign.php b/src/Integrations/ActiveCampaign/ActiveCampaign.php index 440cc86ae..9d160e3b8 100644 --- a/src/Integrations/ActiveCampaign/ActiveCampaign.php +++ b/src/Integrations/ActiveCampaign/ActiveCampaign.php @@ -15,7 +15,6 @@ use EightshiftForms\Integrations\ActiveCampaign\ActiveCampaignClientInterface; use EightshiftForms\Integrations\MapperInterface; use EightshiftForms\Settings\SettingsHelper; -use EightshiftForms\Validation\ValidatorInterface; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; /** @@ -62,25 +61,14 @@ class ActiveCampaign extends AbstractFormBuilder implements MapperInterface, Ser */ private $activeCampaignClient; - /** - * Instance variable of ValidatorInterface data. - * - * @var ValidatorInterface - */ - private $validator; - /** * Create a new instance. * * @param ActiveCampaignClientInterface $activeCampaignClient Inject ActiveCampaign which holds ActiveCampaign connection data. - * @param ValidatorInterface $validator Inject ValidatorInterface which holds validation methods. */ - public function __construct( - ActiveCampaignClientInterface $activeCampaignClient, - ValidatorInterface $validator - ) { + public function __construct(ActiveCampaignClientInterface $activeCampaignClient) + { $this->activeCampaignClient = $activeCampaignClient; - $this->validator = $validator; } /** diff --git a/src/Integrations/ActiveCampaign/ActiveCampaignClient.php b/src/Integrations/ActiveCampaign/ActiveCampaignClient.php index ce339bb8a..cb5ec338d 100644 --- a/src/Integrations/ActiveCampaign/ActiveCampaignClient.php +++ b/src/Integrations/ActiveCampaign/ActiveCampaignClient.php @@ -13,7 +13,9 @@ use EightshiftForms\Hooks\Variables; use EightshiftForms\Integrations\ActiveCampaign\ActiveCampaignClientInterface; use EightshiftForms\Rest\ApiHelper; +use EightshiftForms\Rest\Routes\AbstractBaseRoute; use EightshiftForms\Settings\SettingsHelper; +use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; /** * ActiveCampaignClient integration class. @@ -118,75 +120,70 @@ public function getItem(string $itemId): array */ public function postApplication(string $itemId, array $params, array $files, string $formId): array { + $params = $this->prepareParams($params); + // Map body. $requestBody = [ - 'contact' => $this->prepareParams($params), + 'contact' => $params, ]; + $url = "{$this->getBaseUrl()}contacts"; + // Make an API request. - $response = \wp_remote_request( - "{$this->getBaseUrl()}contacts", + $response = \wp_remote_post( + $url, [ 'headers' => $this->getHeaders(), - 'method' => 'POST', 'body' => \wp_json_encode($requestBody), ] ); - // Output response details. - $details = $this->getApiReponseDetails($response); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsActiveCampaign::SETTINGS_TYPE_KEY, + $response, + $url, + $params, + $files, + $itemId, + $formId + ); + $code = $details['code']; $body = $details['body']; - // Bailout if wp error. - if (\is_wp_error($response)) { - return $this->getApiWpErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, + // On success return output. + if ($code >= 200 && $code <= 299) { + return $this->getApiSuccessOutput( $details, - $requestBody, - $response->get_error_message() + [ + 'contactId' => $body['contact']['id'], + ] ); } - switch ($code) { - case 200: - case 201: - return $this->getApiSuccessOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - [ - 'contactId' => $body['fieldValues'][0]['contact'], - ] - ); + // Filter different error outputs. + switch ($details['code']) { case 403: - return $this->getApiErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - $details, - $requestBody, - $this->getErrorMsg([ - [ - 'code' => 'activeCampaignForbidden', - ] - ]), - ); + $error = 'activeCampaignForbidden'; + break; case 500: - return $this->getApiErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - $details, - $requestBody, - $this->getErrorMsg([ - [ - 'code' => 'activeCampaign500', - ] - ]), - ); + $error = 'activeCampaign500'; + break; default: - return $this->getApiErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - $details, - $requestBody, - $this->getErrorMsg($body['errors'] ?? []), - ); + $error = $body['errors'] ?? []; + break; } + + // Output error. + return $this->getApiErrorOutput( + $details, + $this->getErrorMsg([ + [ + 'code' => $error, + ] + ]), + ); } /** @@ -216,44 +213,37 @@ public function postTag(string $tag, string $contactId): array ]; // Make request to map contact with tags. - $response = \wp_remote_request( - "{$this->getBaseUrl()}contactTags", + $url = "{$this->getBaseUrl()}contactTags"; + + $response = \wp_remote_post( + $url, [ 'headers' => $this->getHeaders(), - 'method' => 'POST', 'body' => \wp_json_encode($requestBody), ] ); - // Output response details. - $details = $this->getApiReponseDetails($response); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsActiveCampaign::SETTINGS_TYPE_KEY, + $response, + $url, + $requestBody + ); + $code = $details['code']; $body = $details['body']; - // Bailout on wp error. - if (\is_wp_error($response)) { - return $this->getApiWpErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - $details, - $requestBody, - $response->get_error_message() - ); + // On success return output. + if ($code >= 200 && $code <= 299) { + return $this->getApiSuccessOutput($details); } - switch ($code) { - case 200: - case 201: - return $this->getApiSuccessOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - ); - default: - return $this->getApiErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - $details, - $requestBody, - $this->getErrorMsg($body['errors'] ?? []), - ); - } + // Output error. + return $this->getApiErrorOutput( + $details, + $this->getErrorMsg($body), + ); } /** @@ -276,44 +266,36 @@ public function postList(string $list, string $contactId): array ]; // Make request to map contact with lists. - $response = \wp_remote_request( - "{$this->getBaseUrl()}contactLists", + $url = "{$this->getBaseUrl()}contactLists"; + + $response = \wp_remote_post( + $url, [ 'headers' => $this->getHeaders(), - 'method' => 'POST', 'body' => \wp_json_encode($requestBody), ] ); - // Output response details. - $details = $this->getApiReponseDetails($response); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsActiveCampaign::SETTINGS_TYPE_KEY, + $response, + $url + ); + $code = $details['code']; $body = $details['body']; - // Bailout on wp error. - if (\is_wp_error($response)) { - return $this->getApiWpErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - $details, - $requestBody, - $response->get_error_message() - ); + // On success return output. + if ($code >= 200 && $code <= 299) { + return $this->getApiSuccessOutput($details); } - switch ($code) { - case 200: - case 201: - return $this->getApiSuccessOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - ); - default: - return $this->getApiErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - $details, - $requestBody, - $this->getErrorMsg($body['errors'] ?? []), - ); - } + // Output error. + return $this->getApiErrorOutput( + $details, + $this->getErrorMsg($body), + ); } /** @@ -327,56 +309,48 @@ private function getExistingTagId(string $tag): string { $requestBody = []; + $url = "{$this->getBaseUrl()}tags"; + // Make api request to check if tag exists. - $response = \wp_remote_request( - "{$this->getBaseUrl()}tags", + $response = \wp_remote_get( + $url, [ 'headers' => $this->getHeaders(), - 'method' => 'GET', ] ); - // Output response details. - $details = $this->getApiReponseDetails($response); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsActiveCampaign::SETTINGS_TYPE_KEY, + $response, + $url + ); + $code = $details['code']; $body = $details['body']; - // Bailout on wp error. - if (\is_wp_error($response)) { - $this->getApiWpErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - $details, - $requestBody, - $response->get_error_message() + // On success return output. + if ($code >= 200 && $code <= 299) { + // Find tag id from array. + $tagId = \array_filter( + $body['tags'], + static function ($item) use ($tag) { + return $item['tag'] === $tag && $item['tagType'] === 'contact'; + } ); - return ''; - } + $tagId = \array_values($tagId); - switch ($code) { - case 200: - case 201: - // Find tag id from array. - $tagId = \array_filter( - $body['tags'], - static function ($item) use ($tag) { - return $item['tag'] === $tag && $item['tagType'] === 'contact'; - } - ); + return $tagId[0]['id'] ?? ''; + } - $tagId = \array_values($tagId); + // Output error. + $this->getApiErrorOutput( + $details, + $this->getErrorMsg($body), + ); - return $tagId[0]['id'] ?? ''; - default: - $this->getApiErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - $details, - $requestBody, - $this->getErrorMsg($body['errors'] ?? []), - ); - - return ''; - } + return ''; } /** @@ -396,64 +370,57 @@ private function createNewTag(string $tag): string ], ]; + $url = "{$this->getBaseUrl()}tags"; + // Make api request to create a new tag. - $response = \wp_remote_request( - "{$this->getBaseUrl()}tags", + $response = \wp_remote_post( + $url, [ 'headers' => $this->getHeaders(), - 'method' => 'POST', 'body' => \wp_json_encode($requestBody), ] ); - // Output response details. - $details = $this->getApiReponseDetails($response); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsActiveCampaign::SETTINGS_TYPE_KEY, + $response, + $url, + $requestBody + ); + $code = $details['code']; $body = $details['body']; - // Bailout on wp error. - if (\is_wp_error($response)) { - $this->getApiWpErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - $details, - $requestBody, - $response->get_error_message() - ); - - return ''; + // On success return output. + if ($code >= 200 && $code <= 299) { + return $body['id'] ?? ''; } - switch ($code) { - case 200: - case 201: - return $body['id'] ?? ''; - default: - $this->getApiErrorOutput( - SettingsActiveCampaign::SETTINGS_TYPE_KEY, - $details, - $requestBody, - $this->getErrorMsg($body['errors'] ?? []), - ); - - return ''; - } + // Output error. + $this->getApiErrorOutput( + $details, + $this->getErrorMsg($body), + ); + + return ''; } /** * Map service messages with our own. * - * @param array> $errors Additional errors got from the API. + * @param array $body API response body. * * @return string */ - private function getErrorMsg(array $errors): string + private function getErrorMsg(array $body): string { $msg = ''; $code = ''; - if ($errors && isset($errors[0])) { - $code = $errors[0]['code'] ?? ''; - $msg = $errors[0]['error'] ?? ''; + if (isset($body[0]['code'])) { + $code = $body[0]['code'] ?? ''; + $msg = $body[0]['error'] ?? ''; } if (!$msg) { @@ -498,17 +465,23 @@ private function getHeaders(): array */ private function getActiveCampaignListFields(string $listId) { + $url = "{$this->getBaseUrl()}forms/{$listId}"; + // Make api request to get form details. $response = \wp_remote_get( - "{$this->getBaseUrl()}forms/{$listId}", + $url, [ 'headers' => $this->getHeaders(), - 'timeout' => 60, ] ); - // Output response details. - $details = $this->getApiReponseDetails($response); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsActiveCampaign::SETTINGS_TYPE_KEY, + $response, + $url + ); + $body = $details['body']; // Bailout if fields are missing. @@ -560,16 +533,22 @@ private function getActiveCampaignListFields(string $listId) */ private function getActiveCampaignLists() { + $url = "{$this->getBaseUrl()}forms"; + $response = \wp_remote_get( - "{$this->getBaseUrl()}forms", + $url, [ 'headers' => $this->getHeaders(), - 'timeout' => 60, ] ); - // Output response details. - $details = $this->getApiReponseDetails($response); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsActiveCampaign::SETTINGS_TYPE_KEY, + $response, + $url + ); + $body = $details['body']; if (!isset($body['forms'])) { @@ -590,28 +569,23 @@ private function prepareParams(array $params): array { $output = []; + $customFields = \array_flip(Components::flattenArray(AbstractBaseRoute::CUSTOM_FORM_PARAMS)); $standardFields = \array_flip(ActiveCampaign::STANDARD_FIELDS); - // Remove unecesery storage field. - if (isset($params['es-form-storage'])) { - unset($params['es-form-storage']); - } - - // Remove unecesery action tags field. - if (isset($params['actionTags'])) { - unset($params['actionTags']); - } - - // Remove unecesery action lists field. - if (isset($params['actionLists'])) { - unset($params['actionLists']); - } - // Map params. foreach ($params as $key => $param) { $value = $param['value'] ?? ''; - if (!$value) { + if ($key === 'actionTags') { + continue; + } + + if ($key === 'actionLists') { + continue; + } + + // Remove unecesery fields. + if (isset($customFields[$key])) { continue; } diff --git a/src/Integrations/ActiveCampaign/SettingsActiveCampaign.php b/src/Integrations/ActiveCampaign/SettingsActiveCampaign.php index be60d85e9..8fb190263 100644 --- a/src/Integrations/ActiveCampaign/SettingsActiveCampaign.php +++ b/src/Integrations/ActiveCampaign/SettingsActiveCampaign.php @@ -18,7 +18,9 @@ use EightshiftForms\Integrations\ActiveCampaign\ActiveCampaignClientInterface; use EightshiftForms\Integrations\MapperInterface; use EightshiftForms\Settings\GlobalSettings\SettingsGlobalDataInterface; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\Settings\SettingsDataInterface; +use EightshiftForms\Troubleshooting\SettingsTroubleshootingDataInterface; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; /** @@ -95,18 +97,28 @@ class SettingsActiveCampaign implements SettingsDataInterface, SettingsGlobalDat */ private $activeCampaign; + /** + * Instance variable for Troubleshooting settings. + * + * @var SettingsTroubleshootingDataInterface + */ + protected $settingsTroubleshooting; + /** * Create a new instance. * * @param ActiveCampaignClientInterface $activeCampaignClient Inject ActiveCampaign which holds ActiveCampaign connect data. * @param MapperInterface $activeCampaign Inject ActiveCampaign which holds ActiveCampaign form data. + * @param SettingsTroubleshootingDataInterface $settingsTroubleshooting Inject Troubleshooting which holds Troubleshooting settings data. */ public function __construct( ActiveCampaignClientInterface $activeCampaignClient, - MapperInterface $activeCampaign + MapperInterface $activeCampaign, + SettingsTroubleshootingDataInterface $settingsTroubleshooting ) { $this->activeCampaignClient = $activeCampaignClient; $this->activeCampaign = $activeCampaign; + $this->settingsTroubleshooting = $settingsTroubleshooting; } /** @@ -173,6 +185,7 @@ public function getSettingsSidebar(): array 'label' => \__('ActiveCampaign', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_INTEGRATION, ]; } @@ -206,7 +219,7 @@ public function getSettingsData(string $formId): array [ 'component' => 'highlighted-content', 'highlightedContentTitle' => \__('Something went wrong', 'eightshift-forms'), - 'highlightedContentSubtitle' => \__('Data from ActiveCampaign couldn\'t be fetched. Check the API key.', 'eightshift-forms'), + 'highlightedContentSubtitle' => \__('Data from ActiveCampaign couldn\'t be fetched. Check the API key, URL or if you have any form created.', 'eightshift-forms'), 'highlightedContentIcon' => 'error', ], ]; @@ -317,11 +330,9 @@ public function getSettingsGlobalData(): array // phpcs:ignore WordPress.WP.I18n.NoHtmlWrappedStrings 'introSubtitle' => \__('
  1. Log in to your ActiveCampaign Account.
  2. -
  3. Navigate to your user profile image (bottom left corner).
  4. -
  5. Click on Account.
  6. -
  7. Click on Extras and API Keys in the tabs section.
  8. -
  9. Click on the Create a Key button.
  10. -
  11. Copy the API key into the field below or use the global constant.
  12. +
  13. Navigate to your Settings page (gear icon in the bottom-left corner).
  14. +
  15. Click on Developer.
  16. +
  17. Copy the API key and URL into the fields below or use the global constant.
', 'eightshift-forms'), ], [ @@ -378,6 +389,9 @@ public function getSettingsGlobalData(): array ); } - return $output; + return [ + ...$output, + ...$this->settingsTroubleshooting->getOutputGlobalTroubleshooting(SettingsActiveCampaign::SETTINGS_TYPE_KEY), + ]; } } diff --git a/src/Integrations/Clearbit/ClearbitClient.php b/src/Integrations/Clearbit/ClearbitClient.php index 7d79bf9f8..0b7aea0f0 100644 --- a/src/Integrations/Clearbit/ClearbitClient.php +++ b/src/Integrations/Clearbit/ClearbitClient.php @@ -10,10 +10,12 @@ namespace EightshiftForms\Integrations\Clearbit; -use EightshiftForms\Helpers\Helper; use EightshiftForms\Hooks\Filters; use EightshiftForms\Hooks\Variables; +use EightshiftForms\Rest\ApiHelper; +use EightshiftForms\Rest\Routes\AbstractBaseRoute; use EightshiftForms\Settings\SettingsHelper; +use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; use EightshiftFormsVendor\EightshiftLibs\Helpers\ObjectHelperTrait; /** @@ -31,6 +33,11 @@ class ClearbitClient implements ClearbitClientInterface */ use ObjectHelperTrait; + /** + * Use API helper trait. + */ + use ApiHelper; + /** * Return Clearbit base url. * @@ -41,115 +48,70 @@ class ClearbitClient implements ClearbitClientInterface /** * API request to post application. * - * @param string $emailKey Email key to map in params. + * @param string $email Email key to map in params. * @param array $params Params array. * @param array $mapData Map data from settings. + * @param string $itemId Item id to search. + * @param string $formId FormId value. * * @return array */ - public function getApplication(string $emailKey, array $params, array $mapData): array + public function getApplication(string $email, array $params, array $mapData, string $itemId, string $formId): array { - $email = isset($params[$emailKey]['value']) ? $params[$emailKey]['value'] : ''; - - if (!$email) { - $output = [ - 'status' => 'error', - 'code' => 400, - 'message' => 'clearbitMissingEmail', - ]; - - Helper::logger([ - 'integration' => 'clearbit', - 'email' => $email, - 'mapKeys' => $mapData, - 'output' => $output, - ]); - - return $output; - } + $url = self::BASE_URL . "combined/find?email={$email}"; - if (!$mapData) { - $output = [ - 'status' => 'error', - 'code' => 400, - 'message' => 'clearbitMissingMapKeys', - ]; - - Helper::logger([ - 'integration' => 'clearbit', - 'email' => $email, - 'mapKeys' => $mapData, - 'output' => $output, - ]); - - return $output; - } + $params = $this->prepareParamsOutput($params); $response = \wp_remote_get( - self::BASE_URL . "combined/find?email={$email}", + $url, [ 'headers' => $this->getHeaders(), ] ); - if (\is_wp_error($response)) { - Helper::logger([ - 'integration' => 'clearbit', - 'type' => 'wp', - 'email' => $email, - 'mapKeys' => $mapData, - 'response' => $response, - ]); - return [ - 'status' => 'error', - 'code' => 400, - 'message' => $this->getErrorMsg('submitWpError'), - ]; - } - - $code = $response['response']['code'] ? $response['response']['code'] : 200; + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsClearbit::SETTINGS_TYPE_KEY, + $response, + $url, + $params, + [], + $itemId, + $formId + ); - $responseBody = \json_decode(\wp_remote_retrieve_body($response), true); + $code = $details['code']; + $body = $details['body']; - if ($code === 200) { + // On success return output. + if ($code >= 200 && $code <= 299) { $dataOutput = []; - foreach ($this->prepareParams($responseBody) as $key => $value) { + foreach ($this->prepareParams($body) as $key => $value) { if (\array_key_exists($key, $mapData) && !empty($value) && !empty($mapData[$key])) { $dataOutput[$mapData[$key]] = $value; } } - return [ - 'status' => 'success', - 'type' => 'service', - 'code' => $code, - 'message' => 'clearbitSuccess', - 'email' => $email, - 'data' => $dataOutput, - ]; + return $this->getApiSuccessOutput( + $details, + [ + 'email' => $email, + 'data' => $dataOutput, + ] + ); } - $responseMessage = $responseBody['error']['type'] ?? ''; - - $output = [ - 'status' => 'error', - 'code' => $code, - 'email' => $email, - 'mapKeys' => $mapData, - 'message' => $this->getErrorMsg($responseMessage), - ]; - - Helper::logger([ - 'integration' => 'clearbit', - 'email' => $email, - 'mapKeys' => $mapData, - 'response' => $response['response'], - 'responseBody' => $responseBody, - 'output' => $output, - ]); - - return $output; + // Output error. + return $this->getApiErrorOutput( + \array_merge( + $details, + [ + 'email' => $email, + ] + ), + $this->getErrorMsg($body), + ); } /** @@ -168,6 +130,39 @@ public function getParams(): array return $output; } + /** + * Prepare params for api. + * + * @param array $params Params. + * + * @return array + */ + private function prepareParamsOutput(array $params = []): array + { + $output = []; + + $customFields = \array_flip(Components::flattenArray(AbstractBaseRoute::CUSTOM_FORM_PARAMS)); + + foreach ($params as $key => $param) { + // Remove unecesery fields. + if (isset($customFields[$key])) { + continue; + } + + if (!isset($param['value'])) { + continue; + } + + if (!isset($param['type']) || $param['type'] === 'hidden') { + continue; + } + + $output[$key] = $param; + } + + return $output; + } + /** * Prepare params * @@ -305,15 +300,19 @@ private function prepareParams(array $params = []): array /** * Map service messages with our own. * - * @param string $msg Message got from the API. + * @param array $body API response body. * * @return string */ - private function getErrorMsg(string $msg): string + private function getErrorMsg(array $body): string { + $msg = $body['error']['type'] ?? ''; + switch ($msg) { case 'auth_required': return 'clearbitAuthRequired'; + case 'email_invalid': + return 'clearbitInvalidEmail'; default: return 'submitWpError'; } diff --git a/src/Integrations/Clearbit/ClearbitClientInterface.php b/src/Integrations/Clearbit/ClearbitClientInterface.php index c478f935e..eb9c3425a 100644 --- a/src/Integrations/Clearbit/ClearbitClientInterface.php +++ b/src/Integrations/Clearbit/ClearbitClientInterface.php @@ -21,13 +21,15 @@ interface ClearbitClientInterface public function getParams(): array; /** - * API request to get application. + * API request to post application. * - * @param string $emailKey Email key to map in params. + * @param string $email Email key to map in params. * @param array $params Params array. * @param array $mapData Map data from settings. + * @param string $itemId Item id to search. + * @param string $formId FormId value. * * @return array */ - public function getApplication(string $emailKey, array $params, array $mapData): array; + public function getApplication(string $email, array $params, array $mapData, string $itemId, string $formId): array; } diff --git a/src/Integrations/Clearbit/SettingsClearbit.php b/src/Integrations/Clearbit/SettingsClearbit.php index c07ff35d1..b28cbc14b 100644 --- a/src/Integrations/Clearbit/SettingsClearbit.php +++ b/src/Integrations/Clearbit/SettingsClearbit.php @@ -13,6 +13,7 @@ use EightshiftForms\Hooks\Filters; use EightshiftForms\Settings\SettingsHelper; use EightshiftForms\Hooks\Variables; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; /** @@ -164,6 +165,7 @@ public function getSettingsSidebar(): array 'label' => \__('Clearbit', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_INTEGRATION, ]; } diff --git a/src/Integrations/Goodbits/GoodbitsClient.php b/src/Integrations/Goodbits/GoodbitsClient.php index efaf690b7..a6cf55f01 100644 --- a/src/Integrations/Goodbits/GoodbitsClient.php +++ b/src/Integrations/Goodbits/GoodbitsClient.php @@ -10,10 +10,12 @@ namespace EightshiftForms\Integrations\Goodbits; -use EightshiftForms\Helpers\Helper; use EightshiftForms\Hooks\Variables; use EightshiftForms\Integrations\ClientInterface; +use EightshiftForms\Rest\ApiHelper; +use EightshiftForms\Rest\Routes\AbstractBaseRoute; use EightshiftForms\Settings\SettingsHelper; +use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; use EightshiftFormsVendor\EightshiftLibs\Helpers\ObjectHelperTrait; /** @@ -31,6 +33,11 @@ class GoodbitsClient implements ClientInterface */ use ObjectHelperTrait; + /** + * Use API helper trait. + */ + use ApiHelper; + /** * Return Goodbits base url. * @@ -102,71 +109,54 @@ public function postApplication(string $itemId, array $params, array $files, str 'subscriber' => $this->prepareParams($params), ]; - $response = \wp_remote_request( - self::BASE_URL . "subscribers", + $url = self::BASE_URL . "subscribers"; + + $response = \wp_remote_post( + $url, [ 'headers' => $this->getHeaders($itemId), - 'method' => 'POST', 'body' => \wp_json_encode($body), ] ); - if (\is_wp_error($response)) { - Helper::logger([ - 'integration' => 'goodbits', - 'type' => 'wp', - 'body' => $body, - 'response' => $response, - ]); - return [ - 'status' => 'error', - 'code' => 400, - 'message' => $this->getErrorMsg('submitWpError'), - ]; - } + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsGoodbits::SETTINGS_TYPE_KEY, + $response, + $url, + $body, + $files, + $itemId, + $formId + ); - $code = $response['response']['code'] ? $response['response']['code'] : 200; + $code = $details['code']; + $body = $details['body']; - if ($code === 200 || $code === 201) { - return [ - 'status' => 'success', - 'code' => 200, - 'message' => 'goodbitsSuccess', - ]; + // On success return output. + if ($code >= 200 && $code <= 299) { + return $this->getApiSuccessOutput($details); } - $responseBody = \json_decode(\wp_remote_retrieve_body($response), true); - $responseMessage = !\is_array($responseBody['errors']) ? $responseBody['errors'] : ''; - $responseErrors = \is_array($responseBody['errors']) ? $responseBody['errors']['message'] : []; - - $output = [ - 'status' => 'error', - 'code' => $code, - 'message' => $this->getErrorMsg($responseMessage, $responseErrors), - ]; - - Helper::logger([ - 'integration' => 'goodbits', - 'type' => 'service', - 'body' => $body, - 'response' => $response['response'], - 'responseBody' => $responseBody, - 'output' => $output, - ]); - - return $output; + // Output error. + return $this->getApiErrorOutput( + $details, + $this->getErrorMsg($body), + ); } /** * Map service messages with our own. * - * @param string $msg Message got from the API. - * @param array $errors Additional errors got from the API. + * @param array $body API response body. * * @return string */ - private function getErrorMsg(string $msg, array $errors = []): string + private function getErrorMsg(array $body): string { + $msg = !\is_array($body['errors']) ? $body['errors'] : ''; + $errors = \is_array($body['errors']) ? $body['errors']['message'] : []; + if ($errors) { $invalidEmail = \array_filter( $errors, @@ -220,12 +210,15 @@ private function prepareParams(array $params): array { $output = []; - foreach ($params as $key => $value) { - $output[$key] = $value['value'] ?? ''; - } + $customFields = \array_flip(Components::flattenArray(AbstractBaseRoute::CUSTOM_FORM_PARAMS)); + + foreach ($params as $key => $param) { + // Remove unnecessary fields. + if (isset($customFields[$key])) { + continue; + } - if (isset($params['es-form-storage'])) { - unset($params['es-form-storage']); + $output[$key] = $param['value'] ?? ''; } return $output; diff --git a/src/Integrations/Goodbits/SettingsGoodbits.php b/src/Integrations/Goodbits/SettingsGoodbits.php index b9014dfee..da3e31dee 100644 --- a/src/Integrations/Goodbits/SettingsGoodbits.php +++ b/src/Integrations/Goodbits/SettingsGoodbits.php @@ -16,7 +16,9 @@ use EightshiftForms\Hooks\Variables; use EightshiftForms\Integrations\ClientInterface; use EightshiftForms\Integrations\MapperInterface; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\Settings\SettingsDataInterface; +use EightshiftForms\Troubleshooting\SettingsTroubleshootingDataInterface; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; /** @@ -88,18 +90,28 @@ class SettingsGoodbits implements SettingsDataInterface, ServiceInterface */ protected $goodbits; + /** + * Instance variable for Troubleshooting settings. + * + * @var SettingsTroubleshootingDataInterface + */ + protected $settingsTroubleshooting; + /** * Create a new instance. * * @param ClientInterface $goodbitsClient Inject Goodbits which holds Goodbits connect data. * @param MapperInterface $goodbits Inject Goodbits which holds Goodbits form data. + * @param SettingsTroubleshootingDataInterface $settingsTroubleshooting Inject Troubleshooting which holds Troubleshooting settings data. */ public function __construct( ClientInterface $goodbitsClient, - MapperInterface $goodbits + MapperInterface $goodbits, + SettingsTroubleshootingDataInterface $settingsTroubleshooting ) { $this->goodbitsClient = $goodbitsClient; $this->goodbits = $goodbits; + $this->settingsTroubleshooting = $settingsTroubleshooting; } /** * Register all the hooks @@ -164,6 +176,7 @@ public function getSettingsSidebar(): array 'label' => \__('Goodbits', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_INTEGRATION, ]; } @@ -348,6 +361,9 @@ public function getSettingsGlobalData(): array ); } - return $output; + return [ + ...$output, + ...$this->settingsTroubleshooting->getOutputGlobalTroubleshooting(SettingsGoodbits::SETTINGS_TYPE_KEY), + ]; } } diff --git a/src/Integrations/Greenhouse/Greenhouse.php b/src/Integrations/Greenhouse/Greenhouse.php index e2eb8ee8b..192ca9f6e 100644 --- a/src/Integrations/Greenhouse/Greenhouse.php +++ b/src/Integrations/Greenhouse/Greenhouse.php @@ -256,16 +256,6 @@ static function ($selectOption) { 'inputId' => 'latitude', 'inputName' => 'latitude', ]; - - global $wp; - - $output[] = [ - 'component' => 'input', - 'inputType' => 'hidden', - 'inputId' => 'es-form-url', - 'inputName' => 'es-form-url', - 'inputValue' => \esc_url(\site_url(\add_query_arg([$_GET], $wp->request))), // phpcs:ignore WordPress.Security.NonceVerification.Recommended - ]; } $output[] = [ diff --git a/src/Integrations/Greenhouse/GreenhouseClient.php b/src/Integrations/Greenhouse/GreenhouseClient.php index ad100aca1..5db0be368 100644 --- a/src/Integrations/Greenhouse/GreenhouseClient.php +++ b/src/Integrations/Greenhouse/GreenhouseClient.php @@ -10,10 +10,15 @@ namespace EightshiftForms\Integrations\Greenhouse; -use EightshiftForms\Helpers\Helper; +use CURLFile; +use EightshiftForms\General\General; +use EightshiftForms\Hooks\Filters; use EightshiftForms\Settings\SettingsHelper; use EightshiftForms\Hooks\Variables; use EightshiftForms\Integrations\ClientInterface; +use EightshiftForms\Rest\ApiHelper; +use EightshiftForms\Rest\Routes\AbstractBaseRoute; +use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; /** * GreenhouseClient integration class. @@ -25,6 +30,11 @@ class GreenhouseClient implements ClientInterface */ use SettingsHelper; + /** + * Use API helper trait. + */ + use ApiHelper; + /** * Return Greenhouse base url. * @@ -129,75 +139,74 @@ public function getItem(string $itemId): array public function postApplication(string $itemId, array $params, array $files, string $formId): array { $paramsPrepared = $this->prepareParams($params); + $paramsFiles = $this->prepareFiles($files); $body = \array_merge( $paramsPrepared, - $this->prepareFiles($files) + $paramsFiles ); - $response = \wp_remote_post( - self::BASE_URL . "boards/{$this->getBoardToken()}/jobs/{$itemId}", + $filterName = Filters::getGeneralSettingsFilterName('httpRequestTimeout'); + + $url = self::BASE_URL . "boards/{$this->getBoardToken()}/jobs/{$itemId}"; + + // Curl used because files are not sent via wp request. + $curl = \curl_init(); // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_init + \curl_setopt_array( // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_setopt_array + $curl, [ - 'headers' => $this->getHeaders(true), - 'body' => \wp_json_encode($body), + \CURLOPT_URL => $url, + \CURLOPT_HTTPAUTH => \CURLAUTH_BASIC, + \CURLOPT_RETURNTRANSFER => true, + \CURLOPT_TIMEOUT => \apply_filters($filterName, General::HTTP_REQUEST_TIMEOUT_DEFAULT), + \CURLOPT_POST => true, + \CURLOPT_POSTFIELDS => $body, + \CURLOPT_HTTPHEADER => $this->getHeaders(true), ] ); + $response = \curl_exec($curl); // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_exec + $code = \curl_getinfo($curl, \CURLINFO_RESPONSE_CODE); // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_getinfo - if (\is_wp_error($response)) { - Helper::logger([ - 'integration' => 'greenhouse', - 'type' => 'wp', - 'body' => $paramsPrepared, - 'response' => $response, - ]); + \curl_close($curl); // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_close - return [ - 'status' => 'error', - 'code' => 400, - 'message' => $this->getErrorMsg('submitWpError'), - ]; - } + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsGreenhouse::SETTINGS_TYPE_KEY, + \json_decode($response, true), + $url, + $paramsPrepared, + $paramsFiles, + $itemId, + $formId, + true + ); - $code = $response['response']['code'] ? $response['response']['code'] : 200; + $code = $details['code']; + $body = $details['body']; - if ($code === 200) { - return [ - 'status' => 'success', - 'code' => $code, - 'message' => 'greenhouseSuccess', - ]; + // On success return output. + if ($code >= 200 && $code <= 299) { + return $this->getApiSuccessOutput($details); } - $responseBody = \json_decode(\wp_remote_retrieve_body($response), true); - $responseMessage = $responseBody['error'] ?? ''; - - $output = [ - 'status' => 'error', - 'code' => $code, - 'message' => $this->getErrorMsg($responseMessage), - ]; - - Helper::logger([ - 'integration' => 'greenhouse', - 'type' => 'service', - 'body' => $paramsPrepared, - 'response' => $response['response'], - 'responseBody' => $responseBody, - 'output' => $output, - ]); - - return $output; + // Output error. + return $this->getApiErrorOutput( + $details, + $this->getErrorMsg($body) + ); } /** * Map service messages with our own. * - * @param string $msg Message got from the API. + * @param array $body API response body. * * @return string */ - private function getErrorMsg(string $msg): string + private function getErrorMsg(array $body): string { + $msg = $body['error'] ?? ''; + switch ($msg) { case 'Bad Request': return 'greenhouseBadRequestError'; @@ -243,21 +252,31 @@ private function getErrorMsg(string $msg): string */ private function getGreenhouseJobs() { + $url = self::BASE_URL . "boards/{$this->getBoardToken()}/jobs"; + $response = \wp_remote_get( - self::BASE_URL . "boards/{$this->getBoardToken()}/jobs", + $url, [ 'headers' => $this->getHeaders(), - 'timeout' => 60, ] ); - $body = \json_decode(\wp_remote_retrieve_body($response), true); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsGreenhouse::SETTINGS_TYPE_KEY, + $response, + $url, + ); + + $code = $details['code']; + $body = $details['body']; - if (!isset($body['jobs'])) { - return []; + // On success return output. + if ($code >= 200 && $code <= 299) { + return $body['jobs'] ?? []; } - return $body['jobs']; + return []; } /** @@ -269,41 +288,51 @@ private function getGreenhouseJobs() */ private function getGreenhouseJob(string $jobId) { + $url = self::BASE_URL . "boards/{$this->getBoardToken()}/jobs/{$jobId}?questions=true"; + $response = \wp_remote_get( - self::BASE_URL . "boards/{$this->getBoardToken()}/jobs/{$jobId}?questions=true", + $url, [ 'headers' => $this->getHeaders(), - 'timeout' => 60, ] ); - $body = \json_decode(\wp_remote_retrieve_body($response), true); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsGreenhouse::SETTINGS_TYPE_KEY, + $response, + $url, + ); - if (isset($body['error'])) { - return []; + $code = $details['code']; + $body = $details['body']; + + // On success return output. + if ($code >= 200 && $code <= 299) { + return $body ?? []; } - return $body; + return []; } /** * Set headers used for fetching data. * - * @param boolean $useAuth If using post method we need to send Authorization header in the request. + * @param boolean $postHeaders If using post method we need to send Authorization header and type in the request. * - * @return array + * @return array */ - private function getHeaders(bool $useAuth = false): array + private function getHeaders(bool $postHeaders = false): array { - $headers = [ - 'Content-Type' => 'application/json; charset=utf-8', - ]; - - if ($useAuth) { - $headers['Authorization'] = "Basic {$this->getApiKey()}"; + if ($postHeaders) { + return [ + 'Authorization: Basic ' . $this->getApiKey(), + ]; } - return $headers; + return [ + 'Content-Type' => 'application/json', + ]; } /** @@ -317,17 +346,21 @@ private function prepareParams(array $params): array { $output = []; - foreach ($params as $key => $value) { + $customFields = \array_flip(Components::flattenArray(AbstractBaseRoute::CUSTOM_FORM_PARAMS)); + + foreach ($params as $key => $param) { // Get gh_src from url and map it. - if ($key === 'es-form-storage' && isset($value['value']['gh_src'])) { - $output['mapped_url_token'] = $value['value']['gh_src']; - } else { - $output[$key] = $value['value'] ?? ''; + if ($key === AbstractBaseRoute::CUSTOM_FORM_PARAM_STORAGE && isset($param['value']['gh_src'])) { + $output['mapped_url_token'] = $param['value']['gh_src']; + continue; + } + + // Remove unecesery fields. + if (isset($customFields[$key])) { + continue; } - } - if (isset($params['es-form-storage'])) { - unset($params['es-form-storage']); + $output[$key] = $param['value'] ?? ''; } return $output; @@ -353,13 +386,13 @@ private function prepareFiles(array $files): array $fileName = $file['fileName'] ?? ''; $path = $file['path'] ?? ''; $id = $file['id'] ?? ''; + $type = $file['type'] ?? ''; - if (!$path || !$fileName || !$id) { + if (!$path) { continue; } - $output["{$id}_content"] = \base64_encode((string) \file_get_contents($path)); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents - $output["{$id}_content_filename"] = $fileName; + $output[$id] = new CURLFile(\realpath($path), $type, $fileName); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents } } diff --git a/src/Integrations/Greenhouse/SettingsGreenhouse.php b/src/Integrations/Greenhouse/SettingsGreenhouse.php index daa415f43..bccefc494 100644 --- a/src/Integrations/Greenhouse/SettingsGreenhouse.php +++ b/src/Integrations/Greenhouse/SettingsGreenhouse.php @@ -17,7 +17,9 @@ use EightshiftForms\Hooks\Variables; use EightshiftForms\Integrations\ClientInterface; use EightshiftForms\Integrations\MapperInterface; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\Settings\SettingsDataInterface; +use EightshiftForms\Troubleshooting\SettingsTroubleshootingDataInterface; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; /** @@ -94,18 +96,28 @@ class SettingsGreenhouse implements SettingsDataInterface, ServiceInterface */ protected $greenhouse; + /** + * Instance variable for Troubleshooting settings. + * + * @var SettingsTroubleshootingDataInterface + */ + protected $settingsTroubleshooting; + /** * Create a new instance. * * @param ClientInterface $greenhouseClient Inject Greenhouse which holds Greenhouse connect data. * @param MapperInterface $greenhouse Inject Greenhouse which holds Greenhouse form data. + * @param SettingsTroubleshootingDataInterface $settingsTroubleshooting Inject Troubleshooting which holds Troubleshooting settings data. */ public function __construct( ClientInterface $greenhouseClient, - MapperInterface $greenhouse + MapperInterface $greenhouse, + SettingsTroubleshootingDataInterface $settingsTroubleshooting ) { $this->greenhouseClient = $greenhouseClient; $this->greenhouse = $greenhouse; + $this->settingsTroubleshooting = $settingsTroubleshooting; } /** @@ -172,6 +184,7 @@ public function getSettingsSidebar(): array 'label' => \__('Greenhouse', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_INTEGRATION, ]; } @@ -389,6 +402,9 @@ public function getSettingsGlobalData(): array ); } - return $output; + return [ + ...$output, + ...$this->settingsTroubleshooting->getOutputGlobalTroubleshooting(SettingsGreenhouse::SETTINGS_TYPE_KEY), + ]; } } diff --git a/src/Integrations/Hubspot/Hubspot.php b/src/Integrations/Hubspot/Hubspot.php index 9ff182528..aab0ba46e 100644 --- a/src/Integrations/Hubspot/Hubspot.php +++ b/src/Integrations/Hubspot/Hubspot.php @@ -40,6 +40,27 @@ class Hubspot extends AbstractFormBuilder implements MapperInterface, ServiceInt */ public const FILTER_FORM_FIELDS_NAME = 'es_hubspot_form_fields_filter'; + /** + * Custom form param for cookie. + * + * @var string + */ + public const CUSTOM_FORM_PARAM_HUBSPOT_COOKIE = 'es-form-hubspot-cookie'; + + /** + * Custom form param for page name. + * + * @var string + */ + public const CUSTOM_FORM_PARAM_HUBSPOT_PAGE_NAME = 'es-form-hubspot-page-name'; + + /** + * Custom form param for page url. + * + * @var string + */ + public const CUSTOM_FORM_PARAM_HUBSPOT_PAGE_URL = 'es-form-hubspot-page-url'; + /** * Instance variable for Hubspot data. * @@ -162,7 +183,7 @@ private function getFields(array $data, string $formId, bool $ssr): array 'component' => 'rich-text', 'richTextId' => "rich-text-{$key}", 'richTextName' => "rich-text-{$key}", - 'richTextFieldLabel' => \__('Rich text', 'eightshift-form') . '-' . $key, + 'richTextFieldLabel' => \__('Rich text', 'eightshift-forms') . '-' . $key, 'richTextContent' => $richText, 'blockSsr' => $ssr, ]; diff --git a/src/Integrations/Hubspot/HubspotClient.php b/src/Integrations/Hubspot/HubspotClient.php index d24336fdc..f101beb4f 100644 --- a/src/Integrations/Hubspot/HubspotClient.php +++ b/src/Integrations/Hubspot/HubspotClient.php @@ -11,11 +11,13 @@ namespace EightshiftForms\Integrations\Hubspot; use CURLFile; -use EightshiftForms\Helpers\Helper; use EightshiftForms\Hooks\Filters; use EightshiftForms\Settings\SettingsHelper; use EightshiftForms\Hooks\Variables; use EightshiftForms\Integrations\ClientInterface; +use EightshiftForms\Rest\ApiHelper; +use EightshiftForms\Rest\Routes\AbstractBaseRoute; +use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; /** * HubspotClient integration class. @@ -27,6 +29,11 @@ class HubspotClient implements HubspotClientInterface */ use SettingsHelper; + /** + * Use API helper trait. + */ + use ApiHelper; + /** * Transient cache name for items. */ @@ -193,9 +200,9 @@ static function ($item) { $body = [ 'context' => [ 'ipAddress' => isset($_SERVER['REMOTE_ADDR']) ? \sanitize_text_field(\wp_unslash($_SERVER['REMOTE_ADDR'])) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended - 'hutk' => $params['es-form-hubspot-cookie']['value'], - 'pageUri' => $params['es-form-hubspot-page-url']['value'], - 'pageName' => $params['es-form-hubspot-page-name']['value'], + 'hutk' => $params[Hubspot::CUSTOM_FORM_PARAM_HUBSPOT_COOKIE]['value'], + 'pageUri' => $params[Hubspot::CUSTOM_FORM_PARAM_HUBSPOT_PAGE_URL]['value'], + 'pageName' => $params[Hubspot::CUSTOM_FORM_PARAM_HUBSPOT_PAGE_NAME]['value'], ], ]; @@ -228,63 +235,48 @@ static function ($item) { } } + $paramsPrepared = $this->prepareParams($params); + $paramsFiles = $this->prepareFiles($files, $formId); + $body['fields'] = \array_merge( $this->prepareParams($params), $this->prepareFiles($files, $formId) ); + $url = $this->getBaseUrl("submissions/v3/integration/secure/submit/{$itemId[1]}/{$itemId[0]}"); + $response = \wp_remote_post( - $this->getBaseUrl("submissions/v3/integration/secure/submit/{$itemId[1]}/{$itemId[0]}"), + $url, [ 'headers' => $this->getHeaders(), 'body' => \wp_json_encode($body), ] ); - if (\is_wp_error($response)) { - Helper::logger([ - 'integration' => 'hubspot', - 'type' => 'wp', - 'body' => $body, - 'response' => $response, - ]); - return [ - 'status' => 'error', - 'code' => 400, - 'message' => $this->getErrorMsg('submitWpError'), - ]; - } + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsHubspot::SETTINGS_TYPE_KEY, + $response, + $url, + $paramsPrepared, + $paramsFiles, + \implode(', ', $itemId), + $formId + ); - $code = $response['response']['code'] ? $response['response']['code'] : 200; + $code = $details['code']; + $body = $details['body']; - if ($code === 200) { - return [ - 'status' => 'success', - 'code' => $code, - 'message' => 'hubspotSuccess', - ]; + // On success return output. + if ($code >= 200 && $code <= 299) { + return $this->getApiSuccessOutput($details); } - $responseBody = \json_decode(\wp_remote_retrieve_body($response), true); - $responseMessage = $responseBody['message'] ?? ''; - $responseErrors = $responseBody['errors'] ?? []; - - $output = [ - 'status' => 'error', - 'code' => $code, - 'message' => $this->getErrorMsg($responseMessage, $responseErrors), - ]; - - Helper::logger([ - 'integration' => 'hubspot', - 'type' => 'service', - 'body' => $body, - 'response' => $response['response'], - 'responseBody' => $responseBody, - 'output' => $output, - ]); - - return $output; + // Output error. + return $this->getApiErrorOutput( + $details, + $this->getErrorMsg($body) + ); } /** @@ -297,99 +289,60 @@ static function ($item) { */ public function postContactProperty(string $email, array $params): array { - if (!$email) { - $output = [ - 'status' => 'error', - 'code' => 400, - 'message' => 'hubspotContactPropertyMissingEmail', - ]; - - Helper::logger([ - 'integration' => 'hubspot', - 'email' => $email, - 'mapKeys' => $params, - 'output' => $output, - ]); - - return $output; - } - - if (!$params) { - $output = [ - 'status' => 'error', - 'code' => 400, - 'message' => 'hubspotContactPropertyMissingMapKeys', - ]; - - Helper::logger([ - 'integration' => 'hubspot', - 'email' => $email, - 'mapKeys' => $params, - 'output' => $output, - ]); - - return $output; - } - $properties = []; + $customFields = \array_flip(Components::flattenArray(AbstractBaseRoute::CUSTOM_FORM_PARAMS)); - foreach ($params as $key => $value) { - $properties[] = [ - 'property' => $key, - 'value' => $value, - ]; + if ($params) { + foreach ($params as $key => $value) { + // Remove unecesery fields. + if (isset($customFields[$key])) { + continue; + } + + $properties[] = [ + 'property' => $key, + 'value' => $value, + ]; + } } $body = [ 'properties' => $properties, ]; + $url = $this->getBaseUrl("contacts/v1/contact/createOrUpdate/email/{$email}", true); + $response = \wp_remote_post( - $this->getBaseUrl("contacts/v1/contact/createOrUpdate/email/{$email}", true), + $url, [ 'headers' => $this->getHeaders(), 'body' => \wp_json_encode($body), ] ); - if (\is_wp_error($response)) { - return [ - 'status' => 'error', - 'code' => 400, - 'message' => $this->getErrorMsg('submitWpError'), - ]; - } - - $code = $response['response']['code'] ? $response['response']['code'] : 200; - - if ($code === 200) { - return [ - 'status' => 'success', - 'code' => $code, - 'message' => 'hubspotContactPropertySuccess', - ]; - } + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsHubspot::SETTINGS_TYPE_KEY, + $response, + $url, + $body + ); - $responseBody = \json_decode(\wp_remote_retrieve_body($response), true); - $responseMessage = $responseBody['message'] ?? ''; - $responseErrors = $responseBody['errors'] ?? []; - $output = [ - 'status' => 'error', - 'code' => $code, - 'message' => $this->getErrorMsg($responseMessage, $responseErrors), - ]; + $code = $details['code']; + $body = $details['body']; - Helper::logger([ - 'integration' => 'hubspot', - 'body' => $body, - 'response' => $response['response'], - 'responseBody' => $responseBody, - 'output' => $output, - ]); + // On success return output. + if ($code >= 200 && $code <= 299) { + return $this->getApiSuccessOutput($details); + } - return $output; + // Output error. + return $this->getApiErrorOutput( + $details, + $this->getErrorMsg($body) + ); } /** @@ -468,13 +421,15 @@ private function postFileMedia(array $file, string $formId): string /** * Map service messages with our own. * - * @param string $msg Message got from the API. - * @param array $errors Additional errors got from the API. + * @param array $body API response body. * * @return string */ - private function getErrorMsg(string $msg, array $errors = []): string + private function getErrorMsg(array $body): string { + $msg = $body['message'] ?? ''; + $errors = $body['errors'] ?? []; + if ($errors && isset($errors[0])) { $msg = $errors[0]['errorType']; } @@ -543,15 +498,31 @@ private function getErrorMsg(string $msg, array $errors = []): string */ private function getHubspotContactProperties() { + $url = $this->getBaseUrl('properties/v1/contacts/properties', true); + $response = \wp_remote_get( - $this->getBaseUrl('properties/v1/contacts/properties', true), + $url, [ 'headers' => $this->getHeaders(), - 'timeout' => 60, ] ); - return \json_decode(\wp_remote_retrieve_body($response), true); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsHubspot::SETTINGS_TYPE_KEY, + $response, + $url, + ); + + $code = $details['code']; + $body = $details['body']; + + // On success return output. + if ($code >= 200 && $code <= 299) { + return $body ?? []; + } + + return []; } /** @@ -561,15 +532,31 @@ private function getHubspotContactProperties() */ private function getHubspotItems() { + $url = $this->getBaseUrl('forms/v2/forms', true); + $response = \wp_remote_get( - $this->getBaseUrl('forms/v2/forms', true), + $url, [ 'headers' => $this->getHeaders(), - 'timeout' => 60, ] ); - return \json_decode(\wp_remote_retrieve_body($response), true); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsHubspot::SETTINGS_TYPE_KEY, + $response, + $url, + ); + + $code = $details['code']; + $body = $details['body']; + + // On success return output. + if ($code >= 200 && $code <= 299) { + return $body ?? []; + } + + return []; } /** @@ -689,19 +676,13 @@ private function prepareParams(array $params): array { $output = []; - unset($params['es-form-hubspot-cookie']); - unset($params['es-form-hubspot-page-name']); - unset($params['es-form-hubspot-page-url']); + $customFields = \array_flip(Components::flattenArray(AbstractBaseRoute::CUSTOM_FORM_PARAMS)); foreach ($params as $key => $param) { - if ($key === 'es-form-storage') { - continue; - } - $type = $param['type'] ?? ''; $value = $param['value'] ?? ''; - if (!$param) { + if (!$value) { continue; } @@ -717,6 +698,11 @@ private function prepareParams(array $params): array $value = \str_replace(', ', ';', $value); } + // Remove unnecessary fields. + if (isset($customFields[$key])) { + continue; + } + $output[] = [ 'name' => $param['name'] ?? '', 'value' => $value, @@ -725,8 +711,13 @@ private function prepareParams(array $params): array } $filterName = Filters::getIntegrationFilterName(SettingsHubspot::SETTINGS_TYPE_KEY, 'localStorageMap'); - if (isset($params['es-form-storage']['value']) && \has_filter($filterName)) { - return \apply_filters($filterName, $output, $params['es-form-storage']['value']) ?? []; + if (isset($params[AbstractBaseRoute::CUSTOM_FORM_PARAM_STORAGE]['value']) && \has_filter($filterName)) { + return \apply_filters( + $filterName, + $output, + $params[AbstractBaseRoute::CUSTOM_FORM_PARAM_STORAGE]['value'], + $params + ) ?? []; } return $output; diff --git a/src/Integrations/Hubspot/SettingsHubspot.php b/src/Integrations/Hubspot/SettingsHubspot.php index 497bdc944..651a772bd 100644 --- a/src/Integrations/Hubspot/SettingsHubspot.php +++ b/src/Integrations/Hubspot/SettingsHubspot.php @@ -19,7 +19,9 @@ use EightshiftForms\Integrations\Clearbit\SettingsClearbitDataInterface; use EightshiftForms\Integrations\ClientInterface; use EightshiftForms\Integrations\MapperInterface; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\Settings\SettingsDataInterface; +use EightshiftForms\Troubleshooting\SettingsTroubleshootingDataInterface; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; /** @@ -135,6 +137,13 @@ class SettingsHubspot implements SettingsDataInterface, ServiceInterface */ protected $hubspot; + /** + * Instance variable for Troubleshooting settings. + * + * @var SettingsTroubleshootingDataInterface + */ + protected $settingsTroubleshooting; + /** * Create a new instance. * @@ -142,17 +151,20 @@ class SettingsHubspot implements SettingsDataInterface, ServiceInterface * @param SettingsClearbitDataInterface $settingsClearbit Inject Clearbit which holds Clearbit settings data. * @param HubspotClientInterface $hubspotClient Inject Hubspot which holds Hubspot connect data. * @param MapperInterface $hubspot Inject HubSpot which holds HubSpot form data. + * @param SettingsTroubleshootingDataInterface $settingsTroubleshooting Inject Troubleshooting which holds Troubleshooting settings data. */ public function __construct( ClearbitClientInterface $clearbitClient, SettingsClearbitDataInterface $settingsClearbit, HubspotClientInterface $hubspotClient, - MapperInterface $hubspot + MapperInterface $hubspot, + SettingsTroubleshootingDataInterface $settingsTroubleshooting ) { $this->clearbitClient = $clearbitClient; $this->settingsClearbit = $settingsClearbit; $this->hubspotClient = $hubspotClient; $this->hubspot = $hubspot; + $this->settingsTroubleshooting = $settingsTroubleshooting; } /** @@ -218,6 +230,7 @@ public function getSettingsSidebar(): array 'label' => \__('HubSpot', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_INTEGRATION, ]; } @@ -379,10 +392,11 @@ public function getSettingsGlobalData(): array ); } - return \array_merge( - $output, - $outputValid - ); + return [ + ...$output, + ...$outputValid, + ...$this->settingsTroubleshooting->getOutputGlobalTroubleshooting(SettingsHubspot::SETTINGS_TYPE_KEY), + ]; } /** diff --git a/src/Integrations/Mailchimp/Mailchimp.php b/src/Integrations/Mailchimp/Mailchimp.php index ebc4e9a89..da7c9f9b4 100644 --- a/src/Integrations/Mailchimp/Mailchimp.php +++ b/src/Integrations/Mailchimp/Mailchimp.php @@ -42,11 +42,11 @@ class Mailchimp extends AbstractFormBuilder implements MapperInterface, ServiceI public const FILTER_FORM_FIELDS_NAME = 'es_mailchimp_form_fields_filter'; /** - * Field Mailchimp Tags. + * Custom form param for tags. * * @var string */ - public const FIELD_MAILCHIMP_TAGS_KEY = 'es-form-mailchimp-tags'; + public const CUSTOM_FORM_PARAM_MAILCHIMP_TAGS = 'es-form-mailchimp-tags'; /** * Instance variable for Mailchimp data. @@ -324,9 +324,9 @@ static function ($item) use ($selectedIds) { $output[] = [ 'component' => 'select', 'selectFieldLabel' => \__('Tags', 'eightshift-forms'), - 'selectId' => self::FIELD_MAILCHIMP_TAGS_KEY, - 'selectName' => self::FIELD_MAILCHIMP_TAGS_KEY, - 'selectTracking' => self::FIELD_MAILCHIMP_TAGS_KEY, + 'selectId' => self::CUSTOM_FORM_PARAM_MAILCHIMP_TAGS, + 'selectName' => self::CUSTOM_FORM_PARAM_MAILCHIMP_TAGS, + 'selectTracking' => self::CUSTOM_FORM_PARAM_MAILCHIMP_TAGS, 'selectOptions' => \array_merge( [ [ @@ -359,7 +359,7 @@ static function ($option) use ($tagsLabels) { ]; break; case 'checkboxes': - $checkboxesFieldName = self::FIELD_MAILCHIMP_TAGS_KEY; + $checkboxesFieldName = self::CUSTOM_FORM_PARAM_MAILCHIMP_TAGS; $output[] = [ 'component' => 'checkboxes', @@ -403,9 +403,9 @@ static function ($item) { $output[] = [ 'component' => 'input', 'inputType' => 'hidden', - 'inputId' => self::FIELD_MAILCHIMP_TAGS_KEY, - 'inputName' => self::FIELD_MAILCHIMP_TAGS_KEY, - 'inputTracking' => self::FIELD_MAILCHIMP_TAGS_KEY, + 'inputId' => self::CUSTOM_FORM_PARAM_MAILCHIMP_TAGS, + 'inputName' => self::CUSTOM_FORM_PARAM_MAILCHIMP_TAGS, + 'inputTracking' => self::CUSTOM_FORM_PARAM_MAILCHIMP_TAGS, 'inputValue' => $tagsItems, 'blockSsr' => $ssr, ]; diff --git a/src/Integrations/Mailchimp/MailchimpClient.php b/src/Integrations/Mailchimp/MailchimpClient.php index 4dbb6140c..241ad7da2 100644 --- a/src/Integrations/Mailchimp/MailchimpClient.php +++ b/src/Integrations/Mailchimp/MailchimpClient.php @@ -10,10 +10,12 @@ namespace EightshiftForms\Integrations\Mailchimp; -use EightshiftForms\Helpers\Helper; use EightshiftForms\Hooks\Variables; use EightshiftForms\Integrations\ClientInterface; +use EightshiftForms\Rest\ApiHelper; +use EightshiftForms\Rest\Routes\AbstractBaseRoute; use EightshiftForms\Settings\SettingsHelper; +use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; /** * MailchimpClient integration class. @@ -25,6 +27,11 @@ class MailchimpClient implements MailchimpClientInterface */ use SettingsHelper; + /** + * Use API helper trait. + */ + use ApiHelper; + /** * Transient cache name for items. */ @@ -166,8 +173,10 @@ public function postApplication(string $itemId, array $params, array $files, str $body['merge_fields'] = $prepareParams; } + $url = "{$this->getBaseUrl()}lists/{$itemId}/members/{$emailHash}"; + $response = \wp_remote_request( - "{$this->getBaseUrl()}lists/{$itemId}/members/{$emailHash}", + $url, [ 'headers' => $this->getHeaders(), 'method' => 'PUT', @@ -175,63 +184,44 @@ public function postApplication(string $itemId, array $params, array $files, str ] ); - if (\is_wp_error($response)) { - Helper::logger([ - 'integration' => 'mailchimp', - 'type' => 'wp', - 'body' => $body, - 'response' => $response, - ]); - - return [ - 'status' => 'error', - 'code' => 400, - 'message' => $this->getErrorMsg('submitWpError'), - ]; - } + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsMailchimp::SETTINGS_TYPE_KEY, + $response, + $url, + $body, + $files, + $itemId, + $formId + ); - $code = $response['response']['code'] ? $response['response']['code'] : 200; + $code = $details['code']; + $body = $details['body']; - if ($code === 200) { - return [ - 'status' => 'success', - 'code' => $code, - 'message' => 'mailchimpSuccess', - ]; + // On success return output. + if ($code >= 200 && $code <= 299) { + return $this->getApiSuccessOutput($details); } - $responseBody = \json_decode(\wp_remote_retrieve_body($response), true); - $responseMessage = $responseBody['detail'] ?? ''; - $responseErrors = $responseBody['errors'] ?? []; - - $output = [ - 'status' => 'error', - 'code' => $code, - 'message' => $this->getErrorMsg($responseMessage, $responseErrors), - ]; - - Helper::logger([ - 'integration' => 'mailchimp', - 'type' => 'service', - 'body' => $body, - 'response' => $response['response'], - 'responseBody' => $responseBody, - 'output' => $output, - ]); - - return $output; + // Output error. + return $this->getApiErrorOutput( + $details, + $this->getErrorMsg($body), + ); } /** * Map service messages with our own. * - * @param string $msg Message got from the API. - * @param array $errors Additional errors got from the API. + * @param array $body API response body. * * @return string */ - private function getErrorMsg(string $msg, array $errors = []): string + private function getErrorMsg(array $body): string { + $msg = $body['detail'] ?? ''; + $errors = $body['errors'] ?? []; + if ($errors) { $invalidEmail = \array_filter( $errors, @@ -269,21 +259,31 @@ static function ($error) { */ private function getMailchimpTags(string $itemId): array { + $url = "{$this->getBaseUrl()}lists/{$itemId}/tag-search"; + $response = \wp_remote_get( - "{$this->getBaseUrl()}lists/{$itemId}/tag-search", + $url, [ 'headers' => $this->getHeaders(), - 'timeout' => 60, ] ); - $body = \json_decode(\wp_remote_retrieve_body($response), true); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsMailchimp::SETTINGS_TYPE_KEY, + $response, + $url, + ); - if (!isset($body['tags'])) { - return []; + $code = $details['code']; + $body = $details['body']; + + // On success return output. + if ($code >= 200 && $code <= 299) { + return $body['tags'] ?? []; } - return $body['tags']; + return []; } /** @@ -310,21 +310,31 @@ private function getHeaders(): array */ private function getMailchimpListFields(string $listId) { + $url = "{$this->getBaseUrl()}lists/{$listId}/merge-fields?count=1000"; + $response = \wp_remote_get( - "{$this->getBaseUrl()}lists/{$listId}/merge-fields?count=1000", + $url, [ 'headers' => $this->getHeaders(), - 'timeout' => 60, ] ); - $body = \json_decode(\wp_remote_retrieve_body($response), true); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsMailchimp::SETTINGS_TYPE_KEY, + $response, + $url, + ); - if (!isset($body['merge_fields'])) { - return []; + $code = $details['code']; + $body = $details['body']; + + // On success return output. + if ($code >= 200 && $code <= 299) { + return $body['merge_fields'] ?? []; } - return $body['merge_fields']; + return []; } /** @@ -334,21 +344,31 @@ private function getMailchimpListFields(string $listId) */ private function getMailchimpLists() { + $url = "{$this->getBaseUrl()}lists?count=100"; + $response = \wp_remote_get( - "{$this->getBaseUrl()}lists?count=100", + $url, [ 'headers' => $this->getHeaders(), - 'timeout' => 60, ] ); - $body = \json_decode(\wp_remote_retrieve_body($response), true); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsMailchimp::SETTINGS_TYPE_KEY, + $response, + $url, + ); - if (!isset($body['lists'])) { - return []; + $code = $details['code']; + $body = $details['body']; + + // On success return output. + if ($code >= 200 && $code <= 299) { + return $body['lists'] ?? []; } - return $body['lists']; + return []; } /** @@ -362,40 +382,37 @@ private function prepareParams(array $params): array { $output = []; - if (isset($params['email_address'])) { - unset($params['email_address']); - } - - if (isset($params[Mailchimp::FIELD_MAILCHIMP_TAGS_KEY])) { - unset($params[Mailchimp::FIELD_MAILCHIMP_TAGS_KEY]); - } + $customFields = \array_flip(Components::flattenArray(AbstractBaseRoute::CUSTOM_FORM_PARAMS)); foreach ($params as $key => $param) { $value = $param['value'] ?? ''; - switch ($param['name']) { - case 'address': - if ($value) { - $output[$key] = [ - 'addr1' => $value, - 'addr2' => '', - 'city' => '&sbsp;', - 'state' => '', - 'zip' => '&sbsp;', - 'country' => '', - ]; - } - break; - default: - $output[$key] = $value; - break; + // Remove email. + if ($key === 'email_address') { + continue; } - } - if (isset($params['es-form-storage'])) { - unset($params['es-form-storage']); - } + // Check for custom address. + if ($key === 'ADDRESS' && $value) { + $output[$key] = [ + 'addr1' => $value, + 'addr2' => '', + 'city' => '&sbsp;', + 'state' => '', + 'zip' => '&sbsp;', + 'country' => '', + ]; + continue; + } + + // Remove unnecessary fields. + if (isset($customFields[$key])) { + continue; + } + + $output[$key] = $value; + } return $output; } @@ -409,7 +426,7 @@ private function prepareParams(array $params): array */ private function prepareTags(array $params): array { - $key = Mailchimp::FIELD_MAILCHIMP_TAGS_KEY; + $key = Mailchimp::CUSTOM_FORM_PARAM_MAILCHIMP_TAGS; if (!isset($params[$key])) { return []; diff --git a/src/Integrations/Mailchimp/SettingsMailchimp.php b/src/Integrations/Mailchimp/SettingsMailchimp.php index 0172b11d1..d9fd83d6e 100644 --- a/src/Integrations/Mailchimp/SettingsMailchimp.php +++ b/src/Integrations/Mailchimp/SettingsMailchimp.php @@ -18,7 +18,9 @@ use EightshiftForms\Integrations\ClientInterface; use EightshiftForms\Integrations\MapperInterface; use EightshiftForms\Settings\GlobalSettings\SettingsGlobalDataInterface; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\Settings\SettingsDataInterface; +use EightshiftForms\Troubleshooting\SettingsTroubleshootingDataInterface; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; /** @@ -105,18 +107,28 @@ class SettingsMailchimp implements SettingsDataInterface, SettingsGlobalDataInte */ protected $mailchimp; + /** + * Instance variable for Troubleshooting settings. + * + * @var SettingsTroubleshootingDataInterface + */ + protected $settingsTroubleshooting; + /** * Create a new instance. * * @param MailchimpClientInterface $mailchimpClient Inject Mailchimp which holds Mailchimp connect data. * @param MapperInterface $mailchimp Inject Mailchimp which holds Mailchimp form data. + * @param SettingsTroubleshootingDataInterface $settingsTroubleshooting Inject Troubleshooting which holds Troubleshooting settings data. */ public function __construct( MailchimpClientInterface $mailchimpClient, - MapperInterface $mailchimp + MapperInterface $mailchimp, + SettingsTroubleshootingDataInterface $settingsTroubleshooting ) { $this->mailchimpClient = $mailchimpClient; $this->mailchimp = $mailchimp; + $this->settingsTroubleshooting = $settingsTroubleshooting; } /** @@ -182,6 +194,7 @@ public function getSettingsSidebar(): array 'label' => \__('Mailchimp', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_INTEGRATION, ]; } @@ -410,7 +423,7 @@ function ($tag) use ($formId) { $this->mailchimp->getFormFields($formId), $formId, [ - Mailchimp::FIELD_MAILCHIMP_TAGS_KEY + Mailchimp::CUSTOM_FORM_PARAM_MAILCHIMP_TAGS ] ), ] @@ -491,6 +504,9 @@ public function getSettingsGlobalData(): array ); } - return $output; + return [ + ...$output, + ...$this->settingsTroubleshooting->getOutputGlobalTroubleshooting(SettingsMailchimp::SETTINGS_TYPE_KEY), + ]; } } diff --git a/src/Integrations/Mailerlite/MailerliteClient.php b/src/Integrations/Mailerlite/MailerliteClient.php index 5ace4befc..6f04c7af3 100644 --- a/src/Integrations/Mailerlite/MailerliteClient.php +++ b/src/Integrations/Mailerlite/MailerliteClient.php @@ -10,10 +10,12 @@ namespace EightshiftForms\Integrations\Mailerlite; -use EightshiftForms\Helpers\Helper; use EightshiftForms\Hooks\Variables; use EightshiftForms\Integrations\ClientInterface; +use EightshiftForms\Rest\ApiHelper; +use EightshiftForms\Rest\Routes\AbstractBaseRoute; use EightshiftForms\Settings\SettingsHelper; +use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; /** * MailerliteClient integration class. @@ -25,6 +27,11 @@ class MailerliteClient implements ClientInterface */ use SettingsHelper; + /** + * Use API helper trait. + */ + use ApiHelper; + /** * Return Mailerlite base url. * @@ -130,69 +137,53 @@ public function postApplication(string $itemId, array $params, array $files, str 'fields' => $this->prepareParams($params), ]; - $response = \wp_remote_request( - self::BASE_URL . "groups/{$itemId}/subscribers", + $url = self::BASE_URL . "groups/{$itemId}/subscribers"; + + $response = \wp_remote_post( + $url, [ 'headers' => $this->getHeaders(), - 'method' => 'POST', 'body' => \wp_json_encode($body), ] ); - if (\is_wp_error($response)) { - Helper::logger([ - 'integration' => 'mailerlite', - 'type' => 'wp', - 'body' => $body, - 'response' => $response, - ]); - return [ - 'status' => 'error', - 'code' => 400, - 'message' => $this->getErrorMsg('submitWpError'), - ]; - } + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsMailerlite::SETTINGS_TYPE_KEY, + $response, + $url, + $body, + $files, + $itemId, + $formId + ); - $code = $response['response']['code'] ? $response['response']['code'] : 200; + $code = $details['code']; + $body = $details['body']; - if ($code === 200) { - return [ - 'status' => 'success', - 'code' => $code, - 'message' => 'mailerliteSuccess', - ]; + // On success return output. + if ($code >= 200 && $code <= 299) { + return $this->getApiSuccessOutput($details); } - $responseBody = \json_decode(\wp_remote_retrieve_body($response), true); - $responseMessage = $responseBody['error']['message'] ?? ''; - - $output = [ - 'status' => 'error', - 'code' => $code, - 'message' => $this->getErrorMsg($responseMessage), - ]; - - Helper::logger([ - 'integration' => 'mailerlite', - 'type' => 'service', - 'body' => $body, - 'response' => $response['response'], - 'responseBody' => $responseBody, - 'output' => $output, - ]); - - return $output; + // Output error. + return $this->getApiErrorOutput( + $details, + $this->getErrorMsg($body) + ); } /** * Map service messages with our own. * - * @param string $msg Message got from the API. + * @param array $body API response body. * * @return string */ - private function getErrorMsg(string $msg): string + private function getErrorMsg(array $body): string { + $msg = $body['error']['message'] ?? ''; + switch ($msg) { case 'Bad Request': return 'mailerliteBadRequestError'; @@ -227,17 +218,31 @@ private function getHeaders(): array */ private function getMailerliteListFields() { + $url = self::BASE_URL . "fields"; + $response = \wp_remote_get( - self::BASE_URL . "fields", + $url, [ 'headers' => $this->getHeaders(), - 'timeout' => 60, ] ); - $body = \json_decode(\wp_remote_retrieve_body($response), true); + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsMailerlite::SETTINGS_TYPE_KEY, + $response, + $url, + ); - return $body ?? []; + $code = $details['code']; + $body = $details['body']; + + // On success return output. + if ($code >= 200 && $code <= 299) { + return $body ?? []; + } + + return []; } /** @@ -247,15 +252,31 @@ private function getMailerliteListFields() */ private function getMailerliteLists() { + $url = self::BASE_URL . "groups"; + $response = \wp_remote_get( - self::BASE_URL . "groups", + $url, [ 'headers' => $this->getHeaders(), - 'timeout' => 60, ] ); - return \json_decode(\wp_remote_retrieve_body($response), true) ?? []; + // Structure response details. + $details = $this->getApiReponseDetails( + SettingsMailerlite::SETTINGS_TYPE_KEY, + $response, + $url, + ); + + $code = $details['code']; + $body = $details['body']; + + // On success return output. + if ($code >= 200 && $code <= 299) { + return $body ?? []; + } + + return []; } /** @@ -269,16 +290,20 @@ private function prepareParams(array $params): array { $output = []; - if (isset($params['email'])) { - unset($params['email']); - } + $customFields = \array_flip(Components::flattenArray(AbstractBaseRoute::CUSTOM_FORM_PARAMS)); - foreach ($params as $key => $value) { - $output[$key] = $value['value'] ?? ''; - } + foreach ($params as $key => $param) { + // Remove email. + if ($key === 'email') { + continue; + } + + // Remove unnecessary fields. + if (isset($customFields[$key])) { + continue; + } - if (isset($params['es-form-storage'])) { - unset($params['es-form-storage']); + $output[$key] = $param['value'] ?? ''; } return $output; diff --git a/src/Integrations/Mailerlite/SettingsMailerlite.php b/src/Integrations/Mailerlite/SettingsMailerlite.php index 157eb7d53..31a0a1dd4 100644 --- a/src/Integrations/Mailerlite/SettingsMailerlite.php +++ b/src/Integrations/Mailerlite/SettingsMailerlite.php @@ -17,7 +17,9 @@ use EightshiftForms\Hooks\Variables; use EightshiftForms\Integrations\ClientInterface; use EightshiftForms\Integrations\MapperInterface; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\Settings\SettingsDataInterface; +use EightshiftForms\Troubleshooting\SettingsTroubleshootingDataInterface; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; /** @@ -89,18 +91,28 @@ class SettingsMailerlite implements SettingsDataInterface, ServiceInterface */ protected $mailerlite; + /** + * Instance variable for Troubleshooting settings. + * + * @var SettingsTroubleshootingDataInterface + */ + protected $settingsTroubleshooting; + /** * Create a new instance. * * @param ClientInterface $mailerliteClient Inject Mailerlite which holds Mailerlite connect data. * @param MapperInterface $mailerlite Inject Mailerlite which holds Mailerlite form data. + * @param SettingsTroubleshootingDataInterface $settingsTroubleshooting Inject Troubleshooting which holds Troubleshooting settings data. */ public function __construct( ClientInterface $mailerliteClient, - MapperInterface $mailerlite + MapperInterface $mailerlite, + SettingsTroubleshootingDataInterface $settingsTroubleshooting ) { $this->mailerliteClient = $mailerliteClient; $this->mailerlite = $mailerlite; + $this->settingsTroubleshooting = $settingsTroubleshooting; } /** * Register all the hooks @@ -165,6 +177,7 @@ public function getSettingsSidebar(): array 'label' => \__('MailerLite', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_INTEGRATION, ]; } @@ -356,6 +369,9 @@ public function getSettingsGlobalData(): array ); } - return $output; + return [ + ...$output, + ...$this->settingsTroubleshooting->getOutputGlobalTroubleshooting(SettingsMailerlite::SETTINGS_TYPE_KEY), + ]; } } diff --git a/src/Labels/Labels.php b/src/Labels/Labels.php index 53f3dcae1..6814bd8ea 100644 --- a/src/Labels/Labels.php +++ b/src/Labels/Labels.php @@ -39,7 +39,7 @@ class Labels implements LabelsInterface 'hubspotSuccess', 'mailerliteSuccess', 'goodbitsSuccess', - 'clearbitSuccess', + 'customSuccess', 'activeCampaignSuccess', ]; diff --git a/src/Mailer/Mailer.php b/src/Mailer/Mailer.php index 2389c7af4..d66df76b4 100644 --- a/src/Mailer/Mailer.php +++ b/src/Mailer/Mailer.php @@ -10,7 +10,11 @@ namespace EightshiftForms\Mailer; +use CURLFile; +use EightshiftForms\Rest\Routes\AbstractBaseRoute; use EightshiftForms\Settings\SettingsHelper; +use EightshiftForms\Troubleshooting\SettingsTroubleshooting; +use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; /** * Class Mailer @@ -62,6 +66,100 @@ public function sendFormEmail( return \wp_mail($to, $subject, $templateHtml, $headers, $files); } + /** + * Send fallback email + * + * @param array $data Data to extract data from. + * + * @return boolean + */ + public function fallbackEmail(array $data): bool + { + $isSettingsValid = \apply_filters(SettingsTroubleshooting::FILTER_SETTINGS_IS_VALID_NAME, []); + + if (!$isSettingsValid) { + return false; + } + + $integration = $data['integration'] ?? ''; + $url = $data['url'] ?? ''; + $files = $data['files'] ?? []; + $response = $data['response'] ? \wp_json_encode($data['response']) : ''; + $formId = $data['formId'] ?? ''; + $listId = $data['listId'] ?? ''; + $params = $data['params'] ?? []; + $code = $data['code'] ?? 400; + $body = $data['body'] ? \wp_json_encode($data['body']) : ''; + + if (\is_array($listId)) { + $listId = \implode(', ', $listId); + } + + $paramsOutput = " +

Form Details:

+
    +
  • formId: {$formId}
  • +
  • listId: {$listId}
  • +
  • integration: {$integration}
  • +
  • response code: {$code}
  • +
  • url: {$url}
  • +
+ "; + + if ($params) { + $paramsOutput .= "

Data sent to integration:

"; + $paramsOutput .= $this->fallbackEmailPrepareParams($params); + } + + if ($response) { + $paramsOutput .= " +

Data got from integration response:

+ {$response} + "; + } + + if ($body) { + $paramsOutput .= " +

Data got from integration response body:

+ {$body} + "; + } + + $filesOutput = []; + if ($files) { + foreach ($files as $file) { + if ($file instanceof CURLFile) { + $filesOutput[] = $file->name; + } + + if (\is_array($file)) { + foreach ($file as $fileItem) { + if (isset($fileItem['path'])) { + $filesOutput[] = $fileItem['path']; + } + } + } + } + } + + $to = $this->getOptionValue(SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_FALLBACK_EMAIL_KEY); + $cc = $this->getOptionValue(SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_FALLBACK_EMAIL_KEY . '-' . $integration); + // translators: %1$s replaces the integration name and %2$s formId. + $subject = \sprintf(\__('Your %1$s form failed: %2$s', 'eightshift-forms'), $integration, $formId); + // translators: %s replaces the parameters list html. + $templateHtml = \sprintf(\__("

It looks like something went wrong with the users form submition, here is all the data to debug.

%s", 'eightshift-forms'), $paramsOutput); + $headers = [ + $this->getType() + ]; + + if ($cc) { + $headers[] = "Cc: {$cc}"; + } + + // Send email. + return \wp_mail($to, $subject, $templateHtml, $headers, $filesOutput); + } + /** * Get Email type. * We use HTML for all. @@ -161,17 +259,20 @@ protected function prepareFields(array $fields): array { $output = []; - foreach ($fields as $field) { + $customFields = \array_flip(Components::flattenArray(AbstractBaseRoute::CUSTOM_FORM_PARAMS)); + + foreach ($fields as $key => $param) { + // Remove unnecessary fields. + if (isset($customFields[$key])) { + continue; + } + $output[] = [ - 'name' => $field['name'] ?? '', - 'value' => $field['value'] ?? '', + 'name' => $param['name'] ?? '', + 'value' => $param['value'] ?? '', ]; } - if (isset($fields['es-form-storage'])) { - unset($fields['es-form-storage']); - } - return $output; } @@ -208,4 +309,30 @@ protected function prepareFiles(array $files): array return $output; } + + /** + * Prepare recursive params for fallback email. + * + * @param array $params Params to check. + * + * @return string + */ + private function fallbackEmailPrepareParams(array $params): string + { + $output = ''; + + foreach ($params as $paramKey => $paramValue) { + if (\is_array($paramValue)) { + $paramValueOutput = '
    '; + $paramValueOutput .= $this->fallbackEmailPrepareParams($paramValue); + $paramValueOutput .= '
'; + + $paramValue = $paramValueOutput; + } + + $output .= "
  • {$paramKey}: {$paramValue}
  • "; + } + + return $output; + } } diff --git a/src/Mailer/MailerInterface.php b/src/Mailer/MailerInterface.php index d301e7b0c..706795c74 100644 --- a/src/Mailer/MailerInterface.php +++ b/src/Mailer/MailerInterface.php @@ -35,4 +35,13 @@ public function sendFormEmail( array $files = [], array $fields = [] ): bool; + + /** + * Send fallback email. + * + * @param array $data Data to extract data from. + * + * @return boolean + */ + public function fallbackEmail(array $data): bool; } diff --git a/src/Mailer/SettingsMailer.php b/src/Mailer/SettingsMailer.php index a47baded5..b0717819f 100644 --- a/src/Mailer/SettingsMailer.php +++ b/src/Mailer/SettingsMailer.php @@ -12,6 +12,7 @@ use EightshiftForms\Helpers\Helper; use EightshiftForms\Hooks\Filters; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\SettingsHelper; use EightshiftForms\Settings\Settings\SettingsDataInterface; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; @@ -135,6 +136,7 @@ public function getSettingsSidebar(): array 'label' => \__('Mailer', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_GENERAL, ]; } diff --git a/src/Rest/ApiHelper.php b/src/Rest/ApiHelper.php index 438c4ed65..06566ae91 100644 --- a/src/Rest/ApiHelper.php +++ b/src/Rest/ApiHelper.php @@ -11,6 +11,8 @@ namespace EightshiftForms\Rest; use EightshiftForms\Helpers\Helper; +use EightshiftForms\Settings\SettingsHelper; +use EightshiftForms\Troubleshooting\SettingsTroubleshooting; use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; /** @@ -19,35 +21,9 @@ trait ApiHelper { /** - * Return API response array with logger. - * - * @param string $integration Integration name. - * @param array $details Details from response. - * @param array $requestBody Request body sent to the API. - * @param string $msg Msg for the user. - * - * @return array + * Use general helper trait. */ - public function getApiWpErrorOutput( - string $integration, - array $details, - array $requestBody, - string $msg - ): array { - Helper::logger([ - 'integration' => Components::kebabToCamelCase($integration, '-'), - 'type' => 'wp', - 'response' => $details, - 'requestBody' => $requestBody, - 'msg' => $msg, - ]); - - return [ - 'status' => 'error', - 'code' => 400, - 'message' => 'submitWpError', - ]; - } + use SettingsHelper; /** * Return API response array details. @@ -56,64 +32,90 @@ public function getApiWpErrorOutput( * * @return array */ - public function getApiReponseDetails($response): array - { + /** + * Return API response array details. + * + * @param string $integration Integration name from settings. + * @param array $response API full reponse. + * @param string $url Url of the request. + * @param array $params All params prepared for API. + * @param array $files All files prepared for API. + * @param string $listId List Id used for API (questions, form id, list id, item id). + * @param string $formId Internal form ID. + * @param boolean $isCurl Used for some changed if native cURL is used. + * + * @return array + */ + public function getApiReponseDetails( + string $integration, + array $response, + string $url, + array $params = [], + array $files = [], + string $listId = '', + string $formId = '', + bool $isCurl = false + ): array { + if ($isCurl) { + $code = $response['status'] ?? 200; + $body = $response; + } else { + $code = $response['response']['code'] ?? 200; + $body = \json_decode($response['body'] ?? '', true) ?? []; + } + return [ - 'code' => $response['response']['code'] ? $response['response']['code'] : 200, - 'body' => \json_decode(\wp_remote_retrieve_body($response), true) ?? [], + 'integration' => Components::kebabToCamelCase($integration, '-'), + 'params' => $params, + 'files' => $files, 'response' => $response['response'] ?? [], - 'url' => $response['url'] ?? '', + 'code' => $code, + 'body' => $body, + 'url' => $url, + 'listId' => $listId, + 'formId' => $formId, ]; } /** * Return API error response array with logger. * - * @param string $integration Integration name. - * @param array $details Details from response. - * @param array $requestBody Request body sent to the API. + * @param array $details Details provided by getApiReponseDetails method. * @param string $msg Msg for the user. * - * @return array + * @return array */ - public function getApiErrorOutput( - string $integration, - array $details, - array $requestBody, - string $msg - ): array { - // Log output. - Helper::logger([ - 'integration' => Components::kebabToCamelCase($integration, '-'), - 'type' => 'service', - 'response' => $details, - 'requestBody' => $requestBody, - 'msg' => $msg, - ]); + public function getApiErrorOutput(array $details, string $msg): array + { + if ($this->isCheckboxOptionChecked(SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_LOG_MODE_KEY, SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_DEBUGGING_KEY)) { + Helper::logger($details); + } return [ 'status' => 'error', - 'code' => $details['code'], + 'code' => $details['code'] ?? 400, 'message' => $msg, + 'data' => $details, ]; } /** * Return API success response array. * - * @param string $integration Integration name. + * @param array $details Details provided by getApiReponseDetails method. * @param array $additional Additional array details to attach to the success output. * - * @return array + * @return array */ - public function getApiSuccessOutput(string $integration, array $additional = []): array + public function getApiSuccessOutput(array $details, array $additional = []): array { - $integration = Components::kebabToCamelCase($integration, '-'); + + $integration = $details['integration'] ?? ''; return \array_merge( [ 'status' => 'success', - 'code' => 200, + 'code' => $details['code'] ?? 200, 'message' => "{$integration}Success", ], $additional diff --git a/src/Rest/Routes/AbstractBaseRoute.php b/src/Rest/Routes/AbstractBaseRoute.php index 7b120ec8d..528d1fb60 100644 --- a/src/Rest/Routes/AbstractBaseRoute.php +++ b/src/Rest/Routes/AbstractBaseRoute.php @@ -12,6 +12,8 @@ use EightshiftForms\Config\Config; use EightshiftForms\Exception\UnverifiedRequestException; +use EightshiftForms\Integrations\Hubspot\Hubspot; +use EightshiftForms\Integrations\Mailchimp\Mailchimp; use EightshiftFormsVendor\EightshiftLibs\Rest\Routes\AbstractRoute; use EightshiftFormsVendor\EightshiftLibs\Rest\CallableRouteInterface; use EightshiftForms\Validation\Validator; // phpcs:ignore @@ -23,6 +25,56 @@ */ abstract class AbstractBaseRoute extends AbstractRoute implements CallableRouteInterface { + /** + * Custom form param for post ID. + * + * @var string + */ + public const CUSTOM_FORM_PARAM_POST_ID = 'es-form-post-id'; + + /** + * Custom form param for type. + * + * @var string + */ + public const CUSTOM_FORM_PARAM_TYPE = 'es-form-type'; + + /** + * Custom form param for single submit. + * + * @var string + */ + public const CUSTOM_FORM_PARAM_SINGLE_SUBMIT = 'es-form-single-submit'; + + /** + * Custom form param for storage. + * + * @var string + */ + public const CUSTOM_FORM_PARAM_STORAGE = 'es-form-storage'; + + /** + * Custom form param for action. + * + * @var string + */ + public const CUSTOM_FORM_PARAM_ACTION = 'es-form-action'; + + /** + * List of all custom form params used. + */ + public const CUSTOM_FORM_PARAMS = [ + 'postId' => self::CUSTOM_FORM_PARAM_POST_ID, + 'type' => self::CUSTOM_FORM_PARAM_TYPE, + 'singleSubmit' => self::CUSTOM_FORM_PARAM_SINGLE_SUBMIT, + 'storage' => self::CUSTOM_FORM_PARAM_STORAGE, + 'action' => self::CUSTOM_FORM_PARAM_ACTION, + 'hubspotCookie' => Hubspot::CUSTOM_FORM_PARAM_HUBSPOT_COOKIE, + 'hubspotPageName' => Hubspot::CUSTOM_FORM_PARAM_HUBSPOT_PAGE_NAME, + 'hubspotPageUrl' => Hubspot::CUSTOM_FORM_PARAM_HUBSPOT_PAGE_URL, + 'mailchimpTags' => Mailchimp::CUSTOM_FORM_PARAM_MAILCHIMP_TAGS, + ]; + /** * Method that returns project Route namespace. * @@ -210,14 +262,18 @@ static function ($item) { * * @param array $params Array of params got from form. * + * @throws UnverifiedRequestException Wrong request response. + * * @return string */ protected function getFormType(array $params): string { - $formType = $params['es-form-type'] ?? ''; + $formType = $params[self::CUSTOM_FORM_PARAM_TYPE] ?? ''; if (!$formType) { - return ''; + throw new UnverifiedRequestException( + \__('Something went wrong while submitting your form. Please try again.', 'eightshift-forms') + ); } return $formType['value'] ?? ''; @@ -254,12 +310,15 @@ protected function getSenderDetails(array $params): array * Return form ID from form params and determins if ID needs decrypting. * * @param array $params Array of params got from form. + * @param bool $throwError Throw error if missing post Id. + * + * @throws UnverifiedRequestException Wrong request response. * * @return string */ - protected function getFormId(array $params): string + protected function getFormId(array $params, bool $throwError = true): string { - $formId = $params['es-form-post-id'] ?? ''; + $formId = $params[self::CUSTOM_FORM_PARAM_POST_ID] ?? ''; if (!$formId) { return ''; @@ -267,34 +326,13 @@ protected function getFormId(array $params): string $formId = $formId['value'] ?? ''; - return $formId; - } - - /** - * Remove uncesesery params before submitting data to validation. - * - * @param array $params Array of params got from form. - * - * @return array - */ - protected function removeUneceseryParams(array $params): array - { - foreach ($params as $key => $value) { - if ($key === 'es-form-type') { - // Allow action parameter for forms with custom actions. - if ($value['value'] !== 'custom') { - unset($params['action']); - } - - unset($params['es-form-type']); - } - - if ($key === 'es-form-post-id') { - unset($params['es-form-post-id']); - } + if (!$formId && $throwError) { + throw new UnverifiedRequestException( + \__('Something went wrong while submitting your form. Please try again.', 'eightshift-forms') + ); } - return $params; + return $formId; } /** @@ -306,11 +344,11 @@ protected function removeUneceseryParams(array $params): array */ protected function extractStorageParams(array $params): array { - if (!isset($params['es-form-storage'])) { + if (!isset($params[self::CUSTOM_FORM_PARAM_STORAGE])) { return $params; } - $storage = $params['es-form-storage']['value'] ?? []; + $storage = $params[self::CUSTOM_FORM_PARAM_STORAGE]['value'] ?? []; if (!$storage) { return $params; @@ -318,7 +356,7 @@ protected function extractStorageParams(array $params): array $storage = \json_decode($storage, true); - $params['es-form-storage']['value'] = $storage; + $params[self::CUSTOM_FORM_PARAM_STORAGE]['value'] = $storage; return $params; } diff --git a/src/Rest/Routes/AbstractFormSubmit.php b/src/Rest/Routes/AbstractFormSubmit.php index 48a5b9264..07a755772 100644 --- a/src/Rest/Routes/AbstractFormSubmit.php +++ b/src/Rest/Routes/AbstractFormSubmit.php @@ -14,7 +14,7 @@ use EightshiftForms\Settings\SettingsHelper; use EightshiftForms\Helpers\UploadHelper; use EightshiftForms\Hooks\Filters; -use EightshiftForms\Hooks\Variables; +use EightshiftForms\Troubleshooting\SettingsTroubleshooting; use WP_REST_Request; /** @@ -51,6 +51,8 @@ protected function getCallbackArguments(): array * * @param WP_REST_Request $request Data got from endpoint url. * + * @throws UnverifiedRequestException Wrong config error. + * * @return WP_REST_Response|mixed If response generated an error, WP_Error, if response * is already an instance, WP_HTTP_Response, otherwise * returns a new WP_REST_Response instance. @@ -66,6 +68,12 @@ public function routeCallback(WP_REST_Request $request) // Get form ID. $formId = $this->getFormId($params); + if (!$formId) { + throw new UnverifiedRequestException( + \esc_html__('Invalid nonce.', 'eightshift-forms') + ); + } + // Determine form type. $formType = $this->getFormType($params); @@ -73,7 +81,7 @@ public function routeCallback(WP_REST_Request $request) $formData = isset(Filters::ALL[$formType]['fields']) ? \apply_filters(Filters::ALL[$formType]['fields'], $formId) : []; // Validate request. - if (!Variables::skipFormValidation()) { + if (!$this->isCheckboxOptionChecked(SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_SKIP_VALIDATION_KEY, SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_DEBUGGING_KEY)) { $this->verifyRequest( $params, $request->get_file_params(), @@ -82,9 +90,6 @@ public function routeCallback(WP_REST_Request $request) ); } - // Remove unecesery internal params before continue. - $params = $this->removeUneceseryParams($params); - // Extract hidden params from local storage set on the frontend. $params = $this->extractStorageParams($params); diff --git a/src/Rest/Routes/CacheDeleteRoute.php b/src/Rest/Routes/CacheDeleteRoute.php index 30a32903d..17043d9a7 100644 --- a/src/Rest/Routes/CacheDeleteRoute.php +++ b/src/Rest/Routes/CacheDeleteRoute.php @@ -82,7 +82,7 @@ public function routeCallback(WP_REST_Request $request) \rest_ensure_response([ 'code' => 400, 'status' => 'error', - 'message' => \esc_html__('Error, you don\'t have enough permissions to perform this action!', 'eightshift-form'), + 'message' => \esc_html__('Error: you don\'t have enough permissions to perform this action!', 'eightshift-forms'), ]); } @@ -92,7 +92,7 @@ public function routeCallback(WP_REST_Request $request) return \rest_ensure_response([ 'code' => 400, 'status' => 'error', - 'message' => \esc_html__('Error, no cache type key was provided.', 'eightshift-form'), + 'message' => \esc_html__('Error: cache type key was not provided.', 'eightshift-forms'), ]); } @@ -102,7 +102,7 @@ public function routeCallback(WP_REST_Request $request) return \rest_ensure_response([ 'code' => 400, 'status' => 'error', - 'message' => \esc_html__('Error, provided cache type doesn\'t exist.', 'eightshift-form'), + 'message' => \esc_html__('Error: provided cache type doesn\'t exist.', 'eightshift-forms'), ]); } @@ -114,7 +114,7 @@ public function routeCallback(WP_REST_Request $request) 'code' => 200, 'status' => 'success', // translators: %s will be replaced with the cache type. - 'message' => \sprintf(\esc_html__('%s cache successfully deleted!', 'eightshift-form'), \ucfirst($type)), + 'message' => \sprintf(\esc_html__('%s cache deleted successfully!', 'eightshift-forms'), \ucfirst($type)), ]); } } diff --git a/src/Rest/Routes/FormSettingsSubmitRoute.php b/src/Rest/Routes/FormSettingsSubmitRoute.php index 063af6846..e2a2d564a 100644 --- a/src/Rest/Routes/FormSettingsSubmitRoute.php +++ b/src/Rest/Routes/FormSettingsSubmitRoute.php @@ -14,8 +14,10 @@ use EightshiftForms\Cache\SettingsCache; use EightshiftForms\Exception\UnverifiedRequestException; use EightshiftForms\Hooks\Filters; -use EightshiftForms\Hooks\Variables; +use EightshiftForms\Settings\SettingsHelper; +use EightshiftForms\Troubleshooting\SettingsTroubleshooting; use EightshiftForms\Validation\ValidatorInterface; +use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; use WP_REST_Request; /** @@ -23,6 +25,11 @@ */ class FormSettingsSubmitRoute extends AbstractBaseRoute { + /** + * Use general helper trait. + */ + use SettingsHelper; + /** * Instance variable of ValidatorInterface data. * @@ -82,12 +89,12 @@ protected function getCallbackArguments(): array public function routeCallback(WP_REST_Request $request) { - // Try catch request. + // Try catch request. try { $params = $this->prepareParams($request->get_body_params()); - // Get encripted form ID and decrypt it. - $formId = $this->getFormId($params); + // Get encrypted form ID and decrypt it. + $formId = $this->getFormId($params, false); // Determine form type. $formType = $this->getFormType($params); @@ -102,7 +109,7 @@ public function routeCallback(WP_REST_Request $request) $formData = isset(Filters::ALL[$formType][$formInternalType]) ? \apply_filters(Filters::ALL[$formType][$formInternalType], $formId) : []; // Validate request. - if (!Variables::skipFormValidation()) { + if (!$this->isCheckboxOptionChecked(SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_SKIP_VALIDATION_KEY, SettingsTroubleshooting::SETTINGS_TROUBLESHOOTING_DEBUGGING_KEY)) { $this->verifyRequest( $params, $request->get_file_params(), @@ -111,8 +118,15 @@ public function routeCallback(WP_REST_Request $request) ); } - // Remove unecesery internal params before continue. - $params = $this->removeUneceseryParams($params); + // Remove unnecessary internal params before continue. + $customFields = \array_flip(Components::flattenArray(AbstractBaseRoute::CUSTOM_FORM_PARAMS)); + + // Remove unnecessary params. + foreach ($params as $key => $value) { + if (isset($customFields[$key])) { + unset($params[$key]); + } + } // Determine form type to use. switch ($formType) { @@ -147,7 +161,7 @@ public function routeCallback(WP_REST_Request $request) return \rest_ensure_response([ 'code' => 200, 'status' => 'success', - 'message' => \esc_html__('Changes saved!', 'eightshift-form'), + 'message' => \esc_html__('Changes saved!', 'eightshift-forms'), ]); } catch (UnverifiedRequestException $e) { // Die if any of the validation fails. @@ -175,7 +189,7 @@ private function cache(array $params) \rest_ensure_response([ 'code' => 400, 'status' => 'error', - 'message' => \esc_html__('You don\'t have enough permissions to perform this action!', 'eightshift-form'), + 'message' => \esc_html__('You don\'t have enough permissions to perform this action!', 'eightshift-forms'), ]); } @@ -192,7 +206,7 @@ private function cache(array $params) return \rest_ensure_response([ 'code' => 200, 'status' => 'success', - 'message' => \esc_html__('Selected cache successfully deleted!', 'eightshift-form'), + 'message' => \esc_html__('Selected cache successfully deleted!', 'eightshift-forms'), ]); } } diff --git a/src/Rest/Routes/FormSubmitActiveCampaignRoute.php b/src/Rest/Routes/FormSubmitActiveCampaignRoute.php index fc42ac4d2..e538fffb0 100644 --- a/src/Rest/Routes/FormSubmitActiveCampaignRoute.php +++ b/src/Rest/Routes/FormSubmitActiveCampaignRoute.php @@ -13,6 +13,7 @@ use EightshiftForms\Integrations\ActiveCampaign\ActiveCampaignClientInterface; use EightshiftForms\Integrations\ActiveCampaign\SettingsActiveCampaign; use EightshiftForms\Labels\LabelsInterface; +use EightshiftForms\Mailer\MailerInterface; use EightshiftForms\Validation\ValidatorInterface; /** @@ -41,21 +42,31 @@ class FormSubmitActiveCampaignRoute extends AbstractFormSubmit */ private $activeCampaignClient; + /** + * Instance variable of MailerInterface data. + * + * @var MailerInterface + */ + public $mailer; + /** * Create a new instance that injects classes * * @param ValidatorInterface $validator Inject ValidatorInterface which holds validation methods. * @param LabelsInterface $labels Inject LabelsInterface which holds labels data. * @param ActiveCampaignClientInterface $activeCampaignClient Inject ActiveCampaign which holds ActiveCampaign connect data. + * @param MailerInterface $mailer Inject MailerInterface which holds mailer methods. */ public function __construct( ValidatorInterface $validator, LabelsInterface $labels, - ActiveCampaignClientInterface $activeCampaignClient + ActiveCampaignClientInterface $activeCampaignClient, + MailerInterface $mailer ) { $this->validator = $validator; $this->labels = $labels; $this->activeCampaignClient = $activeCampaignClient; + $this->mailer = $mailer; } /** @@ -100,7 +111,7 @@ public function submitAction(string $formId, array $params = [], $files = []) ); // Make an additional requests to the API. - if ($response['code'] === 200 && !empty($response['contactId'])) { + if ($response['status'] === 'success' && !empty($response['contactId'])) { // If form has action to save tags. $actionTags = $params['actionTags']['value'] ?? ''; @@ -132,6 +143,11 @@ public function submitAction(string $formId, array $params = [], $files = []) } } + if ($response['status'] === 'error') { + // Send fallback email. + $this->mailer->fallbackEmail($response['data'] ?? []); + } + // Finish. return \rest_ensure_response([ 'code' => $response['code'], diff --git a/src/Rest/Routes/FormSubmitCustomRoute.php b/src/Rest/Routes/FormSubmitCustomRoute.php index 9692334f4..cc2eb39f2 100644 --- a/src/Rest/Routes/FormSubmitCustomRoute.php +++ b/src/Rest/Routes/FormSubmitCustomRoute.php @@ -12,12 +12,19 @@ use EightshiftForms\Validation\ValidatorInterface; use EightshiftForms\Labels\LabelsInterface; +use EightshiftForms\Rest\ApiHelper; +use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; /** * Class FormSubmitCustomRoute */ class FormSubmitCustomRoute extends AbstractFormSubmit { + /** + * Use api helper trait. + */ + use ApiHelper; + /** * Instance variable of ValidatorInterface data. * @@ -69,8 +76,7 @@ public function submitAction(string $formId, array $params = [], $files = []) { $body = []; - $formAction = $params['action']['value']; - unset($params['action']); + $formAction = $params[self::CUSTOM_FORM_PARAM_ACTION]['value']; // If form action is not set or empty. if (!$formAction) { @@ -81,19 +87,32 @@ public function submitAction(string $formId, array $params = [], $files = []) ]); } + // Remove unnecessary internal params before continue. + $customFields = \array_flip(Components::flattenArray(AbstractBaseRoute::CUSTOM_FORM_PARAMS)); + // Format body parameters to a key/value array. - foreach ($params as $param) { - $body[$param['name']] = $param['value']; + foreach ($params as $key => $param) { + $name = $param['name'] ?? ''; + $value = $param['value'] ?? ''; + + if ($name || !$value) { + continue; + } + + if (isset($customFields[$key])) { + continue; + } + + $body[$name] = $value; } // Create a custom form action request. - $customResponse = \wp_remote_request( + $customResponse = \wp_remote_post( $formAction, [ 'headers' => [ 'Content-Type' => 'application/x-www-form-urlencoded', ], - 'method' => 'POST', 'body' => \http_build_query($body), ] ); @@ -112,7 +131,7 @@ public function submitAction(string $formId, array $params = [], $files = []) // If form action is valid we'll return the generic success message. return \rest_ensure_response([ 'status' => 'success', - 'code' => $customResponseCode, + 'code' => 200, 'message' => $this->labels->getLabel('customSuccess', $formId), ]); } diff --git a/src/Rest/Routes/FormSubmitGoodbitsRoute.php b/src/Rest/Routes/FormSubmitGoodbitsRoute.php index f83e44279..4c06c0e67 100644 --- a/src/Rest/Routes/FormSubmitGoodbitsRoute.php +++ b/src/Rest/Routes/FormSubmitGoodbitsRoute.php @@ -15,6 +15,7 @@ use EightshiftForms\Integrations\ClientInterface; use EightshiftForms\Integrations\Goodbits\SettingsGoodbits; use EightshiftForms\Labels\LabelsInterface; +use EightshiftForms\Mailer\MailerInterface; use EightshiftForms\Validation\ValidatorInterface; /** @@ -53,21 +54,31 @@ class FormSubmitGoodbitsRoute extends AbstractFormSubmit */ protected $goodbitsClient; + /** + * Instance variable of MailerInterface data. + * + * @var MailerInterface + */ + public $mailer; + /** * Create a new instance that injects classes * * @param ValidatorInterface $validator Inject ValidatorInterface which holds validation methods. * @param LabelsInterface $labels Inject LabelsInterface which holds labels data. * @param ClientInterface $goodbitsClient Inject Goodbits which holds Goodbits connect data. + * @param MailerInterface $mailer Inject MailerInterface which holds mailer methods. */ public function __construct( ValidatorInterface $validator, LabelsInterface $labels, - ClientInterface $goodbitsClient + ClientInterface $goodbitsClient, + MailerInterface $mailer ) { $this->validator = $validator; $this->labels = $labels; $this->goodbitsClient = $goodbitsClient; + $this->mailer = $mailer; } /** @@ -112,6 +123,11 @@ public function submitAction(string $formId, array $params = [], $files = []) $formId ); + if ($response['status'] === 'error') { + // Send fallback email. + $this->mailer->fallbackEmail($response['data'] ?? []); + } + // Finish. return \rest_ensure_response([ 'code' => $response['code'], diff --git a/src/Rest/Routes/FormSubmitGreenhouseRoute.php b/src/Rest/Routes/FormSubmitGreenhouseRoute.php index 4e625fd62..7aa7fa9bd 100644 --- a/src/Rest/Routes/FormSubmitGreenhouseRoute.php +++ b/src/Rest/Routes/FormSubmitGreenhouseRoute.php @@ -15,6 +15,7 @@ use EightshiftForms\Integrations\Greenhouse\SettingsGreenhouse; use EightshiftForms\Integrations\ClientInterface; use EightshiftForms\Labels\LabelsInterface; +use EightshiftForms\Mailer\MailerInterface; use EightshiftForms\Validation\ValidatorInterface; /** @@ -53,21 +54,31 @@ class FormSubmitGreenhouseRoute extends AbstractFormSubmit */ protected $greenhouseClient; + /** + * Instance variable of MailerInterface data. + * + * @var MailerInterface + */ + public $mailer; + /** * Create a new instance that injects classes * * @param ValidatorInterface $validator Inject ValidatorInterface which holds validation methods. * @param LabelsInterface $labels Inject LabelsInterface which holds labels data. * @param ClientInterface $greenhouseClient Inject ClientInterface which holds Greenhouse connect data. + * @param MailerInterface $mailer Inject MailerInterface which holds mailer methods. */ public function __construct( ValidatorInterface $validator, LabelsInterface $labels, - ClientInterface $greenhouseClient + ClientInterface $greenhouseClient, + MailerInterface $mailer ) { $this->validator = $validator; $this->labels = $labels; $this->greenhouseClient = $greenhouseClient; + $this->mailer = $mailer; } /** @@ -112,6 +123,11 @@ public function submitAction(string $formId, array $params = [], $files = []) $formId ); + if ($response['status'] === 'error') { + // Send fallback email. + $this->mailer->fallbackEmail($response['data'] ?? []); + } + // Always delete the files from the disk. if ($files) { $this->deleteFiles($files); diff --git a/src/Rest/Routes/FormSubmitHubspotRoute.php b/src/Rest/Routes/FormSubmitHubspotRoute.php index 730208aed..546edc883 100644 --- a/src/Rest/Routes/FormSubmitHubspotRoute.php +++ b/src/Rest/Routes/FormSubmitHubspotRoute.php @@ -18,6 +18,7 @@ use EightshiftForms\Integrations\Hubspot\HubspotClientInterface; use EightshiftForms\Integrations\Hubspot\SettingsHubspot; use EightshiftForms\Labels\LabelsInterface; +use EightshiftForms\Mailer\MailerInterface; use EightshiftForms\Validation\ValidatorInterface; /** @@ -63,6 +64,13 @@ class FormSubmitHubspotRoute extends AbstractFormSubmit */ protected $clearbitClient; + /** + * Instance variable of MailerInterface data. + * + * @var MailerInterface + */ + public $mailer; + /** * Create a new instance that injects classes * @@ -70,17 +78,20 @@ class FormSubmitHubspotRoute extends AbstractFormSubmit * @param LabelsInterface $labels Inject LabelsInterface which holds labels data. * @param HubspotClientInterface $hubspotClient Inject HubSpot which holds HubSpot connect data. * @param ClearbitClientInterface $clearbitClient Inject Clearbit which holds clearbit connect data. + * @param MailerInterface $mailer Inject MailerInterface which holds mailer methods. */ public function __construct( ValidatorInterface $validator, LabelsInterface $labels, HubspotClientInterface $hubspotClient, - ClearbitClientInterface $clearbitClient + ClearbitClientInterface $clearbitClient, + MailerInterface $mailer ) { $this->validator = $validator; $this->labels = $labels; $this->hubspotClient = $hubspotClient; $this->clearbitClient = $clearbitClient; + $this->mailer = $mailer; } /** @@ -115,9 +126,11 @@ public function submitAction(string $formId, array $params = [], $files = []) ]); } + $itemId = $this->getSettingsValue(SettingsHubspot::SETTINGS_HUBSPOT_ITEM_ID_KEY, $formId); + // Send application to Hubspot. $response = $this->hubspotClient->postApplication( - $this->getSettingsValue(SettingsHubspot::SETTINGS_HUBSPOT_ITEM_ID_KEY, $formId), + $itemId, $params, $files, $formId @@ -127,22 +140,37 @@ public function submitAction(string $formId, array $params = [], $files = []) $useClearbit = \apply_filters(SettingsClearbit::FILTER_SETTINGS_IS_VALID_NAME, $formId, SettingsHubspot::SETTINGS_TYPE_KEY); if ($useClearbit) { - // Get Clearbit data. - $clearbitResponse = $this->clearbitClient->getApplication( - $this->getSettingsValue(Filters::ALL[SettingsClearbit::SETTINGS_TYPE_KEY]['integration'][SettingsHubspot::SETTINGS_TYPE_KEY]['email'], $formId), - $params, - $this->getOptionValueGroup(Filters::ALL[SettingsClearbit::SETTINGS_TYPE_KEY]['integration'][SettingsHubspot::SETTINGS_TYPE_KEY]['map']) - ); - - // If Clearbit data is ok send data to Hubspot. - if ($clearbitResponse['code'] === 200) { - $this->hubspotClient->postContactProperty( - $clearbitResponse['email'] ?? '', - $clearbitResponse['data'] ?? [] + $emailKey = $this->getSettingsValue(Filters::ALL[SettingsClearbit::SETTINGS_TYPE_KEY]['integration'][SettingsHubspot::SETTINGS_TYPE_KEY]['email'], $formId); + $email = isset($params[$emailKey]['value']) ? $params[$emailKey]['value'] : ''; + + if ($email) { + // Get Clearbit data. + $clearbitResponse = $this->clearbitClient->getApplication( + $email, + $params, + $this->getOptionValueGroup(Filters::ALL[SettingsClearbit::SETTINGS_TYPE_KEY]['integration'][SettingsHubspot::SETTINGS_TYPE_KEY]['map']), + $itemId, + $formId ); + + // If Clearbit data is ok send data to Hubspot. + if ($clearbitResponse['code'] >= 200 && $clearbitResponse['code'] <= 299) { + $this->hubspotClient->postContactProperty( + $clearbitResponse['email'] ?? '', + $clearbitResponse['data'] ?? [] + ); + } else { + // Send fallback email. + $this->mailer->fallbackEmail($clearbitResponse['data'] ?? []); + } } } + if ($response['status'] === 'error') { + // Send fallback email. + $this->mailer->fallbackEmail($response['data'] ?? []); + } + // Always delete the files from the disk. if ($files) { $this->deleteFiles($files); diff --git a/src/Rest/Routes/FormSubmitMailchimpRoute.php b/src/Rest/Routes/FormSubmitMailchimpRoute.php index 7048c34cf..42ad7e52e 100644 --- a/src/Rest/Routes/FormSubmitMailchimpRoute.php +++ b/src/Rest/Routes/FormSubmitMailchimpRoute.php @@ -13,6 +13,7 @@ use EightshiftForms\Integrations\Mailchimp\MailchimpClientInterface; use EightshiftForms\Integrations\Mailchimp\SettingsMailchimp; use EightshiftForms\Labels\LabelsInterface; +use EightshiftForms\Mailer\MailerInterface; use EightshiftForms\Validation\ValidatorInterface; /** @@ -41,21 +42,31 @@ class FormSubmitMailchimpRoute extends AbstractFormSubmit */ protected $mailchimpClient; + /** + * Instance variable of MailerInterface data. + * + * @var MailerInterface + */ + public $mailer; + /** * Create a new instance that injects classes * * @param ValidatorInterface $validator Inject ValidatorInterface which holds validation methods. * @param LabelsInterface $labels Inject LabelsInterface which holds labels data. * @param MailchimpClientInterface $mailchimpClient Inject Mailchimp which holds Mailchimp connect data. + * @param MailerInterface $mailer Inject MailerInterface which holds mailer methods. */ public function __construct( ValidatorInterface $validator, LabelsInterface $labels, - MailchimpClientInterface $mailchimpClient + MailchimpClientInterface $mailchimpClient, + MailerInterface $mailer ) { $this->validator = $validator; $this->labels = $labels; $this->mailchimpClient = $mailchimpClient; + $this->mailer = $mailer; } /** @@ -99,6 +110,11 @@ public function submitAction(string $formId, array $params = [], $files = []) $formId ); + if ($response['status'] === 'error') { + // Send fallback email. + $this->mailer->fallbackEmail($response['data'] ?? []); + } + // Finish. return \rest_ensure_response([ 'code' => $response['code'], diff --git a/src/Rest/Routes/FormSubmitMailerliteRoute.php b/src/Rest/Routes/FormSubmitMailerliteRoute.php index b1e770cb5..31ab85ca6 100644 --- a/src/Rest/Routes/FormSubmitMailerliteRoute.php +++ b/src/Rest/Routes/FormSubmitMailerliteRoute.php @@ -15,6 +15,7 @@ use EightshiftForms\Integrations\ClientInterface; use EightshiftForms\Integrations\Mailerlite\SettingsMailerlite; use EightshiftForms\Labels\LabelsInterface; +use EightshiftForms\Mailer\MailerInterface; use EightshiftForms\Validation\ValidatorInterface; /** @@ -53,21 +54,31 @@ class FormSubmitMailerliteRoute extends AbstractFormSubmit */ protected $mailerliteClient; + /** + * Instance variable of MailerInterface data. + * + * @var MailerInterface + */ + public $mailer; + /** * Create a new instance that injects classes * * @param ValidatorInterface $validator Inject ValidatorInterface which holds validation methods. * @param LabelsInterface $labels Inject LabelsInterface which holds labels data. * @param ClientInterface $mailerliteClient Inject Mailerlite which holds Mailerlite connect data. + * @param MailerInterface $mailer Inject MailerInterface which holds mailer methods. */ public function __construct( ValidatorInterface $validator, LabelsInterface $labels, - ClientInterface $mailerliteClient + ClientInterface $mailerliteClient, + MailerInterface $mailer ) { $this->validator = $validator; $this->labels = $labels; $this->mailerliteClient = $mailerliteClient; + $this->mailer = $mailer; } /** @@ -112,6 +123,11 @@ public function submitAction(string $formId, array $params = [], $files = []) $formId ); + if ($response['status'] === 'error') { + // Send fallback email. + $this->mailer->fallbackEmail($response['data'] ?? []); + } + // Finish. return \rest_ensure_response([ 'code' => $response['code'], diff --git a/src/Settings/Settings/SettingsAll.php b/src/Settings/Settings/SettingsAll.php index e5c531391..afa038747 100644 --- a/src/Settings/Settings/SettingsAll.php +++ b/src/Settings/Settings/SettingsAll.php @@ -19,6 +19,46 @@ */ class SettingsAll extends AbstractFormBuilder implements SettingsAllInterface { + /** + * Settings sidebar type - general + * + * @var string + */ + public const SETTINGS_SIEDBAR_TYPE_GENERAL = 'general'; + + /** + * Settings sidebar type - integration + * + * @var string + */ + public const SETTINGS_SIEDBAR_TYPE_INTEGRATION = 'integration'; + + /** + * Settings sidebar type - troubleshooting + * + * @var string + */ + public const SETTINGS_SIEDBAR_TYPE_TROUBLESHOOTING = 'troubleshooting'; + + /** + * Settings sidebar type - develop + * + * @var string + */ + public const SETTINGS_SIEDBAR_TYPE_DEVELOP = 'develop'; + + /** + * Sidebar Sort order. + * + * @var array + */ + public const SIDEBAR_SORT_ORDER = [ + self::SETTINGS_SIEDBAR_TYPE_GENERAL, + self::SETTINGS_SIEDBAR_TYPE_INTEGRATION, + self::SETTINGS_SIEDBAR_TYPE_TROUBLESHOOTING, + self::SETTINGS_SIEDBAR_TYPE_DEVELOP + ]; + /** * Get all settings sidebar array for building settings page. * diff --git a/src/Settings/Settings/SettingsGeneral.php b/src/Settings/Settings/SettingsGeneral.php index bc51e6f82..0676df974 100644 --- a/src/Settings/Settings/SettingsGeneral.php +++ b/src/Settings/Settings/SettingsGeneral.php @@ -101,6 +101,7 @@ public function getSettingsSidebar(): array 'label' => \__('General', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_GENERAL, ]; } diff --git a/src/Settings/Settings/SettingsLocation.php b/src/Settings/Settings/SettingsLocation.php index 925ea4700..73ef67f0e 100644 --- a/src/Settings/Settings/SettingsLocation.php +++ b/src/Settings/Settings/SettingsLocation.php @@ -62,6 +62,7 @@ public function getSettingsSidebar(): array 'label' => \__('Display locations', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_TROUBLESHOOTING, ]; } diff --git a/src/Settings/Settings/SettingsTest.php b/src/Settings/Settings/SettingsTest.php index 27acbd384..374f92de6 100644 --- a/src/Settings/Settings/SettingsTest.php +++ b/src/Settings/Settings/SettingsTest.php @@ -61,6 +61,7 @@ public function getSettingsSidebar(): array 'label' => \__('Test', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_DEVELOP, ]; } diff --git a/src/Troubleshooting/SettingsTroubleshooting.php b/src/Troubleshooting/SettingsTroubleshooting.php new file mode 100644 index 000000000..8bc9cbddd --- /dev/null +++ b/src/Troubleshooting/SettingsTroubleshooting.php @@ -0,0 +1,247 @@ +isCheckboxOptionChecked(self::SETTINGS_TROUBLESHOOTING_USE_KEY, self::SETTINGS_TROUBLESHOOTING_USE_KEY); + $email = $this->getOptionValue(self::SETTINGS_TROUBLESHOOTING_FALLBACK_EMAIL_KEY); + + if (!$isUsed || empty($email)) { + return false; + } + + return true; + } + + /** + * Get Settings sidebar data. + * + * @return array + */ + public function getSettingsSidebar(): array + { + return [ + 'label' => \__('Troubleshooting', 'eightshift-forms'), + 'value' => self::SETTINGS_TYPE_KEY, + 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_TROUBLESHOOTING, + ]; + } + + /** + * Get Form settings data array + * + * @param string $formId Form Id. + * + * @return array> + */ + public function getSettingsData(string $formId): array + { + return []; + } + + /** + * Get global settings array for building settings page. + * + * @return array> + */ + public function getSettingsGlobalData(): array + { + $output = [ + [ + 'component' => 'intro', + 'introTitle' => \__('Fallback emails', 'eightshift-forms'), + 'introSubtitle' => \__('Your forms will send email fallbacks with all the data if there is any kind of error. This email can be used to debug or provide manual input of the data to any integration.', 'eightshift-forms'), + ], + [ + 'component' => 'checkboxes', + 'checkboxesFieldLabel' => '', + 'checkboxesName' => $this->getSettingsName(self::SETTINGS_TROUBLESHOOTING_USE_KEY), + 'checkboxesId' => $this->getSettingsName(self::SETTINGS_TROUBLESHOOTING_USE_KEY), + 'checkboxesContent' => [ + [ + 'component' => 'checkbox', + 'checkboxLabel' => \__('Use Fallback emails', 'eightshift-forms'), + 'checkboxIsChecked' => $this->isCheckboxOptionChecked(self::SETTINGS_TROUBLESHOOTING_USE_KEY, self::SETTINGS_TROUBLESHOOTING_USE_KEY), + 'checkboxValue' => self::SETTINGS_TROUBLESHOOTING_USE_KEY, + 'checkboxSingleSubmit' => true, + ] + ] + ], + ]; + + $isUsedFallbackEmails = $this->isCheckboxOptionChecked(self::SETTINGS_TROUBLESHOOTING_USE_KEY, self::SETTINGS_TROUBLESHOOTING_USE_KEY); + + if ($isUsedFallbackEmails) { + $output[] = [ + 'component' => 'input', + 'inputName' => $this->getSettingsName(self::SETTINGS_TROUBLESHOOTING_FALLBACK_EMAIL_KEY), + 'inputId' => $this->getSettingsName(self::SETTINGS_TROUBLESHOOTING_FALLBACK_EMAIL_KEY), + 'inputFieldLabel' => \__('Fallback e-mail', 'eightshift-forms'), + 'inputFieldHelp' => \__('Set email where all fallback emails will be sent. Use comma to separate multiple emails.', 'eightshift-forms'), + 'inputType' => 'text', + 'inputIsRequired' => true, + 'inputValue' => $this->getOptionValue(self::SETTINGS_TROUBLESHOOTING_FALLBACK_EMAIL_KEY), + ]; + } + + $outputDebugging = [ + [ + 'component' => 'divider', + ], + [ + 'component' => 'intro', + 'introTitle' => \__('Debugging', 'eightshift-forms'), + 'introSubtitle' => \__('Settings used for debugging forms. USE WITH CAUTION!', 'eightshift-forms'), + ], + [ + 'component' => 'checkboxes', + 'checkboxesFieldLabel' => '', + 'checkboxesName' => $this->getSettingsName(self::SETTINGS_TROUBLESHOOTING_DEBUGGING_KEY), + 'checkboxesId' => $this->getSettingsName(self::SETTINGS_TROUBLESHOOTING_DEBUGGING_KEY), + 'checkboxesContent' => [ + [ + 'component' => 'checkbox', + 'checkboxLabel' => \__('Skip validation', 'eightshift-forms'), + 'checkboxIsChecked' => $this->isCheckboxOptionChecked(self::SETTINGS_TROUBLESHOOTING_SKIP_VALIDATION_KEY, self::SETTINGS_TROUBLESHOOTING_DEBUGGING_KEY), + 'checkboxValue' => self::SETTINGS_TROUBLESHOOTING_SKIP_VALIDATION_KEY, + ], + [ + 'component' => 'checkbox', + 'checkboxLabel' => \__('Skip form reset after submit', 'eightshift-forms'), + 'checkboxIsChecked' => $this->isCheckboxOptionChecked(self::SETTINGS_TROUBLESHOOTING_SKIP_RESET_KEY, self::SETTINGS_TROUBLESHOOTING_DEBUGGING_KEY), + 'checkboxValue' => self::SETTINGS_TROUBLESHOOTING_SKIP_RESET_KEY, + ], + [ + 'component' => 'checkbox', + 'checkboxLabel' => \__('Output logs', 'eightshift-forms'), + 'checkboxIsChecked' => $this->isCheckboxOptionChecked(self::SETTINGS_TROUBLESHOOTING_LOG_MODE_KEY, self::SETTINGS_TROUBLESHOOTING_DEBUGGING_KEY), + 'checkboxValue' => self::SETTINGS_TROUBLESHOOTING_LOG_MODE_KEY, + ] + ] + ], + ]; + + return [ + ...$output, + ...$outputDebugging, + ]; + } + + /** + * Output array settings for form. + * + * @param string $integration Integration name used for troubleshooting. + * + * @return array>|bool|string>> + */ + public function getOutputGlobalTroubleshooting(string $integration): array + { + $isValid = $this->isSettingsGlobalValid(); + + if (!$isValid) { + return []; + } + + return [ + [ + 'component' => 'divider', + ], + [ + 'component' => 'intro', + 'introTitle' => \__('Troubleshooting', 'eightshift-forms'), + 'introSubtitle' => \__('Your forms will send email fallbacks with all the data if there is any kind of error. This email can be used to debug or provide manual input of the data to any integration.', 'eightshift-forms'), + 'introTitleSize' => 'medium', + ], + [ + 'component' => 'input', + 'inputName' => $this->getSettingsName(self::SETTINGS_TROUBLESHOOTING_FALLBACK_EMAIL_KEY . '-' . $integration), + 'inputId' => $this->getSettingsName(self::SETTINGS_TROUBLESHOOTING_FALLBACK_EMAIL_KEY . '-' . $integration), + 'inputFieldLabel' => \__('Fallback e-mail', 'eightshift-forms'), + 'inputFieldHelp' => \__('Set email where this integration fallback emails will be sent. This field will be used as "cc", main "from" field will be used from the main Troubleshooting global setting page. Use comma to separate multiple emails.', 'eightshift-forms'), + 'inputType' => 'text', + 'inputIsRequired' => true, + 'inputValue' => $this->getOptionValue(self::SETTINGS_TROUBLESHOOTING_FALLBACK_EMAIL_KEY . '-' . $integration), + ], + ]; + } +} diff --git a/src/Troubleshooting/SettingsTroubleshootingDataInterface.php b/src/Troubleshooting/SettingsTroubleshootingDataInterface.php new file mode 100644 index 000000000..4f977666e --- /dev/null +++ b/src/Troubleshooting/SettingsTroubleshootingDataInterface.php @@ -0,0 +1,28 @@ +>|bool|string>> + */ + public function getOutputGlobalTroubleshooting(string $integration): array; +} diff --git a/src/Validation/SettingsCaptcha.php b/src/Validation/SettingsCaptcha.php index ead1a9f08..f9a0d540f 100644 --- a/src/Validation/SettingsCaptcha.php +++ b/src/Validation/SettingsCaptcha.php @@ -14,6 +14,7 @@ use EightshiftForms\Hooks\Variables; use EightshiftForms\Settings\SettingsHelper; use EightshiftForms\Labels\LabelsInterface; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\Settings\SettingsDataInterface; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; @@ -130,6 +131,7 @@ public function getSettingsSidebar(): array 'label' => \__('Captcha', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_GENERAL, ]; } diff --git a/src/Validation/SettingsValidation.php b/src/Validation/SettingsValidation.php index 4b88def7f..71189980d 100644 --- a/src/Validation/SettingsValidation.php +++ b/src/Validation/SettingsValidation.php @@ -15,6 +15,7 @@ use EightshiftForms\Settings\SettingsHelper; use EightshiftForms\Labels\LabelsInterface; use EightshiftForms\Helpers\Helper; +use EightshiftForms\Settings\Settings\SettingsAll; use EightshiftForms\Settings\Settings\SettingsDataInterface; use EightshiftFormsVendor\EightshiftLibs\Services\ServiceInterface; @@ -101,6 +102,7 @@ public function getSettingsSidebar(): array 'label' => \__('Validation', 'eightshift-forms'), 'value' => self::SETTINGS_TYPE_KEY, 'icon' => Filters::ALL[self::SETTINGS_TYPE_KEY]['icon'], + 'type' => SettingsAll::SETTINGS_SIEDBAR_TYPE_GENERAL, ]; } diff --git a/src/Validation/Validator.php b/src/Validation/Validator.php index f4740f95c..fe33f3e08 100644 --- a/src/Validation/Validator.php +++ b/src/Validation/Validator.php @@ -12,6 +12,7 @@ use EightshiftFormsVendor\EightshiftLibs\Helpers\Components; use EightshiftForms\Labels\LabelsInterface; +use EightshiftForms\Rest\Routes\AbstractBaseRoute; use EightshiftForms\Settings\SettingsHelper; use EightshiftFormsVendor\EightshiftLibs\Helpers\ObjectHelperTrait; @@ -80,7 +81,7 @@ public function __construct(LabelsInterface $labels) public function validate(array $params = [], array $files = [], string $formId = '', array $formData = []): array { // If single submit skip all validations. - if (isset($params['es-form-single-submit'])) { + if (isset($params[AbstractBaseRoute::CUSTOM_FORM_PARAM_SINGLE_SUBMIT])) { return []; } @@ -201,6 +202,12 @@ private function validateParams(array $params, array $validationReference, strin // Check params. foreach ($params as $paramKey => $paramValue) { $inputValue = $paramValue['value'] ?? ''; + $inputType = $paramValue['type'] ?? ''; + + // No need to validate hidden fields. + if ($inputType === 'hidden') { + continue; + } // Find validation reference by ID. $reference = $validationReference[$paramKey] ?? [];