Compare commits
1466 Commits
3.0.0-rc.2
...
3.8.5
Author | SHA1 | Date | |
---|---|---|---|
081f362799 | |||
23c54824a6 | |||
1a9e6b8dcb | |||
33aa7180f3 | |||
1516153846 | |||
e73b12d6f0 | |||
084be79210 | |||
e2b40fee37 | |||
d8167692ee | |||
43d536e5c3 | |||
7d47e97b51 | |||
e55df2ca2d | |||
406c8bb6bd | |||
a8c7615013 | |||
9369e7883c | |||
51c89d5aa1 | |||
659f748dc9 | |||
2038643c2b | |||
c7aeca116f | |||
5748dfe759 | |||
cc294dd362 | |||
4c79a400c5 | |||
348c740a35 | |||
b6a367f770 | |||
913dcb30c5 | |||
e9c79a1952 | |||
5d8522c83a | |||
e413f83dfa | |||
f202e9d21e | |||
e00c322040 | |||
7186f4b13a | |||
b1feb0c21a | |||
f7469b4307 | |||
a71e6cb7d9 | |||
734e5577e3 | |||
8cdb71ed91 | |||
cb45bf8d51 | |||
a242c06990 | |||
cccf40e65f | |||
c5ee742b01 | |||
364facabd5 | |||
5204bbc4af | |||
4a84974ceb | |||
af0d18a734 | |||
e6269308d0 | |||
8abf14b0c3 | |||
3821a9719a | |||
c1edee7f14 | |||
80b370eed5 | |||
01aac4d602 | |||
a3b79a6907 | |||
38d078df5e | |||
97c5c25628 | |||
adbce928f8 | |||
92d0dfc0e1 | |||
d7f12b5973 | |||
490ea67d18 | |||
1252f35a23 | |||
0d6e2a107a | |||
a8bfadf157 | |||
5f6fe1a7e5 | |||
3184b43b3b | |||
a9c081ea5e | |||
65e0361cd0 | |||
9ed409194c | |||
165bf9c485 | |||
76591a6552 | |||
fb4c2f7082 | |||
abd92dc287 | |||
976317163a | |||
032fd6575d | |||
65e49808c0 | |||
f991a78722 | |||
aff2a1feec | |||
82f176dc43 | |||
d165732f66 | |||
eac68e4032 | |||
d165c44537 | |||
27b9857e6a | |||
45b22f62d7 | |||
c3e2b17aa1 | |||
d8581f761f | |||
a79db03cf7 | |||
b0158ad5a6 | |||
80898a1bde | |||
2e6a2233f2 | |||
3a2ee709eb | |||
7b4e63c8c5 | |||
43a0d92d2f | |||
78e5c1866e | |||
1d2fa78a4d | |||
506c78d068 | |||
0d461c2e30 | |||
2851fa3a4a | |||
1e933df60b | |||
f261d9881e | |||
e5d30d1ad0 | |||
76686e08fe | |||
e8b0e77d0e | |||
2dc0557973 | |||
b48c9bb84a | |||
f7f8b6e17d | |||
8088f95575 | |||
43813758eb | |||
c868c39696 | |||
33149d041c | |||
9b19858734 | |||
d7bba0c678 | |||
1f8e8424e8 | |||
9dc0e61603 | |||
5818db1a23 | |||
f314d5d706 | |||
427e826ee5 | |||
d0f797f88c | |||
42f91f5069 | |||
3318dd3ace | |||
c368b664a1 | |||
d8b9b8e649 | |||
2d30a0884b | |||
8ee72bd8ed | |||
5c66025633 | |||
d87acdd783 | |||
97cb9dd912 | |||
4296cd5a3e | |||
184f38f186 | |||
2f83a74b95 | |||
8f05d88567 | |||
e523fa055e | |||
aef52a18c0 | |||
411fe307d1 | |||
9bb6c2131a | |||
8d8d5d234d | |||
37baf3edae | |||
6505871eac | |||
b12fcce017 | |||
b758ec0797 | |||
96f36d0d1c | |||
eada699607 | |||
38f8bec4c5 | |||
4229ba87f3 | |||
255422e908 | |||
928be9d698 | |||
f250807be5 | |||
03817657a0 | |||
3c7cd193b6 | |||
083372a391 | |||
42b353fa59 | |||
eeffaf9c88 | |||
9c838f02a6 | |||
0a5c3f021f | |||
82baa6cdbe | |||
995d3db5ae | |||
2f69a705d8 | |||
4465a36405 | |||
21916c6863 | |||
1baace589e | |||
b44fe6d522 | |||
90bdcd2213 | |||
d670fe27ee | |||
b0e6283bf6 | |||
380fb91d8d | |||
e8e8eaf74c | |||
35825c3432 | |||
4ce6b399f4 | |||
2c0e187089 | |||
1f6e5e1717 | |||
0cf0c3f71f | |||
92dfa8c593 | |||
a02f909627 | |||
bd08a529db | |||
aab68bc0eb | |||
edea5ecece | |||
dff9154e7d | |||
a8f75fb927 | |||
57abf91a06 | |||
4bb06a949a | |||
7962a005b1 | |||
e4c1511653 | |||
00df3ab248 | |||
01af61a93f | |||
ab696a133b | |||
0b973274af | |||
ce24845896 | |||
ce17adf693 | |||
732fdaf78c | |||
f359ba1499 | |||
289daf2edb | |||
844687411e | |||
bd33fc2aba | |||
9b9f579e4c | |||
37e7f0abc0 | |||
35ea0d9c34 | |||
a1974c92a7 | |||
3dba3d3887 | |||
acf74f65d1 | |||
55affcb556 | |||
a27ee4c83b | |||
4fba615d30 | |||
71e2cc16c3 | |||
d9c9620a67 | |||
6b78762bcf | |||
ad0e04a9c2 | |||
4992b65f14 | |||
84bde18fce | |||
8fc12dbcc4 | |||
fe9038d5a0 | |||
6a52d3130d | |||
feb14d22b4 | |||
cfa49d8e90 | |||
f2baf1c996 | |||
76876aebee | |||
8626dff51c | |||
699e08b1e7 | |||
3b9ac7b479 | |||
5d7a2e54c8 | |||
43c010ecd0 | |||
4bd68ebc5f | |||
4dd9244194 | |||
9308cffd45 | |||
9446e3cd50 | |||
b387eedf7c | |||
36e29581ef | |||
f17eb37525 | |||
3e84a4b4e4 | |||
ee63ecaabb | |||
989cda7f0e | |||
50a807c30f | |||
8537796da9 | |||
6b028a43ad | |||
daeee55118 | |||
f1462b5f3e | |||
2774b6c8dc | |||
874fbc704c | |||
8b067c31e5 | |||
7ab5717cc8 | |||
f4f1b7b8b2 | |||
19655c7a21 | |||
3cc943fcfe | |||
9639400134 | |||
3a2c7b2f94 | |||
86f5b99f54 | |||
2e2d6c05ac | |||
1e37265feb | |||
5a94e0d780 | |||
b757218455 | |||
31f607c4ae | |||
6c478cd017 | |||
d358621366 | |||
bb579370a8 | |||
75d8ab82e8 | |||
78e954df0e | |||
00e18831ab | |||
cc2a39ac26 | |||
b135c342f1 | |||
36cb7249cc | |||
8ed6c46ef9 | |||
5609384af0 | |||
09e82293ff | |||
8fce649d37 | |||
c2b98d93c3 | |||
7640f4c912 | |||
2ee77e0034 | |||
66a0ddb321 | |||
9dc98ffa9e | |||
65904137c1 | |||
c37c459597 | |||
fd6a283f24 | |||
89b7813324 | |||
e4947e1b77 | |||
1005990588 | |||
0f8b35ba0e | |||
2916e4362e | |||
8393bac51b | |||
0ea6afb38a | |||
0c2085864e | |||
a0818b3b9f | |||
729d950e4e | |||
9a11d4cc82 | |||
683e9c9fa1 | |||
2ad9ebf1d3 | |||
0545e48f8e | |||
7e843b5fdf | |||
33de2d6a5d | |||
3290df3bb8 | |||
71fed489ec | |||
8c55fce938 | |||
ebc9c4db39 | |||
dde03e0ae6 | |||
581e1303d0 | |||
04d676f9e3 | |||
fff6c966d8 | |||
20dbc3c74b | |||
ca1be42ab8 | |||
77d2f0224b | |||
6d74ba8bb0 | |||
8f758b11e1 | |||
4611a7ea74 | |||
9aa796681a | |||
b3aae566d5 | |||
bf0c5abd4f | |||
e578b0a4ea | |||
08fa5b88b5 | |||
b75d4db412 | |||
116ee51497 | |||
b16cbf9e7d | |||
32e02cbf52 | |||
9b9eaacafe | |||
b7db18425a | |||
7cc165b3d1 | |||
658d989a60 | |||
c841e5dc60 | |||
feb6e97eca | |||
7091825893 | |||
313a5f8a4e | |||
b00b42e4ca | |||
e3a63b54cc | |||
0af3be61dd | |||
fdc5aad62a | |||
efe4668f8c | |||
b36537a4e8 | |||
62fd56400b | |||
9521134376 | |||
ccf027b336 | |||
5977cb9d63 | |||
962baf71a8 | |||
0f08c5fa37 | |||
20cecc885a | |||
87dbb7ff96 | |||
4070a56547 | |||
df2cd9e6e3 | |||
d6f72087e9 | |||
506f2d0fbf | |||
a41c327996 | |||
0f46ecc433 | |||
03b36e6b86 | |||
2c290c6e4c | |||
b9ce9ecf28 | |||
ac47866295 | |||
705241d330 | |||
c88c8a9c96 | |||
b8ce6ff88c | |||
b855e45817 | |||
e7a994e09c | |||
3589899d6b | |||
e4b15fb9b9 | |||
59d632895e | |||
67b66856f5 | |||
bd76fa889a | |||
8793d904fb | |||
6914a9c224 | |||
24b63d324b | |||
76ac3b49d6 | |||
2573e2f785 | |||
3872ffbbc4 | |||
b7e33aa997 | |||
99dcd6fa0b | |||
f285cfcdc3 | |||
88be42fea1 | |||
32a5a4ef52 | |||
7b665acae3 | |||
2890b66cfd | |||
913e070b32 | |||
0fed3f8355 | |||
2215b53141 | |||
3e84a1f8e1 | |||
74250b87f9 | |||
ec34a9efca | |||
5b077cfb54 | |||
cdac9afc64 | |||
0a298f5fad | |||
4dd17e47ad | |||
d7ab03b1c7 | |||
2e6be59d9d | |||
e5a1af96cb | |||
3a0a241a52 | |||
bcbe89e558 | |||
75bad97b10 | |||
59f9fdb3b6 | |||
89a8f9ded4 | |||
62b5502eb1 | |||
9b7773e0a4 | |||
498c014464 | |||
b3a23bdddc | |||
587eaffc3c | |||
2da6c2ec49 | |||
8a1fe0df6b | |||
d56f2c59ef | |||
ad48aff5c1 | |||
fa7387c9e4 | |||
fbd11cf848 | |||
e469a32f9e | |||
2104451ec3 | |||
9b9abf5cde | |||
dbdf432c1d | |||
8a877ed637 | |||
4da7ed44e5 | |||
952c0f5aa7 | |||
5223687326 | |||
ee07f3e734 | |||
0c585d15e1 | |||
1d362f9cf6 | |||
ff9f9a0a90 | |||
941b88495a | |||
859b87c7c9 | |||
d44c275790 | |||
f942e98645 | |||
3e9d7fc68d | |||
1c6c275295 | |||
1d8e8e8786 | |||
6ad6190da3 | |||
3c499d0626 | |||
d4e3334f9e | |||
603062f23e | |||
e55aab43b5 | |||
a4fdd32c48 | |||
3940381e30 | |||
582f241ea4 | |||
11b29a1efa | |||
f5d59eb00e | |||
1af5802030 | |||
2627ea865a | |||
96b02cccc4 | |||
7d912e941f | |||
572cc1671b | |||
709de7dd54 | |||
24ce88c4d5 | |||
a664d3631b | |||
5912004c10 | |||
f5c56bf492 | |||
d5b9664bde | |||
bd01b5855e | |||
a742dacf32 | |||
e62444d34e | |||
d2f98bc2e5 | |||
ee99195b30 | |||
2f2a651fd2 | |||
28651227d5 | |||
b57c8b7533 | |||
ee4aefb6e3 | |||
8a8b99ad54 | |||
d0b1579731 | |||
7c9943c463 | |||
68b091848c | |||
22d3d00841 | |||
d68dffafd3 | |||
df58322f0e | |||
ea47524cc4 | |||
56f7b4d523 | |||
1d1aa52fa8 | |||
39ae92e580 | |||
2dfb264ca6 | |||
ee2631dbf2 | |||
11067327a1 | |||
8e13eb50bf | |||
7bb2d86f93 | |||
2b351e4f34 | |||
1f30f6997e | |||
a588e95762 | |||
f8e443ed3c | |||
73f51505a2 | |||
576796db22 | |||
72aaaaa94d | |||
0470f773d5 | |||
e71355f73f | |||
aa11af3cf8 | |||
535818fec4 | |||
eba06118a4 | |||
32beb0dcd1 | |||
a70405e76d | |||
a78c9e2f99 | |||
ca8390ed43 | |||
f64cf728fe | |||
ed8c75dd06 | |||
a640e52eaf | |||
3b62367b18 | |||
32a759eca5 | |||
c105df1635 | |||
76aa5678a8 | |||
1855a3a481 | |||
2c96f1902e | |||
f3a5c6e406 | |||
26d4be24c2 | |||
1b98604c90 | |||
2b1eafc16f | |||
1635c3aac3 | |||
861d4ea9e1 | |||
8890dac207 | |||
940c1a66aa | |||
5300e338dc | |||
a73dda6690 | |||
adff7f6f74 | |||
c41fd4cb3b | |||
57de46cb29 | |||
6322932e25 | |||
93143695c4 | |||
a37cb31141 | |||
1dbd002df9 | |||
6599914263 | |||
c974e80dc5 | |||
74211658e0 | |||
72187fefdd | |||
4165ad6959 | |||
0ce72c05f3 | |||
afd801694d | |||
c20ff20f5c | |||
d638273f47 | |||
b053bb395c | |||
788362f5b8 | |||
c8782db74b | |||
3b48bc7517 | |||
bcb1f7e289 | |||
2d7555e79d | |||
bd5be51b0e | |||
ae99a06717 | |||
80c2f8970a | |||
b073f95008 | |||
b2473c9b65 | |||
e92ccdf5b1 | |||
fc5b797738 | |||
867e62d0e8 | |||
566db67e8c | |||
a535e59450 | |||
5ac75591a9 | |||
b993c47181 | |||
d195cd9798 | |||
5fb57b13e4 | |||
9fce8aa126 | |||
6892dbea91 | |||
154e88673c | |||
086ef7429b | |||
2dd15438d8 | |||
937aaae2c4 | |||
37a046a8e9 | |||
6c64ba90c7 | |||
e48a288fae | |||
abe231215b | |||
7d43a41ed9 | |||
a8443a3813 | |||
40526dd083 | |||
cb648da677 | |||
ea0676718e | |||
f5cc1cfe3a | |||
1cc28eb607 | |||
f019eaf38e | |||
ce45c8c553 | |||
9b4e49123d | |||
b2685f7591 | |||
7a3749e043 | |||
dbbb3862bc | |||
a1009eff0d | |||
3096c9a0ab | |||
e93fc41281 | |||
adbebb39f1 | |||
4955673885 | |||
972e470d28 | |||
f1effc30ba | |||
8728a69e69 | |||
5383d16c22 | |||
26f2630e1a | |||
98ac4d5508 | |||
e4f417f595 | |||
bfaec09939 | |||
585f9bcc92 | |||
e7d7674e0e | |||
ba38c55e1f | |||
3e432d7b5d | |||
afefa89466 | |||
cb80b5ca69 | |||
8f6ae91924 | |||
2bba10127d | |||
586f6943e1 | |||
10ee48076a | |||
ee04044676 | |||
63022ccad7 | |||
51d97ddda8 | |||
e8f55493d0 | |||
baab0840b7 | |||
31c3907c8f | |||
75a1c5f95b | |||
66b5aeb578 | |||
18c90fed07 | |||
06dac13327 | |||
7c47b81569 | |||
c25d4d4943 | |||
d6749a591f | |||
251198cf9c | |||
ffb6243614 | |||
0d11416f4e | |||
662b033677 | |||
0d514a8084 | |||
1756df0085 | |||
986cce4a92 | |||
a0947611fb | |||
18a6a1c439 | |||
eb4d2ab829 | |||
cdc6d8a966 | |||
139354f3e6 | |||
37f70b900d | |||
511e07455f | |||
d28b4fda58 | |||
09081631a4 | |||
618723b2bf | |||
5cb39b9bf9 | |||
467cf350e9 | |||
d317eb4fbd | |||
44b96aa9a7 | |||
3b4edd5c95 | |||
a5782a32bc | |||
a3cabad752 | |||
16b2b73727 | |||
c1f5bf9836 | |||
5e49624685 | |||
985e3a9a68 | |||
58f92045c0 | |||
82ec8d7ef9 | |||
1af242c2dd | |||
8f9299afe3 | |||
7775b5ace3 | |||
78f9fea2b0 | |||
783f33b092 | |||
d3250030ba | |||
9e685efd41 | |||
756367dd50 | |||
b5a322a8e1 | |||
3f219379d5 | |||
4037800c10 | |||
7144d4afa8 | |||
658cdd37ac | |||
05dcaeba76 | |||
fbfcfcc85b | |||
8582c19ac0 | |||
5e35a724f0 | |||
c7e83377ba | |||
f6374d0602 | |||
1df25aef76 | |||
3a8b64e11c | |||
1d9b4839f7 | |||
1c5a0bb5bd | |||
eca7de854d | |||
0adaf22eef | |||
c39aeb6283 | |||
7528aba2ef | |||
c3fff8ddbe | |||
ca9e11a231 | |||
4177701044 | |||
65a201c848 | |||
1c4b6a9ab6 | |||
9173b114a7 | |||
cd6828d19e | |||
1c021ac0a0 | |||
4469cef647 | |||
057f0376a5 | |||
7e6b29896f | |||
4c4f6be59b | |||
669d072853 | |||
1033c1c1cf | |||
330067c3f2 | |||
9a942a0bd9 | |||
b9c31698d5 | |||
d079002b09 | |||
cca4fc02c0 | |||
77248e6890 | |||
755179e0d1 | |||
46493b991c | |||
2f05eaf528 | |||
f7397d04ed | |||
3d43970fdc | |||
92864c2d4f | |||
41e028c0d4 | |||
cfbf29b60a | |||
a5137c8d62 | |||
aceb9bb031 | |||
8bc0ad48c0 | |||
449568b37b | |||
c164066522 | |||
5c10a66444 | |||
afed408297 | |||
6e7c7ae8c8 | |||
d3f9fb5f06 | |||
53b4a7ba31 | |||
39212a30e8 | |||
7c7e5bbaa6 | |||
831804d35e | |||
9af98f0afb | |||
5b96789e04 | |||
3b2355d570 | |||
fa44127efa | |||
86e01ce5de | |||
981e4a56e3 | |||
d89eb07b3c | |||
e4b79616ee | |||
21e45a39b4 | |||
b7e492e20e | |||
c2c74d7524 | |||
4d80bdb322 | |||
a58a3c9574 | |||
734d1ded1f | |||
e0f989f6a8 | |||
2265de454e | |||
381fc76d52 | |||
32fdc11c88 | |||
99edc35b08 | |||
66fa22d489 | |||
f1f69c9835 | |||
a6f8953bd3 | |||
8b25acf82c | |||
a9b40802a9 | |||
9eda024b02 | |||
003ddc3b92 | |||
b381923903 | |||
8837c9e14c | |||
8cd83575f2 | |||
03015c0bde | |||
6b36156db5 | |||
4ed39c9620 | |||
f210b15652 | |||
ef5d777fac | |||
ab2b380f2d | |||
c3f974a3c0 | |||
2c020bf283 | |||
17e13c0956 | |||
52a6c4f701 | |||
f09ad47911 | |||
0da2be21c8 | |||
8603e2a96a | |||
ddbabcfcb5 | |||
92f733d30d | |||
93a67e6882 | |||
86aff26d5f | |||
cfc425dfd7 | |||
57b2c577c6 | |||
be4224411f | |||
b2fa79fcbf | |||
1d3102e54b | |||
847916eb1f | |||
adb579a26c | |||
16607dd4be | |||
485c6008c1 | |||
8f9a0239b1 | |||
989baf2aa4 | |||
de91c90ac2 | |||
e10901d939 | |||
426a72c557 | |||
704e4e40f7 | |||
0ff9c006d8 | |||
f7fcec7953 | |||
e484705ac1 | |||
6a8337c67d | |||
f98e58eb99 | |||
476af99130 | |||
0d69b05ac0 | |||
e0052bbb93 | |||
b5b0a02ea4 | |||
3c43c04586 | |||
e87134eca4 | |||
c8f334d782 | |||
7cf63965a0 | |||
eb3cf66958 | |||
ba306ecf66 | |||
a58151fee6 | |||
9eb20f96b2 | |||
57f00bbe0c | |||
287c89749d | |||
b9546b61a8 | |||
606177baaf | |||
b7704c90e8 | |||
9d4f996a6a | |||
774e8d94b5 | |||
4ffadc990e | |||
8ab2cd75c0 | |||
d59ebbbc57 | |||
84638b2818 | |||
a8305adf8d | |||
243ed9d61b | |||
84dfa88a1a | |||
f9204f289f | |||
408b64dc0d | |||
83b24f053e | |||
9acef55352 | |||
4c0f5bb456 | |||
be6647d763 | |||
99732ac14d | |||
0b17a1099b | |||
c0c57f6b67 | |||
bf8b0c81df | |||
d82e3a25ac | |||
98ed295896 | |||
b076e03a75 | |||
a754cfaa52 | |||
3721694f4d | |||
91622f8cd9 | |||
4a3ba73406 | |||
ab3a2f064f | |||
a2270d6689 | |||
e09acbe649 | |||
b54f002635 | |||
aaaf49d071 | |||
8ff28fd974 | |||
126b37f2bb | |||
bebd835790 | |||
9526a6b470 | |||
7eb8ff756b | |||
9159e8d21d | |||
f4ba45490d | |||
9f9b44fd7e | |||
ae9f373cff | |||
80a3bbd030 | |||
aba75849e1 | |||
874a37303a | |||
f467479438 | |||
bb76a8c6aa | |||
63000c48c4 | |||
b88dec06d9 | |||
a0667adace | |||
fef8017134 | |||
87de314c18 | |||
c8a20ec4ae | |||
9739159a6f | |||
0aa4c37ad3 | |||
2c8c8b3d0e | |||
12434b412f | |||
789c2a7d4e | |||
907356bb0f | |||
8800799234 | |||
9c2aa569ea | |||
9ee6920ebb | |||
5d8d9567c7 | |||
5bddc79576 | |||
e2f23df829 | |||
43fbbca050 | |||
5109bc3922 | |||
65b4aeffc8 | |||
17a25120d4 | |||
cb055a2b06 | |||
7361613bba | |||
a8f33d9a7e | |||
c497a72aab | |||
d0f55a1d1b | |||
3b92caf677 | |||
d3c64810a8 | |||
f85a169248 | |||
2dd08c6820 | |||
d7609f48d4 | |||
1d02c688a2 | |||
37c58c6aa4 | |||
4abd034880 | |||
e8c7584b9b | |||
be3eb0a7ff | |||
eee01ee3f1 | |||
56ad9a8976 | |||
20d0ed1aac | |||
f42b61a49e | |||
5e152e920e | |||
0d26b62416 | |||
8e60b2b317 | |||
cae46d9acd | |||
bfad5509c1 | |||
3ae7b436de | |||
30b2eda29f | |||
a519eff231 | |||
fe2ab08a49 | |||
9b914a471b | |||
74f008517b | |||
ba45ee7e35 | |||
a5543d9d78 | |||
3c2ef4b8ee | |||
07417391be | |||
e00d43e506 | |||
c4f285afca | |||
c70097085e | |||
ca6bde8a64 | |||
a1ba783264 | |||
e77d00b179 | |||
b683ae0bc1 | |||
f658fd7776 | |||
1a08155f54 | |||
a3b3e1f8df | |||
87aca7c667 | |||
a7a8cd2be4 | |||
819d4dc17a | |||
6e94db24a2 | |||
5bf532a750 | |||
c63b7d9b91 | |||
a8052c118a | |||
1ad0dce425 | |||
2228f60e2a | |||
99a007fb70 | |||
5e5caab4da | |||
caa0623112 | |||
a05f9bf97b | |||
7202d9dca1 | |||
655dac458a | |||
0e487d2c54 | |||
d2d1657cb2 | |||
3bf800b51d | |||
9910072e72 | |||
8695d147e8 | |||
042d47b027 | |||
a27823ebc5 | |||
1aee4489da | |||
e4a37c3c2d | |||
ea2a91d15e | |||
72193fefae | |||
7e60155bdc | |||
7eae7dde11 | |||
d70c6ce453 | |||
3337637a8b | |||
69ca597f24 | |||
6f20a402f6 | |||
19c7efc9ef | |||
6c25fab6d6 | |||
a45a7a7616 | |||
3053e21910 | |||
699017532e | |||
7465398d17 | |||
ef410fb877 | |||
f154687c53 | |||
b773263fa4 | |||
9870f62984 | |||
1f6430c278 | |||
4018ca4a65 | |||
1342389602 | |||
368f7004c0 | |||
d33cc9d2b8 | |||
e9be62c2d3 | |||
f2733624e4 | |||
46121d74a8 | |||
cb430673f8 | |||
8852f6a3f4 | |||
56e55c072a | |||
77e60d708a | |||
27fbc25bf9 | |||
a91929682f | |||
d45c510ab3 | |||
57366e972a | |||
cbd1ae929e | |||
884f476598 | |||
55296d52ec | |||
8fb4d2e78f | |||
840473aae5 | |||
5d5b61b76f | |||
0e2a67c203 | |||
aaed5add52 | |||
f616ef4d6c | |||
083a6fd3a3 | |||
9a1ec60750 | |||
bd67f5fd36 | |||
ed2a2a6fa8 | |||
6a28fdcac2 | |||
fda1828637 | |||
ebcc094b4d | |||
51cde55217 | |||
23dff7403d | |||
de164f918c | |||
84840c5261 | |||
16b9e0d467 | |||
3c75e7b0bc | |||
c41d5894d0 | |||
1f76f0d98f | |||
e859c090c8 | |||
42528ab309 | |||
d761867a7d | |||
6996615999 | |||
3450a6fc97 | |||
554dbb8dda | |||
d13aa67a07 | |||
1f9c956637 | |||
59d09866a1 | |||
f8ade27a6c | |||
313bcddba1 | |||
fd64702eaa | |||
e2884ac625 | |||
9ce9cb929f | |||
52d33d7fe8 | |||
2ee33b2fc2 | |||
b107be3241 | |||
76148d0a85 | |||
61a7f99e42 | |||
62e3ca8a80 | |||
d431efb288 | |||
adf6485b7d | |||
170fd7f051 | |||
e7ddfc3d29 | |||
b08b53e9c0 | |||
570529a8d8 | |||
5505ffc783 | |||
6e5fc6ad6b | |||
e731b261ab | |||
955f24e71a | |||
89ebc6051a | |||
79fff7b65d | |||
7864e08900 | |||
878cdf46e6 | |||
131d341744 | |||
513062b15d | |||
4926267e36 | |||
aa4d78b1c9 | |||
8d7289e8ba | |||
eb768ff1ee | |||
8afe7f5d97 | |||
97fb5cf66f | |||
8ea4a219e2 | |||
0e08e58288 | |||
dc52ba0d93 | |||
dc569672a9 | |||
159e946093 | |||
3d2a433df1 | |||
fe0476e1c0 | |||
ea552508b4 | |||
dee2ff810c | |||
63ed835d64 | |||
fbf58f23fc | |||
b6f62bd9bc | |||
88ef454844 | |||
3572df6c0c | |||
714e6e013f | |||
91568cbeb6 | |||
393c89b21f | |||
0b491b7943 | |||
e5d7f66561 | |||
b7a7f40cde | |||
a267c7524c | |||
20b59d11a7 | |||
94c7e2a5c0 | |||
d603d99a04 | |||
e7ffe4d694 | |||
018d7bce77 | |||
64c40d5a1c | |||
7e49328d5e | |||
b50b636371 | |||
ec3bb5b95c | |||
6b2503fb36 | |||
e71d47f983 | |||
085d4f566a | |||
29b249de6e | |||
883ae5b0e4 | |||
d78990cda3 | |||
e5b5a8df37 | |||
2ea2db66ec | |||
7b76ddefd5 | |||
6c56ac8509 | |||
dc074bcb14 | |||
0193644b18 | |||
66de11ecd4 | |||
1238b1711d | |||
0061a9daf9 | |||
52a55d3bd1 | |||
7d686eb1d1 | |||
962188bdb8 | |||
72c3f763ec | |||
44c5d8490f | |||
fb85623b86 | |||
d7bf6addf1 | |||
adea1e9be1 | |||
4f77ca31a3 | |||
087f2ebcdd | |||
3d2a63f319 | |||
54dd3b621a | |||
7fea134109 | |||
fcf4bd12d9 | |||
6694555592 | |||
e6eb3d691e | |||
00f2d418cc | |||
c63f218edd | |||
fae849bbfc | |||
2a253ccb8d | |||
ff55e55ad2 | |||
d798ec446d | |||
56b4038073 | |||
076a55f1fb | |||
10eca98dc3 | |||
37aec3ee4f | |||
dc7c629e3b | |||
e4f16eda49 | |||
bf15bda84f | |||
8472a837e8 | |||
fc326131ae | |||
a19753205f | |||
ee404e3b84 | |||
f1918ac953 | |||
d399ddf6b6 | |||
3f06448f37 | |||
83d84e67d6 | |||
46c42c1bb4 | |||
944bf67190 | |||
7cac061a73 | |||
17764b708f | |||
2e0f4dfb19 | |||
7f5bc8681c | |||
bfcb85f744 | |||
cb9aaf120e | |||
97a9465db3 | |||
8dfaf9ba32 | |||
89d0da93d3 | |||
c9cbfb4317 | |||
a8ccfec5c6 | |||
d8609c9e84 | |||
5461c975d4 | |||
11298bc101 | |||
c42cf2f622 | |||
c9f1d38baa | |||
3d78d6bbe9 | |||
2b4288f301 | |||
09a2dd231a | |||
3fd4ef9985 | |||
05979965ba | |||
e625a7602a | |||
a0c41ad7ab | |||
7163747eb9 | |||
c30e2b6cf3 | |||
e9eae92ba9 | |||
0fd6fa8879 | |||
52ca6eac18 | |||
28776b8558 | |||
588ad3eab7 | |||
d791538086 | |||
62173e7996 | |||
0acdcd1ca7 | |||
f39a2c8dda | |||
b648852ef5 | |||
6e45892118 | |||
cd145c51f7 | |||
2c12d9ee2d | |||
5fe28623f1 | |||
3086b3cfc2 | |||
5fd29872ff | |||
732c8a314f | |||
23c650bfa6 | |||
f5ced785e0 | |||
4dc9004303 | |||
893c6bd72b | |||
2ac32484e1 | |||
b31e8ce5f2 | |||
4270e4c315 | |||
4b13395b0c | |||
2e67029ef5 | |||
257517b9a9 | |||
88ee64e15d | |||
c2030e9a86 | |||
16111a99fb | |||
543b3e5a91 | |||
e33b60065e | |||
3bcfadd2ab | |||
4729583d8d | |||
aa3f457595 | |||
3ad490f840 | |||
eb27ed65ae | |||
d543f62c5b | |||
4e6f7a05de | |||
5d467115ad | |||
6858b266fe | |||
852b1a4c08 | |||
b2324db7b4 | |||
e8c85e2a54 | |||
a4ac74c84a | |||
4e378484ea | |||
c55be70b2c | |||
70898790f5 | |||
bfdb535f5b | |||
996e6b16e7 | |||
0217e21753 | |||
f6f79d42e1 | |||
4bbd26b098 | |||
028179af37 | |||
87283bf838 | |||
c680badaa2 | |||
df4a936e43 | |||
e27946ebcc | |||
13e64c012f | |||
f69302be48 | |||
16edfc16ea | |||
229a9c8102 | |||
490091a7e2 | |||
5f58e5ca82 | |||
16beda530a | |||
f1b373924f | |||
6a73c463cb | |||
0271675cd0 | |||
b7555aa640 | |||
fa9ef6e5bd | |||
1c97b004ca | |||
f0ab42adf1 | |||
165d8358d4 | |||
6bd6e74bcb | |||
b23df9e0a4 | |||
7b12affb77 | |||
8c8c01aa75 | |||
0150e699a2 | |||
e6943e2638 | |||
6f80dcb1de | |||
60ed294302 | |||
e8017b58f5 | |||
c79bf7d337 | |||
ac268c1ec9 | |||
1ef131fa2d | |||
1873007550 | |||
fa2ccb51c9 | |||
8f87d654af | |||
dee6e9fbad | |||
ef90264316 | |||
70bf4be723 | |||
07ef727654 | |||
a346e5be29 | |||
5ce1eadde7 | |||
8a4d5395b1 | |||
b5feed0f46 | |||
11f9579101 | |||
c6000c959a | |||
19e67ea2b0 | |||
0f6619e25d | |||
daf747d3be | |||
7393b1f2cf | |||
efe861a9ba | |||
2c358ab179 | |||
ca157fc91d | |||
1fbe5d7bc6 | |||
71c031ccf9 | |||
6449b7ccca | |||
6a956472fe | |||
d6af88d667 | |||
b9184a202f | |||
f898746967 | |||
68165b7b78 | |||
bb8591a67b | |||
bda71ae78e | |||
abd4f6cac2 | |||
87e6cc2a4f | |||
dde598eb64 | |||
0c4407f43a | |||
b6c864e7a1 | |||
3d9dc6465d | |||
9a6fec094a | |||
61af224d7d | |||
9b41641e97 | |||
4e2e9f6f8f | |||
c29dc8b4c7 | |||
98a3c6b156 | |||
69c540288b | |||
651c9f5692 | |||
9ad3778cf7 | |||
c90e0e9f64 | |||
cb1730c4e2 | |||
3dd8a973fd | |||
c3ea088fca | |||
a11d6d7868 | |||
a596add838 | |||
7e7103ddab | |||
0064970ed7 | |||
9d93f3ea95 | |||
ff2a3cd19e | |||
8419d95ea1 | |||
4ad317ac7b | |||
7cccebbf2c | |||
e4f76ee9eb | |||
7f52f72c25 | |||
44afcbbeaf | |||
e816c59539 | |||
c74421a42a | |||
23eb4633c4 | |||
26241afb86 | |||
92dbf966a1 | |||
db226b54a8 | |||
3af059f5c4 | |||
8706abcdf0 | |||
2129d041ac | |||
2a4a44ebb5 | |||
a4f2d5402c | |||
9f5fc151b4 | |||
8a91eb46e6 | |||
e4ab928e82 | |||
a1b02cb862 | |||
84b942b9d2 | |||
1ca99a6209 | |||
6b61abe8c0 | |||
27028ca1ef | |||
eed88926a2 | |||
b25877c514 | |||
119e574495 | |||
7308d253b2 | |||
1c19b71697 | |||
7551fff93f | |||
b2aa919574 | |||
1102bbe483 | |||
b78dd22ba9 | |||
73110ada46 | |||
74dedd06bc | |||
20c936d13b | |||
f135b89de9 | |||
6a83930ae0 | |||
a1d0acfac2 | |||
04be06c0cb | |||
78d52d6298 | |||
5ff7c28c43 | |||
5526f315d2 | |||
d5e25fdeb1 | |||
90a7bf5179 | |||
bf1f696870 | |||
95551ad049 | |||
1ad90680f4 | |||
d69d3cb421 | |||
9adca07393 | |||
a9d129fddc | |||
b4ac09bea3 | |||
b1a403d9b5 | |||
28504fb5e3 | |||
8ebb8e3c02 | |||
c95c2cd1ae | |||
946bee2194 | |||
1f9bd04308 | |||
33572b2dc7 | |||
680446b77e | |||
bf1d76a3a7 | |||
c915fcfdff | |||
02966c3b93 | |||
84dc48daec | |||
12225004f4 | |||
320dfa2ec5 | |||
b5f3016085 | |||
cd53e369d0 | |||
6fc11af774 | |||
42e3a97616 | |||
6b16aa1692 | |||
03d0de74e4 | |||
c3b643df84 | |||
697f9ba5bc | |||
28c75c5b96 | |||
6f255854f2 | |||
91c5f9c43e | |||
62acd6404a | |||
adc1461771 | |||
66cc0964ce | |||
10d77720ad | |||
475114c6f9 | |||
a0fec7d103 | |||
4d9d92a026 | |||
e51aa8c271 | |||
d44adedade | |||
9fb3c50aa7 | |||
907053a349 | |||
f0f85cfb59 | |||
44d0341fb2 | |||
0cdae52c66 | |||
0cd9c8e416 | |||
9e3010ab52 | |||
d831b2df55 | |||
16ff630e88 | |||
d35763662e | |||
10be411b12 | |||
6ecce192f7 | |||
ee07e60fe9 | |||
a35d7a1154 | |||
ebba8dbfd6 | |||
44c637c06b | |||
d54ba734bf | |||
b45fc22306 | |||
994935d4ae | |||
ceb5ce850c | |||
97b5ed945d | |||
873b322245 | |||
12ad9e41e7 | |||
96b418e455 | |||
8ea7861f77 | |||
821976c881 | |||
6f1443e43d | |||
09fcaecdfc | |||
efd72ca9f6 | |||
550b5e9aed | |||
4b7ae5fcff | |||
fa85e12127 | |||
1cce50902b | |||
2048fa5cf9 | |||
f438c8fd31 | |||
0bfa832dad | |||
483dfbe1ec | |||
561fee491d | |||
97d157192a | |||
6b14a8a057 | |||
d27b187f5e | |||
02d49ba2ca | |||
f3571a5855 | |||
3d5f0df213 | |||
595a201fe7 | |||
c3db7d8148 | |||
4a2187ff32 | |||
279c36a30d | |||
fd65117a5d | |||
9305beace3 | |||
ba86cbfb18 | |||
2ac7d1d4ab | |||
e9c55260c8 | |||
99b6a287f8 | |||
d645f0cad4 | |||
b0343254c0 | |||
cc368f015b | |||
4e508855fc | |||
72c0a6f165 | |||
f266508654 | |||
1893d82b2d | |||
bb1dd6a2ec | |||
7097ea5d9b | |||
b4092e0641 | |||
2c068c7bb6 | |||
a1a4fcb978 | |||
f77620d649 | |||
9e932c9078 | |||
de47dbe41b | |||
6d36d67a60 | |||
607bf51b37 | |||
f38ed0272c | |||
eef969439b | |||
272f552f3c | |||
dff9c1012b | |||
52470360a1 | |||
6c62459ed4 | |||
98482cebf9 | |||
509880119e | |||
195ccb5eed | |||
122af1c943 | |||
169940058b | |||
478e60d60e | |||
9fc0ac8c4f | |||
e86b780479 | |||
42f08bdc65 | |||
c2789cdac3 | |||
8a362f49f8 | |||
784a80d1a5 | |||
922d2b4b5f | |||
91e8461cac | |||
8757598a2d | |||
524aabea1d | |||
513181ff47 | |||
6bc288ed54 | |||
77f3a875dd | |||
6584250d1a | |||
e42db162aa | |||
2762096167 | |||
b2ec3e5f7b | |||
e012bd6cbe | |||
a02e64e805 | |||
e4bb3e1133 | |||
998795e0e0 | |||
ec44b84cc9 | |||
efece061d0 | |||
e347fc74a2 | |||
027418a86c | |||
864187aa02 | |||
59ae6619c0 | |||
6aa0be8d01 | |||
657658ea2b | |||
8647128e12 | |||
c8d92b3cd2 | |||
cc8b7b45ed | |||
5b8b8c8441 | |||
7106c640ef | |||
300b84983d | |||
49c1b92838 | |||
d900827850 | |||
1688d4dbe1 | |||
f438eee842 | |||
33733219f6 | |||
737a83cdf3 | |||
9061e1b495 | |||
09199e41a1 | |||
4e91932613 | |||
227de4ecfa | |||
c1ccacf851 |
@ -1,6 +1,4 @@
|
||||
Listen 8080
|
||||
|
||||
<VirtualHost *:8080>
|
||||
<VirtualHost *:80>
|
||||
UseCanonicalName Off
|
||||
ServerName mailpoet.loc
|
||||
DocumentRoot /home/circleci/mailpoet/wordpress
|
||||
@ -8,6 +6,9 @@ Listen 8080
|
||||
LogLevel notice
|
||||
|
||||
<Directory /home/circleci/mailpoet/wordpress>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride All
|
||||
RewriteEngine On
|
||||
Require all granted
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
@ -1,9 +1,9 @@
|
||||
version: 2
|
||||
jobs:
|
||||
qa_js_php5:
|
||||
qa_js_security_php5:
|
||||
working_directory: /home/circleci/mailpoet
|
||||
docker:
|
||||
- image: circleci/php:5.6.30-apache-browsers
|
||||
- image: mailpoet/wordpress:5.6.30_20180417.1
|
||||
- image: circleci/mysql:5.7
|
||||
environment:
|
||||
TZ: /usr/share/zoneinfo/Etc/UTC
|
||||
@ -38,6 +38,10 @@ jobs:
|
||||
command: |
|
||||
mkdir test-results/mocha
|
||||
./do t:j test-results/mocha/junit.xml
|
||||
- run:
|
||||
name: "Composer security check"
|
||||
command: |
|
||||
./do s:composer
|
||||
- run:
|
||||
name: "PHP Unit tests"
|
||||
command: |
|
||||
@ -81,6 +85,7 @@ jobs:
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
php composer.phar install
|
||||
./do install
|
||||
./do compile:all --env production
|
||||
- save_cache:
|
||||
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||
paths:
|
||||
@ -94,13 +99,56 @@ jobs:
|
||||
command: |
|
||||
docker-compose run codeception --steps --debug -vvv --html --xml
|
||||
- store_artifacts:
|
||||
path: ~/mailpoet/tests/acceptance-tests/_output
|
||||
path: tests/_output
|
||||
- store_test_results:
|
||||
path: ~/mailpoet/tests/acceptance-tests/_output
|
||||
path: tests/_output
|
||||
acceptance_tests_multisite:
|
||||
working_directory: /home/circleci/mailpoet
|
||||
machine: true
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Set up virtual host"
|
||||
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
||||
- restore_cache:
|
||||
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||
- restore_cache:
|
||||
key: npm-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: "Set up test environment"
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install circleci-php-5.6.23
|
||||
sudo rm /usr/bin/php
|
||||
sudo ln -s /opt/circleci/php/5.6.23/bin/php /usr/bin/php
|
||||
# Install NodeJS+NPM
|
||||
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
||||
sudo apt-get install nodejs build-essential
|
||||
# install plugin dependencies
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
php composer.phar install
|
||||
./do install
|
||||
./do compile:all --env production
|
||||
- save_cache:
|
||||
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||
paths:
|
||||
- vendor
|
||||
- save_cache:
|
||||
key: npm-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- node_modules
|
||||
- run:
|
||||
name: Run acceptance tests
|
||||
command: |
|
||||
docker-compose run -e MULTISITE=1 codeception --steps --debug -vvv --html --xml
|
||||
- store_artifacts:
|
||||
path: tests/_output
|
||||
- store_test_results:
|
||||
path: tests/_output
|
||||
php7:
|
||||
working_directory: /home/circleci/mailpoet
|
||||
docker:
|
||||
- image: circleci/php:7.1-apache-browsers
|
||||
- image: mailpoet/wordpress:7.1_20180417.1
|
||||
- image: circleci/mysql:5.7
|
||||
environment:
|
||||
TZ: /usr/share/zoneinfo/Etc/UTC
|
||||
@ -109,6 +157,9 @@ jobs:
|
||||
- run:
|
||||
name: "Set up virtual host"
|
||||
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
||||
- run:
|
||||
name: "Prepare example.com for testing"
|
||||
command: echo 127.0.0.1 example.com | sudo tee -a /etc/hosts
|
||||
- restore_cache:
|
||||
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||
- restore_cache:
|
||||
@ -119,7 +170,41 @@ jobs:
|
||||
- run:
|
||||
name: "PHP Unit tests"
|
||||
command: |
|
||||
WP_TEST_PATH="/home/circleci/mailpoet/wordpress" ./do t:u --xml
|
||||
./do t:u --xml
|
||||
- store_test_results:
|
||||
path: tests/_output
|
||||
- store_artifacts:
|
||||
path: tests/_output
|
||||
destination: codeception
|
||||
- store_artifacts:
|
||||
path: /tmp/fake-mailer/
|
||||
destination: fake-mailer
|
||||
php7_multisite:
|
||||
working_directory: /home/circleci/mailpoet
|
||||
docker:
|
||||
- image: mailpoet/wordpress:7.1_20180417.1
|
||||
- image: circleci/mysql:5.7
|
||||
environment:
|
||||
TZ: /usr/share/zoneinfo/Etc/UTC
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Set up virtual host"
|
||||
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
||||
- run:
|
||||
name: "Prepare example.com for testing"
|
||||
command: echo 127.0.0.1 example.com | sudo tee -a /etc/hosts
|
||||
- restore_cache:
|
||||
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||
- restore_cache:
|
||||
key: npm-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: "Set up test environment"
|
||||
command: source ./.circleci/setup.bash && setup php7_multisite
|
||||
- run:
|
||||
name: "PHP Unit tests"
|
||||
command: |
|
||||
./do t:multisite-unit --xml
|
||||
- store_test_results:
|
||||
path: tests/_output
|
||||
- store_artifacts:
|
||||
@ -132,6 +217,8 @@ workflows:
|
||||
version: 2
|
||||
build_and_test:
|
||||
jobs:
|
||||
- qa_js_php5
|
||||
- qa_js_security_php5
|
||||
- php7
|
||||
- php7_multisite
|
||||
- acceptance_tests
|
||||
- acceptance_tests_multisite
|
||||
|
@ -2,47 +2,66 @@
|
||||
|
||||
function setup {
|
||||
local version=$1
|
||||
# install PHP dependencies for WordPress
|
||||
if [[ $version == "php7" ]]; then
|
||||
echo "deb http://packages.dotdeb.org jessie all" | sudo tee -a /etc/apt/sources.list.d/dotdeb.list
|
||||
echo "deb-src http://packages.dotdeb.org jessie all" | sudo tee -a /etc/apt/sources.list.d/dotdeb.list
|
||||
wget -qO - http://www.dotdeb.org/dotdeb.gpg | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get install mysql-client php7.0-mysql zlib1g-dev
|
||||
sudo docker-php-ext-install mysqli pdo pdo_mysql zip
|
||||
else
|
||||
sudo apt-get update
|
||||
sudo apt-get install mysql-client php5-mysql zlib1g-dev
|
||||
sudo docker-php-ext-install mysql mysqli pdo pdo_mysql zip
|
||||
fi
|
||||
local wp_cli_wordpress_path="--path=wordpress"
|
||||
local wp_cli_allow_root="--allow-root"
|
||||
|
||||
# Add a fake sendmail mailer
|
||||
sudo cp ./.circleci/fake-sendmail.php /usr/local/bin/
|
||||
|
||||
# configure Apache
|
||||
sudo cp ./.circleci/mailpoet_php.ini /usr/local/etc/php/conf.d/
|
||||
sudo cp ./.circleci/apache/mailpoet.loc.conf /etc/apache2/sites-available
|
||||
sudo a2dissite 000-default.conf
|
||||
sudo a2ensite mailpoet.loc
|
||||
sudo a2enmod rewrite
|
||||
sudo service apache2 restart
|
||||
# Install NodeJS+NPM
|
||||
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
||||
sudo apt-get install nodejs build-essential
|
||||
|
||||
# install plugin dependencies
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
./composer.phar install
|
||||
./do install
|
||||
# Set up Wordpress
|
||||
|
||||
until mysql -h 127.0.0.1 -u root -e "select 1"; do
|
||||
>&2 echo "Mysql is starting up ... will try again momentarily"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Set up WordPress
|
||||
mysql -h 127.0.0.1 -u root -e "create database wordpress"
|
||||
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
|
||||
chmod +x wp-cli.phar
|
||||
./wp-cli.phar core download --allow-root --path=wordpress
|
||||
wp core download $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
|
||||
# Generate `wp-config.php` file with debugging enabled
|
||||
echo "define(\"WP_DEBUG\", true);" | ./wp-cli.phar core config --allow-root --dbname=wordpress --dbuser=root --dbhost=127.0.0.1 --path=wordpress --extra-php
|
||||
echo "define(\"WP_DEBUG\", true);" | wp core config --dbname=wordpress --dbuser=root --dbhost=127.0.0.1 --extra-php $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
|
||||
# Change default table prefix
|
||||
sed -i "s/\$table_prefix = 'wp_';/\$table_prefix = 'mp_';/" ./wordpress/wp-config.php
|
||||
|
||||
# Install WordPress
|
||||
./wp-cli.phar core install --allow-root --admin_name=admin --admin_password=admin --admin_email=admin@mailpoet.loc --url=http://mailpoet.loc:8080 --title=WordPress --path=wordpress
|
||||
if [[ $version == "php7_multisite" ]]; then
|
||||
# Configure multisite environment
|
||||
wp core multisite-install --admin_name=admin --admin_password=admin --admin_email=admin@mailpoet.loc --url=http://mailpoet.loc --title="WordPress MultiSite" $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
cp ./.circleci/wordpress/.htaccess ./wordpress/
|
||||
|
||||
# Add a second blog
|
||||
wp site create --slug=php7_multisite $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
echo "WP_TEST_MULTISITE_SLUG=php7_multisite" >> .env
|
||||
echo "WP_TEST_PATH_MULTISITE=/home/circleci/mailpoet/wordpress" >> .env
|
||||
echo "HTTP_HOST=mailpoet.loc" >> .env
|
||||
|
||||
# Add a third dummy blog
|
||||
wp site create --slug=dummy_multisite $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
else
|
||||
wp core install --admin_name=admin --admin_password=admin --admin_email=admin@mailpoet.loc --url=http://mailpoet.loc --title="WordPress Single" $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
echo "WP_TEST_PATH=/home/circleci/mailpoet/wordpress" >> .env
|
||||
fi
|
||||
|
||||
# Softlink plugin to plugin path
|
||||
ln -s ../../.. wordpress/wp-content/plugins/mailpoet
|
||||
./wp-cli.phar plugin activate mailpoet --path=wordpress
|
||||
# Create .env file with correct path to WP installation
|
||||
# TODO: Remove this line after PR gets merged and CircleCI env variables change
|
||||
echo "WP_TEST_PATH=\"/home/circleci/mailpoet/wordpress\"" > .env
|
||||
|
||||
# Activate plugin
|
||||
if [[ $version == "php7_multisite" ]]; then
|
||||
wp plugin activate mailpoet --url=http://mailpoet.loc/php7_multisite/ $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
else
|
||||
wp plugin activate mailpoet $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
fi
|
||||
}
|
||||
|
12
.circleci/wordpress/.htaccess
Normal file
@ -0,0 +1,12 @@
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
|
||||
RewriteRule . index.php [L]
|
@ -1,4 +1,6 @@
|
||||
WP_TEST_PATH="/var/www/wordpress"
|
||||
WP_TEST_PATH_MULTISITE="/var/www/wordpress"
|
||||
WP_TEST_MULTISITE_SLUG=""
|
||||
WP_TEST_ENABLE_NETWORK_TESTS="true"
|
||||
WP_TEST_IMPORT_MAILCHIMP_API=""
|
||||
WP_TEST_IMPORT_MAILCHIMP_LISTS="" // (separated with comma)
|
||||
@ -16,4 +18,5 @@ WP_TEST_MAILER_SMTP_LOGIN=""
|
||||
WP_TEST_MAILER_SMTP_PASSWORD=""
|
||||
WP_SVN_USERNAME=""
|
||||
WP_SVN_PASSWORD=""
|
||||
WP_TRANSIFEX_API_TOKEN=""
|
||||
WP_TRANSIFEX_API_TOKEN=""
|
||||
HTTP_HOST="" // URL of your site (used for multisite env and equals to DOMAIN_CURRENT_SITE from wp-config.php)
|
@ -8,80 +8,6 @@
|
||||
"ecmaVersion": 5
|
||||
},
|
||||
"rules": {
|
||||
"import/no-amd": 0,
|
||||
"space-before-function-paren": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"no-undef": 0,
|
||||
"key-spacing": 0,
|
||||
"radix": 0,
|
||||
"no-alert": 0,
|
||||
"block-scoped-var": 0,
|
||||
"guard-for-in": 0,
|
||||
"no-prototype-builtins": 0,
|
||||
"no-restricted-syntax": 0,
|
||||
"newline-per-chained-call": 0,
|
||||
"no-useless-concat": 0,
|
||||
"no-multi-spaces": 0,
|
||||
"no-nested-ternary": 0,
|
||||
"semi-spacing": 0,
|
||||
"no-sequences": 0,
|
||||
"no-useless-return": 0,
|
||||
"array-callback-return": 0,
|
||||
"new-cap": 0,
|
||||
"no-continue": 0,
|
||||
"no-new": 0,
|
||||
"space-unary-ops": 0,
|
||||
"no-redeclare": 0,
|
||||
"no-console": 0,
|
||||
"no-empty": 0,
|
||||
"no-extra-semi": 0,
|
||||
"no-useless-escape": 0,
|
||||
"wrap-iife": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"block-spacing": 0,
|
||||
"computed-property-spacing": 0,
|
||||
"no-plusplus": 0,
|
||||
"array-bracket-spacing": 0,
|
||||
"lines-around-directive": 0,
|
||||
"no-unreachable": 0,
|
||||
"default-case": 0,
|
||||
"no-lonely-if": 0,
|
||||
"space-before-blocks": 0,
|
||||
"no-unneeded-ternary": 0,
|
||||
"no-mixed-operators": 0,
|
||||
"eqeqeq": 0,
|
||||
"space-in-parens": 0,
|
||||
"semi": 0,
|
||||
"max-len": 0,
|
||||
"no-trailing-spaces": 0,
|
||||
"global-require": 0,
|
||||
"no-throw-literal": 0,
|
||||
"no-extra-bind": 0,
|
||||
"one-var-declaration-per-line": 0,
|
||||
"consistent-return": 0,
|
||||
"no-shadow": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"brace-style": 0,
|
||||
"no-else-return": 0,
|
||||
"no-use-before-define": 0,
|
||||
"one-var": 0,
|
||||
"camelcase": 0,
|
||||
"spaced-comment": 0,
|
||||
"padded-blocks": 0,
|
||||
"object-curly-spacing": 0,
|
||||
"strict": 0,
|
||||
"vars-on-top": 0,
|
||||
"no-var": 0,
|
||||
"space-infix-ops": 0,
|
||||
"no-unused-vars": 0,
|
||||
"object-shorthand": 0,
|
||||
"new-parens": 0,
|
||||
"keyword-spacing": 0,
|
||||
"eol-last": 0,
|
||||
"dot-notation": 0,
|
||||
"linebreak-style": 0,
|
||||
"indent": 0,
|
||||
"prefer-template": 0,
|
||||
"func-names": 0
|
||||
"no-underscore-dangle": 0 // Backbone uses underscores, we cannot remove them
|
||||
}
|
||||
}
|
||||
|
@ -10,79 +10,13 @@
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": "webpack"
|
||||
},
|
||||
"rules": {
|
||||
// Exceptions
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"import/no-amd": 0,
|
||||
"react/no-multi-comp": 0,
|
||||
"react/sort-comp": 0,
|
||||
"react/jsx-max-props-per-line": 0,
|
||||
"react/prop-types": 0,
|
||||
"react/jsx-first-prop-new-line": 0,
|
||||
"react/jsx-indent-props": 0,
|
||||
"react/no-is-mounted": 0,
|
||||
"react/jsx-no-target-blank": 0,
|
||||
"react/no-render-return-value": 0,
|
||||
"react/jsx-boolean-value": 0,
|
||||
"react/jsx-indent": 0,
|
||||
"react/jsx-no-bind": 0,
|
||||
"react/no-array-index-key": 0,
|
||||
"react/self-closing-comp": 0,
|
||||
"react/jsx-tag-spacing": 0,
|
||||
"react/jsx-closing-bracket-location": 0,
|
||||
"react/no-string-refs": 0,
|
||||
"react/jsx-curly-spacing": 0,
|
||||
"react/no-did-mount-set-state": 0,
|
||||
"react/prefer-stateless-function": 0,
|
||||
"jsx-a11y/label-has-for": 0,
|
||||
"jsx-a11y/no-static-element-interactions": 0,
|
||||
"jsx-a11y/alt-text": 0,
|
||||
"func-names": 0,
|
||||
"object-shorthand": 0,
|
||||
"no-bitwise": 0,
|
||||
"arrow-body-style": 0,
|
||||
"prefer-template": 0,
|
||||
"keyword-spacing": 0,
|
||||
"default-case": 0,
|
||||
"array-callback-return": 0,
|
||||
"consistent-return": 0,
|
||||
"no-unreachable": 0,
|
||||
"no-extra-semi": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/extensions": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"camelcase": 0,
|
||||
"template-curly-spacing": 0,
|
||||
"eqeqeq": 0,
|
||||
"no-lonely-if": 0,
|
||||
"space-unary-ops": 0,
|
||||
"block-scoped-var": 0,
|
||||
"no-extra-bind": 0,
|
||||
"no-multi-spaces": 0,
|
||||
"class-methods-use-this": 0,
|
||||
"key-spacing": 0,
|
||||
"no-multiple-empty-lines": 0,
|
||||
"space-in-parens": 0,
|
||||
"no-case-declarations": 0,
|
||||
"array-bracket-spacing": 0,
|
||||
"newline-per-chained-call": 0,
|
||||
"no-else-return": 0,
|
||||
"max-len": 0,
|
||||
"no-useless-concat": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"no-sequences": 0,
|
||||
"no-extra-boolean-cast": 0,
|
||||
"dot-notation": 0,
|
||||
"no-shadow": 0,
|
||||
"one-var": 0,
|
||||
"no-alert": 0,
|
||||
"one-var-declaration-per-line": 0,
|
||||
"no-script-url": 0,
|
||||
"wrap-iife": 0,
|
||||
"vars-on-top": 0,
|
||||
"space-infix-ops": 0,
|
||||
"no-irregular-whitespace": 0,
|
||||
"padded-blocks": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-undef": 0
|
||||
"import/extensions": 0 // we wouldn't be able to import jQuery without this line
|
||||
}
|
||||
}
|
||||
|
@ -8,32 +8,9 @@
|
||||
"ecmaVersion": 6
|
||||
},
|
||||
"rules": {
|
||||
"import/no-amd": 0,
|
||||
"no-undef": 0,
|
||||
"one-var": 0,
|
||||
"indent": 0,
|
||||
"linebreak-style": 0,
|
||||
"no-whitespace-before-property": 0,
|
||||
"object-property-newline": 0,
|
||||
"global-require": 0,
|
||||
"semi": 0,
|
||||
"keyword-spacing": 0,
|
||||
"no-bitwise": 0,
|
||||
"newline-per-chained-call": 0,
|
||||
"no-spaced-func": 0,
|
||||
"func-call-spacing": 0,
|
||||
"max-len": 0,
|
||||
"space-unary-ops": 0,
|
||||
"no-unused-vars": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-shadow": 0,
|
||||
"padded-blocks": 0,
|
||||
"vars-on-top": 0,
|
||||
"space-before-blocks": 0,
|
||||
"object-curly-spacing": 0,
|
||||
"one-var-declaration-per-line": 0,
|
||||
// Exceptions
|
||||
"func-names": 0,
|
||||
"space-before-function-paren": 0
|
||||
// Temporary
|
||||
"no-underscore-dangle": 0
|
||||
}
|
||||
}
|
||||
|
4
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
TODO
|
||||
composer.phar
|
||||
/vendor
|
||||
/vendor_backup
|
||||
tests/_output/*
|
||||
tests/_support/_generated/*
|
||||
node_modules
|
||||
@ -20,4 +21,5 @@ assets/js/*.json
|
||||
.vagrant
|
||||
lang
|
||||
.mp_svn
|
||||
/nbproject/
|
||||
/nbproject/
|
||||
tests/_data/acceptanceGenerated.sql
|
24
Dockerfile
@ -1,26 +1,8 @@
|
||||
FROM php:5.6-cli
|
||||
FROM mailpoet/wordpress:5.6-cli_20180417.1
|
||||
|
||||
ENV COMPOSER_ALLOW_SUPERUSER=1
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get -y install \
|
||||
git \
|
||||
zlib1g-dev \
|
||||
libssl-dev \
|
||||
mysql-client \
|
||||
sudo less \
|
||||
--no-install-recommends && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
|
||||
docker-php-ext-install bcmath zip mysqli pdo pdo_mysql && \
|
||||
echo "date.timezone = UTC" >> /usr/local/etc/php/php.ini && \
|
||||
curl -sS https://getcomposer.org/installer | php -- \
|
||||
--filename=composer \
|
||||
--install-dir=/usr/local/bin && \
|
||||
composer global require --optimize-autoloader "hirak/prestissimo" && \
|
||||
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \
|
||||
chmod +x wp-cli.phar && \
|
||||
mv wp-cli.phar /usr/local/bin/wp
|
||||
RUN composer global require --optimize-autoloader "hirak/prestissimo"
|
||||
|
||||
# Prepare application
|
||||
WORKDIR /repo
|
||||
@ -36,4 +18,4 @@ ENV WP_TEST_PATH=/wp-core
|
||||
|
||||
ADD docker-entrypoint.sh /
|
||||
|
||||
RUN ["chmod", "+x", "/docker-entrypoint.sh"]
|
||||
RUN ["chmod", "+x", "/docker-entrypoint.sh"]
|
||||
|
78
RoboFile.php
@ -90,6 +90,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
|
||||
$css_files = array(
|
||||
'assets/css/src/admin.styl',
|
||||
'assets/css/src/admin-global.styl',
|
||||
'assets/css/src/newsletter_editor/newsletter_editor.styl',
|
||||
'assets/css/src/public.styl',
|
||||
'assets/css/src/rtl.styl',
|
||||
@ -152,17 +153,34 @@ class RoboFile extends \Robo\Tasks {
|
||||
return $this->_exec('./tasks/transifex_init.sh');
|
||||
}
|
||||
|
||||
function testUnit($opts=['file' => null, 'xml' => false]) {
|
||||
function testUnit(array $opts=['file' => null, 'xml' => false, 'multisite' => false, 'debug' => false]) {
|
||||
$this->loadEnv();
|
||||
|
||||
$command = 'vendor/bin/codecept run unit -c codeception.unit.yml -f '.(($opts['file']) ? $opts['file'] : '');
|
||||
$command = 'vendor/bin/codecept run unit -c codeception.unit.yml';
|
||||
|
||||
if($opts['multisite']) {
|
||||
$command = 'MULTISITE=true ' . $command;
|
||||
}
|
||||
|
||||
if($opts['file']) {
|
||||
$command .= ' -f ' . $opts['file'];
|
||||
}
|
||||
|
||||
if($opts['xml']) {
|
||||
$command .= ' --xml';
|
||||
}
|
||||
|
||||
if($opts['debug']) {
|
||||
$command .= ' --debug';
|
||||
}
|
||||
|
||||
return $this->_exec($command);
|
||||
}
|
||||
|
||||
function testMultisiteUnit($opts=['file' => null, 'xml' => false, 'multisite' => true]) {
|
||||
return $this->testUnit($opts);
|
||||
}
|
||||
|
||||
function testCoverage($opts=['file' => null, 'xml' => false]) {
|
||||
$this->loadEnv();
|
||||
$command = join(' ', array(
|
||||
@ -197,6 +215,10 @@ class RoboFile extends \Robo\Tasks {
|
||||
return $this->_exec($command);
|
||||
}
|
||||
|
||||
function securityComposer() {
|
||||
return $this->_exec('vendor/bin/security-checker security:check --format=simple');
|
||||
}
|
||||
|
||||
function testDebug($opts=['file' => null, 'xml' => false]) {
|
||||
$this->loadEnv();
|
||||
$this->_exec('vendor/bin/codecept build -c codeception.unit.yml');
|
||||
@ -209,8 +231,27 @@ class RoboFile extends \Robo\Tasks {
|
||||
return $this->_exec($command);
|
||||
}
|
||||
|
||||
function testAcceptance() {
|
||||
return $this->_exec('COMPOSE_HTTP_TIMEOUT=200 docker-compose run codeception --steps --debug -vvv');
|
||||
function testAcceptance($opts=['file' => null, 'keep-deps' => false]) {
|
||||
return $this->_exec(
|
||||
'COMPOSE_HTTP_TIMEOUT=200 docker-compose run ' .
|
||||
($opts['keep-deps'] ? '-e KEEP_DEPS=1 ' : '') .
|
||||
'codeception --steps --debug -vvv ' .
|
||||
'-f ' . ($opts['file'] ? $opts['file'] : '')
|
||||
);
|
||||
}
|
||||
|
||||
function testAcceptanceMultisite($opts=['file' => null, 'keep-deps' => false]) {
|
||||
return $this->_exec(
|
||||
'COMPOSE_HTTP_TIMEOUT=200 docker-compose run ' .
|
||||
($opts['keep-deps'] ? '-e KEEP_DEPS=1 ' : '') .
|
||||
'-e MULTISITE=1 ' .
|
||||
'codeception --steps --debug -vvv' .
|
||||
'-f ' . ($opts['file'] ? $opts['file'] : '')
|
||||
);
|
||||
}
|
||||
|
||||
function deleteDocker() {
|
||||
return $this->_exec('docker-compose down -v --remove-orphans --rmi all');
|
||||
}
|
||||
|
||||
function testFailed() {
|
||||
@ -247,7 +288,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
->taskExec(
|
||||
'./vendor/bin/phpcs '.
|
||||
'--standard=./tasks/code_sniffer/MailPoet '.
|
||||
'--runtime-set testVersion 5.3-7.0 '.
|
||||
'--runtime-set testVersion 5.4-7.2 '.
|
||||
'--ignore=./lib/Util/Sudzy/*,./lib/Util/CSS.php,./lib/Util/XLSXWriter.php,'.
|
||||
'./lib/Util/pQuery/*,./lib/Config/PopulatorData/Templates/* '.
|
||||
'lib/ '.
|
||||
@ -256,7 +297,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
->taskExec(
|
||||
'./vendor/bin/phpcs '.
|
||||
'--standard=./tasks/code_sniffer/MailPoet '.
|
||||
'--runtime-set testVersion 5.4-7.0 '.
|
||||
'--runtime-set testVersion 5.4-7.2 '.
|
||||
'--ignore=./tests/unit/_bootstrap.php '.
|
||||
'tests/unit/ '.
|
||||
$severityFlag
|
||||
@ -288,15 +329,16 @@ class RoboFile extends \Robo\Tasks {
|
||||
}
|
||||
|
||||
function svnPublish($opts = ['force' => false]) {
|
||||
$this->loadWPFunctions();
|
||||
$this->loadEnv();
|
||||
|
||||
$svn_dir = ".mp_svn";
|
||||
$plugin_data = get_plugin_data('mailpoet.php', false, false);
|
||||
$plugin_version = $plugin_data['Version'];
|
||||
$plugin_dist_name = sanitize_title_with_dashes($plugin_data['Name']);
|
||||
$plugin_dist_name = explode('-', $plugin_dist_name);
|
||||
$plugin_dist_name = $plugin_dist_name[0];
|
||||
$plugin_version = $this->getPluginVersion('mailpoet.php');
|
||||
$plugin_dist_name = 'mailpoet';
|
||||
$plugin_dist_file = $plugin_dist_name . '.zip';
|
||||
|
||||
if(!$plugin_version) {
|
||||
throw new \Exception('Could not parse plugin version, check the plugin header');
|
||||
}
|
||||
$this->say('Publishing version: ' . $plugin_version);
|
||||
|
||||
// Sanity checks
|
||||
@ -415,13 +457,9 @@ class RoboFile extends \Robo\Tasks {
|
||||
$dotenv->load();
|
||||
}
|
||||
|
||||
protected function loadWPFunctions() {
|
||||
$this->loadEnv();
|
||||
define('ABSPATH', getenv('WP_TEST_PATH') . '/');
|
||||
define('WPINC', 'wp-includes');
|
||||
require_once(ABSPATH . WPINC . '/functions.php');
|
||||
require_once(ABSPATH . WPINC . '/formatting.php');
|
||||
require_once(ABSPATH . WPINC . '/plugin.php');
|
||||
require_once(ABSPATH . 'wp-admin/includes/plugin.php');
|
||||
protected function getPluginVersion($file) {
|
||||
$data = file_get_contents($file);
|
||||
preg_match('/^[ \t*]*Version:(.*)$/mi', $data, $m);
|
||||
return !empty($m[1]) ? trim($m[1]) : false;
|
||||
}
|
||||
}
|
||||
|
18
assets/css/src/admin-global.styl
Normal file
@ -0,0 +1,18 @@
|
||||
@import 'nib'
|
||||
|
||||
@require 'icons'
|
||||
|
||||
/*
|
||||
Style for Members plugin
|
||||
*/
|
||||
|
||||
.members-tab-title
|
||||
.mailpoet-icon-logo
|
||||
vertical-align: middle;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
font-size: 20px;
|
||||
margin-right: 3px;
|
||||
|
||||
#wpbody
|
||||
padding-bottom: 20px;
|
@ -2,6 +2,7 @@
|
||||
|
||||
@require 'select2/dist/css/select2.css'
|
||||
@require 'datepicker/datepicker'
|
||||
@require 'badge'
|
||||
|
||||
@require 'common'
|
||||
@require 'modal'
|
||||
@ -27,3 +28,8 @@
|
||||
@require 'pages_custom'
|
||||
|
||||
@require 'mp2migrator'
|
||||
|
||||
@require '../../../node_modules/react-confirm-alert/src/react-confirm-alert.css'
|
||||
|
||||
@require 'newsletter_templates'
|
||||
@require 'welcome_wizard'
|
||||
|
9
assets/css/src/badge.styl
Normal file
@ -0,0 +1,9 @@
|
||||
badge()
|
||||
text-transform uppercase
|
||||
cursor pointer
|
||||
color white
|
||||
font-size 0.5625rem
|
||||
font-weight 500
|
||||
border-radius 3px
|
||||
letter-spacing 1px
|
||||
vertical-align middle
|
@ -28,6 +28,20 @@ a:focus
|
||||
.select2-container
|
||||
width: 25em !important
|
||||
|
||||
placeholder-color = #999 /* default Select2 placeholder color for single dropdown */
|
||||
|
||||
input.select2-search__field::-webkit-input-placeholder
|
||||
color: placeholder-color
|
||||
input.select2-search__field:-moz-placeholder
|
||||
color: placeholder-color
|
||||
input.select2-search__field::-moz-placeholder
|
||||
color: placeholder-color
|
||||
input.select2-search__field:-ms-input-placeholder
|
||||
color: placeholder-color
|
||||
|
||||
.select2-container--default.select2-container--focus .select2-selection--multiple
|
||||
border: 1px solid #aaa; /* default Select2 border for single dropdown */
|
||||
|
||||
// textareas
|
||||
textarea.regular-text
|
||||
width: 25em !important
|
||||
@ -57,3 +71,10 @@ progress::-webkit-progress-value
|
||||
progress::-moz-progress-bar
|
||||
background-color: progress-foreground
|
||||
border-radius: progress-border-radius
|
||||
|
||||
span.feedback-tooltip
|
||||
badge()
|
||||
display inline-block
|
||||
padding 0 4px
|
||||
background-color #ca4a1f
|
||||
|
||||
|
24
assets/css/src/icons.styl
Normal file
@ -0,0 +1,24 @@
|
||||
icon-font-path ?= "../fonts"
|
||||
|
||||
@font-face
|
||||
font-family 'mailpoet'
|
||||
src url(icon-font-path + '/mailpoet.ttf?mx0b6n') format('truetype'), url(icon-font-path + '/mailpoet.woff?mx0b6n') format('woff'), url(icon-font-path + '/mailpoet.svg?mx0b6n#mailpoet') format('svg')
|
||||
font-weight normal
|
||||
font-style normal
|
||||
|
||||
[class^="mailpoet-icon-"], [class*=" mailpoet-icon-"]
|
||||
font-family 'mailpoet' !important
|
||||
speak none
|
||||
font-style normal
|
||||
font-weight normal
|
||||
font-variant normal
|
||||
text-transform none
|
||||
line-height 1
|
||||
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing antialiased
|
||||
-moz-osx-font-smoothing grayscale
|
||||
|
||||
.mailpoet-icon-logo
|
||||
&:before
|
||||
content "\e900"
|
@ -2,6 +2,7 @@ $excellent-badge-color = #2993ab
|
||||
$good-badge-color = #f0b849
|
||||
$bad-badge-color = #d54e21
|
||||
$green-badge-color = #55bd56
|
||||
$video-guide-badge-color = #46b450
|
||||
|
||||
#newsletters_container
|
||||
h2.nav-tab-wrapper
|
||||
@ -31,23 +32,34 @@ $green-badge-color = #55bd56
|
||||
|
||||
.mailpoet_badge
|
||||
padding: 4px 6px 3px 6px
|
||||
color: #FFFFFF
|
||||
margin-right: 4px
|
||||
text-transform: uppercase
|
||||
font-size: 0.5625rem
|
||||
font-weight: 500
|
||||
border-radius: 3px
|
||||
letter-spacing: 1px
|
||||
vertical-align: middle
|
||||
badge()
|
||||
|
||||
&_excellent
|
||||
&_excellent, &_teal
|
||||
background: $excellent-badge-color
|
||||
|
||||
&_good
|
||||
&_good, &_yellow
|
||||
background: $good-badge-color
|
||||
|
||||
&_bad
|
||||
&_bad, &_red
|
||||
background: $bad-badge-color
|
||||
|
||||
&_green
|
||||
background: $green-badge-color
|
||||
|
||||
&_video
|
||||
background: $video-guide-badge-color
|
||||
line-height: 5em
|
||||
padding: 7px 6px 7px 6px
|
||||
text-decoration: none
|
||||
|
||||
&:hover, &:active, &:focus
|
||||
color: #FFFFFF
|
||||
background: $green-badge-color
|
||||
|
||||
&_grey
|
||||
background: #c3c3c3
|
||||
|
||||
.dashicons
|
||||
font-size: 14px;
|
||||
line-height: 3.4em;
|
||||
|
@ -41,6 +41,8 @@ $master-column-tool-width = 24px
|
||||
right: initial
|
||||
|
||||
&.mailpoet_display_tools
|
||||
z-index: 21
|
||||
|
||||
.mailpoet_tool_slider
|
||||
left: 0
|
||||
|
||||
@ -163,6 +165,7 @@ $master-column-tool-width = 24px
|
||||
|
||||
.mailpoet_delete_block_confirm
|
||||
color: $warning-text-color
|
||||
float: right
|
||||
|
||||
&:hover
|
||||
color: $warning-text-color
|
||||
@ -170,6 +173,7 @@ $master-column-tool-width = 24px
|
||||
|
||||
.mailpoet_delete_block_cancel
|
||||
color: $warning-alternate-text-color
|
||||
float: right
|
||||
|
||||
&:hover
|
||||
color: $warning-alternate-text-color
|
||||
|
@ -12,9 +12,11 @@
|
||||
font-size: 23px
|
||||
|
||||
.mailpoet_breadcrumbs
|
||||
float: right
|
||||
clear: both
|
||||
float: left
|
||||
margin-bottom: 13px
|
||||
margin-right: 17px
|
||||
margin-left: 17px
|
||||
font-size: 0.9em
|
||||
text-transform: uppercase
|
||||
|
||||
p
|
||||
margin: 0
|
||||
|
@ -48,3 +48,41 @@ $resize-handle-z-index = 2
|
||||
|
||||
.mailpoet_resize_handle
|
||||
display: inline-block
|
||||
|
||||
|
||||
.mailpoet_image_resize_handle_container
|
||||
position: absolute
|
||||
bottom: 0
|
||||
right: 0
|
||||
width: 20px
|
||||
height: 20px
|
||||
|
||||
.mailpoet_image_resize_handle
|
||||
position: relative
|
||||
background: $resize-handle-background-color
|
||||
border-radius(3px)
|
||||
display: inline-block
|
||||
width: 20px
|
||||
height: 20px
|
||||
cursor: nwse-resize
|
||||
z-index: $resize-handle-z-index
|
||||
|
||||
.mailpoet_image_resize_handle_text,
|
||||
.mailpoet_image_resize_handle_icon
|
||||
pointer-events: none
|
||||
|
||||
.mailpoet_image_resize_handle_icon
|
||||
position: absolute
|
||||
top: 0
|
||||
right: 0
|
||||
|
||||
& > svg
|
||||
width: 100%
|
||||
height: 100%
|
||||
fill: $resize-handle-font-color
|
||||
|
||||
.mailpoet_block.mailpoet_image_resize_active > .mailpoet_block_highlight
|
||||
border: 1px dashed $resize-active-color
|
||||
|
||||
.mailpoet_image_resize_handle
|
||||
display: inline-block
|
||||
|
@ -1,15 +1,23 @@
|
||||
#mailpoet_editor_bottom
|
||||
margin-top: 39px
|
||||
margin-left: 29px
|
||||
margin: 10px 0 70px
|
||||
|
||||
.mailpoet_save_wrapper
|
||||
float: right
|
||||
position: relative
|
||||
margin-right: 20px
|
||||
margin-bottom: 10px
|
||||
|
||||
.mailpoet_save_options
|
||||
border-radius(3px)
|
||||
|
||||
float: left
|
||||
position: absolute
|
||||
right: 0
|
||||
z-index: 1000
|
||||
overflow: hidden
|
||||
margin: 5px 0 0 0
|
||||
margin: 5px 0
|
||||
clear: both
|
||||
vertical-align: top
|
||||
white-space: nowrap
|
||||
|
||||
background: $white-color
|
||||
border: 1px solid $content-border-color
|
||||
@ -33,23 +41,19 @@
|
||||
.mailpoet_save_show_options
|
||||
padding: 6px 3px 4px
|
||||
|
||||
&.mailpoet_save_show_options_active
|
||||
.mailpoet_save_show_options_icon::before
|
||||
content: '\f142'
|
||||
|
||||
.mailpoet_save_show_options_icon
|
||||
vertical-align: middle
|
||||
|
||||
&::before
|
||||
content: '\f140'
|
||||
|
||||
.mailpoet_save_as_template_container,
|
||||
.mailpoet_export_template_container
|
||||
border-radius(3px)
|
||||
display: inline-block
|
||||
position: absolute
|
||||
right: 0
|
||||
z-index: 1000
|
||||
clear: both
|
||||
|
||||
margin-top: 5px
|
||||
margin: 5px 0
|
||||
padding: 0 10px
|
||||
background-color: $white-color
|
||||
border: 1px solid $structure-border-color
|
||||
@ -61,7 +65,41 @@
|
||||
.mailpoet_editor_last_saved
|
||||
color: $primary-inactive-color
|
||||
font-size: 0.9em
|
||||
display: inline
|
||||
position: absolute
|
||||
right: 0
|
||||
margin-top: 10px
|
||||
|
||||
.mailpoet_save_error
|
||||
color: $error-text-color
|
||||
|
||||
.mailpoet_save_dropdown_down
|
||||
.mailpoet_save_options,
|
||||
.mailpoet_save_as_template_container,
|
||||
.mailpoet_export_template_container
|
||||
top: 100%
|
||||
bottom: auto
|
||||
|
||||
.mailpoet_save_show_options
|
||||
&.mailpoet_save_show_options_active
|
||||
.mailpoet_save_show_options_icon::before
|
||||
content: '\f142'
|
||||
|
||||
.mailpoet_save_show_options_icon
|
||||
&::before
|
||||
content: '\f140'
|
||||
|
||||
.mailpoet_save_dropdown_up
|
||||
.mailpoet_save_options,
|
||||
.mailpoet_save_as_template_container,
|
||||
.mailpoet_export_template_container
|
||||
top: auto
|
||||
bottom: 100%
|
||||
|
||||
.mailpoet_save_show_options
|
||||
&.mailpoet_save_show_options_active
|
||||
.mailpoet_save_show_options_icon::before
|
||||
content: '\f140'
|
||||
|
||||
.mailpoet_save_show_options_icon
|
||||
&::before
|
||||
content: '\f142'
|
||||
|
@ -14,7 +14,7 @@
|
||||
cursor: pointer
|
||||
|
||||
.mailpoet_automated_latest_content_block_posts
|
||||
overflow: auto
|
||||
overflow: hidden
|
||||
pointer-events: none
|
||||
|
||||
& > .mailpoet_block
|
||||
|
@ -21,10 +21,14 @@ $block-text-line-height = $text-line-height
|
||||
left: 0
|
||||
pointer-events: none
|
||||
border: 1px solid $transparent-color
|
||||
-webkit-transition: 0.3s;
|
||||
transition: 0.3s;
|
||||
|
||||
&:hover > .mailpoet_block_highlight
|
||||
&.mailpoet_highlight > .mailpoet_block_highlight
|
||||
border: 1px dashed $block-hover-highlight-color
|
||||
|
||||
&.mailpoet_highlight > .mailpoet_block_highlight
|
||||
border: 1px dashed $block-hover-highlight-color !important
|
||||
|
||||
.mailpoet_content
|
||||
position: relative
|
||||
|
@ -52,7 +52,9 @@ $three-column-width = ($newsletter-width / 3) - (2 * $column-margin)
|
||||
width: $column-margin + $one-column-width + $column-margin
|
||||
|
||||
// More than one column
|
||||
& > .mailpoet_container_block > .mailpoet_container > .mailpoet_container_block > .mailpoet_container_horizontal
|
||||
& > .mailpoet_container_block > .mailpoet_container > .mailpoet_container_block > .mailpoet_container_horizontal,
|
||||
& > .mailpoet_container_block > .mailpoet_container > .mailpoet_posts_block > .mailpoet_posts_container > .mailpoet_container_block > .mailpoet_container > .mailpoet_container_block > .mailpoet_container_horizontal,
|
||||
& > .mailpoet_container_block > .mailpoet_container .mailpoet_automated_latest_content_block_posts .mailpoet_container_horizontal
|
||||
|
||||
// Column number detection technique found here:
|
||||
// http://stackoverflow.com/questions/8720931/can-css-detect-the-number-of-children-an-element-has
|
||||
|
@ -3,14 +3,19 @@
|
||||
img
|
||||
vertical-align: bottom
|
||||
max-width: 100%
|
||||
width: auto
|
||||
height: auto
|
||||
|
||||
&.mailpoet_full_image
|
||||
padding-left: 0
|
||||
padding-right: 0
|
||||
margin: auto
|
||||
margin-bottom: 0
|
||||
|
||||
.mailpoet_content a:hover
|
||||
cursor: all-scroll
|
||||
.mailpoet_content
|
||||
margin: auto
|
||||
max-width: 100%
|
||||
min-width: 36px
|
||||
|
||||
a:hover
|
||||
cursor: all-scroll
|
||||
|
||||
|
@ -54,3 +54,6 @@
|
||||
|
||||
.mailpoet_post_selection_loading
|
||||
color: #999
|
||||
|
||||
.mailpoet_posts_container > .mailpoet_droppable_block
|
||||
width: 100%
|
||||
|
@ -8,6 +8,8 @@ $content-border-color = $structure-border-color
|
||||
#mailpoet_editor_heading
|
||||
padding-left: 15px
|
||||
margin-left: 2px
|
||||
margin-bottom: 13px
|
||||
clear: both
|
||||
|
||||
#mailpoet_editor_main_wrapper
|
||||
border: 1px solid $content-border-color
|
||||
|
@ -131,6 +131,7 @@ body
|
||||
.notice
|
||||
.update-nag
|
||||
margin-left: 2px + 15px !important
|
||||
margin-right: 20px !important
|
||||
|
||||
/* Make a button group */
|
||||
.mailpoet_button_group
|
||||
|
@ -2,7 +2,7 @@ animation-slide-open-downwards($max-height = 2000px)
|
||||
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
|
||||
max-height: $max-height
|
||||
opacity: 1
|
||||
overflow-y: hidden
|
||||
overflow-y: inherit
|
||||
|
||||
&.mailpoet_closed
|
||||
max-height: 0px
|
||||
|
12
assets/css/src/newsletter_templates.styl
Normal file
@ -0,0 +1,12 @@
|
||||
@require 'newsletter_editor/variables'
|
||||
|
||||
.mailpoet_template_iframe
|
||||
position: absolute
|
||||
z-index: -9999
|
||||
top: 0
|
||||
left: 0
|
||||
width: $newsletter-width
|
||||
max-width: $newsletter-width
|
||||
|
||||
.newsletter-templates-feedback
|
||||
margin: 0 10px
|
@ -1,9 +1,3 @@
|
||||
.mailpoet_notice
|
||||
position: relative
|
||||
|
||||
.mailpoet_notice_close
|
||||
position: absolute
|
||||
right: 0.5em
|
||||
top: 0.5em
|
||||
color: #999
|
||||
text-decoration: none !important
|
||||
clear: both
|
||||
|
@ -46,12 +46,17 @@ Please add custom styles to pages_custom.styl
|
||||
margin-right: auto
|
||||
|
||||
h1
|
||||
margin: 0.2em 200px 0 0
|
||||
margin: 1em 0 0.5em 0
|
||||
padding: 0
|
||||
color: #32373c
|
||||
line-height: 1.2em
|
||||
font-size: 2.8em
|
||||
font-weight: 400
|
||||
text-align: center
|
||||
|
||||
h1.welcome
|
||||
margin-right: 200px
|
||||
text-align: left
|
||||
|
||||
h2
|
||||
margin: 40px 0 .6em
|
||||
|
@ -33,3 +33,6 @@ Custom styles for MailPoet pages.
|
||||
|
||||
p.top-space-triple
|
||||
margin-top: 3em
|
||||
|
||||
p.mailpoet-top-text
|
||||
margin-right: 0
|
||||
|
@ -28,3 +28,36 @@
|
||||
.mailpoet_progress_bar
|
||||
background-color: hsla(191, 78%, 80%, 1)
|
||||
background-image: linear-gradient(top, hsla(191, 78%, 80%, 1), hsla(191, 76%, 67%, 1))
|
||||
|
||||
.mailpoet_stepped_progress_bar
|
||||
margin: auto
|
||||
width: 400px
|
||||
&:before
|
||||
position: relative
|
||||
top: 9px
|
||||
content: ""
|
||||
display: block
|
||||
height: 2px
|
||||
width: 100%
|
||||
border-radius: 2px
|
||||
background-color: #d8d8d8
|
||||
margin: auto
|
||||
|
||||
.mailpoet_stepped_progress_bar_step
|
||||
display: inline-block
|
||||
&:before
|
||||
position: relative
|
||||
content: ""
|
||||
display: block
|
||||
height: 14px
|
||||
width: 14px
|
||||
border-radius: 14px
|
||||
background-color: #d8d8d8
|
||||
margin: auto
|
||||
&.active
|
||||
&:before
|
||||
background-color: #979797
|
||||
|
||||
@media screen and (max-width 520px)
|
||||
.mailpoet_stepped_progress_bar
|
||||
width: 360px
|
||||
|
@ -19,13 +19,15 @@
|
||||
flex-direction column
|
||||
flex-basis 0
|
||||
margin 0 25px 25px 0
|
||||
border 1px solid #dedede
|
||||
background-color #fff
|
||||
border 2px solid #dcdcdc
|
||||
max-width 500px
|
||||
.mailpoet_sending_method_description
|
||||
padding: 25px
|
||||
flex-grow 1
|
||||
flex-shrink 0
|
||||
&:hover:not(.mailpoet_active)
|
||||
border-color #c5c5c5
|
||||
> li:last-child
|
||||
margin-right 0
|
||||
h3
|
||||
@ -37,22 +39,36 @@
|
||||
.mailpoet_status
|
||||
display flex
|
||||
flex-direction row
|
||||
justify-content space-between
|
||||
justify-content flex-end
|
||||
align-items center
|
||||
background-color #2f2f2f
|
||||
background-color #dcdcdc
|
||||
color #fff
|
||||
text-overflow ellipsis
|
||||
padding 15px
|
||||
min-height 2em
|
||||
padding 1em
|
||||
span
|
||||
visibility hidden
|
||||
font-weight bold
|
||||
div
|
||||
margin-left 1em
|
||||
.mailpoet_active
|
||||
border 2px solid #088b00
|
||||
&.mailpoet_invalid_key
|
||||
border 2px solid #dc3232
|
||||
.mailpoet_status
|
||||
background-color #088b00
|
||||
&.mailpoet_invalid_key
|
||||
background-color #dc3232
|
||||
.mailpoet_actions
|
||||
color white
|
||||
a:not(.button-primary)
|
||||
color white
|
||||
span
|
||||
visibility visible
|
||||
#mailpoet_mta_activate
|
||||
visibility hidden
|
||||
.mailpoet_actions
|
||||
color initial
|
||||
|
||||
.tooltip.dashicons.dashicons-editor-help
|
||||
line-height: 1.4
|
||||
@ -62,6 +78,15 @@
|
||||
margin-bottom: 2em
|
||||
margin-top: 2em
|
||||
|
||||
.sending-free-plan-button
|
||||
background: #FF5301
|
||||
border-color: #e64c03
|
||||
text-shadow: 0 -1px 1px #e64c03
|
||||
box-shadow: 0 1px 0 #e64c03
|
||||
margin: 10px 0
|
||||
strong
|
||||
text-transform: uppercase
|
||||
|
||||
.mailpoet_success_item::before
|
||||
content '✔ '
|
||||
|
||||
|
118
assets/css/src/welcome_wizard.styl
Normal file
@ -0,0 +1,118 @@
|
||||
.mailpoet_welcome_wizard_centered_column
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
.mailpoet_welcome_wizard_header img
|
||||
margin-bottom: 20px
|
||||
|
||||
.mailpoet_welcome_wizard_steps
|
||||
padding-top: 20px
|
||||
width: 100%
|
||||
|
||||
.mailpoet_welcome_wizard_step_content
|
||||
margin-top: 40px
|
||||
max-width: 620px
|
||||
min-height: 35%vh;
|
||||
h1
|
||||
font-weight: 400
|
||||
padding-bottom: 10px
|
||||
text-align: center
|
||||
p
|
||||
font-weight: normal
|
||||
font-size: 15px
|
||||
line-height: 22px
|
||||
color: #595c65
|
||||
text-align: center
|
||||
margin: 10px 0;
|
||||
|
||||
#mailpoet_sender_form
|
||||
margin-top: 30px
|
||||
width: 330px
|
||||
label
|
||||
display: inline-block
|
||||
font-size: 15px
|
||||
margin-bottom: 20px
|
||||
input[type="text"]
|
||||
margin-top: 10px
|
||||
font-size: 15px
|
||||
width: 328px
|
||||
height: 30px
|
||||
input[type="submit"]
|
||||
margin: 50px 0 25px 0
|
||||
a
|
||||
font-size: 12px
|
||||
color: #595c65
|
||||
|
||||
.mailpoet_sender_form_loading
|
||||
opacity: .5
|
||||
|
||||
.mailpoet_welcome_wizard_help_info_block
|
||||
padding-left: 180px
|
||||
position: relative
|
||||
margin-bottom: 10px
|
||||
p
|
||||
text-align: left
|
||||
|
||||
.mailpoet_welcome_wizard_support_button
|
||||
background: #32a8d9 url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAA3lBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////ymXGKAAAASXRSTlMAAQIFBwoPEBIUFRscHR8jKCkqLC0/Q0RGSEpNUFFXYHFzeXp9gIKFjKCjpamwvL7Aw8bIy8zN0NLT1dja3ODk5ujq7PT19/3+6Nyy9QAAAklJREFUeAGl1/1W4jwQBvAHBapi0erLK7XsKmLxAyp+SBEVAUXluf8b2ibHnqUk0LT7+3+anJlpMoGe1Wh3B9MZOZsOuu2GBWN2q//NhO9+y4YJtzenxrznIs1xyJXCY6xTDfhj3Gl6jm1ZtuM1O2P+CKpY6eST0sh3kOD4I0qfJ9ArXVG6cwtQFNw7SlclaGzdU3ipY4X6C4X7LSh2n2VN/CJWKvqygs+7yroydlLDWrWJjF5auyT3PKwgRWUod17CIpmrhzJSlR9k1hI1kuuWYaAs116oWFXUd1KBkcpE1LuKWCDyXIOhmsh5gB/HjPgw5jMS93koeqMIY0XRLSEkl5E6Mqgz4kLoiX5G7PKLy74usUT0eQ8Rex5/RviPOodIcknObQAtkqNCtuDCiGQLQF+mOtu2ZcL7gCWq5uCvTWvZJhSO6AwLDZJjZDYm2UCbZAeZdUi20SXZRGZNkl0MSHrIzCM5wFTmKzOH5BQzkjbWlkpXMZvkDIxYgNIkKb1iMfJPwXm3vc2ImjC1w3SNdkDyPW+pXJJPeZvklGSQtz2vSV7k/THeSDaUX9LMPiM7usNALY7ijGSYdgwdQqvwSvJcPQCNgo8Y2VOPXqNt35C81R36m2pPaRf28l03eCQ53Mh30ZXkwrmu2Ph3VC9382BlrMgXrA405sHqKGUerA5x5sHq+GgebDy4/v/IBZlG5qMbJpgP6/tnr0z6MHkmbB+4p9dvXPbxO+sDZehZsYxPI956G/keZQzP95BGfQ7y/Sm4+LUDrT+vKBtiP2phEAAAAABJRU5ErkJggg==') center center no-repeat
|
||||
background-size: 30px
|
||||
display: inline-block
|
||||
height: 54px
|
||||
width: 54px
|
||||
border-radius: 28px
|
||||
position: absolute
|
||||
top: 10px
|
||||
left: 70px
|
||||
box-shadow: 2px 2px 8px #666;
|
||||
transform: scale(0.8)
|
||||
|
||||
.mailpoet_welcome_wizard_video_badge
|
||||
cursor: default
|
||||
position: absolute
|
||||
top: 20px
|
||||
left: 30px
|
||||
line-height: 1.5em
|
||||
padding-bottom: 0
|
||||
.dashicons
|
||||
line-height: 1em
|
||||
&:hover
|
||||
background: $video-guide-badge-color
|
||||
|
||||
.mailpoet_welcome_wizard_mail_icon
|
||||
background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAABrCAYAAADw4IlaAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABT9JREFUeNrsnTFSGz0YhhWSgpJJkS4z9tBS4JbKOUHwDfAJYp8AcwLDCSBdOpwT4IoWCoo0jJ1JlybcIPmEvw0LwyTG1iftrp5nRjGh+PktHmv16t3YzgEA5Mqrp9/Y297pysMnGftMDwRmIuPk8vZm+qyAIt9AHsbMExjTFwnPHgko8vkV75y5gUj0RMLJRukbrHwQk/HfFVD3fRfMCUSmU6yALeYCErCFgJCUDaYAEBAQEAABAQEBYvImwc+8k3F0eXtzzPRXC61iD2VsNVlA/+TG8mQ/yuNQRLzmV59cvF15OJWxm9MluCvjSp78CAWSibclw1diVynkq8oe8FAmYaZ1IMSTr6viDXLbAz5HS8aFTMqZXpbvUMRu1dPLbSXu91x1BZz6IKGBIiQHMmZ6axjYhIyZgXzeg6F6EecSLKuU37t1VvmhS4SUc5ksvyK20CaIeC0/n25xC1TohOvvcu6seqqx1h5Qfuhcxgf5smewGhJSwsg30lUv9B577hY3lfoxTxpC/J2t8tCWcWawGvqQcqVHBfCCkOHDnVuc64XmWFe9SWVCiAaHvjzpz7rJDXn53NXV0D/xI0LKf0PGoVG6vdaQGGzbFfwYxv/PyWhrSAnNQEUkpDwv375ebkPLV7RXnZDymQgYIaS0NKSc66sd8R5CxrlByJjq5dZkL256EO1rNg0pQ4OQcv9q16OF3I9WrgxCxv2Wyv/+1gkZSQUsiXisIWUS+D9d9MoXuYUU/3x9ODM6WvFhsl38211LolVxPjj4yO4WRzahX1Fdl8mRjXF/638vfsXrxwp60btgje4djfKhaXSvbNzfHulebxrzOSXpgvXVNSwd2YR8JfuQ0qhe2bi/nbqEt8UlvRtGQ0rH0Sv/S74DZ9jfashIdk9mJW7Jp1f+59HKqatYf9s4AVXColfuu8x7ZcP+1s/r2v1tIwUsiXh/BOAy7JW1v/Uhw6q/bYfob2sfQpYMKdn0ynXrbxu9Aj4Rcap7Q8teuZtYPqv+1jmj/jYbAYvV0LhXvkjRK2vI8N2tVX/btupvsxKwJGJjeuVSf2txtGLe32YpYEnEY10NLXvllpF4lv3txEXqb7MWUCWcG/fKs5BHNtrfjpxtf9urY+tT6/eGqUOvXOpvLY5WkvS3IXnjak6pV/6qlzaLXvnFRzbG/e217vVq/7YmjXl3LP2nAFa98sC9oFeO0N92mvKeOo17e7aUvXIu/S0CLhdSrHvlwRP5Ri6T/hYBlxfRH0lY9cpj7ZUPcutvCSEvDymWvfKpUcgY1jndsgI+E1KcXa8cksr3twi4xmpoGFLWZepq0t8i4PoiWvbKq4SMWvW3CBhORKteeVlq2d8SQsJK6Fednh4yj12cz82b66o3zX3++ZyQBxEte+VHIcPVvL9lBTQMKc6uV25Mf8sKaC9iyF65cf0tAsYTceTWO7JpZH/LJTh+SPmgd7gseydzcbQyYQZZAUOJeOaW65Ub39+yAqYNKUWv/Mkt7nzZ0hXPX6ZPSLcIGCWkuOpVeVyCARAQEBAAAQEBARAQEBAAAQEBARAQEBAAAQEBARAQEBAQEAABAQEBEBAQEAABAQEBEBAQEAABAQEBEBAQEAABAQEBEBAQEAABoX6s+g6p3b3tnd9MH7ACAgICICAgIAACQi0FnDMVkFJAPsURUnD3qvhqb3tn5uJ8WjjA/VX38vamXd4DDpkTiEjf//G6+NuPXz+/vX/77rt8uc/cgLV8xaeJvi5/VyS8Fgn9x9Vv6uV4k7mCUPs9GV9k9PhEUagMfwQYAFppkb3J5HUdAAAAAElFTkSuQmCC') center center no-repeat
|
||||
background-size: 40px 27px
|
||||
display: inline-block
|
||||
width: 40px
|
||||
height: 27px
|
||||
position: absolute
|
||||
top: 50px
|
||||
left: 75px
|
||||
|
||||
@media screen and (max-width 520px)
|
||||
.mailpoet_welcome_wizard_help_info_block
|
||||
padding-left: 150px
|
||||
.mailpoet_welcome_wizard_support_button
|
||||
left: 40px
|
||||
.mailpoet_welcome_wizard_video_badge
|
||||
left: 10px
|
||||
.mailpoet_welcome_wizard_mail_icon
|
||||
left: 50px
|
||||
|
||||
.mailpoet_welcome_wizard_step_controls
|
||||
margin-top: 50px
|
||||
.button
|
||||
margin:0 10px
|
||||
|
||||
.mailpoet_welcome_wizard_woo_screenshot
|
||||
margin-top: 30px
|
||||
width: 400px
|
||||
|
||||
@media screen and (max-width 520px)
|
||||
.mailpoet_welcome_wizard_woo_screenshot
|
||||
width: 340px
|
||||
|
||||
.welcome_wizard_video
|
||||
position: absolute
|
||||
top: -1000px
|
11
assets/fonts/mailpoet.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Generated by IcoMoon</metadata>
|
||||
<defs>
|
||||
<font id="mailpoet" horiz-adv-x="1024">
|
||||
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||
<missing-glyph horiz-adv-x="1024" />
|
||||
<glyph unicode=" " horiz-adv-x="0" d="" />
|
||||
<glyph unicode="" glyph-name="optimised" horiz-adv-x="972" d="M230.188 949.695c-21.982-3.361-41.376-14.741-48.618-28.188l-5.948-11.637 0.517-265.588c0.776-263.779 0.776-265.847 6.206-273.088 11.12-15.258 40.601-22.24 68.79-16.551 16.551 3.621 25.861 9.827 32.584 21.723 3.879 7.499 4.397 23.791 5.689 185.161l1.293 176.628 60.514-161.111c33.102-88.702 63.875-168.612 68.013-177.404 13.707-29.481 35.687-41.376 72.151-39.049 21.206 1.293 39.308 9.827 47.584 22.499 3.361 5.172 35.947 91.806 72.41 192.662s67.237 185.42 68.53 187.49c1.551 2.587 2.587-69.307 2.587-185.937v-190.335l5.948-11.379c8.533-16.809 20.172-21.982 49.652-21.982 27.929 0.259 39.825 4.914 49.911 20.172l6.982 10.603v530.401l-5.689 9.31c-12.671 20.43-50.17 31.033-91.806 25.601-34.394-4.138-53.79-16.292-66.72-41.118-2.587-5.172-35.947-101.89-73.961-214.902s-69.824-206.367-70.6-207.403c-1.034-1.034-32.326 86.115-69.824 193.954-37.757 107.581-71.892 205.075-76.030 216.453-10.086 26.118-25.601 42.929-45.514 48.877-17.326 5.172-46.807 6.982-64.652 4.138zM54.854 243.443c-20.172-3.879-43.963-19.136-51.204-33.619-6.206-11.379-4.914-32.843 2.587-47.841 23.533-46.807 71.634-86.892 126.717-104.736 17.068-5.689 23.274-5.948 120.252-7.499 97.235-1.551 102.926-1.81 116.373-7.241 29.739-11.896 51.204-35.687 61.807-68.013 3.621-11.12 13.964-21.206 25.861-25.344 4.914-1.551 18.361-2.844 29.739-2.844 16.809 0 23.533 1.293 32.584 5.689 11.896 6.206 13.964 9.31 26.895 38.791 11.896 27.671 39.567 49.652 70.858 56.117 8.533 1.81 47.067 2.844 100.856 2.844 99.563 0 113.786 2.068 151.801 20.689 49.652 24.567 96.978 77.84 101.373 113.529 3.104 26.118-17.326 49.394-51.204 58.187-25.601 6.465-41.635-0.517-54.825-24.050-11.12-19.655-29.998-38.015-47.841-46.29l-14.741-6.982-99.563-1.551c-90.77-1.293-101.373-2.068-120.252-6.982-27.154-7.499-58.444-23.016-80.427-40.084l-17.844-13.964-16.809 13.964c-20.689 16.809-51.462 32.584-78.875 39.825-19.136 5.172-28.705 5.948-120.252 7.241l-99.563 1.551-15.775 7.241c-18.102 8.533-32.584 21.982-48.36 45.773-16.034 24.567-26.895 29.998-50.17 25.601z" />
|
||||
</font></defs></svg>
|
After Width: | Height: | Size: 2.4 KiB |
BIN
assets/fonts/mailpoet.ttf
Normal file
BIN
assets/fonts/mailpoet.woff
Normal file
Before Width: | Height: | Size: 779 B After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 3.3 KiB |
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="47.002px" height="38.003px" viewBox="0 0 47.002 38.003" enable-background="new 0 0 47.002 38.003" xml:space="preserve">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#565656" d="M46.328,36.365c-1.188,1.725-3.553,2.158-5.273,0.962L25.479,26.52
|
||||
c-1.104-0.763-1.674-2.007-1.631-3.257c-2.006,2.157-4.642,3.606-7.594,4.145c-3.626,0.663-7.288-0.13-10.311-2.227
|
||||
c-3.024-2.098-5.054-5.253-5.714-8.887c-0.661-3.636,0.127-7.31,2.221-10.344c4.325-6.264,12.927-7.834,19.177-3.5
|
||||
c5.672,3.938,7.486,11.412,4.537,17.443c1.152-0.486,2.519-0.392,3.627,0.377l15.58,10.808C47.09,32.274,47.52,34.641,46.328,36.365
|
||||
z M23.235,12.09c-0.459-2.534-1.874-4.734-3.982-6.196C14.897,2.87,8.896,3.963,5.878,8.331c-3.014,4.373-1.922,10.388,2.435,13.408
|
||||
c4.356,3.025,10.356,1.932,13.374-2.438C23.146,17.187,23.696,14.625,23.235,12.09z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 814 B After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.9 KiB |
BIN
assets/img/welcome_wizard/mailpoet-logo.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/img/welcome_wizard/woocommerce-auto-emails-tilted.png
Normal file
After Width: | Height: | Size: 40 KiB |
@ -6,10 +6,25 @@ window.mixpanelTrackingId = "8cce373b255e5a76fb22d57b85db0c92";
|
||||
|
||||
if (mailpoet_analytics_enabled) {
|
||||
|
||||
mixpanel.init(window.mixpanelTrackingId);
|
||||
mixpanel.init(window.mixpanelTrackingId, {
|
||||
loaded: function(mixpanel) {
|
||||
// used in lib/Analytics/Analytics.php
|
||||
document.cookie = "mixpanel_distinct_id=" + mixpanel.get_distinct_id();
|
||||
}
|
||||
});
|
||||
|
||||
mixpanel.register({'Platform': 'Plugin'});
|
||||
|
||||
if(typeof window.mailpoet_analytics_public_id === 'string' && window.mailpoet_analytics_public_id.length > 0) {
|
||||
if(window.mailpoet_analytics_new_public_id === true) {
|
||||
mixpanel.alias(window.mailpoet_analytics_public_id);
|
||||
} else {
|
||||
mixpanel.identify(window.mailpoet_analytics_public_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (mailpoet_analytics_data != null) {
|
||||
mixpanel.track('MailPoet 3', mailpoet_analytics_data);
|
||||
mixpanel.people.set(mailpoet_analytics_data);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
define('admin', [
|
||||
'jquery'
|
||||
],
|
||||
function(jQuery) {
|
||||
jQuery(function($) {
|
||||
// dom ready
|
||||
$(function() {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
define('admin', [
|
||||
'jquery'
|
||||
],
|
||||
function admin(jQuery) {
|
||||
jQuery(function adminDomReady($) {
|
||||
// dom ready
|
||||
$(function domReady() {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@ -2,85 +2,86 @@ function requestFailed(errorMessage, xhr) {
|
||||
if (xhr.responseJSON) {
|
||||
return xhr.responseJSON;
|
||||
}
|
||||
var message = errorMessage.replace('%d', xhr.status);
|
||||
return {
|
||||
errors: [
|
||||
{
|
||||
message: message
|
||||
message: errorMessage.replace('%d', xhr.status)
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
define('ajax', ['mailpoet', 'jquery', 'underscore'], function(mp, jQuery, _) {
|
||||
define('ajax', ['mailpoet', 'jquery', 'underscore'], function ajax(mp, jQuery, _) {
|
||||
var MailPoet = mp;
|
||||
|
||||
MailPoet.Ajax = {
|
||||
version: 0.5,
|
||||
options: {},
|
||||
defaults: {
|
||||
url: null,
|
||||
api_version: null,
|
||||
endpoint: null,
|
||||
action: null,
|
||||
token: null,
|
||||
data: {}
|
||||
},
|
||||
post: function(options) {
|
||||
return this.request('post', options);
|
||||
},
|
||||
init: function(options) {
|
||||
// merge options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
version: 0.5,
|
||||
options: {},
|
||||
defaults: {
|
||||
url: null,
|
||||
api_version: null,
|
||||
endpoint: null,
|
||||
action: null,
|
||||
token: null,
|
||||
data: {}
|
||||
},
|
||||
post: function post(options) {
|
||||
return this.request('post', options);
|
||||
},
|
||||
init: function init(options) {
|
||||
// merge options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
|
||||
// set default url
|
||||
if(this.options.url === null) {
|
||||
this.options.url = ajaxurl;
|
||||
}
|
||||
|
||||
// set default token
|
||||
if(this.options.token === null) {
|
||||
this.options.token = window.mailpoet_token;
|
||||
}
|
||||
},
|
||||
getParams: function() {
|
||||
return {
|
||||
action: 'mailpoet',
|
||||
api_version: this.options.api_version,
|
||||
token: this.options.token,
|
||||
endpoint: this.options.endpoint,
|
||||
method: this.options.action,
|
||||
data: this.options.data || {}
|
||||
};
|
||||
},
|
||||
request: function(method, options) {
|
||||
// set options
|
||||
this.init(options);
|
||||
|
||||
// set request params
|
||||
var params = this.getParams();
|
||||
|
||||
// remove null values from the data object
|
||||
if (_.isObject(params.data)) {
|
||||
params.data = _.pick(params.data, function(value) {
|
||||
return (value !== null);
|
||||
});
|
||||
}
|
||||
|
||||
// ajax request
|
||||
var deferred = jQuery.post(
|
||||
this.options.url,
|
||||
params,
|
||||
null,
|
||||
'json'
|
||||
).then(function(data) {
|
||||
return data;
|
||||
}, _.partial(requestFailed, MailPoet.I18n.t('ajaxFailedErrorMessage')));
|
||||
|
||||
// clear options
|
||||
this.options = {};
|
||||
|
||||
return deferred;
|
||||
// set default url
|
||||
if (this.options.url === null) {
|
||||
this.options.url = window.ajaxurl;
|
||||
}
|
||||
|
||||
// set default token
|
||||
if (this.options.token === null) {
|
||||
this.options.token = window.mailpoet_token;
|
||||
}
|
||||
},
|
||||
getParams: function getParams() {
|
||||
return {
|
||||
action: 'mailpoet',
|
||||
api_version: this.options.api_version,
|
||||
token: this.options.token,
|
||||
endpoint: this.options.endpoint,
|
||||
method: this.options.action,
|
||||
data: this.options.data || {}
|
||||
};
|
||||
},
|
||||
request: function request(method, options) {
|
||||
var params;
|
||||
var deferred;
|
||||
// set options
|
||||
this.init(options);
|
||||
|
||||
// set request params
|
||||
params = this.getParams();
|
||||
|
||||
// remove null values from the data object
|
||||
if (_.isObject(params.data)) {
|
||||
params.data = _.pick(params.data, function isNotNull(value) {
|
||||
return (value !== null);
|
||||
});
|
||||
}
|
||||
|
||||
// ajax request
|
||||
deferred = jQuery.post(
|
||||
this.options.url,
|
||||
params,
|
||||
null,
|
||||
'json'
|
||||
).then(function resultHandler(data) {
|
||||
return data;
|
||||
}, _.partial(requestFailed, MailPoet.I18n.t('ajaxFailedErrorMessage')));
|
||||
|
||||
// clear options
|
||||
this.options = {};
|
||||
|
||||
return deferred;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -17,7 +17,7 @@
|
||||
*/
|
||||
var eventsCache = [];
|
||||
|
||||
function track(name, data){
|
||||
function track(name, data) {
|
||||
if (typeof window.mixpanel.track !== 'function') {
|
||||
window.mixpanel.init(window.mixpanelTrackingId);
|
||||
}
|
||||
@ -31,27 +31,18 @@ function exportMixpanel(mp) {
|
||||
if (window.mailpoet_analytics_enabled) {
|
||||
MailPoet.trackEvent = track;
|
||||
} else {
|
||||
MailPoet.trackEvent = function () {};
|
||||
MailPoet.trackEvent = function emptyFunction() {};
|
||||
}
|
||||
}
|
||||
|
||||
function trackCachedEvents() {
|
||||
eventsCache.map(function (event) {
|
||||
eventsCache.forEach(function trackIfEnabled(event) {
|
||||
if (window.mailpoet_analytics_enabled || event.forced) {
|
||||
window.mixpanel.track(event.name, event.data)
|
||||
window.mixpanel.track(event.name, event.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initializeMixpanelWhenLoaded() {
|
||||
if (typeof window.mixpanel === 'object') {
|
||||
exportMixpanel(MailPoet);
|
||||
trackCachedEvents();
|
||||
} else {
|
||||
setTimeout(initializeMixpanelWhenLoaded, 100);
|
||||
}
|
||||
}
|
||||
|
||||
function cacheEvent(forced, name, data) {
|
||||
eventsCache.push({
|
||||
name: name,
|
||||
@ -62,9 +53,18 @@ function cacheEvent(forced, name, data) {
|
||||
|
||||
define(
|
||||
['mailpoet', 'underscore'],
|
||||
function(mp, _) {
|
||||
function analyticsEvent(mp, _) {
|
||||
var MailPoet = mp;
|
||||
|
||||
function initializeMixpanelWhenLoaded() {
|
||||
if (typeof window.mixpanel === 'object') {
|
||||
exportMixpanel(MailPoet);
|
||||
trackCachedEvents();
|
||||
} else {
|
||||
setTimeout(initializeMixpanelWhenLoaded, 100);
|
||||
}
|
||||
}
|
||||
|
||||
MailPoet.trackEvent = _.partial(cacheEvent, false);
|
||||
MailPoet.forceTrackEvent = _.partial(cacheEvent, true);
|
||||
|
||||
|
31
assets/js/src/common/key_value_table.jsx
Normal file
@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
|
||||
const KeyValueTable = props => (
|
||||
<table className={'widefat fixed'} style={{ maxWidth: props.max_width }}>
|
||||
<tbody>
|
||||
{props.children.map(row => (
|
||||
<tr key={`row_${row.key}`}>
|
||||
<td className={'row-title'}>{ row.key }</td><td>{ row.value }</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
KeyValueTable.propTypes = {
|
||||
max_width: React.PropTypes.string,
|
||||
children: React.PropTypes.arrayOf(React.PropTypes.shape({
|
||||
key: React.PropTypes.string.isRequired,
|
||||
value: React.PropTypes.oneOfType([
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.number,
|
||||
React.PropTypes.element,
|
||||
]).isRequired,
|
||||
})).isRequired,
|
||||
};
|
||||
|
||||
KeyValueTable.defaultProps = {
|
||||
max_width: 'auto',
|
||||
};
|
||||
|
||||
module.exports = KeyValueTable;
|
16
assets/js/src/common/loading.jsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
class Loading extends React.Component {
|
||||
componentWillMount() {
|
||||
MailPoet.Modal.loading(true);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
MailPoet.Modal.loading(false);
|
||||
}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default Loading;
|
26
assets/js/src/common/print_boolean.jsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
const PrintBoolean = props => (
|
||||
<span>
|
||||
{(props.children === true && props.truthy) ||
|
||||
(props.children === false && props.falsy) ||
|
||||
(props.unknown)}
|
||||
</span>
|
||||
);
|
||||
|
||||
PrintBoolean.propTypes = {
|
||||
truthy: React.PropTypes.string,
|
||||
falsy: React.PropTypes.string,
|
||||
unknown: React.PropTypes.string,
|
||||
children: React.PropTypes.bool,
|
||||
};
|
||||
|
||||
PrintBoolean.defaultProps = {
|
||||
truthy: MailPoet.I18n.t('yes'),
|
||||
falsy: MailPoet.I18n.t('no'),
|
||||
unknown: MailPoet.I18n.t('unknown'),
|
||||
children: null,
|
||||
};
|
||||
|
||||
module.exports = PrintBoolean;
|
27
assets/js/src/common/stepped_progess_bar.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
|
||||
const SteppedProgressBar = (props) => {
|
||||
if (props.step > props.steps_count) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="mailpoet_stepped_progress_bar">
|
||||
{
|
||||
[...Array(props.steps_count).keys()].map(step => (
|
||||
<div
|
||||
className={`mailpoet_stepped_progress_bar_step ${(step < props.step ? 'active' : '')}`}
|
||||
key={`step_${step}`}
|
||||
style={{ width: `${Math.floor(100 / props.steps_count)}%` }}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
SteppedProgressBar.propTypes = {
|
||||
steps_count: React.PropTypes.number.isRequired,
|
||||
step: React.PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
module.exports = SteppedProgressBar;
|
76
assets/js/src/common/thumbnail.jsx
Normal file
@ -0,0 +1,76 @@
|
||||
import _ from 'underscore';
|
||||
import MailPoet from 'mailpoet';
|
||||
import html2canvas from 'html2canvas';
|
||||
|
||||
/**
|
||||
* Generates a thumbnail from a DOM element.
|
||||
*
|
||||
* @param {DOMElement} element
|
||||
* @return {Promise<String>} DataURL of the generated image.
|
||||
*/
|
||||
export const fromDom = element =>
|
||||
html2canvas(element, {
|
||||
logging: false,
|
||||
}).then(canvas => canvas.toDataURL('image/jpeg'));
|
||||
|
||||
/**
|
||||
* Generates a thumbnail from an URL.
|
||||
*
|
||||
* @param {String} url
|
||||
* @return {Promise<String>} DataURL of the generated image.
|
||||
*/
|
||||
export const fromUrl = url =>
|
||||
new Promise((resolve, reject) => {
|
||||
const iframe = document.createElement('iframe');
|
||||
const protocol = location.href.startsWith('https://') ? 'https:' : 'http:';
|
||||
iframe.src = protocol + url.replace(/^https?:/, '');
|
||||
iframe.style.opacity = 0;
|
||||
iframe.scrolling = 'no';
|
||||
iframe.onload = () => {
|
||||
fromDom(iframe.contentDocument.documentElement)
|
||||
.then((image) => {
|
||||
document.body.removeChild(iframe);
|
||||
resolve(image);
|
||||
})
|
||||
.catch(() => {
|
||||
document.body.removeChild(iframe);
|
||||
reject(MailPoet.I18n.t('errorWhileTakingScreenshot'));
|
||||
});
|
||||
};
|
||||
const onError = () => {
|
||||
document.body.removeChild(iframe);
|
||||
reject(MailPoet.I18n.t('errorWhileTakingScreenshot'));
|
||||
};
|
||||
iframe.onerror = onError;
|
||||
iframe.onError = onError;
|
||||
iframe.className = 'mailpoet_template_iframe';
|
||||
try {
|
||||
document.body.appendChild(iframe);
|
||||
} catch (err) {
|
||||
onError();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Generates a thumbnail from a newsletter's data.
|
||||
*
|
||||
* @param {Object} data
|
||||
* @return {Promise<String>} DataURL of the generated image.
|
||||
*/
|
||||
export const fromNewsletter = data =>
|
||||
new Promise((resolve, reject) => {
|
||||
const json = data;
|
||||
if (!_.isUndefined(json.body)) {
|
||||
json.body = JSON.stringify(json.body);
|
||||
}
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletters',
|
||||
action: 'showPreview',
|
||||
data: json,
|
||||
}).done(response => fromUrl(response.meta.preview_url)
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
).fail(response => reject(response.errors));
|
||||
});
|
||||
|
@ -3,167 +3,169 @@ define('date',
|
||||
'mailpoet',
|
||||
'jquery',
|
||||
'moment'
|
||||
], function(
|
||||
], function ( // eslint-disable-line func-names
|
||||
mp,
|
||||
jQuery,
|
||||
Moment
|
||||
) {
|
||||
'use strict';
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var MailPoet = mp;
|
||||
var MailPoet = mp;
|
||||
|
||||
MailPoet.Date = {
|
||||
version: 0.1,
|
||||
options: {},
|
||||
defaults: {
|
||||
offset: 0,
|
||||
format: 'F, d Y H:i:s'
|
||||
},
|
||||
init: function (opts) {
|
||||
var options = opts || {};
|
||||
MailPoet.Date = {
|
||||
version: 0.1,
|
||||
options: {},
|
||||
defaults: {
|
||||
offset: 0,
|
||||
format: 'F, d Y H:i:s'
|
||||
},
|
||||
init: function init(opts) {
|
||||
var options = opts || {};
|
||||
|
||||
// set UTC offset
|
||||
if (
|
||||
options.offset === undefined
|
||||
// set UTC offset
|
||||
if (
|
||||
options.offset === undefined
|
||||
&& window.mailpoet_date_offset !== undefined
|
||||
) {
|
||||
options.offset = window.mailpoet_date_offset;
|
||||
}
|
||||
// set date format
|
||||
if (
|
||||
options.format === undefined
|
||||
&& window.mailpoet_date_format !== undefined
|
||||
) {
|
||||
options.format = window.mailpoet_date_format;
|
||||
}
|
||||
// merge options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
|
||||
return this;
|
||||
},
|
||||
format: function(date, opts) {
|
||||
var options = opts || {};
|
||||
this.init(options);
|
||||
|
||||
var momentDate = Moment(date, this.convertFormat(options.parseFormat));
|
||||
if (options.offset === 0) momentDate = momentDate.utc();
|
||||
return momentDate.format(this.convertFormat(this.options.format));
|
||||
},
|
||||
toDate: function(date, opts) {
|
||||
var options = opts || {};
|
||||
this.init(options);
|
||||
|
||||
return Moment(date, this.convertFormat(options.parseFormat)).toDate();
|
||||
},
|
||||
short: function(date) {
|
||||
return this.format(date, {
|
||||
format: 'F, j Y'
|
||||
});
|
||||
},
|
||||
full: function(date) {
|
||||
return this.format(date, {
|
||||
format: 'F, j Y H:i:s'
|
||||
});
|
||||
},
|
||||
time: function(date) {
|
||||
return this.format(date, {
|
||||
format: 'H:i:s'
|
||||
});
|
||||
},
|
||||
convertFormat: function(format) {
|
||||
var format_mappings = {
|
||||
date: {
|
||||
d: 'DD',
|
||||
D: 'ddd',
|
||||
j: 'D',
|
||||
l: 'dddd',
|
||||
N: 'E',
|
||||
S: 'o',
|
||||
w: 'e',
|
||||
z: 'DDD',
|
||||
W: 'W',
|
||||
F: 'MMMM',
|
||||
m: 'MM',
|
||||
M: 'MMM',
|
||||
n: 'M',
|
||||
t: '', // no equivalent
|
||||
L: '', // no equivalent
|
||||
o: 'YYYY',
|
||||
Y: 'YYYY',
|
||||
y: 'YY',
|
||||
a: 'a',
|
||||
A: 'A',
|
||||
B: '', // no equivalent
|
||||
g: 'h',
|
||||
G: 'H',
|
||||
h: 'hh',
|
||||
H: 'HH',
|
||||
i: 'mm',
|
||||
s: 'ss',
|
||||
u: 'SSS',
|
||||
e: 'zz', // deprecated since version 1.6.0 of moment.js
|
||||
I: '', // no equivalent
|
||||
O: '', // no equivalent
|
||||
P: '', // no equivalent
|
||||
T: '', // no equivalent
|
||||
Z: '', // no equivalent
|
||||
c: '', // no equivalent
|
||||
r: '', // no equivalent
|
||||
U: 'X'
|
||||
},
|
||||
strftime: {
|
||||
a: 'ddd',
|
||||
A: 'dddd',
|
||||
b: 'MMM',
|
||||
B: 'MMMM',
|
||||
d: 'DD',
|
||||
e: 'D',
|
||||
F: 'YYYY-MM-DD',
|
||||
H: 'HH',
|
||||
I: 'hh',
|
||||
j: 'DDDD',
|
||||
k: 'H',
|
||||
l: 'h',
|
||||
m: 'MM',
|
||||
M: 'mm',
|
||||
p: 'A',
|
||||
S: 'ss',
|
||||
u: 'E',
|
||||
w: 'd',
|
||||
W: 'WW',
|
||||
y: 'YY',
|
||||
Y: 'YYYY',
|
||||
z: 'ZZ',
|
||||
Z: 'z'
|
||||
) {
|
||||
options.offset = window.mailpoet_date_offset;
|
||||
}
|
||||
};
|
||||
// set date format
|
||||
if (
|
||||
options.format === undefined
|
||||
&& window.mailpoet_date_format !== undefined
|
||||
) {
|
||||
options.format = window.mailpoet_date_format;
|
||||
}
|
||||
// merge options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
|
||||
if (!format || format.length <= 0) return format;
|
||||
return this;
|
||||
},
|
||||
format: function format(date, opts) {
|
||||
var options = opts || {};
|
||||
var momentDate;
|
||||
this.init(options);
|
||||
|
||||
var replacements = format_mappings['date'];
|
||||
momentDate = Moment(date, this.convertFormat(options.parseFormat));
|
||||
if (options.offset === 0) momentDate = momentDate.utc();
|
||||
return momentDate.format(this.convertFormat(this.options.format));
|
||||
},
|
||||
toDate: function toDate(date, opts) {
|
||||
var options = opts || {};
|
||||
this.init(options);
|
||||
|
||||
var convertedFormat = [];
|
||||
var escapeToken = false;
|
||||
return Moment(date, this.convertFormat(options.parseFormat)).toDate();
|
||||
},
|
||||
short: function short(date) {
|
||||
return this.format(date, {
|
||||
format: 'F, j Y'
|
||||
});
|
||||
},
|
||||
full: function full(date) {
|
||||
return this.format(date, {
|
||||
format: 'F, j Y H:i:s'
|
||||
});
|
||||
},
|
||||
time: function time(date) {
|
||||
return this.format(date, {
|
||||
format: 'H:i:s'
|
||||
});
|
||||
},
|
||||
convertFormat: function convertFormat(format) {
|
||||
var replacements;
|
||||
var convertedFormat;
|
||||
var escapeToken;
|
||||
var index;
|
||||
var token;
|
||||
var formatMappings = {
|
||||
date: {
|
||||
d: 'DD',
|
||||
D: 'ddd',
|
||||
j: 'D',
|
||||
l: 'dddd',
|
||||
N: 'E',
|
||||
S: 'o',
|
||||
w: 'e',
|
||||
z: 'DDD',
|
||||
W: 'W',
|
||||
F: 'MMMM',
|
||||
m: 'MM',
|
||||
M: 'MMM',
|
||||
n: 'M',
|
||||
t: '', // no equivalent
|
||||
L: '', // no equivalent
|
||||
o: 'YYYY',
|
||||
Y: 'YYYY',
|
||||
y: 'YY',
|
||||
a: 'a',
|
||||
A: 'A',
|
||||
B: '', // no equivalent
|
||||
g: 'h',
|
||||
G: 'H',
|
||||
h: 'hh',
|
||||
H: 'HH',
|
||||
i: 'mm',
|
||||
s: 'ss',
|
||||
u: 'SSS',
|
||||
e: 'zz', // deprecated since version 1.6.0 of moment.js
|
||||
I: '', // no equivalent
|
||||
O: '', // no equivalent
|
||||
P: '', // no equivalent
|
||||
T: '', // no equivalent
|
||||
Z: '', // no equivalent
|
||||
c: '', // no equivalent
|
||||
r: '', // no equivalent
|
||||
U: 'X'
|
||||
},
|
||||
strftime: {
|
||||
a: 'ddd',
|
||||
A: 'dddd',
|
||||
b: 'MMM',
|
||||
B: 'MMMM',
|
||||
d: 'DD',
|
||||
e: 'D',
|
||||
F: 'YYYY-MM-DD',
|
||||
H: 'HH',
|
||||
I: 'hh',
|
||||
j: 'DDDD',
|
||||
k: 'H',
|
||||
l: 'h',
|
||||
m: 'MM',
|
||||
M: 'mm',
|
||||
p: 'A',
|
||||
S: 'ss',
|
||||
u: 'E',
|
||||
w: 'd',
|
||||
W: 'WW',
|
||||
y: 'YY',
|
||||
Y: 'YYYY',
|
||||
z: 'ZZ',
|
||||
Z: 'z'
|
||||
}
|
||||
};
|
||||
|
||||
for(var index = 0, token = ''; format.charAt(index); index += 1){
|
||||
token = format.charAt(index);
|
||||
if (escapeToken === true) {
|
||||
convertedFormat.push('['+token+']');
|
||||
escapeToken = false;
|
||||
} else {
|
||||
if (token === '\\') {
|
||||
if (!format || format.length <= 0) return format;
|
||||
|
||||
replacements = formatMappings.date;
|
||||
convertedFormat = [];
|
||||
escapeToken = false;
|
||||
|
||||
for (index = 0, token = ''; format.charAt(index); index += 1) {
|
||||
token = format.charAt(index);
|
||||
if (escapeToken === true) {
|
||||
convertedFormat.push('[' + token + ']');
|
||||
escapeToken = false;
|
||||
} else if (token === '\\') {
|
||||
// Slash escapes the next symbol to be treated as literal
|
||||
escapeToken = true;
|
||||
continue;
|
||||
} else if (replacements[token] !== undefined) {
|
||||
convertedFormat.push(replacements[token]);
|
||||
} else {
|
||||
convertedFormat.push('['+token+']');
|
||||
convertedFormat.push('[' + token + ']');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return convertedFormat.join('');
|
||||
}
|
||||
};
|
||||
});
|
||||
return convertedFormat.join('');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -1,49 +1,43 @@
|
||||
define([
|
||||
'react',
|
||||
],
|
||||
(
|
||||
React
|
||||
) => {
|
||||
const FormFieldCheckbox = React.createClass({
|
||||
onValueChange: function (e) {
|
||||
e.target.value = this.refs.checkbox.checked ? '1' : '0';
|
||||
return this.props.onValueChange(e);
|
||||
},
|
||||
render: function () {
|
||||
if (this.props.field.values === undefined) {
|
||||
return false;
|
||||
}
|
||||
import React from 'react';
|
||||
|
||||
// isChecked will be true only if the value is "1"
|
||||
// it will be false in case value is "0" or empty
|
||||
const isChecked = !!(~~(this.props.item[this.props.field.name]));
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
(value, index) => {
|
||||
return (
|
||||
<p key={ 'checkbox-' + index }>
|
||||
<label>
|
||||
<input
|
||||
ref="checkbox"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
checked={ isChecked }
|
||||
onChange={ this.onValueChange }
|
||||
name={ this.props.field.name }
|
||||
/>
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
);
|
||||
const FormFieldCheckbox = React.createClass({
|
||||
onValueChange: function onValueChange(e) {
|
||||
e.target.value = this.checkbox.checked ? '1' : '0';
|
||||
return this.props.onValueChange(e);
|
||||
},
|
||||
render: function render() {
|
||||
if (this.props.field.values === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ options }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
// isChecked will be true only if the value is "1"
|
||||
// it will be false in case value is "0" or empty
|
||||
const isChecked = !!(Number(this.props.item[this.props.field.name]));
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
value => (
|
||||
<p key={`checkbox-${value}`}>
|
||||
<label htmlFor={this.props.field.name}>
|
||||
<input
|
||||
ref={(c) => { this.checkbox = c; }}
|
||||
type="checkbox"
|
||||
value="1"
|
||||
checked={isChecked}
|
||||
onChange={this.onValueChange}
|
||||
name={this.props.field.name}
|
||||
id={this.props.field.name}
|
||||
/>
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
)
|
||||
);
|
||||
|
||||
return FormFieldCheckbox;
|
||||
return (
|
||||
<div>
|
||||
{ options }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default FormFieldCheckbox;
|
||||
|
@ -1,251 +1,279 @@
|
||||
define([
|
||||
'react',
|
||||
'moment',
|
||||
], (
|
||||
React,
|
||||
Moment
|
||||
) => {
|
||||
class FormFieldDateYear extends React.Component {
|
||||
render() {
|
||||
const yearsRange = 100;
|
||||
const years = [];
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
if (this.props.placeholder !== undefined) {
|
||||
years.push((
|
||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
function FormFieldDateYear(props) {
|
||||
const yearsRange = 100;
|
||||
const years = [];
|
||||
|
||||
const currentYear = Moment().year();
|
||||
for (let i = currentYear; i >= currentYear - yearsRange; i -= 1) {
|
||||
years.push((
|
||||
<option
|
||||
key={ i }
|
||||
value={ i }
|
||||
>{ i }</option>
|
||||
));
|
||||
}
|
||||
return (
|
||||
<select
|
||||
name={ `${this.props.name}[year]` }
|
||||
value={ this.props.year }
|
||||
onChange={ this.props.onValueChange }
|
||||
>
|
||||
{ years }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
if (props.placeholder !== undefined) {
|
||||
years.push((
|
||||
<option value="" key={0}>{ props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
|
||||
class FormFieldDateMonth extends React.Component {
|
||||
render() {
|
||||
const months = [];
|
||||
const currentYear = moment().year();
|
||||
for (let i = currentYear; i >= currentYear - yearsRange; i -= 1) {
|
||||
years.push((
|
||||
<option
|
||||
key={i}
|
||||
value={i}
|
||||
>{ i }</option>
|
||||
));
|
||||
}
|
||||
return (
|
||||
<select
|
||||
name={`${props.name}[year]`}
|
||||
value={props.year}
|
||||
onChange={props.onValueChange}
|
||||
>
|
||||
{ years }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.placeholder !== undefined) {
|
||||
months.push((
|
||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
FormFieldDateYear.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
year: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
for (let i = 1; i <= 12; i += 1) {
|
||||
months.push((
|
||||
<option
|
||||
key={ i }
|
||||
value={ i }
|
||||
>{ this.props.monthNames[i - 1] }</option>
|
||||
));
|
||||
}
|
||||
return (
|
||||
<select
|
||||
name={ `${this.props.name}[month]` }
|
||||
value={ this.props.month }
|
||||
onChange={ this.props.onValueChange }
|
||||
>
|
||||
{ months }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
function FormFieldDateMonth(props) {
|
||||
const months = [];
|
||||
|
||||
if (props.placeholder !== undefined) {
|
||||
months.push((
|
||||
<option value="" key={0}>{ props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
|
||||
class FormFieldDateDay extends React.Component {
|
||||
render() {
|
||||
const days = [];
|
||||
for (let i = 1; i <= 12; i += 1) {
|
||||
months.push((
|
||||
<option
|
||||
key={i}
|
||||
value={i}
|
||||
>{ props.monthNames[i - 1] }</option>
|
||||
));
|
||||
}
|
||||
return (
|
||||
<select
|
||||
name={`${props.name}[month]`}
|
||||
value={props.month}
|
||||
onChange={props.onValueChange}
|
||||
>
|
||||
{ months }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.placeholder !== undefined) {
|
||||
days.push((
|
||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
FormFieldDateMonth.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
month: PropTypes.string.isRequired,
|
||||
monthNames: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
};
|
||||
|
||||
for (let i = 1; i <= 31; i += 1) {
|
||||
days.push((
|
||||
<option
|
||||
key={ i }
|
||||
value={ i }
|
||||
>{ i }</option>
|
||||
));
|
||||
}
|
||||
function FormFieldDateDay(props) {
|
||||
const days = [];
|
||||
|
||||
return (
|
||||
<select
|
||||
name={ `${this.props.name}[day]` }
|
||||
value={ this.props.day }
|
||||
onChange={ this.props.onValueChange }
|
||||
>
|
||||
{ days }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
if (props.placeholder !== undefined) {
|
||||
days.push((
|
||||
<option value="" key={0}>{ props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
|
||||
class FormFieldDate extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
year: '',
|
||||
month: '',
|
||||
day: '',
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
for (let i = 1; i <= 31; i += 1) {
|
||||
days.push((
|
||||
<option
|
||||
key={i}
|
||||
value={i}
|
||||
>{ i }</option>
|
||||
));
|
||||
}
|
||||
|
||||
return (
|
||||
<select
|
||||
name={`${props.name}[day]`}
|
||||
value={props.day}
|
||||
onChange={props.onValueChange}
|
||||
>
|
||||
{ days }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
FormFieldDateDay.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
day: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
class FormFieldDate extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
year: '',
|
||||
month: '',
|
||||
day: '',
|
||||
};
|
||||
|
||||
this.onValueChange = this.onValueChange.bind(this);
|
||||
}
|
||||
componentDidMount() {
|
||||
this.extractDateParts();
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
if (
|
||||
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||
&& (this.props.item.id !== prevProps.item.id)
|
||||
) {
|
||||
this.extractDateParts();
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
if (
|
||||
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||
&& (this.props.item.id !== prevProps.item.id)
|
||||
) {
|
||||
this.extractDateParts();
|
||||
}
|
||||
}
|
||||
extractDateParts() {
|
||||
const value = (this.props.item[this.props.field.name] !== undefined)
|
||||
? this.props.item[this.props.field.name].trim()
|
||||
: '';
|
||||
}
|
||||
onValueChange(e) {
|
||||
// extract property from name
|
||||
const matches = e.target.name.match(/(.*?)\[(.*?)\]/);
|
||||
let field = null;
|
||||
let property = null;
|
||||
|
||||
if(value === '') {
|
||||
return;
|
||||
}
|
||||
if (matches !== null && matches.length === 3) {
|
||||
field = matches[1];
|
||||
property = matches[2];
|
||||
|
||||
const dateTime = Moment(value);
|
||||
const value = Number(e.target.value);
|
||||
|
||||
this.setState({
|
||||
year: dateTime.format('YYYY'),
|
||||
month: dateTime.format('M'),
|
||||
day: dateTime.format('D'),
|
||||
});
|
||||
}
|
||||
formatValue() {
|
||||
const dateType = this.props.field.params.date_type;
|
||||
|
||||
let value;
|
||||
|
||||
switch(dateType) {
|
||||
case 'year_month_day':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
month: this.state.month,
|
||||
day: this.state.day,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'year_month':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
month: this.state.month,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
value = {
|
||||
month: this.state.month,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'year':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
onValueChange(e) {
|
||||
// extract property from name
|
||||
const matches = e.target.name.match(/(.*?)\[(.*?)\]/);
|
||||
let field = null;
|
||||
let property = null;
|
||||
|
||||
if (matches !== null && matches.length === 3) {
|
||||
field = matches[1];
|
||||
property = matches[2];
|
||||
|
||||
const value = ~~(e.target.value);
|
||||
|
||||
this.setState({
|
||||
[`${property}`]: value,
|
||||
}, () => {
|
||||
this.props.onValueChange({
|
||||
target: {
|
||||
name: field,
|
||||
value: this.formatValue(),
|
||||
},
|
||||
});
|
||||
[`${property}`]: value,
|
||||
}, () => {
|
||||
this.props.onValueChange({
|
||||
target: {
|
||||
name: field,
|
||||
value: this.formatValue(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const monthNames = window.mailpoet_month_names || [];
|
||||
const dateFormats = window.mailpoet_date_formats || {};
|
||||
const dateType = this.props.field.params.date_type;
|
||||
const dateSelects = dateFormats[dateType][0].split('/');
|
||||
|
||||
const fields = dateSelects.map((type) => {
|
||||
switch(type) {
|
||||
case 'YYYY':
|
||||
return (<FormFieldDateYear
|
||||
onValueChange={ this.onValueChange.bind(this) }
|
||||
ref={ 'year' }
|
||||
key={ 'year' }
|
||||
name={ this.props.field.name }
|
||||
year={ this.state.year }
|
||||
placeholder={ this.props.field.year_placeholder }
|
||||
/>);
|
||||
break;
|
||||
|
||||
case 'MM':
|
||||
return (<FormFieldDateMonth
|
||||
onValueChange={ this.onValueChange.bind(this) }
|
||||
ref={ 'month' }
|
||||
key={ 'month' }
|
||||
name={ this.props.field.name }
|
||||
month={ this.state.month }
|
||||
monthNames={ monthNames }
|
||||
placeholder={ this.props.field.month_placeholder }
|
||||
/>);
|
||||
break;
|
||||
|
||||
case 'DD':
|
||||
return (<FormFieldDateDay
|
||||
onValueChange={ this.onValueChange.bind(this) }
|
||||
ref={ 'day' }
|
||||
key={ 'day' }
|
||||
name={ this.props.field.name }
|
||||
day={ this.state.day }
|
||||
placeholder={ this.props.field.day_placeholder }
|
||||
/>);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{fields}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
formatValue() {
|
||||
const dateType = this.props.field.params.date_type;
|
||||
|
||||
return FormFieldDate;
|
||||
});
|
||||
let value;
|
||||
|
||||
switch (dateType) {
|
||||
case 'year_month_day':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
month: this.state.month,
|
||||
day: this.state.day,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'year_month':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
month: this.state.month,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
value = {
|
||||
month: this.state.month,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'year':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
value = {
|
||||
value: 'invalid type',
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
extractDateParts() {
|
||||
const value = (this.props.item[this.props.field.name] !== undefined)
|
||||
? this.props.item[this.props.field.name].trim()
|
||||
: '';
|
||||
|
||||
if (value === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const dateTime = moment(value);
|
||||
|
||||
this.setState({
|
||||
year: dateTime.format('YYYY'),
|
||||
month: dateTime.format('M'),
|
||||
day: dateTime.format('D'),
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const monthNames = window.mailpoet_month_names || [];
|
||||
const dateFormats = window.mailpoet_date_formats || {};
|
||||
const dateType = this.props.field.params.date_type;
|
||||
const dateSelects = dateFormats[dateType][0].split('/');
|
||||
|
||||
const fields = dateSelects.map((type) => {
|
||||
switch (type) {
|
||||
case 'YYYY':
|
||||
return (<FormFieldDateYear
|
||||
onValueChange={this.onValueChange}
|
||||
key={'year'}
|
||||
name={this.props.field.name}
|
||||
year={this.state.year}
|
||||
placeholder={this.props.field.year_placeholder}
|
||||
/>);
|
||||
|
||||
case 'MM':
|
||||
return (<FormFieldDateMonth
|
||||
onValueChange={this.onValueChange}
|
||||
key={'month'}
|
||||
name={this.props.field.name}
|
||||
month={this.state.month}
|
||||
monthNames={monthNames}
|
||||
placeholder={this.props.field.month_placeholder}
|
||||
/>);
|
||||
|
||||
case 'DD':
|
||||
return (<FormFieldDateDay
|
||||
onValueChange={this.onValueChange}
|
||||
key={'day'}
|
||||
name={this.props.field.name}
|
||||
day={this.state.day}
|
||||
placeholder={this.props.field.day_placeholder}
|
||||
/>);
|
||||
|
||||
default:
|
||||
return <div>Invalid date type</div>;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{fields}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FormFieldDate.propTypes = {
|
||||
item: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
field: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
day_placeholder: PropTypes.string,
|
||||
month_placeholder: PropTypes.string,
|
||||
year_placeholder: PropTypes.string,
|
||||
params: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
}).isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default FormFieldDate;
|
||||
|
@ -1,129 +1,119 @@
|
||||
define([
|
||||
'react',
|
||||
'form/fields/text.jsx',
|
||||
'form/fields/textarea.jsx',
|
||||
'form/fields/select.jsx',
|
||||
'form/fields/radio.jsx',
|
||||
'form/fields/checkbox.jsx',
|
||||
'form/fields/selection.jsx',
|
||||
'form/fields/date.jsx',
|
||||
],
|
||||
(
|
||||
React,
|
||||
FormFieldText,
|
||||
FormFieldTextarea,
|
||||
FormFieldSelect,
|
||||
FormFieldRadio,
|
||||
FormFieldCheckbox,
|
||||
FormFieldSelection,
|
||||
FormFieldDate
|
||||
) => {
|
||||
const FormField = React.createClass({
|
||||
renderField: function (data, inline = false) {
|
||||
let description = false;
|
||||
if(data.field.description) {
|
||||
description = (
|
||||
<p className="description">{ data.field.description }</p>
|
||||
);
|
||||
}
|
||||
import React from 'react';
|
||||
import FormFieldText from 'form/fields/text.jsx';
|
||||
import FormFieldTextarea from 'form/fields/textarea.jsx';
|
||||
import FormFieldSelect from 'form/fields/select.jsx';
|
||||
import FormFieldRadio from 'form/fields/radio.jsx';
|
||||
import FormFieldCheckbox from 'form/fields/checkbox.jsx';
|
||||
import FormFieldSelection from 'form/fields/selection.jsx';
|
||||
import FormFieldDate from 'form/fields/date.jsx';
|
||||
import jQuery from 'jquery';
|
||||
|
||||
let field = false;
|
||||
let dataField = data.field;
|
||||
|
||||
if(data.field['field'] !== undefined) {
|
||||
dataField = jQuery.merge(dataField, data.field.field);
|
||||
}
|
||||
|
||||
switch(dataField.type) {
|
||||
case 'text':
|
||||
field = (<FormFieldText {...data} />);
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
field = (<FormFieldTextarea {...data} />);
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
field = (<FormFieldSelect {...data} />);
|
||||
break;
|
||||
|
||||
case 'radio':
|
||||
field = (<FormFieldRadio {...data} />);
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
field = (<FormFieldCheckbox {...data} />);
|
||||
break;
|
||||
|
||||
case 'selection':
|
||||
field = (<FormFieldSelection {...data} />);
|
||||
break;
|
||||
|
||||
case 'date':
|
||||
field = (<FormFieldDate {...data} />);
|
||||
break;
|
||||
|
||||
case 'reactComponent':
|
||||
field = (<data.field.component {...data} />);
|
||||
break;
|
||||
}
|
||||
|
||||
if(inline === true) {
|
||||
return (
|
||||
<span key={ 'field-' + (data.index || 0) }>
|
||||
{ field }
|
||||
{ description }
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div key={ 'field-' + (data.index || 0) }>
|
||||
{ field }
|
||||
{ description }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
render: function () {
|
||||
let field = false;
|
||||
|
||||
if(this.props.field['fields'] !== undefined) {
|
||||
field = this.props.field.fields.map((subfield, index) => {
|
||||
return this.renderField({
|
||||
index: index,
|
||||
field: subfield,
|
||||
item: this.props.item,
|
||||
onValueChange: this.props.onValueChange || false,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
field = this.renderField(this.props);
|
||||
}
|
||||
|
||||
let tip = false;
|
||||
if(this.props.field.tip) {
|
||||
tip = (
|
||||
<p className="description">{ this.props.field.tip }</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label
|
||||
htmlFor={ 'field_'+this.props.field.name }
|
||||
>
|
||||
{ this.props.field.label }
|
||||
{ tip }
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
{ field }
|
||||
</td>
|
||||
</tr>
|
||||
const FormField = React.createClass({
|
||||
renderField: function renderField(data, inline = false) {
|
||||
let description = false;
|
||||
if (data.field.description) {
|
||||
description = (
|
||||
<p className="description">{ data.field.description }</p>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return FormField;
|
||||
let field = false;
|
||||
let dataField = data.field;
|
||||
|
||||
if (data.field.field !== undefined) {
|
||||
dataField = jQuery.merge(dataField, data.field.field);
|
||||
}
|
||||
|
||||
switch (dataField.type) {
|
||||
case 'text':
|
||||
field = (<FormFieldText {...data} />);
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
field = (<FormFieldTextarea {...data} />);
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
field = (<FormFieldSelect {...data} />);
|
||||
break;
|
||||
|
||||
case 'radio':
|
||||
field = (<FormFieldRadio {...data} />);
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
field = (<FormFieldCheckbox {...data} />);
|
||||
break;
|
||||
|
||||
case 'selection':
|
||||
field = (<FormFieldSelection {...data} />);
|
||||
break;
|
||||
|
||||
case 'date':
|
||||
field = (<FormFieldDate {...data} />);
|
||||
break;
|
||||
|
||||
case 'reactComponent':
|
||||
field = (<data.field.component {...data} />);
|
||||
break;
|
||||
|
||||
default:
|
||||
field = 'invalid';
|
||||
break;
|
||||
}
|
||||
|
||||
if (inline === true) {
|
||||
return (
|
||||
<span key={`field-${data.index || 0}`}>
|
||||
{ field }
|
||||
{ description }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div key={`field-${data.index || 0}`}>
|
||||
{ field }
|
||||
{ description }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
render: function render() {
|
||||
let field = false;
|
||||
|
||||
if (this.props.field.fields !== undefined) {
|
||||
field = this.props.field.fields.map((subfield, index) => this.renderField({
|
||||
index,
|
||||
field: subfield,
|
||||
item: this.props.item,
|
||||
onValueChange: this.props.onValueChange || false,
|
||||
}));
|
||||
} else {
|
||||
field = this.renderField(this.props);
|
||||
}
|
||||
|
||||
let tip = false;
|
||||
if (this.props.field.tip) {
|
||||
tip = (
|
||||
<p className="description">{ this.props.field.tip }</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<tr className={`form-field-row-${this.props.field.name}`}>
|
||||
<th scope="row">
|
||||
<label
|
||||
htmlFor={`field_${this.props.field.name}`}
|
||||
>
|
||||
{ this.props.field.label }
|
||||
{ tip }
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
{ field }
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default FormField;
|
||||
|
@ -1,41 +1,36 @@
|
||||
define([
|
||||
'react',
|
||||
],
|
||||
(
|
||||
React
|
||||
) => {
|
||||
const FormFieldRadio = React.createClass({
|
||||
render: function () {
|
||||
if (this.props.field.values === undefined) {
|
||||
return false;
|
||||
}
|
||||
import React from 'react';
|
||||
|
||||
const selected_value = this.props.item[this.props.field.name];
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
(value, index) => {
|
||||
return (
|
||||
<p key={ 'radio-' + index }>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
checked={ selected_value === value }
|
||||
value={ value }
|
||||
onChange={ this.props.onValueChange }
|
||||
name={ this.props.field.name } />
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
);
|
||||
const FormFieldRadio = React.createClass({
|
||||
render: function render() {
|
||||
if (this.props.field.values === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ options }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
const selectedValue = this.props.item[this.props.field.name];
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
value => (
|
||||
<p key={`radio-${value}`}>
|
||||
<label htmlFor={this.props.field.name}>
|
||||
<input
|
||||
type="radio"
|
||||
checked={selectedValue === value}
|
||||
value={value}
|
||||
onChange={this.props.onValueChange}
|
||||
name={this.props.field.name}
|
||||
id={this.props.field.name}
|
||||
/>
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
)
|
||||
);
|
||||
|
||||
return FormFieldRadio;
|
||||
return (
|
||||
<div>
|
||||
{ options }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default FormFieldRadio;
|
||||
|
@ -17,7 +17,7 @@ const FormFieldSelect = React.createClass({
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.field['filter'] !== undefined) {
|
||||
if (this.props.field.filter !== undefined) {
|
||||
filter = this.props.field.filter;
|
||||
}
|
||||
|
||||
@ -41,29 +41,29 @@ const FormFieldSelect = React.createClass({
|
||||
keys = Object.keys(this.props.field.values);
|
||||
}
|
||||
|
||||
const options = keys.map(
|
||||
(value, index) => {
|
||||
|
||||
if (filter !== false && filter(this.props.item, value) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
const options = keys
|
||||
.filter((value) => {
|
||||
if (filter === false) return true;
|
||||
return filter(this.props.item, value);
|
||||
})
|
||||
.map(
|
||||
value => (
|
||||
<option
|
||||
key={ 'option-' + index }
|
||||
value={ value }>
|
||||
key={`option-${value}`}
|
||||
value={value}
|
||||
>
|
||||
{ this.props.field.values[value] }
|
||||
</option>
|
||||
);
|
||||
}
|
||||
);
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<select
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
onChange={ this.props.onValueChange }
|
||||
name={this.props.field.name}
|
||||
id={`field_${this.props.field.name}`}
|
||||
value={this.props.item[this.props.field.name] || ''}
|
||||
onChange={this.props.onValueChange}
|
||||
data-automation-id={this.props.automationId}
|
||||
{...this.props.field.validation}
|
||||
>
|
||||
{placeholder}
|
||||
|
@ -1,194 +1,261 @@
|
||||
define([
|
||||
'react',
|
||||
'react-dom',
|
||||
'jquery',
|
||||
'select2',
|
||||
],
|
||||
(
|
||||
React,
|
||||
ReactDOM,
|
||||
jQuery
|
||||
) => {
|
||||
const Selection = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
items: [],
|
||||
select2: false,
|
||||
};
|
||||
},
|
||||
componentWillMount: function () {
|
||||
this.loadCachedItems();
|
||||
},
|
||||
allowMultipleValues: function () {
|
||||
return (this.props.field.multiple === true);
|
||||
},
|
||||
isSelect2Initialized: function () {
|
||||
return (this.state.select2 === true);
|
||||
},
|
||||
componentDidMount: function () {
|
||||
if(this.allowMultipleValues()) {
|
||||
this.setupSelect2();
|
||||
}
|
||||
},
|
||||
componentDidUpdate: function (prevProps) {
|
||||
if(
|
||||
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||
&& (this.props.item.id !== prevProps.item.id)
|
||||
) {
|
||||
jQuery('#'+this.refs.select.id)
|
||||
.val(this.getSelectedValues())
|
||||
.trigger('change');
|
||||
}
|
||||
},
|
||||
componentWillUnmount: function () {
|
||||
if(this.allowMultipleValues()) {
|
||||
this.destroySelect2();
|
||||
}
|
||||
},
|
||||
destroySelect2: function () {
|
||||
if(this.isSelect2Initialized()) {
|
||||
jQuery('#'+this.refs.select.id).select2('destroy');
|
||||
}
|
||||
},
|
||||
setupSelect2: function () {
|
||||
if(this.isSelect2Initialized()) {
|
||||
return;
|
||||
}
|
||||
import React from 'react';
|
||||
import jQuery from 'jquery';
|
||||
import _ from 'underscore';
|
||||
import 'react-dom';
|
||||
import 'select2';
|
||||
|
||||
const select2 = jQuery('#'+this.refs.select.id).select2({
|
||||
width: (this.props.width || ''),
|
||||
templateResult: function (item) {
|
||||
if(item.element && item.element.selected) {
|
||||
return null;
|
||||
} else {
|
||||
if(item.title) {
|
||||
return item.title;
|
||||
} else {
|
||||
return item.text;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
const Selection = React.createClass({
|
||||
allowMultipleValues: function allowMultipleValues() {
|
||||
return (this.props.field.multiple === true);
|
||||
},
|
||||
isSelect2Initialized: function isSelect2Initialized() {
|
||||
return (jQuery(`#${this.select.id}`).hasClass('select2-hidden-accessible') === true);
|
||||
},
|
||||
isSelect2Component: function isSelect2Component() {
|
||||
return this.allowMultipleValues() || this.props.field.forceSelect2;
|
||||
},
|
||||
componentDidMount: function componentDidMount() {
|
||||
if (this.isSelect2Component()) {
|
||||
this.setupSelect2();
|
||||
}
|
||||
},
|
||||
componentDidUpdate: function componentDidUpdate(prevProps) {
|
||||
if ((this.props.item !== undefined && prevProps.item !== undefined)
|
||||
&& (this.props.item.id !== prevProps.item.id)
|
||||
) {
|
||||
jQuery(`#${this.select.id}`)
|
||||
.val(this.getSelectedValues())
|
||||
.trigger('change');
|
||||
}
|
||||
|
||||
let hasRemoved = false;
|
||||
select2.on('select2:unselecting', () => {
|
||||
hasRemoved = true;
|
||||
});
|
||||
select2.on('select2:opening', (e) => {
|
||||
if(hasRemoved === true) {
|
||||
hasRemoved = false;
|
||||
e.preventDefault();
|
||||
if (this.isSelect2Initialized() &&
|
||||
(this.getFieldId(this.props) !== this.getFieldId(prevProps)) &&
|
||||
this.props.field.resetSelect2OnUpdate !== undefined
|
||||
) {
|
||||
this.resetSelect2();
|
||||
}
|
||||
},
|
||||
componentWillUnmount: function componentWillUnmount() {
|
||||
if (this.isSelect2Component()) {
|
||||
this.destroySelect2();
|
||||
}
|
||||
},
|
||||
getFieldId: function getFieldId(data) {
|
||||
const props = data || this.props;
|
||||
return props.field.id || props.field.name;
|
||||
},
|
||||
resetSelect2: function resetSelect2() {
|
||||
this.destroySelect2();
|
||||
this.setupSelect2();
|
||||
},
|
||||
destroySelect2: function destroySelect2() {
|
||||
if (this.isSelect2Initialized()) {
|
||||
jQuery(`#${this.select.id}`).select2('destroy');
|
||||
this.cleanupAfterSelect2();
|
||||
}
|
||||
},
|
||||
cleanupAfterSelect2: function cleanupAfterSelect2() {
|
||||
// remove DOM elements created by Select2 that are not tracked by React
|
||||
jQuery(`#${this.select.id}`)
|
||||
.find('option:not(.default)')
|
||||
.remove();
|
||||
|
||||
// unbind events (https://select2.org/programmatic-control/methods#event-unbinding)
|
||||
jQuery(`#${this.select.id}`)
|
||||
.off('select2:unselecting')
|
||||
.off('select2:opening');
|
||||
},
|
||||
setupSelect2: function setupSelect2() {
|
||||
if (this.isSelect2Initialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let select2Options = {
|
||||
disabled: this.props.disabled || false,
|
||||
width: (this.props.width || ''),
|
||||
placeholder: {
|
||||
id: '', // the value of the option
|
||||
text: this.props.field.placeholder,
|
||||
},
|
||||
templateResult: function templateResult(item) {
|
||||
if (item.element && item.element.selected) {
|
||||
return null;
|
||||
} else if (item.title) {
|
||||
return item.title;
|
||||
}
|
||||
});
|
||||
return item.text;
|
||||
},
|
||||
};
|
||||
|
||||
select2.on('change', this.handleChange);
|
||||
|
||||
this.setState({ select2: true });
|
||||
},
|
||||
getSelectedValues: function () {
|
||||
if(this.props.field['selected'] !== undefined) {
|
||||
return this.props.field['selected'](this.props.item);
|
||||
} else if(this.props.item !== undefined && this.props.field.name !== undefined) {
|
||||
if (this.allowMultipleValues()) {
|
||||
if (Array.isArray(this.props.item[this.props.field.name])) {
|
||||
return this.props.item[this.props.field.name].map((item) => {
|
||||
return item.id;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return this.props.item[this.props.field.name];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
loadCachedItems: function () {
|
||||
if(typeof(window['mailpoet_'+this.props.field.endpoint]) !== 'undefined') {
|
||||
let items = window['mailpoet_'+this.props.field.endpoint];
|
||||
|
||||
|
||||
if(this.props.field['filter'] !== undefined) {
|
||||
items = items.filter(this.props.field.filter);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
items: items,
|
||||
});
|
||||
}
|
||||
},
|
||||
handleChange: function (e) {
|
||||
if(this.props.onValueChange !== undefined) {
|
||||
if(this.props.field.multiple) {
|
||||
value = jQuery('#'+this.refs.select.id).val();
|
||||
} else {
|
||||
value = e.target.value;
|
||||
}
|
||||
const transformedValue = this.transformChangedValue(value);
|
||||
this.props.onValueChange({
|
||||
target: {
|
||||
value: transformedValue,
|
||||
name: this.props.field.name,
|
||||
const remoteQuery = this.props.field.remoteQuery || null;
|
||||
if (remoteQuery) {
|
||||
select2Options = Object.assign(select2Options, {
|
||||
ajax: {
|
||||
url: window.ajaxurl,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: function data(params) {
|
||||
return {
|
||||
action: 'mailpoet',
|
||||
api_version: window.mailpoet_api_version,
|
||||
token: window.mailpoet_token,
|
||||
endpoint: remoteQuery.endpoint,
|
||||
method: remoteQuery.method,
|
||||
data: Object.assign(
|
||||
remoteQuery.data,
|
||||
{ query: params.term }
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
getLabel: function (item) {
|
||||
if(this.props.field['getLabel'] !== undefined) {
|
||||
return this.props.field.getLabel(item, this.props.item);
|
||||
}
|
||||
return item.name;
|
||||
},
|
||||
getSearchLabel: function (item) {
|
||||
if(this.props.field['getSearchLabel'] !== undefined) {
|
||||
return this.props.field.getSearchLabel(item, this.props.item);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getValue: function (item) {
|
||||
if(this.props.field['getValue'] !== undefined) {
|
||||
return this.props.field.getValue(item, this.props.item);
|
||||
}
|
||||
return item.id;
|
||||
},
|
||||
// When it's impossible to represent the desired value in DOM,
|
||||
// this function may be used to transform the placeholder value into
|
||||
// desired value.
|
||||
transformChangedValue: function (value) {
|
||||
if(typeof this.props.field['transformChangedValue'] === 'function') {
|
||||
return this.props.field.transformChangedValue.call(this, value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
render: function () {
|
||||
const options = this.state.items.map((item, index) => {
|
||||
const label = this.getLabel(item);
|
||||
const searchLabel = this.getSearchLabel(item);
|
||||
const value = this.getValue(item);
|
||||
|
||||
return (
|
||||
<option
|
||||
key={ 'option-'+index }
|
||||
value={ value }
|
||||
title={ searchLabel }
|
||||
>
|
||||
{ label }
|
||||
</option>
|
||||
);
|
||||
processResults: function processResults(response) {
|
||||
return { results: (!_.has(response, 'data')) ?
|
||||
[] :
|
||||
response.data.map(item =>
|
||||
({ id: item.id || item.value, text: item.name || item.text })
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
minimumInputLength: remoteQuery.minimumInputLength || 2,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.props.field.extendSelect2Options !== undefined) {
|
||||
select2Options = Object.assign(select2Options, this.props.field.extendSelect2Options);
|
||||
}
|
||||
|
||||
const select2 = jQuery(`#${this.select.id}`).select2(select2Options);
|
||||
|
||||
let hasRemoved = false;
|
||||
select2.on('select2:unselecting', () => {
|
||||
hasRemoved = true;
|
||||
});
|
||||
select2.on('select2:opening', (e) => {
|
||||
if (hasRemoved === true) {
|
||||
hasRemoved = false;
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
select2.on('change', this.handleChange);
|
||||
},
|
||||
getSelectedValues: function getSelectedValues() {
|
||||
if (this.props.field.selected !== undefined) {
|
||||
return this.props.field.selected(this.props.item);
|
||||
} else if (this.props.item !== undefined && this.props.field.name !== undefined) {
|
||||
if (this.allowMultipleValues()) {
|
||||
if (_.isArray(this.props.item[this.props.field.name])) {
|
||||
return this.props.item[this.props.field.name].map(item => item.id);
|
||||
}
|
||||
} else {
|
||||
return this.props.item[this.props.field.name];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getItems: function getItems() {
|
||||
let items;
|
||||
if (typeof (window[`mailpoet_${this.props.field.endpoint}`]) !== 'undefined') {
|
||||
items = window[`mailpoet_${this.props.field.endpoint}`];
|
||||
} else if (this.props.field.values !== undefined) {
|
||||
items = this.props.field.values;
|
||||
}
|
||||
|
||||
if (_.isArray(items)) {
|
||||
if (this.props.field.filter !== undefined) {
|
||||
items = items.filter(this.props.field.filter);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
handleChange: function handleChange(e) {
|
||||
if (this.props.onValueChange === undefined) return;
|
||||
|
||||
const valueTextPair = jQuery(`#${this.select.id}`).children(':selected').map(function element() {
|
||||
return { id: jQuery(this).val(), text: jQuery(this).text() };
|
||||
});
|
||||
const value = (this.props.field.multiple) ? _.pluck(valueTextPair, 'id') : _.pluck(valueTextPair, 'id').toString();
|
||||
const transformedValue = this.transformChangedValue(value, valueTextPair);
|
||||
|
||||
this.props.onValueChange({
|
||||
target: {
|
||||
value: transformedValue,
|
||||
name: this.props.field.name,
|
||||
id: e.target.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
getLabel: function getLabel(item) {
|
||||
if (this.props.field.getLabel !== undefined) {
|
||||
return this.props.field.getLabel(item, this.props.item);
|
||||
}
|
||||
return item.name;
|
||||
},
|
||||
getSearchLabel: function getSearchLabel(item) {
|
||||
if (this.props.field.getSearchLabel !== undefined) {
|
||||
return this.props.field.getSearchLabel(item, this.props.item);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getValue: function getValue(item) {
|
||||
if (this.props.field.getValue !== undefined) {
|
||||
return this.props.field.getValue(item, this.props.item);
|
||||
}
|
||||
return item.id;
|
||||
},
|
||||
// When it's impossible to represent the desired value in DOM,
|
||||
// this function may be used to transform the placeholder value into
|
||||
// desired value.
|
||||
transformChangedValue: function transformChangedValue(value, textValuePair) {
|
||||
if (typeof this.props.field.transformChangedValue === 'function') {
|
||||
return this.props.field.transformChangedValue.call(this, value, textValuePair);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
insertEmptyOption: function insertEmptyOption() {
|
||||
// https://select2.org/placeholders
|
||||
// For single selects only, in order for the placeholder value to appear,
|
||||
// we must have a blank <option> as the first option in the <select> control.
|
||||
if (this.allowMultipleValues()) return undefined;
|
||||
if (this.props.field.placeholder) return (<option className="default" />);
|
||||
return undefined;
|
||||
},
|
||||
render: function render() {
|
||||
const items = this.getItems(this.props.field);
|
||||
const selectedValues = this.getSelectedValues();
|
||||
const options = items.map((item) => {
|
||||
const label = this.getLabel(item);
|
||||
const searchLabel = this.getSearchLabel(item);
|
||||
const value = this.getValue(item);
|
||||
|
||||
return (
|
||||
<select
|
||||
id={ this.props.field.id || this.props.field.name }
|
||||
ref="select"
|
||||
disabled={this.props.field.disabled}
|
||||
data-placeholder={ this.props.field.placeholder }
|
||||
multiple={ this.props.field.multiple }
|
||||
defaultValue={ this.getSelectedValues() }
|
||||
{...this.props.field.validation}
|
||||
>{ options }</select>
|
||||
<option
|
||||
key={`option-${item.id}`}
|
||||
className="default"
|
||||
value={value}
|
||||
title={searchLabel}
|
||||
>
|
||||
{ label }
|
||||
</option>
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return Selection;
|
||||
return (
|
||||
<select
|
||||
id={this.getFieldId()}
|
||||
ref={(c) => { this.select = c; }}
|
||||
disabled={this.props.field.disabled}
|
||||
data-placeholder={this.props.field.placeholder}
|
||||
multiple={this.props.field.multiple}
|
||||
defaultValue={selectedValues}
|
||||
{...this.props.field.validation}
|
||||
>
|
||||
{ this.insertEmptyOption() }
|
||||
{ options }
|
||||
</select>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default Selection;
|
||||
|
@ -2,30 +2,52 @@ import React from 'react';
|
||||
|
||||
const FormFieldText = React.createClass({
|
||||
render() {
|
||||
let value = this.props.item[this.props.field.name];
|
||||
if (value === undefined) {
|
||||
value = this.props.field.defaultValue || '';
|
||||
const name = this.props.field.name || null;
|
||||
const item = this.props.item || {};
|
||||
let value;
|
||||
let defaultValue;
|
||||
// value should only be set when onChangeValue is configured
|
||||
if (this.props.onValueChange instanceof Function) {
|
||||
value = item[this.props.field.name];
|
||||
// set value to defaultValue if available
|
||||
value = (value === undefined) ?
|
||||
(this.props.field.defaultValue || '') : value;
|
||||
}
|
||||
// defaultValue should only be set only when value is not set
|
||||
if (!value && this.props.field.defaultValue) {
|
||||
defaultValue = this.props.field.defaultValue;
|
||||
}
|
||||
|
||||
let id = this.props.field.id || null;
|
||||
if (!id && this.props.field.name) {
|
||||
id = `field_${this.props.field.name}`;
|
||||
}
|
||||
|
||||
let className = this.props.field.class || null;
|
||||
if (!className && !this.props.field.size) {
|
||||
className = 'regular-text';
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
disabled={
|
||||
(this.props.field['disabled'] !== undefined)
|
||||
? this.props.field.disabled(this.props.item)
|
||||
: false
|
||||
(this.props.field.disabled !== undefined)
|
||||
? this.props.field.disabled(this.props.item)
|
||||
: false
|
||||
}
|
||||
className={ (this.props.field.size) ? '' : 'regular-text' }
|
||||
className={className}
|
||||
size={
|
||||
(this.props.field.size !== 'auto' && this.props.field.size > 0)
|
||||
? this.props.field.size
|
||||
: false
|
||||
? this.props.field.size
|
||||
: false
|
||||
}
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ value }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
onChange={ this.props.onValueChange }
|
||||
name={name}
|
||||
id={id}
|
||||
value={value}
|
||||
defaultValue={defaultValue}
|
||||
placeholder={this.props.field.placeholder}
|
||||
onChange={this.props.onValueChange}
|
||||
{...this.props.field.validation}
|
||||
/>
|
||||
);
|
||||
|
@ -1,26 +1,29 @@
|
||||
define([
|
||||
'react',
|
||||
],
|
||||
(
|
||||
React
|
||||
) => {
|
||||
const FormFieldTextarea = React.createClass({
|
||||
render: function () {
|
||||
return (
|
||||
<textarea
|
||||
type="text"
|
||||
className="regular-text"
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
defaultValue={ this.props.field.defaultValue }
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
return FormFieldTextarea;
|
||||
});
|
||||
const FormFieldTextarea = props => (
|
||||
<textarea
|
||||
type="text"
|
||||
className="regular-text"
|
||||
name={props.field.name}
|
||||
id={`field_${props.field.name}`}
|
||||
value={props.item[props.field.name]}
|
||||
placeholder={props.field.placeholder}
|
||||
defaultValue={props.field.defaultValue}
|
||||
onChange={props.onValueChange}
|
||||
{...props.field.validation}
|
||||
/>
|
||||
);
|
||||
|
||||
FormFieldTextarea.propTypes = {
|
||||
item: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
field: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
defaultValue: PropTypes.string,
|
||||
validation: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
}).isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default FormFieldTextarea;
|
||||
|
@ -1,246 +1,237 @@
|
||||
define(
|
||||
[
|
||||
'react',
|
||||
'mailpoet',
|
||||
'classnames',
|
||||
'react-router',
|
||||
'form/fields/field.jsx',
|
||||
],
|
||||
(
|
||||
React,
|
||||
MailPoet,
|
||||
classNames,
|
||||
Router,
|
||||
FormField
|
||||
) => {
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
import classNames from 'classnames';
|
||||
import FormField from 'form/fields/field.jsx';
|
||||
import jQuery from 'jquery';
|
||||
|
||||
const Form = React.createClass({
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired,
|
||||
},
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
params: {},
|
||||
};
|
||||
},
|
||||
getInitialState: function () {
|
||||
return {
|
||||
const Form = React.createClass({
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired,
|
||||
},
|
||||
getDefaultProps: function getDefaultProps() {
|
||||
return {
|
||||
params: {},
|
||||
};
|
||||
},
|
||||
getInitialState: function getInitialState() {
|
||||
return {
|
||||
loading: false,
|
||||
errors: [],
|
||||
item: {},
|
||||
};
|
||||
},
|
||||
getValues: function getValues() {
|
||||
return this.props.item ? this.props.item : this.state.item;
|
||||
},
|
||||
getErrors: function getErrors() {
|
||||
return this.props.errors ? this.props.errors : this.state.errors;
|
||||
},
|
||||
componentDidMount: function componentDidMount() {
|
||||
if (this.props.params.id !== undefined) {
|
||||
this.loadItem(this.props.params.id);
|
||||
} else {
|
||||
setImmediate(() => {
|
||||
this.setState({
|
||||
item: jQuery('.mailpoet_form').mailpoetSerializeObject(),
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
componentWillReceiveProps: function componentWillReceiveProps(props) {
|
||||
if (props.params.id === undefined) {
|
||||
setImmediate(() => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
errors: [],
|
||||
item: {},
|
||||
});
|
||||
});
|
||||
if (props.item === undefined) {
|
||||
this.form.reset();
|
||||
}
|
||||
}
|
||||
},
|
||||
loadItem: function loadItem(id) {
|
||||
this.setState({ loading: true });
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'get',
|
||||
data: {
|
||||
id,
|
||||
},
|
||||
}).done((response) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
item: response.data,
|
||||
});
|
||||
if (typeof this.props.onItemLoad === 'function') {
|
||||
this.props.onItemLoad(response.data);
|
||||
}
|
||||
}).fail(() => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
item: {},
|
||||
}, function failSetStateCallback() {
|
||||
this.context.router.push('/new');
|
||||
});
|
||||
});
|
||||
},
|
||||
handleSubmit: function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// handle validation
|
||||
if (this.props.isValid !== undefined) {
|
||||
if (this.props.isValid() === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
// only get values from displayed fields
|
||||
const item = {};
|
||||
this.props.fields.forEach((field) => {
|
||||
if (field.fields !== undefined) {
|
||||
field.fields.forEach((subfield) => {
|
||||
item[subfield.name] = this.state.item[subfield.name];
|
||||
});
|
||||
} else {
|
||||
item[field.name] = this.state.item[field.name];
|
||||
}
|
||||
});
|
||||
// set id if specified
|
||||
if (this.props.params.id !== undefined) {
|
||||
item.id = this.props.params.id;
|
||||
}
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'save',
|
||||
data: item,
|
||||
}).always(() => {
|
||||
this.setState({ loading: false });
|
||||
}).done(() => {
|
||||
if (this.props.onSuccess !== undefined) {
|
||||
this.props.onSuccess();
|
||||
} else {
|
||||
this.context.router.push('/');
|
||||
}
|
||||
|
||||
if (this.props.params.id !== undefined) {
|
||||
this.props.messages.onUpdate();
|
||||
} else {
|
||||
this.props.messages.onCreate();
|
||||
}
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
this.setState({ errors: response.errors });
|
||||
}
|
||||
});
|
||||
},
|
||||
handleValueChange: function handleValueChange(e) {
|
||||
if (this.props.onChange) {
|
||||
return this.props.onChange(e);
|
||||
}
|
||||
const item = this.state.item;
|
||||
const field = e.target.name;
|
||||
|
||||
item[field] = e.target.value;
|
||||
|
||||
this.setState({
|
||||
item,
|
||||
});
|
||||
return true;
|
||||
},
|
||||
render: function render() {
|
||||
let errors;
|
||||
if (this.getErrors() !== undefined) {
|
||||
errors = this.getErrors().map(error => (
|
||||
<div className="mailpoet_notice notice inline error is-dismissible" key={`error-${error.message}`}>
|
||||
<p>{ error.message }</p>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
const formClasses = classNames(
|
||||
'mailpoet_form',
|
||||
{ mailpoet_form_loading: this.state.loading || this.props.loading }
|
||||
);
|
||||
|
||||
let beforeFormContent = false;
|
||||
let afterFormContent = false;
|
||||
|
||||
if (this.props.beforeFormContent !== undefined) {
|
||||
beforeFormContent = this.props.beforeFormContent(this.getValues());
|
||||
}
|
||||
|
||||
if (this.props.afterFormContent !== undefined) {
|
||||
afterFormContent = this.props.afterFormContent(this.getValues());
|
||||
}
|
||||
|
||||
const fields = this.props.fields.map((field) => {
|
||||
// Compose an onChange handler from the default and custom one
|
||||
let onValueChange = this.handleValueChange;
|
||||
if (field.onBeforeChange) {
|
||||
onValueChange = (e) => {
|
||||
field.onBeforeChange(e);
|
||||
return this.handleValueChange(e);
|
||||
};
|
||||
},
|
||||
getValues: function () {
|
||||
return this.props.item ? this.props.item : this.state.item;
|
||||
},
|
||||
getErrors: function () {
|
||||
return this.props.errors ? this.props.errors : this.state.errors;
|
||||
},
|
||||
componentDidMount: function () {
|
||||
if(this.isMounted()) {
|
||||
if(this.props.params.id !== undefined) {
|
||||
this.loadItem(this.props.params.id);
|
||||
} else {
|
||||
this.setState({
|
||||
item: jQuery('.mailpoet_form').serializeObject(),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
componentWillReceiveProps: function (props) {
|
||||
if(props.params.id === undefined) {
|
||||
this.setState({
|
||||
loading: false,
|
||||
item: {},
|
||||
});
|
||||
if (props.item === undefined) {
|
||||
this.refs.form.reset();
|
||||
}
|
||||
} else {
|
||||
this.loadItem(props.params.id);
|
||||
}
|
||||
},
|
||||
loadItem: function (id) {
|
||||
this.setState({ loading: true });
|
||||
}
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'get',
|
||||
data: {
|
||||
id: id,
|
||||
},
|
||||
}).done((response) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
item: response.data,
|
||||
});
|
||||
}).fail(() => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
item: {},
|
||||
}, function () {
|
||||
this.context.router.push('/new');
|
||||
});
|
||||
});
|
||||
},
|
||||
handleSubmit: function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
// handle validation
|
||||
if(this.props.isValid !== undefined) {
|
||||
if(this.props.isValid() === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
// only get values from displayed fields
|
||||
const item = {};
|
||||
this.props.fields.map((field) => {
|
||||
if(field['fields'] !== undefined) {
|
||||
field.fields.map((subfield) => {
|
||||
item[subfield.name] = this.state.item[subfield.name];
|
||||
});
|
||||
} else {
|
||||
item[field.name] = this.state.item[field.name];
|
||||
}
|
||||
});
|
||||
// set id if specified
|
||||
if(this.props.params.id !== undefined) {
|
||||
item.id = this.props.params.id;
|
||||
}
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'save',
|
||||
data: item,
|
||||
}).always(() => {
|
||||
this.setState({ loading: false });
|
||||
}).done(() => {
|
||||
if(this.props.onSuccess !== undefined) {
|
||||
this.props.onSuccess();
|
||||
} else {
|
||||
this.context.router.push('/');
|
||||
}
|
||||
|
||||
if(this.props.params.id !== undefined) {
|
||||
this.props.messages.onUpdate();
|
||||
} else {
|
||||
this.props.messages.onCreate();
|
||||
}
|
||||
}).fail((response) => {
|
||||
if(response.errors.length > 0) {
|
||||
this.setState({ errors: response.errors });
|
||||
}
|
||||
});
|
||||
},
|
||||
handleValueChange: function (e) {
|
||||
if (this.props.onChange) {
|
||||
return this.props.onChange(e);
|
||||
} else {
|
||||
const item = this.state.item;
|
||||
const field = e.target.name;
|
||||
|
||||
item[field] = e.target.value;
|
||||
|
||||
this.setState({
|
||||
item: item,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
},
|
||||
render: function () {
|
||||
let errors;
|
||||
if(this.getErrors() !== undefined) {
|
||||
errors = this.getErrors().map((error, index) => {
|
||||
return (
|
||||
<p key={ 'error-'+index } className="mailpoet_error">
|
||||
{ error.message }
|
||||
</p>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const formClasses = classNames(
|
||||
'mailpoet_form',
|
||||
{ mailpoet_form_loading: this.state.loading || this.props.loading }
|
||||
);
|
||||
|
||||
let beforeFormContent = false;
|
||||
let afterFormContent = false;
|
||||
|
||||
if (this.props.beforeFormContent !== undefined) {
|
||||
beforeFormContent = this.props.beforeFormContent(this.getValues());
|
||||
}
|
||||
|
||||
if (this.props.afterFormContent !== undefined) {
|
||||
afterFormContent = this.props.afterFormContent(this.getValues());
|
||||
}
|
||||
|
||||
const fields = this.props.fields.map((field, i) => {
|
||||
// Compose an onChange handler from the default and custom one
|
||||
let onValueChange = this.handleValueChange;
|
||||
if (field.onBeforeChange) {
|
||||
onValueChange = (e) => {
|
||||
field.onBeforeChange(e);
|
||||
return this.handleValueChange(e);
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<FormField
|
||||
field={ field }
|
||||
item={ this.getValues() }
|
||||
onValueChange={ onValueChange }
|
||||
key={ 'field-'+i } />
|
||||
);
|
||||
});
|
||||
|
||||
let actions = false;
|
||||
if(this.props.children) {
|
||||
actions = this.props.children;
|
||||
} else {
|
||||
actions = (
|
||||
<input
|
||||
className="button button-primary"
|
||||
type="submit"
|
||||
value={MailPoet.I18n.t('save')}
|
||||
disabled={this.state.loading} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ beforeFormContent }
|
||||
<form
|
||||
id={ this.props.id }
|
||||
ref="form"
|
||||
className={ formClasses }
|
||||
onSubmit={
|
||||
(this.props.onSubmit !== undefined)
|
||||
? this.props.onSubmit
|
||||
: this.handleSubmit
|
||||
}
|
||||
>
|
||||
{ errors }
|
||||
|
||||
<table className="form-table">
|
||||
<tbody>
|
||||
{fields}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{ actions }
|
||||
</form>
|
||||
{ afterFormContent }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
return (
|
||||
<FormField
|
||||
field={field}
|
||||
item={this.getValues()}
|
||||
onValueChange={onValueChange}
|
||||
key={`field-${field.name}`}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return Form;
|
||||
}
|
||||
);
|
||||
let actions = false;
|
||||
if (this.props.children) {
|
||||
actions = this.props.children;
|
||||
} else {
|
||||
actions = (
|
||||
<input
|
||||
className="button button-primary"
|
||||
type="submit"
|
||||
value={MailPoet.I18n.t('save')}
|
||||
disabled={this.state.loading}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ beforeFormContent }
|
||||
<form
|
||||
id={this.props.id}
|
||||
ref={(c) => { this.form = c; }}
|
||||
className={formClasses}
|
||||
onSubmit={
|
||||
(this.props.onSubmit !== undefined)
|
||||
? this.props.onSubmit
|
||||
: this.handleSubmit
|
||||
}
|
||||
data-automation-id={this.props.automationId}
|
||||
>
|
||||
{ errors }
|
||||
|
||||
<table className="form-table">
|
||||
<tbody>
|
||||
{fields}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{ actions }
|
||||
</form>
|
||||
{ afterFormContent }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default Form;
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Router, Route, IndexRoute, useRouterHistory } from 'react-router';
|
||||
import { createHashHistory } from 'history';
|
||||
import FormList from 'forms/list.jsx';
|
||||
import FormList from './list.jsx';
|
||||
|
||||
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||
|
||||
@ -14,12 +14,12 @@ const App = React.createClass({
|
||||
|
||||
const container = document.getElementById('forms_container');
|
||||
|
||||
if(container) {
|
||||
if (container) {
|
||||
ReactDOM.render((
|
||||
<Router history={ history }>
|
||||
<Route path="/" component={ App }>
|
||||
<IndexRoute component={ FormList } />
|
||||
<Route path="*" component={ FormList } />
|
||||
<Router history={history}>
|
||||
<Route path="/" component={App}>
|
||||
<IndexRoute component={FormList} />
|
||||
<Route path="*" component={FormList} />
|
||||
</Route>
|
||||
</Router>
|
||||
), container);
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import Listing from 'listing/listing.jsx';
|
||||
import classNames from 'classnames';
|
||||
import MailPoet from 'mailpoet';
|
||||
import jQuery from 'jquery';
|
||||
import Listing from '../listing/listing.jsx';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@ -26,7 +27,7 @@ const columns = [
|
||||
|
||||
const messages = {
|
||||
onTrash: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -41,7 +42,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onDelete: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -56,7 +57,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onRestore: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -72,7 +73,7 @@ const messages = {
|
||||
},
|
||||
};
|
||||
|
||||
const bulk_actions = [
|
||||
const bulkActions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
@ -80,20 +81,20 @@ const bulk_actions = [
|
||||
},
|
||||
];
|
||||
|
||||
const item_actions = [
|
||||
const itemActions = [
|
||||
{
|
||||
name: 'edit',
|
||||
label: MailPoet.I18n.t('edit'),
|
||||
link: function (item) {
|
||||
link: function link(item) {
|
||||
return (
|
||||
<a href={ `admin.php?page=mailpoet-form-editor&id=${item.id}` }>{MailPoet.I18n.t('edit')}</a>
|
||||
<a href={`admin.php?page=mailpoet-form-editor&id=${item.id}`}>{MailPoet.I18n.t('edit')}</a>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'duplicate',
|
||||
label: MailPoet.I18n.t('duplicate'),
|
||||
onClick: function (item, refresh) {
|
||||
onClick: function onClick(item, refresh) {
|
||||
return MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'forms',
|
||||
@ -109,7 +110,7 @@ const item_actions = [
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -128,40 +129,39 @@ const FormList = React.createClass({
|
||||
endpoint: 'forms',
|
||||
action: 'create',
|
||||
}).done((response) => {
|
||||
window.location = mailpoet_form_edit_url + response.data.id;
|
||||
window.location = window.mailpoet_form_edit_url + response.data.id;
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
renderItem(form, actions) {
|
||||
const row_classes = classNames(
|
||||
const rowClasses = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
let segments = mailpoet_segments.filter((segment) => {
|
||||
return (jQuery.inArray(segment.id, form.segments) !== -1);
|
||||
}).map((segment) => {
|
||||
return segment.name;
|
||||
}).join(', ');
|
||||
let segments = window.mailpoet_segments
|
||||
.filter(segment => (jQuery.inArray(segment.id, form.segments) !== -1))
|
||||
.map(segment => segment.name)
|
||||
.join(', ');
|
||||
|
||||
if (form.settings.segments_selected_by === 'user') {
|
||||
segments = MailPoet.I18n.t('userChoice') + ' ' + segments;
|
||||
segments = `${MailPoet.I18n.t('userChoice')} ${segments}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={ row_classes }>
|
||||
<td className={rowClasses}>
|
||||
<strong>
|
||||
<a
|
||||
className="row-title"
|
||||
href={ `admin.php?page=mailpoet-form-editor&id=${form.id}` }
|
||||
href={`admin.php?page=mailpoet-form-editor&id=${form.id}`}
|
||||
>{ form.name }</a>
|
||||
</strong>
|
||||
{ actions }
|
||||
@ -185,21 +185,22 @@ const FormList = React.createClass({
|
||||
{MailPoet.I18n.t('pageTitle')} <a
|
||||
className="page-title-action"
|
||||
href="javascript:;"
|
||||
onClick={ this.createForm }
|
||||
onClick={this.createForm}
|
||||
data-automation-id="create_new_form"
|
||||
>{MailPoet.I18n.t('new')}</a>
|
||||
</h1>
|
||||
|
||||
<Listing
|
||||
limit={ mailpoet_listing_per_page }
|
||||
location={ this.props.location }
|
||||
params={ this.props.params }
|
||||
messages={ messages }
|
||||
search={ false }
|
||||
limit={window.mailpoet_listing_per_page}
|
||||
location={this.props.location}
|
||||
params={this.props.params}
|
||||
messages={messages}
|
||||
search={false}
|
||||
endpoint="forms"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions }
|
||||
item_actions={ item_actions }
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
bulk_actions={bulkActions}
|
||||
item_actions={itemActions}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,117 +1,117 @@
|
||||
define('handlebars_helpers', ['handlebars'], function(Handlebars) {
|
||||
/* eslint-disable func-names */
|
||||
define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||
// Handlebars helpers
|
||||
Handlebars.registerHelper('concat', function() {
|
||||
var size = (arguments.length - 1),
|
||||
output = '';
|
||||
for(var i = 0; i < size; i++) {
|
||||
output += arguments[i];
|
||||
};
|
||||
return output;
|
||||
Handlebars.registerHelper('concat', function () {
|
||||
var size = (arguments.length - 1);
|
||||
var output = '';
|
||||
var i;
|
||||
for (i = 0; i < size; i += 1) {
|
||||
output += arguments[i];
|
||||
}
|
||||
return output;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('number_format', function(value, block) {
|
||||
return Number(value).toLocaleString();
|
||||
Handlebars.registerHelper('number_format', function (value) {
|
||||
return Number(value).toLocaleString();
|
||||
});
|
||||
Handlebars.registerHelper('date_format', function(timestamp, block) {
|
||||
if(window.moment) {
|
||||
if(timestamp === undefined || isNaN(timestamp) || timestamp <= 0) {
|
||||
return;
|
||||
}
|
||||
Handlebars.registerHelper('date_format', function (timestamp, block) {
|
||||
var f;
|
||||
if (window.moment) {
|
||||
if (timestamp === undefined || isNaN(timestamp) || timestamp <= 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// set date format
|
||||
var f = block.hash.format || 'MMM Do, YYYY';
|
||||
// check if we passed a timestamp
|
||||
if(parseInt(timestamp, 10) == timestamp) {
|
||||
return moment.unix(timestamp).format(f);
|
||||
} else {
|
||||
return moment.utc(timestamp).format(f);
|
||||
}
|
||||
} else {
|
||||
return timestamp;
|
||||
};
|
||||
// set date format
|
||||
f = block.hash.format || 'MMM Do, YYYY';
|
||||
// check if we passed a timestamp
|
||||
if (/^\s*\d+\s*$/.test(timestamp)) {
|
||||
return window.moment.unix(timestamp).format(f);
|
||||
}
|
||||
return window.moment.utc(timestamp).format(f);
|
||||
}
|
||||
return timestamp;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('cycle', function(value, block) {
|
||||
Handlebars.registerHelper('cycle', function (value, block) {
|
||||
var values = value.split(' ');
|
||||
return values[block.data.index % (values.length + 1)];
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
|
||||
switch (operator) {
|
||||
case '==':
|
||||
return (v1 == v2) ? options.fn(this) : options.inverse(this);
|
||||
case '===':
|
||||
return (v1 === v2) ? options.fn(this) : options.inverse(this);
|
||||
case '!=':
|
||||
return (v1 != v2) ? options.fn(this) : options.inverse(this);
|
||||
case '!==':
|
||||
return (v1 !== v2) ? options.fn(this) : options.inverse(this);
|
||||
case '<':
|
||||
return (v1 < v2) ? options.fn(this) : options.inverse(this);
|
||||
case '<=':
|
||||
return (v1 <= v2) ? options.fn(this) : options.inverse(this);
|
||||
case '>':
|
||||
return (v1 > v2) ? options.fn(this) : options.inverse(this);
|
||||
case '>=':
|
||||
return (v1 >= v2) ? options.fn(this) : options.inverse(this);
|
||||
case '&&':
|
||||
return (v1 && v2) ? options.fn(this) : options.inverse(this);
|
||||
case '||':
|
||||
return (v1 || v2) ? options.fn(this) : options.inverse(this);
|
||||
case 'in':
|
||||
var values = v2.split(',');
|
||||
return (v2.indexOf(v1) !== -1) ? options.fn(this) : options.inverse(this);
|
||||
default:
|
||||
return options.inverse(this);
|
||||
}
|
||||
switch (operator) {
|
||||
case '==':
|
||||
return (v1 == v2) ? options.fn(this) : options.inverse(this); // eslint-disable-line eqeqeq
|
||||
case '===':
|
||||
return (v1 === v2) ? options.fn(this) : options.inverse(this);
|
||||
case '!=':
|
||||
return (v1 != v2) ? options.fn(this) : options.inverse(this); // eslint-disable-line eqeqeq
|
||||
case '!==':
|
||||
return (v1 !== v2) ? options.fn(this) : options.inverse(this);
|
||||
case '<':
|
||||
return (v1 < v2) ? options.fn(this) : options.inverse(this);
|
||||
case '<=':
|
||||
return (v1 <= v2) ? options.fn(this) : options.inverse(this);
|
||||
case '>':
|
||||
return (v1 > v2) ? options.fn(this) : options.inverse(this);
|
||||
case '>=':
|
||||
return (v1 >= v2) ? options.fn(this) : options.inverse(this);
|
||||
case '&&':
|
||||
return (v1 && v2) ? options.fn(this) : options.inverse(this);
|
||||
case '||':
|
||||
return (v1 || v2) ? options.fn(this) : options.inverse(this);
|
||||
case 'in':
|
||||
return (v2.indexOf(v1) !== -1) ? options.fn(this) : options.inverse(this);
|
||||
default:
|
||||
return options.inverse(this);
|
||||
}
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('nl2br', function(value, block) {
|
||||
return value.gsub('\n', '<br />');
|
||||
Handlebars.registerHelper('nl2br', function (value) {
|
||||
return value.gsub('\n', '<br />');
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('json_encode', function(value, block) {
|
||||
return JSON.stringify(value);
|
||||
Handlebars.registerHelper('json_encode', function (value) {
|
||||
return JSON.stringify(value);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('json_decode', function(value, block) {
|
||||
return JSON.parse(value);
|
||||
Handlebars.registerHelper('json_decode', function (value) {
|
||||
return JSON.parse(value);
|
||||
});
|
||||
Handlebars.registerHelper('url', function(value, block) {
|
||||
var url = window.location.protocol + '//' + window.location.host + window.location.pathname;
|
||||
Handlebars.registerHelper('url', function (value) {
|
||||
var url = window.location.protocol + '//' + window.location.host + window.location.pathname;
|
||||
|
||||
return url + value;
|
||||
return url + value;
|
||||
});
|
||||
Handlebars.registerHelper('emailFromMailto', function(value) {
|
||||
var mailtoMatchingRegex = /^mailto\:/i;
|
||||
if (typeof value === 'string' && value.match(mailtoMatchingRegex)) {
|
||||
return value.replace(mailtoMatchingRegex, '');
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
Handlebars.registerHelper('emailFromMailto', function (value) {
|
||||
var mailtoMatchingRegex = /^mailto:/i;
|
||||
if (typeof value === 'string' && value.match(mailtoMatchingRegex)) {
|
||||
return value.replace(mailtoMatchingRegex, '');
|
||||
}
|
||||
return value;
|
||||
});
|
||||
Handlebars.registerHelper('lookup', function(obj, field, options) {
|
||||
return obj && obj[field];
|
||||
Handlebars.registerHelper('lookup', function (obj, field) {
|
||||
return obj && obj[field];
|
||||
});
|
||||
|
||||
|
||||
Handlebars.registerHelper('rsa_key', function(value, block) {
|
||||
// extract all lines into an array
|
||||
if(value === undefined) return '';
|
||||
Handlebars.registerHelper('rsa_key', function (value) {
|
||||
var lines;
|
||||
// extract all lines into an array
|
||||
if (value === undefined) return '';
|
||||
|
||||
var lines = value.trim().split('\n');
|
||||
lines = value.trim().split('\n');
|
||||
|
||||
// remove header & footer
|
||||
lines.shift();
|
||||
lines.pop();
|
||||
// remove header & footer
|
||||
lines.shift();
|
||||
lines.pop();
|
||||
|
||||
// return concatenated lines
|
||||
return lines.join('');
|
||||
// return concatenated lines
|
||||
return lines.join('');
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('trim', function(value, block) {
|
||||
if(value === null || value === undefined) return '';
|
||||
return value.trim();
|
||||
Handlebars.registerHelper('trim', function (value) {
|
||||
if (value === null || value === undefined) return '';
|
||||
return value.trim();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -125,24 +125,23 @@ define('handlebars_helpers', ['handlebars'], function(Handlebars) {
|
||||
* @return {String} The truncated string.
|
||||
*/
|
||||
Handlebars.registerHelper('ellipsis', function (str, limit, append) {
|
||||
var strAppend = append;
|
||||
if (strAppend === undefined) {
|
||||
strAppend = '';
|
||||
}
|
||||
var sanitized = str.replace(/(<([^>]+)>)/g, '');
|
||||
if (sanitized.length > limit) {
|
||||
return sanitized.substr(0, limit - strAppend.length) + strAppend;
|
||||
} else {
|
||||
return sanitized;
|
||||
}
|
||||
var strAppend = append;
|
||||
var sanitized = str.replace(/(<([^>]+)>)/g, '');
|
||||
if (strAppend === undefined) {
|
||||
strAppend = '';
|
||||
}
|
||||
if (sanitized.length > limit) {
|
||||
return sanitized.substr(0, limit - strAppend.length) + strAppend;
|
||||
}
|
||||
return sanitized;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('getNumber', function (string) {
|
||||
return parseInt(string, 10);
|
||||
return parseInt(string, 10);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('fontWithFallback', function(font) {
|
||||
switch(font) {
|
||||
Handlebars.registerHelper('fontWithFallback', function (font) {
|
||||
switch (font) {
|
||||
case 'Arial': return new Handlebars.SafeString("Arial, 'Helvetica Neue', Helvetica, sans-serif");
|
||||
case 'Comic Sans MS': return new Handlebars.SafeString("'Comic Sans MS', 'Marker Felt-Thin', Arial, sans-serif");
|
||||
case 'Courier New': return new Handlebars.SafeString("'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace");
|
||||
|
@ -1,11 +1,11 @@
|
||||
define('helpTooltip', ['mailpoet', 'react', 'react-dom', 'help-tooltip.jsx'],
|
||||
function (mp, React, ReactDOM, TooltipComponent) {
|
||||
function helpTooltip(mp, React, ReactDOM, TooltipComponent) {
|
||||
'use strict';
|
||||
|
||||
var MailPoet = mp;
|
||||
|
||||
MailPoet.helpTooltip = {
|
||||
show: function (domContainerNode, opts) {
|
||||
|
||||
show: function show(domContainerNode, opts) {
|
||||
ReactDOM.render(React.createElement(
|
||||
TooltipComponent, {
|
||||
tooltip: opts.tooltip,
|
||||
@ -15,7 +15,6 @@ define('helpTooltip', ['mailpoet', 'react', 'react-dom', 'help-tooltip.jsx'],
|
||||
), domContainerNode);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -6,15 +6,15 @@ function Tooltip(props) {
|
||||
let tooltipId = props.tooltipId;
|
||||
let tooltip = props.tooltip;
|
||||
// tooltip ID must be unique, defaults to tooltip text
|
||||
if(!props.tooltipId && typeof props.tooltip === 'string') {
|
||||
if (!props.tooltipId && typeof props.tooltip === 'string') {
|
||||
tooltipId = props.tooltip;
|
||||
}
|
||||
|
||||
if(typeof props.tooltip === 'string') {
|
||||
if (typeof props.tooltip === 'string') {
|
||||
tooltip = (<span
|
||||
style={{
|
||||
pointerEvents: 'all',
|
||||
maxWidth: '400',
|
||||
maxWidth: '400px',
|
||||
display: 'inline-block',
|
||||
}}
|
||||
>
|
||||
@ -32,17 +32,16 @@ function Tooltip(props) {
|
||||
data-event="click"
|
||||
data-tip
|
||||
data-for={tooltipId}
|
||||
/>
|
||||
<ReactTooltip
|
||||
globalEventOff="click"
|
||||
multiline
|
||||
id={tooltipId}
|
||||
efect="solid"
|
||||
place={props.place}
|
||||
>
|
||||
</span>
|
||||
<ReactTooltip
|
||||
globalEventOff="click"
|
||||
multiline={true}
|
||||
id={tooltipId}
|
||||
efect="solid"
|
||||
place={props.place}
|
||||
>
|
||||
{tooltip}
|
||||
</ReactTooltip>
|
||||
{tooltip}
|
||||
</ReactTooltip>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
65
assets/js/src/help/cron_status.jsx
Normal file
@ -0,0 +1,65 @@
|
||||
import MailPoet from 'mailpoet';
|
||||
import React from 'react';
|
||||
import KeyValueTable from 'common/key_value_table.jsx';
|
||||
import PrintBoolean from 'common/print_boolean.jsx';
|
||||
|
||||
const CronStatus = (props) => {
|
||||
const status = props.status_data;
|
||||
const activeStatusMapping = {
|
||||
active: MailPoet.I18n.t('running'),
|
||||
inactive: MailPoet.I18n.t('cronWaiting'),
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<h2>{MailPoet.I18n.t('systemStatusCronStatusTitle')}</h2>
|
||||
<KeyValueTable max_width={'400px'}>{[
|
||||
{
|
||||
key: MailPoet.I18n.t('accessible'),
|
||||
value: <PrintBoolean>{status.accessible}</PrintBoolean>,
|
||||
},
|
||||
{
|
||||
key: MailPoet.I18n.t('status'),
|
||||
value: activeStatusMapping[status.status] ? activeStatusMapping[status.status] : MailPoet.I18n.t('unknown'),
|
||||
},
|
||||
{
|
||||
key: MailPoet.I18n.t('lastUpdated'),
|
||||
value: status.updated_at ? MailPoet.Date.full(status.updated_at * 1000) : MailPoet.I18n.t('unknown'),
|
||||
},
|
||||
{
|
||||
key: MailPoet.I18n.t('lastRunStarted'),
|
||||
value: status.run_accessed_at ? MailPoet.Date.full(status.run_started_at * 1000) : MailPoet.I18n.t('unknown'),
|
||||
},
|
||||
{
|
||||
key: MailPoet.I18n.t('lastRunCompleted'),
|
||||
value: status.run_completed_at ? MailPoet.Date.full(status.run_completed_at * 1000) : MailPoet.I18n.t('unknown'),
|
||||
},
|
||||
{
|
||||
key: MailPoet.I18n.t('lastSeenError'),
|
||||
value: status.last_error || MailPoet.I18n.t('none'),
|
||||
}]}
|
||||
</KeyValueTable>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CronStatus.propTypes = {
|
||||
status_data: React.PropTypes.shape({
|
||||
accessible: React.PropTypes.bool,
|
||||
status: React.PropTypes.string,
|
||||
updated_at: React.PropTypes.number,
|
||||
run_accessed_at: React.PropTypes.number,
|
||||
run_completed_at: React.PropTypes.number,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
CronStatus.defaultProps = {
|
||||
status_data: {
|
||||
accessible: null,
|
||||
status: null,
|
||||
updated_at: null,
|
||||
run_accessed_at: null,
|
||||
run_completed_at: null,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = CronStatus;
|
@ -3,8 +3,9 @@ import ReactDOM from 'react-dom';
|
||||
import { Router, Route, IndexRedirect, useRouterHistory } from 'react-router';
|
||||
import { createHashHistory } from 'history';
|
||||
|
||||
import KnowledgeBase from 'help/knowledge_base.jsx';
|
||||
import SystemStatus from 'help/system_status.jsx';
|
||||
import SystemInfo from 'help/system_info.jsx';
|
||||
import KnowledgeBase from 'help/knowledge_base.jsx';
|
||||
|
||||
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||
|
||||
@ -16,17 +17,16 @@ const App = React.createClass({
|
||||
|
||||
const container = document.getElementById('help_container');
|
||||
|
||||
if(container) {
|
||||
|
||||
if (container) {
|
||||
ReactDOM.render((
|
||||
<Router history={ history }>
|
||||
<Route path="/" component={ App }>
|
||||
<Router history={history}>
|
||||
<Route path="/" component={App}>
|
||||
<IndexRedirect to="knowledgeBase" />
|
||||
{/* Pages */}
|
||||
<Route path="knowledgeBase(/)**" params={{ tab: 'knowledgeBase' }} component={ KnowledgeBase } />
|
||||
<Route path="systemInfo(/)**" params={{ tab: 'systemInfo' }} component={ SystemInfo } />
|
||||
<Route path="knowledgeBase(/)**" params={{ tab: 'knowledgeBase' }} component={KnowledgeBase} />
|
||||
<Route path="systemStatus(/)**" params={{ tab: 'systemStatus' }} component={SystemStatus} />
|
||||
<Route path="systemInfo(/)**" params={{ tab: 'systemInfo' }} component={SystemInfo} />
|
||||
</Route>
|
||||
</Router>
|
||||
), container);
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import MailPoet from 'mailpoet';
|
||||
import Tabs from './tabs.jsx';
|
||||
|
||||
function KnowledgeBase() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@ -12,18 +11,18 @@ function KnowledgeBase() {
|
||||
|
||||
<p>{MailPoet.I18n.t('knowledgeBaseIntro')}</p>
|
||||
<ul>
|
||||
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/116-common-problems">Common Problems</a></li>
|
||||
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/165-newsletters">Newsletters</a></li>
|
||||
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/156-migration-questions">Migration Questions</a></li>
|
||||
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/149-sending-methods">Sending Methods</a></li>
|
||||
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/139-subscription-forms">Subscription Forms</a></li>
|
||||
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/114-getting-started">Getting Started</a></li>
|
||||
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/123-newsletter-designer">Newsletter Designer</a></li>
|
||||
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/121-subscribers-and-lists">Subscribers and Lists</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/116-common-problems">Common Problems</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/165-newsletters">Newsletters</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/156-migration-questions">Migration Questions</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/149-sending-methods">Sending Methods</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/139-subscription-forms">Subscription Forms</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/114-getting-started">Getting Started</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/123-newsletter-designer">Newsletter Designer</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/121-subscribers-and-lists">Subscribers and Lists</a></li>
|
||||
</ul>
|
||||
<a target="_blank" href="http://beta.docs.mailpoet.com/" className="button button-primary">{MailPoet.I18n.t('knowledgeBaseButton')}</a>
|
||||
<a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/" className="button button-primary">{MailPoet.I18n.t('knowledgeBaseButton')}</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = KnowledgeBase;
|
||||
|
85
assets/js/src/help/queue_status.jsx
Normal file
@ -0,0 +1,85 @@
|
||||
import MailPoet from 'mailpoet';
|
||||
import React from 'react';
|
||||
import KeyValueTable from 'common/key_value_table.jsx';
|
||||
import TasksList from './tasks_list/tasks_list.jsx';
|
||||
import TasksListDataRow from './tasks_list/tasks_list_data_row.jsx';
|
||||
|
||||
const QueueStatus = (props) => {
|
||||
const status = props.status_data;
|
||||
return (
|
||||
<div>
|
||||
<h2>{MailPoet.I18n.t('systemStatusQueueTitle')}</h2>
|
||||
<KeyValueTable max_width={'400px'}>{
|
||||
[{
|
||||
key: MailPoet.I18n.t('status'),
|
||||
value: status.status === 'paused' ? MailPoet.I18n.t('paused') : MailPoet.I18n.t('running'),
|
||||
}, {
|
||||
key: MailPoet.I18n.t('startedAt'),
|
||||
value: status.started ? MailPoet.Date.full(status.started * 1000) : MailPoet.I18n.t('unknown'),
|
||||
}, {
|
||||
key: MailPoet.I18n.t('sentEmails'),
|
||||
value: status.sent || 0,
|
||||
}, {
|
||||
key: MailPoet.I18n.t('retryAttempt'),
|
||||
value: status.retry_attempt || MailPoet.I18n.t('none'),
|
||||
}, {
|
||||
key: MailPoet.I18n.t('retryAt'),
|
||||
value: status.retry_at ? MailPoet.Date.full(status.retry_at * 1000) : MailPoet.I18n.t('none'),
|
||||
}, {
|
||||
key: MailPoet.I18n.t('error'),
|
||||
value: status.error || MailPoet.I18n.t('none'),
|
||||
}, {
|
||||
key: MailPoet.I18n.t('totalCompletedTasks'),
|
||||
value: status.tasksStatusCounts.completed,
|
||||
}, {
|
||||
key: MailPoet.I18n.t('totalRunningTasks'),
|
||||
value: status.tasksStatusCounts.running,
|
||||
}, {
|
||||
key: MailPoet.I18n.t('totalPausedTasks'),
|
||||
value: status.tasksStatusCounts.paused,
|
||||
}, {
|
||||
key: MailPoet.I18n.t('totalScheduledTasks'),
|
||||
value: status.tasksStatusCounts.scheduled,
|
||||
}]}
|
||||
</KeyValueTable>
|
||||
|
||||
<h4>{MailPoet.I18n.t('scheduledTasks')}</h4>
|
||||
<TasksList show_scheduled_at tasks={status.latestTasks.filter(task => (task.status === 'scheduled'))} />
|
||||
|
||||
<h4>{MailPoet.I18n.t('runningTasks')}</h4>
|
||||
<TasksList tasks={status.latestTasks.filter(task => (task.status === null))} />
|
||||
|
||||
<h4>{MailPoet.I18n.t('completedTasks')}</h4>
|
||||
<TasksList tasks={status.latestTasks.filter(task => (task.status === 'completed'))} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
QueueStatus.propTypes = {
|
||||
status_data: React.PropTypes.shape({
|
||||
status: React.PropTypes.string,
|
||||
started: React.PropTypes.number,
|
||||
sent: React.PropTypes.number,
|
||||
retry_attempt: React.PropTypes.number,
|
||||
retry_at: React.PropTypes.number,
|
||||
tasksStatusCounts: React.PropTypes.shape({
|
||||
completed: React.PropTypes.number.isRequired,
|
||||
running: React.PropTypes.number.isRequired,
|
||||
paused: React.PropTypes.number.isRequired,
|
||||
scheduled: React.PropTypes.number.isRequired,
|
||||
}).isRequired,
|
||||
latestTasks: React.PropTypes.arrayOf(TasksListDataRow.propTypes.task).isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
QueueStatus.defaultProps = {
|
||||
status_data: {
|
||||
status: null,
|
||||
started: null,
|
||||
sent: null,
|
||||
retry_attempt: null,
|
||||
retry_at: null,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = QueueStatus;
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
import _ from 'underscore';
|
||||
|
||||
import Tabs from './tabs.jsx';
|
||||
|
||||
function handleFocus(event) {
|
||||
@ -10,12 +9,10 @@ function handleFocus(event) {
|
||||
|
||||
function printData(data) {
|
||||
if (_.isObject(data)) {
|
||||
const printableData = Object.keys(data).map((key) => {
|
||||
return `${key}: ${data[key]}`;
|
||||
});
|
||||
const printableData = Object.keys(data).map(key => `${key}: ${data[key]}`);
|
||||
|
||||
return (<textarea
|
||||
readOnly={true}
|
||||
readOnly
|
||||
onFocus={handleFocus}
|
||||
value={printableData.join('\n')}
|
||||
style={{
|
||||
@ -23,25 +20,24 @@ function printData(data) {
|
||||
height: '400px',
|
||||
}}
|
||||
/>);
|
||||
} else {
|
||||
return (<p>{MailPoet.I18n.t('systemInfoDataError')}</p>);
|
||||
}
|
||||
return (<p>{MailPoet.I18n.t('systemInfoDataError')}</p>);
|
||||
}
|
||||
|
||||
function KnowledgeBase() {
|
||||
const data = window.help_scout_data;
|
||||
function SystemInfo() {
|
||||
const systemInfoData = window.systemInfoData;
|
||||
return (
|
||||
<div>
|
||||
|
||||
<Tabs tab="systemInfo" />
|
||||
|
||||
<div className="mailpoet_notice notice inline notice-success" style={{ marginTop: '1em' }}>
|
||||
<div className="mailpoet_notice notice inline" style={{ marginTop: '1em' }}>
|
||||
<p>{MailPoet.I18n.t('systemInfoIntro')}</p>
|
||||
</div>
|
||||
|
||||
{printData(data)}
|
||||
{printData(systemInfoData)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = KnowledgeBase;
|
||||
module.exports = SystemInfo;
|
||||
|
75
assets/js/src/help/system_status.jsx
Normal file
@ -0,0 +1,75 @@
|
||||
import MailPoet from 'mailpoet';
|
||||
import React from 'react';
|
||||
import ReactStringReplace from 'react-string-replace';
|
||||
import CronStatus from './cron_status.jsx';
|
||||
import QueueStatus from './queue_status.jsx';
|
||||
import Tabs from './tabs.jsx';
|
||||
|
||||
function renderStatusMessage(status, error, link) {
|
||||
const noticeType = (status) ? 'success' : 'error';
|
||||
let noticeMessage = (status) ?
|
||||
MailPoet.I18n.t('systemStatusConnectionSuccessful') :
|
||||
`${MailPoet.I18n.t('systemStatusConnectionUnsuccessful')} ${error}`;
|
||||
|
||||
if (link) {
|
||||
noticeMessage = ReactStringReplace(
|
||||
noticeMessage,
|
||||
/\[link\](.*?)\[\/link\]/g,
|
||||
match => (
|
||||
<a href={`${link}`} key="kb-link">{ match }</a>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`mailpoet_notice notice inline notice-${noticeType}`} style={{ marginTop: '1em' }}>
|
||||
<p>{noticeMessage}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderCronSection(data) {
|
||||
const status = data.cron.isReachable;
|
||||
const url = data.cron.url;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{MailPoet.I18n.t('systemStatusCronTitle')}</h2>
|
||||
<p>
|
||||
<a href={url} target="_blank">{url}</a>
|
||||
</p>
|
||||
{renderStatusMessage(status, MailPoet.I18n.t('systemStatusCronConnectionUnsuccessfulInfo'), '//beta.docs.mailpoet.com/article/231-sending-does-not-work')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderMSSSection(data) {
|
||||
if (!data.mss.enabled) return undefined;
|
||||
|
||||
const status = data.mss.enabled.isReachable;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{MailPoet.I18n.t('systemStatusMSSTitle')}</h2>
|
||||
{renderStatusMessage(status, MailPoet.I18n.t('systemStatusMSSConnectionUnsuccessfulInfo'), false)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SystemStatus() {
|
||||
const systemStatusData = window.systemStatusData;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tabs tab="systemStatus" />
|
||||
<div className="mailpoet_notice notice inline" style={{ marginTop: '1em' }}>
|
||||
<p>{systemStatusData.mss.enabled ? MailPoet.I18n.t('systemStatusIntroCronMSS') : MailPoet.I18n.t('systemStatusIntroCron')}</p>
|
||||
</div>
|
||||
{renderCronSection(systemStatusData)}
|
||||
{renderMSSSection(systemStatusData)}
|
||||
<CronStatus status_data={systemStatusData.cronStatus} />
|
||||
<QueueStatus status_data={systemStatusData.queueStatus} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
module.exports = SystemStatus;
|
@ -9,6 +9,11 @@ const tabs = [
|
||||
label: MailPoet.I18n.t('tabKnowledgeBaseTitle'),
|
||||
link: '/knowledgeBase',
|
||||
},
|
||||
{
|
||||
name: 'systemStatus',
|
||||
label: MailPoet.I18n.t('tabSystemStatusTitle'),
|
||||
link: '/systemStatus',
|
||||
},
|
||||
{
|
||||
name: 'systemInfo',
|
||||
label: MailPoet.I18n.t('tabSystemInfoTitle'),
|
||||
@ -17,8 +22,7 @@ const tabs = [
|
||||
];
|
||||
|
||||
function Tabs(props) {
|
||||
|
||||
const tabLinks = tabs.map((tab, index) => {
|
||||
const tabLinks = tabs.map((tab) => {
|
||||
const tabClasses = classNames(
|
||||
'nav-tab',
|
||||
{ 'nav-tab-active': (props.tab === tab.name) }
|
||||
@ -26,9 +30,9 @@ function Tabs(props) {
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={ 'tab-'+index }
|
||||
className={ tabClasses }
|
||||
to={ tab.link }
|
||||
key={`tab-${tab.name}`}
|
||||
className={tabClasses}
|
||||
to={tab.link}
|
||||
>{ tab.label }</Link>
|
||||
);
|
||||
});
|
||||
@ -38,7 +42,7 @@ function Tabs(props) {
|
||||
{ tabLinks }
|
||||
</h2>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
Tabs.propTypes = { tab: React.PropTypes.string };
|
||||
Tabs.defaultProps = { tab: 'knowledgeBase' };
|
||||
|
41
assets/js/src/help/tasks_list/tasks_list.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
import TaskListDataRow from './tasks_list_data_row.jsx';
|
||||
import TaskListLabelsRow from './tasks_list_labels_row.jsx';
|
||||
|
||||
const TasksList = (props) => {
|
||||
const colsCount = props.show_scheduled_at ? 6 : 5;
|
||||
|
||||
return (
|
||||
<table className="widefat fixed striped">
|
||||
<thead><TaskListLabelsRow show_scheduled_at={props.show_scheduled_at} /></thead>
|
||||
<tbody>
|
||||
{
|
||||
props.tasks.length ? props.tasks.map(task => (
|
||||
<TaskListDataRow
|
||||
key={task.id}
|
||||
task={task}
|
||||
show_scheduled_at={props.show_scheduled_at}
|
||||
/>)
|
||||
) : (
|
||||
<tr className="no-items">
|
||||
<td colSpan={colsCount}>{MailPoet.I18n.t('nothingToShow')}</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
</tbody>
|
||||
<tfoot><TaskListLabelsRow show_scheduled_at={props.show_scheduled_at} /></tfoot>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
|
||||
TasksList.propTypes = {
|
||||
show_scheduled_at: React.PropTypes.bool,
|
||||
tasks: React.PropTypes.arrayOf(TaskListDataRow.propTypes.task).isRequired,
|
||||
};
|
||||
|
||||
TasksList.defaultProps = {
|
||||
show_scheduled_at: false,
|
||||
};
|
||||
|
||||
module.exports = TasksList;
|
63
assets/js/src/help/tasks_list/tasks_list_data_row.jsx
Normal file
@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
const TasksListDataRow = props => (
|
||||
<tr>
|
||||
<td className="column column-primary">
|
||||
{ props.task.id }
|
||||
</td>
|
||||
<td className="column">
|
||||
{ props.task.type }
|
||||
</td>
|
||||
<td className="column">
|
||||
{ props.task.newsletter ? (
|
||||
<a
|
||||
href={props.task.newsletter.preview_url}
|
||||
data-newsletter-id={props.task.newsletter.newsletter_id}
|
||||
data-queue-id={props.task.newsletter.queue_id}
|
||||
target="_blank"
|
||||
>
|
||||
{props.task.newsletter.subject || MailPoet.I18n.t('preview')}
|
||||
</a>) : MailPoet.I18n.t('none')
|
||||
}
|
||||
</td>
|
||||
<td className="column">
|
||||
{ props.task.priority }
|
||||
</td>
|
||||
{ props.show_scheduled_at ? (
|
||||
<td className="column-date">
|
||||
<abbr>{ MailPoet.Date.format(props.task.scheduled_at * 1000) }</abbr>
|
||||
</td>
|
||||
) : null }
|
||||
<td className="column-date">
|
||||
<abbr>{ MailPoet.Date.format(props.task.updated_at * 1000) }</abbr>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
TasksListDataRow.propTypes = {
|
||||
show_scheduled_at: React.PropTypes.bool,
|
||||
task: React.PropTypes.shape({
|
||||
id: React.PropTypes.number.isRequired,
|
||||
type: React.PropTypes.string.isRequired,
|
||||
priority: React.PropTypes.number.isRequired,
|
||||
updated_at: React.PropTypes.number.isRequired,
|
||||
scheduled_at: React.PropTypes.number,
|
||||
status: React.PropTypes.string,
|
||||
newsletter: React.PropTypes.shape({
|
||||
newsletter_id: React.PropTypes.number.isRequired,
|
||||
queue_id: React.PropTypes.number.isRequired,
|
||||
preview_url: React.PropTypes.string.isRequired,
|
||||
subject: React.PropTypes.string,
|
||||
}),
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
TasksListDataRow.defaultProps = {
|
||||
show_scheduled_at: false,
|
||||
task: {
|
||||
newsletter: null,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = TasksListDataRow;
|
23
assets/js/src/help/tasks_list/tasks_list_labels_row.jsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
const TasksListLabelsRow = props => (
|
||||
<tr>
|
||||
<th className="row-title">Id</th>
|
||||
<th className="row-title">{MailPoet.I18n.t('type')}</th>
|
||||
<th className="row-title">{MailPoet.I18n.t('email')}</th>
|
||||
<th className="row-title">{MailPoet.I18n.t('priority')}</th>
|
||||
{ props.show_scheduled_at ? (<th className="row-title">{MailPoet.I18n.t('scheduledAt')}</th>) : null }
|
||||
<th className="row-title">{MailPoet.I18n.t('updatedAt')}</th>
|
||||
</tr>
|
||||
);
|
||||
|
||||
TasksListLabelsRow.propTypes = {
|
||||
show_scheduled_at: React.PropTypes.bool,
|
||||
};
|
||||
|
||||
TasksListLabelsRow.defaultProps = {
|
||||
show_scheduled_at: false,
|
||||
};
|
||||
|
||||
module.exports = TasksListLabelsRow;
|
@ -1,3 +0,0 @@
|
||||
define([], function() {
|
||||
!function(e, o, n){window.HSCW=o, window.HS=n, n.beacon=n.beacon||{};var t=n.beacon;t.userConfig={}, t.readyQueue=[], t.config=function(e){this.userConfig=e}, t.ready=function(e){this.readyQueue.push(e)}, o.config={docs:{enabled:!0, baseUrl:"//mailpoet3.helpscoutdocs.com/"}, contact:{enabled:!0, formId:"aa21ca80-a4f5-11e6-91aa-0a5fecc78a4d"}};var r=e.getElementsByTagName("script")[0], c=e.createElement("script");c.type="text/javascript", c.async=!0, c.src="https://djtflbt20bdde.cloudfront.net/", r.parentNode.insertBefore(c, r)}(document, window.HSCW||{}, window.HS||{});
|
||||
});
|
@ -1,24 +1,24 @@
|
||||
define('i18n',
|
||||
[
|
||||
'mailpoet'
|
||||
], function(
|
||||
], function i18n(
|
||||
mp
|
||||
) {
|
||||
'use strict';
|
||||
var MailPoet = mp;
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var translations = {};
|
||||
var MailPoet = mp;
|
||||
|
||||
MailPoet.I18n = {
|
||||
add: function(key, value) {
|
||||
translations[key] = value;
|
||||
},
|
||||
t: function(key) {
|
||||
return translations[key] || 'TRANSLATION "%$1s" NOT FOUND'.replace('%$1s', key);
|
||||
},
|
||||
all: function() {
|
||||
return translations;
|
||||
}
|
||||
};
|
||||
var translations = {};
|
||||
|
||||
});
|
||||
MailPoet.I18n = {
|
||||
add: function add(key, value) {
|
||||
translations[key] = value;
|
||||
},
|
||||
t: function t(key) {
|
||||
return translations[key] || 'TRANSLATION "%$1s" NOT FOUND'.replace('%$1s', key);
|
||||
},
|
||||
all: function all() {
|
||||
return translations;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -1,19 +1,20 @@
|
||||
define('iframe', ['mailpoet'], function(mp) {
|
||||
define('iframe', ['mailpoet'], function iframeModule(mp) {
|
||||
'use strict';
|
||||
|
||||
var MailPoet = mp;
|
||||
MailPoet.Iframe = {
|
||||
marginY: 20,
|
||||
autoSize: function(iframe) {
|
||||
if(!iframe) return;
|
||||
autoSize: function autoSize(iframe) {
|
||||
if (!iframe) return;
|
||||
|
||||
this.setSize(
|
||||
iframe,
|
||||
iframe.contentWindow.document.body.scrollHeight
|
||||
);
|
||||
},
|
||||
setSize: function(sizeIframe, i) {
|
||||
setSize: function setSize(sizeIframe, i) {
|
||||
var iframe = sizeIframe;
|
||||
if(!iframe) return;
|
||||
if (!iframe) return;
|
||||
|
||||
iframe.style.height = (
|
||||
parseInt(i, 10) + this.marginY
|
||||
|
@ -2,7 +2,7 @@ define(
|
||||
[
|
||||
'jquery'
|
||||
],
|
||||
function(
|
||||
function ( // eslint-disable-line func-names
|
||||
jQuery
|
||||
) {
|
||||
var $ = jQuery;
|
||||
@ -23,47 +23,50 @@ define(
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* http://benalman.com/about/license/
|
||||
*/
|
||||
$.fn.serializeObject = function(coerce) {
|
||||
var obj = {},
|
||||
coerce_types = { true: !0, false: !1, null: null };
|
||||
$.fn.mailpoetSerializeObject = function (coerce) { // eslint-disable-line func-names
|
||||
var obj = {};
|
||||
var coerceTypes = { true: !0, false: !1, null: null };
|
||||
|
||||
// Iterate over all name=value pairs.
|
||||
$.each( this.serializeArray(), function(j, v){
|
||||
var key = v.name,
|
||||
val = v.value,
|
||||
cur = obj,
|
||||
i = 0,
|
||||
$.each(this.serializeArray(), function (j, v) { // eslint-disable-line func-names
|
||||
var key = v.name;
|
||||
var val = v.value;
|
||||
var cur = obj;
|
||||
var i = 0;
|
||||
|
||||
// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
|
||||
// into its component parts.
|
||||
keys = key.split( '][' ),
|
||||
keys_last = keys.length - 1;
|
||||
// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
|
||||
// into its component parts.
|
||||
var keys = key.split('][');
|
||||
var keysLast = keys.length - 1;
|
||||
|
||||
// If the first keys part contains [ and the last ends with ], then []
|
||||
// are correctly balanced.
|
||||
if ( /\[/.test( keys[0] ) && /\]$/.test( keys[ keys_last ] ) ) {
|
||||
if (/\[/.test(keys[0]) && /\]$/.test(keys[keysLast])) {
|
||||
// Remove the trailing ] from the last keys part.
|
||||
keys[ keys_last ] = keys[ keys_last ].replace( /\]$/, '' );
|
||||
keys[keysLast] = keys[keysLast].replace(/\]$/, '');
|
||||
|
||||
// Split first keys part into two parts on the [ and add them back onto
|
||||
// the beginning of the keys array.
|
||||
keys = keys.shift().split('[').concat( keys );
|
||||
keys = keys.shift().split('[').concat(keys);
|
||||
|
||||
keys_last = keys.length - 1;
|
||||
keysLast = keys.length - 1;
|
||||
} else {
|
||||
// Basic 'foo' style key.
|
||||
keys_last = 0;
|
||||
keysLast = 0;
|
||||
}
|
||||
|
||||
// Coerce values.
|
||||
if ( coerce ) {
|
||||
val = val && !isNaN(val) ? +val // number
|
||||
: val === 'undefined' ? undefined // undefined
|
||||
: coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
|
||||
: val; // string
|
||||
if (coerce) {
|
||||
if (val && !isNaN(val)) { // number
|
||||
val = +val;
|
||||
} else if (val === 'undefined') { // undefined
|
||||
val = undefined;
|
||||
} else if (coerceTypes[val] !== undefined) { // true, false, null
|
||||
val = coerceTypes[val];
|
||||
}
|
||||
}
|
||||
|
||||
if ( keys_last ) {
|
||||
if (keysLast) {
|
||||
// Complex key, build deep object structure based on a few rules:
|
||||
// * The 'cur' pointer starts at the object top-level.
|
||||
// * [] = array push (n is set to array length), [n] = array if n is
|
||||
@ -73,31 +76,23 @@ define(
|
||||
// object or array based on the type of the next keys part.
|
||||
// * Move the 'cur' pointer to the next level.
|
||||
// * Rinse & repeat.
|
||||
for ( ; i <= keys_last; i++ ) {
|
||||
for (; i <= keysLast; i += 1) {
|
||||
key = keys[i] === '' ? cur.length : keys[i];
|
||||
cur[key] = i < keys_last
|
||||
? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] )
|
||||
cur[key] = i < keysLast
|
||||
? cur[key] || (keys[i + 1] && isNaN(keys[i + 1]) ? {} : [])
|
||||
: val;
|
||||
cur = cur[key];
|
||||
}
|
||||
|
||||
} else if ($.isArray(obj[key])) {
|
||||
// val is already an array, so push on the next value.
|
||||
obj[key].push(val);
|
||||
} else if (obj[key] !== undefined) {
|
||||
// val isn't an array, but since a second value has been specified,
|
||||
// convert val into an array.
|
||||
obj[key] = [obj[key], val];
|
||||
} else {
|
||||
// Simple key, even simpler rules, since only scalars and shallow
|
||||
// arrays are allowed.
|
||||
|
||||
if ( $.isArray( obj[key] ) ) {
|
||||
// val is already an array, so push on the next value.
|
||||
obj[key].push( val );
|
||||
|
||||
} else if ( obj[key] !== undefined ) {
|
||||
// val isn't an array, but since a second value has been specified,
|
||||
// convert val into an array.
|
||||
obj[key] = [ obj[key], val ];
|
||||
|
||||
} else {
|
||||
// val is a scalar.
|
||||
obj[key] = val;
|
||||
}
|
||||
// val is a scalar.
|
||||
obj[key] = val;
|
||||
}
|
||||
});
|
||||
|
||||
@ -106,4 +101,4 @@ define(
|
||||
|
||||
return $;
|
||||
}
|
||||
);
|
||||
);
|
||||
|
@ -1,122 +1,114 @@
|
||||
define([
|
||||
'react',
|
||||
'mailpoet',
|
||||
],
|
||||
(
|
||||
React,
|
||||
MailPoet
|
||||
) => {
|
||||
const ListingBulkActions = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
action: false,
|
||||
extra: false,
|
||||
};
|
||||
},
|
||||
handleChangeAction: function (e) {
|
||||
this.setState({
|
||||
action: e.target.value,
|
||||
extra: false,
|
||||
}, () => {
|
||||
const action = this.getSelectedAction();
|
||||
|
||||
// action on select callback
|
||||
if(action !== null && action['onSelect'] !== undefined) {
|
||||
this.setState({
|
||||
extra: action.onSelect(e),
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
handleApplyAction: function (e) {
|
||||
e.preventDefault();
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
const ListingBulkActions = React.createClass({
|
||||
getInitialState: function getInitialState() {
|
||||
return {
|
||||
action: false,
|
||||
extra: false,
|
||||
};
|
||||
},
|
||||
handleChangeAction: function handleChangeAction(e) {
|
||||
this.setState({
|
||||
action: e.target.value,
|
||||
extra: false,
|
||||
}, () => {
|
||||
const action = this.getSelectedAction();
|
||||
|
||||
if(action === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selected_ids = (this.props.selection !== 'all')
|
||||
? this.props.selected_ids
|
||||
: [];
|
||||
|
||||
const data = (action['getData'] !== undefined)
|
||||
? action.getData()
|
||||
: {};
|
||||
|
||||
data.action = this.state.action;
|
||||
|
||||
let onSuccess = function () {};
|
||||
if(action['onSuccess'] !== undefined) {
|
||||
onSuccess = action.onSuccess;
|
||||
}
|
||||
|
||||
if(data.action) {
|
||||
const promise = this.props.onBulkAction(selected_ids, data);
|
||||
if (promise !== false) {
|
||||
promise.then(onSuccess);
|
||||
};
|
||||
}
|
||||
|
||||
this.setState({
|
||||
action: false,
|
||||
extra: false,
|
||||
});
|
||||
},
|
||||
getSelectedAction: function () {
|
||||
const selected_action = this.refs.action.value;
|
||||
if(selected_action.length > 0) {
|
||||
const action = this.props.bulk_actions.filter((action) => {
|
||||
return (action.name === selected_action);
|
||||
// action on select callback
|
||||
if (action !== null && action.onSelect !== undefined) {
|
||||
this.setState({
|
||||
extra: action.onSelect(e),
|
||||
});
|
||||
|
||||
if(action.length > 0) {
|
||||
return action[0];
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
handleApplyAction: function handleApplyAction(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const action = this.getSelectedAction();
|
||||
|
||||
if (action === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedIds = (this.props.selection !== 'all')
|
||||
? this.props.selected_ids
|
||||
: [];
|
||||
|
||||
const data = (action.getData !== undefined)
|
||||
? action.getData()
|
||||
: {};
|
||||
|
||||
data.action = this.state.action;
|
||||
|
||||
let onSuccess = () => {};
|
||||
if (action.onSuccess !== undefined) {
|
||||
onSuccess = action.onSuccess;
|
||||
}
|
||||
|
||||
if (data.action) {
|
||||
const promise = this.props.onBulkAction(selectedIds, data);
|
||||
if (promise !== false) {
|
||||
promise.then(onSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
action: false,
|
||||
extra: false,
|
||||
});
|
||||
},
|
||||
getSelectedAction: function getSelectedAction() {
|
||||
const selectedAction = this.action.value;
|
||||
if (selectedAction.length > 0) {
|
||||
const action = this.props.bulk_actions.filter(act => (act.name === selectedAction));
|
||||
|
||||
if (action.length > 0) {
|
||||
return action[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
render: function render() {
|
||||
if (this.props.bulk_actions.length === 0) {
|
||||
return null;
|
||||
},
|
||||
render: function () {
|
||||
if(this.props.bulk_actions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="alignleft actions bulkactions">
|
||||
<label
|
||||
className="screen-reader-text"
|
||||
htmlFor="bulk-action-selector-top">
|
||||
{MailPoet.I18n.t('selectBulkAction')}
|
||||
</label>
|
||||
return (
|
||||
<div className="alignleft actions bulkactions">
|
||||
<label
|
||||
className="screen-reader-text"
|
||||
htmlFor="bulk-action-selector-top"
|
||||
>
|
||||
{MailPoet.I18n.t('selectBulkAction')}
|
||||
</label>
|
||||
|
||||
<select
|
||||
name="bulk_actions"
|
||||
ref="action"
|
||||
value={ this.state.action }
|
||||
onChange={this.handleChangeAction}
|
||||
>
|
||||
<option value="">{MailPoet.I18n.t('bulkActions')}</option>
|
||||
{ this.props.bulk_actions.map((action, index) => {
|
||||
return (
|
||||
<option
|
||||
value={ action.name }
|
||||
key={ 'action-' + index }
|
||||
>{ action.label }</option>
|
||||
);
|
||||
}) }
|
||||
</select>
|
||||
<input
|
||||
onClick={ this.handleApplyAction }
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('apply')}
|
||||
className="button action" />
|
||||
<select
|
||||
name="bulk_actions"
|
||||
ref={(c) => { this.action = c; }}
|
||||
value={this.state.action}
|
||||
onChange={this.handleChangeAction}
|
||||
>
|
||||
<option value="">{MailPoet.I18n.t('bulkActions')}</option>
|
||||
{ this.props.bulk_actions.map(action => (
|
||||
<option
|
||||
value={action.name}
|
||||
key={`action-${action.name}`}
|
||||
>{ action.label }</option>
|
||||
)) }
|
||||
</select>
|
||||
<input
|
||||
onClick={this.handleApplyAction}
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('apply')}
|
||||
className="button action"
|
||||
/>
|
||||
|
||||
{ this.state.extra }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return ListingBulkActions;
|
||||
{ this.state.extra }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default ListingBulkActions;
|
||||
|
@ -1,107 +1,95 @@
|
||||
define([
|
||||
'react',
|
||||
'jquery',
|
||||
'mailpoet',
|
||||
],
|
||||
(
|
||||
React,
|
||||
jQuery,
|
||||
MailPoet
|
||||
) => {
|
||||
const ListingFilters = React.createClass({
|
||||
handleFilterAction: function () {
|
||||
const filters = {};
|
||||
this.getAvailableFilters().map((filter, i) => {
|
||||
filters[this.refs['filter-'+i].name] = this.refs['filter-'+i].value;
|
||||
});
|
||||
if (this.props.onBeforeSelectFilter) {
|
||||
this.props.onBeforeSelectFilter(filters);
|
||||
}
|
||||
return this.props.onSelectFilter(filters);
|
||||
},
|
||||
handleEmptyTrash: function () {
|
||||
return this.props.onEmptyTrash();
|
||||
},
|
||||
getAvailableFilters: function () {
|
||||
const filters = this.props.filters;
|
||||
return Object.keys(filters).filter((filter) => {
|
||||
return !(
|
||||
filters[filter].length === 0
|
||||
|| (
|
||||
filters[filter].length === 1
|
||||
&& !filters[filter][0].value
|
||||
)
|
||||
);
|
||||
});
|
||||
},
|
||||
componentDidUpdate: function () {
|
||||
const selected_filters = this.props.filter;
|
||||
this.getAvailableFilters().map(
|
||||
(filter, i) => {
|
||||
if (selected_filters[filter] !== undefined && selected_filters[filter]) {
|
||||
jQuery(this.refs['filter-'+i])
|
||||
.val(selected_filters[filter])
|
||||
.trigger('change');
|
||||
}
|
||||
import React from 'react';
|
||||
import jQuery from 'jquery';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
const ListingFilters = React.createClass({
|
||||
handleFilterAction: function handleFilterAction() {
|
||||
const filters = {};
|
||||
this.getAvailableFilters().forEach((filter, i) => {
|
||||
filters[this[`filter-${i}`].name] = this[`filter-${i}`].value;
|
||||
});
|
||||
if (this.props.onBeforeSelectFilter) {
|
||||
this.props.onBeforeSelectFilter(filters);
|
||||
}
|
||||
return this.props.onSelectFilter(filters);
|
||||
},
|
||||
handleEmptyTrash: function handleEmptyTrash() {
|
||||
return this.props.onEmptyTrash();
|
||||
},
|
||||
getAvailableFilters: function getAvailableFilters() {
|
||||
const filters = this.props.filters;
|
||||
return Object.keys(filters).filter(filter => !(
|
||||
filters[filter].length === 0
|
||||
|| (
|
||||
filters[filter].length === 1
|
||||
&& !filters[filter][0].value
|
||||
)
|
||||
));
|
||||
},
|
||||
componentDidUpdate: function componentDidUpdate() {
|
||||
const selectedFilters = this.props.filter;
|
||||
this.getAvailableFilters().forEach(
|
||||
(filter, i) => {
|
||||
if (selectedFilters[filter] !== undefined && selectedFilters[filter]) {
|
||||
jQuery(this[`filter-${i}`])
|
||||
.val(selectedFilters[filter])
|
||||
.trigger('change');
|
||||
}
|
||||
);
|
||||
},
|
||||
render: function () {
|
||||
const filters = this.props.filters;
|
||||
const available_filters = this.getAvailableFilters()
|
||||
.map((filter, i) => {
|
||||
return (
|
||||
<select
|
||||
ref={ `filter-${i}` }
|
||||
key={ `filter-${i}` }
|
||||
name={ filter }
|
||||
>
|
||||
{ filters[filter].map((option, j) => {
|
||||
return (
|
||||
<option
|
||||
value={ option.value }
|
||||
key={ 'filter-option-' + j }
|
||||
>{ option.label }</option>
|
||||
);
|
||||
}) }
|
||||
</select>
|
||||
);
|
||||
});
|
||||
|
||||
let button;
|
||||
|
||||
if (available_filters.length > 0) {
|
||||
button = (
|
||||
<input
|
||||
id="post-query-submit"
|
||||
onClick={ this.handleFilterAction }
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('filter')}
|
||||
className="button" />
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
render: function render() {
|
||||
const filters = this.props.filters;
|
||||
const availableFilters = this.getAvailableFilters()
|
||||
.map((filter, i) => (
|
||||
<select
|
||||
ref={(c) => { this[`filter-${i}`] = c; }}
|
||||
key={`filter-${filter}`}
|
||||
name={filter}
|
||||
>
|
||||
{ filters[filter].map(option => (
|
||||
<option
|
||||
value={option.value}
|
||||
key={`filter-option-${option.value}`}
|
||||
>{ option.label }</option>
|
||||
)) }
|
||||
</select>
|
||||
));
|
||||
|
||||
let empty_trash;
|
||||
if (this.props.group === 'trash') {
|
||||
empty_trash = (
|
||||
<input
|
||||
onClick={ this.handleEmptyTrash }
|
||||
type="submit"
|
||||
value={MailPoet.I18n.t('emptyTrash')}
|
||||
className="button"
|
||||
/>
|
||||
);
|
||||
}
|
||||
let button;
|
||||
|
||||
return (
|
||||
<div className="alignleft actions actions">
|
||||
{ available_filters }
|
||||
{ button }
|
||||
{ empty_trash }
|
||||
</div>
|
||||
if (availableFilters.length > 0) {
|
||||
button = (
|
||||
<input
|
||||
id="post-query-submit"
|
||||
onClick={this.handleFilterAction}
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('filter')}
|
||||
className="button"
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return ListingFilters;
|
||||
let emptyTrash;
|
||||
if (this.props.group === 'trash') {
|
||||
emptyTrash = (
|
||||
<input
|
||||
onClick={this.handleEmptyTrash}
|
||||
type="submit"
|
||||
value={MailPoet.I18n.t('emptyTrash')}
|
||||
className="button"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="alignleft actions actions">
|
||||
{ availableFilters }
|
||||
{ button }
|
||||
{ emptyTrash }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default ListingFilters;
|
||||
|
@ -1,40 +1,58 @@
|
||||
define(['react', 'classnames'], (React, classNames) => {
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const ListingGroups = React.createClass({
|
||||
handleSelect: function (group) {
|
||||
return this.props.onSelectGroup(group);
|
||||
},
|
||||
render: function () {
|
||||
const groups = this.props.groups.map((group, index) => {
|
||||
if(group.name === 'trash' && group.count === 0) {
|
||||
return false;
|
||||
}
|
||||
class ListingGroups extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleSelect = this.handleSelect.bind(this);
|
||||
}
|
||||
|
||||
const classes = classNames(
|
||||
{ current : (group.name === this.props.group) }
|
||||
);
|
||||
handleSelect(group) {
|
||||
return this.props.onSelectGroup(group);
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={index}>
|
||||
{(index > 0) ? ' |' : ''}
|
||||
<a
|
||||
href="javascript:;"
|
||||
className={classes}
|
||||
onClick={this.handleSelect.bind(this, group.name)} >
|
||||
{group.label} <span className="count">({ group.count.toLocaleString() })</span>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
render() {
|
||||
const groups = this.props.groups.map((group, index) => {
|
||||
if (group.name === 'trash' && group.count === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const classes = classNames(
|
||||
{ current: (group.name === this.props.group) }
|
||||
);
|
||||
|
||||
return (
|
||||
<ul className="subsubsub">
|
||||
{ groups }
|
||||
</ul>
|
||||
<li key={group.name}>
|
||||
{(index > 0) ? ' |' : ''}
|
||||
<a
|
||||
href="javascript:;"
|
||||
className={classes}
|
||||
onClick={() => this.handleSelect(group.name)}
|
||||
data-automation-id={`filters_${group.label.replace(' ', '_').toLowerCase()}`}
|
||||
>
|
||||
{group.label}
|
||||
<span className="count">({ parseInt(group.count, 10).toLocaleString() })</span>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return ListingGroups;
|
||||
return (
|
||||
<ul className="subsubsub">
|
||||
{ groups }
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ListingGroups.propTypes = {
|
||||
onSelectGroup: PropTypes.func.isRequired,
|
||||
groups: PropTypes.arrayOf(PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
count: PropTypes.number,
|
||||
})).isRequired,
|
||||
group: PropTypes.any.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
|
||||
export default ListingGroups;
|
||||
|
@ -3,12 +3,12 @@ import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const ListingHeader = React.createClass({
|
||||
handleSelectItems: function () {
|
||||
handleSelectItems: function handleSelectItems() {
|
||||
return this.props.onSelectItems(
|
||||
this.refs.toggle.checked
|
||||
this.toggle.checked
|
||||
);
|
||||
},
|
||||
render: function () {
|
||||
render: function render() {
|
||||
const columns = this.props.columns.map((column, index) => {
|
||||
const renderColumn = column;
|
||||
renderColumn.is_primary = (index === 0);
|
||||
@ -19,26 +19,30 @@ const ListingHeader = React.createClass({
|
||||
<ListingColumn
|
||||
onSort={this.props.onSort}
|
||||
sort_by={this.props.sort_by}
|
||||
key={ 'column-' + index }
|
||||
column={renderColumn} />
|
||||
key={`column-${column.name}`}
|
||||
column={renderColumn}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
let checkbox;
|
||||
|
||||
if(this.props.is_selectable === true) {
|
||||
if (this.props.is_selectable === true) {
|
||||
checkbox = (
|
||||
<th
|
||||
className="manage-column column-cb check-column">
|
||||
<label className="screen-reader-text">
|
||||
className="manage-column column-cb check-column"
|
||||
>
|
||||
<label className="screen-reader-text" htmlFor="select_all">
|
||||
{MailPoet.I18n.t('selectAll')}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="select_all"
|
||||
ref="toggle"
|
||||
checked={ this.props.selection }
|
||||
onChange={ this.handleSelectItems } />
|
||||
id="select_all"
|
||||
ref={(c) => { this.toggle = c; }}
|
||||
checked={this.props.selection}
|
||||
onChange={this.handleSelectItems}
|
||||
/>
|
||||
</th>
|
||||
);
|
||||
}
|
||||
@ -53,12 +57,12 @@ const ListingHeader = React.createClass({
|
||||
});
|
||||
|
||||
const ListingColumn = React.createClass({
|
||||
handleSort: function () {
|
||||
const sort_by = this.props.column.name;
|
||||
const sort_order = (this.props.column.sorted === 'asc') ? 'desc' : 'asc';
|
||||
this.props.onSort(sort_by, sort_order);
|
||||
handleSort: function handleSort() {
|
||||
const sortBy = this.props.column.name;
|
||||
const sortOrder = (this.props.column.sorted === 'asc') ? 'desc' : 'asc';
|
||||
this.props.onSort(sortBy, sortOrder);
|
||||
},
|
||||
render: function () {
|
||||
render: function render() {
|
||||
const classes = classNames(
|
||||
'manage-column',
|
||||
{ 'column-primary': this.props.column.is_primary },
|
||||
@ -68,11 +72,15 @@ const ListingColumn = React.createClass({
|
||||
);
|
||||
let label;
|
||||
|
||||
if(this.props.column.sortable === true) {
|
||||
if (this.props.column.sortable === true) {
|
||||
label = (
|
||||
<a onClick={ this.handleSort }>
|
||||
<a
|
||||
onClick={this.handleSort}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<span>{ this.props.column.label }</span>
|
||||
<span className="sorting-indicator"></span>
|
||||
<span className="sorting-indicator" />
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
@ -80,10 +88,11 @@ const ListingColumn = React.createClass({
|
||||
}
|
||||
return (
|
||||
<th
|
||||
className={ classes }
|
||||
id={this.props.column.name }
|
||||
role="columnheader"
|
||||
className={classes}
|
||||
id={this.props.column.name}
|
||||
scope="col"
|
||||
width={ this.props.column.width || null }
|
||||
width={this.props.column.width || null}
|
||||
>{label}</th>
|
||||
);
|
||||
},
|
||||
|
@ -1,185 +1,187 @@
|
||||
define([
|
||||
'react',
|
||||
'classnames',
|
||||
'mailpoet',
|
||||
], (
|
||||
React,
|
||||
classNames,
|
||||
MailPoet
|
||||
) => {
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
const ListingPages = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
page: null,
|
||||
};
|
||||
},
|
||||
setPage: function (page) {
|
||||
this.setState({
|
||||
page: null,
|
||||
}, () => {
|
||||
this.props.onSetPage(this.constrainPage(page));
|
||||
});
|
||||
},
|
||||
setFirstPage: function () {
|
||||
this.setPage(1);
|
||||
},
|
||||
setLastPage: function () {
|
||||
this.setPage(this.getLastPage());
|
||||
},
|
||||
setPreviousPage: function () {
|
||||
this.setPage(this.constrainPage(
|
||||
parseInt(this.props.page, 10) - 1)
|
||||
);
|
||||
},
|
||||
setNextPage: function () {
|
||||
this.setPage(this.constrainPage(
|
||||
parseInt(this.props.page, 10) + 1)
|
||||
);
|
||||
},
|
||||
constrainPage: function (page) {
|
||||
return Math.min(Math.max(1, Math.abs(~~page)), this.getLastPage());
|
||||
},
|
||||
handleSetManualPage: function (e) {
|
||||
if(e.which === 13) {
|
||||
this.setPage(this.state.page);
|
||||
const ListingPages = React.createClass({
|
||||
getInitialState: function getInitialState() {
|
||||
return {
|
||||
page: null,
|
||||
};
|
||||
},
|
||||
setPage: function setPage(page) {
|
||||
this.setState({
|
||||
page: null,
|
||||
}, () => {
|
||||
this.props.onSetPage(this.constrainPage(page));
|
||||
});
|
||||
},
|
||||
setFirstPage: function setFirstPage() {
|
||||
this.setPage(1);
|
||||
},
|
||||
setLastPage: function setLastPage() {
|
||||
this.setPage(this.getLastPage());
|
||||
},
|
||||
setPreviousPage: function setPreviousPage() {
|
||||
this.setPage(this.constrainPage(
|
||||
parseInt(this.props.page, 10) - 1)
|
||||
);
|
||||
},
|
||||
setNextPage: function setNextPage() {
|
||||
this.setPage(this.constrainPage(
|
||||
parseInt(this.props.page, 10) + 1)
|
||||
);
|
||||
},
|
||||
constrainPage: function constrainPage(page) {
|
||||
return Math.min(Math.max(1, Math.abs(Number(page))), this.getLastPage());
|
||||
},
|
||||
handleSetManualPage: function handleSetManualPage(e) {
|
||||
if (e.which === 13) {
|
||||
this.setPage(this.state.page);
|
||||
}
|
||||
},
|
||||
handleChangeManualPage: function handleChangeManualPage(e) {
|
||||
this.setState({
|
||||
page: e.target.value,
|
||||
});
|
||||
},
|
||||
handleBlurManualPage: function handleBlurManualPage(e) {
|
||||
this.setPage(e.target.value);
|
||||
},
|
||||
getLastPage: function getLastPage() {
|
||||
return Math.ceil(this.props.count / this.props.limit);
|
||||
},
|
||||
render: function render() {
|
||||
if (this.props.count === 0) {
|
||||
return false;
|
||||
}
|
||||
let pagination = false;
|
||||
let firstPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">«</span>
|
||||
);
|
||||
let previousPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">‹</span>
|
||||
);
|
||||
let nextPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">›</span>
|
||||
);
|
||||
let lastPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">»</span>
|
||||
);
|
||||
|
||||
if (this.props.limit > 0 && this.props.count > this.props.limit) {
|
||||
if (this.props.page > 1) {
|
||||
previousPage = (
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={this.setPreviousPage}
|
||||
className="prev-page"
|
||||
>
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('previousPage')}</span>
|
||||
<span aria-hidden="true">‹</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
},
|
||||
handleChangeManualPage: function (e) {
|
||||
this.setState({
|
||||
page: e.target.value,
|
||||
});
|
||||
},
|
||||
handleBlurManualPage: function (e) {
|
||||
this.setPage(e.target.value);
|
||||
},
|
||||
getLastPage: function () {
|
||||
return Math.ceil(this.props.count / this.props.limit);
|
||||
},
|
||||
render: function () {
|
||||
if(this.props.count === 0) {
|
||||
return false;
|
||||
} else {
|
||||
let pagination = false;
|
||||
let firstPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">«</span>
|
||||
|
||||
if (this.props.page > 2) {
|
||||
firstPage = (
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={this.setFirstPage}
|
||||
className="first-page"
|
||||
>
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('firstPage')}</span>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
);
|
||||
let previousPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">‹</span>
|
||||
}
|
||||
|
||||
if (this.props.page < this.getLastPage()) {
|
||||
nextPage = (
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={this.setNextPage}
|
||||
className="next-page"
|
||||
>
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('nextPage')}</span>
|
||||
<span aria-hidden="true">›</span>
|
||||
</a>
|
||||
);
|
||||
let nextPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">›</span>
|
||||
);
|
||||
let lastPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">»</span>
|
||||
}
|
||||
|
||||
if (this.props.page < this.getLastPage() - 1) {
|
||||
lastPage = (
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={this.setLastPage}
|
||||
className="last-page"
|
||||
>
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('lastPage')}</span>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if(this.props.limit > 0 && this.props.count > this.props.limit) {
|
||||
if(this.props.page > 1) {
|
||||
previousPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={ this.setPreviousPage }
|
||||
className="prev-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('previousPage')}</span>
|
||||
<span aria-hidden="true">‹</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
let pageValue = this.props.page;
|
||||
if (this.state.page !== null) {
|
||||
pageValue = this.state.page;
|
||||
}
|
||||
|
||||
if(this.props.page > 2) {
|
||||
firstPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={ this.setFirstPage }
|
||||
className="first-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('firstPage')}</span>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if(this.props.page < this.getLastPage()) {
|
||||
nextPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={ this.setNextPage }
|
||||
className="next-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('nextPage')}</span>
|
||||
<span aria-hidden="true">›</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if(this.props.page < this.getLastPage() - 1) {
|
||||
lastPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={ this.setLastPage }
|
||||
className="last-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('lastPage')}</span>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
let pageValue = this.props.page;
|
||||
if(this.state.page !== null) {
|
||||
pageValue = this.state.page;
|
||||
}
|
||||
|
||||
pagination = (
|
||||
<span className="pagination-links">
|
||||
{firstPage}
|
||||
|
||||
{previousPage}
|
||||
|
||||
<span className="paging-input">
|
||||
<label
|
||||
className="screen-reader-text"
|
||||
htmlFor="current-page-selector">{MailPoet.I18n.t('currentPage')}</label>
|
||||
<input
|
||||
type="text"
|
||||
onChange={ this.handleChangeManualPage }
|
||||
onKeyUp={ this.handleSetManualPage }
|
||||
onBlur={ this.handleBlurManualPage }
|
||||
aria-describedby="table-paging"
|
||||
size="2"
|
||||
ref="page"
|
||||
value={ pageValue }
|
||||
name="paged"
|
||||
id="current-page-selector"
|
||||
className="current-page" />
|
||||
{MailPoet.I18n.t('pageOutOf')}
|
||||
<span className="total-pages">
|
||||
{Math.ceil(this.props.count / this.props.limit).toLocaleString()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
{nextPage}
|
||||
|
||||
{lastPage}
|
||||
pagination = (
|
||||
<span className="pagination-links">
|
||||
{firstPage}
|
||||
|
||||
{previousPage}
|
||||
|
||||
<span className="paging-input">
|
||||
<label
|
||||
className="screen-reader-text"
|
||||
htmlFor="current-page-selector"
|
||||
>{MailPoet.I18n.t('currentPage')}</label>
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.handleChangeManualPage}
|
||||
onKeyUp={this.handleSetManualPage}
|
||||
onBlur={this.handleBlurManualPage}
|
||||
aria-describedby="table-paging"
|
||||
size="2"
|
||||
value={pageValue}
|
||||
name="paged"
|
||||
id="current-page-selector"
|
||||
className="current-page"
|
||||
/>
|
||||
{MailPoet.I18n.t('pageOutOf')}
|
||||
<span className="total-pages">
|
||||
{Math.ceil(this.props.count / this.props.limit).toLocaleString()}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
</span>
|
||||
|
||||
{nextPage}
|
||||
|
||||
{lastPage}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const classes = classNames(
|
||||
'tablenav-pages',
|
||||
{ 'one-page': (this.props.count <= this.props.limit) }
|
||||
);
|
||||
const classes = classNames(
|
||||
'tablenav-pages',
|
||||
{ 'one-page': (this.props.count <= this.props.limit) }
|
||||
);
|
||||
|
||||
let numberOfItemsLabel;
|
||||
if (this.props.count == 1) {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsSingular');
|
||||
} else {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsMultiple')
|
||||
.replace('%$1d', this.props.count.toLocaleString());
|
||||
}
|
||||
return (
|
||||
<div className={ classes }>
|
||||
<span className="displaying-num">{ numberOfItemsLabel }</span>
|
||||
{ pagination }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
let numberOfItemsLabel;
|
||||
if (Number(this.props.count) === 1) {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsSingular');
|
||||
} else {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsMultiple')
|
||||
.replace('%$1d', parseInt(this.props.count, 10).toLocaleString());
|
||||
}
|
||||
|
||||
return ListingPages;
|
||||
return (
|
||||
<div className={classes}>
|
||||
<span className="displaying-num">{ numberOfItemsLabel }</span>
|
||||
{ pagination }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ListingPages;
|
||||
|
@ -1,47 +1,51 @@
|
||||
define([
|
||||
'mailpoet',
|
||||
'react',
|
||||
], (
|
||||
MailPoet,
|
||||
React
|
||||
) => {
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const ListingSearch = React.createClass({
|
||||
handleSearch: function (e) {
|
||||
e.preventDefault();
|
||||
this.props.onSearch(
|
||||
this.refs.search.value
|
||||
);
|
||||
},
|
||||
componentWillReceiveProps: function (nextProps) {
|
||||
this.refs.search.value = nextProps.search;
|
||||
},
|
||||
render: function () {
|
||||
if(this.props.search === false) {
|
||||
return false;
|
||||
} else {
|
||||
return (
|
||||
<form name="search" onSubmit={this.handleSearch}>
|
||||
<p className="search-box">
|
||||
<label htmlFor="search_input" className="screen-reader-text">
|
||||
{MailPoet.I18n.t('searchLabel')}
|
||||
</label>
|
||||
<input
|
||||
type="search"
|
||||
id="search_input"
|
||||
ref="search"
|
||||
name="s"
|
||||
defaultValue={this.props.search} />
|
||||
<input
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('searchLabel')}
|
||||
className="button" />
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
class ListingSearch extends React.Component {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.search.value = nextProps.search;
|
||||
this.handleSearch = this.handleSearch.bind(this);
|
||||
}
|
||||
|
||||
return ListingSearch;
|
||||
});
|
||||
handleSearch(e) {
|
||||
e.preventDefault();
|
||||
this.props.onSearch(
|
||||
this.search.value.trim()
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.search === false) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
<form name="search" onSubmit={this.handleSearch}>
|
||||
<p className="search-box">
|
||||
<label htmlFor="search_input" className="screen-reader-text">
|
||||
{MailPoet.I18n.t('searchLabel')}
|
||||
</label>
|
||||
<input
|
||||
type="search"
|
||||
id="search_input"
|
||||
ref={(c) => { this.search = c; }}
|
||||
name="s"
|
||||
defaultValue={this.props.search}
|
||||
/>
|
||||
<input
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('searchLabel')}
|
||||
className="button"
|
||||
/>
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ListingSearch.propTypes = {
|
||||
search: PropTypes.string.isRequired,
|
||||
onSearch: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ListingSearch;
|
||||
|
@ -1,4 +1,4 @@
|
||||
define('mailpoet', [], function() {
|
||||
define('mailpoet', [], function mailpoet() {
|
||||
// A placeholder for MailPoet object
|
||||
var MailPoet = {};
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
/* eslint-disable func-names */
|
||||
define('modal', ['mailpoet', 'jquery'],
|
||||
function(mp, jQuery) {
|
||||
function (mp, jQuery) {
|
||||
'use strict';
|
||||
|
||||
var MailPoet = mp;
|
||||
/***************************************************************************
|
||||
/** *************************************************************************
|
||||
MailPoet Modal:
|
||||
|
||||
version: 0.9
|
||||
@ -19,7 +21,7 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
|
||||
// loading mode
|
||||
MailPoet.Modal.loading(bool);
|
||||
***************************************************************************/
|
||||
************************************************************************** */
|
||||
|
||||
MailPoet.Modal = {
|
||||
version: 0.9,
|
||||
@ -78,45 +80,44 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
options: {},
|
||||
templates: {
|
||||
overlay: '<div id="mailpoet_modal_overlay" style="display:none;"></div>',
|
||||
popup: '<div id="mailpoet_popup" tabindex="-1">'+
|
||||
'<div class="mailpoet_popup_wrapper">'+
|
||||
'<a href="javascript:;" id="mailpoet_modal_close"></a>'+
|
||||
'<div id="mailpoet_popup_title"><h2></h2></div>'+
|
||||
'<div class="mailpoet_popup_body clearfix"></div>'+
|
||||
'</div>'+
|
||||
popup: '<div id="mailpoet_popup" tabindex="-1">' +
|
||||
'<div class="mailpoet_popup_wrapper">' +
|
||||
'<a href="javascript:;" id="mailpoet_modal_close"></a>' +
|
||||
'<div id="mailpoet_popup_title"><h2></h2></div>' +
|
||||
'<div class="mailpoet_popup_body clearfix"></div>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
loading: '<div id="mailpoet_loading" style="display:none;">'+
|
||||
'<div id="mailpoet_modal_loading_1" class="mailpoet_modal_loading"></div>'+
|
||||
'<div id="mailpoet_modal_loading_2" class="mailpoet_modal_loading"></div>'+
|
||||
'<div id="mailpoet_modal_loading_3" class="mailpoet_modal_loading"></div>'+
|
||||
loading: '<div id="mailpoet_loading" style="display:none;">' +
|
||||
'<div id="mailpoet_modal_loading_1" class="mailpoet_modal_loading"></div>' +
|
||||
'<div id="mailpoet_modal_loading_2" class="mailpoet_modal_loading"></div>' +
|
||||
'<div id="mailpoet_modal_loading_3" class="mailpoet_modal_loading"></div>' +
|
||||
'</div>',
|
||||
panel: '<div id="mailpoet_panel">'+
|
||||
'<a href="javascript:;" id="mailpoet_modal_close"></a>'+
|
||||
'<div class="mailpoet_panel_wrapper" tabindex="-1">'+
|
||||
'<div class="mailpoet_panel_body clearfix"></div>'+
|
||||
'</div>'+
|
||||
panel: '<div id="mailpoet_panel">' +
|
||||
'<a href="javascript:;" id="mailpoet_modal_close"></a>' +
|
||||
'<div class="mailpoet_panel_wrapper" tabindex="-1">' +
|
||||
'<div class="mailpoet_panel_body clearfix"></div>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
subpanel: '<div class="mailpoet_panel_wrapper" tabindex="-1">'+
|
||||
'<div class="mailpoet_panel_body clearfix"></div>'+
|
||||
subpanel: '<div class="mailpoet_panel_wrapper" tabindex="-1">' +
|
||||
'<div class="mailpoet_panel_body clearfix"></div>' +
|
||||
'</div>'
|
||||
},
|
||||
getContentContainer: function() {
|
||||
return jQuery('.mailpoet_'+this.options.type+'_body');
|
||||
getContentContainer: function () {
|
||||
return jQuery('.mailpoet_' + this.options.type + '_body');
|
||||
},
|
||||
setRenderer: function(renderer) {
|
||||
setRenderer: function (renderer) {
|
||||
this.renderer = renderer;
|
||||
return this;
|
||||
},
|
||||
compileTemplate: function(template) {
|
||||
if(this.renderer === 'html') {
|
||||
return function() { return template; };
|
||||
} else {
|
||||
return Handlebars.compile(template);
|
||||
compileTemplate: function (template) {
|
||||
if (this.renderer === 'html') {
|
||||
return function () { return template; };
|
||||
}
|
||||
return false;
|
||||
return window.Handlebars.compile(template);
|
||||
},
|
||||
init: function(options) {
|
||||
if(this.initialized === true) {
|
||||
init: function (options) {
|
||||
var modal;
|
||||
if (this.initialized === true) {
|
||||
this.close();
|
||||
}
|
||||
// merge options
|
||||
@ -131,10 +132,10 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// toggle overlay
|
||||
this.toggleOverlay(this.options.overlay);
|
||||
|
||||
if(this.options.type !== null) {
|
||||
if (this.options.type !== null) {
|
||||
// insert modal depending on its type
|
||||
if(this.options.type === 'popup') {
|
||||
var modal = this.compileTemplate(
|
||||
if (this.options.type === 'popup') {
|
||||
modal = this.compileTemplate(
|
||||
this.templates[this.options.type]
|
||||
);
|
||||
// create modal
|
||||
@ -143,7 +144,7 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// set title
|
||||
jQuery('#mailpoet_popup_title h2')
|
||||
.html(this.options.title);
|
||||
} else if(this.options.type === 'panel') {
|
||||
} else if (this.options.type === 'panel') {
|
||||
// create panel
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.after(this.templates[this.options.type]);
|
||||
@ -152,16 +153,16 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// add proper overlay class
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.removeClass('mailpoet_popup_overlay mailpoet_panel_overlay')
|
||||
.addClass('mailpoet_'+this.options.type+'_overlay');
|
||||
.addClass('mailpoet_' + this.options.type + '_overlay');
|
||||
}
|
||||
|
||||
// set "success" callback if specified
|
||||
if(options.onSuccess !== undefined) {
|
||||
if (options.onSuccess !== undefined) {
|
||||
this.options.onSuccess = options.onSuccess;
|
||||
}
|
||||
|
||||
// set "cancel" callback if specified
|
||||
if(options.onCancel !== undefined) {
|
||||
if (options.onCancel !== undefined) {
|
||||
this.options.onCancel = options.onCancel;
|
||||
}
|
||||
|
||||
@ -178,466 +179,464 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
|
||||
return this;
|
||||
},
|
||||
initOverlay: function(toggle) {
|
||||
if(jQuery('#mailpoet_modal_overlay').length === 0) {
|
||||
// insert overlay into the DOM
|
||||
jQuery('body').append(this.templates.overlay);
|
||||
// insert loading indicator into overlay
|
||||
jQuery('#mailpoet_modal_overlay').append(this.templates.loading);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
toggleOverlay: function(toggle) {
|
||||
if(toggle === true) {
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.removeClass('mailpoet_overlay_hidden');
|
||||
} else {
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.addClass('mailpoet_overlay_hidden');
|
||||
}
|
||||
initOverlay: function () {
|
||||
if (jQuery('#mailpoet_modal_overlay').length === 0) {
|
||||
// insert overlay into the DOM
|
||||
jQuery('body').append(this.templates.overlay);
|
||||
// insert loading indicator into overlay
|
||||
jQuery('#mailpoet_modal_overlay').append(this.templates.loading);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
toggleOverlay: function (toggle) {
|
||||
if (toggle === true) {
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.removeClass('mailpoet_overlay_hidden');
|
||||
} else {
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.addClass('mailpoet_overlay_hidden');
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
setupEvents: function() {
|
||||
return this;
|
||||
},
|
||||
setupEvents: function () {
|
||||
// close popup when user clicks on close button
|
||||
jQuery('#mailpoet_modal_close').on('click', this.cancel.bind(this));
|
||||
|
||||
// close popup when user clicks on overlay
|
||||
jQuery('#mailpoet_modal_overlay').on('click', function(e) {
|
||||
jQuery('#mailpoet_modal_overlay').on('click', function (e) {
|
||||
// we need to make sure that we are actually clicking on the overlay
|
||||
// because when clicking on the popup content, it will trigger
|
||||
// the click event on the overlay
|
||||
if(e.target.id === 'mailpoet_modal_overlay') { this.cancel(); }
|
||||
if (e.target.id === 'mailpoet_modal_overlay') { this.cancel(); }
|
||||
}.bind(this));
|
||||
|
||||
// close popup when user presses ESC key
|
||||
jQuery(document).on('keyup.mailpoet_modal', function(e) {
|
||||
if(this.opened === false) { return false; }
|
||||
if(e.keyCode === 27) { this.cancel(); }
|
||||
jQuery(document).on('keyup.mailpoet_modal', function (e) {
|
||||
if (this.opened === false) { return false; }
|
||||
if (e.keyCode === 27) { this.cancel(); }
|
||||
return true;
|
||||
}.bind(this));
|
||||
|
||||
// make sure the popup is repositioned when the window is resized
|
||||
jQuery(window).on('resize.mailpoet_modal', function() {
|
||||
jQuery(window).on('resize.mailpoet_modal', function () {
|
||||
this.setPosition();
|
||||
}.bind(this));
|
||||
|
||||
return this;
|
||||
},
|
||||
removeEvents: function() {
|
||||
removeEvents: function () {
|
||||
jQuery(document).unbind('keyup.mailpoet_modal');
|
||||
jQuery(window).unbind('resize.mailpoet_modal');
|
||||
jQuery('#mailpoet_modal_close').off('click');
|
||||
if(this.options.overlay === true) {
|
||||
if (this.options.overlay === true) {
|
||||
jQuery('#mailpoet_modal_overlay').off('click');
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
lock: function() {
|
||||
lock: function () {
|
||||
this.locked = true;
|
||||
|
||||
return this;
|
||||
},
|
||||
unlock: function() {
|
||||
unlock: function () {
|
||||
this.locked = false;
|
||||
|
||||
return this;
|
||||
},
|
||||
isLocked: function() {
|
||||
isLocked: function () {
|
||||
return this.locked;
|
||||
},
|
||||
loadTemplate: function() {
|
||||
if(this.subpanels.length > 0) {
|
||||
// hide panel
|
||||
jQuery('.mailpoet_'+this.options.type+'_wrapper').hide();
|
||||
loadTemplate: function () {
|
||||
if (this.subpanels.length > 0) {
|
||||
// hide panel
|
||||
jQuery('.mailpoet_' + this.options.type + '_wrapper').hide();
|
||||
|
||||
// add sub panel wrapper
|
||||
jQuery('#mailpoet_'+this.options.type)
|
||||
.append(this.templates['subpanel']);
|
||||
// add sub panel wrapper
|
||||
jQuery('#mailpoet_' + this.options.type)
|
||||
.append(this.templates.subpanel);
|
||||
|
||||
// add sub panel content
|
||||
jQuery('.mailpoet_'+this.options.type+'_body').last()
|
||||
.html(this.subpanels[(this.subpanels.length - 1)].element);
|
||||
// add sub panel content
|
||||
jQuery('.mailpoet_' + this.options.type + '_body').last()
|
||||
.html(this.subpanels[(this.subpanels.length - 1)].element);
|
||||
|
||||
// focus on sub panel
|
||||
if(this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
} else if (this.options.element) {
|
||||
jQuery('.mailpoet_'+this.options.type+'_body').empty();
|
||||
jQuery('.mailpoet_'+this.options.type+'_body')
|
||||
.append(this.options.element);
|
||||
} else {
|
||||
jQuery('.mailpoet_'+this.options.type+'_body')
|
||||
.html(
|
||||
this.options.body_template(
|
||||
this.options.data
|
||||
)
|
||||
);
|
||||
// focus on sub panel
|
||||
if (this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
loadUrl: function() {
|
||||
if(this.options.method === 'get') {
|
||||
// make ajax request
|
||||
jQuery.getJSON(this.options.url,
|
||||
function(data) {
|
||||
this.options.data = jQuery.extend({}, this.options.data, data);
|
||||
// load template using fetched data
|
||||
this.loadTemplate();
|
||||
// show modal window
|
||||
this.showModal();
|
||||
}.bind(this)
|
||||
} else if (this.options.element) {
|
||||
jQuery('.mailpoet_' + this.options.type + '_body').empty();
|
||||
jQuery('.mailpoet_' + this.options.type + '_body')
|
||||
.append(this.options.element);
|
||||
} else {
|
||||
jQuery('.mailpoet_' + this.options.type + '_body')
|
||||
.html(
|
||||
this.options.body_template(
|
||||
this.options.data
|
||||
)
|
||||
);
|
||||
} else if(this.options.method === 'post') {
|
||||
// make ajax request
|
||||
jQuery.post(this.options.url, JSON.stringify(this.options.params),
|
||||
function(data) {
|
||||
this.options.data = jQuery.extend({}, this.options.data, data);
|
||||
// load template using fetched data
|
||||
this.loadTemplate();
|
||||
// show modal window
|
||||
this.showModal();
|
||||
}.bind(this),
|
||||
'json'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
setDimensions: function() {
|
||||
switch(this.options.type) {
|
||||
case 'popup':
|
||||
// set popup dimensions
|
||||
jQuery('#mailpoet_popup').css({
|
||||
width: this.options.width,
|
||||
height: this.options.height
|
||||
});
|
||||
// set popup wrapper height
|
||||
jQuery('#mailpoet_popup_wrapper').css({
|
||||
height: this.options.height
|
||||
});
|
||||
return this;
|
||||
},
|
||||
loadUrl: function () {
|
||||
if (this.options.method === 'get') {
|
||||
// make ajax request
|
||||
jQuery.getJSON(this.options.url,
|
||||
function (data) {
|
||||
this.options.data = jQuery.extend({}, this.options.data, data);
|
||||
// load template using fetched data
|
||||
this.loadTemplate();
|
||||
// show modal window
|
||||
this.showModal();
|
||||
}.bind(this)
|
||||
);
|
||||
} else if (this.options.method === 'post') {
|
||||
// make ajax request
|
||||
jQuery.post(this.options.url, JSON.stringify(this.options.params),
|
||||
function (data) {
|
||||
this.options.data = jQuery.extend({}, this.options.data, data);
|
||||
// load template using fetched data
|
||||
this.loadTemplate();
|
||||
// show modal window
|
||||
this.showModal();
|
||||
}.bind(this),
|
||||
'json'
|
||||
);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
setDimensions: function () {
|
||||
switch (this.options.type) {
|
||||
case 'popup':
|
||||
// set popup dimensions
|
||||
jQuery('#mailpoet_popup').css({
|
||||
width: this.options.width,
|
||||
height: this.options.height
|
||||
});
|
||||
// set popup wrapper height
|
||||
jQuery('#mailpoet_popup_wrapper').css({
|
||||
height: this.options.height
|
||||
});
|
||||
break;
|
||||
case 'panel':
|
||||
// set dimensions
|
||||
if(this.options.position === 'right') {
|
||||
case 'panel':
|
||||
// set dimensions
|
||||
if (this.options.position === 'right') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
width: this.options.width,
|
||||
right: 0,
|
||||
marginRight: '-' + this.options.width,
|
||||
left: 'auto'
|
||||
});
|
||||
} else if (this.options.position === 'left') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
width: this.options.width,
|
||||
left: 0,
|
||||
marginLeft: '-' + this.options.width,
|
||||
right: 'auto'
|
||||
});
|
||||
}
|
||||
jQuery('#mailpoet_panel').css({ minHeight: 'auto' });
|
||||
break;
|
||||
default: throw new Error('Incorrect type');
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
setPosition: function () {
|
||||
var screenWidth;
|
||||
var screenHeight;
|
||||
var modalWidth;
|
||||
var modalHeight;
|
||||
switch (this.options.type) {
|
||||
case 'popup':
|
||||
screenWidth = jQuery(window).width();
|
||||
screenHeight = jQuery(window).height();
|
||||
modalWidth = jQuery('.mailpoet_' + this.options.type + '_wrapper').width();
|
||||
modalHeight = jQuery('.mailpoet_' + this.options.type + '_wrapper').height();
|
||||
|
||||
// set position of popup depending on screen dimensions.
|
||||
jQuery('#mailpoet_popup').css({
|
||||
top: Math.max(48, parseInt((screenHeight / 2) - (modalHeight / 2), 10)),
|
||||
left: Math.max(0, parseInt((screenWidth / 2) - (modalWidth / 2), 10))
|
||||
});
|
||||
break;
|
||||
case 'panel':
|
||||
setTimeout(function () {
|
||||
// set position of popup depending on screen dimensions.
|
||||
if (this.options.position === 'right') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
width: this.options.width,
|
||||
right: 0,
|
||||
marginRight: '-' + this.options.width,
|
||||
left: 'auto'
|
||||
marginRight: 0
|
||||
});
|
||||
} else if(this.options.position === 'left') {
|
||||
} else if (this.options.position === 'left') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
width: this.options.width,
|
||||
left: 0,
|
||||
marginLeft: '-' + this.options.width,
|
||||
right: 'auto'
|
||||
marginLeft: 0
|
||||
});
|
||||
}
|
||||
jQuery('#mailpoet_panel').css({ minHeight: 'auto' });
|
||||
}.bind(this), 0);
|
||||
break;
|
||||
default: throw new Error('Incorrect type');
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
showModal: function () {
|
||||
// set modal dimensions
|
||||
this.setDimensions();
|
||||
|
||||
// remember the previously focused element
|
||||
this.prevFocus = jQuery(':focus');
|
||||
|
||||
// show popup
|
||||
jQuery('#mailpoet_' + this.options.type).show();
|
||||
|
||||
// display overlay
|
||||
this.showOverlay();
|
||||
|
||||
// set modal position
|
||||
this.setPosition();
|
||||
|
||||
// add class on highlighted elements
|
||||
if (this.options.highlight !== null) {
|
||||
if (this.options.highlight.length > 0) {
|
||||
this.highlightOn(this.options.highlight);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
setPosition: function() {
|
||||
switch(this.options.type) {
|
||||
case 'popup':
|
||||
var screenWidth = jQuery(window).width(),
|
||||
screenHeight = jQuery(window).height(),
|
||||
modalWidth = jQuery('.mailpoet_'+ this.options.type +'_wrapper').width(),
|
||||
modalHeight = jQuery('.mailpoet_'+ this.options.type +'_wrapper').height();
|
||||
if (this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
var top = Math.max(48, parseInt((screenHeight / 2) - (modalHeight / 2))),
|
||||
left = Math.max(0, parseInt((screenWidth / 2) - (modalWidth / 2)));
|
||||
// set popup as opened
|
||||
this.opened = true;
|
||||
|
||||
// set position of popup depending on screen dimensions.
|
||||
jQuery('#mailpoet_popup').css({
|
||||
top: top,
|
||||
left: left
|
||||
});
|
||||
break;
|
||||
case 'panel':
|
||||
setTimeout(function() {
|
||||
// set position of popup depending on screen dimensions.
|
||||
if(this.options.position === 'right') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
marginRight: 0
|
||||
});
|
||||
} else if(this.options.position === 'left') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
marginLeft: 0
|
||||
});
|
||||
}
|
||||
}.bind(this), 0);
|
||||
break;
|
||||
// trigger init event if specified
|
||||
if (this.options.onInit !== null) {
|
||||
this.options.onInit(this);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
focus: function () {
|
||||
if (this.options.type === 'popup') {
|
||||
jQuery('#mailpoet_' + this.options.type).focus();
|
||||
} else {
|
||||
// panel and subpanel
|
||||
jQuery('#mailpoet_' + this.options.type + ' .mailpoet_panel_wrapper')
|
||||
.filter(':visible').focus();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
highlightOn: function (element) {
|
||||
jQuery(element).addClass('mailpoet_modal_highlight');
|
||||
return this;
|
||||
},
|
||||
highlightOff: function () {
|
||||
jQuery('.mailpoet_modal_highlight')
|
||||
.removeClass('mailpoet_modal_highlight');
|
||||
return this;
|
||||
},
|
||||
hideModal: function () {
|
||||
// set modal as closed
|
||||
this.opened = false;
|
||||
|
||||
// hide modal
|
||||
jQuery('#mailpoet_' + this.options.type).hide();
|
||||
|
||||
// remove class on highlighted elements
|
||||
this.highlightOff();
|
||||
|
||||
return this;
|
||||
},
|
||||
showOverlay: function () {
|
||||
jQuery('#mailpoet_modal_overlay').show();
|
||||
return this;
|
||||
},
|
||||
hideOverlay: function () {
|
||||
jQuery('#mailpoet_modal_overlay').hide();
|
||||
return this;
|
||||
},
|
||||
popup: function (opts) {
|
||||
// get options
|
||||
var options = opts || {};
|
||||
// set modal type
|
||||
options.type = 'popup';
|
||||
// set overlay state
|
||||
options.overlay = options.overlay || true;
|
||||
// initialize modal
|
||||
this.init(options);
|
||||
// open modal
|
||||
this.open();
|
||||
|
||||
return this;
|
||||
},
|
||||
panel: function (opts) {
|
||||
// get options
|
||||
var options = opts || {};
|
||||
// reset subpanels
|
||||
this.subpanels = [];
|
||||
// set modal type
|
||||
options.type = 'panel';
|
||||
// set overlay state
|
||||
options.overlay = options.overlay || false;
|
||||
// set highlighted element
|
||||
options.highlight = options.highlight || null;
|
||||
// set modal dimensions
|
||||
options.width = options.width || '40%';
|
||||
options.height = options.height || 'auto';
|
||||
// initialize modal
|
||||
this.init(options);
|
||||
// open modal
|
||||
this.open();
|
||||
|
||||
return this;
|
||||
},
|
||||
subpanel: function (options) {
|
||||
if (this.opened === false) {
|
||||
// if no panel is already opened, let's create one instead
|
||||
this.panel(options);
|
||||
} else {
|
||||
// if a panel is already opened, add a sub panel to it
|
||||
this.subpanels.push(options);
|
||||
this.loadTemplate();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
loading: function (toggle) {
|
||||
// make sure the overlay is initialized and that it's visible
|
||||
this.initOverlay(true);
|
||||
|
||||
if (toggle === true) {
|
||||
this.showLoading();
|
||||
} else {
|
||||
this.hideLoading();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
showLoading: function () {
|
||||
jQuery('#mailpoet_loading').show();
|
||||
|
||||
// add loading class to overlay
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.addClass('mailpoet_overlay_loading');
|
||||
|
||||
return this;
|
||||
},
|
||||
hideLoading: function () {
|
||||
jQuery('#mailpoet_loading').hide();
|
||||
|
||||
// remove loading class from overlay
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.removeClass('mailpoet_overlay_loading');
|
||||
|
||||
return this;
|
||||
},
|
||||
open: function () {
|
||||
// load template if specified
|
||||
if (this.options.template !== null) {
|
||||
// check if a url was specified to get extra data
|
||||
if (this.options.url !== null) {
|
||||
this.loadUrl();
|
||||
} else {
|
||||
// load template
|
||||
this.loadTemplate();
|
||||
|
||||
// show modal window
|
||||
this.showModal();
|
||||
}
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
showModal: function() {
|
||||
// set modal dimensions
|
||||
this.setDimensions();
|
||||
|
||||
// remember the previously focused element
|
||||
this.prevFocus = jQuery(':focus');
|
||||
|
||||
// add a flag on the body so that we can prevent scrolling
|
||||
jQuery('body').addClass('mailpoet_modal_opened');
|
||||
|
||||
// show popup
|
||||
jQuery('#mailpoet_'+this.options.type).show();
|
||||
|
||||
// display overlay
|
||||
this.showOverlay();
|
||||
|
||||
// set modal position
|
||||
this.setPosition();
|
||||
|
||||
// add class on highlighted elements
|
||||
if(this.options.highlight !== null) {
|
||||
if(this.options.highlight.length > 0) {
|
||||
this.highlightOn(this.options.highlight);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
success: function () {
|
||||
if (this.subpanels.length > 0) {
|
||||
if (this.subpanels[(this.subpanels.length - 1)].onSuccess !== undefined) {
|
||||
this
|
||||
.subpanels[(this.subpanels.length - 1)]
|
||||
.onSuccess(this.subpanels[(this.subpanels.length - 1)].data);
|
||||
}
|
||||
} else if (this.options.onSuccess !== null) {
|
||||
this.options.onSuccess(this.options.data);
|
||||
}
|
||||
this.close();
|
||||
|
||||
if(this.options.focus) {
|
||||
return this;
|
||||
},
|
||||
cancel: function () {
|
||||
if (this.subpanels.length > 0) {
|
||||
if (this.subpanels[(this.subpanels.length - 1)].onCancel !== undefined) {
|
||||
this
|
||||
.subpanels[(this.subpanels.length - 1)]
|
||||
.onCancel(this.subpanels[(this.subpanels.length - 1)].data);
|
||||
}
|
||||
} else if (this.options.onCancel !== null) {
|
||||
this.options.onCancel(this.options.data);
|
||||
}
|
||||
this.close();
|
||||
|
||||
return this;
|
||||
},
|
||||
destroy: function () {
|
||||
this.hideOverlay();
|
||||
|
||||
// remove extra modal
|
||||
if (jQuery('#mailpoet_' + this.options.type).length > 0) {
|
||||
jQuery('#mailpoet_' + this.options.type).remove();
|
||||
}
|
||||
|
||||
this.initialized = false;
|
||||
|
||||
return this;
|
||||
},
|
||||
close: function () {
|
||||
if (this.isLocked() === true) { return this; }
|
||||
|
||||
if (this.subpanels.length > 0) {
|
||||
// close subpanel
|
||||
jQuery('.mailpoet_' + this.options.type + '_wrapper').last().remove();
|
||||
|
||||
// show previous panel
|
||||
jQuery('.mailpoet_' + this.options.type + '_wrapper').last().show();
|
||||
|
||||
// remove last subpanels
|
||||
this.subpanels.pop();
|
||||
|
||||
// focus on previous panel
|
||||
if (this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
// set popup as opened
|
||||
this.opened = true;
|
||||
|
||||
// trigger init event if specified
|
||||
if(this.options.onInit !== null) {
|
||||
this.options.onInit(this);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
focus: function() {
|
||||
if(this.options.type == 'popup') {
|
||||
jQuery('#mailpoet_'+this.options.type).focus();
|
||||
} else {
|
||||
// panel and subpanel
|
||||
jQuery('#mailpoet_'+this.options.type+' .mailpoet_panel_wrapper')
|
||||
.filter(':visible').focus();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
highlightOn: function(element) {
|
||||
jQuery(element).addClass('mailpoet_modal_highlight');
|
||||
return this;
|
||||
},
|
||||
highlightOff: function() {
|
||||
jQuery('.mailpoet_modal_highlight')
|
||||
.removeClass('mailpoet_modal_highlight');
|
||||
return this;
|
||||
},
|
||||
hideModal: function(callback) {
|
||||
// set modal as closed
|
||||
this.opened = false;
|
||||
|
||||
// hide modal
|
||||
jQuery('#mailpoet_'+this.options.type).hide();
|
||||
|
||||
// remove class on highlighted elements
|
||||
this.highlightOff();
|
||||
|
||||
// remove class from body to let it be scrollable
|
||||
jQuery('body').removeClass('mailpoet_modal_opened');
|
||||
|
||||
return this;
|
||||
},
|
||||
showOverlay: function(force) {
|
||||
jQuery('#mailpoet_modal_overlay').show();
|
||||
return this;
|
||||
},
|
||||
hideOverlay: function() {
|
||||
jQuery('#mailpoet_modal_overlay').hide();
|
||||
return this;
|
||||
},
|
||||
popup: function(opts) {
|
||||
// get options
|
||||
var options = opts || {};
|
||||
// set modal type
|
||||
options.type = 'popup';
|
||||
// set overlay state
|
||||
options.overlay = options.overlay || true;
|
||||
// initialize modal
|
||||
this.init(options);
|
||||
// open modal
|
||||
this.open();
|
||||
|
||||
return this;
|
||||
},
|
||||
panel: function(opts) {
|
||||
// get options
|
||||
var options = opts || {};
|
||||
// reset subpanels
|
||||
this.subpanels = [];
|
||||
// set modal type
|
||||
options.type = 'panel';
|
||||
// set overlay state
|
||||
options.overlay = options.overlay || false;
|
||||
// set highlighted element
|
||||
options.highlight = options.highlight || null;
|
||||
// set modal dimensions
|
||||
options.width = options.width || '40%';
|
||||
options.height = options.height || 'auto';
|
||||
// initialize modal
|
||||
this.init(options);
|
||||
// open modal
|
||||
this.open();
|
||||
|
||||
return this;
|
||||
},
|
||||
subpanel: function(options) {
|
||||
if(this.opened === false) {
|
||||
// if no panel is already opened, let's create one instead
|
||||
this.panel(options);
|
||||
} else {
|
||||
// if a panel is already opened, add a sub panel to it
|
||||
this.subpanels.push(options);
|
||||
this.loadTemplate();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
loading: function(toggle) {
|
||||
// make sure the overlay is initialized and that it's visible
|
||||
this.initOverlay(true);
|
||||
|
||||
if(toggle === true) {
|
||||
this.showLoading();
|
||||
} else {
|
||||
this.hideLoading();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
showLoading: function() {
|
||||
jQuery('#mailpoet_loading').show();
|
||||
|
||||
// add loading class to overlay
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.addClass('mailpoet_overlay_loading');
|
||||
|
||||
return this;
|
||||
},
|
||||
hideLoading: function() {
|
||||
jQuery('#mailpoet_loading').hide();
|
||||
|
||||
// remove loading class from overlay
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.removeClass('mailpoet_overlay_loading');
|
||||
|
||||
return this;
|
||||
},
|
||||
open: function() {
|
||||
// load template if specified
|
||||
if(this.options.template !== null) {
|
||||
// check if a url was specified to get extra data
|
||||
if(this.options.url !== null) {
|
||||
this.loadUrl();
|
||||
} else {
|
||||
// load template
|
||||
this.loadTemplate();
|
||||
|
||||
// show modal window
|
||||
this.showModal();
|
||||
}
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
success: function() {
|
||||
if(this.subpanels.length > 0) {
|
||||
if(this.subpanels[(this.subpanels.length - 1)].onSuccess !== undefined) {
|
||||
this.subpanels[(this.subpanels.length - 1)].onSuccess(this.subpanels[(this.subpanels.length - 1)].data);
|
||||
}
|
||||
} else {
|
||||
if(this.options.onSuccess !== null) {
|
||||
this.options.onSuccess(this.options.data);
|
||||
}
|
||||
}
|
||||
this.close();
|
||||
|
||||
return this;
|
||||
},
|
||||
cancel: function() {
|
||||
if(this.subpanels.length > 0) {
|
||||
if(this.subpanels[(this.subpanels.length - 1)].onCancel !== undefined) {
|
||||
this.subpanels[(this.subpanels.length - 1)].onCancel(this.subpanels[(this.subpanels.length - 1)].data);
|
||||
}
|
||||
} else {
|
||||
if(this.options.onCancel !== null) {
|
||||
this.options.onCancel(this.options.data);
|
||||
}
|
||||
}
|
||||
this.close();
|
||||
|
||||
return this;
|
||||
},
|
||||
destroy: function() {
|
||||
this.hideOverlay();
|
||||
|
||||
// remove extra modal
|
||||
if(jQuery('#mailpoet_'+this.options.type).length > 0) {
|
||||
jQuery('#mailpoet_'+this.options.type).remove();
|
||||
}
|
||||
|
||||
this.initialized = false;
|
||||
|
||||
return this;
|
||||
},
|
||||
close: function() {
|
||||
if(this.isLocked() === true) { return this; }
|
||||
|
||||
if(this.subpanels.length > 0) {
|
||||
// close subpanel
|
||||
jQuery('.mailpoet_'+this.options.type+'_wrapper').last().remove();
|
||||
|
||||
// show previous panel
|
||||
jQuery('.mailpoet_'+this.options.type+'_wrapper').last().show();
|
||||
|
||||
// remove last subpanels
|
||||
this.subpanels.pop();
|
||||
|
||||
// focus on previous panel
|
||||
if(this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// remove event handlers
|
||||
this.removeEvents();
|
||||
|
||||
// hide modal window
|
||||
this.hideModal();
|
||||
|
||||
// destroy modal element
|
||||
this.destroy();
|
||||
|
||||
// restore the previously focused element
|
||||
if(this.prevFocus !== undefined){
|
||||
this.prevFocus.focus();
|
||||
}
|
||||
|
||||
// reset options
|
||||
this.options = {
|
||||
onSuccess: null,
|
||||
onCancel: null
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// remove event handlers
|
||||
this.removeEvents();
|
||||
|
||||
// hide modal window
|
||||
this.hideModal();
|
||||
|
||||
// destroy modal element
|
||||
this.destroy();
|
||||
|
||||
// restore the previously focused element
|
||||
if (this.prevFocus !== undefined) {
|
||||
this.prevFocus.focus();
|
||||
}
|
||||
|
||||
// reset options
|
||||
this.options = {
|
||||
onSuccess: null,
|
||||
onCancel: null
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -1,5 +1,7 @@
|
||||
define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
/* eslint-disable func-names */
|
||||
define('mp2migrator', ['mailpoet', 'jquery'], function (mp, jQuery) {
|
||||
'use strict';
|
||||
|
||||
var MailPoet = mp;
|
||||
MailPoet.MP2Migrator = {
|
||||
|
||||
@ -11,7 +13,7 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
clearTimeout(MailPoet.MP2Migrator.displayLogs_timeout);
|
||||
clearTimeout(MailPoet.MP2Migrator.updateProgressbar_timeout);
|
||||
clearTimeout(MailPoet.MP2Migrator.update_wordpress_info_timeout);
|
||||
setTimeout(MailPoet.MP2Migrator.updateDisplay, 1000)
|
||||
setTimeout(MailPoet.MP2Migrator.updateDisplay, 1000);
|
||||
},
|
||||
|
||||
stopLogger: function () {
|
||||
@ -25,51 +27,54 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
|
||||
displayLogs: function () {
|
||||
jQuery.ajax({
|
||||
url: mailpoet_mp2_migrator.log_file_url,
|
||||
url: window.mailpoet_mp2_migrator.log_file_url,
|
||||
cache: false
|
||||
}).done(function (result) {
|
||||
jQuery('#logger').html('');
|
||||
result.split('\n').forEach(function (resultRow) {
|
||||
var row = resultRow;
|
||||
if(row.substr(0, 7) === '[ERROR]' || row.substr(0, 9) === '[WARNING]' || row === MailPoet.I18n.t('import_stopped_by_user')) {
|
||||
if (row.substr(0, 7) === '[ERROR]' || row.substr(0, 9) === '[WARNING]' || row === MailPoet.I18n.t('import_stopped_by_user')) {
|
||||
row = '<span class="error_msg">' + row + '</span>'; // Mark the errors in red
|
||||
}
|
||||
// Test if the import is complete
|
||||
else if(row === MailPoet.I18n.t('import_complete')) {
|
||||
} else if (row === MailPoet.I18n.t('import_complete')) { // Test if the import is complete
|
||||
jQuery('#import-actions').hide();
|
||||
jQuery('#upgrade-completed').show();
|
||||
}
|
||||
jQuery('#logger').append(row + '<br />\n');
|
||||
|
||||
});
|
||||
jQuery('#logger').append('<span class="error_msg">' + MailPoet.MP2Migrator.fatal_error + '</span>' + '<br />\n');
|
||||
jQuery('#logger').append('<span class="error_msg">' + MailPoet.MP2Migrator.fatal_error + '</span><br />\n');
|
||||
}).always(function () {
|
||||
if(MailPoet.MP2Migrator.is_logging) {
|
||||
MailPoet.MP2Migrator.displayLogs_timeout = setTimeout(MailPoet.MP2Migrator.displayLogs, 1000);
|
||||
if (MailPoet.MP2Migrator.is_logging) {
|
||||
MailPoet.MP2Migrator.displayLogs_timeout = setTimeout(
|
||||
MailPoet.MP2Migrator.displayLogs,
|
||||
1000
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
updateProgressbar: function () {
|
||||
jQuery.ajax({
|
||||
url: mailpoet_mp2_migrator.progress_url,
|
||||
url: window.mailpoet_mp2_migrator.progress_url,
|
||||
cache: false,
|
||||
dataType: 'json'
|
||||
}).always(function (result) {
|
||||
// Move the progress bar
|
||||
var progress = 0;
|
||||
if((result.total !== undefined) && (Number(result.total) !== 0)) {
|
||||
progress = Math.round(Number(result.current) / Number(result.total) * 100);
|
||||
if ((result.total !== undefined) && (Number(result.total) !== 0)) {
|
||||
progress = Math.round((Number(result.current) / Number(result.total)) * 100);
|
||||
}
|
||||
jQuery('#progressbar').progressbar('option', 'value', progress);
|
||||
jQuery('#progresslabel').html(progress + '%');
|
||||
if(Number(result.current) !== 0) {
|
||||
if (Number(result.current) !== 0) {
|
||||
jQuery('#skip-import').hide();
|
||||
jQuery('#progressbar').show();
|
||||
jQuery('#logger-container').show();
|
||||
}
|
||||
if(MailPoet.MP2Migrator.is_logging) {
|
||||
MailPoet.MP2Migrator.updateProgressbar_timeout = setTimeout(MailPoet.MP2Migrator.updateProgressbar, 1000);
|
||||
if (MailPoet.MP2Migrator.is_logging) {
|
||||
MailPoet.MP2Migrator.updateProgressbar_timeout = setTimeout(
|
||||
MailPoet.MP2Migrator.updateProgressbar,
|
||||
1000
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -96,19 +101,20 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
}
|
||||
}).always(function () {
|
||||
MailPoet.MP2Migrator.stopLogger();
|
||||
MailPoet.MP2Migrator.updateDisplay(); // Get the latest information after the import was stopped
|
||||
// Get the latest information after the import was stopped
|
||||
MailPoet.MP2Migrator.updateDisplay();
|
||||
MailPoet.MP2Migrator.reactivateImportButton();
|
||||
}).done(function (response) {
|
||||
if(response) {
|
||||
if (response) {
|
||||
MailPoet.MP2Migrator.fatal_error = response.data;
|
||||
}
|
||||
}).fail(function (response) {
|
||||
if(response.errors.length > 0) {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(function (error) {
|
||||
return error.message;
|
||||
}),
|
||||
{scroll: true}
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -132,14 +138,15 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
}).always(function () {
|
||||
jQuery('#stop-import').removeAttr('disabled'); // Enable the button
|
||||
MailPoet.MP2Migrator.reactivateImportButton();
|
||||
MailPoet.MP2Migrator.updateDisplay(); // Get the latest information after the import was stopped
|
||||
// Get the latest information after the import was stopped
|
||||
MailPoet.MP2Migrator.updateDisplay();
|
||||
}).fail(function (response) {
|
||||
if(response.errors.length > 0) {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(function (error) {
|
||||
return error.message;
|
||||
}),
|
||||
{scroll: true}
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -157,12 +164,12 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
}).done(function () {
|
||||
MailPoet.MP2Migrator.gotoWelcomePage();
|
||||
}).fail(function (response) {
|
||||
if(response.errors.length > 0) {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(function (error) {
|
||||
return error.message;
|
||||
}),
|
||||
{scroll: true}
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -175,35 +182,34 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Actions to run when the DOM is ready
|
||||
*/
|
||||
jQuery(function () {
|
||||
jQuery('#progressbar').progressbar({value: 0});
|
||||
jQuery('#progressbar').progressbar({ value: 0 });
|
||||
|
||||
// Import button
|
||||
jQuery('#import').click(function() {
|
||||
jQuery('#import').click(function () {
|
||||
MailPoet.MP2Migrator.startImport();
|
||||
});
|
||||
|
||||
|
||||
// Stop import button
|
||||
jQuery('#stop-import').click(function() {
|
||||
jQuery('#stop-import').click(function () {
|
||||
MailPoet.MP2Migrator.stopImport();
|
||||
});
|
||||
|
||||
// Skip import link
|
||||
jQuery('#skip-import').click(function() {
|
||||
jQuery('#skip-import').click(function () {
|
||||
MailPoet.MP2Migrator.skipImport();
|
||||
});
|
||||
|
||||
// Go to welcome page
|
||||
jQuery('#goto-welcome').click(function() {
|
||||
jQuery('#goto-welcome').click(function () {
|
||||
MailPoet.MP2Migrator.gotoWelcomePage();
|
||||
});
|
||||
|
||||
|
||||
// Update the display
|
||||
MailPoet.MP2Migrator.updateDisplay();
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,12 +1,8 @@
|
||||
define([
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.radio',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'handlebars',
|
||||
'handlebars_helpers'
|
||||
], function(Backbone, Marionette, BackboneRadio, jQuery, _, Handlebars) {
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.radio'
|
||||
], function (Backbone, Marionette, BackboneRadio) { // eslint-disable-line func-names
|
||||
var Radio = BackboneRadio;
|
||||
|
||||
var AppView = Marionette.View.extend({
|
||||
@ -16,19 +12,20 @@ define([
|
||||
contentRegion: '#mailpoet_editor_content',
|
||||
sidebarRegion: '#mailpoet_editor_sidebar',
|
||||
bottomRegion: '#mailpoet_editor_bottom',
|
||||
headingRegion: '#mailpoet_editor_heading'
|
||||
headingRegion: '#mailpoet_editor_heading',
|
||||
topRegion: '#mailpoet_editor_top'
|
||||
}
|
||||
});
|
||||
|
||||
var EditorApplication = Marionette.Application.extend({
|
||||
region: '#mailpoet_editor',
|
||||
|
||||
onStart: function() {
|
||||
onStart: function () { // eslint-disable-line func-names
|
||||
this._appView = new AppView();
|
||||
this.showView(this._appView);
|
||||
},
|
||||
|
||||
getChannel: function(channel) {
|
||||
getChannel: function (channel) { // eslint-disable-line func-names
|
||||
if (channel === undefined) {
|
||||
return Radio.channel('global');
|
||||
}
|
||||
@ -40,5 +37,4 @@ define([
|
||||
window.EditorApplication = app;
|
||||
|
||||
return app;
|
||||
|
||||
});
|
||||
|
@ -5,11 +5,11 @@
|
||||
* For more check: http://marionettejs.com/docs/marionette.behaviors.html#behaviorslookup
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette'
|
||||
], function(BackboneMarionette) {
|
||||
'backbone.marionette'
|
||||
], function (BackboneMarionette) { // eslint-disable-line func-names
|
||||
var Marionette = BackboneMarionette;
|
||||
var BehaviorsLookup = {};
|
||||
Marionette.Behaviors.behaviorsLookup = function() {
|
||||
Marionette.Behaviors.behaviorsLookup = function () { // eslint-disable-line func-names
|
||||
return BehaviorsLookup;
|
||||
};
|
||||
|
||||
|
@ -4,23 +4,43 @@
|
||||
* Adds a color picker integration with the view
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'mailpoet',
|
||||
'spectrum'
|
||||
], function(Marionette, BehaviorsLookup, MailPoet, Spectrum) {
|
||||
'backbone.marionette',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'mailpoet',
|
||||
'spectrum'
|
||||
], function (Marionette, BehaviorsLookup, MailPoet) { // eslint-disable-line func-names
|
||||
var BL = BehaviorsLookup;
|
||||
|
||||
BL.ColorPickerBehavior = Marionette.Behavior.extend({
|
||||
onRender: function() {
|
||||
this.view.$('.mailpoet_color').spectrum({
|
||||
clickoutFiresChange: true,
|
||||
showInput: true,
|
||||
showInitial: true,
|
||||
preferredFormat: 'hex6',
|
||||
allowEmpty: true,
|
||||
chooseText: MailPoet.I18n.t('selectColor'),
|
||||
cancelText: MailPoet.I18n.t('cancelColorSelection')
|
||||
onRender: function () { // eslint-disable-line func-names
|
||||
var that = this;
|
||||
var preferredFormat = 'hex6';
|
||||
this.view.$('.mailpoet_color').each(function () { // eslint-disable-line func-names
|
||||
var $input = that.view.$(this);
|
||||
var updateColorInput = function (color) { // eslint-disable-line func-names
|
||||
if (color && color.getAlpha() > 0) {
|
||||
$input.val(color.toString(preferredFormat));
|
||||
} else {
|
||||
$input.val('');
|
||||
}
|
||||
$input.trigger('change');
|
||||
};
|
||||
$input.spectrum({
|
||||
clickoutFiresChange: true,
|
||||
showInput: true,
|
||||
showInitial: true,
|
||||
showPalette: true,
|
||||
showSelectionPalette: true,
|
||||
palette: [],
|
||||
localStorageKey: 'newsletter_editor.spectrum.palette',
|
||||
preferredFormat: preferredFormat,
|
||||
allowEmpty: true,
|
||||
chooseText: MailPoet.I18n.t('selectColor'),
|
||||
cancelText: MailPoet.I18n.t('cancelColorSelection'),
|
||||
change: updateColorInput,
|
||||
move: updateColorInput,
|
||||
hide: updateColorInput
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|