Compare commits
508 Commits
5.4.2
...
update-plu
Author | SHA1 | Date | |
---|---|---|---|
c3382d314e | |||
81180caccb | |||
10c82b687d | |||
00c9bf3dcd | |||
27b66e9dc9 | |||
0a166aa9e8 | |||
1727a87e1b | |||
05b7346fe8 | |||
3e68608859 | |||
97ea3f1364 | |||
699c9851d3 | |||
3b9db4f98b | |||
79eca8133a | |||
29fc396df6 | |||
4834863505 | |||
50e986715e | |||
9abf223005 | |||
30719a7840 | |||
0616e9db7b | |||
cb4815f417 | |||
044d203ade | |||
ee8daf137e | |||
b1ed43c333 | |||
076de3b8c6 | |||
e177f3bb36 | |||
c866950d54 | |||
5470b079a1 | |||
5190ac1ff2 | |||
e2286167d8 | |||
2deceb3360 | |||
e058fbd608 | |||
40ad420cfb | |||
dc8699cff4 | |||
da00efc1dc | |||
dade6add4e | |||
8a6ae87fd5 | |||
1464e9b9f6 | |||
bc90aab4dc | |||
145bca48e4 | |||
99dbdb124f | |||
32c7e06152 | |||
7ecb2b5f80 | |||
97be547fdb | |||
ad52971692 | |||
d26fb1b026 | |||
f1abe65e8e | |||
7dfdbef152 | |||
1cb5eda659 | |||
bf5cde8363 | |||
e7169304e0 | |||
00545b3e10 | |||
6a012a8dd6 | |||
a49b978050 | |||
9a2502826f | |||
350a90f872 | |||
1afc0d6260 | |||
0100372027 | |||
78f5791990 | |||
0aba89b89c | |||
849568ac2f | |||
a0ac8862ef | |||
ee6662571f | |||
6127dda6bb | |||
02fc9b2618 | |||
ddf6570f50 | |||
fbe79ae570 | |||
66741b9bc1 | |||
f20237ab1c | |||
bd266ed403 | |||
26ea3a0d04 | |||
a28ff7da50 | |||
ecb040c4a4 | |||
441541e3eb | |||
a41aa8ad54 | |||
94115d0ef2 | |||
c5f94e97b5 | |||
dadf76c519 | |||
14e5a82a49 | |||
edb5a0982c | |||
6ac171bdeb | |||
9b65494bf8 | |||
74f2281ff0 | |||
3d45ea92e2 | |||
6f8a1716c0 | |||
997de285c9 | |||
dc28138da1 | |||
f57fcab0e7 | |||
1b565bf430 | |||
0a02295c1e | |||
b7d6437b77 | |||
23be78a735 | |||
99547cff21 | |||
d7bf0de442 | |||
ffad0ea2e6 | |||
3a87e8a14e | |||
0dd6d64a26 | |||
5ec9e3890e | |||
2c2d6936ba | |||
d7a9062791 | |||
f688ab61a2 | |||
fabc3fd3a4 | |||
0c478691bb | |||
2c7387e781 | |||
019c1a6e44 | |||
58d1a48ab5 | |||
ef4a421eea | |||
9de0e0c137 | |||
17694b3c60 | |||
8b35b447f0 | |||
9120c7da2e | |||
6f396bd22c | |||
08bdf69b52 | |||
2a1e752f63 | |||
78ef34ba8c | |||
74d71a54b2 | |||
cbd8355806 | |||
072c1c0670 | |||
7c887075d6 | |||
adc91d5451 | |||
ce44e07c59 | |||
5680b5d97e | |||
49ccabb5d8 | |||
0d1e141e2f | |||
06532320b5 | |||
d168219ecd | |||
815ff9211b | |||
3300510dc2 | |||
27dbf2f29d | |||
2146323894 | |||
17cb0baa65 | |||
c052130a60 | |||
ce9cfb9a3f | |||
f8fc171c36 | |||
2070b502ad | |||
3693abf0d4 | |||
07629938cb | |||
4059e5cef0 | |||
99d7191a23 | |||
eb03fc9bac | |||
92e9642e1f | |||
3c7b5aba85 | |||
dbe2e6c7e2 | |||
233a4438a5 | |||
e88d0613a6 | |||
9aa900e071 | |||
b2a0f09a7f | |||
142b0f3d2c | |||
7bca42a32e | |||
b98342c295 | |||
8e6c81ae75 | |||
013dd6aad8 | |||
fc7ed37f0d | |||
3f44160ab4 | |||
b53f69cddb | |||
68a338fb79 | |||
1fc1342959 | |||
7e6f268fae | |||
63710534f2 | |||
29f1b0868c | |||
ef577ff865 | |||
19b8467fdc | |||
8c38cad8d4 | |||
bba68229ac | |||
05bf20ec20 | |||
c45345bcaf | |||
5f5fed418a | |||
84b1499932 | |||
e39c00d090 | |||
a8626874f8 | |||
fc69fce1ba | |||
c69096b259 | |||
49c8561baf | |||
a8a412b405 | |||
0ae5e8c7fe | |||
afb183bd71 | |||
79ec5a5ffd | |||
59b90d2836 | |||
6c005c96d7 | |||
c05a2edfba | |||
80f35169a2 | |||
97cc600e82 | |||
5f100aa872 | |||
edf09958ef | |||
d715d16cad | |||
67c3174a95 | |||
0c56b424e7 | |||
d2be185aac | |||
7717fc8d8c | |||
7d25909f17 | |||
94f7b67ccd | |||
78a632d1bc | |||
b032314cd0 | |||
3ba6aed303 | |||
9b2f6680b6 | |||
ad83043084 | |||
a16f20b93d | |||
cea59c8e38 | |||
81c8f88ab9 | |||
711f410f53 | |||
2e207efce1 | |||
d0326c4416 | |||
0dccc4a33d | |||
3ab2cd92bf | |||
c3cb92cd47 | |||
760c6e7506 | |||
993ac809df | |||
aab23e7adc | |||
da81fc89d8 | |||
80d698c8c7 | |||
150403f158 | |||
6f9211ca57 | |||
1e21223a73 | |||
e73ffc1a79 | |||
503e111722 | |||
8b9203fe09 | |||
9f65f04271 | |||
5de7df1433 | |||
1537cacbf4 | |||
34a8625318 | |||
69aa7b906e | |||
735baa2e73 | |||
0cb66ecc40 | |||
3f48d088f3 | |||
24355167cc | |||
37ceec2277 | |||
5abca8d264 | |||
ff3d0de5da | |||
dd2c88dfbe | |||
5decd55bf7 | |||
780c72d092 | |||
7b2d113330 | |||
498cfda3b1 | |||
03f4c8d9c3 | |||
ccd07ba46d | |||
fd6a20ebf2 | |||
4cc3fc3316 | |||
6ba1e19621 | |||
378ab3bc94 | |||
e429bd99b1 | |||
d23a495d07 | |||
17cad108d9 | |||
c102ef7003 | |||
70e6a9328b | |||
9fdcf2f4b3 | |||
2115425bd8 | |||
9247c72be8 | |||
798345c6e5 | |||
74c52597c5 | |||
8f114a051f | |||
cca1748b8e | |||
1dadf26c7d | |||
54815b44f3 | |||
5a16de1521 | |||
3f6358a2fe | |||
52e9a98093 | |||
16e66ac62b | |||
e7d015895a | |||
99ad2e71ac | |||
11743ddcd4 | |||
0a35f88ca7 | |||
161ba6a8e7 | |||
dd37e6a543 | |||
69d1714487 | |||
29254bc962 | |||
aa2987352e | |||
a2b75a73f3 | |||
1ebfa4116c | |||
9ae0f6a0fa | |||
3c14b2b309 | |||
1161fcef20 | |||
5fb8247163 | |||
68e8bd36dc | |||
8466897f28 | |||
a49729bbfa | |||
5ee615dbb0 | |||
09747dd98a | |||
f368745289 | |||
713ef8d83c | |||
ab50ce5288 | |||
662593464e | |||
1fbfcf0fb0 | |||
a617062e72 | |||
e97869e6eb | |||
32f4594239 | |||
0289ed0362 | |||
254191b669 | |||
1c0ace86af | |||
106d22448f | |||
3eed54875c | |||
8ca2e50321 | |||
65728f6315 | |||
9876538c14 | |||
aad7030f3c | |||
d7c10a48b6 | |||
709d5a8177 | |||
e58405490b | |||
49164f6688 | |||
74dbbdd0af | |||
af05486fac | |||
384e8bf59f | |||
0d424ef037 | |||
77c54e2e2b | |||
132ffde58f | |||
91f90a03d1 | |||
82172969d1 | |||
30f11dc9cc | |||
2c3932bdd3 | |||
6eca06780f | |||
e5aeae7989 | |||
9b9187bb3e | |||
a5db2564c8 | |||
ddd2108109 | |||
2e661f8b8e | |||
2fc0ad3c9e | |||
62b77b724c | |||
e56fe9a95e | |||
0e13133499 | |||
e4c80a09b6 | |||
6fbaebcc09 | |||
041d76830b | |||
9b0cce9c0d | |||
891bc34efa | |||
635ba35a52 | |||
0b44ccdb4a | |||
9edd4a6096 | |||
cece3711f8 | |||
bc1581b174 | |||
2aae43b91e | |||
b264c74ce9 | |||
64264460e8 | |||
cc38100109 | |||
0173ddd38e | |||
29c1f7462a | |||
bbe2f988e0 | |||
a570fa1641 | |||
74c2a99e80 | |||
74e4b333ac | |||
9282cf91c6 | |||
d17faaa13a | |||
2c7c815e0a | |||
2c031b01fc | |||
bcc07c7503 | |||
3a3b53545f | |||
1d11502e93 | |||
7f2f9ff7ca | |||
99d4c3bcaf | |||
1a9b711b1f | |||
20409a39dd | |||
22f9707505 | |||
cd9b8dab64 | |||
2f0cec5fde | |||
ea894e2917 | |||
132c247d97 | |||
3ce8e38a00 | |||
363382d2a4 | |||
7e282fa03a | |||
38cd7491be | |||
a7feee3c48 | |||
d6b4fd9bc6 | |||
c37800bca8 | |||
0d6acde41a | |||
513ba77b22 | |||
c0e61fbdc4 | |||
7b86b932c3 | |||
6f9c71faf1 | |||
84999fde99 | |||
9ff838f964 | |||
99717e5de8 | |||
7f606234a3 | |||
2d4e8f4df6 | |||
93eb0813f9 | |||
bd218fb503 | |||
0ba53c660f | |||
9c38938a72 | |||
df171fdcc7 | |||
d2ebfba8c3 | |||
995190108c | |||
798cb26445 | |||
8744ddde1c | |||
60d579da6a | |||
0230099c92 | |||
f8787f45c0 | |||
53a7df4eea | |||
1e0ee91f1c | |||
78de943ed0 | |||
28b92ed349 | |||
57e56b15bc | |||
edbe8df310 | |||
5992708be3 | |||
720ec613df | |||
3bda992f13 | |||
8e608816a9 | |||
c8577b0611 | |||
8e9c9c5bda | |||
57b1635c24 | |||
e1e079e1a5 | |||
43f7542c64 | |||
c64e4caf27 | |||
bc10ae7216 | |||
269b55ed08 | |||
6472021359 | |||
7f61415a39 | |||
d1166edbd8 | |||
c908cabcf6 | |||
b5e8cf84c8 | |||
ebe7151269 | |||
1e1bec4ce0 | |||
15818f37ef | |||
993cbb4fd7 | |||
fdc0d15b63 | |||
0f7009438c | |||
de03e84eb4 | |||
8f512a7967 | |||
f906cd686f | |||
708969954a | |||
3ea73b807f | |||
b8419c513c | |||
040d09a25c | |||
296111a89a | |||
b93bb81a98 | |||
2d4d29196f | |||
1c8771efef | |||
8a4ea2d899 | |||
1ca3044639 | |||
cd540f7b5d | |||
8fe52e6979 | |||
08ba9b1da9 | |||
36130c9809 | |||
52a6122e74 | |||
4f948dfd65 | |||
f79c0cec55 | |||
a80a6bf5c5 | |||
8661a169b3 | |||
bc0ce09d61 | |||
fb5166ab7c | |||
16e4346d07 | |||
651e80b8f0 | |||
17bda157aa | |||
9e9974d694 | |||
80963a0667 | |||
d6d71c7cc3 | |||
ea6c705a97 | |||
c35bf6a008 | |||
23b5af7fc4 | |||
e181725127 | |||
4b761ac74d | |||
2e52419ffd | |||
5b443027bf | |||
d5c8566dd0 | |||
80c1888edd | |||
babc7453ad | |||
e7ae20f689 | |||
a9e64503b7 | |||
301a9a71f1 | |||
55f2d83abb | |||
79de8cc17c | |||
ab3d6a1fcb | |||
2ed8dfd76b | |||
3acdd311a7 | |||
282c987c39 | |||
fbf4272e0e | |||
8b1f7c9a88 | |||
2896ed45eb | |||
cec952fbde | |||
cb091e0dc6 | |||
5743ccbc5f | |||
d6415e07e1 | |||
ca75489ab0 | |||
e028d96ebb | |||
f01596322b | |||
411943f39b | |||
2334805b95 | |||
1ce1ddc952 | |||
8b3f7a489f | |||
8505f5b174 | |||
45e0e56389 | |||
d682b82bc5 | |||
4b29658adc | |||
10b2c2b648 | |||
1fdda485d3 | |||
9f273a3ff2 | |||
7811535e72 | |||
1717a0a235 | |||
c7049468c2 | |||
fb74526d9d | |||
ebefee4de7 | |||
9ebcf9b506 | |||
1cff0c5ee1 | |||
c612eb1c4e | |||
ed8d64816c | |||
ab00d505b9 | |||
02dabf9c01 | |||
21dde5362a | |||
b3801f3f54 | |||
677c8af6fa | |||
662258e9df | |||
59666c3110 | |||
cd184c373c | |||
8c2ede4ff9 | |||
98bc96b1b8 | |||
abf03d6cc7 | |||
46c8ae94bc | |||
f952ee0d1f | |||
7bf5642107 | |||
cb9c2fd88f | |||
a5f0dbc2a3 | |||
64b550e27d | |||
5d6d7eba57 |
@ -197,10 +197,10 @@ jobs:
|
||||
- run:
|
||||
name: Download additional WP Plugins for tests
|
||||
command: |
|
||||
./do download:woo-commerce-zip 9.4.2
|
||||
./do download:woo-commerce-subscriptions-zip 6.9.1
|
||||
./do download:woo-commerce-zip 9.6.1
|
||||
./do download:woo-commerce-subscriptions-zip 7.1.0
|
||||
./do download:woo-commerce-memberships-zip 1.26.5
|
||||
./do download:automate-woo-zip 6.1.2
|
||||
./do download:automate-woo-zip 6.1.5
|
||||
- run:
|
||||
name: Dump tests ENV variables for acceptance tests
|
||||
command: |
|
||||
@ -1082,8 +1082,8 @@ workflows:
|
||||
- acceptance_tests:
|
||||
<<: *slack-fail-post-step
|
||||
name: acceptance_oldest
|
||||
woo_core_version: 9.3.3
|
||||
woo_subscriptions_version: 6.8.0
|
||||
woo_core_version: 9.5.2
|
||||
woo_subscriptions_version: 7.0.0
|
||||
woo_memberships_version: 1.25.2
|
||||
automate_woo_version: 6.0.33
|
||||
mysql_command: --max_allowed_packet=100M
|
||||
@ -1123,8 +1123,8 @@ workflows:
|
||||
- integration_tests:
|
||||
<<: *slack-fail-post-step
|
||||
name: integration_oldest
|
||||
woo_core_version: 9.3.3
|
||||
woo_subscriptions_version: 6.8.0
|
||||
woo_core_version: 9.5.2
|
||||
woo_subscriptions_version: 7.0.0
|
||||
woo_memberships_version: 1.25.2
|
||||
automate_woo_version: 6.0.33
|
||||
codeception_image_version: 7.4-cli_20220605.0
|
||||
@ -1186,8 +1186,8 @@ workflows:
|
||||
- acceptance_tests:
|
||||
<<: *slack-fail-post-step
|
||||
name: acceptance_with_premium_oldest
|
||||
woo_core_version: 9.3.3
|
||||
woo_subscriptions_version: 6.8.0
|
||||
woo_core_version: 9.5.2
|
||||
woo_subscriptions_version: 7.0.0
|
||||
woo_memberships_version: 1.25.2
|
||||
automate_woo_version: 6.0.33
|
||||
codeception_image_version: 7.4-cli_20220605.0
|
||||
@ -1198,8 +1198,8 @@ workflows:
|
||||
- integration_tests:
|
||||
<<: *slack-fail-post-step
|
||||
name: integration_with_premium_oldest
|
||||
woo_core_version: 9.3.3
|
||||
woo_subscriptions_version: 6.8.0
|
||||
woo_core_version: 9.5.2
|
||||
woo_subscriptions_version: 7.0.0
|
||||
woo_memberships_version: 1.25.2
|
||||
automate_woo_version: 6.0.33
|
||||
codeception_image_version: 7.4-cli_20220605.0
|
||||
|
@ -13,6 +13,9 @@ max_line_length = off
|
||||
[packages/php/email-editor/**]
|
||||
indent_style = tab
|
||||
|
||||
[packages/js/email-editor/**.{js,jsx,ts,tsx,scss}]
|
||||
indent_style = tab
|
||||
|
||||
[*.php]
|
||||
ij_php_align_key_value_pairs = false
|
||||
ij_php_align_multiline_chained_methods = false
|
||||
@ -59,3 +62,4 @@ ij_php_space_after_colon_in_return_type = true
|
||||
ij_php_space_before_else_keyword = true
|
||||
ij_php_for_statement_new_line_after_left_paren = true
|
||||
ij_php_class_brace_style = end_of_line
|
||||
ij_php_comma_after_last_array_element = true
|
||||
|
@ -41,10 +41,13 @@ e66c76133ec3ef667e382203426d91a4b4aa5174
|
||||
ab27eaee2df740c0add4331a7f8c115a87ecfa2b
|
||||
|
||||
# Move email editor to JS packages folder
|
||||
374a79c49b6051a2cb2dc7b5cee5c6e69de16aba
|
||||
912282f57ccc839491ff951ec5cf7aa10c14f429
|
||||
|
||||
# Switch email editor js packages to WP coding style
|
||||
d8ba1af97864e373b81dd08bd884055a138e1306
|
||||
b2fb96f8793b63db629d5237010d87332330c51e
|
||||
|
||||
# Email editor Prettier autoformatting
|
||||
cdf89c46599e68648a4522d968a0d1948895060a
|
||||
8c604453b1d82e3a2c731241e1c96ea8b32ec716
|
||||
|
||||
# Move email editor components out of the engine folder
|
||||
1c3ea9cd0a5fc8848a64d840e2fa16a6c7d8c1fe
|
||||
|
14
.github/workflows/email-editor-package.yml
vendored
14
.github/workflows/email-editor-package.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: mailpoet/vendor-prefixed
|
||||
key: ${{ runner.os }}-vendor-prefixed-${{ matrix.php-version }}-${{ hashFiles('mailpoet/prefixer/composer.lock') }}-${{ hashFiles('mailpoet/composer.json') }}
|
||||
key: ${{ runner.os }}-vendor-prefixed-${{ matrix.php-version }}-${{ hashFiles('mailpoet/prefixer/composer.lock') }}-${{ hashFiles('mailpoet/prefixer/composer.json') }}
|
||||
|
||||
- name: Cache Composer vendor for test environment
|
||||
id: composer-tests-env-cache
|
||||
@ -41,7 +41,7 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: packages/php/email-editor/vendor
|
||||
key: ${{ runner.os }}-composer-email-editor-${{ matrix.php-version }}-${{ hashFiles('packages/php/email-editor/composer.lock') }}-${{ hashFiles('mailpoet/composer.json') }}
|
||||
key: ${{ runner.os }}-composer-email-editor-${{ matrix.php-version }}-${{ hashFiles('packages/php/email-editor/composer.lock') }}-${{ hashFiles('packages/php/email-editor/composer.json') }}
|
||||
|
||||
- name: Set up PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
@ -63,7 +63,8 @@ jobs:
|
||||
|
||||
# Install MailPoet dependencies only if the cache was not hit
|
||||
- name: Install mailpoet dependencies
|
||||
if: steps.composer-mailpoet-cache.outputs.cache-hit != 'true'
|
||||
if: |
|
||||
steps.composer-mailpoet-cache.outputs.cache-hit != 'true' || steps.vendor-prefixed-cache.outputs.cache-hit != 'true'
|
||||
run: ./tools/vendor/composer.phar install
|
||||
working-directory: mailpoet
|
||||
|
||||
@ -143,7 +144,7 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: mailpoet/vendor-prefixed
|
||||
key: ${{ runner.os }}-vendor-prefixed-${{ matrix.php-version }}-${{ hashFiles('mailpoet/prefixer/composer.lock') }}-${{ hashFiles('mailpoet/composer.json') }}
|
||||
key: ${{ runner.os }}-vendor-prefixed-${{ matrix.php-version }}-${{ hashFiles('mailpoet/prefixer/composer.lock') }}-${{ hashFiles('mailpoet/prefixer/composer.json') }}
|
||||
|
||||
- name: Cache Composer vendor for test environment
|
||||
id: composer-tests-env-cache
|
||||
@ -157,7 +158,7 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: packages/php/email-editor/vendor
|
||||
key: ${{ runner.os }}-composer-email-editor-${{ matrix.php-version }}-${{ hashFiles('packages/php/email-editor/composer.lock') }}-${{ hashFiles('mailpoet/composer.json') }}
|
||||
key: ${{ runner.os }}-composer-email-editor-${{ matrix.php-version }}-${{ hashFiles('packages/php/email-editor/composer.lock') }}-${{ hashFiles('packages/php/email-editor/composer.json') }}
|
||||
|
||||
- name: Set up PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
@ -179,7 +180,8 @@ jobs:
|
||||
|
||||
# Install MailPoet dependencies only if the cache was not hit
|
||||
- name: Install mailpoet dependencies
|
||||
if: steps.composer-mailpoet-cache.outputs.cache-hit != 'true'
|
||||
if: |
|
||||
steps.composer-mailpoet-cache.outputs.cache-hit != 'true' || steps.vendor-prefixed-cache.outputs.cache-hit != 'true'
|
||||
run: ./tools/vendor/composer.phar install
|
||||
working-directory: mailpoet
|
||||
|
||||
|
@ -4,3 +4,5 @@
|
||||
[ "$MP_GIT_HOOKS_ENABLE" != "true" ] && exit 0
|
||||
|
||||
installIfUpdates
|
||||
|
||||
./do cleanup:cached-files
|
||||
|
30
.woodpecker.yml
Normal file
30
.woodpecker.yml
Normal file
@ -0,0 +1,30 @@
|
||||
clone:
|
||||
git:
|
||||
image: woodpeckerci/plugin-git
|
||||
settings:
|
||||
depth: 1
|
||||
|
||||
steps:
|
||||
build:
|
||||
image: node:current-bookworm-slim
|
||||
commands:
|
||||
- apt update
|
||||
- apt install php php-symfony bash -y
|
||||
- npm install pnpm
|
||||
- cd mailpoet
|
||||
- bash build.sh
|
||||
- mkdir ../output
|
||||
- mv mailpoet.zip ../output
|
||||
- cd ..
|
||||
|
||||
release:
|
||||
image: woodpeckerci/plugin-gitea-release:latest
|
||||
settings:
|
||||
base_url: https://git.cavemanon.xyz
|
||||
api_key:
|
||||
from_secret: releasesmithapikey
|
||||
files: "output/"
|
||||
prerelease: false
|
||||
title: "${CI_COMMIT_TAG}"
|
||||
when:
|
||||
- event: tag
|
@ -46,7 +46,6 @@ services:
|
||||
NPM_CONFIG_CACHE: '/tmp/.npm'
|
||||
XDG_CACHE_HOME: '/tmp/.cache'
|
||||
MAILPOET_DEV_SITE: 1
|
||||
MP_ENV: development
|
||||
volumes:
|
||||
- './wordpress:/var/www/html'
|
||||
- './tsconfig.base.json:/var/www/html/wp-content/plugins/tsconfig.base.json:ro'
|
||||
@ -81,7 +80,6 @@ services:
|
||||
WORDPRESS_DB_USER: wordpress
|
||||
WORDPRESS_DB_PASSWORD: wordpress
|
||||
PHP_IDE_CONFIG: 'serverName=Mailpoet'
|
||||
MP_ENV: test
|
||||
volumes:
|
||||
- './mailpoet:/var/www/html/wp-content/plugins/mailpoet'
|
||||
- './mailpoet-premium:/var/www/html/wp-content/plugins/mailpoet-premium'
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
return $this->taskExecStack()
|
||||
->stopOnFail()
|
||||
->exec('./tools/vendor/composer.phar install')
|
||||
->exec('cd ../packages/php/email-editor && ../../../mailpoet/tools/vendor/composer.phar install && cd -')
|
||||
->exec('cd .. && pnpm install --frozen-lockfile --prefer-offline')
|
||||
->addCode([$this, 'cleanupCachedFiles'])
|
||||
->run();
|
||||
@ -32,6 +33,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
return $this->taskExecStack()
|
||||
->stopOnFail()
|
||||
->exec('./tools/vendor/composer.phar install')
|
||||
->exec('cd ../packages/php/email-editor && ../../../mailpoet/tools/vendor/composer.phar install && cd -')
|
||||
->addCode([$this, 'cleanupCachedFiles'])
|
||||
->run();
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
.mailpoet_feature_announcement {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.button.mailpoet_feature_announcement_button {
|
||||
height: 28px;
|
||||
min-height: auto;
|
||||
padding: 0 5px 1px;
|
||||
position: relative;
|
||||
|
||||
@include respond-to(small-screen) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet_feature_announcement_icon {
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.mailpoet_feature_announcement_dot:before {
|
||||
background: #d54e21;
|
||||
border-radius: 10px;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
right: -4px;
|
||||
top: -4px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.mailpoet_in_beamer_update_notice {
|
||||
background: #f00;
|
||||
bottom: 0;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
padding: 20px 10px;
|
||||
position: fixed;
|
||||
right: -400px;
|
||||
text-align: center;
|
||||
transition: right 0.2s ease-in;
|
||||
width: 400px;
|
||||
z-index: 10000000000; // really has to be this high
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.beamer_show & {
|
||||
right: 0;
|
||||
}
|
||||
}
|
@ -95,10 +95,6 @@ h1.title.mailpoet-newsletter-listing-heading {
|
||||
#mailpoet_editor_steps_heading {
|
||||
.mailpoet-top-bar {
|
||||
left: 0;
|
||||
|
||||
.mailpoet-top-bar-beamer {
|
||||
top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,3 +161,12 @@ ul.sending-method-benefits {
|
||||
.mailpoet_install_premium_message {
|
||||
margin-bottom: $grid-gap-medium;
|
||||
}
|
||||
|
||||
.mailpoet-verify-key-button {
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.mailpoet-premium-key-toggle {
|
||||
height: 34px;
|
||||
padding: 0 10px !important;
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
$beamer-dot-size: 8px;
|
||||
|
||||
.mailpoet-top-bar {
|
||||
align-items: center;
|
||||
background-color: $color-white;
|
||||
@ -83,7 +81,7 @@ $beamer-dot-size: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-top-bar-beamer {
|
||||
.mailpoet-top-bar-tutorial {
|
||||
align-items: center;
|
||||
background-color: $color-white;
|
||||
border: none;
|
||||
@ -96,6 +94,7 @@ $beamer-dot-size: 8px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
top: 4px;
|
||||
width: 75px;
|
||||
|
||||
svg {
|
||||
@ -103,15 +102,3 @@ $beamer-dot-size: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-top-bar-beamer-dot:before {
|
||||
background: $color-editor-warning;
|
||||
border-radius: $beamer-dot-size;
|
||||
content: '';
|
||||
display: block;
|
||||
height: $beamer-dot-size;
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 2px;
|
||||
width: $beamer-dot-size;
|
||||
}
|
||||
|
@ -87,7 +87,6 @@
|
||||
@import 'components-plugin/newsletter-types';
|
||||
@import 'components-plugin/newsletter-template-styles';
|
||||
@import 'components-plugin/welcome-wizard';
|
||||
@import 'components-plugin/feature-announcement';
|
||||
@import 'components-plugin/newsletter-congratulate';
|
||||
@import 'components-plugin/discounts';
|
||||
@import 'components-plugin/review-request';
|
||||
|
@ -53,7 +53,7 @@ function exportMixpanel() {
|
||||
|
||||
if (
|
||||
window.mailpoet_analytics_enabled &&
|
||||
window.MailPoet.libs3rdPartyEnabled
|
||||
window.mailpoet_3rd_party_libs_enabled
|
||||
) {
|
||||
window.MailPoet.trackEvent = track;
|
||||
} else {
|
||||
|
@ -1,33 +0,0 @@
|
||||
import classnames from 'classnames';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { withFeatureAnnouncement } from './with-feature-announcement';
|
||||
|
||||
type Props = {
|
||||
hasNews: boolean;
|
||||
onBeamerClick: () => void;
|
||||
};
|
||||
|
||||
function FeatureAnnouncementComponent({ hasNews, onBeamerClick }: Props) {
|
||||
const buttonClasses = classnames(
|
||||
'button mailpoet_feature_announcement_button',
|
||||
hasNews ? 'mailpoet_feature_announcement_dot' : '',
|
||||
);
|
||||
return (
|
||||
<div className="mailpoet_feature_announcement">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onBeamerClick}
|
||||
className={buttonClasses}
|
||||
title={MailPoet.I18n.t('whatsNew')}
|
||||
>
|
||||
<span className="mailpoet_feature_announcement_icon dashicons dashicons-carrot" />
|
||||
</button>
|
||||
<span id="beamer-empty-element" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const FeatureAnnouncement = withFeatureAnnouncement(
|
||||
FeatureAnnouncementComponent,
|
||||
);
|
||||
export { FeatureAnnouncement };
|
@ -1,111 +0,0 @@
|
||||
import { ComponentType, FC } from 'react';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import ReactStringReplace from 'react-string-replace';
|
||||
import jQuery from 'jquery';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
interface FeatureAnnouncementWindow extends Window {
|
||||
Beamer: {
|
||||
show: () => void;
|
||||
};
|
||||
mailpoet_feature_announcement_has_news: boolean;
|
||||
mailpoet_update_available: boolean;
|
||||
beamer_config: {
|
||||
product_id: string;
|
||||
selector: string;
|
||||
language: string;
|
||||
callback: () => void;
|
||||
filter?: string;
|
||||
};
|
||||
mailpoet_user_locale: string;
|
||||
}
|
||||
|
||||
declare let window: FeatureAnnouncementWindow;
|
||||
|
||||
export const withFeatureAnnouncement = <P extends Record<string, unknown>>(
|
||||
Component: ComponentType<P>,
|
||||
): FC<Omit<P, 'hasNews' | 'onBeamerClick'>> => {
|
||||
const isBeamerInitialized = () => typeof window.Beamer !== 'undefined';
|
||||
let showDot = window.mailpoet_feature_announcement_has_news;
|
||||
let beamerCallback;
|
||||
|
||||
function showPluginUpdateNotice() {
|
||||
if (
|
||||
!window.mailpoet_update_available ||
|
||||
document.getElementById('mailpoet_update_notice')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const updateMailPoetNotice = ReactStringReplace(
|
||||
MailPoet.I18n.t('updateMailPoetNotice'),
|
||||
/\[link\](.*?)\[\/link\]/,
|
||||
(match) => `<a href="update-core.php">${match}</a>`,
|
||||
).join('');
|
||||
jQuery('#beamerOverlay').append(
|
||||
`<p id="mailpoet_update_notice" class="mailpoet_in_beamer_update_notice">${updateMailPoetNotice}</p>`,
|
||||
);
|
||||
}
|
||||
|
||||
function updateLastAnnouncementSeenValue() {
|
||||
const data = { last_announcement_seen: Math.floor(Date.now() / 1000) };
|
||||
void MailPoet.Ajax.post({
|
||||
api_version: MailPoet.apiVersion,
|
||||
endpoint: 'user_flags',
|
||||
action: 'set',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
function loadBeamer() {
|
||||
window.beamer_config = {
|
||||
product_id: 'VvHbhYWy7118',
|
||||
selector: '#beamer-empty-element',
|
||||
language: window.mailpoet_user_locale,
|
||||
callback: beamerCallback,
|
||||
};
|
||||
if (MailPoet.isWoocommerceActive) {
|
||||
window.beamer_config.filter = 'woocommerce';
|
||||
}
|
||||
MailPoet.Modal.loading(true);
|
||||
window.mailpoet_feature_announcement_has_news = false;
|
||||
const s = document.createElement('script');
|
||||
s.type = 'text/javascript';
|
||||
s.src = 'https://app.getbeamer.com/js/beamer-embed.js';
|
||||
document.getElementsByTagName('body')[0].appendChild(s);
|
||||
}
|
||||
|
||||
function showBeamer(event = null) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (!isBeamerInitialized()) {
|
||||
loadBeamer();
|
||||
return;
|
||||
}
|
||||
showDot = false;
|
||||
beamerCallback = noop; // We show Beamer panel only on first callback after initialization
|
||||
MailPoet.Modal.loading(false);
|
||||
window.Beamer.show();
|
||||
updateLastAnnouncementSeenValue();
|
||||
showPluginUpdateNotice();
|
||||
}
|
||||
|
||||
beamerCallback = () => {
|
||||
if (!isBeamerInitialized()) {
|
||||
return;
|
||||
}
|
||||
showBeamer();
|
||||
};
|
||||
|
||||
return function withFeatureAnnouncementRenderer({
|
||||
...props
|
||||
}: Omit<P, 'hasNews' | 'onBeamerClick'>) {
|
||||
return (
|
||||
<Component
|
||||
{...(props as P)}
|
||||
onBeamerClick={(e) => showBeamer(e)}
|
||||
hasNews={showDot}
|
||||
/>
|
||||
);
|
||||
};
|
||||
};
|
@ -16,7 +16,11 @@ export type ApiError = {
|
||||
|
||||
export const initializeApi = () => {
|
||||
apiFetch.use((options, next) => {
|
||||
if (options.path && options.path.startsWith('/wc-analytics/')) {
|
||||
if (
|
||||
options.path &&
|
||||
(options.path.startsWith('/wc-analytics/') ||
|
||||
options.path.startsWith('/wp/v2/'))
|
||||
) {
|
||||
return apiFetch.createRootURLMiddleware(`${api.root}/`)(options, next);
|
||||
}
|
||||
return apiFetch.createRootURLMiddleware(apiUrl)(options, next);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { TopBarWithBeamer } from 'common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from 'common/top-bar/top-bar';
|
||||
import { SlotFillProvider } from '@wordpress/components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { registerTranslations } from 'common';
|
||||
@ -72,7 +72,7 @@ function Automations(): JSX.Element {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopBarWithBeamer />
|
||||
<TopBarWithBoundary />
|
||||
<GlobalNotices />
|
||||
<Notices />
|
||||
<MssAccessNotices />
|
||||
|
@ -143,6 +143,7 @@ export function* activate() {
|
||||
return {
|
||||
type: 'ACTIVATE',
|
||||
automation: data?.data ?? automation,
|
||||
saved: !!data?.data,
|
||||
} as const;
|
||||
}
|
||||
|
||||
|
@ -41,11 +41,15 @@ export function reducer(state: State, action): State {
|
||||
savedState: 'saved',
|
||||
};
|
||||
case 'ACTIVATE':
|
||||
return {
|
||||
...state,
|
||||
automationData: action.automation,
|
||||
savedState: 'saved',
|
||||
};
|
||||
return action.saved
|
||||
? {
|
||||
...state,
|
||||
automationData: action.automation,
|
||||
savedState: 'saved',
|
||||
}
|
||||
: {
|
||||
...state,
|
||||
};
|
||||
case 'DEACTIVATE':
|
||||
return {
|
||||
...state,
|
||||
|
@ -2,7 +2,7 @@ import { createRoot } from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { dispatch, select, useSelect } from '@wordpress/data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { TopBarWithBeamer } from '../../../../common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from '../../../../common/top-bar/top-bar';
|
||||
import { Notices } from '../../../listing/components/notices';
|
||||
import { Header } from './components/header';
|
||||
import { Overview } from './components/overview';
|
||||
@ -49,7 +49,7 @@ function TopBarWithBreadcrumb(): JSX.Element {
|
||||
}));
|
||||
|
||||
return (
|
||||
<TopBarWithBeamer>
|
||||
<TopBarWithBoundary>
|
||||
<p className="mailpoet-automation-analytics-title">
|
||||
<a href={MailPoet.urls.automationListing}>
|
||||
{__('Automations', 'mailpoet')}
|
||||
@ -57,7 +57,7 @@ function TopBarWithBreadcrumb(): JSX.Element {
|
||||
› <strong>{automation.name}</strong>
|
||||
<AutomationStatus status={automation.status} />
|
||||
</p>
|
||||
</TopBarWithBeamer>
|
||||
</TopBarWithBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { Step } from '../../../../../editor/components/automation/types';
|
||||
import { storeName } from '../../../../../editor/store';
|
||||
|
||||
const transactionalTriggers = [
|
||||
'mailpoet:custom-trigger',
|
||||
'woocommerce:order-status-changed',
|
||||
'woocommerce:order-created',
|
||||
'woocommerce:order-completed',
|
||||
@ -15,6 +16,9 @@ const transactionalTriggers = [
|
||||
'woocommerce-subscriptions:subscription-status-changed',
|
||||
'woocommerce-subscriptions:trial-ended',
|
||||
'woocommerce-subscriptions:trial-started',
|
||||
'woocommerce:buys-from-a-tag',
|
||||
'woocommerce:buys-from-a-category',
|
||||
'woocommerce:buys-a-product',
|
||||
];
|
||||
|
||||
export function isTransactional(step: Step): boolean {
|
||||
|
@ -7,6 +7,7 @@ import { step as AbandonedCartTrigger } from './steps/abandoned-cart';
|
||||
import { MailPoet } from '../../../mailpoet';
|
||||
import { step as BuysAProductTrigger } from './steps/buys-a-product';
|
||||
import { step as BuysFromACategory } from './steps/buys-from-a-category';
|
||||
import { step as BuysFromATag } from './steps/buys-from-a-tag';
|
||||
import { step as MadeAReview } from './steps/made-a-review';
|
||||
// Insert new imports here
|
||||
|
||||
@ -21,6 +22,7 @@ export const initialize = (): void => {
|
||||
registerStepType(AbandonedCartTrigger);
|
||||
registerStepType(BuysAProductTrigger);
|
||||
registerStepType(BuysFromACategory);
|
||||
registerStepType(BuysFromATag);
|
||||
registerStepType(MadeAReview);
|
||||
// Insert new steps here
|
||||
};
|
||||
|
@ -0,0 +1,86 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Search } from '@woocommerce/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { PanelBody } from '@wordpress/components';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
import { addQueryArgs } from '@wordpress/url';
|
||||
import { dispatch, useSelect } from '@wordpress/data';
|
||||
import { PlainBodyTitle } from '../../../../../editor/components';
|
||||
import { storeName } from '../../../../../editor/store';
|
||||
import { OrderStatusPanel } from '../../order-status-changed/edit/order-status-panel';
|
||||
import autocompleter from './tag-autocompleter';
|
||||
|
||||
type Tag = {
|
||||
key: string | number;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
async function fetchTags(include: number[], callback: (tags: Tag[]) => void) {
|
||||
const path = addQueryArgs('/wp/v2/product_tag/', { include });
|
||||
const data: { id: number; name: string }[] = await apiFetch({
|
||||
path,
|
||||
method: 'GET',
|
||||
});
|
||||
callback(data.map((item) => ({ key: item?.id, label: item?.name })));
|
||||
}
|
||||
|
||||
export function Edit(): JSX.Element {
|
||||
const [current, setCurrent] = useState<Tag[]>([]);
|
||||
const { selectedStep } = useSelect((select) => ({
|
||||
selectedStep: select(storeName).getSelectedStep(),
|
||||
}));
|
||||
const tagIds: number[] = useMemo(
|
||||
() => (selectedStep.args?.tag_ids as number[]) ?? [],
|
||||
[selectedStep],
|
||||
);
|
||||
const [isBusy, setIsBusy] = useState(tagIds.length > 0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isBusy) {
|
||||
return;
|
||||
}
|
||||
void fetchTags(tagIds, (tags: Tag[]) => {
|
||||
setCurrent(tags);
|
||||
setIsBusy(false);
|
||||
});
|
||||
}, [isBusy, tagIds]);
|
||||
return (
|
||||
<>
|
||||
<PanelBody opened>
|
||||
<PlainBodyTitle title={__('Tags', 'mailpoet')} />
|
||||
<Search
|
||||
disabled={isBusy}
|
||||
type="custom"
|
||||
autocompleter={autocompleter}
|
||||
className={`mailpoet-product-search ${isBusy ? 'is-busy' : ''}`}
|
||||
placeholder={__('Search for a tag', 'mailpoet')}
|
||||
selected={current}
|
||||
onChange={(items: Tag[]) => {
|
||||
setCurrent(items);
|
||||
void dispatch(storeName).updateStepArgs(
|
||||
selectedStep.id,
|
||||
'tag_ids',
|
||||
items.map((item) => item.key),
|
||||
);
|
||||
}}
|
||||
multiple
|
||||
inlineTags
|
||||
/>
|
||||
</PanelBody>
|
||||
|
||||
<OrderStatusPanel
|
||||
label={__('Order settings', 'mailpoet')}
|
||||
showFrom={false}
|
||||
showTo
|
||||
toLabel={__('Order status', 'mailpoet')}
|
||||
onChange={(status, property) => {
|
||||
void dispatch(storeName).updateStepArgs(
|
||||
selectedStep.id,
|
||||
property,
|
||||
status,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { addQueryArgs } from '@wordpress/url';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
import { AutoCompleter } from '@woocommerce/components/build-types/search/autocompleters';
|
||||
|
||||
const tagAutoCompleter: AutoCompleter = {
|
||||
name: 'tags',
|
||||
className: 'woocommerce-search__product-result',
|
||||
options(search) {
|
||||
const query = search
|
||||
? {
|
||||
search,
|
||||
per_page: 10,
|
||||
orderby: 'count',
|
||||
}
|
||||
: {};
|
||||
return apiFetch({
|
||||
path: addQueryArgs('/wp/v2/product_tag', query),
|
||||
});
|
||||
},
|
||||
isDebounced: true,
|
||||
getOptionIdentifier(tag) {
|
||||
return tag.id as number;
|
||||
},
|
||||
getOptionKeywords(tag) {
|
||||
return [tag.name] as string[];
|
||||
},
|
||||
getFreeTextOptions(query) {
|
||||
const label = (
|
||||
<span key="name" className="woocommerce-search__result-name">
|
||||
{__('Search results', 'mailpoet')}
|
||||
</span>
|
||||
);
|
||||
const titleOption = {
|
||||
key: 'title',
|
||||
label,
|
||||
value: { id: query, name: query },
|
||||
};
|
||||
|
||||
return [titleOption];
|
||||
},
|
||||
getOptionLabel(tag) {
|
||||
return (
|
||||
<span
|
||||
key="name"
|
||||
className="woocommerce-search__result-name"
|
||||
aria-label={tag.name}
|
||||
>
|
||||
{tag.name}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
// This is slightly different than gutenberg/Autocomplete, we don't support different methods
|
||||
// of replace/insertion, so we can just return the value.
|
||||
getOptionCompletion(tag) {
|
||||
const value = {
|
||||
key: tag.id,
|
||||
label: tag.name,
|
||||
};
|
||||
return value;
|
||||
},
|
||||
};
|
||||
|
||||
export default tagAutoCompleter;
|
@ -0,0 +1,26 @@
|
||||
export function Icon(): JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M5.73173 4.00134C5.8081 3.03517 6.61579 2.27551 7.60166 2.27551C8.58753 2.27551 9.39522 3.03517 9.47159 4.00134H10.1904C10.7479 4.00134 11.2033 4.45681 11.2033 5.01426V10.1917C11.2033 10.7492 10.7479 11.2047 10.1904 11.2047H5.01292C4.45547 11.2047 4 10.7492 4 10.1917V5.01426C4 4.45681 4.45547 4.00134 5.01292 4.00134H5.73173ZM7.60166 3.43843C7.36389 3.43843 7.15161 3.55664 7.02145 3.73861C6.96586 3.81633 6.92553 3.90533 6.90474 4.00134H8.29858C8.27779 3.90533 8.23746 3.81633 8.18187 3.73861C8.05171 3.55664 7.83943 3.43843 7.60166 3.43843ZM5.16291 5.16426V9.65285C5.16291 9.86763 5.33703 10.0417 5.55181 10.0417H9.65151C9.86629 10.0417 10.0404 9.86763 10.0404 9.65285V5.16426H9.47749V5.87717C9.47749 6.19732 9.21618 6.45863 8.89603 6.45863C8.57589 6.45863 8.31458 6.19732 8.31458 5.87717V5.16426H6.88875V5.87717C6.88875 6.19732 6.62743 6.45863 6.30729 6.45863C5.98714 6.45863 5.72583 6.19732 5.72583 5.87717V5.16426H5.16291Z"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M13.5858 4.58722C13.9609 4.21215 14.4696 4.00143 15 4.00143H18C18.5304 4.00143 19.0391 4.21215 19.4142 4.58722C19.7893 4.96229 20 5.471 20 6.00143V9.00143C20 9.53187 19.7893 10.0406 19.4142 10.4156C19.0391 10.7907 18.5304 11.0014 18 11.0014H15C14.4696 11.0014 13.9609 10.7907 13.5858 10.4156C13.2107 10.0406 13 9.53187 13 9.00143V6.00143C13 5.471 13.2107 4.96229 13.5858 4.58722ZM15 5.50143H18C18.1326 5.50143 18.2598 5.55411 18.3536 5.64788C18.4473 5.74165 18.5 5.86883 18.5 6.00143V9.00143C18.5 9.13404 18.4473 9.26122 18.3536 9.35499C18.2598 9.44876 18.1326 9.50143 18 9.50143H15C14.8674 9.50143 14.7402 9.44876 14.6464 9.35499C14.5527 9.26122 14.5 9.13404 14.5 9.00143V6.00143C14.5 5.86883 14.5527 5.74165 14.6464 5.64788C14.7402 5.55411 14.8674 5.50143 15 5.50143Z"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M13.5858 13.5844C13.9609 13.2093 14.4696 12.9986 15 12.9986H18C18.5304 12.9986 19.0391 13.2093 19.4142 13.5844C19.7893 13.9594 20 14.4681 20 14.9986V17.9986C20 18.529 19.7893 19.0377 19.4142 19.4128C19.0391 19.7879 18.5304 19.9986 18 19.9986H15C14.4696 19.9986 13.9609 19.7879 13.5858 19.4128C13.2107 19.0377 13 18.529 13 17.9986V14.9986C13 14.4681 13.2107 13.9594 13.5858 13.5844ZM15 14.4986H18C18.1326 14.4986 18.2598 14.5513 18.3536 14.645C18.4473 14.7388 18.5 14.866 18.5 14.9986V17.9986C18.5 18.1312 18.4473 18.2584 18.3536 18.3521C18.2598 18.4459 18.1326 18.4986 18 18.4986H15C14.8674 18.4986 14.7402 18.4459 14.6464 18.3521C14.5527 18.2584 14.5 18.1312 14.5 17.9986V14.9986C14.5 14.866 14.5527 14.7388 14.6464 14.645C14.7402 14.5513 14.8674 14.4986 15 14.4986Z"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M4.58579 13.5844C4.21071 13.9594 4 14.4681 4 14.9986V17.9986C4 18.529 4.21071 19.0377 4.58579 19.4128C4.96086 19.7879 5.46957 19.9986 6 19.9986H9C9.53043 19.9986 10.0391 19.7879 10.4142 19.4128C10.7893 19.0377 11 18.529 11 17.9986V14.9986C11 14.4681 10.7893 13.9594 10.4142 13.5844C10.0391 13.2093 9.53043 12.9986 9 12.9986H6C5.46957 12.9986 4.96086 13.2093 4.58579 13.5844ZM9 14.4986H6C5.86739 14.4986 5.74021 14.5513 5.64645 14.645C5.55268 14.7388 5.5 14.866 5.5 14.9986V17.9986C5.5 18.1312 5.55268 18.2584 5.64645 18.3521C5.74021 18.4459 5.86739 18.4986 6 18.4986H9C9.13261 18.4986 9.25979 18.4459 9.35355 18.3521C9.44732 18.2584 9.5 18.1312 9.5 17.9986V14.9986C9.5 14.866 9.44732 14.7388 9.35355 14.645C9.25979 14.5513 9.13261 14.4986 9 14.4986Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { StepType } from '../../../../editor/store';
|
||||
import { Edit } from './edit';
|
||||
import { Icon } from './icon';
|
||||
|
||||
const keywords = [
|
||||
// translators: noun, used as a search keyword for "Customer buys from a tag" trigger
|
||||
__('tag', 'mailpoet'),
|
||||
// translators: verb, used as a search keyword for "Customer buys from a tag" trigger
|
||||
__('buy', 'mailpoet'),
|
||||
// translators: verb, used as a search keyword for "Customer buys from a tag" trigger
|
||||
__('purchase', 'mailpoet'),
|
||||
// translators: noun, used as a search keyword for "Customer buys from a tag" trigger
|
||||
__('ecommerce', 'mailpoet'),
|
||||
// translators: noun, used as a search keyword for "Customer buys from a tag" trigger
|
||||
__('woocommerce', 'mailpoet'),
|
||||
// translators: noun, used as a search keyword for "Customer buys from a tag" trigger
|
||||
__('product', 'mailpoet'),
|
||||
// translators: noun, used as a search keyword for "Customer buys from a tag" trigger
|
||||
__('order', 'mailpoet'),
|
||||
];
|
||||
export const step: StepType = {
|
||||
key: 'woocommerce:buys-from-a-tag',
|
||||
group: 'triggers',
|
||||
title: () => __('Customer buys from a tag', 'mailpoet'),
|
||||
description: () =>
|
||||
__(
|
||||
'Start the automation when a customer buys a product from a tag.',
|
||||
'mailpoet',
|
||||
),
|
||||
|
||||
subtitle: () => __('Trigger', 'mailpoet'),
|
||||
keywords,
|
||||
foreground: '#2271b1',
|
||||
background: '#f0f6fc',
|
||||
icon: () => <Icon />,
|
||||
edit: () => <Edit />,
|
||||
} as const;
|
@ -4,27 +4,27 @@ import { Icon } from './icon';
|
||||
import { PremiumModalForStepEdit } from '../../../../components/premium-modal-steps-edit';
|
||||
|
||||
const keywords = [
|
||||
// translators: noun, used as a search keyword for "Customer makes a review" trigger
|
||||
// translators: noun, used as a search keyword for "Customer posts a review" trigger
|
||||
__('review', 'mailpoet'),
|
||||
// translators: verb, used as a search keyword for "Customer makes a review" trigger
|
||||
// translators: verb, used as a search keyword for "Customer posts a review" trigger
|
||||
__('buy', 'mailpoet'),
|
||||
// translators: noun, used as a search keyword for "Customer makes a review" trigger
|
||||
// translators: noun, used as a search keyword for "Customer posts a review" trigger
|
||||
__('comment', 'mailpoet'),
|
||||
// translators: noun, used as a search keyword for "Customer makes a review" trigger
|
||||
// translators: noun, used as a search keyword for "Customer posts a review" trigger
|
||||
__('ecommerce', 'mailpoet'),
|
||||
// translators: noun, used as a search keyword for "Customer makes a review" trigger
|
||||
// translators: noun, used as a search keyword for "Customer posts a review" trigger
|
||||
__('woocommerce', 'mailpoet'),
|
||||
// translators: noun, used as a search keyword for "Customer makes a review" trigger
|
||||
// translators: noun, used as a search keyword for "Customer posts a review" trigger
|
||||
__('product', 'mailpoet'),
|
||||
// translators: noun, used as a search keyword for "Customer makes a review" trigger
|
||||
// translators: noun, used as a search keyword for "Customer posts a review" trigger
|
||||
__('order', 'mailpoet'),
|
||||
];
|
||||
export const step: StepType = {
|
||||
key: 'woocommerce:made-a-review',
|
||||
group: 'triggers',
|
||||
title: () => __('Customer makes a review', 'mailpoet'),
|
||||
title: () => __('Customer posts a review', 'mailpoet'),
|
||||
description: () =>
|
||||
__('Start the automation when a customer makes a review.', 'mailpoet'),
|
||||
__('Start the automation when a customer posts a review.', 'mailpoet'),
|
||||
|
||||
subtitle: () => __('Trigger', 'mailpoet'),
|
||||
keywords,
|
||||
|
@ -3,7 +3,7 @@ import { __ } from '@wordpress/i18n';
|
||||
import { registerTranslations } from 'common';
|
||||
import { automationTemplateCategories, automationTemplates } from './config';
|
||||
import { initializeApi } from '../api';
|
||||
import { TopBarWithBeamer } from '../../common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from '../../common/top-bar/top-bar';
|
||||
import { FromScratchButton } from './components/from-scratch';
|
||||
import { BackButton, PageHeader } from '../../common/page-header';
|
||||
import { MailPoet } from '../../mailpoet';
|
||||
@ -50,7 +50,7 @@ function Templates(): JSX.Element {
|
||||
|
||||
return (
|
||||
<div className="mailpoet-main-container">
|
||||
<TopBarWithBeamer />
|
||||
<TopBarWithBoundary />
|
||||
<PageHeader
|
||||
heading={__('Start with a template', 'mailpoet')}
|
||||
headingPrefix={
|
||||
|
@ -1,21 +1,40 @@
|
||||
import { Input } from 'common/index';
|
||||
import { _x } from '@wordpress/i18n';
|
||||
import { Button, Input } from 'common/index';
|
||||
import { useAction, useSelector } from 'settings/store/hooks';
|
||||
import { useState } from 'react';
|
||||
|
||||
type KeyInputPropType = {
|
||||
placeholder?: string;
|
||||
isFullWidth?: boolean;
|
||||
forceRevealed?: boolean;
|
||||
};
|
||||
|
||||
export function KeyInput({
|
||||
placeholder,
|
||||
isFullWidth = false,
|
||||
forceRevealed = false,
|
||||
}: KeyInputPropType) {
|
||||
const state = useSelector('getKeyActivationState')();
|
||||
const setState = useAction('updateKeyActivationState');
|
||||
const [isRevealed, setIsRevealed] = useState(false);
|
||||
const inputType = forceRevealed || isRevealed ? 'text' : 'password';
|
||||
const toggleButton = !forceRevealed && (
|
||||
<Button
|
||||
className="mailpoet-premium-key-toggle"
|
||||
variant="tertiary"
|
||||
onClick={() => setIsRevealed(!isRevealed)}
|
||||
>
|
||||
{isRevealed
|
||||
? // translators: Used as a button to show or hide the premium key
|
||||
_x('Hide', 'verb', 'mailpoet')
|
||||
: // translators: Used as a button to show or hide the premium key
|
||||
_x('Show', 'verb', 'mailpoet')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<Input
|
||||
type="text"
|
||||
type={inputType}
|
||||
id="mailpoet_premium_key"
|
||||
name="premium[premium_key]"
|
||||
placeholder={placeholder}
|
||||
@ -29,6 +48,7 @@ export function KeyInput({
|
||||
key: event.target.value.trim() || null,
|
||||
})
|
||||
}
|
||||
iconEnd={toggleButton}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { action } from '_storybook/action';
|
||||
import { TopBar } from '../top-bar';
|
||||
|
||||
export default {
|
||||
@ -17,7 +16,7 @@ export function TopBarWithoutChildren() {
|
||||
left: '0px',
|
||||
}}
|
||||
>
|
||||
<TopBar hasNews={false} onBeamerClick={action('beamer click')} />
|
||||
<TopBar />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -34,7 +33,7 @@ export function TopBarWithoutChildrenWithNews() {
|
||||
left: '0px',
|
||||
}}
|
||||
>
|
||||
<TopBar hasNews onBeamerClick={action('beamer click')} />
|
||||
<TopBar />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { action } from '_storybook/action';
|
||||
import { TopBar } from '../top-bar';
|
||||
import { Button } from '../../button/button';
|
||||
|
||||
@ -18,7 +17,7 @@ export function TopBarWithChildren() {
|
||||
left: '0px',
|
||||
}}
|
||||
>
|
||||
<TopBar hasNews={false} onBeamerClick={action('beamer click')}>
|
||||
<TopBar>
|
||||
<Button>Button</Button>
|
||||
</TopBar>
|
||||
</div>
|
||||
|
@ -1,22 +0,0 @@
|
||||
export function BeamerIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M6 17V14.5002L6.00101 14.4997C7.10049 13.9995 7.90003 12.8998 8 11.7002V9.50016C8 7.96615 8.78223 6.62769 10.0309 5.95845C10.3288 5.79879 10.6533 5.67721 11 5.60016C10.9 5.50016 10.8 5.30016 10.8 5.10016C10.8 5.01146 10.8109 4.92493 10.8315 4.84189C10.95 4.36325 11.3887 4.00017 11.9 4.00017C11.9334 4.00017 11.9672 3.99696 12 4C12.0332 3.99688 12.0661 4.00004 12.1 4.00004C12.6113 4.00004 13.05 4.36313 13.1685 4.84177C13.1891 4.92481 13.2 5.01133 13.2 5.10004C13.2 5.30004 13.1 5.50004 13 5.60004C13.3467 5.67709 13.6712 5.79867 13.9691 5.95833C15.2178 6.62757 16 7.96602 16 9.50004V11.7C16.1 12.9 16.9 14.0002 18 14.5002V17.0002L6 17ZM17 16H7V15.148C8.2117 14.4742 8.98499 13.2218 9.09655 11.8831L9.1 9.50004C9.1 8.15301 9.99683 6.94434 11.3676 6.56356L11.7403 6.46006L12.0003 6.40228L12.2553 6.45896L12.6324 6.56368C14.0032 6.94446 14.9 8.15313 14.9 9.50016L14.9035 11.8832C15.015 13.2219 15.7883 14.4743 17 15.1481V16ZM12.0001 5.3601C12.221 5.3601 12.4001 5.18102 12.4001 4.9601C12.4001 4.73919 12.221 4.5601 12.0001 4.5601C11.7792 4.5601 11.6001 4.73919 11.6001 4.9601C11.6001 5.18102 11.7792 5.3601 12.0001 5.3601Z"
|
||||
fill="#2C3338"
|
||||
/>
|
||||
<path
|
||||
d="M13.8303 19C14.0017 18.7054 14.1 18.3637 14.1 18L9.9 18.0002C9.9 18.3638 9.99834 18.7055 10.1697 19.0002C10.5169 19.5968 11.1636 20.0002 11.9 20.0002C11.9339 20.0002 11.9677 19.9993 12.0012 19.9976C12.034 19.9992 12.0669 20 12.1 20C12.8364 20 13.4831 19.5967 13.8303 19Z"
|
||||
fill="#2C3338"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
@ -1,62 +1,25 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import classnames from 'classnames';
|
||||
import { withFeatureAnnouncement } from 'announcements/with-feature-announcement';
|
||||
import { HideScreenOptions } from 'common/hide-screen-options/hide-screen-options';
|
||||
import { MailPoetLogoResponsive } from './mailpoet-logo-responsive';
|
||||
import { BeamerIcon } from './beamer-icon';
|
||||
import { ScreenOptionsFix } from './screen-options-fix';
|
||||
import { withBoundary } from '../error-boundary';
|
||||
import { MailPoet } from '../../mailpoet';
|
||||
|
||||
type Props = {
|
||||
children?: ReactNode;
|
||||
hasNews?: boolean;
|
||||
onBeamerClick?: () => void;
|
||||
logoWithLink?: boolean;
|
||||
hideScreenOptions?: boolean;
|
||||
};
|
||||
|
||||
export function TopBar({
|
||||
children,
|
||||
hasNews,
|
||||
onBeamerClick,
|
||||
logoWithLink = true,
|
||||
hideScreenOptions = false,
|
||||
}: Props) {
|
||||
const buttonClasses = classnames(
|
||||
'mailpoet-top-bar-beamer',
|
||||
hasNews ? 'mailpoet-top-bar-beamer-dot' : '',
|
||||
);
|
||||
return (
|
||||
<div className="mailpoet-top-bar">
|
||||
<MailPoetLogoResponsive withLink={logoWithLink} />
|
||||
<div className="mailpoet-top-bar-children">{children}</div>
|
||||
<div className="mailpoet-flex-grow" />
|
||||
{onBeamerClick && MailPoet.libs3rdPartyEnabled && (
|
||||
<div>
|
||||
<a
|
||||
role="button"
|
||||
onClick={onBeamerClick}
|
||||
className={buttonClasses}
|
||||
title={__('What’s new', 'mailpoet')}
|
||||
tabIndex={0}
|
||||
onKeyDown={(event) => {
|
||||
if (
|
||||
['keydown', 'keypress'].includes(event.type) &&
|
||||
['Enter', ' '].includes(event.key)
|
||||
) {
|
||||
event.preventDefault();
|
||||
onBeamerClick();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<BeamerIcon />
|
||||
<span>{__('Updates', 'mailpoet')}</span>
|
||||
</a>
|
||||
<span id="beamer-empty-element" />
|
||||
</div>
|
||||
)}
|
||||
<ScreenOptionsFix />
|
||||
{hideScreenOptions && <HideScreenOptions />}
|
||||
</div>
|
||||
@ -64,4 +27,4 @@ export function TopBar({
|
||||
}
|
||||
|
||||
TopBar.displayName = 'TopBar';
|
||||
export const TopBarWithBeamer = withFeatureAnnouncement(withBoundary(TopBar));
|
||||
export const TopBarWithBoundary = withBoundary(TopBar);
|
||||
|
@ -5,7 +5,7 @@ import { Categories } from 'common/categories/categories';
|
||||
import { Background } from 'common/background/background';
|
||||
import { Loading } from 'common/loading';
|
||||
import { TemplateBox } from 'common/template-box/template-box';
|
||||
import { TopBarWithBeamer } from 'common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from 'common/top-bar/top-bar';
|
||||
import { Notice } from 'notices/notice';
|
||||
import { TemplateData } from './store/types';
|
||||
import { storeName } from './store/constants';
|
||||
@ -91,7 +91,7 @@ export function Selection(): JSX.Element {
|
||||
),
|
||||
),
|
||||
)}
|
||||
<TopBarWithBeamer />
|
||||
<TopBarWithBoundary />
|
||||
{selectTemplateFailed && (
|
||||
<Notice type="error" scroll renderInPlace>
|
||||
<p>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { TopBarWithBeamer } from 'common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from 'common/top-bar/top-bar';
|
||||
import { Button } from 'common/button/button';
|
||||
import { plusIcon } from 'common/button/icon/plus';
|
||||
|
||||
@ -14,7 +14,7 @@ export const onAddNewForm = () => {
|
||||
function FormsHeading() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
return (
|
||||
<TopBarWithBeamer>
|
||||
<TopBarWithBoundary>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
@ -27,7 +27,7 @@ function FormsHeading() {
|
||||
>
|
||||
{MailPoet.I18n.t('new')}
|
||||
</Button>
|
||||
</TopBarWithBeamer>
|
||||
</TopBarWithBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
|
10
mailpoet/assets/js/src/global.d.ts
vendored
10
mailpoet/assets/js/src/global.d.ts
vendored
@ -15,9 +15,9 @@ type ErrorResponse = {
|
||||
type MtaLog = {
|
||||
status: string;
|
||||
error: {
|
||||
error_message: ?string;
|
||||
error_code: ?string;
|
||||
operation: ?string;
|
||||
error_message?: string;
|
||||
error_code?: string;
|
||||
operation?: string;
|
||||
};
|
||||
};
|
||||
|
||||
@ -134,6 +134,7 @@ interface Window {
|
||||
mailpoet_date_format: string;
|
||||
mailpoet_listing_per_page: string;
|
||||
mailpoet_3rd_party_libs_enabled: string;
|
||||
mailpoet_analytics_enabled: boolean;
|
||||
mailpoet_datetime_format: string;
|
||||
mailpoet_api_version: string;
|
||||
mailpoet_email_regex: RegExp;
|
||||
@ -294,4 +295,7 @@ interface Window {
|
||||
dataInconsistencies: {
|
||||
[key: string]: number;
|
||||
};
|
||||
mailpoet_block_email_editor_enabled: boolean;
|
||||
satismeter: (action: string, data: Record<string, unknown>) => void;
|
||||
mailpoet_display_nps_email_editor: boolean;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Tooltip } from 'help-tooltip.jsx';
|
||||
import { Tooltip } from './help-tooltip.jsx';
|
||||
import { createElement } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
|
@ -73,7 +73,7 @@ export function ProductDiscovery({ onHide }: Props): JSX.Element {
|
||||
title={MailPoet.I18n.t('brandWooEmails')}
|
||||
description={MailPoet.I18n.t('brandWooEmailsDesc')}
|
||||
link="admin.php?page=mailpoet-settings#/woocommerce"
|
||||
imgSrc={`${MailPoet.cdnUrl}homepage/woo-transactional-email-illustration.png`}
|
||||
imgSrc={`${MailPoet.cdnUrl}homepage/woo-transactional-email-illustration.20241219.png`}
|
||||
isDone={tasksStatus.brandWooEmails}
|
||||
doneMessage={MailPoet.I18n.t('brandWooEmailsDone')}
|
||||
/>,
|
||||
|
@ -13,7 +13,7 @@ export function Resources(): JSX.Element {
|
||||
link="https://kb.mailpoet.com/article/141-create-an-email-types-of-campaigns?utm_source=plugin&utm_medium=homepage&utm_campaign=resources"
|
||||
abstract={MailPoet.I18n.t('createAnEmailAbstract')}
|
||||
title={MailPoet.I18n.t('createAnEmailTitle')}
|
||||
imgSrc={`${MailPoet.cdnUrl}homepage/resources/add_email.png`}
|
||||
imgSrc={`${MailPoet.cdnUrl}homepage/resources/add_email.20241219.png`}
|
||||
/>,
|
||||
<ResourcePost
|
||||
key="createAForm"
|
||||
|
@ -2,7 +2,7 @@ import { createRoot } from 'react-dom/client';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ErrorBoundary, registerTranslations } from 'common';
|
||||
import { GlobalContext, useGlobalContextValue } from 'context';
|
||||
import { TopBarWithBeamer } from 'common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from 'common/top-bar/top-bar';
|
||||
import { GlobalNotices } from 'notices/global-notices';
|
||||
import { HomepageNotices } from 'homepage/notices';
|
||||
import { HomepageSections } from './components/homepage-sections';
|
||||
@ -16,7 +16,7 @@ function App(): JSX.Element {
|
||||
}, []);
|
||||
return (
|
||||
<GlobalContext.Provider value={useGlobalContextValue(window)}>
|
||||
<TopBarWithBeamer />
|
||||
<TopBarWithBoundary />
|
||||
<GlobalNotices />
|
||||
<HomepageNotices />
|
||||
{isStoreInitialized ? <HomepageSections /> : null}
|
||||
|
@ -12,7 +12,7 @@ const Images = {
|
||||
icon_4: `${MailPoet.cdnUrl}landingpage/feature_icon_4.png`,
|
||||
},
|
||||
wooCommerceFeatureImages: {
|
||||
feature_1: `${MailPoet.cdnUrl}landingpage/woo_feature_automate_your_marketing.png`,
|
||||
feature_1: `${MailPoet.cdnUrl}landingpage/woo_feature_automate_your_marketing.20241219.png`,
|
||||
feature_2: `${MailPoet.cdnUrl}landingpage/woo_feature_measure_revenue_per_email.png`,
|
||||
feature_3: `${MailPoet.cdnUrl}landingpage/woo_feature_let_your_brand_shine.png`,
|
||||
feature_4: `${MailPoet.cdnUrl}landingpage/woo_feature_rescue_abandoned_carts.png`,
|
||||
|
@ -3,7 +3,7 @@ import { GlobalContext, useGlobalContextValue } from 'context';
|
||||
import { ErrorBoundary, registerTranslations } from 'common';
|
||||
import { Background } from 'common/background/background';
|
||||
import { HideScreenOptions } from 'common/hide-screen-options/hide-screen-options';
|
||||
import { TopBarWithBeamer } from 'common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from 'common/top-bar/top-bar';
|
||||
import { Header } from './header';
|
||||
import { Footer } from './footer';
|
||||
import { Faq } from './faq';
|
||||
@ -14,7 +14,7 @@ function Landingpage() {
|
||||
<GlobalContext.Provider value={useGlobalContextValue(window)}>
|
||||
<main>
|
||||
<HideScreenOptions />
|
||||
<TopBarWithBeamer />
|
||||
<TopBarWithBoundary />
|
||||
|
||||
<Background color="#fff" />
|
||||
|
||||
|
@ -23,8 +23,11 @@ class ListingComponent extends Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { params: propsParams, auto_refresh: autoRefresh = false } =
|
||||
this.props;
|
||||
const {
|
||||
params: propsParams,
|
||||
auto_refresh: autoRefresh = false,
|
||||
refreshRef = null,
|
||||
} = this.props;
|
||||
this.isComponentMounted = true;
|
||||
const params = propsParams || {};
|
||||
this.initWithParams(params);
|
||||
@ -34,6 +37,10 @@ class ListingComponent extends Component {
|
||||
this.getItems();
|
||||
});
|
||||
}
|
||||
|
||||
if (refreshRef) {
|
||||
refreshRef.current = () => this.getItems();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@ -751,6 +758,7 @@ ListingComponent.propTypes = {
|
||||
isItemDeletable: PropTypes.func,
|
||||
isItemToggleable: PropTypes.func,
|
||||
className: PropTypes.string,
|
||||
refreshRef: PropTypes.shape({ current: PropTypes.func }),
|
||||
};
|
||||
|
||||
ListingComponent.displayName = 'Listing';
|
||||
|
@ -14,7 +14,7 @@
|
||||
"inserter": false,
|
||||
"lock": false
|
||||
},
|
||||
"apiVersion": 2,
|
||||
"apiVersion": 3,
|
||||
"$schema": "https://schemas.wp.org/trunk/block.json",
|
||||
"attributes": {
|
||||
"logo": {
|
||||
|
@ -2,8 +2,11 @@ import { registerBlockType } from '@wordpress/blocks';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
|
||||
import { PanelBody, RadioControl, Icon } from '@wordpress/components';
|
||||
import { useState } from 'react';
|
||||
import metadata from './block.json';
|
||||
import MailPoetIcon from './mailpoet-icon';
|
||||
import { PremiumModal } from '../../common/premium-modal';
|
||||
import './style.scss';
|
||||
|
||||
const getCdnUrl = () => window.mailpoet_cdn_url;
|
||||
const getPremiumPluginStatus = () => window.mailpoet_premium_active;
|
||||
@ -15,8 +18,31 @@ function LogoImage({
|
||||
logoSrc: string;
|
||||
style?: React.CSSProperties;
|
||||
}): JSX.Element {
|
||||
const [isModalOpened, setIsModalOpened] = useState(false);
|
||||
|
||||
return (
|
||||
<img src={logoSrc} style={style} alt="Powered by MailPoet" width="100px" />
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="mailpoet-email-footer-credit"
|
||||
onClick={() => setIsModalOpened(true)}
|
||||
>
|
||||
<img
|
||||
src={logoSrc}
|
||||
style={style}
|
||||
alt="Powered by MailPoet"
|
||||
width="100px"
|
||||
/>
|
||||
</button>
|
||||
{!!isModalOpened && (
|
||||
<PremiumModal onRequestClose={() => setIsModalOpened(false)}>
|
||||
{__(
|
||||
'A MailPoet logo will appear in the footer of all emails sent with the free version of MailPoet.',
|
||||
'mailpoet',
|
||||
)}
|
||||
</PremiumModal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -65,15 +91,6 @@ function Edit({
|
||||
) as unknown as string,
|
||||
value: 'light',
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<LogoImage
|
||||
logoSrc={`${cdnUrl}email-editor/logo-dark.png`}
|
||||
style={{ background: '#000000' }}
|
||||
/>
|
||||
) as unknown as string,
|
||||
value: 'dark',
|
||||
},
|
||||
]}
|
||||
onChange={(value) => {
|
||||
setAttributes({
|
||||
|
@ -0,0 +1,28 @@
|
||||
.mailpoet-email-footer-credit {
|
||||
appearance: none;
|
||||
background: none;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// Those styles are copied from the file _commons.scss but because there is only a few of them
|
||||
// it's better to keep them here to avoid creating some logic to include another file.
|
||||
.mailpoet-premium-modal.components-modal__frame {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.mailpoet-premium-modal-footer {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.mailpoet-premium-modal-error {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 8px;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
// Core MailPoet styles. We need this here because the email editor does not register
|
||||
// under the mailpoet namespace and does not have access to the MailPoet plugin styles
|
||||
// These styles are required by ReviewRequest (mailpoet/assets/js/src/review-request.tsx) feature
|
||||
|
||||
// Settings
|
||||
// Global variables, config switches. Not producing any CSS.
|
||||
@ -23,3 +24,6 @@
|
||||
// Actual UI components.
|
||||
@import '../../../css/src/components-plugin/legacy-modal';
|
||||
@import '../../../css/src/components-plugin/review-request.scss';
|
||||
|
||||
//Integration
|
||||
@import './integration';
|
||||
|
@ -2,10 +2,54 @@
|
||||
// We have something similar for the PHP package in `mailpoet/lib/EmailEditor/Integrations`
|
||||
// Here, we can expose MailPoet specific components for use in the Email editor.
|
||||
|
||||
import { addFilter } from '@wordpress/hooks';
|
||||
import { withNpsPoll } from '../nps-poll';
|
||||
import { addFilter, addAction } from '@wordpress/hooks';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { withSatismeterSurvey } from './satismeter-survey';
|
||||
import './index.scss';
|
||||
import { useValidationRules } from './validate-email-content';
|
||||
|
||||
addFilter('mailpoet_email_editor_wrap_editor_component', 'mailpoet', (editor) =>
|
||||
withNpsPoll(editor),
|
||||
withSatismeterSurvey(editor),
|
||||
);
|
||||
|
||||
addFilter(
|
||||
'mailpoet_email_editor_content_validation_rules',
|
||||
'mailpoet',
|
||||
(validationRules: []) => [...validationRules, ...useValidationRules()],
|
||||
);
|
||||
|
||||
const EVENTS_TO_TRACK = [
|
||||
'email_editor_events_editor_layout_loaded', // email editor was opened
|
||||
'email_editor_events_template_select_modal_template_selected', // a template was selected from the template-select modal
|
||||
'email_editor_events_template_select_modal_start_from_scratch_clicked', // start from scratch
|
||||
'email_editor_events_header_campaign_name_title_updated', // campaign title was used
|
||||
'email_editor_events_header_preview_dropdown_mobile_selected', // preview option - mobile
|
||||
'email_editor_events_header_preview_dropdown_desktop_selected', // preview option - desktop
|
||||
'email_editor_events_header_preview_dropdown_send_test_email_selected', // preview option - send test email
|
||||
'email_editor_events_sent_preview_email', // preview email sent
|
||||
'email_editor_events_header_preview_dropdown_preview_in_new_tab_selected', // preview option - in new tab
|
||||
'email_editor_events_rich_text_with_button_personalization_tags_shortcode_icon_clicked', // personalization_tags modal opened
|
||||
'email_editor_events_personalization_tags_modal_tag_insert_button_clicked', // personalization_tags inserted
|
||||
'email_editor_events_rich_text_with_button_input_field_updated', // either subject or preheader updated
|
||||
'email_editor_events_styles_sidebar_screen_typography_opened', // styles sidebar-typography was seen
|
||||
'email_editor_events_styles_sidebar_screen_colors_opened', // styles sidebar-colors was seen
|
||||
'email_editor_events_styles_sidebar_screen_layout_opened', // styles sidebar-layout was seen
|
||||
'email_editor_events_header_send_button_clicked', // Send button clicked
|
||||
'email_editor_events_trash_modal_move_to_trash_button_clicked', // Move to trash button was clicked
|
||||
];
|
||||
|
||||
addAction('mailpoet_email_editor_events', 'mailpoet', (editorEvents) => {
|
||||
const { name, ...data } = editorEvents;
|
||||
// To prevent going over mixpanel quota, we will limit the number of email editor events we track with mixpanel
|
||||
// Tracks will log all events. This will be done in MAILPOET-5995
|
||||
if (EVENTS_TO_TRACK.includes(String(name))) {
|
||||
MailPoet.trackEvent(name, data);
|
||||
}
|
||||
});
|
||||
|
||||
// enable email editor event tracking
|
||||
addFilter(
|
||||
'mailpoet_email_editor_events_tracking_enabled',
|
||||
'mailpoet',
|
||||
() => !!window.mailpoet_analytics_enabled,
|
||||
);
|
||||
|
@ -0,0 +1,10 @@
|
||||
.mailpoet-editor-feedback-button {
|
||||
bottom: 5px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
}
|
||||
// To be able to scroll up a bit more in case the sidebar content is long button is displayed over it.
|
||||
.edit-post-sidebar {
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 40px;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { Button } from '@wordpress/components';
|
||||
import { useLayoutEffect, useState } from '@wordpress/element';
|
||||
import { commentContent } from '@wordpress/icons';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { initializeSatismeterSurvey } from 'nps-poll';
|
||||
|
||||
const emailEditorSatismeterWriteId = '9qCj2SJBE1s5OhnX5NYfRXu82pEDUB9x';
|
||||
|
||||
export function withSatismeterSurvey(Component) {
|
||||
return function WrappedBySurvey(props) {
|
||||
const [surveyAvailable, setSurveyAvailable] = useState(false);
|
||||
|
||||
const triggerSurvey = () => {
|
||||
// The survey is configured to open when we track the 'Request feedback' event
|
||||
window.satismeter('track', { event: 'Request feedback' });
|
||||
};
|
||||
|
||||
useLayoutEffect(() => {
|
||||
// Initialize Satismeter Survey for the email editor
|
||||
void initializeSatismeterSurvey(emailEditorSatismeterWriteId)
|
||||
.then(() => {
|
||||
if (!window.satismeter) {
|
||||
return;
|
||||
}
|
||||
setSurveyAvailable(true);
|
||||
|
||||
// We want to show the survey immediately when there has been enough usage
|
||||
if (window.mailpoet_display_nps_email_editor) {
|
||||
window.mailpoet_display_nps_email_editor = false;
|
||||
void MailPoet.Ajax.post({
|
||||
api_version: MailPoet.apiVersion,
|
||||
endpoint: 'user_flags',
|
||||
action: 'set',
|
||||
data: {
|
||||
email_editor_survey_seen: MailPoet.Date.toGmtDatetimeString(
|
||||
new Date(),
|
||||
),
|
||||
},
|
||||
}).then(triggerSurvey);
|
||||
}
|
||||
})
|
||||
// Survey may fail to initialize when 3rd party libs are not allowed. It is OK we don't need to react.
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Component {...props} />
|
||||
{surveyAvailable && (
|
||||
<Button
|
||||
icon={commentContent}
|
||||
variant="tertiary"
|
||||
className="mailpoet-editor-feedback-button"
|
||||
onClick={triggerSurvey}
|
||||
>
|
||||
{__('Share feedback', 'mailpoet')}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
import { useMemo } from '@wordpress/element';
|
||||
import { createBlock } from '@wordpress/blocks';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { dispatch, useSelect } from '@wordpress/data';
|
||||
import { store as blockEditorStore } from '@wordpress/block-editor';
|
||||
import { store as coreDataStore } from '@wordpress/core-data';
|
||||
|
||||
const emailEditorStore = 'email-editor/editor';
|
||||
|
||||
const contentLink = `<a data-link-href='[mailpoet/subscription-unsubscribe-url]' contenteditable='false' style='text-decoration: underline;' class='mailpoet-email-editor__personalization-tags-link'>${__(
|
||||
'Unsubscribe',
|
||||
'mailpoet',
|
||||
)}</a> | <a data-link-href='[mailpoet/subscription-manage-url]' contenteditable='false' style='text-decoration: underline;' class='mailpoet-email-editor__personalization-tags-link'>${__(
|
||||
'Manage subscription',
|
||||
'mailpoet',
|
||||
)}</a>`;
|
||||
|
||||
export function useValidationRules() {
|
||||
const { contentBlockId, hasFooter } = useSelect((select) => {
|
||||
const allBlocks = select(blockEditorStore).getBlocks();
|
||||
const noBodyBlocks = allBlocks.filter(
|
||||
(block) =>
|
||||
block.name !== 'mailpoet/powered-by-mailpoet' &&
|
||||
block.name !== 'core/post-content',
|
||||
);
|
||||
// @ts-expect-error getBlocksByName is not defined in types
|
||||
const blocks = select(blockEditorStore).getBlocksByName(
|
||||
'core/post-content',
|
||||
) as string[] | undefined;
|
||||
return {
|
||||
contentBlockId: blocks?.[0],
|
||||
hasFooter: noBodyBlocks.length > 0,
|
||||
};
|
||||
});
|
||||
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
const { editedTemplateContent, postTemplateId } = useSelect((mapSelect) => ({
|
||||
editedTemplateContent:
|
||||
// @ts-ignore
|
||||
mapSelect(emailEditorStore).getCurrentTemplateContent() as string,
|
||||
postTemplateId:
|
||||
// @ts-ignore
|
||||
mapSelect(emailEditorStore).getCurrentTemplate()?.id as string,
|
||||
}));
|
||||
|
||||
return useMemo(() => {
|
||||
const linksParagraphBlock = createBlock('core/paragraph', {
|
||||
align: 'center',
|
||||
fontSize: 'small',
|
||||
content: contentLink,
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'missing-unsubscribe-link',
|
||||
test: (emailContent: string) =>
|
||||
!emailContent.includes('[mailpoet/subscription-unsubscribe-url]'),
|
||||
message: __(
|
||||
'All emails must include an "Unsubscribe" link.',
|
||||
'mailpoet',
|
||||
),
|
||||
actions: [
|
||||
{
|
||||
label: __('Insert link', 'mailpoet'),
|
||||
onClick: () => {
|
||||
if (!hasFooter) {
|
||||
void dispatch(blockEditorStore).insertBlock(
|
||||
linksParagraphBlock,
|
||||
undefined,
|
||||
contentBlockId,
|
||||
);
|
||||
} else {
|
||||
void dispatch(coreDataStore).editEntityRecord(
|
||||
'postType',
|
||||
'wp_template',
|
||||
postTemplateId,
|
||||
{
|
||||
content: `
|
||||
${editedTemplateContent}
|
||||
<!-- wp:paragraph {"align":"center","fontSize":"small"} -->
|
||||
${contentLink}
|
||||
<!-- /wp:paragraph -->
|
||||
`,
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}, [contentBlockId, postTemplateId, hasFooter, editedTemplateContent]);
|
||||
}
|
@ -73,10 +73,16 @@ BehaviorsLookup.TextEditorBehavior = Marionette.Behavior.extend({
|
||||
return url;
|
||||
}
|
||||
|
||||
return this.documentBaseURI.toAbsolute(
|
||||
const result = this.documentBaseURI.toAbsolute(
|
||||
url,
|
||||
this.options.get('remove_script_host'),
|
||||
);
|
||||
// Because TinyMCE contains an issue when inserted URLs ampersands are encoded twice
|
||||
// We remove one of them to store the URL correctly into the database.
|
||||
// related GH issues:
|
||||
// - https://github.com/tinymce/tinymce/issues/9774
|
||||
// - https://github.com/tinymce/tinymce/issues/9618
|
||||
return result.replace('&', '&');
|
||||
},
|
||||
|
||||
plugins: this.options.plugins,
|
||||
|
@ -34,10 +34,7 @@ const redirectToNewsletterHome = () => {
|
||||
|
||||
const getEditorLink = (newsletter: NewsletterType) => {
|
||||
let editorHref = `?page=mailpoet-newsletter-editor&id=${newsletter.id}`;
|
||||
if (
|
||||
MailPoet.FeaturesController.isSupported('gutenberg_email_editor') &&
|
||||
newsletter.wp_post_id
|
||||
) {
|
||||
if (newsletter.wp_post_id) {
|
||||
editorHref = MailPoet.getBlockEmailEditorUrl(newsletter.wp_post_id);
|
||||
}
|
||||
return editorHref;
|
||||
|
@ -3,7 +3,7 @@ import { __, _x } from '@wordpress/i18n';
|
||||
import { Hooks } from 'wp-js-hooks';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { TopBarWithBeamer } from 'common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from 'common/top-bar/top-bar';
|
||||
import { HideScreenOptions } from 'common/hide-screen-options/hide-screen-options';
|
||||
import { RemoveWrapMargin } from 'common/remove-wrap-margin/remove-wrap-margin';
|
||||
import { Tabs } from 'common/tabs/tabs';
|
||||
@ -87,7 +87,7 @@ export function CampaignStatsPage() {
|
||||
<>
|
||||
<HideScreenOptions />
|
||||
<RemoveWrapMargin />
|
||||
<TopBarWithBeamer />
|
||||
<TopBarWithBoundary />
|
||||
|
||||
<div className="mailpoet-stats-page">
|
||||
<ErrorBoundary>
|
||||
|
@ -45,23 +45,41 @@ export function EditorSelectModal({
|
||||
if (!isModalOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MailPoet.trackEvent(
|
||||
'New Email Editor > try new email editor modal opened',
|
||||
{},
|
||||
{ send_immediately: true },
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={__('New editor', 'mailpoet')}
|
||||
onRequestClose={onClose}
|
||||
title={__('Try the new email editor', 'mailpoet')}
|
||||
onRequestClose={() => {
|
||||
MailPoet.trackEvent(
|
||||
'New Email Editor > try new email editor modal closed',
|
||||
);
|
||||
onClose();
|
||||
}}
|
||||
className="mailpoet-new-editor-modal"
|
||||
>
|
||||
<div className="mailpoet-new-editor-modal-image">
|
||||
<span className="mailpoet-new-editor-modal-image__beta_label">
|
||||
{__('Beta version', 'mailpoet')}
|
||||
{__('Alpha version', 'mailpoet')}
|
||||
</span>
|
||||
<img
|
||||
src={`${MailPoet.cdnUrl}email-editor/new-editor-modal-header.png`}
|
||||
alt={__('New editor', 'mailpoet')}
|
||||
alt={__('Try the new email editor', 'mailpoet')}
|
||||
width="324"
|
||||
height="130"
|
||||
/>
|
||||
</div>
|
||||
<p>
|
||||
{__(
|
||||
'Get a sneak peek of an early version of the upcoming email design experience and help shape its development.',
|
||||
'mailpoet',
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
'Create modern, beautiful emails that embody your brand with advanced customization and editing capabilities.',
|
||||
@ -79,7 +97,12 @@ export function EditorSelectModal({
|
||||
type="button"
|
||||
variant="tertiary"
|
||||
onClick={() => {
|
||||
onClose();
|
||||
MailPoet.trackEvent(
|
||||
'New Email Editor > try new email editor modal cancel button clicked',
|
||||
{},
|
||||
{ send_immediately: true },
|
||||
onClose,
|
||||
);
|
||||
}}
|
||||
>
|
||||
{__('Cancel', 'mailpoet')}
|
||||
@ -88,7 +111,14 @@ export function EditorSelectModal({
|
||||
type="button"
|
||||
variant="primary"
|
||||
isBusy={isLoading}
|
||||
onClick={createNewsletterAndOpenEditor}
|
||||
onClick={() => {
|
||||
MailPoet.trackEvent(
|
||||
'New Email Editor > try new email editor modal create with new editor button clicked',
|
||||
{},
|
||||
{ send_immediately: true },
|
||||
createNewsletterAndOpenEditor,
|
||||
);
|
||||
}}
|
||||
>
|
||||
{__('Continue', 'mailpoet')}
|
||||
</Button>
|
||||
|
@ -66,7 +66,7 @@ function TutorialIcon(): JSX.Element {
|
||||
<a
|
||||
role="button"
|
||||
onClick={displayTutorial}
|
||||
className="mailpoet-top-bar-beamer"
|
||||
className="mailpoet-top-bar-tutorial"
|
||||
title={__('Tutorial', 'mailpoet')}
|
||||
tabIndex={0}
|
||||
onKeyDown={(event) => {
|
||||
@ -82,7 +82,7 @@ function TutorialIcon(): JSX.Element {
|
||||
<Icon icon={video} />
|
||||
<span>{__('Tutorial', 'mailpoet')}</span>
|
||||
</a>
|
||||
<span id="beamer-empty-element" />
|
||||
<span />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { TopBarWithBeamer } from 'common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from 'common/top-bar/top-bar';
|
||||
import { plusIcon } from 'common/button/icon/plus';
|
||||
|
||||
export function ListingHeading() {
|
||||
return (
|
||||
<TopBarWithBeamer>
|
||||
<TopBarWithBoundary>
|
||||
<Link
|
||||
id="mailpoet-new-email"
|
||||
className="mailpoet-button button-secondary"
|
||||
@ -17,6 +17,6 @@ export function ListingHeading() {
|
||||
{plusIcon}
|
||||
<span>{__('New email', 'mailpoet')}</span>
|
||||
</Link>
|
||||
</TopBarWithBeamer>
|
||||
</TopBarWithBoundary>
|
||||
);
|
||||
}
|
||||
|
@ -76,12 +76,8 @@ const messages = {
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: MailPoet.FeaturesController.isSupported('gutenberg_email_editor')
|
||||
? 'name'
|
||||
: 'subject',
|
||||
label: MailPoet.FeaturesController.isSupported('gutenberg_email_editor')
|
||||
? __('Name', 'mailpoet')
|
||||
: __('Subject', 'mailpoet'),
|
||||
name: 'name',
|
||||
label: __('Name', 'mailpoet'),
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
@ -114,10 +110,7 @@ const bulkActions = [
|
||||
|
||||
const confirmEdit = (newsletter) => {
|
||||
let editorHref = `?page=mailpoet-newsletter-editor&id=${newsletter.id}`;
|
||||
if (
|
||||
MailPoet.FeaturesController.isSupported('gutenberg_email_editor') &&
|
||||
newsletter.wp_post_id
|
||||
) {
|
||||
if (newsletter.wp_post_id) {
|
||||
editorHref = MailPoet.getBlockEmailEditorUrl(newsletter.wp_post_id);
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ import { MssAccessNotices } from 'notices/mss-access-notices';
|
||||
import { CampaignStatsPage } from './campaign-stats/page';
|
||||
import { CorruptEmailNotice } from '../notices/corrupt-email-notice';
|
||||
import { LegacyAutomaticEmailsNotice } from '../notices/legacy-automatic-emails-notice';
|
||||
import { TopBarWithBeamer } from '../common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from '../common/top-bar/top-bar';
|
||||
import { BackButton, PageHeader } from '../common/page-header';
|
||||
|
||||
const trackTabSwitch = (tabKey) =>
|
||||
@ -98,7 +98,7 @@ Tabs.displayName = 'NewsletterTabs';
|
||||
function NewNewsletter({ history }) {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<TopBarWithBeamer />
|
||||
<TopBarWithBoundary />
|
||||
<div className="mailpoet-main-container">
|
||||
<PageHeader
|
||||
heading={__('What would you like to create?', 'mailpoet')}
|
||||
|
@ -75,10 +75,7 @@ function validateNewsletter(newsletter: NewsLetter) {
|
||||
// Don't validate emails created in the new editor.
|
||||
// The editor uses a different data format and will have own validation and also own send panel.
|
||||
// We are using the send page for the new editor only temporarily.
|
||||
if (
|
||||
MailPoet.FeaturesController.isSupported('gutenberg_email_editor') &&
|
||||
newsletter.wp_post_id !== null
|
||||
) {
|
||||
if (newsletter.wp_post_id !== null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -322,6 +319,13 @@ class NewsletterSendComponent extends Component<
|
||||
thumbnailPromise,
|
||||
validationError: validateNewsletter(response.data),
|
||||
});
|
||||
|
||||
if (response.data?.wp_post_id) {
|
||||
MailPoet.trackEvent(
|
||||
'New Email Editor > Send page opened-Newsletter created by BlockEmailEditor',
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.fail(() => {
|
||||
@ -471,6 +475,9 @@ class NewsletterSendComponent extends Component<
|
||||
scheduled: wasScheduled,
|
||||
'Segment Applied': !!this.state.item.options.filterSegmentId,
|
||||
segments,
|
||||
editor: this.state.item.wp_post_id
|
||||
? 'BlockEmailEditor'
|
||||
: 'legacyEditor',
|
||||
});
|
||||
if (wasScheduled) {
|
||||
this.context.notices.success(
|
||||
@ -871,10 +878,8 @@ class NewsletterSendComponent extends Component<
|
||||
<a
|
||||
className="mailpoet-link"
|
||||
href={
|
||||
MailPoet.FeaturesController.isSupported(
|
||||
'gutenberg_email_editor',
|
||||
) && wpPostId
|
||||
? MailPoet.getBlockEmailEditorUrl(wpPostId)
|
||||
wpPostId
|
||||
? MailPoet.getBlockEmailEditorUrl(Number(wpPostId))
|
||||
: `?page=mailpoet-newsletter-editor&id=${Number(
|
||||
this.props.params.id,
|
||||
)}`
|
||||
|
@ -1,6 +1,6 @@
|
||||
import classnames from 'classnames';
|
||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { memo, useEffect, useState, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { __, _x } from '@wordpress/i18n';
|
||||
|
||||
@ -45,6 +45,8 @@ function SendingStatus() {
|
||||
sent: false,
|
||||
});
|
||||
|
||||
const refreshRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
@ -74,7 +76,11 @@ function SendingStatus() {
|
||||
)}
|
||||
</h1>
|
||||
<StatsLink newsletter={newsletter} />
|
||||
<SendingStatusListing location={location} params={params} />
|
||||
<SendingStatusListing
|
||||
location={location}
|
||||
params={params}
|
||||
refreshRef={refreshRef}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -85,27 +91,28 @@ const compareProps = (prev, next) =>
|
||||
prev.location.pathname === next.location.pathname &&
|
||||
prev.params.id === next.params.id;
|
||||
|
||||
const onRenderItem = (item) => (
|
||||
const onRenderItem = (item, refreshRef) => (
|
||||
<div>
|
||||
<ListingItem {...item} />
|
||||
<ListingItem {...item} refreshRef={refreshRef} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const SendingStatusListing = memo(
|
||||
({ location, params }) => (
|
||||
({ location, params, refreshRef }) => (
|
||||
<Listing
|
||||
limit={window.mailpoet_listing_per_page}
|
||||
location={location}
|
||||
params={params}
|
||||
endpoint="sending_task_subscribers"
|
||||
base_url="sending-status/:id"
|
||||
onRenderItem={onRenderItem}
|
||||
onRenderItem={(item) => onRenderItem(item, refreshRef)}
|
||||
getListingItemKey={(item) => `${item.taskId}-${item.subscriberId}`}
|
||||
columns={columns}
|
||||
messages={messages}
|
||||
auto_refresh
|
||||
sort_by="failed"
|
||||
sort_order="desc"
|
||||
refreshRef={refreshRef}
|
||||
afterGetItems={(state) => {
|
||||
checkMailerStatus(state);
|
||||
checkCronStatus(state);
|
||||
@ -121,6 +128,7 @@ SendingStatusListing.propTypes = {
|
||||
params: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
refreshRef: PropTypes.shape({ current: PropTypes.func }),
|
||||
};
|
||||
|
||||
function StatsLink({
|
||||
@ -154,6 +162,7 @@ function ListingItem({
|
||||
subscriberId,
|
||||
lastName,
|
||||
firstName,
|
||||
refreshRef = null,
|
||||
error = '',
|
||||
}) {
|
||||
const resend = () => {
|
||||
@ -163,7 +172,7 @@ function ListingItem({
|
||||
action: 'resend',
|
||||
data: { taskId, subscriberId },
|
||||
})
|
||||
.done(() => window.mailpoet_listing.forceUpdate())
|
||||
.done(() => refreshRef?.current && refreshRef.current())
|
||||
.fail((res) => MailPoet.Notice.showApiErrorNotice(res));
|
||||
};
|
||||
|
||||
@ -250,6 +259,7 @@ ListingItem.propTypes = {
|
||||
firstName: PropTypes.string.isRequired,
|
||||
processed: PropTypes.number.isRequired,
|
||||
subscriberId: PropTypes.number.isRequired,
|
||||
refreshRef: PropTypes.shape({ current: PropTypes.func }),
|
||||
};
|
||||
ListingItem.displayName = 'ListingItem';
|
||||
SendingStatus.displayName = 'SendingStatus';
|
||||
|
@ -36,9 +36,7 @@ export function NewsletterTypes({
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
|
||||
const [isSelectEditorModalOpen, setIsSelectEditorModalOpen] = useState(false);
|
||||
const isNewEmailEditorEnabled = MailPoet.FeaturesController.isSupported(
|
||||
'gutenberg_email_editor',
|
||||
);
|
||||
const isNewEmailEditorEnabled = window.mailpoet_block_email_editor_enabled;
|
||||
|
||||
const setupNewsletter = (type): void => {
|
||||
if (type !== undefined) {
|
||||
@ -156,12 +154,21 @@ export function NewsletterTypes({
|
||||
<Icon icon={chevronDown} size={24} />
|
||||
</Button>
|
||||
)}
|
||||
onToggle={(isOpen) =>
|
||||
isOpen &&
|
||||
MailPoet.trackEvent('New Email Editor > create email icon clicked')
|
||||
}
|
||||
renderContent={() => (
|
||||
<MenuItem
|
||||
variant="tertiary"
|
||||
onClick={() => setIsSelectEditorModalOpen(true)}
|
||||
onClick={() => {
|
||||
setIsSelectEditorModalOpen(true);
|
||||
MailPoet.trackEvent(
|
||||
'New Email Editor > creating using new email editor button clicked',
|
||||
);
|
||||
}}
|
||||
>
|
||||
{__('Create using new editor (Beta)', 'mailpoet')}
|
||||
{__('Create using the new email editor (Alpha)', 'mailpoet')}
|
||||
</MenuItem>
|
||||
)}
|
||||
/>
|
||||
|
@ -228,6 +228,13 @@ export function MailerError({
|
||||
);
|
||||
}
|
||||
|
||||
let checkSettingsNotice = null;
|
||||
if (mtaMethod === 'PHPMail') {
|
||||
checkSettingsNotice = <PHPMailerCheckSettingsNotice />;
|
||||
} else if (mtaMethod !== 'MailPoet') {
|
||||
checkSettingsNotice = <MailerCheckSettingsNotice />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<p>
|
||||
@ -242,11 +249,7 @@ export function MailerError({
|
||||
)}
|
||||
: <i>{message}</i>
|
||||
</p>
|
||||
{mtaMethod === 'PHPMail' ? (
|
||||
<PHPMailerCheckSettingsNotice />
|
||||
) : (
|
||||
<MailerCheckSettingsNotice />
|
||||
)}
|
||||
{checkSettingsNotice}
|
||||
<p>
|
||||
<a
|
||||
href="#"
|
||||
|
@ -5,78 +5,101 @@ import satismeter from 'satismeter-loader';
|
||||
import { ReviewRequest } from 'review-request';
|
||||
import { getTrackingData } from 'analytics.js';
|
||||
|
||||
const useNpsPoll = () => {
|
||||
useLayoutEffect(() => {
|
||||
const showReviewRequestModal = () => {
|
||||
MailPoet.Modal.popup({
|
||||
width: 800,
|
||||
template: ReactDOMServer.renderToString(
|
||||
ReviewRequest({
|
||||
username:
|
||||
window.mailpoet_current_wp_user_firstname ||
|
||||
window.mailpoet_current_wp_user.user_login,
|
||||
reviewRequestIllustrationUrl:
|
||||
window.mailpoet_review_request_illustration_url,
|
||||
installedDaysAgo: window.mailpoet_installed_days_ago,
|
||||
}),
|
||||
),
|
||||
onInit: () => {
|
||||
document
|
||||
.getElementById('mailpoet_review_request_not_now')
|
||||
.addEventListener('click', () => MailPoet.Modal.close());
|
||||
},
|
||||
});
|
||||
};
|
||||
export const initializeSatismeterSurvey = (writeId = null) => {
|
||||
const showReviewRequestModal = () => {
|
||||
MailPoet.Modal.popup({
|
||||
width: 800,
|
||||
template: ReactDOMServer.renderToString(
|
||||
ReviewRequest({
|
||||
username:
|
||||
window.mailpoet_current_wp_user_firstname ||
|
||||
window.mailpoet_current_wp_user.user_login,
|
||||
reviewRequestIllustrationUrl:
|
||||
window.mailpoet_review_request_illustration_url,
|
||||
installedDaysAgo: window.mailpoet_installed_days_ago,
|
||||
}),
|
||||
),
|
||||
onInit: () => {
|
||||
document
|
||||
.getElementById('mailpoet_review_request_not_now')
|
||||
.addEventListener('click', () => MailPoet.Modal.close());
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const callSatismeter = (trackingData) => {
|
||||
const newUsersPollId = '6L479eVPXk7pBn6S';
|
||||
const oldUsersPollId = 'k0aJAsQAWI2ERyGv';
|
||||
const formPollId = 'EqOgKsgZd832Sz9w';
|
||||
const emailEditorPollId = '9qCj2SJBE1s5OhnX5NYfRXu82pEDUB9x';
|
||||
let writeKey;
|
||||
if (window.mailpoet_display_nps_email_editor) {
|
||||
writeKey = emailEditorPollId;
|
||||
} else if (window.mailpoet_display_nps_form) {
|
||||
writeKey = formPollId;
|
||||
} else if (window.mailpoet_is_new_user) {
|
||||
writeKey = newUsersPollId;
|
||||
} else {
|
||||
writeKey = oldUsersPollId;
|
||||
}
|
||||
satismeter({
|
||||
writeKey,
|
||||
userId: window.mailpoet_current_wp_user.ID + window.mailpoet_site_url,
|
||||
traits: {
|
||||
name: window.mailpoet_current_wp_user.user_nicename,
|
||||
email: window.mailpoet_current_wp_user.user_email,
|
||||
mailpoetVersion: window.mailpoet_version,
|
||||
mailpoetPremiumIsActive: window.mailpoet_premium_active,
|
||||
createdAt: trackingData.installedAtIso,
|
||||
newslettersSent: trackingData.newslettersSent,
|
||||
welcomeEmails: trackingData.welcomeEmails,
|
||||
postnotificationEmails: trackingData.postnotificationEmails,
|
||||
woocommerceEmails: trackingData.woocommerceEmails,
|
||||
subscribers: trackingData.subscribers,
|
||||
lists: trackingData.lists,
|
||||
sendingMethod: trackingData.sendingMethod,
|
||||
woocommerceIsInstalled: trackingData.woocommerceIsInstalled,
|
||||
},
|
||||
events: {
|
||||
submit: (response) => {
|
||||
if (response.rating >= 9 && response.completed) {
|
||||
showReviewRequestModal();
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
const callSatismeter = (trackingData, customWriteId) => {
|
||||
const newUsersPollId = '6L479eVPXk7pBn6S';
|
||||
const oldUsersPollId = 'k0aJAsQAWI2ERyGv';
|
||||
const formPollId = 'EqOgKsgZd832Sz9w';
|
||||
let writeKey;
|
||||
if (customWriteId) {
|
||||
writeKey = customWriteId;
|
||||
} else if (window.mailpoet_display_nps_form) {
|
||||
writeKey = formPollId;
|
||||
} else if (window.mailpoet_is_new_user) {
|
||||
writeKey = newUsersPollId;
|
||||
} else {
|
||||
writeKey = oldUsersPollId;
|
||||
}
|
||||
const traits = {
|
||||
name: window.mailpoet_current_wp_user.user_nicename,
|
||||
email: window.mailpoet_current_wp_user.user_email,
|
||||
mailpoetVersion: window.mailpoet_version,
|
||||
mailpoetPremiumIsActive: window.mailpoet_premium_active,
|
||||
createdAt: trackingData.installedAtIso,
|
||||
newslettersSent: trackingData.newslettersSent,
|
||||
welcomeEmails: trackingData.welcomeEmails,
|
||||
postnotificationEmails: trackingData.postnotificationEmails,
|
||||
woocommerceEmails: trackingData.woocommerceEmails,
|
||||
subscribers: trackingData.subscribers,
|
||||
lists: trackingData.lists,
|
||||
sendingMethod: trackingData.sendingMethod,
|
||||
woocommerceIsInstalled: trackingData.woocommerceIsInstalled,
|
||||
woocommerceVersion: trackingData.woocommerceVersion,
|
||||
WordPressVersion: trackingData.WordPressVersion,
|
||||
blockTheme: trackingData.blockTheme,
|
||||
themeVersion: trackingData.themeVersion,
|
||||
theme: trackingData.theme,
|
||||
};
|
||||
if (trackingData.gutenbergVersion) {
|
||||
traits.gutenbergVersion = trackingData.gutenbergVersion;
|
||||
}
|
||||
if (trackingData.wooCommerceVersion) {
|
||||
traits.wooCommerceVersion = trackingData.wooCommerceVersion;
|
||||
}
|
||||
satismeter({
|
||||
writeKey,
|
||||
userId: window.mailpoet_current_wp_user.ID + window.mailpoet_site_url,
|
||||
traits,
|
||||
events: {
|
||||
submit: (response) => {
|
||||
if (response.rating >= 9 && response.completed) {
|
||||
showReviewRequestModal();
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (
|
||||
window.mailpoet_display_nps_poll &&
|
||||
window.mailpoet_3rd_party_libs_enabled
|
||||
) {
|
||||
getTrackingData().then(({ data }) => callSatismeter(data));
|
||||
getTrackingData().then(({ data }) => {
|
||||
callSatismeter(data, writeId);
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const useNpsPoll = () => {
|
||||
useLayoutEffect(() => {
|
||||
// Survey may fail to initialize when 3rd party libs are not allowed. It is OK. We don't need to react.
|
||||
initializeSatismeterSurvey().catch(() => {});
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
|
@ -87,11 +87,12 @@ jQuery(($) => {
|
||||
const cachebust = `${new Date().getTime()}`;
|
||||
|
||||
// regenerate captcha phrase
|
||||
// TODO fix hard-coded URL. Refactor to pass it as a parameter
|
||||
const url = new URL(window.location.href.split('?')[0]);
|
||||
url.searchParams.set('mailpoet_router', '');
|
||||
url.searchParams.set('mailpoet_page', 'subscriptions');
|
||||
url.searchParams.set('endpoint', 'subscription');
|
||||
url.searchParams.set('action', 'captchaRefresh');
|
||||
url.searchParams.set('mailpoet_page', 'template');
|
||||
url.searchParams.set('endpoint', 'captcha');
|
||||
url.searchParams.set('action', 'refresh');
|
||||
url.searchParams.set(
|
||||
'data',
|
||||
btoa(JSON.stringify({ captcha_session_id: captchaSessionId })),
|
||||
|
@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n';
|
||||
import { useParams, useLocation } from 'react-router-dom';
|
||||
|
||||
import { HideScreenOptions } from 'common/hide-screen-options/hide-screen-options';
|
||||
import { TopBarWithBeamer } from 'common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from 'common/top-bar/top-bar';
|
||||
import { Form } from './form';
|
||||
import { storeName } from './store';
|
||||
import { BackButton, PageHeader } from '../../common/page-header';
|
||||
@ -36,7 +36,7 @@ export function Editor(): JSX.Element {
|
||||
|
||||
return (
|
||||
<div className="mailpoet-main-container">
|
||||
<TopBarWithBeamer />
|
||||
<TopBarWithBoundary />
|
||||
<HideScreenOptions />
|
||||
|
||||
<PageHeader
|
||||
|
@ -7,14 +7,14 @@ import { plusIcon } from '../../common/button/icon/plus';
|
||||
import { PageHeader } from '../../common/page-header';
|
||||
import { SubscribersCacheMessage } from '../../common/subscribers-cache-message';
|
||||
import { SubscribersInPlan } from '../../common/subscribers-in-plan';
|
||||
import { TopBarWithBeamer } from '../../common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from '../../common/top-bar/top-bar';
|
||||
import { MailPoet } from '../../mailpoet';
|
||||
import { MssAccessNotices } from '../../notices/mss-access-notices';
|
||||
|
||||
export function DynamicSegmentList(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<TopBarWithBeamer hideScreenOptions />
|
||||
<TopBarWithBoundary hideScreenOptions />
|
||||
<Notices />
|
||||
|
||||
<PageHeader heading={__('Segments', 'mailpoet')}>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Button, SearchControl } from '@wordpress/components';
|
||||
import { HideScreenOptions } from 'common/hide-screen-options/hide-screen-options';
|
||||
import { TopBarWithBeamer } from 'common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from 'common/top-bar/top-bar';
|
||||
import {
|
||||
templates,
|
||||
templateCategories,
|
||||
@ -54,7 +54,7 @@ export function SegmentTemplates(): JSX.Element {
|
||||
return (
|
||||
<div className="mailpoet-main-container">
|
||||
<HideScreenOptions />
|
||||
<TopBarWithBeamer />
|
||||
<TopBarWithBoundary />
|
||||
<PageHeader
|
||||
className="mailpoet-templates-header"
|
||||
heading={__('Start with a pre-built segment', 'mailpoet')}
|
||||
|
@ -6,7 +6,7 @@ import { SubscribersLimitNotice } from 'notices/subscribers-limit-notice';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { BackButton, PageHeader } from '../../common/page-header';
|
||||
import { TopBarWithBeamer } from '../../common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from '../../common/top-bar/top-bar';
|
||||
|
||||
const fields = [
|
||||
{
|
||||
@ -49,7 +49,7 @@ function SegmentForm() {
|
||||
|
||||
return (
|
||||
<div className="mailpoet-main-container">
|
||||
<TopBarWithBeamer />
|
||||
<TopBarWithBoundary />
|
||||
<Background color="#fff" />
|
||||
<HideScreenOptions />
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { TopBarWithBeamer } from 'common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from 'common/top-bar/top-bar';
|
||||
import { plusIcon } from 'common/button/icon/plus';
|
||||
import { SubscribersInPlan } from 'common/subscribers-in-plan';
|
||||
import { MssAccessNotices } from 'notices/mss-access-notices';
|
||||
@ -9,7 +9,7 @@ import { SubscribersCacheMessage } from 'common/subscribers-cache-message';
|
||||
function ListHeading(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<TopBarWithBeamer>
|
||||
<TopBarWithBoundary>
|
||||
<Link
|
||||
className="mailpoet-button button-secondary"
|
||||
to="/new"
|
||||
@ -18,7 +18,7 @@ function ListHeading(): JSX.Element {
|
||||
{plusIcon}
|
||||
<span>{MailPoet.I18n.t('new')}</span>
|
||||
</Link>
|
||||
</TopBarWithBeamer>
|
||||
</TopBarWithBoundary>
|
||||
|
||||
<SubscribersInPlan
|
||||
subscribersInPlan={MailPoet.subscribersCount}
|
||||
|
@ -12,6 +12,7 @@ import { Reinstall } from './reinstall';
|
||||
import { RecalculateSubscriberScore } from './recalculate-subscriber-score';
|
||||
import { Logging } from './logging';
|
||||
import { BounceAddress } from './bounce-address';
|
||||
import { CaptchaOnSignup } from './captcha-on-signup';
|
||||
|
||||
export function Advanced() {
|
||||
return (
|
||||
@ -27,6 +28,7 @@ export function Advanced() {
|
||||
<ShareData />
|
||||
<Libs3rdParty />
|
||||
<Captcha />
|
||||
<CaptchaOnSignup />
|
||||
<Reinstall />
|
||||
<Logging />
|
||||
<SaveButton />
|
||||
|
@ -0,0 +1,44 @@
|
||||
import { t } from 'common/functions';
|
||||
import { Radio } from 'common/form/radio/radio';
|
||||
import { useSelector, useSetting } from 'settings/store/hooks';
|
||||
import { Inputs, Label } from 'settings/components';
|
||||
|
||||
export function CaptchaOnSignup() {
|
||||
const [enabled, setEnabled] = useSetting(
|
||||
'captcha',
|
||||
'on_register_forms',
|
||||
'enabled',
|
||||
);
|
||||
const hasWooCommerce = useSelector('hasWooCommerce')();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Label
|
||||
title={t('captchaOnRegisterTitle')}
|
||||
description={t(
|
||||
hasWooCommerce
|
||||
? 'captchaOnRegisterWooActiveDescription'
|
||||
: 'captchaOnRegisterWooInactiveDescription',
|
||||
)}
|
||||
htmlFor=""
|
||||
/>
|
||||
<Inputs>
|
||||
<Radio
|
||||
id="captcha-on-register-enabled"
|
||||
value="1"
|
||||
checked={enabled === '1'}
|
||||
onCheck={setEnabled}
|
||||
/>
|
||||
<label htmlFor="captcha-on-register-enabled">{t('yes')}</label>
|
||||
<span className="mailpoet-gap" />
|
||||
<Radio
|
||||
id="captcha-on-register-disabled"
|
||||
value=""
|
||||
checked={enabled === ''}
|
||||
onCheck={setEnabled}
|
||||
/>
|
||||
<label htmlFor="captcha-on-register-disabled">{t('no')}</label>
|
||||
</Inputs>
|
||||
</>
|
||||
);
|
||||
}
|
@ -50,6 +50,7 @@ function asObject<T extends Schema>(schema: T) {
|
||||
function asIs<T>(value: T): T {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function normalizeSettings(data: Record<string, unknown>): Settings {
|
||||
const text = asString('');
|
||||
const disabledCheckbox = asBoolean('1', '0', '0');
|
||||
@ -135,6 +136,9 @@ export function normalizeSettings(data: Record<string, unknown>): Settings {
|
||||
recaptcha_secret_token: text,
|
||||
recaptcha_invisible_site_token: text,
|
||||
recaptcha_invisible_secret_token: text,
|
||||
on_register_forms: asObject({
|
||||
enabled: disabledRadio,
|
||||
}),
|
||||
}),
|
||||
logging: asEnum(['everything', 'errors', 'nothing'], 'errors'),
|
||||
mta_group: asEnum(['mailpoet', 'website', 'smtp'], 'website'),
|
||||
|
@ -65,6 +65,9 @@ export type Settings = {
|
||||
recaptcha_secret_token: string;
|
||||
recaptcha_invisible_site_token: string;
|
||||
recaptcha_invisible_secret_token: string;
|
||||
on_register_forms: {
|
||||
enabled: '' | '1';
|
||||
};
|
||||
};
|
||||
logging: 'everything' | 'errors' | 'nothing';
|
||||
mta_group: 'mailpoet' | 'website' | 'smtp';
|
||||
|
@ -7,7 +7,7 @@ import { Form } from 'form/form.jsx';
|
||||
import { HideScreenOptions } from 'common/hide-screen-options/hide-screen-options';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { SubscribersLimitNotice } from 'notices/subscribers-limit-notice';
|
||||
import { TopBarWithBeamer } from '../common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from '../common/top-bar/top-bar';
|
||||
import { BackButton, PageHeader } from '../common/page-header';
|
||||
|
||||
const fields = [
|
||||
@ -242,7 +242,7 @@ function SubscriberForm() {
|
||||
const backUrl = location.state?.backUrl || '/';
|
||||
return (
|
||||
<div className="mailpoet-main-container">
|
||||
<TopBarWithBeamer />
|
||||
<TopBarWithBoundary />
|
||||
<Background color="#fff" />
|
||||
<HideScreenOptions />
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { TopBarWithBeamer } from 'common/top-bar/top-bar';
|
||||
import { TopBarWithBoundary } from 'common/top-bar/top-bar';
|
||||
import { plusIcon } from 'common/button/icon/plus';
|
||||
|
||||
export function SubscribersHeading() {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<TopBarWithBeamer>
|
||||
<TopBarWithBoundary>
|
||||
<Link
|
||||
className="mailpoet-button button-secondary"
|
||||
to={{
|
||||
@ -36,6 +36,6 @@ export function SubscribersHeading() {
|
||||
>
|
||||
{MailPoet.I18n.t('export')}
|
||||
</a>
|
||||
</TopBarWithBeamer>
|
||||
</TopBarWithBoundary>
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ function WelcomeWizardStepLayoutBody(props) {
|
||||
return (
|
||||
<div className="mailpoet-wizard-step">
|
||||
<div className="mailpoet-wizard-step-illustration">
|
||||
<img src={props.illustrationUrl} width="500" alt="" />
|
||||
<img src={props.illustrationUrl} alt="" />
|
||||
</div>
|
||||
<div className="mailpoet-wizard-step-content">{props.children}</div>
|
||||
</div>
|
||||
|
@ -55,6 +55,7 @@ function MSSStepSecondPart(): JSX.Element {
|
||||
'welcomeWizardMSSSecondPartInputPlaceholder',
|
||||
)}
|
||||
isFullWidth
|
||||
forceRevealed
|
||||
/>
|
||||
</label>
|
||||
|
||||
|
@ -43,6 +43,9 @@ if [ -d 'vendor-prefixed' ]; then
|
||||
mv vendor-prefixed vendor-prefixed-backup
|
||||
fi
|
||||
|
||||
echo '[BUILD] Install email editor dependencies'
|
||||
cd ../packages/php/email-editor && ../../../mailpoet/tools/vendor/composer.phar install --no-dev --prefer-dist --optimize-autoloader --no-scripts && cd -
|
||||
|
||||
# Production libraries.
|
||||
echo '[BUILD] Fetching production libraries'
|
||||
mkdir vendor-prefixed
|
||||
|
2405
mailpoet/changelog.txt
Normal file
2405
mailpoet/changelog.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -61,6 +61,9 @@
|
||||
"MailPoet\\Test\\DataGenerator\\": "tests/DataGenerator"
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
"soundasleep/html2text": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"pre-install-cmd": [
|
||||
"@php tools/install.php",
|
||||
|
10
mailpoet/composer.lock
generated
10
mailpoet/composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "9957467448f215809ac9dc8fbcab2b00",
|
||||
"content-hash": "51893b0f5ed38d130932b86b48e55b94",
|
||||
"packages": [
|
||||
{
|
||||
"name": "dragonmantank/cron-expression",
|
||||
@ -73,10 +73,11 @@
|
||||
"dist": {
|
||||
"type": "path",
|
||||
"url": "../packages/php/email-editor",
|
||||
"reference": "311798cfd57b26bb5df1fc7f97b5732e45603419"
|
||||
"reference": "53577c5aa3a97e82c58284d48c3aa339cb2a15d4"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4"
|
||||
"php": ">=7.4",
|
||||
"soundasleep/html2text": "^2.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -101,6 +102,9 @@
|
||||
],
|
||||
"code-style-fix": [
|
||||
"../../../mailpoet/tasks/code_sniffer/vendor/bin/phpcbf -p"
|
||||
],
|
||||
"phpstan": [
|
||||
"php ./tasks/run-phpstan.php"
|
||||
]
|
||||
},
|
||||
"description": "Email editor based on WordPress Gutenberg package.",
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
namespace MailPoet\API\JSON;
|
||||
|
||||
use MailPoet\Captcha\CaptchaConstants;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Exception;
|
||||
use MailPoet\Logging\LoggerFactory;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Subscription\Captcha\CaptchaConstants;
|
||||
use MailPoet\Tracy\ApiPanel\ApiPanel;
|
||||
use MailPoet\Tracy\DIPanel\DIPanel;
|
||||
use MailPoet\Util\Helpers;
|
||||
@ -28,6 +28,7 @@ class API {
|
||||
private $availableApiVersions = [
|
||||
'v1',
|
||||
];
|
||||
|
||||
/** @var ContainerInterface */
|
||||
private $container;
|
||||
|
||||
@ -71,7 +72,7 @@ class API {
|
||||
}
|
||||
|
||||
public function init() {
|
||||
// admin security token and API version
|
||||
// admin security token and API version
|
||||
WPFunctions::get()->addAction(
|
||||
'admin_head',
|
||||
[$this, 'setTokenAndAPIVersion']
|
||||
@ -106,10 +107,12 @@ class API {
|
||||
}
|
||||
|
||||
$ignoreToken = (
|
||||
$this->settings->get('captcha.type') != CaptchaConstants::TYPE_DISABLED &&
|
||||
$this->requestEndpoint === 'subscribers' &&
|
||||
$this->requestMethod === 'subscribe'
|
||||
);
|
||||
$this->settings->get('captcha.type') != CaptchaConstants::TYPE_DISABLED &&
|
||||
$this->requestEndpoint === 'subscribers' &&
|
||||
$this->requestMethod === 'subscribe'
|
||||
) || (
|
||||
$this->requestEndpoint === 'captcha'
|
||||
);
|
||||
|
||||
if (!$ignoreToken && $this->wp->wpVerifyNonce($this->requestToken, 'mailpoet_token') === false) {
|
||||
$errorMessage = __("Sorry, but we couldn't connect to the MailPoet server. Please refresh the web page and try again.", 'mailpoet');
|
||||
@ -122,20 +125,20 @@ class API {
|
||||
}
|
||||
|
||||
public function setRequestData($data, $requestType) {
|
||||
$this->requestApiVersion = !empty($data['api_version']) ? $data['api_version'] : false;
|
||||
$this->requestApiVersion = (!empty($data['api_version']) && is_string($data['api_version'])) ? $data['api_version'] : false;
|
||||
|
||||
$this->requestEndpoint = isset($data['endpoint'])
|
||||
$this->requestEndpoint = (isset($data['endpoint']) && is_string($data['endpoint']))
|
||||
? Helpers::underscoreToCamelCase(trim($data['endpoint']))
|
||||
: null;
|
||||
|
||||
// JS part of /wp-admin/customize.php does not like a 'method' field in a form widget
|
||||
$methodParamName = isset($data['mailpoet_method']) ? 'mailpoet_method' : 'method';
|
||||
$this->requestMethod = isset($data[$methodParamName])
|
||||
$this->requestMethod = (isset($data[$methodParamName]) && is_string($data[$methodParamName]))
|
||||
? Helpers::underscoreToCamelCase(trim($data[$methodParamName]))
|
||||
: null;
|
||||
$this->requestType = $requestType;
|
||||
|
||||
$this->requestToken = isset($data['token'])
|
||||
$this->requestToken = (isset($data['token']) && is_string($data['token']))
|
||||
? trim($data['token'])
|
||||
: null;
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace MailPoet\API\JSON;
|
||||
|
||||
use MailPoet\API\JSON\v1\RedirectResponse;
|
||||
use MailPoet\Config\AccessControl;
|
||||
|
||||
abstract class Endpoint {
|
||||
@ -41,6 +42,10 @@ abstract class Endpoint {
|
||||
return new ErrorResponse($errors, $meta, Response::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
public function redirectResponse($url) {
|
||||
return new RedirectResponse($url);
|
||||
}
|
||||
|
||||
public function isMethodAllowed($name, $type) {
|
||||
// Block GET requests on POST endpoints, but allow POST requests on GET endpoints (some plugins
|
||||
// change REQUEST_METHOD to POST on GET requests, which caused them to be blocked)
|
||||
|
@ -6,6 +6,7 @@ use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
abstract class Response {
|
||||
const STATUS_OK = 200;
|
||||
const REDIRECT = 302;
|
||||
const STATUS_BAD_REQUEST = 400;
|
||||
const STATUS_UNAUTHORIZED = 401;
|
||||
const STATUS_FORBIDDEN = 403;
|
||||
@ -15,16 +16,20 @@ abstract class Response {
|
||||
|
||||
public $status;
|
||||
public $meta;
|
||||
public $location;
|
||||
|
||||
public function __construct(
|
||||
$status,
|
||||
$meta = []
|
||||
) {
|
||||
public function __construct($status, $meta = [], $location = null) { // phpcs:ignore
|
||||
$this->status = $status;
|
||||
$this->meta = $meta;
|
||||
$this->location = $location;
|
||||
}
|
||||
|
||||
public function send() {
|
||||
if ($this->status === self::REDIRECT && $this->location) {
|
||||
header("Location: " . $this->location, true, $this->status);
|
||||
exit;
|
||||
}
|
||||
|
||||
WPFunctions::get()->statusHeader($this->status);
|
||||
|
||||
$data = $this->getData();
|
||||
|
@ -113,10 +113,31 @@ class NewslettersResponseBuilder {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NewsletterEntity[] $newsletters
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function processPersonalizationTags(?string $content): ?string {
|
||||
if (is_null($content) || strlen($content) === 0) {
|
||||
return $content;
|
||||
}
|
||||
if (strpos($content, '<!--') === false) {
|
||||
// we don't need to parse anything if there are no personalization tags
|
||||
return $content;
|
||||
}
|
||||
if (!class_exists('\MailPoet\EmailEditor\Engine\PersonalizationTags\HTML_Tag_Processor')) {
|
||||
// editor is not active, we cannot process personalization tags
|
||||
return $content;
|
||||
}
|
||||
|
||||
$content_processor = new \MailPoet\EmailEditor\Engine\PersonalizationTags\HTML_Tag_Processor($content);
|
||||
while ($content_processor->next_token()) {
|
||||
$type = $content_processor->get_token_type();
|
||||
if ($type === '#comment') {
|
||||
$token = $content_processor->get_modifiable_text();
|
||||
$content_processor->replace_token($token);
|
||||
}
|
||||
}
|
||||
$content_processor->flush_updates();
|
||||
return $content_processor->get_updated_html();
|
||||
}
|
||||
|
||||
public function buildForListing(array $newsletters): array {
|
||||
$statistics = $this->newslettersStatsRepository->getBatchStatistics($newsletters);
|
||||
$latestQueues = $this->getBatchLatestQueuesWithTasks($newsletters);
|
||||
@ -131,6 +152,12 @@ class NewslettersResponseBuilder {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NewsletterEntity $newsletter
|
||||
* @param NewsletterStatistics|null $statistics
|
||||
* @param SendingQueueEntity|null $latestQueue
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function buildListingItem(NewsletterEntity $newsletter, NewsletterStatistics $statistics = null, SendingQueueEntity $latestQueue = null): array {
|
||||
$couponBlockLogs = array_map(function ($item) {
|
||||
return "Coupon block: $item";
|
||||
@ -138,7 +165,7 @@ class NewslettersResponseBuilder {
|
||||
$data = [
|
||||
'id' => (string)$newsletter->getId(), // (string) for BC
|
||||
'hash' => $newsletter->getHash(),
|
||||
'subject' => $newsletter->getSubject(),
|
||||
'subject' => $this->processPersonalizationTags($newsletter->getSubject()),
|
||||
'type' => $newsletter->getType(),
|
||||
'status' => $newsletter->getStatus(),
|
||||
'sent_at' => ($sentAt = $newsletter->getSentAt()) ? $sentAt->format(self::DATE_FORMAT) : null,
|
||||
@ -254,7 +281,7 @@ class NewslettersResponseBuilder {
|
||||
'meta' => $queue->getMeta(),
|
||||
'task_id' => (string)$task->getId(), // (string) for BC
|
||||
'newsletter_id' => ($newsletter = $queue->getNewsletter()) ? (string)$newsletter->getId() : null, // (string) for BC
|
||||
'newsletter_rendered_subject' => $queue->getNewsletterRenderedSubject(),
|
||||
'newsletter_rendered_subject' => $this->processPersonalizationTags($queue->getNewsletterRenderedSubject()),
|
||||
'count_total' => (string)$queue->getCountTotal(), // (string) for BC
|
||||
'count_processed' => (string)$queue->getCountProcessed(), // (string) for BC
|
||||
'count_to_process' => (string)$queue->getCountToProcess(), // (string) for BC
|
||||
|
33
mailpoet/lib/API/JSON/v1/Captcha.php
Normal file
33
mailpoet/lib/API/JSON/v1/Captcha.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\Captcha\CaptchaSession;
|
||||
use MailPoet\Captcha\CaptchaUrlFactory;
|
||||
use MailPoet\Config\AccessControl;
|
||||
|
||||
class Captcha extends APIEndpoint {
|
||||
private CaptchaSession $captchaSession;
|
||||
private CaptchaUrlFactory $urlFactory;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::NO_ACCESS_RESTRICTION,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
CaptchaSession $captchaSession,
|
||||
CaptchaUrlFactory $urlFactory
|
||||
) {
|
||||
$this->captchaSession = $captchaSession;
|
||||
$this->urlFactory = $urlFactory;
|
||||
}
|
||||
|
||||
public function render(array $data = []) {
|
||||
$sessionId = $this->captchaSession->generateSessionId();
|
||||
$data = array_merge($data, ['captcha_session_id' => $sessionId]);
|
||||
$captchaUrl = $this->urlFactory->getCaptchaUrl($data);
|
||||
|
||||
return $this->redirectResponse($captchaUrl);
|
||||
}
|
||||
}
|
16
mailpoet/lib/API/JSON/v1/RedirectResponse.php
Normal file
16
mailpoet/lib/API/JSON/v1/RedirectResponse.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
use MailPoet\API\JSON\Response;
|
||||
|
||||
class RedirectResponse extends Response {
|
||||
|
||||
public function __construct($location) { // phpcs:ignore
|
||||
parent::__construct(self::REDIRECT, [], $location);
|
||||
}
|
||||
|
||||
public function getData() {
|
||||
return [];
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ namespace MailPoet\API\JSON\v1;
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\API\JSON\ResponseBuilders\ScheduledTaskSubscriberResponseBuilder;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Cron\CronHelper;
|
||||
@ -125,9 +126,18 @@ class SendingTaskSubscribers extends APIEndpoint {
|
||||
]);
|
||||
}
|
||||
|
||||
if ($newsletter->canBeSetActive() && $newsletter->getStatus() !== NewsletterEntity::STATUS_ACTIVE) {
|
||||
return $this->errorResponse([
|
||||
// translators: This error occurs when resending a failed email message to a recipient and the associated email definition (e.g., a welcome email, an automation email) is inactive.
|
||||
APIError::BAD_REQUEST => __('Failed to resend! The email is not active. Please activate it first.', 'mailpoet'),
|
||||
], [], Response::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$taskSubscriber->resetToUnprocessed();
|
||||
$taskSubscriber->getTask()->setStatus(null);
|
||||
$newsletter->setStatus(NewsletterEntity::STATUS_SENDING);
|
||||
if (!$newsletter->canBeSetActive()) {
|
||||
$newsletter->setStatus(NewsletterEntity::STATUS_SENDING);
|
||||
}
|
||||
// Each repository flushes all changes
|
||||
$this->scheduledTaskSubscribersRepository->flush();
|
||||
return $this->successResponse([]);
|
||||
|
@ -298,15 +298,17 @@ class Services extends APIEndpoint {
|
||||
}
|
||||
|
||||
public function pingBridge() {
|
||||
try {
|
||||
$bridgePingResponse = $this->bridge->pingBridge();
|
||||
} catch (\Exception $e) {
|
||||
$response = $this->bridge->pingBridge();
|
||||
if ($this->wp->isWpError($response)) {
|
||||
/** @var \WP_Error $response */
|
||||
$errorDesc = $this->getErrorDescriptionByCode(Bridge::CHECK_ERROR_UNKNOWN);
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => $e->getMessage(),
|
||||
APIError::UNKNOWN => "{$errorDesc}: {$response->get_error_message()}",
|
||||
]);
|
||||
}
|
||||
if (!$this->bridge->validateBridgePingResponse($bridgePingResponse)) {
|
||||
$code = $bridgePingResponse ?: Bridge::CHECK_ERROR_UNKNOWN;
|
||||
|
||||
if (!$this->bridge->validateBridgePingResponse($response)) {
|
||||
$code = $this->wp->wpRemoteRetrieveResponseCode($response) ?: Bridge::CHECK_ERROR_UNKNOWN;
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => $this->getErrorDescriptionByCode($code),
|
||||
]);
|
||||
|
@ -72,7 +72,7 @@ class API {
|
||||
},
|
||||
'permission_callback' => function () use ($endpointClass) {
|
||||
$endpoint = $this->endpointContainer->get($endpointClass);
|
||||
return $endpoint->checkPermissions();
|
||||
return $endpoint->checkPermissions(); // nosemgrep: scanner.php.wp.security.rest-route.permission-callback.incorrect-return
|
||||
},
|
||||
'args' => $schema,
|
||||
]);
|
||||
|
@ -134,8 +134,6 @@ class PageRenderer {
|
||||
? $installer->generatePluginDownloadUrl()
|
||||
: null;
|
||||
|
||||
$lastAnnouncementDate = $this->settings->get('last_announcement_date');
|
||||
$lastAnnouncementSeen = $this->userFlags->get('last_announcement_seen');
|
||||
$wpSegment = $this->segmentRepository->getWPUsersSegment();
|
||||
$wpSegmentState = ($wpSegment instanceof SegmentEntity) && $wpSegment->getDeletedAt() === null ?
|
||||
SegmentEntity::SEGMENT_ENABLED : SegmentEntity::SEGMENT_DISABLED;
|
||||
@ -157,8 +155,6 @@ class PageRenderer {
|
||||
'mailpoet_api_key_state' => $this->settings->get('mta.mailpoet_api_key_state'),
|
||||
'mta_method' => $this->settings->get('mta.method'),
|
||||
'premium_key_state' => $this->settings->get('premium.premium_key_state'),
|
||||
'last_announcement_seen' => $lastAnnouncementSeen,
|
||||
'feature_announcement_has_news' => (empty($lastAnnouncementSeen) || $lastAnnouncementSeen < $lastAnnouncementDate),
|
||||
'wp_segment_state' => $wpSegmentState,
|
||||
'tracking_config' => $this->trackingConfig->getConfig(),
|
||||
'is_new_user' => $this->installation->isNewInstallation(),
|
||||
|
@ -302,7 +302,7 @@ class FormEditor {
|
||||
}
|
||||
|
||||
private function getPreviewPageUrl() {
|
||||
$mailpoetPage = Pages::getDefaultMailPoetPage();
|
||||
$mailpoetPage = Pages::getMailPoetPage(Pages::PAGE_SUBSCRIPTIONS);
|
||||
if (!$mailpoetPage) {
|
||||
return null;
|
||||
}
|
||||
|
@ -52,9 +52,10 @@ class Help {
|
||||
* @param array<string, string> $systemInfoData The system info data array.
|
||||
*/
|
||||
$systemInfoData = WPFunctions::get()->applyFilters('mailpoet_system_info_data', $this->systemReportCollector->getData(true));
|
||||
|
||||
try {
|
||||
$cronPingUrl = $this->cronHelper->getCronUrl(CronDaemon::ACTION_PING);
|
||||
$cronPingResponse = $this->cronHelper->pingDaemon();
|
||||
$cronPingResponse = $this->systemReportCollector->getCronPingResponse();
|
||||
} catch (\Exception $e) {
|
||||
$cronPingResponse = __('Can‘t generate cron URL.', 'mailpoet') . ' (' . $e->getMessage() . ')';
|
||||
$cronPingUrl = $cronPingResponse;
|
||||
@ -62,6 +63,7 @@ class Help {
|
||||
|
||||
$mailerLog = MailerLog::getMailerLog();
|
||||
$mailerLog['sent'] = MailerLog::sentSince();
|
||||
$bridgePingResponse = $this->systemReportCollector->getBridgePingResponse();
|
||||
$systemStatusData = [
|
||||
'cron' => [
|
||||
'url' => $cronPingUrl,
|
||||
@ -70,16 +72,18 @@ class Help {
|
||||
],
|
||||
'mss' => [
|
||||
'enabled' => $this->bridge->isMailpoetSendingServiceEnabled(),
|
||||
'isReachable' => $this->bridge->validateBridgePingResponse($this->bridge->pingBridge()),
|
||||
'isReachable' => $this->bridge->validateBridgePingResponse($bridgePingResponse),
|
||||
],
|
||||
'cronStatus' => $this->cronHelper->getDaemon(),
|
||||
'queueStatus' => $mailerLog,
|
||||
];
|
||||
|
||||
$systemStatusData['cronStatus']['accessible'] = $this->cronHelper->isDaemonAccessible();
|
||||
$systemStatusData['queueStatus']['tasksStatusCounts'] = $this->scheduledTasksRepository->getCountsPerStatus();
|
||||
$systemStatusData['queueStatus']['latestTasks'] = array_map(function ($task) {
|
||||
return $this->buildTaskData($task);
|
||||
}, $this->scheduledTasksRepository->getLatestTasks(SendingQueue::TASK_TYPE));
|
||||
|
||||
$scheduledTasks = $this->scheduledTasksRepository->getLatestTasks(SendingQueue::TASK_TYPE);
|
||||
$systemStatusData['queueStatus']['latestTasks'] = array_map(fn($task) => $this->buildTaskData($task), $scheduledTasks);
|
||||
|
||||
$this->pageRenderer->displayPage(
|
||||
'help.html',
|
||||
[
|
||||
@ -132,6 +136,7 @@ class Help {
|
||||
$subscriber = $subscribers->first() ? $subscribers->first()->getSubscriber() : null;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $task->getId(),
|
||||
'type' => $task->getType(),
|
||||
|
@ -6,6 +6,8 @@ use MailPoet\AdminPages\PageRenderer;
|
||||
use MailPoet\AutomaticEmails\AutomaticEmails;
|
||||
use MailPoet\Config\Env;
|
||||
use MailPoet\Config\Menu;
|
||||
use MailPoet\EmailEditor\Engine\Dependency_Check;
|
||||
use MailPoet\EmailEditor\Integrations\MailPoet\DependencyNotice;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\SegmentEntity;
|
||||
use MailPoet\Listing\PageLimit;
|
||||
@ -53,6 +55,10 @@ class Newsletters {
|
||||
|
||||
private WooCommerce $wooCommerceSegment;
|
||||
|
||||
private Dependency_Check $dependencyCheck;
|
||||
|
||||
private DependencyNotice $dependencyNotice;
|
||||
|
||||
private CapabilitiesManager $capabilitiesManager;
|
||||
|
||||
public function __construct(
|
||||
@ -70,6 +76,8 @@ class Newsletters {
|
||||
AuthorizedEmailsController $authorizedEmailsController,
|
||||
UserFlagsController $userFlagsController,
|
||||
WooCommerce $wooCommerceSegment,
|
||||
Dependency_Check $dependencyCheck,
|
||||
DependencyNotice $dependencyNotice,
|
||||
CapabilitiesManager $capabilitiesManager
|
||||
) {
|
||||
$this->pageRenderer = $pageRenderer;
|
||||
@ -86,6 +94,8 @@ class Newsletters {
|
||||
$this->authorizedEmailsController = $authorizedEmailsController;
|
||||
$this->userFlagsController = $userFlagsController;
|
||||
$this->wooCommerceSegment = $wooCommerceSegment;
|
||||
$this->dependencyCheck = $dependencyCheck;
|
||||
$this->dependencyNotice = $dependencyNotice;
|
||||
$this->capabilitiesManager = $capabilitiesManager;
|
||||
}
|
||||
|
||||
@ -160,6 +170,8 @@ class Newsletters {
|
||||
|
||||
$data['legacy_automatic_emails_notice_dismissed'] = (bool)$this->userFlagsController->get('legacy_automatic_emails_notice_dismissed');
|
||||
|
||||
$data['block_email_editor_enabled'] = $this->dependencyCheck->are_dependencies_met(); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
$this->dependencyNotice->displayMessageIfNeeded();
|
||||
$this->pageRenderer->displayPage('newsletters.html', $data);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ namespace MailPoet\AdminPages\Pages;
|
||||
|
||||
use MailPoet\AdminPages\AssetsController;
|
||||
use MailPoet\AdminPages\PageRenderer;
|
||||
use MailPoet\Captcha\CaptchaRenderer;
|
||||
use MailPoet\Config\Installer;
|
||||
use MailPoet\Config\ServicesChecker;
|
||||
use MailPoet\Segments\SegmentsSimpleListRepository;
|
||||
@ -13,7 +14,6 @@ use MailPoet\Services\Bridge;
|
||||
use MailPoet\Settings\Hosts;
|
||||
use MailPoet\Settings\Pages;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Subscription\Captcha\CaptchaRenderer;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoet\WP\Notice as WPNotice;
|
||||
|
||||
|
@ -5,6 +5,7 @@ namespace MailPoet\Analytics;
|
||||
use MailPoet\Automation\Engine\Data\Automation;
|
||||
use MailPoet\Automation\Engine\Data\Step;
|
||||
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||
use MailPoet\Captcha\CaptchaConstants;
|
||||
use MailPoet\Config\ServicesChecker;
|
||||
use MailPoet\Cron\CronTrigger;
|
||||
use MailPoet\Entities\DynamicSegmentFilterData;
|
||||
@ -51,7 +52,6 @@ use MailPoet\Settings\TrackingConfig;
|
||||
use MailPoet\Subscribers\ConfirmationEmailCustomizer;
|
||||
use MailPoet\Subscribers\NewSubscriberNotificationMailer;
|
||||
use MailPoet\Subscribers\SubscriberListingRepository;
|
||||
use MailPoet\Subscription\Captcha\CaptchaConstants;
|
||||
use MailPoet\Tags\TagRepository;
|
||||
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
|
||||
use MailPoet\WooCommerce\Helper as WooCommerceHelper;
|
||||
@ -161,11 +161,10 @@ class Reporter {
|
||||
'WP_MAX_MEMORY_LIMIT' => WP_MAX_MEMORY_LIMIT,
|
||||
'PHP memory_limit' => ini_get('memory_limit'),
|
||||
'PHP max_execution_time' => ini_get('max_execution_time'),
|
||||
'users_can_register' => $this->wp->getOption('users_can_register') ? 'yes' : 'no',
|
||||
'MailPoet Free version' => MAILPOET_VERSION,
|
||||
'MailPoet Premium version' => (defined('MAILPOET_PREMIUM_VERSION')) ? MAILPOET_PREMIUM_VERSION : 'N/A',
|
||||
'Total number of subscribers' => $this->subscribersFeature->getSubscribersCount(),
|
||||
'Sending Method' => isset($mta['method']) ? $mta['method'] : null,
|
||||
'Sending Method' => $mta['method'] ?? null,
|
||||
"Send all site's emails with" => $this->settings->get('send_transactional_emails') ? 'current sending method' : 'default WordPress sending method',
|
||||
'Date of plugin installation' => $this->settings->get('installed_at'),
|
||||
'Subscribe in comments' => (boolean)$this->settings->get('subscribe.on_comment.enabled', false),
|
||||
@ -183,11 +182,17 @@ class Reporter {
|
||||
'Number of active post notifications' => $newsletters['notifications_count'],
|
||||
'Number of active welcome emails' => $newsletters['welcome_newsletters_count'],
|
||||
'Total number of standard newsletters sent' => $newsletters['sent_newsletters_count'],
|
||||
'Total number of block editor gutenberg newsletters' => $newsletters['total_gutenberg_newsletter_count'],
|
||||
'Number of block editor gutenberg newsletters sent' => $newsletters['sent_gutenberg_newsletter_count'],
|
||||
'Number of segments' => isset($segments['dynamic']) ? (int)$segments['dynamic'] : 0,
|
||||
'Number of lists' => isset($segments['default']) ? (int)$segments['default'] : 0,
|
||||
'Number of subscriber tags' => $this->tagRepository->countBy([]),
|
||||
'Site is using block theme' => $this->wp->wpIsBlockTheme(),
|
||||
'Stop sending to inactive subscribers' => $inactiveSubscribersStatus,
|
||||
'CAPTCHA setting' => $this->settings->get(CaptchaConstants::TYPE_SETTING_NAME, '') ?: 'disabled',
|
||||
'Is CAPTCHA on register forms enabled' => $this->settings->get(CaptchaConstants::ON_REGISTER_FORMS_SETTING_NAME, false) ? 'yes' : 'no',
|
||||
'users_can_register' => $this->wp->getOption('users_can_register') ? 'yes' : 'no',
|
||||
'Is WooCommerce account creation on "My account" enabled' => $this->wp->getOption('woocommerce_enable_myaccount_registration') ?? 'no',
|
||||
'Plugin > MailPoet Premium' => $this->wp->isPluginActive('mailpoet-premium/mailpoet-premium.php'),
|
||||
'Plugin > bounce add-on' => $this->wp->isPluginActive('mailpoet-bounce-handler/mailpoet-bounce-handler.php'),
|
||||
'Plugin > Bloom' => $this->wp->isPluginActive('bloom-for-publishers/bloom.php'),
|
||||
@ -412,11 +417,15 @@ class Reporter {
|
||||
}
|
||||
|
||||
public function getTrackingData() {
|
||||
global $wp_version, $woocommerce; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
$newsletters = $this->newslettersRepository->getAnalytics();
|
||||
$segments = $this->segmentsRepository->getCountsPerType();
|
||||
$mta = $this->settings->get('mta', []);
|
||||
$installedAt = new Carbon($this->settings->get('installed_at'));
|
||||
return [
|
||||
$theme = $this->wp->wpGetTheme();
|
||||
$result = [
|
||||
'WordPressVersion' => $wp_version, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
'pluginGutenberg' => $this->wp->isPluginActive('gutenberg/gutenberg.php'),
|
||||
'installedAtIso' => $installedAt->format(Carbon::ISO8601),
|
||||
'newslettersSent' => $newsletters['sent_newsletters_count'],
|
||||
'welcomeEmails' => $newsletters['welcome_newsletters_count'],
|
||||
@ -424,9 +433,19 @@ class Reporter {
|
||||
'woocommerceEmails' => $newsletters['automatic_emails_count'],
|
||||
'subscribers' => $this->subscribersFeature->getSubscribersCount(),
|
||||
'lists' => isset($segments['default']) ? (int)$segments['default'] : 0,
|
||||
'sendingMethod' => isset($mta['method']) ? $mta['method'] : null,
|
||||
'sendingMethod' => $mta['method'] ?? null,
|
||||
'woocommerceIsInstalled' => $this->woocommerceHelper->isWooCommerceActive(),
|
||||
'blockTheme' => $this->wp->wpIsBlockTheme(),
|
||||
'theme' => $theme->Name, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
'themeVersion' => $theme->Version, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
];
|
||||
if (defined('GUTENBERG_VERSION')) {
|
||||
$result['gutenbergVersion'] = GUTENBERG_VERSION;
|
||||
}
|
||||
if ($this->woocommerceHelper->isWooCommerceActive()) {
|
||||
$result['wooCommerceVersion'] = $woocommerce->version;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function isFilterTypeActive(string $filterType, string $action): bool {
|
||||
|
@ -148,7 +148,7 @@ class StepHandler {
|
||||
}, $subjectEntries));
|
||||
|
||||
$step->validate($validationArgs);
|
||||
$step->run($args, $this->stepRunControllerFactory->createController($args));
|
||||
$step->run($args, $this->stepRunControllerFactory->createController($args, $logger));
|
||||
|
||||
// check if run is not completed by now (e.g., one of if/else branches is empty)
|
||||
$automationRun = $this->automationRunStorage->getAutomationRun($runId);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user