mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 15:29:57 +01:00
Compare commits
1531 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b6e4d4300 | ||
|
|
7c145c7b1e | ||
|
|
38030c348d | ||
|
|
625aa3f1bd | ||
|
|
8e337647a2 | ||
|
|
7cfc8dc00e | ||
|
|
30195b6c9e | ||
|
|
a628cb6775 | ||
|
|
531ce433fe | ||
|
|
849097921a | ||
|
|
c8af0d8a38 | ||
|
|
9a1ab6ee0b | ||
|
|
04615b3a17 | ||
|
|
70ff5c2e94 | ||
|
|
b82d51e3cc | ||
|
|
4d26acfd66 | ||
|
|
42f976c82a | ||
|
|
6477d13bea | ||
|
|
ba1e106aaf | ||
|
|
5f9fa481cb | ||
|
|
84554ae71f | ||
|
|
5f9c434aea | ||
|
|
a0c3140e6d | ||
|
|
440fa263c2 | ||
|
|
f5b10564ca | ||
|
|
eb98597220 | ||
|
|
6992d52767 | ||
|
|
3a31d54600 | ||
|
|
3d5b54515a | ||
|
|
b9f1741ea1 | ||
|
|
0fa5273c56 | ||
|
|
916b7cfc44 | ||
|
|
317efc182a | ||
|
|
4bcfcfc4f8 | ||
|
|
02b3b9a4d7 | ||
|
|
57d0fc3774 | ||
|
|
2d1bca11df | ||
|
|
ac0de93139 | ||
|
|
9e39860868 | ||
|
|
b13a4d331c | ||
|
|
065cd5b7b7 | ||
|
|
1d34b5389e | ||
|
|
23df7cc90d | ||
|
|
c9a5d5a406 | ||
|
|
a5d2c86e59 | ||
|
|
f095cc760b | ||
|
|
c256b0675d | ||
|
|
a7af12d026 | ||
|
|
d7995e9be4 | ||
|
|
af2f4b66b9 | ||
|
|
15464aa10c | ||
|
|
7cfc02e7d9 | ||
|
|
c6b0a50e4e | ||
|
|
ac4f093590 | ||
|
|
a03746145b | ||
|
|
3a3b84af61 | ||
|
|
3ba68bf5c5 | ||
|
|
454e9c1d8e | ||
|
|
ee9fb4aeec | ||
|
|
ba2c224d5d | ||
|
|
515eab7dad | ||
|
|
18b0e01a3e | ||
|
|
57c6414d1c | ||
|
|
fdfcaaeb39 | ||
|
|
561b91894c | ||
|
|
aea01a3937 | ||
|
|
9b34178332 | ||
|
|
eced8facb8 | ||
|
|
074e13325e | ||
|
|
d7378f87c5 | ||
|
|
4091c16e97 | ||
|
|
56408b37ac | ||
|
|
fd18eeb62c | ||
|
|
cbd4ed311f | ||
|
|
8122703e3b | ||
|
|
2920b6143b | ||
|
|
8870463afc | ||
|
|
23fa2324a8 | ||
|
|
0df1082778 | ||
|
|
6647b8da3c | ||
|
|
f0b9411e19 | ||
|
|
5c1cc5cdd7 | ||
|
|
df9eb60ffa | ||
|
|
9893792768 | ||
|
|
379033aae4 | ||
|
|
193ab52a35 | ||
|
|
9eb6662db8 | ||
|
|
5e48146f59 | ||
|
|
627702a3a1 | ||
|
|
d9ee6de3d0 | ||
|
|
14df5a6d5f | ||
|
|
2738107b1e | ||
|
|
e53f26ca13 | ||
|
|
d3e80d62e5 | ||
|
|
b7aa20ed88 | ||
|
|
26da4a1aa3 | ||
|
|
4345a629f1 | ||
|
|
5bb4ca7822 | ||
|
|
79ee06f518 | ||
|
|
bc2435efe9 | ||
|
|
504a29f496 | ||
|
|
0948e9db9d | ||
|
|
c298464314 | ||
|
|
67a00a799c | ||
|
|
589c9e4445 | ||
|
|
d1925c8935 | ||
|
|
2923658bb9 | ||
|
|
c333da60d6 | ||
|
|
cf62db1329 | ||
|
|
625a39a892 | ||
|
|
ffc93a77a9 | ||
|
|
0cee2b6a97 | ||
|
|
a24ec2c433 | ||
|
|
e5727462e7 | ||
|
|
c1cb4e192f | ||
|
|
b0c12063a1 | ||
|
|
e4b1e87b9a | ||
|
|
6e136bf83f | ||
|
|
36504d7123 | ||
|
|
8ac4615117 | ||
|
|
7ed04ed3b0 | ||
|
|
c5b2440488 | ||
|
|
37ce236a43 | ||
|
|
31ab55558c | ||
|
|
c51830feec | ||
|
|
291e91891a | ||
|
|
307ede3183 | ||
|
|
d4a20c71c2 | ||
|
|
ec68068b97 | ||
|
|
d9c1445542 | ||
|
|
26f4d05e87 | ||
|
|
b6c941fc3e | ||
|
|
cedcc845d5 | ||
|
|
02a3ed2e0e | ||
|
|
a729daa3d3 | ||
|
|
ba23cceae7 | ||
|
|
d3e9083869 | ||
|
|
09d92ebab6 | ||
|
|
2777bedb51 | ||
|
|
f181e1d237 | ||
|
|
8f7dc43e1f | ||
|
|
249c2f1e69 | ||
|
|
6a4686d17b | ||
|
|
3529943bb0 | ||
|
|
1c63f4bf46 | ||
|
|
380177b777 | ||
|
|
bc22c8d2b1 | ||
|
|
c5cdeaef07 | ||
|
|
a1fb4a2487 | ||
|
|
d34593928d | ||
|
|
5184f8e6a3 | ||
|
|
ea358c239f | ||
|
|
2cf90cc60b | ||
|
|
db24d3e53e | ||
|
|
6a0caebe2e | ||
|
|
ac85946e0f | ||
|
|
fc55a8e49b | ||
|
|
b093aa9fa6 | ||
|
|
e60e9fa3dd | ||
|
|
29bcbf042f | ||
|
|
19719ecac1 | ||
|
|
4d288ad2ee | ||
|
|
eccaf4acff | ||
|
|
c2a2ef212e | ||
|
|
47ab76c7e3 | ||
|
|
dd30b96210 | ||
|
|
3e053784c2 | ||
|
|
7b97cd0cf1 | ||
|
|
809767883b | ||
|
|
d8e38665f1 | ||
|
|
f25ea8f056 | ||
|
|
7c0ba80530 | ||
|
|
6eb083bebe | ||
|
|
3a822f7bc4 | ||
|
|
9a2268a54e | ||
|
|
611171371b | ||
|
|
e1d4fe36f4 | ||
|
|
079d8c19a4 | ||
|
|
418c6dd7f8 | ||
|
|
08304d5064 | ||
|
|
5cdeb28e6b | ||
|
|
c5efd17a9c | ||
|
|
6276ead820 | ||
|
|
b5cdc12478 | ||
|
|
ff511b8968 | ||
|
|
037a84c46f | ||
|
|
3d0f29a172 | ||
|
|
85c239bc18 | ||
|
|
1fb6a39d9d | ||
|
|
9aeac93c9a | ||
|
|
6188076fe6 | ||
|
|
0fc7fd3411 | ||
|
|
078c8d23d5 | ||
|
|
8c845c77c1 | ||
|
|
cb373dae59 | ||
|
|
0a42a889ec | ||
|
|
e8f5080f35 | ||
|
|
00f73957dd | ||
|
|
3a0480e0d8 | ||
|
|
0cee7bcdc8 | ||
|
|
30401df4b7 | ||
|
|
f5d1e98491 | ||
|
|
8f9b2d22c1 | ||
|
|
2b17767b53 | ||
|
|
00a7094802 | ||
|
|
6eaf44e397 | ||
|
|
24b52c77fe | ||
|
|
7a9b906925 | ||
|
|
53bef264e7 | ||
|
|
75b74c4ab3 | ||
|
|
ca3a9aecd6 | ||
|
|
8f7902af94 | ||
|
|
8df3fa31fb | ||
|
|
ff7a1b861e | ||
|
|
d6a82e5c56 | ||
|
|
f714e6fa14 | ||
|
|
6817133819 | ||
|
|
76670e47a1 | ||
|
|
961b24a019 | ||
|
|
068de42e83 | ||
|
|
75f9c0a892 | ||
|
|
3a1b301b32 | ||
|
|
9bb1d99ae4 | ||
|
|
cf052b5bd4 | ||
|
|
65a18fd270 | ||
|
|
d4775cc970 | ||
|
|
07ee5b42f7 | ||
|
|
d5048fea10 | ||
|
|
f3149068e3 | ||
|
|
f5d3a5132a | ||
|
|
70e0ecfef5 | ||
|
|
631e0e0f3f | ||
|
|
b66287cbb9 | ||
|
|
9e6d38bc1a | ||
|
|
6cbe4aeec4 | ||
|
|
e219f56ff4 | ||
|
|
e16b29c566 | ||
|
|
1b3e3ede4d | ||
|
|
0a601f10c0 | ||
|
|
56ce4ab0f2 | ||
|
|
1f6c2f5a0a | ||
|
|
5f3ddd9389 | ||
|
|
fd0c9823fa | ||
|
|
d23588a217 | ||
|
|
ca53b8afca | ||
|
|
053bc03a54 | ||
|
|
78dc70bcdc | ||
|
|
ea00c044d3 | ||
|
|
11cd2b086e | ||
|
|
ae6f0b5505 | ||
|
|
247d1a9aa6 | ||
|
|
6273c2395d | ||
|
|
1396525251 | ||
|
|
de46afff1c | ||
|
|
54fd54d3f0 | ||
|
|
0b41eea2bb | ||
|
|
63207d370b | ||
|
|
e92d88df8b | ||
|
|
a9e6d3665f | ||
|
|
2453927ba3 | ||
|
|
94e6cb02f3 | ||
|
|
9b2b909139 | ||
|
|
c9a3f349ad | ||
|
|
439b539dcb | ||
|
|
ab7f0a2e95 | ||
|
|
b50dbf3ff0 | ||
|
|
d3f1f833ab | ||
|
|
3df099a0e3 | ||
|
|
54dccd11ef | ||
|
|
77ec847e20 | ||
|
|
8bf5fcb8ec | ||
|
|
ff41d76501 | ||
|
|
d9772ed5c6 | ||
|
|
8dba1b37c7 | ||
|
|
8b017dc647 | ||
|
|
c8ee05d671 | ||
|
|
40216e310d | ||
|
|
6a6f99e9ae | ||
|
|
14ad7cf3ac | ||
|
|
ce327dca42 | ||
|
|
ded7670ac3 | ||
|
|
5bf1ec0fd9 | ||
|
|
306c0505a6 | ||
|
|
325cb69a65 | ||
|
|
c3df9b6484 | ||
|
|
db6bfc00fc | ||
|
|
51b5d7a939 | ||
|
|
9490f62dee | ||
|
|
1661dc9ef7 | ||
|
|
aa7731dc5d | ||
|
|
4150564538 | ||
|
|
38043ebade | ||
|
|
9a694a8d3d | ||
|
|
f6997f54eb | ||
|
|
2631c18484 | ||
|
|
c0d819b97a | ||
|
|
972d32c969 | ||
|
|
7f0e51f92f | ||
|
|
6f78c2288f | ||
|
|
b331ba42b8 | ||
|
|
fb3efba204 | ||
|
|
58e09dcff4 | ||
|
|
747055f60e | ||
|
|
aa07c64440 | ||
|
|
6b9f1d8414 | ||
|
|
b4bc1d292a | ||
|
|
c98553d19b | ||
|
|
1ace31216f | ||
|
|
495cec930c | ||
|
|
44ad0ca1ea | ||
|
|
4ba8e9e99d | ||
|
|
1eb85b366d | ||
|
|
3821ae441f | ||
|
|
b01e5f7366 | ||
|
|
b375a543ec | ||
|
|
2c9d848af5 | ||
|
|
8d65c5c2c0 | ||
|
|
0af731b6a2 | ||
|
|
9d870b2c45 | ||
|
|
82abf87f75 | ||
|
|
81ef023b37 | ||
|
|
776fffb2dc | ||
|
|
cfd5d9e209 | ||
|
|
23716ff729 | ||
|
|
18921a4f14 | ||
|
|
569c42724b | ||
|
|
18abf7d644 | ||
|
|
12efdb9fe4 | ||
|
|
fdf884036d | ||
|
|
998018af3e | ||
|
|
cae71f3d60 | ||
|
|
f7e43fab35 | ||
|
|
4f7a4ac1e2 | ||
|
|
5fc7b34ce7 | ||
|
|
cbf73b2290 | ||
|
|
f344a8166e | ||
|
|
9532317928 | ||
|
|
4cf85c462d | ||
|
|
6fe2964f37 | ||
|
|
2108a902c2 | ||
|
|
1520eec677 | ||
|
|
e3ba3a3ea4 | ||
|
|
1c104f9358 | ||
|
|
f8b9536eb8 | ||
|
|
0e34628c6f | ||
|
|
614bc0b254 | ||
|
|
cc6eafdb09 | ||
|
|
df4cbdcc09 | ||
|
|
f5e53a9a4c | ||
|
|
46f654fcee | ||
|
|
aa3a3f4a17 | ||
|
|
59b3b6cc02 | ||
|
|
6d4b8e8401 | ||
|
|
9ac7a60835 | ||
|
|
790dbd381d | ||
|
|
ac6c8a985b | ||
|
|
fea22e0409 | ||
|
|
ac7d595a2d | ||
|
|
819e412e09 | ||
|
|
bdfec68340 | ||
|
|
0b7ef6c8fb | ||
|
|
9880ce977a | ||
|
|
e88f924274 | ||
|
|
fe53dc88e5 | ||
|
|
fdfd6558f2 | ||
|
|
6d9e3dcad1 | ||
|
|
20c4468edd | ||
|
|
7f80e650b1 | ||
|
|
1995837b3f | ||
|
|
88c0617279 | ||
|
|
b8b1bed7ed | ||
|
|
03c6e74c4d | ||
|
|
71639de5ec | ||
|
|
39310cd4af | ||
|
|
582352b2d2 | ||
|
|
f2c271f66d | ||
|
|
d99e1f519e | ||
|
|
c53e2843be | ||
|
|
f438ce04fb | ||
|
|
8be5952b92 | ||
|
|
cfc8610a16 | ||
|
|
ad8a5d9870 | ||
|
|
75cb4e392d | ||
|
|
ec9dd14cb4 | ||
|
|
6e8c852bfa | ||
|
|
f204979ada | ||
|
|
6275327b0a | ||
|
|
f1126e63b1 | ||
|
|
7157ed08f9 | ||
|
|
d33627f1fe | ||
|
|
c2611cfeaf | ||
|
|
056614fa6b | ||
|
|
4c19226959 | ||
|
|
d41a31f614 | ||
|
|
d83dc07368 | ||
|
|
f30d8748f1 | ||
|
|
b4710c292f | ||
|
|
c3cd399087 | ||
|
|
f8a2c94903 | ||
|
|
747b081809 | ||
|
|
55903dab11 | ||
|
|
a6c094f9aa | ||
|
|
b9b978e452 | ||
|
|
bad24f8a85 | ||
|
|
57cffbc4c8 | ||
|
|
86d71bf37f | ||
|
|
c6dd169916 | ||
|
|
cbc85b7a09 | ||
|
|
016af0f419 | ||
|
|
1903d44bf8 | ||
|
|
351c270e0e | ||
|
|
ab3d9f89ec | ||
|
|
97220a27df | ||
|
|
0310fdf99f | ||
|
|
f1a535b7d4 | ||
|
|
6879d8b6ce | ||
|
|
046c5f69ac | ||
|
|
40687f8cc0 | ||
|
|
b8f27ecfd0 | ||
|
|
16dd2ef9d0 | ||
|
|
aaff2f486f | ||
|
|
d9a62373b8 | ||
|
|
bef4f6a0db | ||
|
|
a66c282ee5 | ||
|
|
2d50cc2b2d | ||
|
|
82f1182503 | ||
|
|
360bed8697 | ||
|
|
e4a833b59e | ||
|
|
f8809d5d62 | ||
|
|
8adffb8714 | ||
|
|
9b6649174c | ||
|
|
7f03f7c844 | ||
|
|
843596944f | ||
|
|
0f6c5fe49d | ||
|
|
b94c4e775a | ||
|
|
fc97e88928 | ||
|
|
5b47e6130c | ||
|
|
86edc18f21 | ||
|
|
5ee36e786a | ||
|
|
c4e2e5da8d | ||
|
|
2c14ec0abd | ||
|
|
5b782fd04c | ||
|
|
bdd579ad2d | ||
|
|
d94fa8d2de | ||
|
|
c0f1d4e2da | ||
|
|
e82f49bdab | ||
|
|
b65d48541d | ||
|
|
e2f68194d7 | ||
|
|
bd6f8f4ae6 | ||
|
|
9d4cce93fc | ||
|
|
28ce18f8f0 | ||
|
|
c060d1a36e | ||
|
|
3a05dcf659 | ||
|
|
291ed38a5a | ||
|
|
411d689a48 | ||
|
|
df279f879f | ||
|
|
9e58bccd49 | ||
|
|
092f3b4e24 | ||
|
|
005bb77f55 | ||
|
|
258840d198 | ||
|
|
c483da9efe | ||
|
|
4b3f202c6f | ||
|
|
07571465a7 | ||
|
|
7d7590b914 | ||
|
|
f965961401 | ||
|
|
814ea7f81a | ||
|
|
91a0790695 | ||
|
|
2b8a6c6a89 | ||
|
|
55648d3452 | ||
|
|
60316d1b75 | ||
|
|
7826608384 | ||
|
|
de151c8a9d | ||
|
|
b0a1effaf9 | ||
|
|
fd52d124dd | ||
|
|
b772864f2d | ||
|
|
cdb10cfc1f | ||
|
|
da7f0b7dce | ||
|
|
25461f7ca8 | ||
|
|
da7df9f865 | ||
|
|
00c5dba210 | ||
|
|
f30219d85a | ||
|
|
223e75d326 | ||
|
|
46d4f4a481 | ||
|
|
419ebeafa8 | ||
|
|
4be7917d41 | ||
|
|
191686dffe | ||
|
|
fa791ed4ab | ||
|
|
e29f8a9657 | ||
|
|
e66f6583b1 | ||
|
|
399d90e6cd | ||
|
|
ec2eaae0d3 | ||
|
|
12389b1e0d | ||
|
|
ff6e5a20c3 | ||
|
|
7d6526f962 | ||
|
|
b8fad8452b | ||
|
|
5e9107f376 | ||
|
|
7faaff304a | ||
|
|
94acbf6005 | ||
|
|
a65c21acba | ||
|
|
bfbe4ce1b8 | ||
|
|
aaa636f357 | ||
|
|
d0241ba7ee | ||
|
|
b2230225cc | ||
|
|
e063718e4f | ||
|
|
03436a670f | ||
|
|
8be508de5d | ||
|
|
9370a86246 | ||
|
|
6c5237cdd8 | ||
|
|
2cf5ecd212 | ||
|
|
8e9125772e | ||
|
|
62e863dec0 | ||
|
|
e00098ea6a | ||
|
|
f12bd1b51f | ||
|
|
93942a74cf | ||
|
|
401fd04379 | ||
|
|
4fd54f1692 | ||
|
|
9565994e5c | ||
|
|
6876e1a38a | ||
|
|
7a8e737802 | ||
|
|
9f968611b1 | ||
|
|
3bd4f9499a | ||
|
|
069ef7b12c | ||
|
|
9d6207f80e | ||
|
|
22b493996c | ||
|
|
5284717570 | ||
|
|
96d11e4ffa | ||
|
|
a156247dc3 | ||
|
|
0e4a88d538 | ||
|
|
a2d1cdfc13 | ||
|
|
dcb0c490ba | ||
|
|
7687bdf751 | ||
|
|
85ef04abc4 | ||
|
|
69ae59b933 | ||
|
|
c5e538421e | ||
|
|
d7b1689047 | ||
|
|
bd8396ba6e | ||
|
|
859aff590b | ||
|
|
73d8a844d9 | ||
|
|
5db395799d | ||
|
|
ad1ecf21c3 | ||
|
|
497ca2a5cd | ||
|
|
4e578ed95e | ||
|
|
8b258c8828 | ||
|
|
148117edcb | ||
|
|
a57358f311 | ||
|
|
ee0ad1126d | ||
|
|
2db0002ffd | ||
|
|
a74e07c491 | ||
|
|
8a12abf795 | ||
|
|
62c7cfcd10 | ||
|
|
9257d6982f | ||
|
|
58da1cd489 | ||
|
|
69078aaf49 | ||
|
|
33aa0737a4 | ||
|
|
8b47608cc0 | ||
|
|
d25014779d | ||
|
|
5f0380f547 | ||
|
|
bdbd392c6e | ||
|
|
d53a594971 | ||
|
|
2604da4b04 | ||
|
|
77bc8029bb | ||
|
|
11e4a4cad8 | ||
|
|
e37f634a38 | ||
|
|
0a18502c2c | ||
|
|
b8c81c72e4 | ||
|
|
37c2219b87 | ||
|
|
62f6f3910a | ||
|
|
b450e6998e | ||
|
|
10800a0896 | ||
|
|
cf61525130 | ||
|
|
24db65cd90 | ||
|
|
daf7e66ec3 | ||
|
|
18c9a1793e | ||
|
|
2b0f235e23 | ||
|
|
24eca428a6 | ||
|
|
677b0b7055 | ||
|
|
10bd72ad51 | ||
|
|
4ddc98b2b6 | ||
|
|
c1f18f5ecf | ||
|
|
d16a88e731 | ||
|
|
39d0d640e6 | ||
|
|
bfb55f0e1d | ||
|
|
44b07712d5 | ||
|
|
c8ae9e4dcc | ||
|
|
52d1412e4c | ||
|
|
b820130636 | ||
|
|
7d4ecd4272 | ||
|
|
f907b589e1 | ||
|
|
a9d80d73d6 | ||
|
|
fe0ad9c9c2 | ||
|
|
dfa8ce1ad4 | ||
|
|
1ea5dad728 | ||
|
|
a2b81c9378 | ||
|
|
01b0b1602b | ||
|
|
7d4aef0f3b | ||
|
|
5485893c8b | ||
|
|
124543f4a7 | ||
|
|
e84488e126 | ||
|
|
ed2cf5174d | ||
|
|
d0d0a6c224 | ||
|
|
b04f4f0001 | ||
|
|
c822920c36 | ||
|
|
e6354236c1 | ||
|
|
068948e1a2 | ||
|
|
f8bf7ba010 | ||
|
|
586105907d | ||
|
|
abd56ffee3 | ||
|
|
5678b4d067 | ||
|
|
1abb940318 | ||
|
|
15af5bfae7 | ||
|
|
d550a016a9 | ||
|
|
9c8df27bf1 | ||
|
|
1ce0176ab6 | ||
|
|
5bc46c49d2 | ||
|
|
292d45a7db | ||
|
|
2d375954ab | ||
|
|
7ff6d2a828 | ||
|
|
ccac8a93bc | ||
|
|
bcbfa0e32a | ||
|
|
453cd62a51 | ||
|
|
56e1cbc78e | ||
|
|
58cd77449c | ||
|
|
db92c7b32d | ||
|
|
6ba54d2b3d | ||
|
|
987429208c | ||
|
|
2eae104c7a | ||
|
|
6f2be2a2d2 | ||
|
|
e75960fee3 | ||
|
|
ec71ccdd0d | ||
|
|
a5d47a36f0 | ||
|
|
866b63530a | ||
|
|
57feac985c | ||
|
|
c72c980ad2 | ||
|
|
790d29cbfb | ||
|
|
b9947f6984 | ||
|
|
79b7c3c38f | ||
|
|
a9df896538 | ||
|
|
ad713a1342 | ||
|
|
b58479cba6 | ||
|
|
107341f33f | ||
|
|
1ed0fcb379 | ||
|
|
63892782b4 | ||
|
|
f5a480c72e | ||
|
|
d80a29b34d | ||
|
|
5aefc60f2e | ||
|
|
683c1fe477 | ||
|
|
6b4692f6c7 | ||
|
|
1351f11551 | ||
|
|
58280b8d3f | ||
|
|
652ca75f40 | ||
|
|
4b5f1590aa | ||
|
|
152c987ed4 | ||
|
|
19311c7ec1 | ||
|
|
eec2d122cc | ||
|
|
62f39fe39c | ||
|
|
394fce3825 | ||
|
|
9c6934c0aa | ||
|
|
993bc5170b | ||
|
|
d285eda4c2 | ||
|
|
0bcbe39521 | ||
|
|
78502b2026 | ||
|
|
df0c806381 | ||
|
|
d089a1d9c8 | ||
|
|
e41072c448 | ||
|
|
36d18d531c | ||
|
|
fb18412fb3 | ||
|
|
9a8c2b9aa5 | ||
|
|
678b39a170 | ||
|
|
d03b0d92f4 | ||
|
|
11f9ba74e8 | ||
|
|
f4f5bffcd9 | ||
|
|
d763f9c63e | ||
|
|
44acd3a969 | ||
|
|
4d7510dc11 | ||
|
|
f4a3efc3bc | ||
|
|
40bc980084 | ||
|
|
cf62d1dfa2 | ||
|
|
378e59563d | ||
|
|
1196e06dd6 | ||
|
|
028bbf08c6 | ||
|
|
1d6a474b31 | ||
|
|
79e68eb5df | ||
|
|
13a1c7cfee | ||
|
|
a3812141dd | ||
|
|
5833f1e2da | ||
|
|
88311f21cf | ||
|
|
64b33d60f4 | ||
|
|
8ccbcf0488 | ||
|
|
4fb485d1dd | ||
|
|
244c34a536 | ||
|
|
52a704e53d | ||
|
|
5852ca4179 | ||
|
|
04820b8adc | ||
|
|
5be92b7a8a | ||
|
|
addecbbb22 | ||
|
|
ec8bf9357a | ||
|
|
432b4b1e68 | ||
|
|
ea5935b1d1 | ||
|
|
65db6ea26c | ||
|
|
a77b5e69e7 | ||
|
|
c9da739ea3 | ||
|
|
fa3c9095c7 | ||
|
|
9e4ee6ec91 | ||
|
|
95442ef0b5 | ||
|
|
1f92ec715a | ||
|
|
f81503dd70 | ||
|
|
0642e34a77 | ||
|
|
4e8e3e1865 | ||
|
|
48170d2fa0 | ||
|
|
8936e82e9b | ||
|
|
e0162d276d | ||
|
|
14ff7fae33 | ||
|
|
2c78b3efca | ||
|
|
5104bde6d4 | ||
|
|
59f05ec8a2 | ||
|
|
20047fbdaf | ||
|
|
81dd33c2e9 | ||
|
|
1cf390206e | ||
|
|
8169a6f3f8 | ||
|
|
2165ded8d4 | ||
|
|
ef80e28d1d | ||
|
|
234abb38d9 | ||
|
|
3a722ba44e | ||
|
|
3f56c5a727 | ||
|
|
e55b239536 | ||
|
|
c2f374f0db | ||
|
|
29c6a70611 | ||
|
|
0bbbe96ae0 | ||
|
|
40010f7ff7 | ||
|
|
49aae0c50e | ||
|
|
5ea7ec1013 | ||
|
|
75ee0670f6 | ||
|
|
6e327880a3 | ||
|
|
ceaa20d5d4 | ||
|
|
10589e7940 | ||
|
|
5e9a7a112a | ||
|
|
a672a765b4 | ||
|
|
4794097992 | ||
|
|
f012d2d13a | ||
|
|
d0a22ad416 | ||
|
|
6dee662b40 | ||
|
|
1d8b87e33f | ||
|
|
5db91538c6 | ||
|
|
7d6fb9eee6 | ||
|
|
694b14511d | ||
|
|
bbab97137f | ||
|
|
3408db0c9b | ||
|
|
1bbf8dffeb | ||
|
|
887b34dd31 | ||
|
|
0f5166d690 | ||
|
|
e507300134 | ||
|
|
e38c5cac4a | ||
|
|
463a55897c | ||
|
|
192cc4eb9b | ||
|
|
e5fe28b720 | ||
|
|
defc70e656 | ||
|
|
d589dbcbea | ||
|
|
3ed8620d7a | ||
|
|
bc5501eecb | ||
|
|
2ae6bac390 | ||
|
|
118621cb8b | ||
|
|
3118ed5f56 | ||
|
|
ac17fc8efd | ||
|
|
85c1ec67c2 | ||
|
|
959a2ec379 | ||
|
|
1372c9b1cc | ||
|
|
67dd5f256d | ||
|
|
2c3ced3fba | ||
|
|
a592f6fe0b | ||
|
|
9123cb7796 | ||
|
|
df09c01a25 | ||
|
|
b97301d82d | ||
|
|
6b887a98cd | ||
|
|
18a26b42e2 | ||
|
|
85de4ed0e3 | ||
|
|
80125ce298 | ||
|
|
27542d4fa4 | ||
|
|
a47e446b60 | ||
|
|
471fb4da6f | ||
|
|
a2a6888982 | ||
|
|
864a938f8d | ||
|
|
bdd30238bf | ||
|
|
528ce7131e | ||
|
|
53bd1641bb | ||
|
|
b10725cebc | ||
|
|
719cc5466a | ||
|
|
e373cf18e0 | ||
|
|
7ef5fa5630 | ||
|
|
d139b0388a | ||
|
|
014ab5d7ee | ||
|
|
64f3949967 | ||
|
|
7913edd34b | ||
|
|
2136dc34fe | ||
|
|
c559b42151 | ||
|
|
a33d974c74 | ||
|
|
ede7af6b9d | ||
|
|
5cd4bf5c98 | ||
|
|
045fae9b6f | ||
|
|
bbd46644e0 | ||
|
|
3e67c0a878 | ||
|
|
924f01158d | ||
|
|
8d5dd60fb4 | ||
|
|
958ea586ec | ||
|
|
5f6cc58186 | ||
|
|
426f59e41a | ||
|
|
fff9fa0ca5 | ||
|
|
57aced69cf | ||
|
|
afac6baa11 | ||
|
|
b4630aeb38 | ||
|
|
cac02663e6 | ||
|
|
e59f1e5a82 | ||
|
|
90d077d7c2 | ||
|
|
a77d6bcfa0 | ||
|
|
bee5abfbf0 | ||
|
|
664447a67b | ||
|
|
b850443090 | ||
|
|
98d0c760a9 | ||
|
|
76bb7fe4af | ||
|
|
26584b9909 | ||
|
|
6a9724dd3e | ||
|
|
c7a41ddfda | ||
|
|
2df05dd16d | ||
|
|
50c2c55554 | ||
|
|
d72b99c5b2 | ||
|
|
5adadfdb40 | ||
|
|
7574195ca2 | ||
|
|
5b5ef98495 | ||
|
|
226e5350b7 | ||
|
|
b9b83c3b16 | ||
|
|
971fb2b19a | ||
|
|
378b60783c | ||
|
|
d901558481 | ||
|
|
1515ee9193 | ||
|
|
9c34471800 | ||
|
|
6f16f6f134 | ||
|
|
7c0dcd6808 | ||
|
|
33790dbb33 | ||
|
|
ea8b7b7a3a | ||
|
|
8714aa9202 | ||
|
|
3731d61b78 | ||
|
|
927e99fcb3 | ||
|
|
ef6ec100f2 | ||
|
|
386a39e274 | ||
|
|
38d3b5cf43 | ||
|
|
5718299210 | ||
|
|
2e64d560b1 | ||
|
|
82270e0c13 | ||
|
|
ca22b56148 | ||
|
|
eca3896bfc | ||
|
|
1c2c38545a | ||
|
|
464e1fc6c7 | ||
|
|
02b8499b0c | ||
|
|
b5caa41386 | ||
|
|
a8ff0e3892 | ||
|
|
ebab884441 | ||
|
|
2c95992eb1 | ||
|
|
1666774a1e | ||
|
|
3cc0dc08db | ||
|
|
f9bcf48700 | ||
|
|
8a618fee64 | ||
|
|
a6f7637134 | ||
|
|
f04ef0b359 | ||
|
|
608457bd01 | ||
|
|
508cf1ffdb | ||
|
|
6f38933e81 | ||
|
|
8ff2ca4c5a | ||
|
|
6a0d5c69ab | ||
|
|
2747877195 | ||
|
|
584b33d41a | ||
|
|
f4330ff77d | ||
|
|
60c43184cb | ||
|
|
2fe2eb9f21 | ||
|
|
04ee52d1ad | ||
|
|
99e047171f | ||
|
|
ddc8668837 | ||
|
|
e568b992a9 | ||
|
|
23221a8f8f | ||
|
|
4eed10cd4b | ||
|
|
da02c0992a | ||
|
|
2437112f3c | ||
|
|
276b4bb0ab | ||
|
|
acf271344d | ||
|
|
7b5d6f7031 | ||
|
|
767ce29e39 | ||
|
|
daad16ef1b | ||
|
|
1718135614 | ||
|
|
a269d49392 | ||
|
|
3a8775f545 | ||
|
|
842dc0d49e | ||
|
|
3f9ed2f344 | ||
|
|
5640baa175 | ||
|
|
f102a8cb3d | ||
|
|
a65c468e81 | ||
|
|
4cc3f44caa | ||
|
|
8afe004a7f | ||
|
|
185e9b5272 | ||
|
|
78b76cf023 | ||
|
|
3606d3d0bd | ||
|
|
3be95e752f | ||
|
|
1532de8f20 | ||
|
|
95bd217c3c | ||
|
|
f633c921cc | ||
|
|
e8c79ffd97 | ||
|
|
e842eb9d9e | ||
|
|
71bdb5e25e | ||
|
|
b58f36b5af | ||
|
|
dc20f4f32a | ||
|
|
c765fff4b3 | ||
|
|
032f5654a4 | ||
|
|
0e01e52cac | ||
|
|
37b2c97dc1 | ||
|
|
c61b07febb | ||
|
|
620fd7ed88 | ||
|
|
b314ea13aa | ||
|
|
3e8572dbe9 | ||
|
|
5ba7b4154c | ||
|
|
ad8d0a2ab1 | ||
|
|
4a1e16449d | ||
|
|
41d31cb5ea | ||
|
|
909e2cbf89 | ||
|
|
1290503895 | ||
|
|
a204b24d78 | ||
|
|
8be5a2182b | ||
|
|
ee30694cf1 | ||
|
|
37a9a30187 | ||
|
|
de34910a6d | ||
|
|
86c969998f | ||
|
|
158874039a | ||
|
|
175cebc4a1 | ||
|
|
20aba0fd7c | ||
|
|
efc2da90d3 | ||
|
|
12ad800d1f | ||
|
|
de6c35f4ab | ||
|
|
46816a74e9 | ||
|
|
1111c3d1b1 | ||
|
|
e919685ad3 | ||
|
|
238ba9b9b4 | ||
|
|
1d966a0c92 | ||
|
|
3e87b21a8e | ||
|
|
21309f1451 | ||
|
|
225644f82b | ||
|
|
285fffc7df | ||
|
|
7c760387f3 | ||
|
|
94ff668044 | ||
|
|
270b1191f5 | ||
|
|
6208c516c2 | ||
|
|
b695c9591e | ||
|
|
911da0f34f | ||
|
|
14c2e257a0 | ||
|
|
52ec745ed0 | ||
|
|
8f42dc3c33 | ||
|
|
45530114a7 | ||
|
|
5b8baed0bc | ||
|
|
936e95a146 | ||
|
|
61a3f3f7c1 | ||
|
|
b3545ccb8b | ||
|
|
d217f1e4fb | ||
|
|
bb373061ae | ||
|
|
c65cb83348 | ||
|
|
e3a6436031 | ||
|
|
f56a5f0ad6 | ||
|
|
9044721c89 | ||
|
|
ce5729ba53 | ||
|
|
fc70a50fd6 | ||
|
|
a8d61cb5de | ||
|
|
d8af66cfd7 | ||
|
|
1aa69e61ac | ||
|
|
be3ecf1377 | ||
|
|
571674a4f5 | ||
|
|
a06520337f | ||
|
|
bf2b7dcf7b | ||
|
|
86faceb94f | ||
|
|
963e365da4 | ||
|
|
4332ac35db | ||
|
|
7e5d58f94b | ||
|
|
6b3b6106bf | ||
|
|
f0c12e6246 | ||
|
|
741934b1da | ||
|
|
35e7bfe506 | ||
|
|
dcc2ddfdb8 | ||
|
|
f59441eb55 | ||
|
|
9cb6ddf742 | ||
|
|
035a84b55d | ||
|
|
d892343e57 | ||
|
|
aaa3976e5a | ||
|
|
0d4afd7a37 | ||
|
|
a3a8976fdf | ||
|
|
4edb1ca61b | ||
|
|
d6374f259a | ||
|
|
7b4f40d3b1 | ||
|
|
9c4eb549b7 | ||
|
|
c380fb638b | ||
|
|
908e993727 | ||
|
|
4128864e39 | ||
|
|
304f800018 | ||
|
|
fcc0c5e345 | ||
|
|
77887d83e9 | ||
|
|
3514ff64fe | ||
|
|
b56ede81f0 | ||
|
|
427c39e88e | ||
|
|
e450923409 | ||
|
|
c862b0bc26 | ||
|
|
575a1e4603 | ||
|
|
a74ccad282 | ||
|
|
7cb678bdb6 | ||
|
|
14110d5475 | ||
|
|
c742eee1cc | ||
|
|
ffeb5648c6 | ||
|
|
86c87929ec | ||
|
|
e0e92b843c | ||
|
|
a996137317 | ||
|
|
9fb55d8d0f | ||
|
|
861f53c5dc | ||
|
|
a588395ba9 | ||
|
|
e80b7fe19e | ||
|
|
4e23f07fcd | ||
|
|
a571f42e1b | ||
|
|
2fd5492286 | ||
|
|
4e227fcae5 | ||
|
|
99c6a78134 | ||
|
|
a93ef3f752 | ||
|
|
0814d5e3bb | ||
|
|
2e8be3c67f | ||
|
|
191bf8730a | ||
|
|
09e8dfdbfd | ||
|
|
a1ea841034 | ||
|
|
f9f836959c | ||
|
|
c4ce2d1648 | ||
|
|
0f66032c9b | ||
|
|
8678f22f6b | ||
|
|
4ca1f1f4ca | ||
|
|
913efdbd6a | ||
|
|
9c123f7d3d | ||
|
|
2b00e93f22 | ||
|
|
8322a0cfa3 | ||
|
|
ab6b82eaaa | ||
|
|
b16e8066ca | ||
|
|
09e10d16ea | ||
|
|
ef8e1c2fdf | ||
|
|
e2843e6477 | ||
|
|
bc1dd2a7b4 | ||
|
|
3e52101bf8 | ||
|
|
d11772b681 | ||
|
|
32a9acc62e | ||
|
|
d568ddfa5c | ||
|
|
1656f0160b | ||
|
|
14b33824b1 | ||
|
|
7902912269 | ||
|
|
2db04e43d1 | ||
|
|
467c33d3e1 | ||
|
|
87f17d296d | ||
|
|
feeee9ef86 | ||
|
|
7ba964f161 | ||
|
|
77205fb3d8 | ||
|
|
4bba284bf5 | ||
|
|
a6e78de197 | ||
|
|
8fc7755e90 | ||
|
|
fdfb5bb8ac | ||
|
|
d0800136b7 | ||
|
|
d368aeafd4 | ||
|
|
e59c596886 | ||
|
|
411656a9d6 | ||
|
|
8599faaf3b | ||
|
|
90e55ead29 | ||
|
|
f85c94b403 | ||
|
|
b4fa7e38b9 | ||
|
|
411871f636 | ||
|
|
b01d1baa53 | ||
|
|
c3c33e954c | ||
|
|
edd0bc25a2 | ||
|
|
6c57104658 | ||
|
|
26d7181eca | ||
|
|
1fb611655f | ||
|
|
b561c2b47c | ||
|
|
9beefb3162 | ||
|
|
41851b73b9 | ||
|
|
c325c24897 | ||
|
|
efd4e52571 | ||
|
|
565f26db8e | ||
|
|
b641b0e442 | ||
|
|
2a6276a941 | ||
|
|
eb1b9567df | ||
|
|
7dafa2a207 | ||
|
|
5253aa6aef | ||
|
|
1b34530a4a | ||
|
|
ae41b6f5ff | ||
|
|
4d09a345a4 | ||
|
|
8ac3451fbc | ||
|
|
15f1f2a03d | ||
|
|
ffa9ef6a7e | ||
|
|
a75c0cbe62 | ||
|
|
c795ead402 | ||
|
|
91270c9c66 | ||
|
|
9259c2f660 | ||
|
|
138d1e93e3 | ||
|
|
5db9f16174 | ||
|
|
342eac1047 | ||
|
|
88c859fde2 | ||
|
|
8b5bce0b6d | ||
|
|
09c4cf66f5 | ||
|
|
46b859b8df | ||
|
|
a7c23c58e5 | ||
|
|
d206711354 | ||
|
|
0007100a97 | ||
|
|
3d999501a0 | ||
|
|
52f83775e3 | ||
|
|
a35cf5a830 | ||
|
|
39837d0826 | ||
|
|
66bd6a4046 | ||
|
|
4e9d3395e0 | ||
|
|
248c7764f0 | ||
|
|
f72eb1b002 | ||
|
|
25caa5138a | ||
|
|
dffb227df6 | ||
|
|
7c161d5cbc | ||
|
|
7b2313ef0b | ||
|
|
1ad2a3f212 | ||
|
|
1f2363b623 | ||
|
|
d1c069c2ee | ||
|
|
67e772a5ce | ||
|
|
59bb167bd4 | ||
|
|
5c9eb1cdb8 | ||
|
|
94a54d2a82 | ||
|
|
f201d48112 | ||
|
|
df51b64b35 | ||
|
|
93ff915737 | ||
|
|
e30ab9a043 | ||
|
|
8e59a08f9e | ||
|
|
5a874006b4 | ||
|
|
5d639cc633 | ||
|
|
ab7038b49e | ||
|
|
651b354d3e | ||
|
|
90c2079529 | ||
|
|
fa064301a2 | ||
|
|
5424047d02 | ||
|
|
ce2b80aeb9 | ||
|
|
e91ae5542d | ||
|
|
922f263005 | ||
|
|
01af81a0a6 | ||
|
|
bd8c274f38 | ||
|
|
05fb69daa7 | ||
|
|
21f95eba53 | ||
|
|
4ef56054ee | ||
|
|
bec9292a5c | ||
|
|
0fa6328816 | ||
|
|
b30240c340 | ||
|
|
59eb3b4cb2 | ||
|
|
5fa047c1b7 | ||
|
|
30481da51d | ||
|
|
beb8f09a9d | ||
|
|
603bb6c878 | ||
|
|
dac3e57fd4 | ||
|
|
b601d2c8fd | ||
|
|
ab75201f11 | ||
|
|
45b1b0a2ef | ||
|
|
ca2f657c98 | ||
|
|
5c33882f5b | ||
|
|
0f85214693 | ||
|
|
4570c00041 | ||
|
|
4a0c8846e2 | ||
|
|
41e51cd86d | ||
|
|
e2ed3098a3 | ||
|
|
4e9ca82a0f | ||
|
|
f032f310b5 | ||
|
|
6b665b112c | ||
|
|
d9dbe5520d | ||
|
|
5b8674122a | ||
|
|
2e245cd36f | ||
|
|
e36a2ea1b0 | ||
|
|
53b7c95b0d | ||
|
|
46975cca22 | ||
|
|
a418acc32b | ||
|
|
eeb35fc521 | ||
|
|
90ca9f9d49 | ||
|
|
ba267a389e | ||
|
|
0ab99806db | ||
|
|
c300a3b8f8 | ||
|
|
8480fb68ac | ||
|
|
8e82056afa | ||
|
|
3e34d54b9a | ||
|
|
9ad7b208ba | ||
|
|
1fa62d2bdc | ||
|
|
b659c56aec | ||
|
|
3bd02b95fe | ||
|
|
9e5ad84a48 | ||
|
|
40a6c4bf72 | ||
|
|
22acffac5c | ||
|
|
ede749821d | ||
|
|
f26e518c03 | ||
|
|
0f6a517589 | ||
|
|
3530f4fdef | ||
|
|
b92476d40d | ||
|
|
0d0bb2c229 | ||
|
|
de59bad0f8 | ||
|
|
72ad49610c | ||
|
|
dcd1f3b10d | ||
|
|
52cf554ea2 | ||
|
|
f30f6485ba | ||
|
|
dd8b503aa0 | ||
|
|
dab30673e0 | ||
|
|
13689c2065 | ||
|
|
6e23627f26 | ||
|
|
7db85cc79c | ||
|
|
9b6f218f33 | ||
|
|
829da9ee3a | ||
|
|
033b54104e | ||
|
|
e5cedd074b | ||
|
|
a6741cb761 | ||
|
|
8cbc2a27cd | ||
|
|
5f1639dc63 | ||
|
|
ed87faad92 | ||
|
|
8d8b803e66 | ||
|
|
e4ed00d84a | ||
|
|
239f34d40c | ||
|
|
20b9ca56fa | ||
|
|
647ae0fda3 | ||
|
|
806dbd9ee5 | ||
|
|
1ab8442630 | ||
|
|
040c34d693 | ||
|
|
505661404b | ||
|
|
a2ea6faf4d | ||
|
|
ce51491b4d | ||
|
|
8942aa8afc | ||
|
|
d241223aa3 | ||
|
|
256cbe3f12 | ||
|
|
8b31ee173e | ||
|
|
182970eb78 | ||
|
|
9ed3da3df2 | ||
|
|
14eaa4d00a | ||
|
|
e134e3dbd9 | ||
|
|
5bfb168cd7 | ||
|
|
5aef09a410 | ||
|
|
76732ab671 | ||
|
|
d16f83fdd8 | ||
|
|
02e10ff8fe | ||
|
|
6a44d8f286 | ||
|
|
4b614d871f | ||
|
|
f54d9af758 | ||
|
|
f883191d99 | ||
|
|
de5ead78d1 | ||
|
|
44bbdf7e39 | ||
|
|
4b4eedf467 | ||
|
|
bb477fd3b1 | ||
|
|
758e316a65 | ||
|
|
2c38e24d00 | ||
|
|
3286d70092 | ||
|
|
9fc37e46fa | ||
|
|
f304f429c5 | ||
|
|
ca24e63d22 | ||
|
|
65c73f639f | ||
|
|
d2833a1997 | ||
|
|
aa8f764436 | ||
|
|
f1909d80db | ||
|
|
7718dd7e98 | ||
|
|
cc66070e85 | ||
|
|
bbdc54b406 | ||
|
|
c013f63b26 | ||
|
|
aa007badb5 | ||
|
|
618a59921a | ||
|
|
bb2e7a720b | ||
|
|
039f71dc61 | ||
|
|
c36e6abd66 | ||
|
|
c2b1142b7a | ||
|
|
e03fb200a6 | ||
|
|
27b8db4c10 | ||
|
|
a964a34a6f | ||
|
|
1c28fd4c4c | ||
|
|
e8529e7d0b | ||
|
|
a6032af594 | ||
|
|
ea49415e14 | ||
|
|
7b26022f9f | ||
|
|
443fecfeb6 | ||
|
|
c3324e3702 | ||
|
|
9e60408769 | ||
|
|
3737bc9371 | ||
|
|
3d2360c995 | ||
|
|
08b8505b6d | ||
|
|
afd53d76c2 | ||
|
|
9607a99a7d | ||
|
|
7e63935001 | ||
|
|
3d767a4d25 | ||
|
|
ee53e1be6e | ||
|
|
57c65ad881 | ||
|
|
a372ae90c2 | ||
|
|
676924cce5 | ||
|
|
c8739c40a5 | ||
|
|
36a3a95ed9 | ||
|
|
95c58c8361 | ||
|
|
66c17a8f53 | ||
|
|
00ff9ac42d | ||
|
|
7172da8ed6 | ||
|
|
c55ea919ef | ||
|
|
53216631a6 | ||
|
|
69d6b52a0e | ||
|
|
e13a8304e6 | ||
|
|
3c2b17853c | ||
|
|
ea1e0a76c1 | ||
|
|
833fe8b729 | ||
|
|
23d508b390 | ||
|
|
288b2a1953 | ||
|
|
e8b24479b9 | ||
|
|
1485c23aba | ||
|
|
d43357f366 | ||
|
|
86b1f1fbac | ||
|
|
c8e5aa05f9 | ||
|
|
04ccce1f67 | ||
|
|
5826821895 | ||
|
|
3ed341304b | ||
|
|
025e73affd | ||
|
|
5635ba2bb7 | ||
|
|
b5e26133a7 | ||
|
|
95637a243c | ||
|
|
cd417a1509 | ||
|
|
631ae3d3d5 | ||
|
|
7c34224304 | ||
|
|
6062e47377 | ||
|
|
c97faa0238 | ||
|
|
a94abb4fb2 | ||
|
|
8ab317b49a | ||
|
|
44dda3d607 | ||
|
|
75ed986437 | ||
|
|
3ac785b9ce | ||
|
|
de367e1558 | ||
|
|
4fead303d1 | ||
|
|
41898af46f | ||
|
|
fe8833876c | ||
|
|
e116998914 | ||
|
|
053f96dec1 | ||
|
|
94494c3c96 | ||
|
|
69b39b4b21 | ||
|
|
5afae3c3f2 | ||
|
|
eed3d84a10 | ||
|
|
e7b996104f | ||
|
|
02f544f813 | ||
|
|
69b5a779e4 | ||
|
|
3f176c1924 | ||
|
|
91d20d8840 | ||
|
|
6998505e3c | ||
|
|
ffa2e0a6f6 | ||
|
|
b54e4fb71b | ||
|
|
8f391d327a | ||
|
|
e0e29f468b | ||
|
|
89b9cc5367 | ||
|
|
fa5c1e495d | ||
|
|
7fdb2c10cb | ||
|
|
e422eebd3c | ||
|
|
3dca7e3539 | ||
|
|
75210b102e | ||
|
|
f0e97a7277 | ||
|
|
f6c30cbeae | ||
|
|
cca7b6b1d4 | ||
|
|
e16c81516e | ||
|
|
c765787102 | ||
|
|
dff3872b43 | ||
|
|
3c91cea232 | ||
|
|
a2e9c013ad | ||
|
|
dd82ab45bc | ||
|
|
46b710b435 | ||
|
|
a6d3e1ee8e | ||
|
|
140c9a941f | ||
|
|
2b29b17044 | ||
|
|
5316f0f28c | ||
|
|
eb58fe9e97 | ||
|
|
5d4ea87402 | ||
|
|
9f1d7240a9 | ||
|
|
ac4d6cc8d0 | ||
|
|
84894274f0 | ||
|
|
ea09002012 | ||
|
|
601ec5cb7a | ||
|
|
565947e074 | ||
|
|
25d1767e6c | ||
|
|
c079f9b95b | ||
|
|
85ec2ee3a0 | ||
|
|
20cfb45c14 | ||
|
|
5314558a8e | ||
|
|
965f69f680 | ||
|
|
02b93d510a | ||
|
|
13a56dd4da | ||
|
|
4cf1b8c400 | ||
|
|
9c805a4317 | ||
|
|
b7b1182e14 | ||
|
|
8dfb0ca993 | ||
|
|
f695aaaea4 | ||
|
|
b398d04d96 | ||
|
|
574df5cf80 | ||
|
|
ac9ef4da76 | ||
|
|
a10893eaad | ||
|
|
34dcd8c346 | ||
|
|
5079077e1d | ||
|
|
0e0d5b23be | ||
|
|
e6c8b30882 | ||
|
|
a222e353ba | ||
|
|
1f120a0127 | ||
|
|
fe05c9b87b | ||
|
|
f795a634b5 | ||
|
|
888b93926c | ||
|
|
687f29f912 | ||
|
|
273bc9d970 | ||
|
|
d593d5a392 | ||
|
|
18fb688cde | ||
|
|
d8fccb0edd | ||
|
|
5843347c46 | ||
|
|
30cfe3bdfa | ||
|
|
fedb0625b8 | ||
|
|
72d012e401 | ||
|
|
8d77b50055 | ||
|
|
42eefbd34c | ||
|
|
e5e5bf1bd8 | ||
|
|
01ec334127 | ||
|
|
916469a903 | ||
|
|
55f205c801 | ||
|
|
8a3fc57cd8 | ||
|
|
6cd1cca4fc | ||
|
|
1c9926ff38 | ||
|
|
638477b06d | ||
|
|
7e3ca73b0e | ||
|
|
57a3a20868 | ||
|
|
4e45c5be95 | ||
|
|
ca89156c4f | ||
|
|
dc5390b3dc | ||
|
|
0e8c7eae99 | ||
|
|
115bdb7e10 | ||
|
|
9738c55633 | ||
|
|
23921c1a35 | ||
|
|
1f3547b15b | ||
|
|
6974a24669 | ||
|
|
2cf35ec2c7 | ||
|
|
d21bb6b8c7 | ||
|
|
9b8b480c8c | ||
|
|
ac654d56d0 | ||
|
|
db9e1a197e | ||
|
|
19bb9a6966 | ||
|
|
b7a1c7b72a | ||
|
|
042486bc73 | ||
|
|
42cc1c6b01 | ||
|
|
407d2c8323 | ||
|
|
8ab14c0639 | ||
|
|
48c281024f | ||
|
|
f7e1bec0cf | ||
|
|
41c7973fc7 | ||
|
|
bc93e70d11 | ||
|
|
320ab41435 | ||
|
|
7213c393fd | ||
|
|
6caadc8396 | ||
|
|
e0d1385061 | ||
|
|
e1eed973a2 | ||
|
|
f4645fc77e | ||
|
|
136ec07450 | ||
|
|
e01605de55 | ||
|
|
5cc48c4e01 | ||
|
|
5e2545c606 | ||
|
|
d227a82056 | ||
|
|
0fd2627cea | ||
|
|
cfcd955cc2 | ||
|
|
7d59b69709 | ||
|
|
e8eb1d9a44 | ||
|
|
2117748f18 | ||
|
|
e76733e8c3 | ||
|
|
2da5f90e9c | ||
|
|
81b100cd3b | ||
|
|
19be8f5104 | ||
|
|
9825daa79b | ||
|
|
10d405112c | ||
|
|
ad64a9d857 | ||
|
|
c4c70d082c | ||
|
|
23a928c5b7 | ||
|
|
8a7f624558 | ||
|
|
331f416e36 | ||
|
|
3a48c79b21 | ||
|
|
0c66de25c4 | ||
|
|
92e8dbf4ea | ||
|
|
aca04cee6b | ||
|
|
0bd227c22d | ||
|
|
7bc45bd9e0 | ||
|
|
9e5363b4df | ||
|
|
37f69d89cc | ||
|
|
30e96aa4ff | ||
|
|
7e1d3b260e | ||
|
|
be558ccac9 | ||
|
|
e991056106 | ||
|
|
b795155345 | ||
|
|
99d0c7cb3e | ||
|
|
01b85f19bc | ||
|
|
65ba214494 | ||
|
|
ccff51144e | ||
|
|
c8f6e7b0e1 | ||
|
|
7cccf8e237 | ||
|
|
e1f646c308 | ||
|
|
2a49a2ba7c | ||
|
|
e9bbd1e0b0 | ||
|
|
84e259f0f1 | ||
|
|
b3f4461f34 | ||
|
|
1df6b76e25 | ||
|
|
2153e20bc5 | ||
|
|
0cbc98ab53 | ||
|
|
0915a0413d | ||
|
|
f4d3d302f4 | ||
|
|
5d5e2264c0 | ||
|
|
b39fc72bd2 | ||
|
|
d6d50c4b66 | ||
|
|
791ef8ad88 | ||
|
|
e0861e5505 | ||
|
|
48c9176d90 | ||
|
|
33cb20561e | ||
|
|
0acb38f586 | ||
|
|
1a41e00a4f | ||
|
|
48ef93e495 | ||
|
|
3f7da86711 | ||
|
|
11aa2314d5 | ||
|
|
179c5065ca | ||
|
|
7c60f73942 | ||
|
|
7095c665b7 | ||
|
|
c6d94885e0 | ||
|
|
d9ddab8239 | ||
|
|
a118d45177 | ||
|
|
416c400367 | ||
|
|
e6839530d8 | ||
|
|
fea9e53be3 | ||
|
|
c5b3792a60 | ||
|
|
4f83b5da5b | ||
|
|
504c8faf4c | ||
|
|
4d6db5b334 | ||
|
|
ec1fc1f1e3 | ||
|
|
563cf8900c | ||
|
|
0850c2f362 | ||
|
|
f30334d80f | ||
|
|
9057a804a2 | ||
|
|
4c5c26033a | ||
|
|
a4f679adcf | ||
|
|
e9e12392ac | ||
|
|
0411c3a98b |
@@ -13,5 +13,5 @@ indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# 2 space indentation
|
||||
[*.{yaml,.yml}]
|
||||
[*.{yaml,yml}]
|
||||
indent_size = 2
|
||||
|
||||
8
.github/FUNDING.yml
vendored
Normal file
8
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: grav
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
custom: # Replace with a single custom sponsorship URL
|
||||
66
.github/workflows/build.yaml
vendored
Normal file
66
.github/workflows/build.yaml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
name: Release Builds
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: "!github.event.release.prerelease"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 7.3
|
||||
extensions: opcache, gd
|
||||
coverage: none
|
||||
env:
|
||||
COMPOSER_TOKEN: ${{ secrets.GLOBAL_TOKEN }}
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get -y update -qq < /dev/null > /dev/null
|
||||
sudo apt-get -y install -qq git zip < /dev/null > /dev/null
|
||||
|
||||
- name: Retrieval of Builder Scripts
|
||||
run: |
|
||||
# Real Grav URL
|
||||
curl --silent -H "Authorization: token ${{ secrets.GLOBAL_TOKEN }}" -H "Accept: application/vnd.github.v3.raw" ${{ secrets.BUILD_SCRIPT_URL }} --output build-grav.sh
|
||||
|
||||
# Development Local URL
|
||||
# curl ${{ secrets.BUILD_SCRIPT_URL }} --output build-grav.sh
|
||||
|
||||
- name: Grav Builder
|
||||
run: |
|
||||
bash ./build-grav.sh
|
||||
|
||||
- name: Upload Grav Release Assets
|
||||
id: upload-release-asset
|
||||
uses: alexellis/upload-assets@0.2.3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GLOBAL_TOKEN }}
|
||||
with:
|
||||
asset_paths: '["./grav-dist/*.zip"]'
|
||||
|
||||
slack:
|
||||
name: Slack
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
- uses: technote-space/workflow-conclusion-action@v2
|
||||
- uses: 8398a7/action-slack@v3
|
||||
with:
|
||||
status: failure
|
||||
fields: repo,message,author,action
|
||||
icon_emoji: ':octocat:'
|
||||
author_name: 'Github Action Build'
|
||||
text: '🚚 Automated Build Failure'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GLOBAL_TOKEN }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
72
.github/workflows/tests.yaml
vendored
Normal file
72
.github/workflows/tests.yaml
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
name: PHP Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
pull_request:
|
||||
branches: [ develop ]
|
||||
|
||||
jobs:
|
||||
|
||||
unit-tests:
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php: [ 8.0, 7.4, 7.3]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: opcache, gd
|
||||
coverage: none
|
||||
env:
|
||||
COMPOSER_TOKEN: ${{ secrets.GLOBAL_TOKEN }}
|
||||
|
||||
- name: Update composer
|
||||
run: composer update
|
||||
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate
|
||||
|
||||
- name: Get composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist --no-progress
|
||||
|
||||
- name: Run test suite
|
||||
run: vendor/bin/codecept run
|
||||
|
||||
slack:
|
||||
name: Slack
|
||||
needs: unit-tests
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
- uses: technote-space/workflow-conclusion-action@v2
|
||||
- uses: 8398a7/action-slack@v3
|
||||
with:
|
||||
status: failure
|
||||
fields: repo,message,author,action
|
||||
icon_emoji: ':octocat:'
|
||||
author_name: 'Github Action Tests'
|
||||
text: '💥 Automated Test Failure'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GLOBAL_TOKEN }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -44,3 +44,4 @@ tests/_support/_generated/*
|
||||
tests/cache/*
|
||||
tests/error.log
|
||||
system/templates/testing/*
|
||||
/user/config/versions.yaml
|
||||
|
||||
@@ -27,6 +27,9 @@ RewriteEngine On
|
||||
# If you experience problems on your site block out the operations listed below
|
||||
# This attempts to block the most common type of exploit `attempts` to Grav
|
||||
#
|
||||
# Block out any script trying to use twig tags in URL.
|
||||
RewriteCond %{REQUEST_URI} ({{|}}|{%|%}) [OR]
|
||||
RewriteCond %{QUERY_STRING} ({{|}}|{%25|%25}) [OR]
|
||||
# Block out any script trying to base64_encode data within the URL.
|
||||
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
|
||||
# Block out any script that includes a <script> tag in URL.
|
||||
|
||||
44
.phan/config.php
Normal file
44
.phan/config.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
return [
|
||||
"target_php_version" => null,
|
||||
'pretend_newer_core_functions_exist' => true,
|
||||
'allow_missing_properties' => false,
|
||||
'null_casts_as_any_type' => false,
|
||||
'null_casts_as_array' => false,
|
||||
'array_casts_as_null' => false,
|
||||
'strict_method_checking' => true,
|
||||
'quick_mode' => false,
|
||||
'simplify_ast' => false,
|
||||
'directory_list' => [
|
||||
'.',
|
||||
],
|
||||
"exclude_analysis_directory_list" => [
|
||||
'vendor/'
|
||||
],
|
||||
'exclude_file_list' => [
|
||||
'system/src/Grav/Common/Errors/Resources/layout.html.php',
|
||||
'tests/_support/AcceptanceTester.php',
|
||||
'tests/_support/FunctionalTester.php',
|
||||
'tests/_support/UnitTester.php',
|
||||
],
|
||||
'autoload_internal_extension_signatures' => [
|
||||
'memcached' => '.phan/internal_stubs/memcached.phan_php',
|
||||
'memcache' => '.phan/internal_stubs/memcache.phan_php',
|
||||
'redis' => '.phan/internal_stubs/Redis.phan_php',
|
||||
],
|
||||
'plugins' => [
|
||||
'AlwaysReturnPlugin',
|
||||
'UnreachableCodePlugin',
|
||||
'DuplicateArrayKeyPlugin',
|
||||
'PregRegexCheckerPlugin',
|
||||
'PrintfCheckerPlugin',
|
||||
],
|
||||
'suppress_issue_types' => [
|
||||
'PhanUnreferencedUseNormal',
|
||||
'PhanTypeObjectUnsetDeclaredProperty',
|
||||
'PhanTraitParentReference',
|
||||
'PhanTypeInvalidThrowsIsInterface',
|
||||
'PhanRequiredTraitNotAdded',
|
||||
'PhanDeprecatedFunction', // Uncomment this to see all the deprecated calls
|
||||
]
|
||||
];
|
||||
5153
.phan/internal_stubs/Redis.phan_php
Normal file
5153
.phan/internal_stubs/Redis.phan_php
Normal file
File diff suppressed because it is too large
Load Diff
460
.phan/internal_stubs/memcache.phan_php
Normal file
460
.phan/internal_stubs/memcache.phan_php
Normal file
@@ -0,0 +1,460 @@
|
||||
<?php
|
||||
|
||||
// Start of memcache v.3.0.8
|
||||
|
||||
class MemcachePool {
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Open memcached server connection
|
||||
* @link https://php.net/manual/en/memcache.connect.php
|
||||
* @param string $host <p>
|
||||
* Point to the host where memcached is listening for connections. This parameter
|
||||
* may also specify other transports like <em>unix:///path/to/memcached.sock</em>
|
||||
* to use UNIX domain sockets, in this case <b>port</b> must also
|
||||
* be set to <em>0</em>.
|
||||
* </p>
|
||||
* @param int $port [optional] <p>
|
||||
* Point to the port where memcached is listening for connections. Set this
|
||||
* parameter to <em>0</em> when using UNIX domain sockets.
|
||||
* </p>
|
||||
* <p>
|
||||
* Please note: <b>port</b> defaults to
|
||||
* {@link https://php.net/manual/ru/memcache.ini.php#ini.memcache.default-port memcache.default_port}
|
||||
* if not specified. For this reason it is wise to specify the port
|
||||
* explicitly in this method call.
|
||||
* </p>
|
||||
* @param int $timeout [optional] <p>Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow.</p>
|
||||
* @return bool <p>Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.</p>
|
||||
*/
|
||||
public function connect ($host, $port, $timeout = 1) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.0.0)<br/>
|
||||
* Add a memcached server to connection pool
|
||||
* @link https://php.net/manual/en/memcache.addserver.php
|
||||
* @param string $host <p>
|
||||
* Point to the host where memcached is listening for connections. This parameter
|
||||
* may also specify other transports like unix:///path/to/memcached.sock
|
||||
* to use UNIX domain sockets, in this case <i>port</i> must also
|
||||
* be set to 0.
|
||||
* </p>
|
||||
* @param int $port [optional] <p>
|
||||
* Point to the port where memcached is listening for connections.
|
||||
* Set this
|
||||
* parameter to 0 when using UNIX domain sockets.
|
||||
* </p>
|
||||
* <p>
|
||||
* Please note: <i>port</i> defaults to
|
||||
* memcache.default_port
|
||||
* if not specified. For this reason it is wise to specify the port
|
||||
* explicitly in this method call.
|
||||
* </p>
|
||||
* @param bool $persistent [optional] <p>
|
||||
* Controls the use of a persistent connection. Default to <b>TRUE</b>.
|
||||
* </p>
|
||||
* @param int $weight [optional] <p>
|
||||
* Number of buckets to create for this server which in turn control its
|
||||
* probability of it being selected. The probability is relative to the
|
||||
* total weight of all servers.
|
||||
* </p>
|
||||
* @param int $timeout [optional] <p>
|
||||
* Value in seconds which will be used for connecting to the daemon. Think
|
||||
* twice before changing the default value of 1 second - you can lose all
|
||||
* the advantages of caching if your connection is too slow.
|
||||
* </p>
|
||||
* @param int $retry_interval [optional] <p>
|
||||
* Controls how often a failed server will be retried, the default value
|
||||
* is 15 seconds. Setting this parameter to -1 disables automatic retry.
|
||||
* Neither this nor the <i>persistent</i> parameter has any
|
||||
* effect when the extension is loaded dynamically via <b>dl</b>.
|
||||
* </p>
|
||||
* <p>
|
||||
* Each failed connection struct has its own timeout and before it has expired
|
||||
* the struct will be skipped when selecting backends to serve a request. Once
|
||||
* expired the connection will be successfully reconnected or marked as failed
|
||||
* for another <i>retry_interval</i> seconds. The typical
|
||||
* effect is that each web server child will retry the connection about every
|
||||
* <i>retry_interval</i> seconds when serving a page.
|
||||
* </p>
|
||||
* @param bool $status [optional] <p>
|
||||
* Controls if the server should be flagged as online. Setting this parameter
|
||||
* to <b>FALSE</b> and <i>retry_interval</i> to -1 allows a failed
|
||||
* server to be kept in the pool so as not to affect the key distribution
|
||||
* algorithm. Requests for this server will then failover or fail immediately
|
||||
* depending on the <i>memcache.allow_failover</i> setting.
|
||||
* Default to <b>TRUE</b>, meaning the server should be considered online.
|
||||
* </p>
|
||||
* @param callable $failure_callback [optional] <p>
|
||||
* Allows the user to specify a callback function to run upon encountering an
|
||||
* error. The callback is run before failover is attempted. The function takes
|
||||
* two parameters, the hostname and port of the failed server.
|
||||
* </p>
|
||||
* @param int $timeoutms [optional] <p>
|
||||
* </p>
|
||||
* @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function addServer ($host, $port = 11211, $persistent = true, $weight = null, $timeout = 1, $retry_interval = 15, $status = true, callable $failure_callback = null, $timeoutms = null) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.1.0)<br/>
|
||||
* Changes server parameters and status at runtime
|
||||
* @link https://secure.php.net/manual/en/memcache.setserverparams.php
|
||||
* @param string $host <p>Point to the host where memcached is listening for connections.</p.
|
||||
* @param int $port [optional] <p>
|
||||
* Point to the port where memcached is listening for connections.
|
||||
* </p>
|
||||
* @param int $timeout [optional] <p>
|
||||
* Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow.
|
||||
* </p>
|
||||
* @param int $retry_interval [optional] <p>
|
||||
* Controls how often a failed server will be retried, the default value
|
||||
* is 15 seconds. Setting this parameter to -1 disables automatic retry.
|
||||
* Neither this nor the <b>persistent</b> parameter has any
|
||||
* effect when the extension is loaded dynamically via {@link https://secure.php.net/manual/en/function.dl.php dl()}.
|
||||
* </p>
|
||||
* @param bool $status [optional] <p>
|
||||
* Controls if the server should be flagged as online. Setting this parameter
|
||||
* to <b>FALSE</b> and <b>retry_interval</b> to -1 allows a failed
|
||||
* server to be kept in the pool so as not to affect the key distribution
|
||||
* algorithm. Requests for this server will then failover or fail immediately
|
||||
* depending on the <b>memcache.allow_failover</b> setting.
|
||||
* Default to <b>TRUE</b>, meaning the server should be considered online.
|
||||
* </p>
|
||||
* @param callable $failure_callback [optional] <p>
|
||||
* Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted.
|
||||
* The function takes two parameters, the hostname and port of the failed server.
|
||||
* </p>
|
||||
* @return bool <p>Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.</p>
|
||||
*/
|
||||
public function setServerParams ($host, $port = 11211, $timeout = 1, $retry_interval = 15, $status = true, callable $failure_callback = null) {}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function setFailureCallback () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.1.0)<br/>
|
||||
* Returns server status
|
||||
* @link https://php.net/manual/en/memcache.getserverstatus.php
|
||||
* @param string $host Point to the host where memcached is listening for connections.
|
||||
* @param int $port Point to the port where memcached is listening for connections.
|
||||
* @return int Returns a the servers status. 0 if server is failed, non-zero otherwise
|
||||
*/
|
||||
public function getServerStatus ($host, $port = 11211) {}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function findServer () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Return version of the server
|
||||
* @link https://php.net/manual/en/memcache.getversion.php
|
||||
* @return string|false Returns a string of server version number or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function getVersion () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.0.0)<br/>
|
||||
* Add an item to the server. If the key already exists, the value will not be added and <b>FALSE</b> will be returned.
|
||||
* @link https://php.net/manual/en/memcache.add.php
|
||||
* @param string $key The key that will be associated with the item.
|
||||
* @param mixed $var The variable to store. Strings and integers are stored as is, other types are stored serialized.
|
||||
* @param int $flag [optional] <p>
|
||||
* Use <b>MEMCACHE_COMPRESSED</b> to store the item
|
||||
* compressed (uses zlib).
|
||||
* </p>
|
||||
* @param int $expire [optional] <p>Expiration time of the item.
|
||||
* If it's equal to zero, the item will never expire.
|
||||
* You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days).</p>
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure. Returns <b>FALSE</b> if such key already exist. For the rest Memcache::add() behaves similarly to Memcache::set().
|
||||
*/
|
||||
public function add ($key , $var, $flag = null, $expire = null) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Stores an item var with key on the memcached server. Parameter expire is expiration time in seconds.
|
||||
* If it's 0, the item never expires (but memcached server doesn't guarantee this item to be stored all the time,
|
||||
* it could be deleted from the cache to make place for other items).
|
||||
* You can use MEMCACHE_COMPRESSED constant as flag value if you want to use on-the-fly compression (uses zlib).
|
||||
* @link https://php.net/manual/en/memcache.set.php
|
||||
* @param string $key The key that will be associated with the item.
|
||||
* @param mixed $var The variable to store. Strings and integers are stored as is, other types are stored serialized.
|
||||
* @param int $flag [optional] Use MEMCACHE_COMPRESSED to store the item compressed (uses zlib).
|
||||
* @param int $expire [optional] Expiration time of the item. If it's equal to zero, the item will never expire. You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days).
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function set ($key, $var, $flag = null, $expire = null) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Replace value of the existing item
|
||||
* @link https://php.net/manual/en/memcache.replace.php
|
||||
* @param string $key <p>The key that will be associated with the item.</p>
|
||||
* @param mixed $var <p>The variable to store. Strings and integers are stored as is, other types are stored serialized.</p>
|
||||
* @param int $flag [optional] <p>Use <b>MEMCACHE_COMPRESSED</b> to store the item compressed (uses zlib).</p>
|
||||
* @param int $expire [optional] <p>Expiration time of the item. If it's equal to zero, the item will never expire. You can also use Unix timestamp or a number of seconds starting from current time, but in the latter case the number of seconds may not exceed 2592000 (30 days).</p>
|
||||
* @return bool Returns TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function replace ($key, $var, $flag = null, $expire = null) {}
|
||||
|
||||
public function cas () {}
|
||||
|
||||
public function append () {}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function prepend () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Retrieve item from the server
|
||||
* @link https://php.net/manual/en/memcache.get.php
|
||||
* @param string|array $key <p>
|
||||
* The key or array of keys to fetch.
|
||||
* </p>
|
||||
* @param int|array $flags [optional] <p>
|
||||
* If present, flags fetched along with the values will be written to this parameter. These
|
||||
* flags are the same as the ones given to for example {@link https://php.net/manual/en/memcache.set.php Memcache::set()}.
|
||||
* The lowest byte of the int is reserved for pecl/memcache internal usage (e.g. to indicate
|
||||
* compression and serialization status).
|
||||
* </p>
|
||||
* @return string|array|false <p>
|
||||
* Returns the string associated with the <b>key</b> or
|
||||
* an array of found key-value pairs when <b>key</b> is an {@link https://php.net/manual/en/language.types.array.php array}.
|
||||
* Returns <b>FALSE</b> on failure, <b>key</b> is not found or
|
||||
* <b>key</b> is an empty {@link https://php.net/manual/en/language.types.array.php array}.
|
||||
* </p>
|
||||
*/
|
||||
public function get ($key, &$flags = null) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Delete item from the server
|
||||
* https://secure.php.net/manual/ru/memcache.delete.php
|
||||
* @param $key string The key associated with the item to delete.
|
||||
* @param $timeout int [optional] This deprecated parameter is not supported, and defaults to 0 seconds. Do not use this parameter.
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function delete ($key, $timeout = 0 ) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Get statistics of the server
|
||||
* @link https://php.net/manual/ru/memcache.getstats.php
|
||||
* @param string $type [optional] <p>
|
||||
* The type of statistics to fetch.
|
||||
* Valid values are {reset, malloc, maps, cachedump, slabs, items, sizes}.
|
||||
* According to the memcached protocol spec these additional arguments "are subject to change for the convenience of memcache developers".</p>
|
||||
* @param int $slabid [optional] <p>
|
||||
* Used in conjunction with <b>type</b> set to
|
||||
* cachedump to identify the slab to dump from. The cachedump
|
||||
* command ties up the server and is strictly to be used for
|
||||
* debugging purposes.
|
||||
* </p>
|
||||
* @param int $limit [optional] <p>
|
||||
* Used in conjunction with <b>type</b> set to cachedump to limit the number of entries to dump.
|
||||
* </p>
|
||||
* @return array|false Returns an associative array of server statistics or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function getStats ($type = null, $slabid = null, $limit = 100) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.0.0)<br/>
|
||||
* Get statistics from all servers in pool
|
||||
* @link https://php.net/manual/en/memcache.getextendedstats.php
|
||||
* @param string $type [optional] <p>The type of statistics to fetch. Valid values are {reset, malloc, maps, cachedump, slabs, items, sizes}. According to the memcached protocol spec these additional arguments "are subject to change for the convenience of memcache developers".</p>
|
||||
* @param int $slabid [optional] <p>
|
||||
* Used in conjunction with <b>type</b> set to
|
||||
* cachedump to identify the slab to dump from. The cachedump
|
||||
* command ties up the server and is strictly to be used for
|
||||
* debugging purposes.
|
||||
* </p>
|
||||
* @param int $limit Used in conjunction with type set to cachedump to limit the number of entries to dump.
|
||||
* @return array|false Returns a two-dimensional associative array of server statistics or <b>FALSE</b>
|
||||
* Returns a two-dimensional associative array of server statistics or <b>FALSE</b>
|
||||
* on failure.
|
||||
*/
|
||||
public function getExtendedStats ($type = null, $slabid = null, $limit = 100) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 2.0.0)<br/>
|
||||
* Enable automatic compression of large values
|
||||
* @link https://php.net/manual/en/memcache.setcompressthreshold.php
|
||||
* @param int $thresold <p>Controls the minimum value length before attempting to compress automatically.</p>
|
||||
* @param float $min_saving [optional] <p>Specifies the minimum amount of savings to actually store the value compressed. The supplied value must be between 0 and 1. Default value is 0.2 giving a minimum 20% compression savings.</p>
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function setCompressThreshold ($thresold, $min_saving = 0.2) {}
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Increment item's value
|
||||
* @link https://php.net/manual/en/memcache.increment.php
|
||||
* @param $key string Key of the item to increment.
|
||||
* @param $value int [optional] increment the item by <b>value</b>
|
||||
* @return int|false Returns new items value on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function increment ($key, $value = 1) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Decrement item's value
|
||||
* @link https://php.net/manual/en/memcache.decrement.php
|
||||
* @param $key string Key of the item do decrement.
|
||||
* @param $value int Decrement the item by <b>value</b>.
|
||||
* @return int|false Returns item's new value on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function decrement ($key, $value = 1) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.4.0)<br/>
|
||||
* Close memcached server connection
|
||||
* @link https://php.net/manual/en/memcache.close.php
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function close () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 1.0.0)<br/>
|
||||
* Flush all existing items at the server
|
||||
* @link https://php.net/manual/en/memcache.flush.php
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function flush () {}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a connection to a set of memcache servers.
|
||||
* @link https://php.net/manual/en/class.memcache.php
|
||||
*/
|
||||
class Memcache extends MemcachePool {
|
||||
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.4.0)<br/>
|
||||
* Open memcached server persistent connection
|
||||
* @link https://php.net/manual/en/memcache.pconnect.php
|
||||
* @param string $host <p>
|
||||
* Point to the host where memcached is listening for connections. This parameter
|
||||
* may also specify other transports like unix:///path/to/memcached.sock
|
||||
* to use UNIX domain sockets, in this case <i>port</i> must also
|
||||
* be set to 0.
|
||||
* </p>
|
||||
* @param int $port [optional] <p>
|
||||
* Point to the port where memcached is listening for connections. Set this
|
||||
* parameter to 0 when using UNIX domain sockets.
|
||||
* </p>
|
||||
* @param int $timeout [optional] <p>
|
||||
* Value in seconds which will be used for connecting to the daemon. Think
|
||||
* twice before changing the default value of 1 second - you can lose all
|
||||
* the advantages of caching if your connection is too slow.
|
||||
* </p>
|
||||
* @return mixed a Memcache object or <b>FALSE</b> on failure.
|
||||
*/
|
||||
public function pconnect ($host, $port, $timeout = 1) {}
|
||||
}
|
||||
|
||||
// string $host [, int $port [, int $timeout ]]
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)</br>
|
||||
* Memcache::connect — Open memcached server connection
|
||||
* @link https://php.net/manual/en/memcache.connect.php
|
||||
* @param string $host <p>
|
||||
* Point to the host where memcached is listening for connections.
|
||||
* This parameter may also specify other transports like
|
||||
* unix:///path/to/memcached.sock to use UNIX domain sockets,
|
||||
* in this case port must also be set to 0.
|
||||
* </p>
|
||||
* @param int $port [optional] <p>
|
||||
* Point to the port where memcached is listening for connections.
|
||||
* Set this parameter to 0 when using UNIX domain sockets.
|
||||
* Note: port defaults to memcache.default_port if not specified.
|
||||
* For this reason it is wise to specify the port explicitly in this method call.
|
||||
* </p>
|
||||
* @param int $timeout [optional] <p>
|
||||
* Value in seconds which will be used for connecting to the daemon.
|
||||
* </p>
|
||||
* @return bool Returns <b>TRUE</b> on success or <b>FALSE</b> on failure.
|
||||
*/
|
||||
function memcache_connect ($host, $port, $timeout = 1) {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.4.0)
|
||||
* Memcache::pconnect — Open memcached server persistent connection
|
||||
*
|
||||
* @link https://php.net/manual/en/memcache.pconnect.php#example-5242
|
||||
* @param $host
|
||||
* @param null $port
|
||||
* @param int $timeout
|
||||
* @return Memcache
|
||||
*/
|
||||
function memcache_pconnect ($host, $port=null, $timeout=1) {}
|
||||
|
||||
function memcache_add_server () {}
|
||||
|
||||
function memcache_set_server_params () {}
|
||||
|
||||
function memcache_set_failure_callback () {}
|
||||
|
||||
function memcache_get_server_status () {}
|
||||
|
||||
function memcache_get_version () {}
|
||||
|
||||
function memcache_add () {}
|
||||
|
||||
function memcache_set () {}
|
||||
|
||||
function memcache_replace () {}
|
||||
|
||||
function memcache_cas () {}
|
||||
|
||||
function memcache_append () {}
|
||||
|
||||
function memcache_prepend () {}
|
||||
|
||||
function memcache_get () {}
|
||||
|
||||
function memcache_delete () {}
|
||||
|
||||
/**
|
||||
* (PECL memcache >= 0.2.0)<br/>
|
||||
* Turn debug output on/off
|
||||
* @link https://php.net/manual/en/function.memcache-debug.php
|
||||
* @param bool $on_off <p>
|
||||
* Turns debug output on if equals to <b>TRUE</b>.
|
||||
* Turns debug output off if equals to <b>FALSE</b>.
|
||||
* </p>
|
||||
* @return bool <b>TRUE</b> if PHP was built with --enable-debug option, otherwise
|
||||
* returns <b>FALSE</b>.
|
||||
*/
|
||||
function memcache_debug ($on_off) {}
|
||||
|
||||
function memcache_get_stats () {}
|
||||
|
||||
function memcache_get_extended_stats () {}
|
||||
|
||||
function memcache_set_compress_threshold () {}
|
||||
|
||||
function memcache_increment () {}
|
||||
|
||||
function memcache_decrement () {}
|
||||
|
||||
function memcache_close () {}
|
||||
|
||||
function memcache_flush () {}
|
||||
|
||||
define ('MEMCACHE_COMPRESSED', 2);
|
||||
define ('MEMCACHE_USER1', 65536);
|
||||
define ('MEMCACHE_USER2', 131072);
|
||||
define ('MEMCACHE_USER3', 262144);
|
||||
define ('MEMCACHE_USER4', 524288);
|
||||
define ('MEMCACHE_HAVE_SESSION', 1);
|
||||
|
||||
// End of memcache v.3.0.8
|
||||
?>
|
||||
1308
.phan/internal_stubs/memcached.phan_php
Normal file
1308
.phan/internal_stubs/memcached.phan_php
Normal file
File diff suppressed because it is too large
Load Diff
14
.travis.yml
14
.travis.yml
@@ -3,10 +3,9 @@ php:
|
||||
- '7.1'
|
||||
- '7.2'
|
||||
- '7.3'
|
||||
- '7.4'
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- master
|
||||
- build_test
|
||||
notifications:
|
||||
email:
|
||||
@@ -45,10 +44,11 @@ before_install:
|
||||
- if [ $TRAVIS_BRANCH == 'develop' ] || [ $TRAVIS_PULL_REQUEST != 'false' ]; then
|
||||
composer install --dev --prefer-dist;
|
||||
fi
|
||||
- if [ $TRAVIS_BRANCH != 'develop' ] && [ $TRAVIS_PHP_VERSION == "7.1" ] && [ $TRAVIS_PULL_REQUEST == "false" ]; then
|
||||
export TRAVIS_TAG=$(curl --fail --user "${GH_API_USER}" -s https://api.github.com/repos/getgrav/grav/releases/latest | grep tag_name | head -n 1 | cut -d '"' -f 4);
|
||||
eval "$(curl -sL https://raw.githubusercontent.com/travis-ci/gimme/master/gimme | GIMME_GO_VERSION=1.8 bash)";
|
||||
go get github.com/aktau/github-release;
|
||||
- |
|
||||
if [ $TRAVIS_BRANCH != 'develop' ] && [ $TRAVIS_PHP_VERSION == "7.1" ] && [ $TRAVIS_PULL_REQUEST == "false" ]; then
|
||||
export TRAVIS_TAG=$(curl -H "Authorization: token ${GH_TOKEN}" --fail -s https://api.github.com/repos/getgrav/grav/releases/latest | grep tag_name | head -n 1 | cut -d '"' -f 4);
|
||||
eval "$(curl -sL https://raw.githubusercontent.com/travis-ci/gimme/master/gimme | GIMME_GO_VERSION=1.13 bash)";
|
||||
go get github.com/github-release/github-release;
|
||||
git clone --quiet --depth=50 --branch=master https://${BB_TOKEN}bitbucket.org/rockettheme/grav-devtools.git $RT_DEVTOOLS &>/dev/null;
|
||||
if [ ! -z "$TRAVIS_TAG" ]; then
|
||||
cd ${RT_DEVTOOLS};
|
||||
@@ -56,7 +56,7 @@ before_install:
|
||||
fi;
|
||||
fi
|
||||
before_script:
|
||||
- if [ $TRAVIS_PHP_VERSION != 'hhvm' ]; then phpenv config-rm xdebug.ini; fi
|
||||
- phpenv config-rm xdebug.ini
|
||||
script:
|
||||
- if [ $TRAVIS_BRANCH == 'develop' ] || [ $TRAVIS_PULL_REQUEST != 'false' ]; then
|
||||
vendor/bin/codecept run;
|
||||
|
||||
939
CHANGELOG.md
939
CHANGELOG.md
@@ -1,3 +1,924 @@
|
||||
# v1.7.3
|
||||
## 01/21/2021
|
||||
|
||||
1. [](#improved)
|
||||
* IMPORTANT - Please [checkout the process](https://getgrav.org/blog/grav-170-cli-self-upgrade-bug) to `self-upgrade` from CLI if you are on **Grav 1.7.0 or 1.7.1**
|
||||
* Added support for symlinking individual plugins and themes by using `bin/grav install -p myplugin` or `-t mytheme`
|
||||
* Added support for symlinking plugins and themes with `hebe.json` file to support custom folder structures
|
||||
* Added support for running post-install scripts in `bin/gpm selfupgrade` if Grav was updated manually
|
||||
1. [](#bugfix)
|
||||
* Fixed default GPM Channel back to 'stable' - this was inadvertently left as 'testing' [#3163](https://github.com/getgrav/grav/issues/3163)
|
||||
* Fixed broken stream initialization if `environment://` paths aren't streams
|
||||
* Fixed Clockwork debugger in sub-folder multi-site setups
|
||||
* Fixed `Unsupported option "curl" passed to "Symfony\Component\HttpClient\CurlHttpClient"` in `bin/gpm selfupgrade` [#3165](https://github.com/getgrav/grav/issues/3165)
|
||||
|
||||
# v1.7.2
|
||||
## 01/21/2021
|
||||
|
||||
1. [](#improved)
|
||||
* This release was pulled due to a bug in the installer, 1.7.3 replaces it.
|
||||
|
||||
# v1.7.1
|
||||
## 01/20/2021
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed fatal error when `site.taxonomies` contains a bad value
|
||||
* Sanitize valid Page extensions from `Page::template_format()`
|
||||
* Fixed `bin/gpm index` erroring out [#3158](https://github.com/getgrav/grav/issues/3158)
|
||||
* Fixed `bin/gpm selfupgrade` failing to report failed Grav update [#3116](https://github.com/getgrav/grav/issues/3116)
|
||||
* Fixed `bin/gpm selfupgrade` error on `Call to undefined method` [#3160](https://github.com/getgrav/grav/issues/3160)
|
||||
* Flex Pages: Fixed fatal error when trying to move a page to Root (/) [#3161](https://github.com/getgrav/grav/issues/3161)
|
||||
* Fixed twig parsing errors in pages where twig is parsed after markdown [#3162](https://github.com/getgrav/grav/issues/3162)
|
||||
* Fixed `lighttpd.conf` access-deny rule [#1876](https://github.com/getgrav/grav/issues/1876)
|
||||
* Fixed page metadata being double-escaped [#3121](https://github.com/getgrav/grav/issues/3121)
|
||||
|
||||
# v1.7.0
|
||||
## 01/19/2021
|
||||
|
||||
1. [](#new)
|
||||
* Requires **PHP 7.3.6**
|
||||
* Read about this release in the [Grav 1.7 Released](https://getgrav.org/blog/grav-1.7-released) blog post
|
||||
* Read the full list of all changes in the [Changelog on GitHub](https://github.com/getgrav/grav/blob/1.7.0/CHANGELOG.md)
|
||||
* Please read [Grav 1.7 Upgrade Guide](https://learn.getgrav.org/17/advanced/grav-development/grav-17-upgrade-guide) before upgrading!
|
||||
* Added support for overriding configuration by using environment variables
|
||||
* Use PHP 7.4 serialization (the old `Serializable` methods are now final and cannot be overridden)
|
||||
* Enabled `ETag` setting by default for 304 responses
|
||||
* Added `FlexCollection::getDistinctValues()` to get all the assigned values from the field
|
||||
* `Flex Pages` method `$page->header()` returns `\Grav\Common\Page\Header` object, old `Page` class still returns `stdClass`
|
||||
1. [](#improved)
|
||||
* Make it possible to use an absolute path when loading a blueprint
|
||||
* Make serialize methods final in `ContentBlock`, `AbstractFile`, `FormTrait`, `ObjectCollectionTrait` and `ObjectTrait`
|
||||
* Added support for relative paths in `PageObject::getLevelListing()` [#3110](https://github.com/getgrav/grav/issues/3110)
|
||||
* Better `--env` and `--lang` support for `bin/grav`, `bin/gpm` and `bin/plugin` console commands
|
||||
* **BC BREAK** Shorthand for `--env`: `-e` will not work anymore as it conflicts with some plugins
|
||||
* Added support for locking the `start` and `limit` in a Page Collection
|
||||
1. [](#bugfix)
|
||||
* Fixed port issue with `system.custom_base_url`
|
||||
* Hide errors with `exif_read_data` in `ImageFile`
|
||||
* Fixed unserialize in `MarkdownFormatter` and `Framework\File` classes
|
||||
* Fixed pages with session messages should never be cached [#3108](https://github.com/getgrav/grav/issues/3108)
|
||||
* Fixed `Filesystem::normalize()` with dot-dot paths
|
||||
* Fixed Flex sorting issues [grav-plugin-flex-objects#92](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/92)
|
||||
* Fixed Clockwork missing dumped arrays and objects
|
||||
* Fixed fatal error in PHP 8 when trying to access root page
|
||||
* Fixed Array->String conversion error when `languages:translations: false` [admin#1896](https://github.com/getgrav/grav-plugin-admin/issues/1896)
|
||||
* Fixed `Inflector` methods when translation is missing `GRAV.INFLECTOR_*` translations
|
||||
* Fixed exception when changing parent of new page [grav-plugin-admin#2018](https://github.com/getgrav/grav-plugin-admin/issues/2018)
|
||||
* Fixed ordering issue with moving pages [grav-plugin-admin#2015](https://github.com/getgrav/grav-plugin-admin/issues/2015)
|
||||
* Fixed Flex Pages cache not invalidating if saving an old `Page` object [#3152](https://github.com/getgrav/grav/issues/3152)
|
||||
* Fixed multiple issues with `system.language.translations: false`
|
||||
* Fixed page collections containing dummy items for untranslated default language [#2985](https://github.com/getgrav/grav/issues/2985)
|
||||
* Fixed streams in `setup.php` being overridden by `system/streams.yaml` [#2450](https://github.com/getgrav/grav/issues/2450)
|
||||
* Fixed `ERR_TOO_MANY_REDIRECTS` with HTTPS = 'On' [#3155](https://github.com/getgrav/grav/issues/3155)
|
||||
* Fixed page collection pagination not behaving as it did in Grav 1.6
|
||||
|
||||
# v1.7.0-rc.20
|
||||
## 12/15/2020
|
||||
|
||||
1. [](#new)
|
||||
* Update phpstan to version 0.12
|
||||
* Auto-Escape enabled by default. Manually enable **Twig Compatibility** and disable **Auto-Escape** to use the old setting.
|
||||
* Updated unit tests to use codeception 4.1
|
||||
* Added support for setting `GRAV_ENVIRONMENT` by using environment variable or a constant
|
||||
* Added support for setting `GRAV_SETUP_PATH` by using environment variable (constant already worked)
|
||||
* Added support for setting `GRAV_ENVIRONMENTS_PATH` by using environment variable or a constant
|
||||
* Added support for setting `GRAV_ENVIRONMENT_PATH` by using environment variable or a constant
|
||||
1. [](#improved)
|
||||
* Improved `bin/grav install` command
|
||||
1. [](#bugfix)
|
||||
* Fixed potential error when upgrading Grav
|
||||
* Fixed broken list in `bin/gpm index` [#3092](https://github.com/getgrav/grav/issues/3092)
|
||||
* Fixed CLI/GPM command failures returning 0 (success) value [#3017](https://github.com/getgrav/grav/issues/3017)
|
||||
* Fixed unimplemented `PageObject::getOriginal()` call [#3098](https://github.com/getgrav/grav/issues/3098)
|
||||
* Fixed `Argument 1 passed to Grav\Common\User\DataUser\User::filterUsername() must be of the type string` [#3101](https://github.com/getgrav/grav/issues/3101)
|
||||
* Fixed broken check if php exif module is enabled in `ImageFile::fixOrientation()`
|
||||
* Fixed `StaticResizeTrait::resize()` bad image height/width attributes if `null` values are passed to the method
|
||||
* Fixed twig script/style tag `{% script 'file.js' at 'bottom' %}`, replaces broken `in` operator [#3084](https://github.com/getgrav/grav/issues/3084)
|
||||
* Fixed dropped query params when `?` is preceded with `/` [#2964](https://github.com/getgrav/grav/issues/2964)
|
||||
|
||||
# v1.7.0-rc.19
|
||||
## 12/02/2020
|
||||
|
||||
1. [](#bugfix)
|
||||
* Updated composer libraries with latest Toolbox v1.5.6 that contains critical fixes
|
||||
|
||||
# v1.7.0-rc.18
|
||||
## 12/02/2020
|
||||
|
||||
1. [](#new)
|
||||
* Set minimum requirements to **PHP 7.3.6**
|
||||
* Updated Clockwork to v5.0
|
||||
* Added `FlexDirectoryInterface` interface
|
||||
* Renamed `PageCollectionInterface::nonModular()` into `PageCollectionInterface::pages()` and deprecated the old method
|
||||
* Renamed `PageCollectionInterface::modular()` into `PageCollectionInterface::modules()` and deprecated the old method'
|
||||
* Upgraded `bin/composer.phar` to `2.0.2` which is all new and much faster
|
||||
* Added search option `same_as` to Flex Objects
|
||||
* Added PHP 8 compatible `function_exists()`: `Utils::functionExists()`
|
||||
* New sites have `compatibility` features turned off by default, upgrading from older versions will keep the settings on
|
||||
1. [](#improved)
|
||||
* Updated bundled JQuery to latest version `3.5.1`
|
||||
* Forward a `sid` to GPM when downloading a premium package via CLI
|
||||
* Allow `JsonFormatter` options to be passed as a string
|
||||
* Hide Flex Pages frontend configuration (not ready for production use)
|
||||
* Improve Flex configuration: gather views together in blueprint
|
||||
* Added XSS detection to all forms. See [documentation](https://learn.getgrav.org/17/forms/forms/form-options#xss-checks)
|
||||
* Better handling of missing repository index [grav-plugin-admin#1916](https://github.com/getgrav/grav-plugin-admin/issues/1916)
|
||||
* Added support for having all sites / environments under `user/env` folder [#3072](https://github.com/getgrav/grav/issues/3072)
|
||||
* Added `FlexObject::refresh()` method to make sure object is up to date
|
||||
1. [](#bugfix)
|
||||
* *Menu Visibility Requires Access* Security option setting wrong frontmatter [login#265](https://github.com/getgrav/grav-plugin-login/issues/265)
|
||||
* Accessing page with unsupported file extension (jpg, pdf, xsl) will use wrong mime type [#3031](https://github.com/getgrav/grav/issues/3031)
|
||||
* Fixed media crashing on a bad image
|
||||
* Fixed bug in collections where filter `type: false` did not work
|
||||
* Fixed `print_r()` in twig
|
||||
* Fixed sorting by groups in `Flex Users`
|
||||
* Changing `Flex Page` template causes the other language versions of that page to lose their content [admin#1958](https://github.com/getgrav/grav-plugin-admin/issues/1958)
|
||||
* Fixed plugins getting initialized multiple times (by CLI commands for example)
|
||||
* Fixed `header.admin.children_display_order` in Flex Pages to work just like with regular pages
|
||||
* Fixed `Utils::isFunctionDisabled()` method if there are spaces in `disable_functions` [#3023](https://github.com/getgrav/grav/issues/3023)
|
||||
* Fixed potential fatal error when creating flex index using cache [#3062](https://github.com/getgrav/grav/issues/3062)
|
||||
* Fixed fatal error in `CompiledFile` if the cached version is broken
|
||||
* Fixed updated media missing from media when editing Flex Object after page reload
|
||||
* Fixed issue with `config-default@` breaking on set [#1972](https://github.com/getgrav/grav-plugin-admin/issues/1971)
|
||||
* Escape titles in Flex pages list [flex-objects#84](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/84)
|
||||
* Fixed Purge successful message only working in Scheduler but broken in CLI and Admin [#1935](https://github.com/getgrav/grav-plugin-admin/issues/1935)
|
||||
* Fixed `system://` stream is causing issues in Admin, making Media tab to disappear and possibly causing other issues [#3072](https://github.com/getgrav/grav/issues/3072)
|
||||
* Fixed CLI self-upgrade from Grav 1.6 [#3079](https://github.com/getgrav/grav/issues/3079)
|
||||
* Fixed `bin/grav yamllinter -a` and `-f` not following symlinks [#3080](https://github.com/getgrav/grav/issues/3080)
|
||||
* Fixed `|safe_email` filter to return safe and escaped UTF-8 HTML [#3072](https://github.com/getgrav/grav/issues/3072)
|
||||
* Fixed exception in CLI GPM and backup commands when `php-zip` is not enabled [#3075](https://github.com/getgrav/grav/issues/3075)
|
||||
* Fix for XSS advisory [GHSA-cvmr-6428-87w9](https://github.com/getgrav/grav/security/advisories/GHSA-cvmr-6428-87w9)
|
||||
* Fixed Flex and Page ordering to be natural and case insensitive [flex-objects#87](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/87)
|
||||
* Fixed plugin/theme priority ordering to be numeric
|
||||
|
||||
# v1.7.0-rc.17
|
||||
## 10/07/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added a `Uri::getAllHeaders()` compatibility function
|
||||
1. [](#improved)
|
||||
* Fall back through various templates scenarios if they don't exist in theme to avoid unhelpful error.
|
||||
* Added default templates for `external.html.twig`, `default.html.twig`, and `modular.html.twig`
|
||||
* Improve Media classes
|
||||
* _POTENTIAL BREAKING CHANGE:_ Added reload argument to `FlexStorageInterface::getMetaData()`
|
||||
1. [](#bugfix)
|
||||
* Fixed `Security::sanitizeSVG()` creating an empty file if SVG file cannot be parsed
|
||||
* Fixed infinite loop in blueprints with `extend@` to a parent stream
|
||||
* Added missing `Stream::create()` method
|
||||
* Added missing `onBlueprintCreated` event for Flex Pages
|
||||
* Fixed `onBlueprintCreated` firing multiple times recursively
|
||||
* Fixed media upload failing with custom folders
|
||||
* Fixed `unset()` in `ObjectProperty` class
|
||||
* Fixed `FlexObject::freeMedia()` method causing media to become null
|
||||
* Fixed bug in `Flex Form` making it impossible to set nested values
|
||||
* Fixed `Flex User` avatar when using folder storage, also allow multiple images
|
||||
* Fixed Referer reference during GPM calls.
|
||||
* Fixed fatal error with toggled lists
|
||||
|
||||
# v1.7.0-rc.16
|
||||
## 09/01/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `svg_image()` twig function to make it easier to 'include' SVG source in Twig
|
||||
* Added a helper `Utils::fullPath()` to get the full path to a file be it stream, relative, etc.
|
||||
1. [](#improved)
|
||||
* Added `themes` to cached blueprints and configuration
|
||||
1. [](#bugfix)
|
||||
* Fixed `Flex Pages` issue with `getRoute()` returning path with language prefix for default language if set not to do that
|
||||
* Fixed `Flex Pages` bug where reordering pages causes page content to disappear if default language uses wrong extension (`.md` vs `.en.md`)
|
||||
* Fixed `Flex Pages` bug where `onAdminSave` passes page as `$event['page']` instead of `$event['object']` [#2995](https://github.com/getgrav/grav/issues/2995)
|
||||
* Fixed `Flex Pages` bug where changing a modular page template added duplicate file [admin#1899](https://github.com/getgrav/grav-plugin-admin/issues/1899)
|
||||
* Fixed `Flex Pages` bug where renaming slug causes bad ordering range after save [#2997](https://github.com/getgrav/grav/issues/2997)
|
||||
|
||||
# v1.7.0-rc.15
|
||||
## 07/22/2020
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed Flex index file caching [#2962](https://github.com/getgrav/grav/issues/2962)
|
||||
* Fixed various issues with Exif data reading and images being incorrectly rotated [#1923](https://github.com/getgrav/grav-plugin-admin/issues/1923)
|
||||
|
||||
# v1.7.0-rc.14
|
||||
## 07/09/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Added ability to `noprocess` specific items only in Link/Image Excerpts, e.g. `http://foo.com/page?id=foo&target=_blank&noprocess=id` [#2954](https://github.com/getgrav/grav/pull/2954)
|
||||
1. [](#bugfix)
|
||||
* Regression: Default language fix broke `Language::getLanguageURLPrefix()` and `Language::isIncludeDefaultLanguage()` methods when not using multi-language
|
||||
* Reverted `Language::getDefault()` and `Language::getLanguage()` to return false again because of plugin compatibility (updated docblocks)
|
||||
* Fixed UTF-8 issue in `Excerpts::getExcerptsFromHtml`
|
||||
* Fixed some compatibility issues with recent Changes to `Assets` handling
|
||||
* Fixed issue with `CSS_IMPORTS_REGEX` breaking with complex URLs [#2958](https://github.com/getgrav/grav/issues/2958)
|
||||
* Moved duplicated `CSS_IMPORT_REGEX` to local variable in `AssetUtilsTrait::moveImports()`
|
||||
* Fixed page media only accepting images [#2943](https://github.com/getgrav/grav/issues/2943)
|
||||
|
||||
# v1.7.0-rc.13
|
||||
## 07/01/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added support for uploading and deleting images directly in `Media`
|
||||
* Added new `onAfterCacheClear` event
|
||||
1. [](#improved)
|
||||
* Improved `CvsFormatter` to attempt to encode non-scalar variables into JSON before giving up
|
||||
* Moved image loading into its own trait to be used by images+static images
|
||||
* Adjusted asset types to enable extension of assets in class [#2937](https://github.com/getgrav/grav/pull/2937)
|
||||
* Composer update for vendor library updates
|
||||
* Updated bundled `composer.phar` to `2.0.0-dev`
|
||||
1. [](#bugfix)
|
||||
* Fixed `MediaUploadTrait::copyUploadedFile()` not adding uploaded media to the collection
|
||||
* Fixed regression in saving media to a new Flex Object [admin#1867](https://github.com/getgrav/grav-plugin-admin/issues/1867)
|
||||
* Fixed `Trying to get property 'username' of non-object` error in Flex [flex-objects#62](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/62)
|
||||
* Fixed retina images not working in Flex [flex-objects#64](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/64)
|
||||
* Fixed plugin initialization in CLI
|
||||
* Fixed broken logic in `Page::topParent()` when dealing with first-level pages
|
||||
* Fixed broken `Flex Page` authorization for groups
|
||||
* Fixed missing `onAdminSave` and `onAdminAfterSave` events when using `Flex Pages` and `Flex Users` [flex-objects#58](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/58)
|
||||
* Fixed new `User Group` allowing bad group name to be saved [admin#1917](https://github.com/getgrav/grav-plugin-admin/issues/1917)
|
||||
* Fixed `Language::getDefault()` returning false and not 'en'
|
||||
* Fixed non-text links in `Excerpts::getExcerptFromHtml`
|
||||
* Fixed CLI commands not properly intializing Plugins so events can fire
|
||||
|
||||
# v1.7.0-rc.12
|
||||
## 06/08/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Changed `Folder::hasChildren` to `Folder::countChildren`
|
||||
* Added `Content Editor` option to user account blueprint
|
||||
1. [](#bugfix)
|
||||
* Fixed new `Flex Page` not having correct form fields for the page type
|
||||
* Fixed new `Flex User` erroring out on save (thanks @mikebi42)
|
||||
* Fixed `Flex Object` request cache clear when saving object
|
||||
* Fixed blueprint value filtering in lists [#2923](https://github.com/getgrav/grav/issues/2923)
|
||||
* Fixed blueprint for `system.pages.hide_empty_folders` [#1925](https://github.com/getgrav/grav/issues/2925)
|
||||
* Fixed file field in `Flex Objects` (use `Grav\Common\Flex\Types\GenericObject` instead of `FlexObject`) [flex-objects#37](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/37)
|
||||
* Fixed saving nested file fields in `Flex Objects` [flex-objects#34](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/34)
|
||||
* JSON Route of homepage with no ‘route’ set is valid [form#425](https://github.com/getgrav/grav-plugin-form/issues/425)
|
||||
|
||||
# v1.7.0-rc.11
|
||||
## 05/14/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added support for native `loading=lazy` attributes on images. Can be set in `system.images.defaults` or per md image with `?loading=lazy` [#2910](https://github.com/getgrav/grav/issues/2910)
|
||||
1. [](#improved)
|
||||
* Added `PageCollection::all()` to mimic Pages class
|
||||
* Added system configuration support for `HTTP_X_Forwarded` headers (host disabled by default)
|
||||
* Updated `PHPUserAgentParser` to 1.0.0
|
||||
* Improved docblocks
|
||||
* Fixed some phpstan issues
|
||||
* Tighten vendor requirements
|
||||
1. [](#bugfix)
|
||||
* Fix for uppercase image extensions
|
||||
* Fix for `&` errors in HTML when passed to `Excerpts.php`
|
||||
|
||||
# v1.7.0-rc.10
|
||||
## 04/30/2020
|
||||
|
||||
1. [](#new)
|
||||
* Changed `Response::get()` used by **GPM/Admin** to use [Symfony HttpClient v4.4](https://symfony.com/doc/current/components/http_client.html) (`composer install --nodev` required for Git installations)
|
||||
* Added new `Excerpts::processLinkHtml()` method
|
||||
1. [](#bugfix)
|
||||
* Fixed `Flex Pages` admin with PHP `intl` extension enabled when using custom page order
|
||||
* Fixed saving non-numeric-prefix `Flex Page` changing to numeric-prefix [flex-objects#56](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/56)
|
||||
* Copying `Flex Page` in admin does nothing [flex-objects#55](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/55)
|
||||
* Force GPM progress to be between 0-100%
|
||||
|
||||
# v1.7.0-rc.9
|
||||
## 04/27/2020
|
||||
|
||||
1. [](#new)
|
||||
* Support for `webp` image format in Page Media [#1168](https://github.com/getgrav/grav/issues/1168)
|
||||
* Added `Route::getBase()` method
|
||||
1. [](#improved)
|
||||
* Support symlinks when saving `File`
|
||||
1. [](#bugfix)
|
||||
* Fixed flex objects with integer keys not working [#2863](https://github.com/getgrav/grav/issues/2863)
|
||||
* Fixed `Pages::instances()` returning null values when using `Flex Pages` [#2889](https://github.com/getgrav/grav/issues/2889)
|
||||
* Fixed Flex Page parent `header.admin.children_display_order` setting being ignored in Admin [#2881](https://github.com/getgrav/grav/issues/2881)
|
||||
* Implemented missing Flex `$pageCollection->batch()` and `$pageCollection->order()` methods
|
||||
* Fixed user avatar creation for new `Flex Users` when using folder storage
|
||||
* Fixed `Trying to access array offset on value of type null` PHP 7.4 error in `Plugin.php`
|
||||
* Fixed Gregwar Image library using `.jpeg` for cached images, rather use `.jpg`
|
||||
* Fixed `Flex Pages` with `00.home` page not having ordering set
|
||||
* Fixed `Flex Pages` not updating empty content on save [#2890](https://github.com/getgrav/grav/issues/2890)
|
||||
* Fixed creating new Flex User with file storage
|
||||
* Fixed saving new `Flex Object` with custom key
|
||||
* Fixed broken `Plugin::config()` method
|
||||
|
||||
# v1.7.0-rc.8
|
||||
## 03/19/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added `MediaTrait::freeMedia()` method to free media (and memory)
|
||||
* Added `Folder::hasChildren()` method to determine if a folder has child folders
|
||||
1. [](#improved)
|
||||
* Save memory when updating large flex indexes
|
||||
* Better `Content-Encoding` handling in Apache when content compression is disabled [#2619](https://github.com/getgrav/grav/issues/2619)
|
||||
1. [](#bugfix)
|
||||
* Fixed creating new `Flex User` when folder storage has been selected
|
||||
* Fixed some bugs in Flex root page methods
|
||||
* Fixed bad default redirect code in `ControllerResponseTrait::createRedirectResponse()`
|
||||
* Fixed issue with PHP `HTTP_X_HTTP_METHOD_OVERRIDE` [#2847](https://github.com/getgrav/grav/issues/2847)
|
||||
* Fixed numeric usernames not working in `Flex Users`
|
||||
* Implemented missing Flex `$page->move()` method
|
||||
|
||||
# v1.7.0-rc.7
|
||||
## 03/05/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added `Session::regenerateId()` method to properly prevent session fixation issues
|
||||
* Added configuration option `system.strict_mode.blueprint_compat` to maintain old `validation: strict` behavior [#1273](https://github.com/getgrav/grav/issues/1273)
|
||||
1. [](#improved)
|
||||
* Improved Flex events
|
||||
* Updated CLI commands to use the new methods to initialize Grav
|
||||
1. [](#bugfix)
|
||||
* Fixed Flex Pages having broken `isFirst()`, `isLast()`, `prevSibling()`, `nextSibling()` and `adjacentSibling()`
|
||||
* Fixed broken ordering sometimes when saving/moving visible `Flex Page` [#2837](https://github.com/getgrav/grav/issues/2837)
|
||||
* Fixed ordering being lost when saving modular `Flex Page`
|
||||
* Fixed `validation: strict` not working in blueprints (see `system.strict_mode.blueprint_compat` setting) [#1273](https://github.com/getgrav/grav/issues/1273)
|
||||
* Fixed `Blueprint::extend()` and `Blueprint::embed()` not initializing dynamic properties
|
||||
* Fixed fatal error on storing flex flash using new object without a key
|
||||
* Regression: Fixed unchecking toggleable having no effect in Flex forms
|
||||
* Fixed changing page template in Flex Pages [#2828](https://github.com/getgrav/grav/issues/2828)
|
||||
|
||||
# v1.7.0-rc.6
|
||||
## 02/11/2020
|
||||
|
||||
1. [](#new)
|
||||
* Plugins & Themes: Call `$plugin->autoload()` and `$theme->autoload()` automatically when object gets initialized
|
||||
* CLI: Added `$grav->initializeCli()` method
|
||||
* Flex Directory: Implemented customizable configuration
|
||||
* Flex Storages: Added support for renaming directory entries
|
||||
1. [](#improved)
|
||||
* Vendor updates to latest
|
||||
1. [](#bugfix)
|
||||
* Regression: Fixed fatal error in blueprints [#2811](https://github.com/getgrav/grav/issues/2811)
|
||||
* Regression: Fixed bad method call in FlexDirectory::getAuthorizeRule()
|
||||
* Regression: Fixed fatal error in admin if the site has custom permissions in `onAdminRegisterPermissions`
|
||||
* Regression: Fixed flex user index with folder storage
|
||||
* Regression: Fixed fatal error in `bin/plugin` command
|
||||
* Fixed `FlexObject::triggerEvent()` not emitting events [#2816](https://github.com/getgrav/grav/issues/2816)
|
||||
* Grav 1.7: Fixed saving Flex configuration with ignored values becoming null
|
||||
* Grav 1.7: Fixed `bin/plugin` initialization
|
||||
* Grav 1.7: Fixed Flex Page cache key not taking account active language
|
||||
|
||||
# v1.7.0-rc.5
|
||||
## 02/03/2020
|
||||
|
||||
1. [](#bugfix)
|
||||
* Regression: Flex not working in PHP 7.2 or older
|
||||
* Fixed creating first user from admin not clearing Flex User directory cache [#2809](https://github.com/getgrav/grav/issues/2809)
|
||||
* Fixed Flex Pages allowing root page to be deleted
|
||||
|
||||
# v1.7.0-rc.4
|
||||
## 02/03/2020
|
||||
|
||||
1. [](#new)
|
||||
* _POTENTIAL BREAKING CHANGE:_ Upgraded Parsedown to 1.7 for Parsedown-Extra 0.8. Plugins that extend Parsedown may need a fix to render as HTML
|
||||
* Added `$grav['flex']` to access all registered Flex Directories
|
||||
* Added `$grav->dispatchEvent()` method for PSR-14 events
|
||||
* Added `FlexRegisterEvent` which triggers when `$grav['flex']` is being accessed the first time
|
||||
* Added Flex cache configuration options
|
||||
* Added `PluginsLoadedEvent` which triggers after plugins have been loaded but not yet initialized
|
||||
* Added `SessionStartEvent` which triggers when session is started
|
||||
* Added `PermissionsRegisterEvent` which triggers when `$grav['permissions']` is being accessed the first time
|
||||
* Added support for Flex Directory specific configuration
|
||||
* Added support for more advanced ACL
|
||||
* Added `flatten_array` filter to form field validation
|
||||
* Added support for `security@: or: [admin.super, admin.pages]` in blueprints (nested AND/OR mode support)
|
||||
1. [](#improved)
|
||||
* Blueprint validation: Added `validate: value_type: bool|int|float|string|trim` to `array` to filter all the values inside the array
|
||||
* Twig `url()` takes now third parameter (`true`) to return URL on non-existing file instead of returning false
|
||||
1. [](#bugfix)
|
||||
* Grav 1.7: Fixed blueprint loading issues [#2782](https://github.com/getgrav/grav/issues/2782)
|
||||
* Fixed PHP 7.4 compatibility issue with `Stream`
|
||||
* Fixed new `Flex Users` being stored with wrong filename, login issues [#2785](https://github.com/getgrav/grav/issues/2785)
|
||||
* Fixed `ignore_empty: true` not removing empty values in blueprint filtering
|
||||
* Fixed `{{ false|string }}` twig to return '0' instead of ''
|
||||
* Fixed twig `url()` failing if stream has extra slash in it (e.g. `user:///data`)
|
||||
* Fixed `Blueprint::filter()` returning null instead of array if there is nothing to return
|
||||
* Fixed `Cannot use a scalar value as an array` error in `Utils::arrayUnflattenDotNotation()`, ignore nested structure instead
|
||||
* Fixed `Route` instance in multi-site setups
|
||||
* Fixed `system.translations: false` breaking `Inflector` methods
|
||||
* Fixed filtering ignored (eg. `security@: admin.super`) fields causing `Flex Objects` to lose data on save
|
||||
* Grav 1.7: Fixed `Flex Pages` unserialize issues if Flex-Objects Plugin has not been installed
|
||||
* Grav 1.7: Require Flex-Objects Plugin to edit Flex Accounts
|
||||
* Grav 1.7: Fixed bad result on testing `isPage()` when using Flex Pages
|
||||
|
||||
# v1.7.0-rc.3
|
||||
## 01/02/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added root page support for `Flex Pages`
|
||||
1. [](#improved)
|
||||
* Twig filter `|yaml_serialize`: added support for `JsonSerializable` objects and other array-like objects
|
||||
* Added support for returning Flex Page specific permissions for admin and testing
|
||||
* Updated copyright dates to `2020`
|
||||
* Various vendor updates
|
||||
1. [](#bugfix)
|
||||
* Grav 1.7: Fixed error on page initialization [#2753](https://github.com/getgrav/grav/issues/2753)
|
||||
* Fixed checking ACL for another user (who is not currently logged in) in a Flex Object or Directory
|
||||
* Fixed bug in Windows where `Filesystem::dirname()` returns backslashes
|
||||
* Fixed Flex object issues in Windows [#2773](https://github.com/getgrav/grav/issues/2773)
|
||||
|
||||
# v1.7.0-rc.2
|
||||
## 12/04/2019
|
||||
|
||||
1. [](#new)
|
||||
* Updated Symfony Components to 4.4
|
||||
* Added support for page specific CRUD permissions (`Flex Pages` only)
|
||||
* Added new `-r <job-id>` option for Scheduler CLI command to force-run a job [#2720](https://github.com/getgrav/grav/issues/2720)
|
||||
* Added `Utils::isAssoc()` and `Utils::isNegative()` helper methods
|
||||
* Changed `UserInterface::authorize()` to return `null` having the same meaning as `false` if access is denied because of no matching rule
|
||||
* Changed `FlexAuthorizeInterface::isAuthorized()` to return `null` having the same meaning as `false` if access is denied because of no matching rule
|
||||
* Moved all Flex type classes under `Grav\Common\Flex`
|
||||
* DEPRECATED `Grav\Common\User\Group` in favor of `$grav['user_groups']`, which contains Flex UserGroup collection
|
||||
* DEPRECATED `$page->modular()` in favor of `$page->isModule()` for better readability
|
||||
* Fixed phpstan issues in all code up to level 3
|
||||
1. [](#improved)
|
||||
* Improved twig `|array` filter to work with iterators and objects with `toArray()` method
|
||||
* Updated Flex `SimpleStorage` code to feature match the other storages
|
||||
* Improved user and group ACL to support deny permissions (`Flex Users` only)
|
||||
* Improved twig `authorize()` function to work better with nested rule parameters
|
||||
* Output the current username that Scheduler is using if crontab not setup
|
||||
* Translations: rename MODULAR to MODULE everywhere
|
||||
* Optimized `Flex Pages` collection filtering
|
||||
* Frontend optimizations for `Flex Pages`
|
||||
1. [](#bugfix)
|
||||
* Regression: Fixed Grav update bug [#2722](https://github.com/getgrav/grav/issues/2722)
|
||||
* Fixed fatal error when calling `{{ grav.undefined }}`
|
||||
* Grav 1.7: Reverted `$object->getStorageKey()` interface as it was not a good idea, added `getMasterKey()` for pages
|
||||
* Grav 1.7: Fixed logged in user being able to delete his own account from admin account manager
|
||||
|
||||
# v1.7.0-rc.1
|
||||
## 11/06/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added `Flex Pages` to Grav core and removed Flex Objects plugin dependency
|
||||
* Added `Utils::simpleTemplate()` method for very simple variable templating
|
||||
* Added `array_diff()` twig function
|
||||
* Added `template_from_string()` twig function
|
||||
* Updated Symfony Components to 4.3
|
||||
1. [](#improved)
|
||||
* Improved `Scheduler` cron command check and more useful CLI information
|
||||
* Improved `Flex Users`: obey blueprints and allow Flex to be used in admin only
|
||||
* Improved `Flex` to support custom site template paths
|
||||
* Changed Twig `{% cache %}` tag to not need unique key, and `lifetime` is now optional
|
||||
* Added mime support for file formatters
|
||||
* Updated built-in `composer.phar` to latest `1.9.0`
|
||||
* Updated vendor libraries
|
||||
* Use `Symfony EventDispatcher` directly and not rockettheme/toolbox wrapper
|
||||
1. [](#bugfix)
|
||||
* Fixed exception caused by missing template type based on `Accept:` header [#2705](https://github.com/getgrav/grav/issues/2705)
|
||||
* Fixed `Page::untranslatedLanguages()` not being symmetrical to `Page::translatedLanguages()`
|
||||
* Fixed `Flex Pages` not calling `onPageProcessed` event when cached
|
||||
* Fixed phpstan issues in Framework up to level 7
|
||||
* Fixed issue with duplicate configuration settings in Flex Directory
|
||||
* Fixed fatal error if there are numeric folders in `Flex Pages`
|
||||
* Fixed error on missing `Flex` templates in if `Flex Objects` plugin isn't installed
|
||||
* Fixed `PageTranslateTrait::getAllLanguages()` and `getAllLanguages()` to include default language
|
||||
* Fixed multi-language saving issues with default language in `Flex Pages`
|
||||
* Selfupgrade CLI: Fixed broken selfupgrade assets reference [#2681](https://github.com/getgrav/grav/issues/2681)
|
||||
* Grav 1.7: Fixed PHP 7.1 compatibility issues
|
||||
* Grav 1.7: Fixed fatal error in multi-site setups
|
||||
* Grav 1.7: Fixed `Flex Pages` routing if using translated slugs or `system.hide_in_urls` setting
|
||||
* Grav 1.7: Fixed bug where Flex index file couldn't be disabled
|
||||
|
||||
# v1.7.0-beta.10
|
||||
## 10/03/2019
|
||||
|
||||
1. [](#improved)
|
||||
* Flex: Removed extra exists check when creating object (messes up "non-existing" pages)
|
||||
* Support customizable null character replacement in `CSVFormatter::decode()`
|
||||
1. [](#bugfix)
|
||||
* Fixed wrong Grav param separator when using `Route` class
|
||||
* Fixed Flex User Avatar not fully backwards compatible with old user
|
||||
* Grav 1.7: Fixed prev/next page missing pages if pagination was turned on in page header
|
||||
* Grav 1.7: Reverted setting language for every page during initialization
|
||||
* Grav 1.7: Fixed numeric language inconsistencies
|
||||
|
||||
# v1.7.0-beta.9
|
||||
## 09/26/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `{% cache %}` Twig tag eliminating need for `twigcache` extension.
|
||||
1. [](#improved)
|
||||
* Improved blueprint initialization in Flex Objects (fixes content aware fields)
|
||||
* Improved Flex FolderStorage class to better hide storage specific logic
|
||||
* Exception will output a badly formatted line in `CsvFormatter::decode()`
|
||||
1. [](#bugfix)
|
||||
* Fixed error when activating Flex Accounts in GRAV system configuration (PHP 7.1)
|
||||
* Fixed Grav parameter handling in `RouteFactory::createFromString()`
|
||||
|
||||
# v1.7.0-beta.8
|
||||
## 09/19/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added new `Security::sanitizeSVG()` function
|
||||
* Backwards compatibility break: `FlexStorageInterface::getStoragePath()` and `getMediaPath()` can now return null
|
||||
1. [](#improved)
|
||||
* Several FlexObject loading improvements
|
||||
* Added `bin/grav page-system-validator [-r|--record] [-c|--check]` to test Flex Pages
|
||||
* Improved language support for `Route` class
|
||||
1. [](#bugfix)
|
||||
* Regression: Fixed language fallback
|
||||
* Regression: Fixed translations when language code is used for non-language purposes
|
||||
* Regression: Allow SVG avatar images for users
|
||||
* Fixed error in `Session::getFlashObject()` if Flex Form is being used
|
||||
* Fixed broken Twig `dump()`
|
||||
* Fixed `Page::modular()` and `Page::modularTwig()` returning `null` for folders and other non-initialized pages
|
||||
* Fixed 404 error when you click to non-routable menu item with children: redirect to the first child instead
|
||||
* Fixed wrong `Pages::dispatch()` calls (with redirect) when we really meant to call `Pages::find()`
|
||||
* Fixed avatars not being displayed with flex users [#2431](https://github.com/getgrav/grav/issues/2431)
|
||||
* Fixed initial Flex Object state when creating a new objects in a form
|
||||
|
||||
# v1.7.0-beta.7
|
||||
## 08/30/2019
|
||||
|
||||
1. [](#improved)
|
||||
* Improved language support
|
||||
1. [](#bugfix)
|
||||
* `FlexForm`: Fixed some compatibility issues with Form plugin
|
||||
|
||||
# v1.7.0-beta.6
|
||||
## 08/29/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added experimental support for `Flex Pages` (**Flex Objects** plugin required)
|
||||
1. [](#improved)
|
||||
* Improved `bin/grav yamllinter` CLI command by adding an option to find YAML Linting issues from the whole site or custom folder
|
||||
* Added support for not instantiating pages, useful to speed up tasks
|
||||
* Greatly improved speed of loading Flex collections
|
||||
1. [](#bugfix)
|
||||
* Fixed `$page->summary()` always striping HTML tags if the summary was set by `$page->setSummary()`
|
||||
* Fixed `Flex->getObject()` when using Flex Key
|
||||
* Grav 1.7: Fixed enabling PHP Debug Bar causes fatal error in Gantry [#2634](https://github.com/getgrav/grav/issues/2634)
|
||||
* Grav 1.7: Fixed broken taxonomies [#2633](https://github.com/getgrav/grav/issues/2633)
|
||||
* Grav 1.7: Fixed unpublished blog posts being displayed on the front-end [#2650](https://github.com/getgrav/grav/issues/2650)
|
||||
|
||||
# v1.7.0-beta.5
|
||||
## 08/11/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `bin/grav server` CLI command to easily run Symfony or PHP built-in webservers
|
||||
* Added `hasFlexFeature()` method to test if `FlexObject` or `FlexCollection` implements a given feature
|
||||
* Added `getFlexFeatures()` method to return all features that `FlexObject` or `FlexCollection` implements
|
||||
* DEPRECATED `FlexDirectory::update()` and `FlexDirectory::remove()`
|
||||
* Added `FlexStorage::getMetaData()` to get updated object meta information for listed keys
|
||||
* Added `Language::getPageExtensions()` to get full list of supported page language extensions
|
||||
* Added `$grav->close()` method to properly terminate the request with a response
|
||||
* Added `Pages::getCollection()` method
|
||||
1. [](#improved)
|
||||
* Better support for Symfony local server `symfony server:start`
|
||||
* Make `Route` objects immutable
|
||||
* `FlexDirectory::getObject()` can now be called without any parameters to create a new object
|
||||
* Flex objects no longer return temporary key if they do not have one; empty key is returned instead
|
||||
* Updated vendor libraries
|
||||
* Moved `collection()` and `evaluate()` logic from `Page` class into `Pages` class
|
||||
1. [](#bugfix)
|
||||
* Fixed `Form` not to use deleted flash object until the end of the request fixing issues with reset
|
||||
* Fixed `FlexForm` to allow multiple form instances with non-existing objects
|
||||
* Fixed `FlexObject` search by using `key`
|
||||
* Grav 1.7: Fixed clockwork messages with arrays and objects
|
||||
|
||||
# v1.7.0-beta.4
|
||||
## 07/01/2019
|
||||
|
||||
1. [](#new)
|
||||
* Updated with Grav 1.6.12 features, improvements & fixes
|
||||
* Added new configuration option `system.debugger.censored` to hide potentially sensitive information
|
||||
* Added new configuration option `system.languages.include_default_lang_file_extension` to keep default language in `.md` files if set to `false`
|
||||
* Added configuration option to set fallback content languages individually for every language
|
||||
1. [](#improved)
|
||||
* Updated Vendor libraries
|
||||
1. [](#bugfix)
|
||||
* Fixed `.md` page to be assigned to the default language and to be listed in translated/untranslated page list
|
||||
* Fixed `Language::getFallbackPageExtensions()` to fall back only to default language instead of going through all languages
|
||||
* Fixed `Language::getFallbackPageExtensions()` returning wrong file extensions when passing custom page extension
|
||||
|
||||
# v1.7.0-beta.3
|
||||
## 06/24/2019
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed Clockwork on Windows machines
|
||||
* Fixed parent field issues on Windows machines
|
||||
* Fixed unreliable Clockwork calls in sub-folders
|
||||
|
||||
# v1.7.0-beta.2
|
||||
## 06/21/2019
|
||||
|
||||
1. [](#new)
|
||||
* Updated with Grav 1.6.11 fixes
|
||||
1. [](#improved)
|
||||
* Updated the Clockwork text
|
||||
|
||||
# v1.7.0-beta.1
|
||||
## 06/14/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added support for [Clockwork](https://underground.works/clockwork) developer tools (now default debugger)
|
||||
* Added support for [Tideways XHProf](https://github.com/tideways/php-xhprof-extension) PHP Extension for profiling method calls
|
||||
* Added Twig profiling for Clockwork debugger
|
||||
* Added support for Twig 2.11 (compatible with Twig 1.40+)
|
||||
* Optimization: Initialize debugbar only after the configuration has been loaded
|
||||
* Optimization: Combine some early Grav processors into a single one
|
||||
|
||||
# v1.6.31
|
||||
## 12/14/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Allow all CSS and JS via `robots.txt` [#2006](https://github.com/getgrav/grav/issues/2006) [#3067](https://github.com/getgrav/grav/issues/3067)
|
||||
1. [](#bugfix)
|
||||
* Fixed `pages` field escaping issues, needs admin update, too [admin#1990](https://github.com/getgrav/grav-plugin-admin/issues/1990)
|
||||
* Fix `svg-image` issue with classes applied to all elements [#3068](https://github.com/getgrav/grav/issues/3068)
|
||||
|
||||
# v1.6.30
|
||||
## 12/03/2020
|
||||
|
||||
1. [](#bugfix)
|
||||
* Rollback `samesite` cookie logic as it causes issues with PHP < 7.3 [#309](https://github.com/getgrav/grav/issues/3089)
|
||||
* Fixed issue with `.travis.yml` due to GitHub API deprecated functionality
|
||||
|
||||
# v1.6.29
|
||||
## 12/02/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added basic support for `user/config/versions.yaml`
|
||||
1. [](#improved)
|
||||
* Updated bundled JQuery to latest version `3.5.1`
|
||||
* Forward a `sid` to GPM when downloading a premium package via CLI
|
||||
* Better handling of missing repository index [grav-plugin-admin#1916](https://github.com/getgrav/grav-plugin-admin/issues/1916)
|
||||
* Set `grav_cli` as referrer when using `Response` from CLI
|
||||
* Add option for timeout in `self-upgrade` command [#3013](https://github.com/getgrav/grav/pull/3013)
|
||||
* Allow to set SameSite from system.yaml [#3063](https://github.com/getgrav/grav/pull/3063)
|
||||
* Update media.yaml with some MS Office mimetypes [#3070](https://github.com/getgrav/grav/pull/3070)
|
||||
1. [](#bugfix)
|
||||
* Fixed hardcoded system folder in blueprints, config and language streams
|
||||
* Added `.htaccess` rule to block attempts to use Twig in the request URL
|
||||
* Fix compatibility with Symfony 4.2 and up. [#3048](https://github.com/getgrav/grav/pull/3048)
|
||||
* Fix failing example custom shceduled job. [#3050](https://github.com/getgrav/grav/pull/3050)
|
||||
* Fix for XSS advisory [GHSA-cvmr-6428-87w9](https://github.com/getgrav/grav/security/advisories/GHSA-cvmr-6428-87w9)
|
||||
* Fix uploads_dangerous_extensions checking [#3060](https://github.com/getgrav/grav/pull/3060)
|
||||
* Remove redundant prefixing of `.` to extension [#3060](https://github.com/getgrav/grav/pull/3060)
|
||||
* Check exact extension in checkFilename utility [#3061](https://github.com/getgrav/grav/pull/3061)
|
||||
|
||||
# v1.6.28
|
||||
## 10/07/2020
|
||||
|
||||
1. [](#new)
|
||||
* Back-ported twig `{% cache %}` tag from Grav 1.7
|
||||
* Back-ported `Utils::fullPath()` helper function from Grav 1.7
|
||||
* Back-ported `{{ svg_image() }}` Twig function from Grav 1.7
|
||||
* Back-ported `Folder::countChildren()` function from Grav 1.7
|
||||
1. [](#improved)
|
||||
* Use new `{{ theme_var() }}` enhanced logic from Grav 1.7
|
||||
* Improved `Excerpts` class with fixes and functionality from Grav 1.7
|
||||
* Ensure `onBlueprintCreated()` is initialized first
|
||||
* Do not cache default `404` error page
|
||||
* Composer update of vendor libraries
|
||||
* Switched `Caddyfile` to use new Caddy2 syntax + improved usability
|
||||
1. [](#bugfix)
|
||||
* Fixed Referer reference during GPM calls.
|
||||
* Fixed fatal error with toggled lists
|
||||
|
||||
# v1.6.27
|
||||
## 09/01/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Right trim route for safety
|
||||
* Use the proper ellipsis for summary [#2939](https://github.com/getgrav/grav/pull/2939)
|
||||
* Left pad schedule times with zeros [#2921](https://github.com/getgrav/grav/pull/2921)
|
||||
|
||||
# v1.6.26
|
||||
## 06/08/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Added new configuration option to control the supported attributes in markdown links [#2882](https://github.com/getgrav/grav/issues/2882)
|
||||
1. [](#bugfix)
|
||||
* Fixed blueprint for `system.pages.hide_empty_folders` [#1925](https://github.com/getgrav/grav/issues/2925)
|
||||
* JSON Route of homepage with no ‘route’ set is valid
|
||||
* Fix case-insensitive search of location header [form#425](https://github.com/getgrav/grav-plugin-form/issues/425)
|
||||
|
||||
# v1.6.25
|
||||
## 05/14/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Added system configuration support for `HTTP_X_Forwarded` headers (host disabled by default)
|
||||
* Updated `PHPUserAgentParser` to 1.0.0
|
||||
* Bump `Go` to version 1.13 in `travis.yaml`
|
||||
|
||||
# v1.6.24
|
||||
## 04/27/2020
|
||||
|
||||
1. [](#improved)
|
||||
* Added support for `X-Forwarded-Host` [#2891](https://github.com/getgrav/grav/pull/2891)
|
||||
* Disable XDebug in Travis builds
|
||||
|
||||
# v1.6.23
|
||||
## 03/19/2020
|
||||
|
||||
1. [](#new)
|
||||
* Moved `Parsedown` 1.6 and `ParsedownExtra` 0.7 into `Grav\Framework\Parsedown` to allow fixes
|
||||
* Added `aliases.php` with references to direct `\Parsedown` and `\ParsedownExtra` references
|
||||
1. [](#improved)
|
||||
* Upgraded `jQuery` to latest 3.4.1 version [#2859](https://github.com/getgrav/grav/issues/2859)
|
||||
1. [](#bugfix)
|
||||
* Fixed PHP 7.4 issue in ParsedownExtra [#2832](https://github.com/getgrav/grav/issues/2832)
|
||||
* Fix for [user reported](https://twitter.com/OriginalSicksec) CVE path-based open redirect
|
||||
* Fix for `stream_set_option` error with PHP 7.4 via Toolbox#28 [#2850](https://github.com/getgrav/grav/issues/2850)
|
||||
|
||||
# v1.6.22
|
||||
## 03/05/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added `Pages::reset()` method
|
||||
1. [](#improved)
|
||||
* Updated Negotiation library to address issues [#2513](https://github.com/getgrav/grav/issues/2513)
|
||||
1. [](#bugfix)
|
||||
* Fixed issue with search plugins not being able to switch between page translations
|
||||
* Fixed issues with `Pages::baseRoute()` not picking up active language reliably
|
||||
* Reverted `validation: strict` fix as it breaks sites, see [#1273](https://github.com/getgrav/grav/issues/1273)
|
||||
|
||||
# v1.6.21
|
||||
## 02/11/2020
|
||||
|
||||
1. [](#new)
|
||||
* Added `ConsoleCommand::setLanguage()` method to set language to be used from CLI
|
||||
* Added `ConsoleCommand::initializeGrav()` method to properly set up Grav instance to be used from CLI
|
||||
* Added `ConsoleCommand::initializePlugins()`method to properly set up all plugins to be used from CLI
|
||||
* Added `ConsoleCommand::initializeThemes()`method to properly set up current theme to be used from CLI
|
||||
* Added `ConsoleCommand::initializePages()` method to properly set up pages to be used from CLI
|
||||
1. [](#improved)
|
||||
* Vendor updates
|
||||
1. [](#bugfix)
|
||||
* Fixed `bin/plugin` CLI calling `$themes->init()` way too early (removed it, use above methods instead)
|
||||
* Fixed call to `$grav['page']` crashing CLI
|
||||
* Fixed encoding problems when PHP INI setting `default_charset` is not `utf-8` [#2154](https://github.com/getgrav/grav/issues/2154)
|
||||
|
||||
# v1.6.20
|
||||
## 02/03/2020
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed incorrect routing caused by `str_replace()` in `Uri::init()` [#2754](https://github.com/getgrav/grav/issues/2754)
|
||||
* Fixed session cookie is being set twice in the HTTP header [#2745](https://github.com/getgrav/grav/issues/2745)
|
||||
* Fixed session not restarting if user was invalid (downgrading from Grav 1.7)
|
||||
* Fixed filesystem iterator calls with non-existing folders
|
||||
* Fixed `checkbox` field not being saved, requires also Form v4.0.2 [#1225](https://github.com/getgrav/grav/issues/1225)
|
||||
* Fixed `validation: strict` not working in blueprints [#1273](https://github.com/getgrav/grav/issues/1273)
|
||||
* Fixed `Data::filter()` removing empty fields (such as empty list) by default [#2805](https://github.com/getgrav/grav/issues/2805)
|
||||
* Fixed fatal error with non-integer page param value [#2803](https://github.com/getgrav/grav/issues/2803)
|
||||
* Fixed `Assets::addInlineJs()` parameter type mismatch between v1.5 and v1.6 [#2659](https://github.com/getgrav/grav/issues/2659)
|
||||
* Fixed `site.metadata` saving issues [#2615](https://github.com/getgrav/grav/issues/2615)
|
||||
|
||||
# v1.6.19
|
||||
## 12/04/2019
|
||||
|
||||
1. [](#new)
|
||||
* Catch PHP 7.4 deprecation messages and report them in debugbar instead of throwing fatal error
|
||||
1. [](#bugfix)
|
||||
* Fixed fatal error when calling `{{ grav.undefined }}`
|
||||
* Fixed multiple issues when there are no pages in the site
|
||||
* PHP 7.4 fix for [#2750](https://github.com/getgrav/grav/issues/2750)
|
||||
|
||||
# v1.6.18
|
||||
## 12/02/2019
|
||||
|
||||
1. [](#bugfix)
|
||||
* PHP 7.4 fix in `Pages::buildSort()`
|
||||
* Updated vendor libraries for PHP 7.4 fixes in Twig and other libraries
|
||||
* Fixed fatal error when `$page->id()` is null [#2731](https://github.com/getgrav/grav/pull/2731)
|
||||
* Fixed cache conflicts on pages with no set id
|
||||
* Fix rewrite rule for for `lighttpd` default config [#721](https://github.com/getgrav/grav/pull/2721)
|
||||
|
||||
# v1.6.17
|
||||
## 11/06/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added working ETag (304 Not Modified) support based on the final rendered HTML
|
||||
1. [](#improved)
|
||||
* Safer file handling + customizable null char replacement in `CsvFormatter::decode()`
|
||||
* Change of Behavior: `Inflector::hyphenize` will now automatically trim dashes at beginning and end of a string.
|
||||
* Change in Behavior for `Folder::all()` so no longer fails if trying to copy non-existent dot file [#2581](https://github.com/getgrav/grav/pull/2581)
|
||||
* renamed composer `test-plugins` script to `phpstan-plugins` to be more explicit [#2637](https://github.com/getgrav/grav/pull/2637)
|
||||
1. [](#bugfix)
|
||||
* Fixed PHP 7.1 bug in FlexMedia
|
||||
* Fix cache image generation when using cropResize [#2639](https://github.com/getgrav/grav/pull/2639)
|
||||
* Fix `array_merge()` exception with non-array page header metadata [#2701](https://github.com/getgrav/grav/pull/2701)
|
||||
|
||||
# v1.6.16
|
||||
## 09/19/2019
|
||||
|
||||
1. [](#bugfix)
|
||||
* Fixed Flex user creation if file storage is being used [#2444](https://github.com/getgrav/grav/issues/2444)
|
||||
* Fixed `Badly encoded JSON data` warning when uploading files [#2663](https://github.com/getgrav/grav/issues/2663)
|
||||
|
||||
# v1.6.15
|
||||
## 08/20/2019
|
||||
|
||||
1. [](#improved)
|
||||
* Improved robots.txt [#2632](https://github.com/getgrav/grav/issues/2632)
|
||||
1. [](#bugfix)
|
||||
* Fixed broken markdown Twig tag [#2635](https://github.com/getgrav/grav/issues/2635)
|
||||
* Force Symfony 4.2 in Grav 1.6 to remove a bunch of deprecated messages
|
||||
|
||||
# v1.6.14
|
||||
## 08/18/2019
|
||||
|
||||
1. [](#bugfix)
|
||||
* Actually include fix for `system\router.php` [#2627](https://github.com/getgrav/grav/issues/2627)
|
||||
|
||||
# v1.6.13
|
||||
## 08/16/2019
|
||||
|
||||
1. [](#bugfix)
|
||||
* Regression fix for `system\router.php` [#2627](https://github.com/getgrav/grav/issues/2627)
|
||||
|
||||
# v1.6.12
|
||||
## 08/14/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added support for custom `FormFlash` save locations
|
||||
* Added a new `Utils::arrayLower()` method for lowercasing arrays
|
||||
* Support new GRAV_BASEDIR environment variable [#2541](https://github.com/getgrav/grav/pull/2541)
|
||||
* Allow users to override plugin handler priorities [#2165](https://github.com/getgrav/grav/pull/2165)
|
||||
1. [](#improved)
|
||||
* Use new `Utils::getSupportedPageTypes()` to enforce `html,htm` at the front of the list [#2531](https://github.com/getgrav/grav/issues/2531)
|
||||
* Updated vendor libraries
|
||||
* Markdown filter is now page-aware so that it works with modular references [admin#1731](https://github.com/getgrav/grav-plugin-admin/issues/1731)
|
||||
* Check of `GRAV_USER_INSTANCE` constant is already defined [#2621](https://github.com/getgrav/grav/pull/2621)
|
||||
1. [](#bugfix)
|
||||
* Fixed some potential issues when `$grav['user']` is not set
|
||||
* Fixed error when calling `Media::add($name, null)`
|
||||
* Fixed `url()` returning wrong path if using stream with grav root path in it, eg: `user-data://shop` when Grav is in `/shop`
|
||||
* Fixed `url()` not returning a path to non-existing file (`user-data://shop` => `/user/data/shop`) if it is set to fail gracefully
|
||||
* Fixed `url()` returning false on unknown streams, such as `ftp://domain.com`, they should be treated as external URL
|
||||
* Fixed Flex User to have permissions to save and delete his own user
|
||||
* Fixed new Flex User creation not being possible because of username could not be given
|
||||
* Fixed fatal error 'Expiration date must be an integer, a DateInterval or null, "double" given' [#2529](https://github.com/getgrav/grav/issues/2529)
|
||||
* Fixed non-existing Flex object having a bad media folder
|
||||
* Fixed collections using `page@.self:` should allow modular pages if requested
|
||||
* Fixed an error when trying to delete a file from non-existing Flex Object
|
||||
* Fixed `FlexObject::exists()` failing sometimes just after the object has been saved
|
||||
* Fixed CSV formatter not encoding strings with `"` and `,` properly
|
||||
* Fixed var order in `Validation.php` [#2610](https://github.com/getgrav/grav/issues/2610)
|
||||
|
||||
# v1.6.11
|
||||
## 06/21/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added `FormTrait::getAllFlashes()` method to get all the available form flash objects for the form
|
||||
* Added creation and update timestamps to `FormFlash` objects
|
||||
1. [](#improved)
|
||||
* Added `FormFlashInterface`, changed constructor to take `$config` array
|
||||
1. [](#bugfix)
|
||||
* Fixed error in `ImageMedium::url()` if the image cache folder does not exist
|
||||
* Fixed empty form flash name after file upload or form state update
|
||||
* Fixed a bug in `Route::withParam()` method
|
||||
* Fixed issue with `FormFlash` objects when there is no session initialized
|
||||
|
||||
# v1.6.10
|
||||
## 06/14/2019
|
||||
|
||||
1. [](#improved)
|
||||
* Added **page blueprints** to `YamlLinter` CLI and Admin reports
|
||||
* Removed `Gitter` and `Slack` [#2502](https://github.com/getgrav/grav/issues/2502)
|
||||
* Optimizations for Plugin/Theme loading
|
||||
* Generalized markdown classes so they can be used outside of `Page` scope with a custom `Excerpts` class instance
|
||||
* Change minimal port number to 0 (unix socket) [#2452](https://github.com/getgrav/grav/issues/2452)
|
||||
1. [](#bugfix)
|
||||
* Force question to install demo content in theme update [#2493](https://github.com/getgrav/grav/issues/2493)
|
||||
* Fixed GPM errors from blueprints not being logged [#2505](https://github.com/getgrav/grav/issues/2505)
|
||||
* Don't error when IP is invalid [#2507](https://github.com/getgrav/grav/issues/2507)
|
||||
* Fixed regression with `bin/plugin` not listing the plugins available (1c725c0)
|
||||
* Fixed bitwise operator in `TwigExtension::exifFunc()` [#2518](https://github.com/getgrav/grav/issues/2518)
|
||||
* Fixed issue with lang prefix incorrectly identifying as admin [#2511](https://github.com/getgrav/grav/issues/2511)
|
||||
* Fixed issue with `U0ils::pathPrefixedBYLanguageCode()` and trailing slash [#2510](https://github.com/getgrav/grav/issues/2511)
|
||||
* Fixed regresssion issue of `Utils::Url()` not returning `false` on failure. Added new optional `fail_gracefully` 3rd attribute to return string that caused failure [#2524](https://github.com/getgrav/grav/issues/2524)
|
||||
|
||||
# v1.6.9
|
||||
## 05/09/2019
|
||||
|
||||
1. [](#new)
|
||||
* Added `Route::withoutParams()` methods
|
||||
* Added `Pages::setCheckMethod()` method to override page configuration in Admin Plugin
|
||||
* Added `Cache::clearCache('invalidate')` parameter for just invalidating the cache without deleting any cached files
|
||||
* Made `UserCollectionInderface` to extend `Countable` to get the count of existing users
|
||||
1. [](#improved)
|
||||
* Flex admin: added default search options for flex objects
|
||||
* Flex collection and object now fall back to the default template if template file doesn't exist
|
||||
* Updated Vendor libraries including Twig 1.40.1
|
||||
* Updated language files from `https://crowdin.com/project/grav-core`
|
||||
1. [](#bugfix)
|
||||
* Fixed `$grav['route']` from being modified when the route instance gets modified
|
||||
* Fixed Assets options array mixed with standalone priority [#2477](https://github.com/getgrav/grav/issues/2477)
|
||||
* Fix for `avatar_url` provided by 3rd party providers
|
||||
* Fixed non standard `lang` code lengths in `Utils` and `Session` detection
|
||||
* Fixed saving a new object in Flex `SimpleStorage`
|
||||
* Fixed exception in `Flex::getDirectories()` if the first parameter is set
|
||||
* Output correct "Last Updated" in `bin/gpm info` command
|
||||
* Checkbox getting interpreted as string, so created new `Validation::filterCheckbox()`
|
||||
* Fixed backwards compatibility to `select` field with `selectize.create` set to true [git-sync#141](https://github.com/trilbymedia/grav-plugin-git-sync/issues/141)
|
||||
* Fixed `YamlFormatter::decode()` to always return array [#2494](https://github.com/getgrav/grav/pull/2494)
|
||||
* Fixed empty `$grav['request']->getAttribute('route')->getExtension()`
|
||||
|
||||
# v1.6.8
|
||||
## 04/23/2019
|
||||
|
||||
@@ -25,18 +946,18 @@
|
||||
## 04/17/2019
|
||||
|
||||
1. [](#new)
|
||||
* `FormInterface` now implements `RenderInterface`
|
||||
* Added new `FormInterface::getTask()` method which reads the task from `form.task` in the blueprint
|
||||
* `FormInterface` now implements `RenderInterface`
|
||||
* Added new `FormInterface::getTask()` method which reads the task from `form.task` in the blueprint
|
||||
1. [](#improved)
|
||||
* Updated vendor libraries to latest
|
||||
1. [](#bugfix)
|
||||
* Rollback `redirect_default_route` logic as it has issues with multi-lang [#2459](https://github.com/getgrav/grav/issues/2459)
|
||||
* Fix potential issue with `|contains` Twig filter on PHP 7.3
|
||||
* Fix potential issue with `|contains` Twig filter on PHP 7.3
|
||||
* Fixed bug in text field filtering: return empty string if value isn't a string or number [#2460](https://github.com/getgrav/grav/issues/2460)
|
||||
* Force Asset `priority` to be an integer and not throw error if invalid string passed [#2461](https://github.com/getgrav/grav/issues/2461)
|
||||
* Fixed bug in text field filtering: return empty string if value isn't a string or number
|
||||
* Fixed `FlexForm` missing getter methods for defining form variables
|
||||
|
||||
|
||||
# v1.6.5
|
||||
## 04/15/2019
|
||||
|
||||
@@ -97,7 +1018,7 @@
|
||||
* Added `Grav\Framework\Object\ObjectIndex` class
|
||||
* Added `Grav\Framework\Flex` classes
|
||||
* Added support for hiding form fields in blueprints by using dynamic property like `security@: admin.foobar`, `scope@: object` or `scope-ignore@: object` to any field
|
||||
* New experimental **FlexObjects** powered `Users` for increased performance and capability (**disabled** by default)
|
||||
* New experimental **FlexObjects** powered `Users` for increased performance and capability (**disabled** by default)
|
||||
* Added PSR-7 and PSR-15 classes
|
||||
* Added `Grav\Framework\DI\Container` class
|
||||
* Added `Grav\Framework\RequestHandler\RequestHandler` class
|
||||
@@ -173,7 +1094,7 @@
|
||||
* Added ability to reset `Page::metadata` to allow rebuilding from automatically generated values
|
||||
* Added back missing `page.types` field in system content configuration [admin#1612](https://github.com/getgrav/grav-plugin-admin/issues/1612)
|
||||
* Console commands: add method for invalidating cache
|
||||
* Updated languages
|
||||
* Updated languages
|
||||
* Improved `$page->forms()` call, added `$page->addForms()`
|
||||
* Updated languages from crowdin
|
||||
* Fixed `ImageMedium` constructor warning when file does not exist
|
||||
@@ -194,7 +1115,7 @@
|
||||
* Added apcu autoloader optimization
|
||||
* Additional helper methods in `Language`, `Languages`, and `LanguageCodes` classes
|
||||
* Call `onFatalException` event also on internal PHP errors
|
||||
* Built-in PHP Webserver: log requests before handling them
|
||||
* Built-in PHP Webserver: log requests before handling them
|
||||
* Added support for syslog and syslog facility logging (default: 'file')
|
||||
* Improved usability of `System` configuration blueprint with side-tabs
|
||||
1. [](#bugfix)
|
||||
@@ -219,7 +1140,7 @@
|
||||
* Fixed failed login if user attempts to log in with upper case non-english letters
|
||||
* Removed extra authenticated/authorized fields when saving existing user from a form
|
||||
* Fixed `Grav\Framework\Route::__toString()` returning relative URL, not relative route
|
||||
* Fixed handling of `append_url_extension` inside of `Page::templateFormat()` [#2264](https://github.com/getgrav/grav/issues/2264)
|
||||
* Fixed handling of `append_url_extension` inside of `Page::templateFormat()` [#2264](https://github.com/getgrav/grav/issues/2264)
|
||||
* Fixed a broken language string [#2261](https://github.com/getgrav/grav/issues/2261)
|
||||
* Fixed clearing cache having no effect on Doctrine cache
|
||||
* Fixed `Medium::relativePath()` for streams
|
||||
@@ -272,7 +1193,7 @@
|
||||
* Updated vendor libraries
|
||||
1. [](#bugfix)
|
||||
* Support spaces with filenames in responsive images [#2300](https://github.com/getgrav/grav/pull/2300)
|
||||
|
||||
|
||||
# v1.5.6
|
||||
## 12/14/2018
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ The issue tracker is the preferred channel for [bug reports](#bugs),
|
||||
requests](#pull-requests), but please respect the following restrictions:
|
||||
|
||||
* Please **do not** use the issue tracker for support requests. Use
|
||||
[the Forum](http://getgrav.org/forum) or [the Gitter chat](https://gitter.im/getgrav/grav).
|
||||
[the Forum](http://getgrav.org/forum) or [the Chat](https://chat.getgrav.org/).
|
||||
|
||||
|
||||
<a name="bugs"></a>
|
||||
@@ -110,7 +110,8 @@ Good pull requests - patches, improvements, new features - are a fantastic
|
||||
help. They should remain focused in scope and avoid containing unrelated
|
||||
commits.
|
||||
|
||||
**Please ask first** in [Slack](https://getgrav.org/slack) or in the Forum before embarking on any significant pull request (e.g.
|
||||
**Please ask first** in [the Forum](http://getgrav.org/forum) or [the Chat](https://chat.getgrav.org/)
|
||||
before embarking on any significant pull request (e.g.
|
||||
implementing features, refactoring code..),
|
||||
otherwise you risk spending a lot of time working on something that the
|
||||
project's developers might not want to merge into the project.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Grav
|
||||
Copyright (c) 2021 Grav
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
20
README.md
20
README.md
@@ -21,9 +21,13 @@ The underlying architecture of Grav is designed to use well-established and _bes
|
||||
|
||||
# Requirements
|
||||
|
||||
- PHP 7.1.3 or higher. Check the [required modules list](https://learn.getgrav.org/basics/requirements#php-requirements)
|
||||
- PHP 7.3.6 or higher. Check the [required modules list](https://learn.getgrav.org/basics/requirements#php-requirements)
|
||||
- Check the [Apache](https://learn.getgrav.org/basics/requirements#apache-requirements) or [IIS](https://learn.getgrav.org/basics/requirements#iis-requirements) requirements
|
||||
|
||||
# Documentation
|
||||
|
||||
The full documentation can be found from [learn.getgrav.org](https://learn.getgrav.org).
|
||||
|
||||
# QuickStart
|
||||
|
||||
These are the options to get Grav:
|
||||
@@ -84,6 +88,11 @@ To update plugins and themes:
|
||||
$ bin/gpm update
|
||||
```
|
||||
|
||||
## Upgrading from older version
|
||||
|
||||
* [Upgrading to Grav 1.7](https://learn.getgrav.org/16/advanced/grav-development/grav-17-upgrade-guide)
|
||||
* [Upgrading to Grav 1.6](https://learn.getgrav.org/16/advanced/grav-development/grav-16-upgrade-guide)
|
||||
* [Upgrading from Grav <1.6](https://learn.getgrav.org/16/advanced/grav-development/grav-15-upgrade-guide)
|
||||
|
||||
# Contributing
|
||||
We appreciate any contribution to Grav, whether it is related to bugs, grammar, or simply a suggestion or improvement! Please refer to the [Contributing guide](CONTRIBUTING.md) for more guidance on this topic.
|
||||
@@ -128,7 +137,14 @@ See [LICENSE](LICENSE.txt)
|
||||
|
||||
# Running Tests
|
||||
|
||||
First install the dev dependencies by running `composer update` from the Grav root.
|
||||
First install the dev dependencies by running `composer install` from the Grav root.
|
||||
|
||||
Then `composer test` will run the Unit Tests, which should be always executed successfully on any site.
|
||||
Windows users should use the `composer test-windows` command.
|
||||
You can also run a single unit test file, e.g. `composer test tests/unit/Grav/Common/AssetsTest.php`
|
||||
|
||||
To run phpstan tests, you should run:
|
||||
|
||||
* `composer phpstan` for global tests
|
||||
* `composer phpstan-framework` for more strict tests
|
||||
* `composer phpstan-plugins` to test all installed plugins
|
||||
|
||||
15
SECURITY.md
Normal file
15
SECURITY.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We are focusing our security updates on the following versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 1.7.x | :white_check_mark: |
|
||||
| 1.6.x | :white_check_mark: |
|
||||
| < 1.6 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please contact contact@getgrav.org with a detailed explaination of the security issue found and we will work with you to get it resolved as fast as possible.
|
||||
Binary file not shown.
41
bin/gpm
41
bin/gpm
@@ -2,8 +2,8 @@
|
||||
<?php
|
||||
|
||||
use Grav\Common\Composer;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Console\Application\GpmApplication;
|
||||
|
||||
\define('GRAV_CLI', true);
|
||||
\define('GRAV_REQUEST_TIME', microtime(true));
|
||||
@@ -28,6 +28,13 @@ if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
|
||||
// Set internal encoding.
|
||||
if (!\extension_loaded('mbstring')) {
|
||||
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
|
||||
}
|
||||
@ini_set('default_charset', 'UTF-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
if (!file_exists(GRAV_ROOT . '/index.php')) {
|
||||
exit('FATAL: Must be run from ROOT directory of Grav!');
|
||||
}
|
||||
@@ -36,37 +43,7 @@ if (!function_exists('curl_version')) {
|
||||
exit('FATAL: GPM requires PHP Curl module to be installed');
|
||||
}
|
||||
|
||||
$climate = new League\CLImate\CLImate;
|
||||
$climate->arguments->add([
|
||||
'environment' => [
|
||||
'prefix' => 'e',
|
||||
'longPrefix' => 'env',
|
||||
'description' => 'Configuration Environment',
|
||||
'defaultValue' => 'localhost'
|
||||
]
|
||||
]);
|
||||
$climate->arguments->parse();
|
||||
|
||||
// Set up environment based on params.
|
||||
$environment = $climate->arguments->get('environment');
|
||||
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav->setup($environment);
|
||||
|
||||
$grav['config']->init();
|
||||
$grav['uri']->init();
|
||||
$grav['users'];
|
||||
|
||||
$app = new Application('Grav Package Manager', GRAV_VERSION);
|
||||
$app->addCommands(array(
|
||||
new \Grav\Console\Gpm\IndexCommand(),
|
||||
new \Grav\Console\Gpm\VersionCommand(),
|
||||
new \Grav\Console\Gpm\InfoCommand(),
|
||||
new \Grav\Console\Gpm\InstallCommand(),
|
||||
new \Grav\Console\Gpm\UninstallCommand(),
|
||||
new \Grav\Console\Gpm\UpdateCommand(),
|
||||
new \Grav\Console\Gpm\SelfupgradeCommand(),
|
||||
new \Grav\Console\Gpm\DirectInstallCommand(),
|
||||
));
|
||||
|
||||
$app = new GpmApplication('Grav Package Manager', GRAV_VERSION);
|
||||
$app->run();
|
||||
|
||||
44
bin/grav
44
bin/grav
@@ -3,8 +3,7 @@
|
||||
|
||||
use Grav\Common\Composer;
|
||||
use Grav\Common\Grav;
|
||||
use League\CLImate\CLImate;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Grav\Console\Application\GravApplication;
|
||||
|
||||
\define('GRAV_CLI', true);
|
||||
\define('GRAV_REQUEST_TIME', microtime(true));
|
||||
@@ -25,43 +24,22 @@ if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) {
|
||||
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
|
||||
}
|
||||
|
||||
$climate = new League\CLImate\CLImate;
|
||||
$climate->arguments->add([
|
||||
'environment' => [
|
||||
'prefix' => 'e',
|
||||
'longPrefix' => 'env',
|
||||
'description' => 'Configuration Environment',
|
||||
'defaultValue' => 'localhost'
|
||||
]
|
||||
]);
|
||||
$climate->arguments->parse();
|
||||
|
||||
// Set up environment based on params.
|
||||
$environment = $climate->arguments->get('environment');
|
||||
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav->setup($environment);
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
|
||||
// Set internal encoding.
|
||||
if (!\extension_loaded('mbstring')) {
|
||||
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
|
||||
}
|
||||
@ini_set('default_charset', 'UTF-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
|
||||
if (!file_exists(GRAV_ROOT . '/index.php')) {
|
||||
exit('FATAL: Must be run from ROOT directory of Grav!');
|
||||
}
|
||||
|
||||
$app = new Application('Grav CLI Application', GRAV_VERSION);
|
||||
$app->addCommands(array(
|
||||
new \Grav\Console\Cli\InstallCommand(),
|
||||
new \Grav\Console\Cli\ComposerCommand(),
|
||||
new \Grav\Console\Cli\SandboxCommand(),
|
||||
new \Grav\Console\Cli\CleanCommand(),
|
||||
new \Grav\Console\Cli\ClearCacheCommand(),
|
||||
new \Grav\Console\Cli\BackupCommand(),
|
||||
new \Grav\Console\Cli\NewProjectCommand(),
|
||||
new \Grav\Console\Cli\SchedulerCommand(),
|
||||
new \Grav\Console\Cli\SecurityCommand(),
|
||||
new \Grav\Console\Cli\LogViewerCommand(),
|
||||
new \Grav\Console\Cli\YamlLinterCommand(),
|
||||
));
|
||||
$app = new GravApplication('Grav CLI Application', GRAV_VERSION);
|
||||
$app->run();
|
||||
|
||||
127
bin/plugin
127
bin/plugin
@@ -2,12 +2,8 @@
|
||||
<?php
|
||||
|
||||
use Grav\Common\Composer;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Console\Application\PluginApplication;
|
||||
|
||||
\define('GRAV_CLI', true);
|
||||
\define('GRAV_REQUEST_TIME', microtime(true));
|
||||
@@ -32,120 +28,19 @@ if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
|
||||
// Set internal encoding.
|
||||
if (!\extension_loaded('mbstring')) {
|
||||
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
|
||||
}
|
||||
@ini_set('default_charset', 'UTF-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
if (!file_exists(GRAV_ROOT . '/index.php')) {
|
||||
exit('FATAL: Must be run from ROOT directory of Grav!');
|
||||
}
|
||||
|
||||
$climate = new League\CLImate\CLImate;
|
||||
$climate->arguments->add([
|
||||
'environment' => [
|
||||
'prefix' => 'e',
|
||||
'longPrefix' => 'env',
|
||||
'description' => 'Configuration Environment',
|
||||
'defaultValue' => 'localhost'
|
||||
]
|
||||
]);
|
||||
$climate->arguments->parse();
|
||||
|
||||
$environment = $climate->arguments->get('environment');
|
||||
|
||||
// Bootstrap Grav container.
|
||||
$grav = Grav::instance(array('loader' => $autoload));
|
||||
$grav->setup($environment);
|
||||
|
||||
$grav['config']->init();
|
||||
$grav['uri']->init();
|
||||
$grav['users'];
|
||||
$grav['plugins']->init();
|
||||
$grav['themes']->init();
|
||||
|
||||
$app = new Application('Grav Plugins Commands', GRAV_VERSION);
|
||||
$pattern = '([A-Z]\w+Command\.php)';
|
||||
|
||||
// get arguments and strip the application name
|
||||
if (null === $argv) {
|
||||
$argv = $_SERVER['argv'];
|
||||
}
|
||||
|
||||
$bin = array_shift($argv);
|
||||
$name = array_shift($argv);
|
||||
$argv = array_merge([$bin], $argv);
|
||||
|
||||
$input = new ArgvInput($argv);
|
||||
|
||||
/** @var \Grav\Common\Data\Data $plugin */
|
||||
$plugin = $grav['plugins']->get($name);
|
||||
|
||||
$output = new ConsoleOutput();
|
||||
$output->getFormatter()->setStyle('red', new OutputFormatterStyle('red', null, array('bold')));
|
||||
$output->getFormatter()->setStyle('white', new OutputFormatterStyle('white', null, array('bold')));
|
||||
|
||||
if (is_null($plugin)) {
|
||||
$output->writeln('');
|
||||
$output->writeln("<red>$name plugin not found</red>");
|
||||
die;
|
||||
}
|
||||
|
||||
if (!$plugin->enabled) {
|
||||
$output->writeln('');
|
||||
$output->writeln("<red>$name not enabled</red>");
|
||||
die;
|
||||
}
|
||||
|
||||
|
||||
if (!$name) {
|
||||
$output->writeln('');
|
||||
$output->writeln('<red>Usage:</red>');
|
||||
$output->writeln(" {$bin} [slug] [command] [arguments]");
|
||||
$output->writeln('');
|
||||
$output->writeln('<red>Example:</red>');
|
||||
$output->writeln(" {$bin} error log -l 1 --trace");
|
||||
$list = Folder::all('plugins://', ['compare' => 'Pathname', 'pattern' => '/\/cli\/' . $pattern . '$/usm', 'levels' => 2]);
|
||||
|
||||
$total = 0;
|
||||
if (count($list)) {
|
||||
$available = [];
|
||||
$output->writeln('');
|
||||
$output->writeln('<red>Plugins with CLI available:</red>');
|
||||
foreach ($list as $index => $entry) {
|
||||
$split = explode('/', $entry);
|
||||
$entry = array_shift($split);
|
||||
$index = str_pad($index++ + 1, 2, '0', STR_PAD_LEFT);
|
||||
$total = str_pad($total++ + 1, 2, '0', STR_PAD_LEFT);
|
||||
if (\in_array($entry, $available, true)) {
|
||||
$total--;
|
||||
continue;
|
||||
}
|
||||
|
||||
$available[] = $entry;
|
||||
$commands_count = $index - $total + 1;
|
||||
$output->writeln(' ' . $total . '. <red>' . str_pad($entry, 15) . "</red> <white>{$bin} {$entry} list</white>");
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($plugin === null) {
|
||||
$output->writeln("<red>Grav Plugin <white>'{$name}'</white> is not installed</red>");
|
||||
exit;
|
||||
}
|
||||
|
||||
$path = 'plugins://' . $name . '/cli';
|
||||
|
||||
try {
|
||||
$commands = Folder::all($path, ['compare' => 'Filename', 'pattern' => '/' . $pattern . '$/usm', 'levels' => 1]);
|
||||
} catch (\RuntimeException $e) {
|
||||
$output->writeln("<red>No Console Commands for <white>'{$name}'</white> where found in <white>'{$path}'</white></red>");
|
||||
exit;
|
||||
}
|
||||
|
||||
foreach ($commands as $command_path) {
|
||||
$full_path = $grav['locator']->findResource("plugins://{$name}/cli/{$command_path}");
|
||||
require_once $full_path;
|
||||
|
||||
$command_class = 'Grav\Plugin\Console\\' . preg_replace('/.php$/', '', $command_path);
|
||||
$command = new $command_class();
|
||||
$app->add($command);
|
||||
}
|
||||
|
||||
$app->run($input);
|
||||
$app = new PluginApplication('Grav Plugins Commands', GRAV_VERSION);
|
||||
$app->run();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
actor: Tester
|
||||
bootstrap: _bootstrap.php
|
||||
paths:
|
||||
tests: tests
|
||||
log: tests/_output
|
||||
@@ -6,7 +7,6 @@ paths:
|
||||
support: tests/_support
|
||||
envs: tests/_envs
|
||||
settings:
|
||||
bootstrap: _bootstrap.php
|
||||
colors: true
|
||||
memory_limit: 1024M
|
||||
extensions:
|
||||
|
||||
119
composer.json
119
composer.json
@@ -2,65 +2,81 @@
|
||||
"name": "getgrav/grav",
|
||||
"type": "project",
|
||||
"description": "Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS",
|
||||
"keywords": ["cms","flat-file cms","flat cms","flatfile cms","php"],
|
||||
"keywords": [
|
||||
"cms",
|
||||
"flat-file cms",
|
||||
"flat cms",
|
||||
"flatfile cms",
|
||||
"php"
|
||||
],
|
||||
"homepage": "https://getgrav.org",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=7.1.3",
|
||||
"php": "^7.3.6 || ^8.0",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-dom": "*",
|
||||
"symfony/polyfill-iconv": "^1.9",
|
||||
"symfony/polyfill-php72": "^1.9",
|
||||
"symfony/polyfill-php73": "^1.9",
|
||||
|
||||
"ext-libxml": "*",
|
||||
"symfony/polyfill-mbstring": "~1.20",
|
||||
"symfony/polyfill-iconv": "^1.20",
|
||||
"symfony/polyfill-php74": "^1.20",
|
||||
"symfony/polyfill-php80": "^1.20",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"psr/http-server-middleware": "^1.0",
|
||||
|
||||
"kodus/psr7-server": "*",
|
||||
"nyholm/psr7": "^1.0",
|
||||
|
||||
"twig/twig": "~1.35",
|
||||
"erusev/parsedown": "1.6.4",
|
||||
"erusev/parsedown-extra": "~0.7",
|
||||
"symfony/yaml": "~4.2",
|
||||
"symfony/console": "~4.2",
|
||||
"symfony/event-dispatcher": "~4.2",
|
||||
"symfony/var-dumper": "~4.2",
|
||||
"symfony/process": "~4.2",
|
||||
"doctrine/cache": "^1.8",
|
||||
"doctrine/collections": "^1.5",
|
||||
"guzzlehttp/psr7": "^1.4",
|
||||
"filp/whoops": "~2.2",
|
||||
|
||||
"nyholm/psr7": "^1.3",
|
||||
"twig/twig": "~1.44",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"erusev/parsedown-extra": "~0.8",
|
||||
"symfony/contracts": "~1.1",
|
||||
"symfony/yaml": "~4.4",
|
||||
"symfony/console": "~4.4",
|
||||
"symfony/event-dispatcher": "~4.4",
|
||||
"symfony/var-dumper": "~4.4",
|
||||
"symfony/process": "~4.4",
|
||||
"doctrine/cache": "^1.10",
|
||||
"doctrine/collections": "^1.6",
|
||||
"guzzlehttp/psr7": "^1.7",
|
||||
"filp/whoops": "~2.9",
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"monolog/monolog": "~1.0",
|
||||
"gregwar/image": "2.*",
|
||||
"donatj/phpuseragentparser": "~0.10",
|
||||
"pimple/pimple": "~3.2",
|
||||
"rockettheme/toolbox": "~1.4",
|
||||
"maximebf/debugbar": "~1.15",
|
||||
"league/climate": "^3.4",
|
||||
"monolog/monolog": "~1.25",
|
||||
"gregwar/image": "dev-php8",
|
||||
"gregwar/cache": "dev-php8",
|
||||
"donatj/phpuseragentparser": "~1.1",
|
||||
"pimple/pimple": "~3.3",
|
||||
"rockettheme/toolbox": "~1.5",
|
||||
"maximebf/debugbar": "~1.16",
|
||||
"league/climate": "^3.6",
|
||||
"antoligy/dom-string-iterators": "^1.0",
|
||||
"miljar/php-exif": "^0.6.4",
|
||||
"composer/ca-bundle": "^1.0",
|
||||
"miljar/php-exif": "^0.6",
|
||||
"composer/ca-bundle": "^1.2",
|
||||
"dragonmantank/cron-expression": "^1.2",
|
||||
"phive/twig-extensions-deferred": "^1.0",
|
||||
"willdurand/negotiation": "^2.3"
|
||||
"willdurand/negotiation": "^3.0",
|
||||
"itsgoingd/clockwork": "^5.0",
|
||||
"enshrined/svg-sanitize": "~0.13",
|
||||
"symfony/http-client": "^4.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^2.4",
|
||||
"phpstan/phpstan": "^0.11",
|
||||
"phpstan/phpstan-deprecation-rules": "^0.11.0",
|
||||
"phpunit/php-code-coverage": "~6.0",
|
||||
"fzaninotto/faker": "^1.8",
|
||||
"victorjonsson/markdowndocs": "dev-master"
|
||||
"codeception/codeception": "^4.1",
|
||||
"phpstan/phpstan": "^0.12",
|
||||
"phpstan/phpstan-deprecation-rules": "^0.12",
|
||||
"phpunit/php-code-coverage": "~9.2",
|
||||
"fzaninotto/faker": "^1.9",
|
||||
"victorjonsson/markdowndocs": "dev-master",
|
||||
"codeception/module-asserts": "^1.3",
|
||||
"codeception/module-phpbrowser": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"symfony/polyfill-php72": "*",
|
||||
"symfony/polyfill-php73": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "Recommended for better performance",
|
||||
"ext-iconv": "Recommended for better performance",
|
||||
"ext-zend-opcache": "Recommended for better performance",
|
||||
"ext-intl": "Recommended for multi-language sites",
|
||||
"ext-memcache": "Needed to support Memcache servers",
|
||||
@@ -70,29 +86,42 @@
|
||||
"config": {
|
||||
"apcu-autoloader": true,
|
||||
"platform": {
|
||||
"php": "7.1.3"
|
||||
"php": "7.3.6"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/trilbymedia/PHP-Markdown-Documentation-Generator"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/getgrav/Cache"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/getgrav/Image"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/Grav"
|
||||
},
|
||||
"files": ["system/defines.php"]
|
||||
"files": [
|
||||
"system/defines.php"
|
||||
]
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["VERSION"]
|
||||
"exclude": [
|
||||
"VERSION"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"api-17": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.17.md",
|
||||
"post-create-project-cmd": "bin/grav install",
|
||||
"phpstan": "vendor/bin/phpstan analyse -l 2 -c ./tests/phpstan/phpstan.neon system/src --memory-limit=256M",
|
||||
"phpstan-framework": "vendor/bin/phpstan analyse -l 5 -c ./tests/phpstan/phpstan.neon system/src/Grav/Framework --memory-limit=256M",
|
||||
"test-plugins": "vendor/bin/phpstan analyse -l 0 -c ./tests/phpstan/plugins.neon user/plugins --memory-limit=256M",
|
||||
"phpstan": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src",
|
||||
"phpstan-framework": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src/Grav/Framework system/src/Grav/Events system/src/Grav/Installer",
|
||||
"phpstan-plugins": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/plugins.neon --memory-limit=400M user/plugins",
|
||||
"test": "vendor/bin/codecept run unit",
|
||||
"test-windows": "vendor\\bin\\codecept run unit"
|
||||
},
|
||||
|
||||
4381
composer.lock
generated
4381
composer.lock
generated
File diff suppressed because it is too large
Load Diff
29
index.php
29
index.php
@@ -10,16 +10,30 @@
|
||||
namespace Grav;
|
||||
|
||||
\define('GRAV_REQUEST_TIME', microtime(true));
|
||||
\define('GRAV_PHP_MIN', '7.1.3');
|
||||
\define('GRAV_PHP_MIN', '7.3.6');
|
||||
|
||||
if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) {
|
||||
die(sprintf('You are running PHP %s, but Grav needs at least <strong>PHP %s</strong> to run.', $ver, $req));
|
||||
}
|
||||
|
||||
if (PHP_SAPI === 'cli-server' && !isset($_SERVER['PHP_CLI_ROUTER'])) {
|
||||
die("PHP webserver requires a router to run Grav, please use: <pre>php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php</pre>");
|
||||
if (PHP_SAPI === 'cli-server') {
|
||||
$symfony_server = stripos(getenv('_'), 'symfony') !== false || stripos($_SERVER['SERVER_SOFTWARE'], 'symfony
|
||||
') !== false;
|
||||
if (!isset($_SERVER['PHP_CLI_ROUTER']) && !$symfony_server) {
|
||||
die("PHP webserver requires a router to run Grav, please use: <pre>php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php</pre>");
|
||||
}
|
||||
}
|
||||
|
||||
// Set timezone to default, falls back to system if php.ini not set
|
||||
date_default_timezone_set(@date_default_timezone_get());
|
||||
|
||||
// Set internal encoding.
|
||||
if (!\extension_loaded('mbstring')) {
|
||||
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
|
||||
}
|
||||
@ini_set('default_charset', 'UTF-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
// Ensure vendor libraries exist
|
||||
$autoload = __DIR__ . '/vendor/autoload.php';
|
||||
if (!is_file($autoload)) {
|
||||
@@ -32,15 +46,6 @@ $loader = require $autoload;
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
// Set timezone to default, falls back to system if php.ini not set
|
||||
date_default_timezone_set(@date_default_timezone_get());
|
||||
|
||||
// Set internal encoding if mbstring loaded
|
||||
if (!\extension_loaded('mbstring')) {
|
||||
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
|
||||
}
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
// Get the Grav instance
|
||||
$grav = Grav::instance(
|
||||
array(
|
||||
|
||||
4
now.json
Normal file
4
now.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "*.php", "use": "@now/php" }]
|
||||
}
|
||||
@@ -10,3 +10,7 @@ Disallow: /user/
|
||||
Allow: /user/pages/
|
||||
Allow: /user/themes/
|
||||
Allow: /user/images/
|
||||
Allow: /
|
||||
Allow: *.css$
|
||||
Allow: *.js$
|
||||
Allow: /system/*.js$
|
||||
@@ -1,70 +0,0 @@
|
||||
div.phpdebugbar {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.phpdebugbar pre {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div > * {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div.phpdebugbar-header-right > * {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header, .phpdebugbar a.phpdebugbar-restore-btn {
|
||||
background-image: url(grav.png);
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-restore-btn {
|
||||
width: 13px;
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-tab.phpdebugbar-active {
|
||||
background: #3DB9EC;
|
||||
color: #fff;
|
||||
margin-top: -1px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar {
|
||||
border-top: 1px solid #ddd;
|
||||
padding-left: 5px;
|
||||
padding-right: 2px;
|
||||
padding-top: 2px;
|
||||
background-color: #fafafa !important;
|
||||
width: auto !important;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar input {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar .phpdebugbar-widgets-filter {
|
||||
|
||||
}
|
||||
|
||||
|
||||
.phpdebugbar input[type=text] {
|
||||
padding: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.phpdebugbar dl.phpdebugbar-widgets-varlist, ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
font-family: "DejaVu Sans Mono", Menlo, Monaco, Consolas, Courier, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.phpdebugbar pre, .phpdebugbar code {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
2
system/assets/debugger/clockwork.css
Normal file
2
system/assets/debugger/clockwork.css
Normal file
@@ -0,0 +1,2 @@
|
||||
/** Clockwork Debugger CSS **/
|
||||
.clockwork-badge{position:fixed;z-index:10;bottom:0;left:0;padding:2px 4px;background-color:#eee;border:1px solid #ccc;border-bottom:0;border-left:0;display:flex;align-items:center}.clockwork-badge:hover{width:auto}.clockwork-badge:hover:after{content:'Grav Clockwork debugger enabled. Install Clockwork Browser extension (Chrome or Firefox), open your Developer tools and then select the Clockwork tab.'}.clockwork-badge:after{margin-left:10px;font-family:Monaco,Consolas,"Lucida Console",monospace;font-size:12px;line-height:1.5;color:#666}.clockwork-badge i{display:block;float:left;height:22px;width:22px;min-width:22px;background-size:contain;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==)}
|
||||
3
system/assets/debugger/clockwork.js
Normal file
3
system/assets/debugger/clockwork.js
Normal file
@@ -0,0 +1,3 @@
|
||||
/** Clockwork Debugger JS **/
|
||||
document.addEventListener("DOMContentLoaded",function () {
|
||||
var e=document.createElement("div");e.appendChild(document.createElement("i")),e.className="clockwork-badge",document.body.appendChild(e)});
|
||||
70
system/assets/debugger/phpdebugbar.css
Normal file
70
system/assets/debugger/phpdebugbar.css
Normal file
@@ -0,0 +1,70 @@
|
||||
div.phpdebugbar {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.phpdebugbar pre {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div > * {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header > div.phpdebugbar-header-right > * {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
.phpdebugbar div.phpdebugbar-header, .phpdebugbar a.phpdebugbar-restore-btn {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==);
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-restore-btn {
|
||||
width: 13px;
|
||||
}
|
||||
|
||||
.phpdebugbar a.phpdebugbar-tab.phpdebugbar-active {
|
||||
background: #3DB9EC;
|
||||
color: #fff;
|
||||
margin-top: -1px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar {
|
||||
border-top: 1px solid #ddd;
|
||||
padding-left: 5px;
|
||||
padding-right: 2px;
|
||||
padding-top: 2px;
|
||||
background-color: #fafafa !important;
|
||||
width: auto !important;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar input {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.phpdebugbar .phpdebugbar-widgets-toolbar .phpdebugbar-widgets-filter {
|
||||
|
||||
}
|
||||
|
||||
|
||||
.phpdebugbar input[type=text] {
|
||||
padding: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.phpdebugbar dl.phpdebugbar-widgets-varlist, ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
font-family: "DejaVu Sans Mono", Menlo, Monaco, Consolas, Courier, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.phpdebugbar pre, .phpdebugbar code {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 1.6 KiB |
4
system/assets/jquery/jquery-3.x.min.js
vendored
4
system/assets/jquery/jquery-3.x.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -39,12 +39,13 @@ form:
|
||||
.command:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.COMMAND
|
||||
placeholder: 'cd ~;ls -lah;'
|
||||
placeholder: 'ls'
|
||||
validate:
|
||||
required: true
|
||||
.args:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.EXTRA_ARGUMENTS
|
||||
placeholder: '-lah'
|
||||
.at:
|
||||
type: cron
|
||||
label: PLUGIN_ADMIN.SCHEDULER_RUNAT
|
||||
|
||||
@@ -105,3 +105,15 @@ form:
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
|
||||
sanitize_svg:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.SANITIZE_SVG
|
||||
help: PLUGIN_ADMIN.SANITIZE_SVG_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
default: true
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
@@ -65,7 +65,7 @@ form:
|
||||
|
||||
summary.size:
|
||||
type: text
|
||||
size: x-small
|
||||
size: small
|
||||
append: PLUGIN_ADMIN.CHARACTERS
|
||||
label: PLUGIN_ADMIN.SUMMARY_SIZE
|
||||
help: PLUGIN_ADMIN.SUMMARY_SIZE_HELP
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
8
system/blueprints/flex/accounts.yaml
Normal file
8
system/blueprints/flex/accounts.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
title: Flex User Accounts
|
||||
description: Manage your User Accounts in Flex.
|
||||
type: flex-objects
|
||||
|
||||
# Deprecated in Grav 1.7.0-rc.4: file was renamed to user-accounts.yaml
|
||||
extends@:
|
||||
type: user-accounts
|
||||
context: blueprints://flex
|
||||
17
system/blueprints/flex/configure/compat.yaml
Normal file
17
system/blueprints/flex/configure/compat.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
form:
|
||||
compatibility:
|
||||
type: tab
|
||||
title: Compatibility
|
||||
fields:
|
||||
object.compat.events:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: Admin event compatibility
|
||||
help: Enables onAdminSave and onAdminAfterSave events for plugins
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
212
system/blueprints/flex/pages.yaml
Normal file
212
system/blueprints/flex/pages.yaml
Normal file
@@ -0,0 +1,212 @@
|
||||
title: Pages
|
||||
description: Manage your Grav Pages in Flex.
|
||||
type: flex-objects
|
||||
|
||||
# Extends a page (blueprint gets overridden inside the object)
|
||||
extends@:
|
||||
type: default
|
||||
context: blueprints://pages
|
||||
|
||||
#
|
||||
# HIGHLY SPECIALIZED FLEX TYPE, AVOID USING PAGES AS BASE FOR YOUR OWN TYPE.
|
||||
#
|
||||
|
||||
# Flex configuration
|
||||
config:
|
||||
# Administration Configuration (needs Flex Objects plugin)
|
||||
admin:
|
||||
# Admin router
|
||||
router:
|
||||
path: '/pages'
|
||||
|
||||
# Permissions
|
||||
permissions:
|
||||
# Primary permissions
|
||||
admin.pages:
|
||||
type: crudl
|
||||
label: Pages
|
||||
admin.configuration.pages:
|
||||
type: default
|
||||
label: Pages Configuration
|
||||
|
||||
# Admin menu
|
||||
menu:
|
||||
list:
|
||||
route: '/pages'
|
||||
title: PLUGIN_ADMIN.PAGES
|
||||
icon: fa-file-text
|
||||
authorize: ['admin.pages.list', 'admin.super']
|
||||
priority: 5
|
||||
|
||||
# Admin template type (folder)
|
||||
template: pages
|
||||
|
||||
# Allowed admin actions
|
||||
actions:
|
||||
list: true
|
||||
create: true
|
||||
read: true
|
||||
update: true
|
||||
delete: true
|
||||
|
||||
# List view
|
||||
list:
|
||||
# Fields shown in the list view
|
||||
fields:
|
||||
published:
|
||||
width: 8
|
||||
alias: header.published
|
||||
visible:
|
||||
width: 8
|
||||
field:
|
||||
label: Visible
|
||||
type: toggle
|
||||
menu:
|
||||
link: edit
|
||||
alias: header.menu
|
||||
full_route:
|
||||
field:
|
||||
label: Route
|
||||
type: text
|
||||
link: edit
|
||||
sort:
|
||||
field: key
|
||||
name:
|
||||
width: 8
|
||||
field:
|
||||
label: Type
|
||||
type: text
|
||||
translations:
|
||||
width: 8
|
||||
field:
|
||||
label: Translations
|
||||
type: text
|
||||
# updated_date:
|
||||
# alias: header.update_date
|
||||
|
||||
# Extra options
|
||||
options:
|
||||
# Default number of records for pagination
|
||||
per_page: 20
|
||||
# Default ordering
|
||||
order:
|
||||
by: key
|
||||
dir: asc
|
||||
|
||||
# TODO: not used yet
|
||||
buttons:
|
||||
back:
|
||||
icon: reply
|
||||
title: PLUGIN_ADMIN.BACK
|
||||
add:
|
||||
icon: plus
|
||||
label: PLUGIN_ADMIN.ADD
|
||||
|
||||
edit:
|
||||
title:
|
||||
template: "{% if object.root %}Root <small>( <root> ){% else %}{{ (form.value('header.title') ?? form.value('folder'))|e }} <small>( {{ (object.getRoute().toString(false) ?: '/')|e }} )</small>{% endif %}"
|
||||
|
||||
# TODO: not used yet
|
||||
buttons:
|
||||
back:
|
||||
icon: reply
|
||||
title: PLUGIN_ADMIN.BACK
|
||||
preview:
|
||||
icon: eye
|
||||
title: PLUGIN_ADMIN.PREVIEW
|
||||
add:
|
||||
icon: plus
|
||||
label: PLUGIN_ADMIN.ADD
|
||||
copy:
|
||||
icon: copy
|
||||
label: PLUGIN_ADMIN.COPY
|
||||
move:
|
||||
icon: arrows
|
||||
label: PLUGIN_ADMIN.MOVE
|
||||
delete:
|
||||
icon: close
|
||||
label: PLUGIN_ADMIN.DELETE
|
||||
save:
|
||||
icon: check
|
||||
label: PLUGIN_ADMIN.SAVE
|
||||
|
||||
# Preview View
|
||||
preview:
|
||||
enabled: true
|
||||
|
||||
# Configure view
|
||||
configure:
|
||||
authorize: 'admin.configuration.pages'
|
||||
|
||||
# Site Configuration
|
||||
site:
|
||||
# Hide from flex types
|
||||
hidden: true
|
||||
templates:
|
||||
collection:
|
||||
# Lookup for the template layout files for collections of objects
|
||||
paths:
|
||||
- 'flex/{TYPE}/collection/{LAYOUT}{EXT}'
|
||||
object:
|
||||
# Lookup for the template layout files for objects
|
||||
paths:
|
||||
- 'flex/{TYPE}/object/{LAYOUT}{EXT}'
|
||||
defaults:
|
||||
# Default template {TYPE}; overridden by filename of this blueprint if template folder exists
|
||||
type: pages
|
||||
# Default template {LAYOUT}; can be overridden in render calls (usually Twig in templates)
|
||||
layout: default
|
||||
|
||||
# Default filters for frontend.
|
||||
filter:
|
||||
- withPublished
|
||||
|
||||
# Data Configuration
|
||||
data:
|
||||
object: 'Grav\Common\Flex\Types\Pages\PageObject'
|
||||
collection: 'Grav\Common\Flex\Types\Pages\PageCollection'
|
||||
index: 'Grav\Common\Flex\Types\Pages\PageIndex'
|
||||
storage:
|
||||
class: 'Grav\Common\Flex\Types\Pages\Storage\PageStorage'
|
||||
options:
|
||||
formatter:
|
||||
class: 'Grav\Framework\File\Formatter\MarkdownFormatter'
|
||||
folder: 'page://'
|
||||
# Keep index file in filesystem to speed up lookups
|
||||
indexed: true
|
||||
# Set default ordering of the pages
|
||||
ordering:
|
||||
key: ASC
|
||||
search:
|
||||
# Search options
|
||||
options:
|
||||
contains: 1
|
||||
# Fields to be searched
|
||||
fields:
|
||||
- key
|
||||
- menu
|
||||
- title
|
||||
- name
|
||||
|
||||
blueprints:
|
||||
configure:
|
||||
fields:
|
||||
import@:
|
||||
type: configure/compat
|
||||
context: blueprints://flex
|
||||
|
||||
# Regular form definition
|
||||
form:
|
||||
fields:
|
||||
lang:
|
||||
type: hidden
|
||||
value: ''
|
||||
|
||||
tabs:
|
||||
fields:
|
||||
security:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.SECURITY
|
||||
import@:
|
||||
type: partials/security
|
||||
context: blueprints://pages
|
||||
70
system/blueprints/flex/shared/configure.yaml
Normal file
70
system/blueprints/flex/shared/configure.yaml
Normal file
@@ -0,0 +1,70 @@
|
||||
form:
|
||||
validation: loose
|
||||
|
||||
fields:
|
||||
tabs:
|
||||
type: tabs
|
||||
fields:
|
||||
cache:
|
||||
type: tab
|
||||
title: Caching
|
||||
fields:
|
||||
object.cache.index.enabled:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.FLEX_INDEX_CACHE_ENABLED
|
||||
highlight: 1
|
||||
config-default@: system.flex.cache.index.enabled
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
object.cache.index.lifetime:
|
||||
type: text
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.FLEX_INDEX_CACHE_LIFETIME
|
||||
config-default@: system.flex.cache.index.lifetime
|
||||
validate:
|
||||
type: int
|
||||
|
||||
object.cache.object.enabled:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.FLEX_OBJECT_CACHE_ENABLED
|
||||
highlight: 1
|
||||
config-default@: system.flex.cache.object.enabled
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
object.cache.object.lifetime:
|
||||
type: text
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.FLEX_OBJECT_CACHE_LIFETIME
|
||||
config-default@: system.flex.cache.object.lifetime
|
||||
validate:
|
||||
type: int
|
||||
|
||||
object.cache.render.enabled:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.FLEX_RENDER_CACHE_ENABLED
|
||||
highlight: 1
|
||||
config-default@: system.flex.cache.render.enabled
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
object.cache.render.lifetime:
|
||||
type: text
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.FLEX_RENDER_CACHE_LIFETIME
|
||||
config-default@: system.flex.cache.render.lifetime
|
||||
validate:
|
||||
type: int
|
||||
142
system/blueprints/flex/user-accounts.yaml
Normal file
142
system/blueprints/flex/user-accounts.yaml
Normal file
@@ -0,0 +1,142 @@
|
||||
title: User Accounts
|
||||
description: Manage your User Accounts in Flex.
|
||||
type: flex-objects
|
||||
|
||||
# Extends user account
|
||||
extends@:
|
||||
type: account
|
||||
context: blueprints://user
|
||||
|
||||
#
|
||||
# HIGHLY SPECIALIZED FLEX TYPE, AVOID USING USER ACCOUNTS AS BASE FOR YOUR OWN TYPE.
|
||||
#
|
||||
|
||||
# Flex configuration
|
||||
config:
|
||||
# Administration Configuration (needs Flex Objects plugin)
|
||||
admin:
|
||||
# Admin router
|
||||
router:
|
||||
path: '/accounts/users'
|
||||
actions:
|
||||
configure:
|
||||
path: '/accounts/configure'
|
||||
redirects:
|
||||
'/user': '/accounts/users'
|
||||
'/accounts': '/accounts/users'
|
||||
|
||||
# Permissions
|
||||
permissions:
|
||||
# Primary permissions
|
||||
admin.users:
|
||||
type: crudl
|
||||
label: User Accounts
|
||||
admin.configuration.users:
|
||||
type: default
|
||||
label: Accounts Configuration
|
||||
|
||||
# Admin menu
|
||||
menu:
|
||||
base:
|
||||
location: '/accounts'
|
||||
route: '/accounts/users'
|
||||
index: 0
|
||||
title: PLUGIN_ADMIN.ACCOUNTS
|
||||
icon: fa-users
|
||||
authorize: ['admin.users.list', 'admin.super']
|
||||
priority: 6
|
||||
|
||||
# Admin template type (folder)
|
||||
template: user-accounts
|
||||
|
||||
# List view
|
||||
list:
|
||||
# Fields shown in the list view
|
||||
fields:
|
||||
username:
|
||||
link: edit
|
||||
search: true
|
||||
field:
|
||||
label: PLUGIN_ADMIN.USERNAME
|
||||
email:
|
||||
search: true
|
||||
fullname:
|
||||
search: true
|
||||
# Extra options
|
||||
options:
|
||||
per_page: 20
|
||||
order:
|
||||
by: username
|
||||
dir: asc
|
||||
|
||||
# Edit view
|
||||
edit:
|
||||
title:
|
||||
template: "{{ form.value('fullname') ?? form.value('username') }} <{{ form.value('email') }}>"
|
||||
|
||||
# Configure view
|
||||
configure:
|
||||
hidden: true
|
||||
authorize: 'admin.configuration.users'
|
||||
form: 'accounts'
|
||||
title:
|
||||
template: "{{ 'PLUGIN_ADMIN.ACCOUNTS'|tu }} {{ 'PLUGIN_ADMIN.CONFIGURATION'|tu }}"
|
||||
|
||||
# Site Configuration
|
||||
site:
|
||||
# Hide from flex types
|
||||
hidden: true
|
||||
templates:
|
||||
collection:
|
||||
# Lookup for the template layout files for collections of objects
|
||||
paths:
|
||||
- 'flex/{TYPE}/collection/{LAYOUT}{EXT}'
|
||||
object:
|
||||
# Lookup for the template layout files for objects
|
||||
paths:
|
||||
- 'flex/{TYPE}/object/{LAYOUT}{EXT}'
|
||||
defaults:
|
||||
# Default template {TYPE}; overridden by filename of this blueprint if template folder exists
|
||||
type: user-accounts
|
||||
# Default template {LAYOUT}; can be overridden in render calls (usually Twig in templates)
|
||||
layout: default
|
||||
|
||||
# Data Configuration
|
||||
data:
|
||||
object: 'Grav\Common\Flex\Types\Users\UserObject'
|
||||
collection: 'Grav\Common\Flex\Types\Users\UserCollection'
|
||||
index: 'Grav\Common\Flex\Types\Users\UserIndex'
|
||||
storage:
|
||||
class: 'Grav\Common\Flex\Types\Users\Storage\UserFileStorage'
|
||||
options:
|
||||
formatter:
|
||||
class: 'Grav\Framework\File\Formatter\YamlFormatter'
|
||||
folder: 'account://'
|
||||
pattern: '{FOLDER}/{KEY}{EXT}'
|
||||
indexed: true
|
||||
key: username
|
||||
case_sensitive: false
|
||||
search:
|
||||
options:
|
||||
contains: 1
|
||||
fields:
|
||||
- key
|
||||
- email
|
||||
|
||||
blueprints:
|
||||
configure:
|
||||
fields:
|
||||
import@:
|
||||
type: configure/compat
|
||||
context: blueprints://flex
|
||||
|
||||
# Regular form definition
|
||||
form:
|
||||
fields:
|
||||
username:
|
||||
flex-disabled@: exists
|
||||
disabled: false
|
||||
flex-readonly@: exists
|
||||
readonly: false
|
||||
validate:
|
||||
required: true
|
||||
122
system/blueprints/flex/user-groups.yaml
Normal file
122
system/blueprints/flex/user-groups.yaml
Normal file
@@ -0,0 +1,122 @@
|
||||
title: User Groups
|
||||
description: Manage your User Groups in Flex.
|
||||
type: flex-objects
|
||||
|
||||
# Extends user group
|
||||
extends@:
|
||||
type: group
|
||||
context: blueprints://user
|
||||
|
||||
# Flex configuration
|
||||
config:
|
||||
# Administration Configuration (needs Flex Objects plugin)
|
||||
admin:
|
||||
# Admin router
|
||||
router:
|
||||
path: '/accounts/groups'
|
||||
actions:
|
||||
configure:
|
||||
path: '/accounts/configure'
|
||||
redirects:
|
||||
'/accounts': '/accounts/groups'
|
||||
|
||||
# Permissions
|
||||
permissions:
|
||||
# Primary permissions
|
||||
admin.users:
|
||||
type: crudl
|
||||
label: User Accounts
|
||||
admin.configuration.users:
|
||||
type: default
|
||||
label: Accounts Configuration
|
||||
|
||||
# Admin menu
|
||||
menu:
|
||||
base:
|
||||
location: '/accounts'
|
||||
route: '/accounts/groups'
|
||||
index: 1
|
||||
title: PLUGIN_ADMIN.ACCOUNTS
|
||||
icon: fa-users
|
||||
authorize: ['admin.users.list', 'admin.super']
|
||||
priority: 6
|
||||
|
||||
# Admin template type (folder)
|
||||
template: user-groups
|
||||
|
||||
# List view
|
||||
list:
|
||||
# Fields shown in the list view
|
||||
fields:
|
||||
groupname:
|
||||
link: edit
|
||||
search: true
|
||||
readableName:
|
||||
search: true
|
||||
description:
|
||||
search: true
|
||||
# Extra options
|
||||
options:
|
||||
per_page: 20
|
||||
order:
|
||||
by: groupname
|
||||
dir: asc
|
||||
|
||||
# Edit view
|
||||
edit:
|
||||
title:
|
||||
template: "{{ form.value('readableName') ?? form.value('groupname') }}"
|
||||
|
||||
# Configure view
|
||||
configure:
|
||||
hidden: true
|
||||
authorize: 'admin.configuration.users'
|
||||
form: 'accounts'
|
||||
title:
|
||||
template: "{{ 'PLUGIN_ADMIN.ACCOUNTS'|tu }} {{ 'PLUGIN_ADMIN.CONFIGURATION'|tu }}"
|
||||
|
||||
# Site Configuration
|
||||
site:
|
||||
# Hide from flex types
|
||||
hidden: true
|
||||
templates:
|
||||
collection:
|
||||
# Lookup for the template layout files for collections of objects
|
||||
paths:
|
||||
- 'flex/{TYPE}/collection/{LAYOUT}{EXT}'
|
||||
object:
|
||||
# Lookup for the template layout files for objects
|
||||
paths:
|
||||
- 'flex/{TYPE}/object/{LAYOUT}{EXT}'
|
||||
defaults:
|
||||
# Default template {TYPE}; overridden by filename of this blueprint if template folder exists
|
||||
type: user-groups
|
||||
# Default template {LAYOUT}; can be overridden in render calls (usually Twig in templates)
|
||||
layout: default
|
||||
|
||||
# Data Configuration
|
||||
data:
|
||||
object: 'Grav\Common\Flex\Types\UserGroups\UserGroupObject'
|
||||
collection: 'Grav\Common\Flex\Types\UserGroups\UserGroupCollection'
|
||||
index: 'Grav\Common\Flex\Types\UserGroups\UserGroupIndex'
|
||||
storage:
|
||||
class: 'Grav\Framework\Flex\Storage\SimpleStorage'
|
||||
options:
|
||||
formatter:
|
||||
class: 'Grav\Framework\File\Formatter\YamlFormatter'
|
||||
folder: 'user://config/groups.yaml'
|
||||
key: groupname
|
||||
search:
|
||||
options:
|
||||
contains: 1
|
||||
fields:
|
||||
- key
|
||||
- groupname
|
||||
- description
|
||||
|
||||
blueprints:
|
||||
configure:
|
||||
fields:
|
||||
import@:
|
||||
type: configure/compat
|
||||
context: blueprints://flex
|
||||
@@ -1,4 +1,4 @@
|
||||
title: PLUGIN_ADMIN.MODULAR
|
||||
title: PLUGIN_ADMIN.MODULE
|
||||
extends@: default
|
||||
|
||||
form:
|
||||
@@ -13,7 +13,7 @@ form:
|
||||
|
||||
modular_title:
|
||||
type: spacer
|
||||
title: PLUGIN_ADMIN.MODULAR_SETUP
|
||||
title: PLUGIN_ADMIN.MODULE_SETUP
|
||||
|
||||
header.content.items:
|
||||
type: text
|
||||
@@ -34,5 +34,3 @@ form:
|
||||
help: '"desc" or "asc" are valid values'
|
||||
placeholder: desc
|
||||
size: small
|
||||
|
||||
|
||||
|
||||
71
system/blueprints/pages/partials/security.yaml
Normal file
71
system/blueprints/pages/partials/security.yaml
Normal file
@@ -0,0 +1,71 @@
|
||||
form:
|
||||
fields:
|
||||
_site:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.PAGE_ACCESS
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
|
||||
header.login.visibility_requires_access:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.PAGE_VISIBILITY_REQUIRES_ACCESS
|
||||
help: PLUGIN_ADMIN.PAGE_VISIBILITY_REQUIRES_ACCESS_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
|
||||
header.access:
|
||||
type: acl_picker
|
||||
label: PLUGIN_ADMIN.PAGE_ACCESS
|
||||
help: PLUGIN_ADMIN.PAGE_ACCESS_HELP
|
||||
ignore_empty: true
|
||||
data_type: access
|
||||
validate:
|
||||
type: array
|
||||
value_type: bool
|
||||
|
||||
_admin:
|
||||
security@: {or: [admin.super, admin.configuration.pages]}
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.PAGE PERMISSIONS
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
|
||||
header.permissions.inherit:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.PAGE_INHERIT_PERMISSIONS
|
||||
help: PLUGIN_ADMIN.PAGE_INHERIT_PERMISSIONS_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
header.permissions.authors:
|
||||
type: list
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.PAGE_AUTHORS
|
||||
help: PLUGIN_ADMIN.PAGE_AUTHORS_HELP
|
||||
|
||||
fields:
|
||||
value:
|
||||
type: text
|
||||
placeholder: PLUGIN_ADMIN.USERNAME
|
||||
style: vertical
|
||||
|
||||
header.permissions.groups:
|
||||
ignore@: true
|
||||
type: acl_picker
|
||||
label: PLUGIN_ADMIN.PAGE_GROUPS
|
||||
help: PLUGIN_ADMIN.PAGE_GROUPS_HELP
|
||||
ignore_empty: true
|
||||
data_type: permissions
|
||||
16
system/blueprints/pages/root.yaml
Normal file
16
system/blueprints/pages/root.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
title: PLUGIN_ADMIN.ROOT
|
||||
|
||||
rules:
|
||||
slug:
|
||||
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
|
||||
min: 1
|
||||
max: 200
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
|
||||
fields:
|
||||
|
||||
tabs:
|
||||
type: tabs
|
||||
active: 1
|
||||
@@ -67,6 +67,15 @@ form:
|
||||
default: 'en'
|
||||
help: PLUGIN_ADMIN.LANGUAGE_HELP
|
||||
|
||||
content_editor:
|
||||
type: select
|
||||
label: PLUGIN_ADMIN.CONTENT_EDITOR
|
||||
size: medium
|
||||
classes: fancy
|
||||
data-options@: 'Grav\Plugin\Admin\Admin::contentEditor'
|
||||
default: 'default'
|
||||
help: PLUGIN_ADMIN.CONTENT_EDITOR_HELP
|
||||
|
||||
twofa_check:
|
||||
type: conditional
|
||||
condition: config.plugins.admin.twofa_enabled
|
||||
@@ -99,6 +108,8 @@ form:
|
||||
sublabel: PLUGIN_ADMIN.2FA_SECRET_HELP
|
||||
|
||||
|
||||
|
||||
|
||||
security:
|
||||
security@: admin.super
|
||||
title: PLUGIN_ADMIN.ACCESS_LEVELS
|
||||
@@ -121,7 +132,9 @@ form:
|
||||
access:
|
||||
security@: admin.super
|
||||
type: permissions
|
||||
check_authorize: true
|
||||
label: PLUGIN_ADMIN.PERMISSIONS
|
||||
ignore_empty: true
|
||||
validate:
|
||||
type: array
|
||||
value_type: bool
|
||||
|
||||
@@ -12,5 +12,7 @@ form:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.USERNAME
|
||||
help: PLUGIN_ADMIN.USERNAME_HELP
|
||||
unset-disabled@: true
|
||||
unset-readonly@: true
|
||||
validate:
|
||||
required: true
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
title: User Accounts
|
||||
description: User Accounts
|
||||
type: flex-objects
|
||||
|
||||
extends@: 'user/account'
|
||||
|
||||
config:
|
||||
admin:
|
||||
list:
|
||||
fields:
|
||||
username:
|
||||
link: edit
|
||||
search: true
|
||||
email:
|
||||
search: true
|
||||
fullname:
|
||||
search: true
|
||||
options:
|
||||
per_page: 20
|
||||
order:
|
||||
by: username
|
||||
dir: asc
|
||||
|
||||
menu:
|
||||
list:
|
||||
route: '/accounts'
|
||||
title: Accounts
|
||||
icon: fa-users
|
||||
authorize: ['admin.users', 'admin.accounts', 'admin.super']
|
||||
@@ -1,37 +1,55 @@
|
||||
title: Group
|
||||
rules:
|
||||
slug:
|
||||
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
|
||||
min: 1
|
||||
max: 200
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
validation: loose
|
||||
|
||||
fields:
|
||||
spacer:
|
||||
type: spacer
|
||||
text: '<br>'
|
||||
fields:
|
||||
groupname:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_ADMIN.GROUP_NAME
|
||||
flex-disabled@: exists
|
||||
flex-readonly@: exists
|
||||
validate:
|
||||
required: true
|
||||
rule: slug
|
||||
|
||||
groupname:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_ADMIN.NAME
|
||||
disabled: true
|
||||
readonly: true
|
||||
readableName:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_ADMIN.DISPLAY_NAME
|
||||
|
||||
readableName:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_ADMIN_PRO.READABLE_NAME
|
||||
description:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_ADMIN.DESCRIPTION
|
||||
|
||||
description:
|
||||
type: text
|
||||
size: large
|
||||
label: PLUGIN_ADMIN.DESCRIPTION
|
||||
icon:
|
||||
type: text
|
||||
size: small
|
||||
label: PLUGIN_ADMIN.ICON
|
||||
|
||||
icon:
|
||||
type: text
|
||||
size: small
|
||||
label: PLUGIN_ADMIN_PRO.ICON
|
||||
enabled:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.ENABLED
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
access:
|
||||
type: permissions
|
||||
label: PLUGIN_ADMIN.PERMISSIONS
|
||||
ignore_empty: true
|
||||
validate:
|
||||
type: array
|
||||
access:
|
||||
type: permissions
|
||||
check_authorize: false
|
||||
label: PLUGIN_ADMIN.PERMISSIONS
|
||||
ignore_empty: true
|
||||
validate:
|
||||
type: array
|
||||
value_type: bool
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
title: PLUGIN_ADMIN_PRO.ADD_GROUP
|
||||
|
||||
rules:
|
||||
slug:
|
||||
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
|
||||
min: 1
|
||||
max: 200
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
fields:
|
||||
@@ -14,3 +20,4 @@ form:
|
||||
help: PLUGIN_ADMIN_PRO.GROUP_NAME_HELP
|
||||
validate:
|
||||
required: true
|
||||
rule: slug
|
||||
|
||||
@@ -24,6 +24,10 @@ types:
|
||||
type: image
|
||||
thumb: media/thumb-png.png
|
||||
mime: image/png
|
||||
webp:
|
||||
type: image
|
||||
thumb: media/thumb-webp.png
|
||||
mime: image/webp
|
||||
gif:
|
||||
type: animated
|
||||
thumb: media/thumb-gif.png
|
||||
@@ -103,7 +107,7 @@ types:
|
||||
docx:
|
||||
type: file
|
||||
thumb: media/thumb-docx.png
|
||||
mime: application/msword
|
||||
mime: application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
xls:
|
||||
type: file
|
||||
thumb: media/thumb-xls.png
|
||||
@@ -111,7 +115,7 @@ types:
|
||||
xlsx:
|
||||
type: file
|
||||
thumb: media/thumb-xlsx.png
|
||||
mime: application/vnd.ms-excel
|
||||
mime: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
ppt:
|
||||
type: file
|
||||
thumb: media/thumb-ppt.png
|
||||
@@ -119,7 +123,7 @@ types:
|
||||
pptx:
|
||||
type: file
|
||||
thumb: media/thumb-pptx.png
|
||||
mime: application/vnd.ms-powerpoint
|
||||
mime: application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||||
pps:
|
||||
type: file
|
||||
thumb: media/thumb-pps.png
|
||||
|
||||
53
system/config/permissions.yaml
Normal file
53
system/config/permissions.yaml
Normal file
@@ -0,0 +1,53 @@
|
||||
actions:
|
||||
site:
|
||||
type: access
|
||||
label: Site
|
||||
admin:
|
||||
type: access
|
||||
label: Admin
|
||||
admin.pages:
|
||||
type: access
|
||||
label: Pages
|
||||
admin.users:
|
||||
type: access
|
||||
label: User Accounts
|
||||
|
||||
types:
|
||||
default:
|
||||
type: access
|
||||
|
||||
crud:
|
||||
type: compact
|
||||
letters:
|
||||
c:
|
||||
action: create
|
||||
label: PLUGIN_ADMIN.CREATE
|
||||
r:
|
||||
action: read
|
||||
label: PLUGIN_ADMIN.READ
|
||||
u:
|
||||
action: update
|
||||
label: PLUGIN_ADMIN.UPDATE
|
||||
d:
|
||||
action: delete
|
||||
label: PLUGIN_ADMIN.DELETE
|
||||
|
||||
crudp:
|
||||
type: crud
|
||||
letters:
|
||||
p:
|
||||
action: publish
|
||||
label: PLUGIN_ADMIN.PUBLISH
|
||||
|
||||
crudl:
|
||||
type: crud
|
||||
letters:
|
||||
l:
|
||||
action: list
|
||||
label: PLUGIN_ADMIN.LIST
|
||||
|
||||
crudpl:
|
||||
type: crud
|
||||
use:
|
||||
- crudp
|
||||
- crudl
|
||||
@@ -36,3 +36,4 @@ uploads_dangerous_extensions:
|
||||
- htm
|
||||
- js
|
||||
- exe
|
||||
sanitize_svg: true
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
schemes:
|
||||
image:
|
||||
type: Stream
|
||||
paths:
|
||||
- user://images
|
||||
- system://images
|
||||
|
||||
page:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user://pages
|
||||
|
||||
account:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user://accounts
|
||||
@@ -10,23 +10,31 @@ custom_base_url: '' # Set the base_url manually, e.
|
||||
username_regex: '^[a-z0-9_-]{3,16}$' # Only lowercase chars, digits, dashes, underscores. 3 - 16 chars
|
||||
pwd_regex: '(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}' # At least one number, one uppercase and lowercase letter, and be at least 8+ chars
|
||||
intl_enabled: true # Special logic for PHP International Extension (mod_intl)
|
||||
http_x_forwarded: # Configuration options for the various HTTP_X_FORWARD headers
|
||||
protocol: true
|
||||
host: false
|
||||
port: true
|
||||
ip: true
|
||||
|
||||
languages:
|
||||
supported: [] # List of languages supported. eg: [en, fr, de]
|
||||
default_lang: # Default is the first supported language. Must be one of the supported languages
|
||||
include_default_lang: true # Include the default lang prefix in all URLs
|
||||
pages_fallback_only: false # Only fallback to find page content through supported languages
|
||||
translations: true # Enable translations by default
|
||||
include_default_lang_file_extension: true # If true, include language code for the default language in file extension: default.en.md
|
||||
translations: true # If false, translation keys are used instead of translated strings
|
||||
translations_fallback: true # Fallback through supported translations if active lang doesn't exist
|
||||
session_store_active: false # Store active language in session
|
||||
http_accept_language: false # Attempt to set the language based on http_accept_language header in the browser
|
||||
override_locale: false # Override the default or system locale with language specific one
|
||||
content_fallback: {} # Custom language fallbacks. eg: {fr: ['fr', 'en']}
|
||||
pages_fallback_only: false # DEPRECATED: Use `content_fallback` instead
|
||||
|
||||
home:
|
||||
alias: '/home' # Default path for home, ie /
|
||||
hide_in_urls: false # Hide the home route in URLs
|
||||
|
||||
pages:
|
||||
type: regular # EXPERIMENTAL: Page type: regular or flex
|
||||
theme: quark # Default theme (defaults to "quark" theme)
|
||||
order:
|
||||
by: default # Order pages by "default", "alpha" or "date"
|
||||
@@ -54,12 +62,18 @@ pages:
|
||||
special_chars: # List of special characters to automatically convert to entities
|
||||
'>': 'gt'
|
||||
'<': 'lt'
|
||||
valid_link_attributes: # Valid attributes to pass through via markdown links
|
||||
- rel
|
||||
- target
|
||||
- id
|
||||
- class
|
||||
- classes
|
||||
types: [html,htm,xml,txt,json,rss,atom] # list of valid page types
|
||||
append_url_extension: '' # Append page's extension in Page urls (e.g. '.html' results in /path/page.html)
|
||||
expires: 604800 # Page expires time in seconds (604800 seconds = 7 days)
|
||||
cache_control: # Can be blank for no setting, or a valid `cache-control` text value
|
||||
last_modified: false # Set the last modified date header based on file modification timestamp
|
||||
etag: false # Set the etag header tag
|
||||
etag: true # Set the etag header tag
|
||||
vary_accept_encoding: false # Add `Vary: Accept-Encoding` header
|
||||
redirect_default_route: false # Automatically redirect to a page's default route
|
||||
redirect_default_code: 302 # Default code to use for redirects
|
||||
@@ -94,7 +108,7 @@ twig:
|
||||
cache: true # Set to true to enable Twig caching
|
||||
debug: true # Enable Twig debug
|
||||
auto_reload: true # Refresh cache on changes
|
||||
autoescape: false # Autoescape Twig vars (DEPRECATED, always enabled in strict mode)
|
||||
autoescape: true # Autoescape Twig vars (DEPRECATED, always enabled in strict mode)
|
||||
undefined_functions: true # Allow undefined functions
|
||||
undefined_filters: true # Allow undefined filters
|
||||
umask_fix: false # By default Twig creates cached files as 755, fix switches this to 775
|
||||
@@ -125,6 +139,8 @@ log:
|
||||
|
||||
debugger:
|
||||
enabled: false # Enable Grav debugger and following settings
|
||||
provider: clockwork # Debugger provider: debugbar | clockwork
|
||||
censored: false # Censor potentially sensitive information (POST parameters, cookies, files, configuration and most array/object data in log messages)
|
||||
shutdown:
|
||||
close_connection: true # Close the connection before calling onShutdown(). false for debugging
|
||||
|
||||
@@ -133,8 +149,10 @@ images:
|
||||
cache_all: false # Cache all image by default
|
||||
cache_perms: '0755' # MUST BE IN QUOTES!! Default cache folder perms. Usually '0755' or '0775'
|
||||
debug: false # Show an overlay over images indicating the pixel depth of the image when working with retina for example
|
||||
auto_fix_orientation: false # Automatically fix the image orientation based on the Exif data
|
||||
auto_fix_orientation: true # Automatically fix the image orientation based on the Exif data
|
||||
seofriendly: false # SEO-friendly processed image names
|
||||
defaults:
|
||||
loading: auto # Let browser pick [auto|lazy|eager]
|
||||
|
||||
media:
|
||||
enable_media_timestamp: false # Enable media timestamps
|
||||
@@ -150,6 +168,7 @@ session:
|
||||
uniqueness: path # Should sessions be `path` based or `security.salt` based
|
||||
secure: false # Set session secure. If true, indicates that communication for this cookie must be over an encrypted transmission. Enable this only on sites that run exclusively on HTTPS
|
||||
httponly: true # Set session HTTP only. If true, indicates that cookies should be used only over HTTP, and JavaScript modification is not allowed.
|
||||
samesite: Lax # Set session SameSite. Possible values are Lax, Strict and None. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
|
||||
split: true # Sessions should be independent between site and plugins (such as admin)
|
||||
path:
|
||||
|
||||
@@ -161,9 +180,22 @@ gpm:
|
||||
official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security
|
||||
|
||||
accounts:
|
||||
type: data # Account type: data or flex
|
||||
storage: file # Flex storage type: file or folder
|
||||
type: regular # EXPERIMENTAL: Account type: regular or flex
|
||||
storage: file # EXPERIMENTAL: Flex storage type: file or folder
|
||||
|
||||
flex:
|
||||
cache:
|
||||
index:
|
||||
enabled: true # Set to true to enable Flex index caching. Is used to cache timestamps in files
|
||||
lifetime: 60 # Lifetime of cached index in seconds (0 = infinite)
|
||||
object:
|
||||
enabled: true # Set to true to enable Flex object caching. Is used to cache object data
|
||||
lifetime: 600 # Lifetime of cached objects in seconds (0 = infinite)
|
||||
render:
|
||||
enabled: true # Set to true to enable Flex render caching. Is used to cache rendered output
|
||||
lifetime: 600 # Lifetime of cached HTML in seconds (0 = infinite)
|
||||
|
||||
strict_mode:
|
||||
yaml_compat: true # Grav 1.5+: Enables YAML backwards compatibility
|
||||
twig_compat: true # Grav 1.5+: Enables deprecated Twig autoescape setting (autoescape: false)
|
||||
yaml_compat: false # Set to true to enable YAML backwards compatibility
|
||||
twig_compat: false # Set to true to enable deprecated Twig settings (autoescape: false)
|
||||
blueprint_compat: false # Set to true to enable backward compatible strict support for blueprints
|
||||
|
||||
@@ -2,28 +2,58 @@
|
||||
/**
|
||||
* @package Grav\Core
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '1.6.8');
|
||||
define('GRAV_VERSION', '1.7.3');
|
||||
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
|
||||
define('GRAV_TESTING', false);
|
||||
define('DS', '/');
|
||||
|
||||
// PHP minimum requirement
|
||||
if (!defined('GRAV_PHP_MIN')) {
|
||||
define('GRAV_PHP_MIN', '7.1.3');
|
||||
define('GRAV_PHP_MIN', '7.3.6');
|
||||
}
|
||||
|
||||
// Directory separator
|
||||
if (!defined('DS')) {
|
||||
define('DS', '/');
|
||||
}
|
||||
|
||||
// Directories and Paths
|
||||
if (!defined('GRAV_ROOT')) {
|
||||
define('GRAV_ROOT', str_replace(DIRECTORY_SEPARATOR, DS, getcwd()));
|
||||
$path = rtrim(str_replace(DIRECTORY_SEPARATOR, DS, getenv('GRAV_ROOT') ?: getcwd()), DS);
|
||||
define('GRAV_ROOT', $path);
|
||||
}
|
||||
define('ROOT_DIR', GRAV_ROOT . '/');
|
||||
define('USER_PATH', 'user/');
|
||||
if (!defined('GRAV_USER_PATH')) {
|
||||
$path = rtrim(getenv('GRAV_USER_PATH') ?: 'user', DS);
|
||||
define('GRAV_USER_PATH', $path);
|
||||
}
|
||||
if (!defined('GRAV_CACHE_PATH')) {
|
||||
$path = rtrim(getenv('GRAV_CACHE_PATH') ?: 'cache', DS);
|
||||
define('GRAV_CACHE_PATH', $path);
|
||||
}
|
||||
if (!defined('GRAV_LOG_PATH')) {
|
||||
$path = rtrim(getenv('GRAV_LOG_PATH') ?: 'logs', DS);
|
||||
define('GRAV_LOG_PATH', $path);
|
||||
}
|
||||
if (!defined('GRAV_TMP_PATH')) {
|
||||
$path = rtrim(getenv('GRAV_TMP_PATH') ?: 'tmp', DS);
|
||||
define('GRAV_TMP_PATH', $path);
|
||||
}
|
||||
if (!defined('GRAV_BACKUP_PATH')) {
|
||||
$path = rtrim(getenv('GRAV_BACKUP_PATH') ?: 'backup', DS);
|
||||
define('GRAV_BACKUP_PATH', $path);
|
||||
}
|
||||
unset($path);
|
||||
|
||||
define('USER_PATH', GRAV_USER_PATH . DS);
|
||||
define('CACHE_PATH', GRAV_CACHE_PATH . DS);
|
||||
define('ROOT_DIR', GRAV_ROOT . DS);
|
||||
define('USER_DIR', ROOT_DIR . USER_PATH);
|
||||
define('CACHE_DIR', ROOT_DIR . 'cache/');
|
||||
define('CACHE_DIR', ROOT_DIR . CACHE_PATH);
|
||||
|
||||
// DEPRECATED: Do not use!
|
||||
define('ASSETS_DIR', ROOT_DIR . 'assets/');
|
||||
@@ -36,7 +66,7 @@ define('LIB_DIR', SYSTEM_DIR .'src/');
|
||||
define('PLUGINS_DIR', USER_DIR .'plugins/');
|
||||
define('THEMES_DIR', USER_DIR .'themes/');
|
||||
define('VENDOR_DIR', ROOT_DIR .'vendor/');
|
||||
define('LOG_DIR', ROOT_DIR .'logs/');
|
||||
define('LOG_DIR', ROOT_DIR . GRAV_LOG_PATH . DS);
|
||||
// END DEPRECATED
|
||||
|
||||
// Some extensions
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* @package Grav\Core
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -10,8 +10,6 @@ if (!defined('GRAV_ROOT')) {
|
||||
die();
|
||||
}
|
||||
|
||||
use Grav\Installer\Install;
|
||||
|
||||
require_once __DIR__ . '/src/Grav/Installer/Install.php';
|
||||
|
||||
return Install::instance();
|
||||
return Grav\Installer\Install::instance();
|
||||
|
||||
@@ -125,6 +125,8 @@ GRAV:
|
||||
- 'Freitag'
|
||||
- 'Samstag'
|
||||
- 'Sonntag'
|
||||
YES: 'Ja'
|
||||
NO: 'Nein'
|
||||
CRON:
|
||||
EVERY: jede
|
||||
EVERY_HOUR: jede Stunde
|
||||
|
||||
@@ -94,9 +94,10 @@ GRAV:
|
||||
YR_PLURAL: yrs
|
||||
DEC_PLURAL: decs
|
||||
FORM:
|
||||
VALIDATION_FAIL: <b>Validation failed:</b>
|
||||
INVALID_INPUT: Invalid input in
|
||||
MISSING_REQUIRED_FIELD: Missing required field:
|
||||
VALIDATION_FAIL: '<b>Validation failed:</b>'
|
||||
INVALID_INPUT: 'Invalid input in'
|
||||
MISSING_REQUIRED_FIELD: 'Missing required field:'
|
||||
XSS_ISSUES: "Potential XSS issues detected in '%s' field"
|
||||
MONTHS_OF_THE_YEAR: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
|
||||
DAYS_OF_THE_WEEK: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
|
||||
YES: "Yes"
|
||||
|
||||
@@ -23,7 +23,7 @@ GRAV:
|
||||
BAD_DATE: Fecha errónea
|
||||
AGO: antes
|
||||
FROM_NOW: desde ahora
|
||||
JUST_NOW: justo ahora
|
||||
JUST_NOW: hace un momento
|
||||
SECOND: segundo
|
||||
MINUTE: minuto
|
||||
HOUR: hora
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\npealkiri: %1$s\n---\n\n# Viga: vigane Frontmatter'i\n\nasukoht: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- 'informatsioon'
|
||||
- 'rice'
|
||||
- 'money'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- 'kala'
|
||||
- 'lammas'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'inimesed'
|
||||
'man': 'mees'
|
||||
'child': 'lapsed'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': '.'
|
||||
'first': '.'
|
||||
'second': '.'
|
||||
'third': '.'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Kuupäev määramata
|
||||
BAD_DATE: Vigane kuupäev
|
||||
AGO: tagasi
|
||||
FROM_NOW: praegusest
|
||||
JUST_NOW: just nüüd
|
||||
SECOND: sekund
|
||||
MINUTE: minut
|
||||
HOUR: tundi
|
||||
@@ -60,3 +79,7 @@ GRAV:
|
||||
- 'reede'
|
||||
- 'laupäev'
|
||||
- 'pühapäev'
|
||||
CRON:
|
||||
EVERY: iga
|
||||
EVERY_MONTH: iga kuu
|
||||
TEXT_PERIOD: Iga <b />
|
||||
|
||||
@@ -14,6 +14,8 @@ GRAV:
|
||||
'/sis$/i': 'ses'
|
||||
'/([ti])um$/i': '\1a'
|
||||
'/(buffal|tomat)o$/i': '\1es'
|
||||
'/(bu)s$/i': 'Bus'
|
||||
'/(alias|status)/i': 'alias|status'
|
||||
'/(ax|test)is$/i': '\1s'
|
||||
'/s$/i': 's'
|
||||
'/$/': 's'
|
||||
|
||||
@@ -11,6 +11,8 @@ GRAV:
|
||||
- 'fish'
|
||||
- 'sheep'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Engin dagsetning gefin
|
||||
BAD_DATE: Röng dagsetning
|
||||
AGO: síðan
|
||||
JUST_NOW: í þessu
|
||||
SECOND: sekúndu
|
||||
@@ -45,6 +47,7 @@ GRAV:
|
||||
DEC_PLURAL: árat
|
||||
FORM:
|
||||
VALIDATION_FAIL: <b>Sannvottun mistókst:</b>
|
||||
INVALID_INPUT: Ógilt inntak í
|
||||
MISSING_REQUIRED_FIELD: 'Vantar nauðsynlegan reit:'
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'janúar'
|
||||
@@ -67,3 +70,11 @@ GRAV:
|
||||
- 'Föstudagur'
|
||||
- 'Laugardagur'
|
||||
- 'Sunnudagur'
|
||||
CRON:
|
||||
TEXT_TIME: ' á <b />:<b />'
|
||||
TEXT_DOW: ' á <b />'
|
||||
TEXT_MONTH: ' af <b />'
|
||||
TEXT_DOM: ' á <b />'
|
||||
ERROR1: Merkið %s er ekki stutt!
|
||||
ERROR3: Það ætti að setja jquery_element inn í stillingar jqCron
|
||||
ERROR4: Óþekkt segð
|
||||
|
||||
@@ -35,3 +35,12 @@ GRAV:
|
||||
- 'Outubro'
|
||||
- 'Novembro'
|
||||
- 'Dezembro'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- 'information'
|
||||
- 'arroz'
|
||||
- 'money'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- 'fish'
|
||||
- 'sheep'
|
||||
|
||||
@@ -1,10 +1,75 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Chyba: Chybný frontmatter\n\nPath: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_PLURALS:
|
||||
'/(quiz)$/i': '\1zes'
|
||||
'/^(ox)$/i': '\1en'
|
||||
'/([m|l])ouse$/i': '\1ice'
|
||||
'/(matr|vert|ind)ix|ex$/i': '\1ices'
|
||||
'/(x|ch|ss|sh)$/i': '\1es'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([^aeiouy]|qu)y$/i': '\1ies'
|
||||
'/(hive)$/i': '\1s'
|
||||
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
|
||||
'/sis$/i': 'ses'
|
||||
'/([ti])um$/i': '\1a'
|
||||
'/(buffal|tomat)o$/i': '\1oes'
|
||||
'/(bu)s$/i': '\1ses'
|
||||
'/(alias|status)/i': '\1es'
|
||||
'/(octop|vir)us$/i': '\1i'
|
||||
'/(ax|test)is$/i': '\1es'
|
||||
'/s$/i': 's'
|
||||
'/$/': 's'
|
||||
INFLECTOR_SINGULAR:
|
||||
'/(quiz)zes$/i': '\1'
|
||||
'/(matr)ices$/i': '\1ix'
|
||||
'/(vert|ind)ices$/i': '\1ex'
|
||||
'/^(ox)en/i': '\1'
|
||||
'/(alias|status)es$/i': '\1'
|
||||
'/([octop|vir])i$/i': '\1us'
|
||||
'/(cris|ax|test)es$/i': '\1is'
|
||||
'/(shoe)s$/i': '\1'
|
||||
'/(o)es$/i': '\1'
|
||||
'/(bus)es$/i': '\1'
|
||||
'/([m|l])ice$/i': '\1ouse'
|
||||
'/(x|ch|ss|sh)es$/i': '\1'
|
||||
'/(m)ovies$/i': '\1ovie'
|
||||
'/(s)eries$/i': '\1eries'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([lr])ves$/i': '\1f'
|
||||
'/(tive)s$/i': '\1'
|
||||
'/(hive)s$/i': '\1'
|
||||
'/([^f])ves$/i': '\1fe'
|
||||
'/(^analy)ses$/i': '\1sis'
|
||||
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
|
||||
'/([ti])a$/i': '\1um'
|
||||
'/(n)ews$/i': '\1ews'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'vybavenie'
|
||||
- 'informácie'
|
||||
- 'ryža'
|
||||
- 'peniaze'
|
||||
- 'druhy'
|
||||
- 'séria'
|
||||
- 'ryba'
|
||||
- 'ovce'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'ľudia'
|
||||
'man': 'muži'
|
||||
'child': 'deti'
|
||||
'sex': 'pohlavia'
|
||||
'move': 'pohyby'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': '.'
|
||||
'first': '.'
|
||||
'second': '.'
|
||||
'third': '.'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Neposkytnutý žiaden dátum
|
||||
BAD_DATE: Nesprávny dátum
|
||||
AGO: pred
|
||||
FROM_NOW: odteraz
|
||||
JUST_NOW: práve teraz
|
||||
SECOND: sekunda
|
||||
MINUTE: minúta
|
||||
HOUR: hodina
|
||||
@@ -14,10 +79,12 @@ GRAV:
|
||||
YEAR: rok
|
||||
DECADE: desaťročie
|
||||
SEC: sek
|
||||
MIN: min
|
||||
HR: hod
|
||||
WK: t
|
||||
MO: m
|
||||
YR: r
|
||||
DEC: dec
|
||||
SECOND_PLURAL: sekúnd
|
||||
MINUTE_PLURAL: minút
|
||||
HOUR_PLURAL: hodín
|
||||
@@ -58,3 +125,20 @@ GRAV:
|
||||
- 'Piatok'
|
||||
- 'Sobota'
|
||||
- 'Nedeľa'
|
||||
CRON:
|
||||
EVERY: každý
|
||||
EVERY_HOUR: každú hodinu
|
||||
EVERY_MINUTE: každú minútu
|
||||
EVERY_DAY_OF_WEEK: každý deň v týždni
|
||||
EVERY_DAY_OF_MONTH: každý deň v mesiaci
|
||||
EVERY_MONTH: každý mesiac
|
||||
TEXT_PERIOD: Každý <b />
|
||||
TEXT_MINS: ' at <b /> minute(s) past the hour'
|
||||
TEXT_TIME: ' at <b />:<b />'
|
||||
TEXT_DOW: ' on <b />'
|
||||
TEXT_MONTH: ' of <b />'
|
||||
TEXT_DOM: ' on <b />'
|
||||
ERROR1: Tag %s nieje podporovaný!
|
||||
ERROR2: Chybný počet položiek
|
||||
ERROR3: jquery_element musí byť nastavený v nastaveniach pre jqCron
|
||||
ERROR4: Neznámy výraz
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "--- titel: %1$s --- # Fel: Ogiltig Frontmatter-sökväg: `%2$s` **%3$s** ``` %4$s ```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'utrustning'
|
||||
- 'information'
|
||||
- 'ris'
|
||||
- 'pengar'
|
||||
- 'arter'
|
||||
- 'serier'
|
||||
- 'fisk'
|
||||
- 'får'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'personer'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Inget datum har angivits
|
||||
BAD_DATE: Ogiltigt datum
|
||||
|
||||
@@ -1,24 +1,44 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# 錯誤: 不正確的 Frontmatter\n\n路徑: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: 沒有提供日期
|
||||
BAD_DATE: 錯誤日期
|
||||
AGO: 之前
|
||||
FROM_NOW: 之後
|
||||
JUST_NOW: 剛剛
|
||||
SECOND: 秒
|
||||
MINUTE: 分
|
||||
HOUR: 小時
|
||||
DAY: 天
|
||||
WEEK: 週
|
||||
MONTH: 月
|
||||
YEAR: 年
|
||||
DECADE: 十年
|
||||
SEC: 秒
|
||||
MIN: 分
|
||||
HR: 小時
|
||||
WK: 週
|
||||
MO: 月
|
||||
YR: 年
|
||||
DEC: 十年
|
||||
SECOND_PLURAL: 秒
|
||||
MINUTE_PLURAL: 分
|
||||
HOUR_PLURAL: 時
|
||||
DAY_PLURAL: 日
|
||||
WEEK_PLURAL: 周
|
||||
HOUR_PLURAL: 小時
|
||||
DAY_PLURAL: 天
|
||||
WEEK_PLURAL: 週
|
||||
MONTH_PLURAL: 月
|
||||
YEAR_PLURAL: 年
|
||||
DECADE_PLURAL: 十年
|
||||
SEC_PLURAL: 秒
|
||||
MIN_PLURAL: 分
|
||||
HR_PLURAL: 時
|
||||
WK_PLURAL: 周
|
||||
WK_PLURAL: 週
|
||||
MO_PLURAL: 月
|
||||
YR_PLURAL: 年
|
||||
DEC_PLURAL: 十年
|
||||
FORM:
|
||||
MISSING_REQUIRED_FIELD: 遺漏必填欄位:
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- '一月'
|
||||
- '二月'
|
||||
|
||||
@@ -1,57 +1,122 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# 錯誤: 不正確的 Frontmatter\n\n路徑: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
FRONTMATTER_ERROR_PAGE: "---\n标题: %1$s\n---\n\n# 错误:无效参数\n\n位置: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_PLURALS:
|
||||
'/(quiz)$/i': '\1zes'
|
||||
'/^(ox)$/i': '\1en'
|
||||
'/([m|l])ouse$/i': '\1ice'
|
||||
'/(matr|vert|ind)ix|ex$/i': '\1ices'
|
||||
'/(x|ch|ss|sh)$/i': '\1es'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([^aeiouy]|qu)y$/i': '\1ies'
|
||||
'/(hive)$/i': '\1s'
|
||||
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
|
||||
'/sis$/i': 'ses'
|
||||
'/([ti])um$/i': '\1a'
|
||||
'/(buffal|tomat)o$/i': '\1oes'
|
||||
'/(bu)s$/i': '\1ses'
|
||||
'/(alias|status)/i': '\1es'
|
||||
'/(octop|vir)us$/i': '\1i'
|
||||
'/(ax|test)is$/i': '\1es'
|
||||
'/s$/i': 's'
|
||||
'/$/': 's'
|
||||
INFLECTOR_SINGULAR:
|
||||
'/(quiz)zes$/i': '\1'
|
||||
'/(matr)ices$/i': '\1ix'
|
||||
'/(vert|ind)ices$/i': '\1ex'
|
||||
'/^(ox)en/i': '\1'
|
||||
'/(alias|status)es$/i': '\1'
|
||||
'/([octop|vir])i$/i': '\1us'
|
||||
'/(cris|ax|test)es$/i': '\1is'
|
||||
'/(shoe)s$/i': '\1'
|
||||
'/(o)es$/i': '\1'
|
||||
'/(bus)es$/i': '\1'
|
||||
'/([m|l])ice$/i': '\1ouse'
|
||||
'/(x|ch|ss|sh)es$/i': '\1'
|
||||
'/(m)ovies$/i': '\1ovie'
|
||||
'/(s)eries$/i': '\1eries'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1y'
|
||||
'/([lr])ves$/i': '\1f'
|
||||
'/(tive)s$/i': '\1'
|
||||
'/(hive)s$/i': '\1'
|
||||
'/([^f])ves$/i': '\1fe'
|
||||
'/(^analy)ses$/i': '\1sis'
|
||||
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
|
||||
'/([ti])a$/i': '\1um'
|
||||
'/(n)ews$/i': '\1ews'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- '装备'
|
||||
- '信息'
|
||||
- '大米'
|
||||
- '钱'
|
||||
- '物种'
|
||||
- '系列'
|
||||
- '鱼'
|
||||
- '羊'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': '人员'
|
||||
'man': '男人'
|
||||
'child': '儿童'
|
||||
'sex': '性别'
|
||||
'move': '移动'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': 'th'
|
||||
'first': 'st'
|
||||
'second': 'md'
|
||||
'third': 'rd'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: 沒有提供日期
|
||||
BAD_DATE: 錯誤日期
|
||||
AGO: 之前
|
||||
FROM_NOW: 之後
|
||||
JUST_NOW: 剛剛
|
||||
NO_DATE_PROVIDED: 无日期信息
|
||||
BAD_DATE: 无效日期
|
||||
AGO: 前
|
||||
FROM_NOW: 距今
|
||||
JUST_NOW: 刚刚
|
||||
SECOND: 秒
|
||||
MINUTE: 分
|
||||
HOUR: 小時
|
||||
MINUTE: 分钟
|
||||
HOUR: 小时
|
||||
DAY: 天
|
||||
WEEK: 週
|
||||
WEEK: 周
|
||||
MONTH: 月
|
||||
YEAR: 年
|
||||
DECADE: 十年
|
||||
SEC: 秒
|
||||
MIN: 分
|
||||
HR: 小時
|
||||
WK: 週
|
||||
MIN: 分钟
|
||||
HR: 小时
|
||||
WK: 周
|
||||
MO: 月
|
||||
YR: 年
|
||||
DEC: 十年
|
||||
DEC: 年代
|
||||
SECOND_PLURAL: 秒
|
||||
MINUTE_PLURAL: 分
|
||||
HOUR_PLURAL: 小時
|
||||
HOUR_PLURAL: 小时
|
||||
DAY_PLURAL: 天
|
||||
WEEK_PLURAL: 週
|
||||
WEEK_PLURAL: 周
|
||||
MONTH_PLURAL: 月
|
||||
YEAR_PLURAL: 年
|
||||
DECADE_PLURAL: 十年
|
||||
SEC_PLURAL: 秒
|
||||
MIN_PLURAL: 分
|
||||
HR_PLURAL: 時
|
||||
WK_PLURAL: 週
|
||||
HR_PLURAL: 时
|
||||
WK_PLURAL: 周
|
||||
MO_PLURAL: 月
|
||||
YR_PLURAL: 年
|
||||
DEC_PLURAL: 十年
|
||||
DEC_PLURAL: 年代
|
||||
FORM:
|
||||
MISSING_REQUIRED_FIELD: 遺漏必填欄位:
|
||||
VALIDATION_FAIL: <b>验证失败:</b>
|
||||
INVALID_INPUT: 无效输入
|
||||
MISSING_REQUIRED_FIELD: 必填字段缺失:
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- '一月'
|
||||
- '二月'
|
||||
- '三月'
|
||||
- '四月'
|
||||
- '五月'
|
||||
- '六月'
|
||||
- '七月'
|
||||
- '八月'
|
||||
- '九月'
|
||||
- '十月'
|
||||
- '十一月'
|
||||
- '十二月'
|
||||
- '1月'
|
||||
- '2月'
|
||||
- '3月'
|
||||
- '4月'
|
||||
- '5月'
|
||||
- '6月'
|
||||
- '7月'
|
||||
- '8月'
|
||||
- '9月'
|
||||
- '10月'
|
||||
- '11月'
|
||||
- '12月'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- '星期一'
|
||||
- '星期二'
|
||||
@@ -60,4 +125,20 @@ GRAV:
|
||||
- '星期五'
|
||||
- '星期六'
|
||||
- '星期日'
|
||||
|
||||
CRON:
|
||||
EVERY: 每隔
|
||||
EVERY_HOUR: 每小时
|
||||
EVERY_MINUTE: 每分钟
|
||||
EVERY_DAY_OF_WEEK: 一周中的每一天
|
||||
EVERY_DAY_OF_MONTH: 月份中的每一天
|
||||
EVERY_MONTH: 每月
|
||||
TEXT_PERIOD: 所有 <b />
|
||||
TEXT_MINS: ' 在 <b /> 小时过后的分钟'
|
||||
TEXT_TIME: ' 在 <b />:<b />'
|
||||
TEXT_DOW: ' on <b />'
|
||||
TEXT_MONTH: ' of <b />'
|
||||
TEXT_DOM: ' on <b />'
|
||||
ERROR1: 不支持分享类型 %s
|
||||
ERROR2: 无效数字
|
||||
ERROR3: 请在 jqCron 设置中设定 jquery_element
|
||||
ERROR4: 无法识别表达式
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
title: Not Found
|
||||
routable: false
|
||||
notfound: true
|
||||
expires: 0
|
||||
---
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Core
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -17,11 +17,22 @@ if (is_file($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $_SERVER['SCRIPT_N
|
||||
return false;
|
||||
}
|
||||
|
||||
$grav_index = 'index.php';
|
||||
|
||||
/* Check the GRAV_BASEDIR environment variable and use if set */
|
||||
|
||||
$grav_basedir = getenv('GRAV_BASEDIR') ?: '';
|
||||
if ($grav_basedir) {
|
||||
$grav_index = ltrim($grav_basedir, '/') . DIRECTORY_SEPARATOR . $grav_index;
|
||||
$grav_basedir = DIRECTORY_SEPARATOR . trim($grav_basedir, DIRECTORY_SEPARATOR);
|
||||
define('GRAV_ROOT', str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . $grav_basedir);
|
||||
}
|
||||
|
||||
$_SERVER = array_merge($_SERVER, $_ENV);
|
||||
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['SCRIPT_NAME'] = DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['PHP_SELF'] = DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'] . $grav_basedir .DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['SCRIPT_NAME'] = $grav_basedir . DIRECTORY_SEPARATOR . 'index.php';
|
||||
$_SERVER['PHP_SELF'] = $grav_basedir . DIRECTORY_SEPARATOR . 'index.php';
|
||||
|
||||
error_log(sprintf('%s:%d [%d]: %s', $_SERVER['REMOTE_ADDR'], $_SERVER['REMOTE_PORT'], http_response_code(), $_SERVER['REQUEST_URI']), 4);
|
||||
|
||||
require 'index.php';
|
||||
require $grav_index;
|
||||
|
||||
@@ -3,30 +3,41 @@
|
||||
/**
|
||||
* @package Grav\Common
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use Closure;
|
||||
use Grav\Common\Assets\Pipeline;
|
||||
use Grav\Common\Assets\Traits\LegacyAssetsTrait;
|
||||
use Grav\Common\Assets\Traits\TestingAssetsTrait;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Framework\Object\PropertyObject;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use function call_user_func_array;
|
||||
use function count;
|
||||
use function func_get_args;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Class Assets
|
||||
* @package Grav\Common
|
||||
*/
|
||||
class Assets extends PropertyObject
|
||||
{
|
||||
use TestingAssetsTrait;
|
||||
use LegacyAssetsTrait;
|
||||
|
||||
const CSS = 'css';
|
||||
const JS = 'js';
|
||||
const CSS_COLLECTION = 'assets_css';
|
||||
const JS_COLLECTION = 'assets_js';
|
||||
const CSS_TYPE = 'Css';
|
||||
const JS_TYPE = 'Js';
|
||||
const INLINE_CSS_TYPE = 'InlineCss';
|
||||
const INLINE_JS_TYPE = 'InlineJs';
|
||||
const CSS_TYPE = Assets\Css::class;
|
||||
const JS_TYPE = Assets\Js::class;
|
||||
const INLINE_CSS_TYPE = Assets\InlineCss::class;
|
||||
const INLINE_JS_TYPE = Assets\InlineJs::class;
|
||||
|
||||
/** @const Regex to match CSS and JavaScript files */
|
||||
const DEFAULT_REGEX = '/.\.(css|js)$/i';
|
||||
@@ -37,31 +48,55 @@ class Assets extends PropertyObject
|
||||
/** @const Regex to match JavaScript files */
|
||||
const JS_REGEX = '/.\.js$/i';
|
||||
|
||||
/** @var string */
|
||||
protected $assets_dir;
|
||||
/** @var string */
|
||||
protected $assets_url;
|
||||
|
||||
/** @var array */
|
||||
protected $assets_css = [];
|
||||
/** @var array */
|
||||
protected $assets_js = [];
|
||||
|
||||
// Config Options
|
||||
// Following variables come from the configuration:
|
||||
/** @var bool */
|
||||
protected $css_pipeline;
|
||||
/** @var bool */
|
||||
protected $css_pipeline_include_externals;
|
||||
/** @var bool */
|
||||
protected $css_pipeline_before_excludes;
|
||||
/** @var bool */
|
||||
protected $inlinecss_pipeline_include_externals;
|
||||
/** @var bool */
|
||||
protected $inlinecss_pipeline_before_excludes;
|
||||
/** @var bool */
|
||||
protected $js_pipeline;
|
||||
/** @var bool */
|
||||
protected $js_pipeline_include_externals;
|
||||
/** @var bool */
|
||||
protected $js_pipeline_before_excludes;
|
||||
/** @var bool */
|
||||
protected $inlinejs_pipeline_include_externals;
|
||||
/** @var bool */
|
||||
protected $inlinejs_pipeline_before_excludes;
|
||||
/** @var array */
|
||||
protected $pipeline_options = [];
|
||||
|
||||
|
||||
/** @var Closure|string */
|
||||
protected $fetch_command;
|
||||
/** @var string */
|
||||
protected $autoload;
|
||||
/** @var bool */
|
||||
protected $enable_asset_timestamp;
|
||||
/** @var array|null */
|
||||
protected $collections;
|
||||
/** @var string */
|
||||
protected $timestamp;
|
||||
|
||||
|
||||
/**
|
||||
* Initialization called in the Grav lifecycle to initialize the Assets with appropriate configuration
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
@@ -92,7 +127,6 @@ class Assets extends PropertyObject
|
||||
* assets and/or collections that will be automatically added on startup.
|
||||
*
|
||||
* @param array $config Configurable options.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function config(array $config)
|
||||
@@ -119,35 +153,35 @@ class Assets extends PropertyObject
|
||||
* It automatically detects the asset type (JavaScript, CSS or collection).
|
||||
* You may add more than one asset passing an array as argument.
|
||||
*
|
||||
* @param array|string $asset
|
||||
* @param string|string[] $asset
|
||||
* @return $this
|
||||
*/
|
||||
public function add($asset)
|
||||
{
|
||||
$args = \func_get_args();
|
||||
$args = func_get_args();
|
||||
|
||||
// More than one asset
|
||||
if (\is_array($asset)) {
|
||||
if (is_array($asset)) {
|
||||
foreach ($asset as $a) {
|
||||
array_shift($args);
|
||||
$args = array_merge([$a], $args);
|
||||
\call_user_func_array([$this, 'add'], $args);
|
||||
call_user_func_array([$this, 'add'], $args);
|
||||
}
|
||||
} elseif (isset($this->collections[$asset])) {
|
||||
array_shift($args);
|
||||
$args = array_merge([$this->collections[$asset]], $args);
|
||||
\call_user_func_array([$this, 'add'], $args);
|
||||
call_user_func_array([$this, 'add'], $args);
|
||||
} else {
|
||||
// Get extension
|
||||
$extension = pathinfo(parse_url($asset, PHP_URL_PATH), PATHINFO_EXTENSION);
|
||||
|
||||
// JavaScript or CSS
|
||||
if (\strlen($extension) > 0) {
|
||||
if ($extension !== '') {
|
||||
$extension = strtolower($extension);
|
||||
if ($extension === 'css') {
|
||||
\call_user_func_array([$this, 'addCss'], $args);
|
||||
call_user_func_array([$this, 'addCss'], $args);
|
||||
} elseif ($extension === 'js') {
|
||||
\call_user_func_array([$this, 'addJs'], $args);
|
||||
call_user_func_array([$this, 'addJs'], $args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,12 +189,20 @@ class Assets extends PropertyObject
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $collection
|
||||
* @param string $type
|
||||
* @param string|string[] $asset
|
||||
* @param array $options
|
||||
* @return $this
|
||||
*/
|
||||
protected function addType($collection, $type, $asset, $options)
|
||||
{
|
||||
if (\is_array($asset)) {
|
||||
if (is_array($asset)) {
|
||||
foreach ($asset as $a) {
|
||||
$this->addType($collection, $type, $a, $options);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -172,7 +214,7 @@ class Assets extends PropertyObject
|
||||
// If pipeline disabled, set to position if provided, else after
|
||||
if (isset($options['pipeline'])) {
|
||||
if ($options['pipeline'] === false) {
|
||||
$exclude_type = ($type === $this::JS_TYPE || $type === $this::INLINE_JS_TYPE) ? $this::JS_TYPE : $this::CSS_TYPE;
|
||||
$exclude_type = ($type === $this::JS_TYPE || $type === $this::INLINE_JS_TYPE) ? $this::JS : $this::CSS;
|
||||
$excludes = strtolower($exclude_type . '_pipeline_before_excludes');
|
||||
if ($this->{$excludes}) {
|
||||
$default = 'after';
|
||||
@@ -190,11 +232,10 @@ class Assets extends PropertyObject
|
||||
$options['timestamp'] = $this->timestamp;
|
||||
|
||||
// Set order
|
||||
$options['order'] = \count($this->$collection);
|
||||
$options['order'] = count($this->$collection);
|
||||
|
||||
// Create asset of correct type
|
||||
$asset_class = "\\Grav\\Common\\Assets\\{$type}";
|
||||
$asset_object = new $asset_class();
|
||||
$asset_object = new $type();
|
||||
|
||||
// If exists
|
||||
if ($asset_object->init($asset, $options)) {
|
||||
@@ -202,7 +243,6 @@ class Assets extends PropertyObject
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,7 +252,7 @@ class Assets extends PropertyObject
|
||||
*/
|
||||
public function addCss($asset)
|
||||
{
|
||||
return $this->addType(Assets::CSS_COLLECTION,Assets::CSS_TYPE, $asset, $this->unifyLegacyArguments(\func_get_args(), Assets::CSS_TYPE));
|
||||
return $this->addType($this::CSS_COLLECTION, $this::CSS_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::CSS_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,7 +262,7 @@ class Assets extends PropertyObject
|
||||
*/
|
||||
public function addInlineCss($asset)
|
||||
{
|
||||
return $this->addType(Assets::CSS_COLLECTION, Assets::INLINE_CSS_TYPE, $asset, $this->unifyLegacyArguments(\func_get_args(), Assets::INLINE_CSS_TYPE));
|
||||
return $this->addType($this::CSS_COLLECTION, $this::INLINE_CSS_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::INLINE_CSS_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,7 +272,7 @@ class Assets extends PropertyObject
|
||||
*/
|
||||
public function addJs($asset)
|
||||
{
|
||||
return $this->addType(Assets::JS_COLLECTION, Assets::JS_TYPE, $asset, $this->unifyLegacyArguments(\func_get_args(), Assets::JS_TYPE));
|
||||
return $this->addType($this::JS_COLLECTION, $this::JS_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::JS_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,20 +282,19 @@ class Assets extends PropertyObject
|
||||
*/
|
||||
public function addInlineJs($asset)
|
||||
{
|
||||
return $this->addType(Assets::JS_COLLECTION, Assets::INLINE_JS_TYPE, $asset, $this->unifyLegacyArguments(\func_get_args(), Assets::INLINE_JS_TYPE));
|
||||
return $this->addType($this::JS_COLLECTION, $this::INLINE_JS_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::INLINE_JS_TYPE));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add/replace collection.
|
||||
*
|
||||
* @param string $collectionName
|
||||
* @param array $assets
|
||||
* @param string $collectionName
|
||||
* @param array $assets
|
||||
* @param bool $overwrite
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function registerCollection($collectionName, Array $assets, $overwrite = false)
|
||||
public function registerCollection($collectionName, array $assets, $overwrite = false)
|
||||
{
|
||||
if ($overwrite || !isset($this->collections[$collectionName])) {
|
||||
$this->collections[$collectionName] = $assets;
|
||||
@@ -264,26 +303,33 @@ class Assets extends PropertyObject
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $assets
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param bool $sort
|
||||
* @return array|false
|
||||
*/
|
||||
protected function filterAssets($assets, $key, $value, $sort = false)
|
||||
{
|
||||
$results = array_filter($assets, function($asset) use ($key, $value) {
|
||||
$results = array_filter($assets, function ($asset) use ($key, $value) {
|
||||
|
||||
if ($key === 'position' && $value === 'pipeline') {
|
||||
|
||||
$type = $asset->getType();
|
||||
|
||||
if ($asset->getRemote() && $this->{$type . '_pipeline_include_externals'} === false && $asset['position'] === 'pipeline' ) {
|
||||
if ($this->{$type . '_pipeline_before_excludes'}) {
|
||||
if ($asset->getRemote() && $this->{strtolower($type) . '_pipeline_include_externals'} === false && $asset['position'] === 'pipeline') {
|
||||
if ($this->{strtolower($type) . '_pipeline_before_excludes'}) {
|
||||
$asset->setPosition('after');
|
||||
} else {
|
||||
$asset->setPosition('before');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($asset[$key] === $value) return true;
|
||||
if ($asset[$key] === $value) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -295,17 +341,25 @@ class Assets extends PropertyObject
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $assets
|
||||
* @return array
|
||||
*/
|
||||
protected function sortAssets($assets)
|
||||
{
|
||||
uasort ($assets, function($a, $b) {
|
||||
if ($a['priority'] == $b['priority']) {
|
||||
return $a['order'] - $b['order'];
|
||||
}
|
||||
return $b['priority'] - $a['priority'];
|
||||
uasort($assets, static function ($a, $b) {
|
||||
return $b['priority'] <=> $a['priority'] ?: $a['order'] <=> $b['order'];
|
||||
});
|
||||
|
||||
return $assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $group
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function render($type, $group = 'head', $attributes = [])
|
||||
{
|
||||
$before_output = '';
|
||||
@@ -352,7 +406,6 @@ class Assets extends PropertyObject
|
||||
*
|
||||
* @param string $group name of the group
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function css($group = 'head', $attributes = [])
|
||||
@@ -365,7 +418,6 @@ class Assets extends PropertyObject
|
||||
*
|
||||
* @param string $group name of the group
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function js($group = 'head', $attributes = [])
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,12 @@ use Grav\Common\Grav;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Framework\Object\PropertyObject;
|
||||
use SplFileInfo;
|
||||
|
||||
/**
|
||||
* Class BaseAsset
|
||||
* @package Grav\Common\Assets
|
||||
*/
|
||||
abstract class BaseAsset extends PropertyObject
|
||||
{
|
||||
use AssetUtilsTrait;
|
||||
@@ -22,32 +27,46 @@ abstract class BaseAsset extends PropertyObject
|
||||
protected const CSS_ASSET = true;
|
||||
protected const JS_ASSET = false;
|
||||
|
||||
/** @const Regex to match CSS import content */
|
||||
protected const CSS_IMPORT_REGEX = '{@import(.*?);}';
|
||||
|
||||
/** @var string|false */
|
||||
protected $asset;
|
||||
|
||||
/** @var string */
|
||||
protected $asset_type;
|
||||
/** @var int */
|
||||
protected $order;
|
||||
/** @var string */
|
||||
protected $group;
|
||||
/** @var string */
|
||||
protected $position;
|
||||
/** @var int */
|
||||
protected $priority;
|
||||
/** @var array */
|
||||
protected $attributes = [];
|
||||
|
||||
|
||||
/** @var string */
|
||||
protected $timestamp;
|
||||
/** @var int|false */
|
||||
protected $modified;
|
||||
/** @var bool */
|
||||
protected $remote;
|
||||
/** @var string */
|
||||
protected $query = '';
|
||||
|
||||
// Private Bits
|
||||
private $base_url;
|
||||
private $fetch_command;
|
||||
/** @var bool */
|
||||
private $css_rewrite = false;
|
||||
/** @var bool */
|
||||
private $css_minify = false;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract function render();
|
||||
|
||||
/**
|
||||
* BaseAsset constructor.
|
||||
* @param array $elements
|
||||
* @param string|null $key
|
||||
*/
|
||||
public function __construct(array $elements = [], $key = null)
|
||||
{
|
||||
$base_config = [
|
||||
@@ -64,6 +83,11 @@ abstract class BaseAsset extends PropertyObject
|
||||
parent::__construct($elements, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|false $asset
|
||||
* @param array $options
|
||||
* @return $this|false
|
||||
*/
|
||||
public function init($asset, $options)
|
||||
{
|
||||
$config = Grav::instance()['config'];
|
||||
@@ -88,7 +112,6 @@ abstract class BaseAsset extends PropertyObject
|
||||
|
||||
// Move this to render?
|
||||
if (!$this->remote) {
|
||||
|
||||
$asset_parts = parse_url($asset);
|
||||
if (isset($asset_parts['query'])) {
|
||||
$this->query = $asset_parts['query'];
|
||||
@@ -109,7 +132,7 @@ abstract class BaseAsset extends PropertyObject
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = new \SplFileInfo($path);
|
||||
$file = new SplFileInfo($path);
|
||||
|
||||
$asset = $this->buildLocalLink($file->getPathname());
|
||||
|
||||
@@ -122,19 +145,30 @@ abstract class BaseAsset extends PropertyObject
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|false
|
||||
*/
|
||||
public function getAsset()
|
||||
{
|
||||
return $this->asset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getRemote()
|
||||
{
|
||||
return $this->remote;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $position
|
||||
* @return $this
|
||||
*/
|
||||
public function setPosition($position)
|
||||
{
|
||||
$this->position = $position;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -163,7 +197,7 @@ abstract class BaseAsset extends PropertyObject
|
||||
*
|
||||
* @param string $asset the asset string reference
|
||||
*
|
||||
* @return string the final link url to the asset
|
||||
* @return string|false the final link url to the asset
|
||||
*/
|
||||
protected function buildLocalLink($asset)
|
||||
{
|
||||
@@ -190,6 +224,7 @@ abstract class BaseAsset extends PropertyObject
|
||||
* @param string $file
|
||||
* @param string $dir
|
||||
* @param bool $local
|
||||
* @return string
|
||||
*/
|
||||
protected function cssRewrite($file, $dir, $local)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -11,8 +11,17 @@ namespace Grav\Common\Assets;
|
||||
|
||||
use Grav\Common\Utils;
|
||||
|
||||
/**
|
||||
* Class Css
|
||||
* @package Grav\Common\Assets
|
||||
*/
|
||||
class Css extends BaseAsset
|
||||
{
|
||||
/**
|
||||
* Css constructor.
|
||||
* @param array $elements
|
||||
* @param string|null $key
|
||||
*/
|
||||
public function __construct(array $elements = [], $key = null)
|
||||
{
|
||||
$base_options = [
|
||||
@@ -28,10 +37,13 @@ class Css extends BaseAsset
|
||||
parent::__construct($merged_attributes, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
if (isset($this->attributes['loading']) && $this->attributes['loading'] === 'inline') {
|
||||
$buffer = $this->gatherLinks( [$this], self::CSS_ASSET);
|
||||
$buffer = $this->gatherLinks([$this], self::CSS_ASSET);
|
||||
return "<style>\n" . trim($buffer) . "\n</style>\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -11,8 +11,17 @@ namespace Grav\Common\Assets;
|
||||
|
||||
use Grav\Common\Utils;
|
||||
|
||||
/**
|
||||
* Class InlineCss
|
||||
* @package Grav\Common\Assets
|
||||
*/
|
||||
class InlineCss extends BaseAsset
|
||||
{
|
||||
/**
|
||||
* InlineCss constructor.
|
||||
* @param array $elements
|
||||
* @param string|null $key
|
||||
*/
|
||||
public function __construct(array $elements = [], $key = null)
|
||||
{
|
||||
$base_options = [
|
||||
@@ -25,6 +34,9 @@ class InlineCss extends BaseAsset
|
||||
parent::__construct($merged_attributes, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return '<style' . $this->renderAttributes(). ">\n" . trim($this->asset) . "\n</style>\n";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -11,8 +11,17 @@ namespace Grav\Common\Assets;
|
||||
|
||||
use Grav\Common\Utils;
|
||||
|
||||
/**
|
||||
* Class InlineJs
|
||||
* @package Grav\Common\Assets
|
||||
*/
|
||||
class InlineJs extends BaseAsset
|
||||
{
|
||||
/**
|
||||
* InlineJs constructor.
|
||||
* @param array $elements
|
||||
* @param string|null $key
|
||||
*/
|
||||
public function __construct(array $elements = [], $key = null)
|
||||
{
|
||||
$base_options = [
|
||||
@@ -25,6 +34,9 @@ class InlineJs extends BaseAsset
|
||||
parent::__construct($merged_attributes, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return '<script' . $this->renderAttributes(). ">\n" . trim($this->asset) . "\n</script>\n";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -11,8 +11,17 @@ namespace Grav\Common\Assets;
|
||||
|
||||
use Grav\Common\Utils;
|
||||
|
||||
/**
|
||||
* Class Js
|
||||
* @package Grav\Common\Assets
|
||||
*/
|
||||
class Js extends BaseAsset
|
||||
{
|
||||
/**
|
||||
* Js constructor.
|
||||
* @param array $elements
|
||||
* @param string|null $key
|
||||
*/
|
||||
public function __construct(array $elements = [], $key = null)
|
||||
{
|
||||
$base_options = [
|
||||
@@ -24,10 +33,13 @@ class Js extends BaseAsset
|
||||
parent::__construct($merged_attributes, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
if (isset($this->attributes['loading']) && $this->attributes['loading'] === 'inline') {
|
||||
$buffer = $this->gatherLinks( [$this], self::JS_ASSET);
|
||||
$buffer = $this->gatherLinks([$this], self::JS_ASSET);
|
||||
return '<script' . $this->renderAttributes() . ">\n" . trim($buffer) . "\n</script>\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -15,8 +15,15 @@ use Grav\Common\Grav;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Framework\Object\PropertyObject;
|
||||
use MatthiasMullie\Minify\CSS;
|
||||
use MatthiasMullie\Minify\JS;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* Class Pipeline
|
||||
* @package Grav\Common\Assets
|
||||
*/
|
||||
class Pipeline extends PropertyObject
|
||||
{
|
||||
use AssetUtilsTrait;
|
||||
@@ -30,40 +37,42 @@ class Pipeline extends PropertyObject
|
||||
/** @const Regex to match CSS sourcemap comments */
|
||||
protected const CSS_SOURCEMAP_REGEX = '{\/\*# (.*?) \*\/}';
|
||||
|
||||
/** @const Regex to match CSS import content */
|
||||
protected const CSS_IMPORT_REGEX = '{@import(.*?);}';
|
||||
|
||||
protected const FIRST_FORWARDSLASH_REGEX = '{^\/{1}\w}';
|
||||
|
||||
protected $css_minify;
|
||||
protected $css_minify_windows;
|
||||
protected $css_rewrite;
|
||||
// Following variables come from the configuration:
|
||||
/** @var bool */
|
||||
protected $css_minify = false;
|
||||
/** @var bool */
|
||||
protected $css_minify_windows = false;
|
||||
/** @var bool */
|
||||
protected $css_rewrite = false;
|
||||
/** @var bool */
|
||||
protected $css_pipeline_include_externals = true;
|
||||
/** @var bool */
|
||||
protected $js_minify = false;
|
||||
/** @var bool */
|
||||
protected $js_minify_windows = false;
|
||||
/** @var bool */
|
||||
protected $js_pipeline_include_externals = true;
|
||||
|
||||
protected $js_minify;
|
||||
protected $js_minify_windows;
|
||||
|
||||
protected $base_url;
|
||||
/** @var string */
|
||||
protected $assets_dir;
|
||||
/** @var string */
|
||||
protected $assets_url;
|
||||
/** @var string */
|
||||
protected $timestamp;
|
||||
/** @var array */
|
||||
protected $attributes;
|
||||
protected $query;
|
||||
/** @var string */
|
||||
protected $query = '';
|
||||
/** @var string */
|
||||
protected $asset;
|
||||
|
||||
/**
|
||||
* Closure used by the pipeline to fetch assets.
|
||||
*
|
||||
* Useful when file_get_contents() function is not available in your PHP
|
||||
* installation or when you want to apply any kind of preprocessing to
|
||||
* your assets before they get pipelined.
|
||||
*
|
||||
* The closure will receive as the only parameter a string with the path/URL of the asset and
|
||||
* it should return the content of the asset file as a string.
|
||||
*
|
||||
* @var \Closure
|
||||
* Pipeline constructor.
|
||||
* @param array $elements
|
||||
* @param string|null $key
|
||||
*/
|
||||
protected $fetch_command;
|
||||
|
||||
public function __construct(array $elements = [], ?string $key = null)
|
||||
{
|
||||
parent::__construct($elements, $key);
|
||||
@@ -88,7 +97,6 @@ class Pipeline extends PropertyObject
|
||||
* @param array $assets
|
||||
* @param string $group
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return bool|string URL or generated content if available, else false
|
||||
*/
|
||||
public function renderCss($assets, $group, $attributes = [])
|
||||
@@ -125,7 +133,7 @@ class Pipeline extends PropertyObject
|
||||
|
||||
// Minify if required
|
||||
if ($this->shouldMinify('css')) {
|
||||
$minifier = new \MatthiasMullie\Minify\CSS();
|
||||
$minifier = new CSS();
|
||||
$minifier->add($buffer);
|
||||
$buffer = $minifier->minify();
|
||||
}
|
||||
@@ -152,7 +160,6 @@ class Pipeline extends PropertyObject
|
||||
* @param array $assets
|
||||
* @param string $group
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return bool|string URL or generated content if available, else false
|
||||
*/
|
||||
public function renderJs($assets, $group, $attributes = [])
|
||||
@@ -189,7 +196,7 @@ class Pipeline extends PropertyObject
|
||||
|
||||
// Minify if required
|
||||
if ($this->shouldMinify('js')) {
|
||||
$minifier = new \MatthiasMullie\Minify\JS();
|
||||
$minifier = new JS();
|
||||
$minifier->add($buffer);
|
||||
$buffer = $minifier->minify();
|
||||
}
|
||||
@@ -217,8 +224,7 @@ class Pipeline extends PropertyObject
|
||||
* @param string $file the css source file
|
||||
* @param string $dir , $local relative path to the css file
|
||||
* @param bool $local is this a local or remote asset
|
||||
*
|
||||
* @return mixed
|
||||
* @return string
|
||||
*/
|
||||
protected function cssRewrite($file, $dir, $local)
|
||||
{
|
||||
@@ -244,16 +250,16 @@ class Pipeline extends PropertyObject
|
||||
|
||||
$new_url = ($local ? $this->base_url: '') . $old_url;
|
||||
|
||||
$fixed = str_replace($matches[2], $new_url, $matches[0]);
|
||||
|
||||
return $fixed;
|
||||
return str_replace($matches[2], $new_url, $matches[0]);
|
||||
}, $file);
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
private function shouldMinify($type = 'css')
|
||||
{
|
||||
$check = $type . '_minify';
|
||||
|
||||
@@ -3,17 +3,42 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets\Traits
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Assets\Traits;
|
||||
|
||||
use Closure;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use function dirname;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Trait AssetUtilsTrait
|
||||
* @package Grav\Common\Assets\Traits
|
||||
*/
|
||||
trait AssetUtilsTrait
|
||||
{
|
||||
/**
|
||||
* @var Closure|null
|
||||
*
|
||||
* Closure used by the pipeline to fetch assets.
|
||||
*
|
||||
* Useful when file_get_contents() function is not available in your PHP
|
||||
* installation or when you want to apply any kind of preprocessing to
|
||||
* your assets before they get pipelined.
|
||||
*
|
||||
* The closure will receive as the only parameter a string with the path/URL of the asset and
|
||||
* it should return the content of the asset file as a string.
|
||||
*/
|
||||
protected $fetch_command;
|
||||
|
||||
/** @var string */
|
||||
protected $base_url;
|
||||
|
||||
/**
|
||||
* Determine whether a link is local or remote.
|
||||
* Understands both "http://" and "https://" as well as protocol agnostic links "//"
|
||||
@@ -38,7 +63,6 @@ trait AssetUtilsTrait
|
||||
*
|
||||
* @param array $assets
|
||||
* @param bool $css
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function gatherLinks(array $assets, $css = true)
|
||||
@@ -57,7 +81,7 @@ trait AssetUtilsTrait
|
||||
if (0 === strpos($link, '//')) {
|
||||
$link = 'http:' . $link;
|
||||
}
|
||||
$relative_dir = \dirname($relative_path);
|
||||
$relative_dir = dirname($relative_path);
|
||||
} else {
|
||||
// Fix to remove relative dir if grav is in one
|
||||
if (($this->base_url !== '/') && Utils::startsWith($relative_path, $this->base_url)) {
|
||||
@@ -65,11 +89,12 @@ trait AssetUtilsTrait
|
||||
$relative_path = ltrim(preg_replace($base_url, '/', $link, 1), '/');
|
||||
}
|
||||
|
||||
$relative_dir = \dirname($relative_path);
|
||||
$relative_dir = dirname($relative_path);
|
||||
$link = ROOT_DIR . $relative_path;
|
||||
}
|
||||
|
||||
$file = ($this->fetch_command instanceof \Closure) ? @$this->fetch_command->__invoke($link) : @file_get_contents($link);
|
||||
// TODO: looks like this is not being used.
|
||||
$file = $this->fetch_command instanceof Closure ? @$this->fetch_command->__invoke($link) : @file_get_contents($link);
|
||||
|
||||
// No file found, skip it...
|
||||
if ($file === false) {
|
||||
@@ -102,14 +127,15 @@ trait AssetUtilsTrait
|
||||
* Moves @import statements to the top of the file per the CSS specification
|
||||
*
|
||||
* @param string $file the file containing the combined CSS files
|
||||
*
|
||||
* @return string the modified file with any @imports at the top of the file
|
||||
*/
|
||||
protected function moveImports($file)
|
||||
{
|
||||
$regex = '{@import.*?["\']([^"\']+)["\'].*?;}';
|
||||
|
||||
$imports = [];
|
||||
|
||||
$file = (string)preg_replace_callback(self::CSS_IMPORT_REGEX, function ($matches) use (&$imports) {
|
||||
$file = (string)preg_replace_callback($regex, function ($matches) use (&$imports) {
|
||||
$imports[] = $matches[0];
|
||||
|
||||
return '';
|
||||
@@ -133,11 +159,11 @@ trait AssetUtilsTrait
|
||||
if (is_numeric($key)) {
|
||||
$key = $value;
|
||||
}
|
||||
if (\is_array($value)) {
|
||||
if (is_array($value)) {
|
||||
$value = implode(' ', $value);
|
||||
}
|
||||
|
||||
if (\in_array($key, $no_key, true)) {
|
||||
if (in_array($key, $no_key, true)) {
|
||||
$element = htmlentities($value, ENT_QUOTES, 'UTF-8', false);
|
||||
} else {
|
||||
$element = $key . '="' . htmlentities($value, ENT_QUOTES, 'UTF-8', false) . '"';
|
||||
@@ -152,7 +178,7 @@ trait AssetUtilsTrait
|
||||
/**
|
||||
* Render Querystring
|
||||
*
|
||||
* @param string $asset
|
||||
* @param string|null $asset
|
||||
* @return string
|
||||
*/
|
||||
protected function renderQueryString($asset = null)
|
||||
|
||||
@@ -3,17 +3,23 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets\Traits
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Assets\Traits;
|
||||
|
||||
use Grav\Common\Assets;
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
|
||||
/**
|
||||
* Trait LegacyAssetsTrait
|
||||
* @package Grav\Common\Assets\Traits
|
||||
*/
|
||||
trait LegacyAssetsTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* @param array $args
|
||||
* @param string $type
|
||||
@@ -24,39 +30,50 @@ trait LegacyAssetsTrait
|
||||
// First argument is always the asset
|
||||
array_shift($args);
|
||||
|
||||
if (\count($args) === 0) {
|
||||
if (count($args) === 0) {
|
||||
return [];
|
||||
}
|
||||
if (\count($args) === 1 && \is_array($args[0])) {
|
||||
// New options array format
|
||||
if (count($args) === 1 && is_array($args[0])) {
|
||||
return $args[0];
|
||||
}
|
||||
// Handle obscure case where options array is mixed with a priority
|
||||
if (count($args) === 2 && is_array($args[0]) && is_int($args[1])) {
|
||||
$arguments = $args[0];
|
||||
$arguments['priority'] = $args[1];
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case(Assets::INLINE_CSS_TYPE):
|
||||
$defaults = ['priority' => null, 'group' => null];
|
||||
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
|
||||
break;
|
||||
|
||||
case(Assets::JS_TYPE):
|
||||
case (Assets::JS_TYPE):
|
||||
$defaults = ['priority' => null, 'pipeline' => true, 'loading' => null, 'group' => null];
|
||||
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
|
||||
break;
|
||||
|
||||
case(Assets::INLINE_JS_TYPE):
|
||||
case (Assets::INLINE_JS_TYPE):
|
||||
$defaults = ['priority' => null, 'group' => null, 'attributes' => null];
|
||||
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
|
||||
|
||||
// special case to handle old attributes being passed in
|
||||
if (isset($arguments['attributes'])) {
|
||||
$old_attributes = $arguments['attributes'];
|
||||
$arguments = array_merge($arguments, $old_attributes);
|
||||
if (is_array($old_attributes)) {
|
||||
$arguments = array_merge($arguments, $old_attributes);
|
||||
} else {
|
||||
$arguments['type'] = $old_attributes;
|
||||
}
|
||||
}
|
||||
unset($arguments['attributes']);
|
||||
|
||||
break;
|
||||
|
||||
case (Assets::INLINE_CSS_TYPE):
|
||||
$defaults = ['priority' => null, 'group' => null];
|
||||
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
|
||||
break;
|
||||
|
||||
default:
|
||||
case(Assets::CSS_TYPE):
|
||||
case (Assets::CSS_TYPE):
|
||||
$defaults = ['priority' => null, 'pipeline' => true, 'group' => null, 'loading' => null];
|
||||
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
|
||||
}
|
||||
@@ -64,6 +81,11 @@ trait LegacyAssetsTrait
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $args
|
||||
* @param array $defaults
|
||||
* @return array
|
||||
*/
|
||||
protected function createArgumentsFromLegacy(array $args, array $defaults)
|
||||
{
|
||||
// Remove arguments with old default values.
|
||||
@@ -86,8 +108,7 @@ trait LegacyAssetsTrait
|
||||
* @param int $priority
|
||||
* @param bool $pipeline
|
||||
* @param string $group name of the group
|
||||
*
|
||||
* @return \Grav\Common\Assets
|
||||
* @return Assets
|
||||
* @deprecated Please use dynamic method with ['loading' => 'async'].
|
||||
*/
|
||||
public function addAsyncJs($asset, $priority = 10, $pipeline = true, $group = 'head')
|
||||
@@ -104,8 +125,7 @@ trait LegacyAssetsTrait
|
||||
* @param int $priority
|
||||
* @param bool $pipeline
|
||||
* @param string $group name of the group
|
||||
*
|
||||
* @return \Grav\Common\Assets
|
||||
* @return Assets
|
||||
* @deprecated Please use dynamic method with ['loading' => 'defer'].
|
||||
*/
|
||||
public function addDeferJs($asset, $priority = 10, $pipeline = true, $group = 'head')
|
||||
@@ -114,5 +134,4 @@ trait LegacyAssetsTrait
|
||||
|
||||
return $this->addJs($asset, $priority, $pipeline, 'defer', $group);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,21 +3,29 @@
|
||||
/**
|
||||
* @package Grav\Common\Assets\Traits
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Assets\Traits;
|
||||
|
||||
use FilesystemIterator;
|
||||
use Grav\Common\Grav;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use RegexIterator;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Trait TestingAssetsTrait
|
||||
* @package Grav\Common\Assets\Traits
|
||||
*/
|
||||
trait TestingAssetsTrait
|
||||
{
|
||||
/**
|
||||
* Determines if an asset exists as a collection, CSS or JS reference
|
||||
*
|
||||
* @param string $asset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($asset)
|
||||
@@ -39,7 +47,6 @@ trait TestingAssetsTrait
|
||||
* Set the array of collections explicitly
|
||||
*
|
||||
* @param array $collections
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCollection($collections)
|
||||
@@ -54,7 +61,7 @@ trait TestingAssetsTrait
|
||||
* If a $key is provided, it will try to return only that asset
|
||||
* else it will return null
|
||||
*
|
||||
* @param null|string $key the asset key
|
||||
* @param string|null $key the asset key
|
||||
* @return array
|
||||
*/
|
||||
public function getCss($key = null)
|
||||
@@ -73,7 +80,7 @@ trait TestingAssetsTrait
|
||||
* If a $key is provided, it will try to return only that asset
|
||||
* else it will return null
|
||||
*
|
||||
* @param null|string $key the asset key
|
||||
* @param string|null $key the asset key
|
||||
* @return array
|
||||
*/
|
||||
public function getJs($key = null)
|
||||
@@ -91,7 +98,6 @@ trait TestingAssetsTrait
|
||||
* Set the whole array of CSS assets
|
||||
*
|
||||
* @param array $css
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCss($css)
|
||||
@@ -105,7 +111,6 @@ trait TestingAssetsTrait
|
||||
* Set the whole array of JS assets
|
||||
*
|
||||
* @param array $js
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setJs($js)
|
||||
@@ -119,7 +124,6 @@ trait TestingAssetsTrait
|
||||
* Removes an item from the CSS array if set
|
||||
*
|
||||
* @param string $key The asset key
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeCss($key)
|
||||
@@ -136,7 +140,6 @@ trait TestingAssetsTrait
|
||||
* Removes an item from the JS array if set
|
||||
*
|
||||
* @param string $key The asset key
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeJs($key)
|
||||
@@ -153,7 +156,6 @@ trait TestingAssetsTrait
|
||||
* Sets the state of CSS Pipeline
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCssPipeline($value)
|
||||
@@ -167,7 +169,6 @@ trait TestingAssetsTrait
|
||||
* Sets the state of JS Pipeline
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setJsPipeline($value)
|
||||
@@ -230,7 +231,7 @@ trait TestingAssetsTrait
|
||||
* Get the timestamp for assets
|
||||
*
|
||||
* @param bool $include_join
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTimestamp($include_join = true)
|
||||
{
|
||||
@@ -246,7 +247,6 @@ trait TestingAssetsTrait
|
||||
*
|
||||
* @param string $directory Relative to the Grav root path, or a stream identifier
|
||||
* @param string $pattern (regex)
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addDir($directory, $pattern = self::DEFAULT_REGEX)
|
||||
@@ -296,7 +296,6 @@ trait TestingAssetsTrait
|
||||
* Add all JavaScript assets within $directory
|
||||
*
|
||||
* @param string $directory Relative to the Grav root path, or a stream identifier
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addDirJs($directory)
|
||||
@@ -308,7 +307,6 @@ trait TestingAssetsTrait
|
||||
* Add all CSS assets within $directory
|
||||
*
|
||||
* @param string $directory Relative to the Grav root path, or a stream identifier
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addDirCss($directory)
|
||||
@@ -321,15 +319,16 @@ trait TestingAssetsTrait
|
||||
*
|
||||
* @param string $directory
|
||||
* @param string $pattern (regex)
|
||||
* @param string $ltrim Will be trimmed from the left of the file path
|
||||
*
|
||||
* @param string|null $ltrim Will be trimmed from the left of the file path
|
||||
* @return array
|
||||
*/
|
||||
protected function rglob($directory, $pattern, $ltrim = null)
|
||||
{
|
||||
$iterator = new \RegexIterator(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory,
|
||||
\FilesystemIterator::SKIP_DOTS)), $pattern);
|
||||
$offset = \strlen($ltrim);
|
||||
$iterator = new RegexIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(
|
||||
$directory,
|
||||
FilesystemIterator::SKIP_DOTS
|
||||
)), $pattern);
|
||||
$offset = strlen($ltrim);
|
||||
$files = [];
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
@@ -338,6 +337,4 @@ trait TestingAssetsTrait
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
/**
|
||||
* @package Grav\Common\Backup
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Backup;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use FilesystemIterator;
|
||||
use GlobIterator;
|
||||
use Grav\Common\Filesystem\Archiver;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Inflector;
|
||||
@@ -17,81 +21,126 @@ use Grav\Common\Scheduler\Scheduler;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use RocketTheme\Toolbox\File\JsonFile;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RuntimeException;
|
||||
use SplFileInfo;
|
||||
use stdClass;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Class Backups
|
||||
* @package Grav\Common\Backup
|
||||
*/
|
||||
class Backups
|
||||
{
|
||||
protected const BACKUP_FILENAME_REGEXZ = "#(.*)--(\d*).zip#";
|
||||
|
||||
protected const BACKUP_DATE_FORMAT = 'YmdHis';
|
||||
|
||||
/** @var string */
|
||||
protected static $backup_dir;
|
||||
|
||||
protected static $backups = null;
|
||||
/** @var array|null */
|
||||
protected static $backups;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
|
||||
/** @var EventDispatcher $dispatcher */
|
||||
$dispatcher = Grav::instance()['events'];
|
||||
$dispatcher = $grav['events'];
|
||||
$dispatcher->addListener('onSchedulerInitialized', [$this, 'onSchedulerInitialized']);
|
||||
Grav::instance()->fireEvent('onBackupsInitialized', new Event(['backups' => $this]));
|
||||
|
||||
$grav->fireEvent('onBackupsInitialized', new Event(['backups' => $this]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setup()
|
||||
{
|
||||
if (null === static::$backup_dir) {
|
||||
static::$backup_dir = Grav::instance()['locator']->findResource('backup://', true, true);
|
||||
$grav = Grav::instance();
|
||||
static::$backup_dir = $grav['locator']->findResource('backup://', true, true);
|
||||
Folder::create(static::$backup_dir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Event $event
|
||||
* @return void
|
||||
*/
|
||||
public function onSchedulerInitialized(Event $event)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
|
||||
/** @var Scheduler $scheduler */
|
||||
$scheduler = $event['scheduler'];
|
||||
|
||||
/** @var Inflector $inflector */
|
||||
$inflector = Grav::instance()['inflector'];
|
||||
$inflector = $grav['inflector'];
|
||||
|
||||
foreach (static::getBackupProfiles() as $id => $profile) {
|
||||
$at = $profile['schedule_at'];
|
||||
$name = $inflector::hyphenize($profile['name']);
|
||||
$logs = 'logs/backup-' . $name . '.out';
|
||||
/** @var Job $job */
|
||||
$job = $scheduler->addFunction('Grav\Common\Backup\Backups::backup', [$id], $name );
|
||||
$job = $scheduler->addFunction('Grav\Common\Backup\Backups::backup', [$id], $name);
|
||||
$job->at($at);
|
||||
$job->output($logs);
|
||||
$job->backlink('/tools/backups');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $backup
|
||||
* @param string $base_url
|
||||
* @return string
|
||||
*/
|
||||
public function getBackupDownloadUrl($backup, $base_url)
|
||||
{
|
||||
$param_sep = $param_sep = Grav::instance()['config']->get('system.param_sep', ':');
|
||||
$download = urlencode(base64_encode($backup));
|
||||
$url = rtrim(Grav::instance()['uri']->rootUrl(true), '/') . '/' . trim($base_url,
|
||||
'/') . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
|
||||
$download = urlencode(base64_encode(basename($backup)));
|
||||
$url = rtrim(Grav::instance()['uri']->rootUrl(true), '/') . '/' . trim(
|
||||
$base_url,
|
||||
'/'
|
||||
) . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getBackupProfiles()
|
||||
{
|
||||
return Grav::instance()['config']->get('backups.profiles');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getPurgeConfig()
|
||||
{
|
||||
return Grav::instance()['config']->get('backups.purge');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getBackupNames()
|
||||
{
|
||||
return array_column(static::getBackupProfiles(), 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float|int
|
||||
*/
|
||||
public static function getTotalBackupsSize()
|
||||
{
|
||||
$backups = static::getAvailableBackups();
|
||||
@@ -100,24 +149,29 @@ class Backups
|
||||
return $size ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @return array
|
||||
*/
|
||||
public static function getAvailableBackups($force = false)
|
||||
{
|
||||
if ($force || null === static::$backups) {
|
||||
static::$backups = [];
|
||||
$backups_itr = new \GlobIterator(static::$backup_dir . '/*.zip', \FilesystemIterator::KEY_AS_FILENAME);
|
||||
$inflector = Grav::instance()['inflector'];
|
||||
|
||||
$grav = Grav::instance();
|
||||
$backups_itr = new GlobIterator(static::$backup_dir . '/*.zip', FilesystemIterator::KEY_AS_FILENAME);
|
||||
$inflector = $grav['inflector'];
|
||||
$long_date_format = DATE_RFC2822;
|
||||
|
||||
/**
|
||||
* @var string $name
|
||||
* @var \SplFileInfo $file
|
||||
* @var SplFileInfo $file
|
||||
*/
|
||||
foreach ($backups_itr as $name => $file) {
|
||||
|
||||
if (preg_match(static::BACKUP_FILENAME_REGEXZ, $name, $matches)) {
|
||||
$date = \DateTime::createFromFormat(static::BACKUP_DATE_FORMAT, $matches[2]);
|
||||
$date = DateTime::createFromFormat(static::BACKUP_DATE_FORMAT, $matches[2]);
|
||||
$timestamp = $date->getTimestamp();
|
||||
$backup = new \stdClass();
|
||||
$backup = new stdClass();
|
||||
$backup->title = $inflector->titleize($matches[1]);
|
||||
$backup->time = $date;
|
||||
$backup->date = $date->format($long_date_format);
|
||||
@@ -137,28 +191,29 @@ class Backups
|
||||
/**
|
||||
* Backup
|
||||
*
|
||||
* @param int $id
|
||||
* @param int $id
|
||||
* @param callable|null $status
|
||||
*
|
||||
* @return null|string
|
||||
* @return string|null
|
||||
*/
|
||||
public static function backup($id = 0, callable $status = null)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
|
||||
$profiles = static::getBackupProfiles();
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
$locator = $grav['locator'];
|
||||
|
||||
if (isset($profiles[$id])) {
|
||||
$backup = (object) $profiles[$id];
|
||||
} else {
|
||||
throw new \RuntimeException('No backups defined...');
|
||||
throw new RuntimeException('No backups defined...');
|
||||
}
|
||||
|
||||
$name = Grav::instance()['inflector']->underscorize($backup->name);
|
||||
$name = $grav['inflector']->underscorize($backup->name);
|
||||
$date = date(static::BACKUP_DATE_FORMAT, time());
|
||||
$filename = trim($name, '_') . '--' . $date . '.zip';
|
||||
$destination = static::$backup_dir . DS . $filename;
|
||||
$max_execution_time = ini_set('max_execution_time', 600);
|
||||
$max_execution_time = ini_set('max_execution_time', '600');
|
||||
$backup_root = $backup->root;
|
||||
|
||||
if ($locator->isStream($backup_root)) {
|
||||
@@ -168,7 +223,7 @@ class Backups
|
||||
}
|
||||
|
||||
if (!file_exists($backup_root)) {
|
||||
throw new \RuntimeException("Backup location: {$backup_root} does not exist...");
|
||||
throw new RuntimeException("Backup location: {$backup_root} does not exist...");
|
||||
}
|
||||
|
||||
$options = [
|
||||
@@ -176,7 +231,6 @@ class Backups
|
||||
'exclude_paths' => static::convertExclude($backup->exclude_paths ?? ''),
|
||||
];
|
||||
|
||||
/** @var Archiver $archiver */
|
||||
$archiver = Archiver::create('zip');
|
||||
$archiver->setArchive($destination)->setOptions($options)->compress($backup_root, $status)->addEmptyFolders($options['exclude_paths'], $status);
|
||||
|
||||
@@ -195,16 +249,16 @@ class Backups
|
||||
}
|
||||
|
||||
// Log the backup
|
||||
Grav::instance()['log']->notice('Backup Created: ' . $destination);
|
||||
$grav['log']->notice('Backup Created: ' . $destination);
|
||||
|
||||
// Fire Finished event
|
||||
Grav::instance()->fireEvent('onBackupFinished', new Event(['backup' => $destination]));
|
||||
$grav->fireEvent('onBackupFinished', new Event(['backup' => $destination]));
|
||||
|
||||
// Purge anything required
|
||||
static::purge();
|
||||
|
||||
// Log
|
||||
$log = JsonFile::instance(Grav::instance()['locator']->findResource("log://backup.log", true, true));
|
||||
$log = JsonFile::instance($locator->findResource("log://backup.log", true, true));
|
||||
$log->content([
|
||||
'time' => time(),
|
||||
'location' => $destination
|
||||
@@ -214,26 +268,29 @@ class Backups
|
||||
return $destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function purge()
|
||||
{
|
||||
$purge_config = static::getPurgeConfig();
|
||||
$trigger = $purge_config['trigger'];
|
||||
$backups = static::getAvailableBackups(true);
|
||||
|
||||
switch ($trigger)
|
||||
{
|
||||
switch ($trigger) {
|
||||
case 'number':
|
||||
$backups_count = count($backups);
|
||||
if ($backups_count > $purge_config['max_backups_count']) {
|
||||
$last = end($backups);
|
||||
unlink ($last->path);
|
||||
unlink($last->path);
|
||||
static::purge();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'time':
|
||||
$last = end($backups);
|
||||
$now = new \DateTime();
|
||||
$now = new DateTime();
|
||||
$interval = $now->diff($last->time);
|
||||
if ($interval->days > $purge_config['max_backups_time']) {
|
||||
unlink($last->path);
|
||||
@@ -253,9 +310,14 @@ class Backups
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $exclude
|
||||
* @return array
|
||||
*/
|
||||
protected static function convertExclude($exclude)
|
||||
{
|
||||
$lines = preg_split("/[\s,]+/", $exclude);
|
||||
return array_map('trim', $lines, array_fill(0, \count($lines), '/'));
|
||||
|
||||
return array_map('trim', $lines, array_fill(0, count($lines), '/'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,21 @@
|
||||
/**
|
||||
* @package Grav\Common
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function donatj\UserAgent\parse_user_agent;
|
||||
|
||||
/**
|
||||
* Internally uses the PhpUserAgent package https://github.com/donatj/PhpUserAgent
|
||||
*/
|
||||
class Browser
|
||||
{
|
||||
/** @var string[] */
|
||||
protected $useragent = [];
|
||||
|
||||
/**
|
||||
@@ -23,7 +27,7 @@ class Browser
|
||||
{
|
||||
try {
|
||||
$this->useragent = parse_user_agent();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->useragent = parse_user_agent("Mozilla/5.0 (compatible; Unknown;)");
|
||||
}
|
||||
}
|
||||
@@ -108,7 +112,7 @@ class Browser
|
||||
/**
|
||||
* Get the current major version identifier
|
||||
*
|
||||
* @return string the browser major version identifier
|
||||
* @return int the browser major version identifier
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
@@ -135,7 +139,7 @@ class Browser
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if “Do Not Track” is set by browser
|
||||
* @see https://www.w3.org/TR/tracking-dnt/
|
||||
|
||||
@@ -3,19 +3,27 @@
|
||||
/**
|
||||
* @package Grav\Common
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use DirectoryIterator;
|
||||
use \Doctrine\Common\Cache as DoctrineCache;
|
||||
use Exception;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Scheduler\Scheduler;
|
||||
use LogicException;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use function dirname;
|
||||
use function extension_loaded;
|
||||
use function function_exists;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* The GravCache object is used throughout Grav to store and retrieve cached data.
|
||||
@@ -29,36 +37,34 @@ use RocketTheme\Toolbox\Event\EventDispatcher;
|
||||
*/
|
||||
class Cache extends Getters
|
||||
{
|
||||
/**
|
||||
* @var string Cache key.
|
||||
*/
|
||||
/** @var string Cache key. */
|
||||
protected $key;
|
||||
|
||||
/** @var int */
|
||||
protected $lifetime;
|
||||
|
||||
/** @var int */
|
||||
protected $now;
|
||||
|
||||
/** @var Config $config */
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var DoctrineCache\CacheProvider
|
||||
*/
|
||||
/** @var DoctrineCache\CacheProvider */
|
||||
protected $driver;
|
||||
|
||||
/**
|
||||
* @var CacheInterface
|
||||
*/
|
||||
/** @var CacheInterface */
|
||||
protected $simpleCache;
|
||||
|
||||
/** @var string */
|
||||
protected $driver_name;
|
||||
|
||||
/** @var string */
|
||||
protected $driver_setting;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
/** @var bool */
|
||||
protected $enabled;
|
||||
|
||||
/** @var string */
|
||||
protected $cache_dir;
|
||||
|
||||
protected static $standard_remove = [
|
||||
@@ -115,7 +121,6 @@ class Cache extends Getters
|
||||
* Initialization that sets a base key and the driver based on configuration settings
|
||||
*
|
||||
* @param Grav $grav
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init(Grav $grav)
|
||||
@@ -174,7 +179,7 @@ class Cache extends Getters
|
||||
$current = basename($this->cache_dir);
|
||||
$count = 0;
|
||||
|
||||
foreach (new \DirectoryIterator($cache_dir) as $file) {
|
||||
foreach (new DirectoryIterator($cache_dir) as $file) {
|
||||
$dir = $file->getBasename();
|
||||
if ($dir === $current || $file->isDot() || $file->isFile()) {
|
||||
continue;
|
||||
@@ -191,6 +196,7 @@ class Cache extends Getters
|
||||
* Public accessor to set the enabled state of the cache
|
||||
*
|
||||
* @param bool|int $enabled
|
||||
* @return void
|
||||
*/
|
||||
public function setEnabled($enabled)
|
||||
{
|
||||
@@ -260,24 +266,28 @@ class Cache extends Getters
|
||||
case 'memcache':
|
||||
if (extension_loaded('memcache')) {
|
||||
$memcache = new \Memcache();
|
||||
$memcache->connect($this->config->get('system.cache.memcache.server', 'localhost'),
|
||||
$this->config->get('system.cache.memcache.port', 11211));
|
||||
$memcache->connect(
|
||||
$this->config->get('system.cache.memcache.server', 'localhost'),
|
||||
$this->config->get('system.cache.memcache.port', 11211)
|
||||
);
|
||||
$driver = new DoctrineCache\MemcacheCache();
|
||||
$driver->setMemcache($memcache);
|
||||
} else {
|
||||
throw new \LogicException('Memcache PHP extension has not been installed');
|
||||
throw new LogicException('Memcache PHP extension has not been installed');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'memcached':
|
||||
if (extension_loaded('memcached')) {
|
||||
$memcached = new \Memcached();
|
||||
$memcached->addServer($this->config->get('system.cache.memcached.server', 'localhost'),
|
||||
$this->config->get('system.cache.memcached.port', 11211));
|
||||
$memcached->addServer(
|
||||
$this->config->get('system.cache.memcached.server', 'localhost'),
|
||||
$this->config->get('system.cache.memcached.port', 11211)
|
||||
);
|
||||
$driver = new DoctrineCache\MemcachedCache();
|
||||
$driver->setMemcached($memcached);
|
||||
} else {
|
||||
throw new \LogicException('Memcached PHP extension has not been installed');
|
||||
throw new LogicException('Memcached PHP extension has not been installed');
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -290,8 +300,10 @@ class Cache extends Getters
|
||||
if ($socket) {
|
||||
$redis->connect($socket);
|
||||
} else {
|
||||
$redis->connect($this->config->get('system.cache.redis.server', 'localhost'),
|
||||
$this->config->get('system.cache.redis.port', 6379));
|
||||
$redis->connect(
|
||||
$this->config->get('system.cache.redis.server', 'localhost'),
|
||||
$this->config->get('system.cache.redis.port', 6379)
|
||||
);
|
||||
}
|
||||
|
||||
// Authenticate with password if set
|
||||
@@ -302,7 +314,7 @@ class Cache extends Getters
|
||||
$driver = new DoctrineCache\RedisCache();
|
||||
$driver->setRedis($redis);
|
||||
} else {
|
||||
throw new \LogicException('Redis PHP extension has not been installed');
|
||||
throw new LogicException('Redis PHP extension has not been installed');
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -318,8 +330,7 @@ class Cache extends Getters
|
||||
* Gets a cached entry if it exists based on an id. If it does not exist, it returns false
|
||||
*
|
||||
* @param string $id the id of the cached entry
|
||||
*
|
||||
* @return object|bool returns the cached entry, can be any type, or false if doesn't exist
|
||||
* @return mixed|bool returns the cached entry, can be any type, or false if doesn't exist
|
||||
*/
|
||||
public function fetch($id)
|
||||
{
|
||||
@@ -334,8 +345,8 @@ class Cache extends Getters
|
||||
* Stores a new cached entry.
|
||||
*
|
||||
* @param string $id the id of the cached entry
|
||||
* @param array|object $data the data for the cached entry to store
|
||||
* @param int $lifetime the lifetime to store the entry in seconds
|
||||
* @param array|object|int $data the data for the cached entry to store
|
||||
* @param int|null $lifetime the lifetime to store the entry in seconds
|
||||
*/
|
||||
public function save($id, $data, $lifetime = null)
|
||||
{
|
||||
@@ -393,6 +404,8 @@ class Cache extends Getters
|
||||
|
||||
/**
|
||||
* Getter method to get the cache key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getKey()
|
||||
{
|
||||
@@ -401,6 +414,9 @@ class Cache extends Getters
|
||||
|
||||
/**
|
||||
* Setter method to set key (Advanced)
|
||||
*
|
||||
* @param string $key
|
||||
* @return void
|
||||
*/
|
||||
public function setKey($key)
|
||||
{
|
||||
@@ -412,7 +428,6 @@ class Cache extends Getters
|
||||
* Helper method to clear all Grav caches
|
||||
*
|
||||
* @param string $remove standard|all|assets-only|images-only|cache-only
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function clearCache($remove = 'standard')
|
||||
@@ -437,13 +452,15 @@ class Cache extends Getters
|
||||
case 'tmp-only':
|
||||
$remove_paths = self::$tmp_remove;
|
||||
break;
|
||||
case 'invalidate':
|
||||
$remove_paths = [];
|
||||
break;
|
||||
default:
|
||||
if (Grav::instance()['config']->get('system.cache.clear_images_by_default')) {
|
||||
$remove_paths = self::$standard_remove;
|
||||
} else {
|
||||
$remove_paths = self::$standard_remove_no_images;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Delete entries in the doctrine cache if required
|
||||
@@ -456,11 +473,12 @@ class Cache extends Getters
|
||||
Grav::instance()->fireEvent('onBeforeCacheClear', new Event(['remove' => $remove, 'paths' => &$remove_paths]));
|
||||
|
||||
foreach ($remove_paths as $stream) {
|
||||
|
||||
// Convert stream to a real path
|
||||
try {
|
||||
$path = $locator->findResource($stream, true, true);
|
||||
if($path === false) continue;
|
||||
if ($path === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$anything = false;
|
||||
$files = glob($path . '/*');
|
||||
@@ -484,7 +502,7 @@ class Cache extends Getters
|
||||
if ($anything) {
|
||||
$output[] = '<red>Cleared: </red>' . $path . '/*';
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
// stream not found or another error while deleting files.
|
||||
$output[] = '<red>ERROR: </red>' . $e->getMessage();
|
||||
}
|
||||
@@ -507,9 +525,14 @@ class Cache extends Getters
|
||||
@opcache_reset();
|
||||
}
|
||||
|
||||
Grav::instance()->fireEvent('onAfterCacheClear', new Event(['remove' => $remove, 'output' => &$output]));
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function invalidateCache()
|
||||
{
|
||||
$user_config = USER_DIR . 'config/system.yaml';
|
||||
@@ -525,14 +548,13 @@ class Cache extends Getters
|
||||
if (function_exists('opcache_reset')) {
|
||||
@opcache_reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the cache lifetime programmatically
|
||||
*
|
||||
* @param int $future timestamp
|
||||
* @return void
|
||||
*/
|
||||
public function setLifetime($future)
|
||||
{
|
||||
@@ -540,7 +562,7 @@ class Cache extends Getters
|
||||
return;
|
||||
}
|
||||
|
||||
$interval = $future - $this->now;
|
||||
$interval = (int)($future - $this->now);
|
||||
if ($interval > 0 && $interval < $this->getLifetime()) {
|
||||
$this->lifetime = $interval;
|
||||
}
|
||||
@@ -550,12 +572,12 @@ class Cache extends Getters
|
||||
/**
|
||||
* Retrieve the cache lifetime (in seconds)
|
||||
*
|
||||
* @return mixed
|
||||
* @return int
|
||||
*/
|
||||
public function getLifetime()
|
||||
{
|
||||
if ($this->lifetime === null) {
|
||||
$this->lifetime = $this->config->get('system.cache.lifetime') ?: 604800; // 1 week default
|
||||
$this->lifetime = (int)($this->config->get('system.cache.lifetime') ?: 604800); // 1 week default
|
||||
}
|
||||
|
||||
return $this->lifetime;
|
||||
@@ -564,7 +586,7 @@ class Cache extends Getters
|
||||
/**
|
||||
* Returns the current driver name
|
||||
*
|
||||
* @return mixed
|
||||
* @return string
|
||||
*/
|
||||
public function getDriverName()
|
||||
{
|
||||
@@ -574,7 +596,7 @@ class Cache extends Getters
|
||||
/**
|
||||
* Returns the current driver setting
|
||||
*
|
||||
* @return mixed
|
||||
* @return string
|
||||
*/
|
||||
public function getDriverSetting()
|
||||
{
|
||||
@@ -598,20 +620,30 @@ class Cache extends Getters
|
||||
|
||||
/**
|
||||
* Static function to call as a scheduled Job to purge old Doctrine files
|
||||
*
|
||||
* @param bool $echo
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public static function purgeJob()
|
||||
public static function purgeJob($echo = false)
|
||||
{
|
||||
/** @var Cache $cache */
|
||||
$cache = Grav::instance()['cache'];
|
||||
$deleted_folders = $cache->purgeOldCache();
|
||||
$msg = 'Purged ' . $deleted_folders . ' old cache folders...';
|
||||
|
||||
echo 'Purged ' . $deleted_folders . ' old cache folders...';
|
||||
if ($echo) {
|
||||
echo $msg;
|
||||
} else {
|
||||
return $msg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static function to call as a scheduled Job to clear Grav cache
|
||||
*
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
public static function clearJob($type)
|
||||
{
|
||||
@@ -621,6 +653,10 @@ class Cache extends Getters
|
||||
echo strip_tags(implode("\n", $result));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Event $event
|
||||
* @return void
|
||||
*/
|
||||
public function onSchedulerInitialized(Event $event)
|
||||
{
|
||||
/** @var Scheduler $scheduler */
|
||||
@@ -632,7 +668,7 @@ class Cache extends Getters
|
||||
$name = 'cache-purge';
|
||||
$logs = 'logs/' . $name . '.out';
|
||||
|
||||
$job = $scheduler->addFunction('Grav\Common\Cache::purgeJob', [], $name );
|
||||
$job = $scheduler->addFunction('Grav\Common\Cache::purgeJob', [true], $name);
|
||||
$job->at($at);
|
||||
$job->output($logs);
|
||||
$job->backlink('/config/system#caching');
|
||||
@@ -643,12 +679,9 @@ class Cache extends Getters
|
||||
$name = 'cache-clear';
|
||||
$logs = 'logs/' . $name . '.out';
|
||||
|
||||
$job = $scheduler->addFunction('Grav\Common\Cache::clearJob', [$clear_type], $name );
|
||||
$job = $scheduler->addFunction('Grav\Common\Cache::clearJob', [$clear_type], $name);
|
||||
$job->at($at);
|
||||
$job->output($logs);
|
||||
$job->backlink('/config/system#caching');
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,12 +3,18 @@
|
||||
/**
|
||||
* @package Grav\Common
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use function function_exists;
|
||||
|
||||
/**
|
||||
* Class Composer
|
||||
* @package Grav\Common
|
||||
*/
|
||||
class Composer
|
||||
{
|
||||
/** @const Default composer location */
|
||||
@@ -21,12 +27,12 @@ class Composer
|
||||
*/
|
||||
public static function getComposerLocation()
|
||||
{
|
||||
if (!\function_exists('shell_exec') || stripos(PHP_OS, 'win') === 0) {
|
||||
if (!function_exists('shell_exec') || stripos(PHP_OS, 'win') === 0) {
|
||||
return self::DEFAULT_PATH;
|
||||
}
|
||||
|
||||
// check for global composer install
|
||||
$path = trim(shell_exec('command -v composer'));
|
||||
$path = trim((string)shell_exec('command -v composer'));
|
||||
|
||||
// fall back to grav bundled composer
|
||||
if (!$path || !preg_match('/(composer|composer\.phar)$/', $path)) {
|
||||
|
||||
@@ -3,78 +3,70 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Exception;
|
||||
use RocketTheme\Toolbox\File\PhpFile;
|
||||
use RuntimeException;
|
||||
use function get_class;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Class CompiledBase
|
||||
* @package Grav\Common\Config
|
||||
*/
|
||||
abstract class CompiledBase
|
||||
{
|
||||
/**
|
||||
* @var int Version number for the compiled file.
|
||||
*/
|
||||
/** @var int Version number for the compiled file. */
|
||||
public $version = 1;
|
||||
|
||||
/**
|
||||
* @var string Filename (base name) of the compiled configuration.
|
||||
*/
|
||||
/** @var string Filename (base name) of the compiled configuration. */
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var string|bool Configuration checksum.
|
||||
*/
|
||||
/** @var string|bool Configuration checksum. */
|
||||
public $checksum;
|
||||
|
||||
/**
|
||||
* @var string Timestamp of compiled configuration
|
||||
*/
|
||||
public $timestamp;
|
||||
/** @var int Timestamp of compiled configuration */
|
||||
public $timestamp = 0;
|
||||
|
||||
/**
|
||||
* @var string Cache folder to be used.
|
||||
*/
|
||||
/** @var string Cache folder to be used. */
|
||||
protected $cacheFolder;
|
||||
|
||||
/**
|
||||
* @var array List of files to load.
|
||||
*/
|
||||
/** @var array List of files to load. */
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var mixed Configuration object.
|
||||
*/
|
||||
/** @var mixed Configuration object. */
|
||||
protected $object;
|
||||
|
||||
/**
|
||||
* @param string $cacheFolder Cache folder to be used.
|
||||
* @param array $files List of files as returned from ConfigFileFinder class.
|
||||
* @param string $path Base path for the file list.
|
||||
* @throws \BadMethodCallException
|
||||
* @throws BadMethodCallException
|
||||
*/
|
||||
public function __construct($cacheFolder, array $files, $path)
|
||||
{
|
||||
if (!$cacheFolder) {
|
||||
throw new \BadMethodCallException('Cache folder not defined.');
|
||||
throw new BadMethodCallException('Cache folder not defined.');
|
||||
}
|
||||
|
||||
$this->path = $path ? rtrim($path, '\\/') . '/' : '';
|
||||
$this->cacheFolder = $cacheFolder;
|
||||
$this->files = $files;
|
||||
$this->timestamp = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filename for the compiled PHP file.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|null $name
|
||||
* @return $this
|
||||
*/
|
||||
public function name($name = null)
|
||||
@@ -88,8 +80,12 @@ abstract class CompiledBase
|
||||
|
||||
/**
|
||||
* Function gets called when cached configuration is saved.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function modified() {}
|
||||
public function modified()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timestamp of compiled configuration
|
||||
@@ -136,6 +132,9 @@ abstract class CompiledBase
|
||||
return $this->checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function createFilename()
|
||||
{
|
||||
return "{$this->cacheFolder}/{$this->name()->name}.php";
|
||||
@@ -145,11 +144,14 @@ abstract class CompiledBase
|
||||
* Create configuration object.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
abstract protected function createObject(array $data = []);
|
||||
|
||||
/**
|
||||
* Finalize configuration object.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract protected function finalizeObject();
|
||||
|
||||
@@ -157,7 +159,8 @@ abstract class CompiledBase
|
||||
* Load single configuration file and append it to the correct position.
|
||||
*
|
||||
* @param string $name Name of the position.
|
||||
* @param string $filename File to be loaded.
|
||||
* @param string|string[] $filename File(s) to be loaded.
|
||||
* @return void
|
||||
*/
|
||||
abstract protected function loadFile($name, $filename);
|
||||
|
||||
@@ -197,10 +200,9 @@ abstract class CompiledBase
|
||||
}
|
||||
|
||||
$cache = include $filename;
|
||||
if (
|
||||
!\is_array($cache)
|
||||
if (!is_array($cache)
|
||||
|| !isset($cache['checksum'], $cache['data'], $cache['@class'])
|
||||
|| $cache['@class'] !== \get_class($this)
|
||||
|| $cache['@class'] !== get_class($this)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -222,7 +224,8 @@ abstract class CompiledBase
|
||||
* Save compiled file.
|
||||
*
|
||||
* @param string $filename
|
||||
* @throws \RuntimeException
|
||||
* @return void
|
||||
* @throws RuntimeException
|
||||
* @internal
|
||||
*/
|
||||
protected function saveCompiledFile($filename)
|
||||
@@ -232,7 +235,7 @@ abstract class CompiledBase
|
||||
// Attempt to lock the file for writing.
|
||||
try {
|
||||
$file->lock(false);
|
||||
} catch (\Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
// Another process has locked the file; we will check this in a bit.
|
||||
}
|
||||
|
||||
@@ -242,7 +245,7 @@ abstract class CompiledBase
|
||||
}
|
||||
|
||||
$cache = [
|
||||
'@class' => \get_class($this),
|
||||
'@class' => get_class($this),
|
||||
'timestamp' => time(),
|
||||
'checksum' => $this->checksum(),
|
||||
'files' => $this->files,
|
||||
@@ -256,6 +259,9 @@ abstract class CompiledBase
|
||||
$this->modified();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getState()
|
||||
{
|
||||
return $this->object->toArray();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,12 @@ use Grav\Common\Grav;
|
||||
*/
|
||||
class CompiledBlueprints extends CompiledBase
|
||||
{
|
||||
/**
|
||||
* CompiledBlueprints constructor.
|
||||
* @param string $cacheFolder
|
||||
* @param array $files
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($cacheFolder, array $files, $path)
|
||||
{
|
||||
parent::__construct($cacheFolder, $files, $path);
|
||||
@@ -45,7 +51,7 @@ class CompiledBlueprints extends CompiledBase
|
||||
/**
|
||||
* Create configuration object.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $data
|
||||
*/
|
||||
protected function createObject(array $data = [])
|
||||
{
|
||||
@@ -64,6 +70,8 @@ class CompiledBlueprints extends CompiledBase
|
||||
|
||||
/**
|
||||
* Finalize configuration object.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function finalizeObject()
|
||||
{
|
||||
@@ -74,6 +82,7 @@ class CompiledBlueprints extends CompiledBase
|
||||
*
|
||||
* @param string $name Name of the position.
|
||||
* @param array $files Files to be loaded.
|
||||
* @return void
|
||||
*/
|
||||
protected function loadFile($name, $files)
|
||||
{
|
||||
@@ -112,6 +121,9 @@ class CompiledBlueprints extends CompiledBase
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getState()
|
||||
{
|
||||
return $this->object->getState();
|
||||
|
||||
@@ -3,26 +3,33 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use function is_callable;
|
||||
|
||||
/**
|
||||
* Class CompiledConfig
|
||||
* @package Grav\Common\Config
|
||||
*/
|
||||
class CompiledConfig extends CompiledBase
|
||||
{
|
||||
/**
|
||||
* @var callable Blueprints loader.
|
||||
*/
|
||||
/** @var callable Blueprints loader. */
|
||||
protected $callable;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $withDefaults;
|
||||
/** @var bool */
|
||||
protected $withDefaults = false;
|
||||
|
||||
/**
|
||||
* CompiledConfig constructor.
|
||||
* @param string $cacheFolder
|
||||
* @param array $files
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($cacheFolder, array $files, $path)
|
||||
{
|
||||
parent::__construct($cacheFolder, $files, $path);
|
||||
@@ -58,10 +65,11 @@ class CompiledConfig extends CompiledBase
|
||||
* Create configuration object.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
protected function createObject(array $data = [])
|
||||
{
|
||||
if ($this->withDefaults && empty($data) && \is_callable($this->callable)) {
|
||||
if ($this->withDefaults && empty($data) && is_callable($this->callable)) {
|
||||
$blueprints = $this->callable;
|
||||
$data = $blueprints()->getDefaults();
|
||||
}
|
||||
@@ -71,6 +79,8 @@ class CompiledConfig extends CompiledBase
|
||||
|
||||
/**
|
||||
* Finalize configuration object.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function finalizeObject()
|
||||
{
|
||||
@@ -80,6 +90,8 @@ class CompiledConfig extends CompiledBase
|
||||
|
||||
/**
|
||||
* Function gets called when cached configuration is saved.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function modified()
|
||||
{
|
||||
@@ -91,6 +103,7 @@ class CompiledConfig extends CompiledBase
|
||||
*
|
||||
* @param string $name Name of the position.
|
||||
* @param string $filename File to be loaded.
|
||||
* @return void
|
||||
*/
|
||||
protected function loadFile($name, $filename)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -11,8 +11,18 @@ namespace Grav\Common\Config;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
|
||||
/**
|
||||
* Class CompiledLanguages
|
||||
* @package Grav\Common\Config
|
||||
*/
|
||||
class CompiledLanguages extends CompiledBase
|
||||
{
|
||||
/**
|
||||
* CompiledLanguages constructor.
|
||||
* @param string $cacheFolder
|
||||
* @param array $files
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($cacheFolder, array $files, $path)
|
||||
{
|
||||
parent::__construct($cacheFolder, $files, $path);
|
||||
@@ -24,6 +34,7 @@ class CompiledLanguages extends CompiledBase
|
||||
* Create configuration object.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
protected function createObject(array $data = [])
|
||||
{
|
||||
@@ -32,6 +43,8 @@ class CompiledLanguages extends CompiledBase
|
||||
|
||||
/**
|
||||
* Finalize configuration object.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function finalizeObject()
|
||||
{
|
||||
@@ -42,6 +55,8 @@ class CompiledLanguages extends CompiledBase
|
||||
|
||||
/**
|
||||
* Function gets called when cached configuration is saved.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function modified()
|
||||
{
|
||||
@@ -53,6 +68,7 @@ class CompiledLanguages extends CompiledBase
|
||||
*
|
||||
* @param string $name Name of the position.
|
||||
* @param string $filename File to be loaded.
|
||||
* @return void
|
||||
*/
|
||||
protected function loadFile($name, $filename)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -14,9 +14,15 @@ use Grav\Common\Grav;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Service\ConfigServiceProvider;
|
||||
use Grav\Common\Utils;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Class Config
|
||||
* @package Grav\Common\Config
|
||||
*/
|
||||
class Config extends Data
|
||||
{
|
||||
/** @var string */
|
||||
public $environment;
|
||||
|
||||
/** @var string */
|
||||
@@ -28,6 +34,9 @@ class Config extends Data
|
||||
/** @var bool */
|
||||
protected $modified = false;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
if (null === $this->key) {
|
||||
@@ -37,6 +46,10 @@ class Config extends Data
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $checksum
|
||||
* @return string|null
|
||||
*/
|
||||
public function checksum($checksum = null)
|
||||
{
|
||||
if ($checksum !== null) {
|
||||
@@ -46,6 +59,10 @@ class Config extends Data
|
||||
return $this->checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|null $modified
|
||||
* @return bool
|
||||
*/
|
||||
public function modified($modified = null)
|
||||
{
|
||||
if ($modified !== null) {
|
||||
@@ -55,6 +72,10 @@ class Config extends Data
|
||||
return $this->modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $timestamp
|
||||
* @return int
|
||||
*/
|
||||
public function timestamp($timestamp = null)
|
||||
{
|
||||
if ($timestamp !== null) {
|
||||
@@ -64,6 +85,9 @@ class Config extends Data
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function reload()
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
@@ -86,6 +110,9 @@ class Config extends Data
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function debug()
|
||||
{
|
||||
/** @var Debugger $debugger */
|
||||
@@ -97,11 +124,14 @@ class Config extends Data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$setup = Grav::instance()['setup']->toArray();
|
||||
foreach ($setup as $key => $value) {
|
||||
if ($key === 'streams' || !\is_array($value)) {
|
||||
if ($key === 'streams' || !is_array($value)) {
|
||||
// Optimized as streams and simple values are fully defined in setup.
|
||||
$this->items[$key] = $value;
|
||||
} else {
|
||||
|
||||
@@ -3,16 +3,23 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use DirectoryIterator;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use RecursiveDirectoryIterator;
|
||||
|
||||
/**
|
||||
* Class ConfigFileFinder
|
||||
* @package Grav\Common\Config
|
||||
*/
|
||||
class ConfigFileFinder
|
||||
{
|
||||
/** @var string */
|
||||
protected $base = '';
|
||||
|
||||
/**
|
||||
@@ -40,6 +47,7 @@ class ConfigFileFinder
|
||||
foreach ($paths as $folder) {
|
||||
$list += $this->detectRecursive($folder, $pattern, $levels);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -61,6 +69,7 @@ class ConfigFileFinder
|
||||
|
||||
$list += $files[trim($path, '/')];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -78,6 +87,7 @@ class ConfigFileFinder
|
||||
foreach ($paths as $folder) {
|
||||
$list = array_merge_recursive($list, $this->detectAll($folder, $pattern, $levels));
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -96,6 +106,7 @@ class ConfigFileFinder
|
||||
foreach ($folders as $folder) {
|
||||
$list += $this->detectInFolder($folder, $filename);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -103,7 +114,7 @@ class ConfigFileFinder
|
||||
* Find filename from a list of folders.
|
||||
*
|
||||
* @param array $folders
|
||||
* @param string $filename
|
||||
* @param string|null $filename
|
||||
* @return array
|
||||
*/
|
||||
public function locateInFolders(array $folders, $filename = null)
|
||||
@@ -113,6 +124,7 @@ class ConfigFileFinder
|
||||
$path = trim(Folder::getRelativePath($folder), '/');
|
||||
$list[$path] = $this->detectInFolder($folder, $filename);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -166,7 +178,7 @@ class ConfigFileFinder
|
||||
'filters' => [
|
||||
'pre-key' => $this->base,
|
||||
'key' => $pattern,
|
||||
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
|
||||
'value' => function (RecursiveDirectoryIterator $file) use ($path) {
|
||||
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
|
||||
}
|
||||
],
|
||||
@@ -187,7 +199,7 @@ class ConfigFileFinder
|
||||
* Detects all directories with the lookup file and returns them with last modification time.
|
||||
*
|
||||
* @param string $folder Location to look up from.
|
||||
* @param string $lookup Filename to be located (defaults to directory name).
|
||||
* @param string|null $lookup Filename to be located (defaults to directory name).
|
||||
* @return array
|
||||
* @internal
|
||||
*/
|
||||
@@ -200,9 +212,7 @@ class ConfigFileFinder
|
||||
$list = [];
|
||||
|
||||
if (is_dir($folder)) {
|
||||
$iterator = new \DirectoryIterator($folder);
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
$iterator = new DirectoryIterator($folder);
|
||||
foreach ($iterator as $directory) {
|
||||
if (!$directory->isDir() || $directory->isDot()) {
|
||||
continue;
|
||||
@@ -244,7 +254,7 @@ class ConfigFileFinder
|
||||
'filters' => [
|
||||
'pre-key' => $this->base,
|
||||
'key' => $pattern,
|
||||
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
|
||||
'value' => function (RecursiveDirectoryIterator $file) use ($path) {
|
||||
return ["{$path}/{$file->getSubPathname()}" => $file->getMTime()];
|
||||
}
|
||||
],
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -12,24 +12,25 @@ namespace Grav\Common\Config;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Utils;
|
||||
|
||||
/**
|
||||
* Class Languages
|
||||
* @package Grav\Common\Config
|
||||
*/
|
||||
class Languages extends Data
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
/** @var string|null */
|
||||
protected $checksum;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $modified;
|
||||
/** @var bool */
|
||||
protected $modified = false;
|
||||
|
||||
/** @var int */
|
||||
protected $timestamp = 0;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
* @param string|null $checksum
|
||||
* @return string|null
|
||||
*/
|
||||
protected $timestamp;
|
||||
|
||||
|
||||
public function checksum($checksum = null)
|
||||
{
|
||||
if ($checksum !== null) {
|
||||
@@ -39,6 +40,10 @@ class Languages extends Data
|
||||
return $this->checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|null $modified
|
||||
* @return bool
|
||||
*/
|
||||
public function modified($modified = null)
|
||||
{
|
||||
if ($modified !== null) {
|
||||
@@ -48,6 +53,10 @@ class Languages extends Data
|
||||
return $this->modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $timestamp
|
||||
* @return int
|
||||
*/
|
||||
public function timestamp($timestamp = null)
|
||||
{
|
||||
if ($timestamp !== null) {
|
||||
@@ -57,6 +66,9 @@ class Languages extends Data
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reformat()
|
||||
{
|
||||
if (isset($this->items['plugins'])) {
|
||||
@@ -65,17 +77,29 @@ class Languages extends Data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function mergeRecursive(array $data)
|
||||
{
|
||||
$this->items = Utils::arrayMergeRecursiveUnique($this->items, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $lang
|
||||
* @return array
|
||||
*/
|
||||
public function flattenByLang($lang)
|
||||
{
|
||||
$language = $this->items[$lang];
|
||||
return Utils::arrayFlattenDotNotation($language);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
public function unflatten($array)
|
||||
{
|
||||
return Utils::arrayUnflattenDotNotation($array);
|
||||
|
||||
@@ -3,19 +3,28 @@
|
||||
/**
|
||||
* @package Grav\Common\Config
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Config;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Utils;
|
||||
use InvalidArgumentException;
|
||||
use Pimple\Container;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RuntimeException;
|
||||
use function defined;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Class Setup
|
||||
* @package Grav\Common\Config
|
||||
*/
|
||||
class Setup extends Data
|
||||
{
|
||||
/**
|
||||
@@ -28,28 +37,58 @@ class Setup extends Data
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Current environment normalized to lower case.
|
||||
* @var string|null Current environment normalized to lower case.
|
||||
*/
|
||||
public static $environment;
|
||||
|
||||
/** @var array */
|
||||
protected $streams = [
|
||||
'system' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['system'],
|
||||
]
|
||||
],
|
||||
'user' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'force' => true,
|
||||
'prefixes' => [
|
||||
'' => ['user'],
|
||||
'' => [] // Set in constructor
|
||||
]
|
||||
],
|
||||
'cache' => [
|
||||
'type' => 'Stream',
|
||||
'force' => true,
|
||||
'prefixes' => [
|
||||
'' => [], // Set in constructor
|
||||
'images' => ['images']
|
||||
]
|
||||
],
|
||||
'log' => [
|
||||
'type' => 'Stream',
|
||||
'force' => true,
|
||||
'prefixes' => [
|
||||
'' => [] // Set in constructor
|
||||
]
|
||||
],
|
||||
'tmp' => [
|
||||
'type' => 'Stream',
|
||||
'force' => true,
|
||||
'prefixes' => [
|
||||
'' => [] // Set in constructor
|
||||
]
|
||||
],
|
||||
'backup' => [
|
||||
'type' => 'Stream',
|
||||
'force' => true,
|
||||
'prefixes' => [
|
||||
'' => [] // Set in constructor
|
||||
]
|
||||
],
|
||||
'environment' => [
|
||||
'type' => 'ReadOnlyStream'
|
||||
// If not defined, environment will be set up in the constructor.
|
||||
],
|
||||
'system' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['system'],
|
||||
]
|
||||
],
|
||||
'asset' => [
|
||||
'type' => 'Stream',
|
||||
'prefixes' => [
|
||||
@@ -59,13 +98,13 @@ class Setup extends Data
|
||||
'blueprints' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['environment://blueprints', 'user://blueprints', 'system/blueprints'],
|
||||
'' => ['environment://blueprints', 'user://blueprints', 'system://blueprints'],
|
||||
]
|
||||
],
|
||||
'config' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['environment://config', 'user://config', 'system/config'],
|
||||
'' => ['environment://config', 'user://config', 'system://config'],
|
||||
]
|
||||
],
|
||||
'plugins' => [
|
||||
@@ -89,36 +128,7 @@ class Setup extends Data
|
||||
'languages' => [
|
||||
'type' => 'ReadOnlyStream',
|
||||
'prefixes' => [
|
||||
'' => ['environment://languages', 'user://languages', 'system/languages'],
|
||||
]
|
||||
],
|
||||
'cache' => [
|
||||
'type' => 'Stream',
|
||||
'force' => true,
|
||||
'prefixes' => [
|
||||
'' => ['cache'],
|
||||
'images' => ['images']
|
||||
]
|
||||
],
|
||||
'log' => [
|
||||
'type' => 'Stream',
|
||||
'force' => true,
|
||||
'prefixes' => [
|
||||
'' => ['logs']
|
||||
]
|
||||
],
|
||||
'backup' => [
|
||||
'type' => 'Stream',
|
||||
'force' => true,
|
||||
'prefixes' => [
|
||||
'' => ['backup']
|
||||
]
|
||||
],
|
||||
'tmp' => [
|
||||
'type' => 'Stream',
|
||||
'force' => true,
|
||||
'prefixes' => [
|
||||
'' => ['tmp']
|
||||
'' => ['environment://languages', 'user://languages', 'system://languages'],
|
||||
]
|
||||
],
|
||||
'image' => [
|
||||
@@ -153,27 +163,56 @@ class Setup extends Data
|
||||
*/
|
||||
public function __construct($container)
|
||||
{
|
||||
// Configure main streams.
|
||||
$this->streams['user']['prefixes'][''] = [GRAV_USER_PATH];
|
||||
$this->streams['cache']['prefixes'][''] = [GRAV_CACHE_PATH];
|
||||
$this->streams['log']['prefixes'][''] = [GRAV_LOG_PATH];
|
||||
$this->streams['tmp']['prefixes'][''] = [GRAV_TMP_PATH];
|
||||
$this->streams['backup']['prefixes'][''] = [GRAV_BACKUP_PATH];
|
||||
|
||||
// If environment is not set, look for the environment variable and then the constant.
|
||||
$environment = static::$environment ??
|
||||
(defined('GRAV_ENVIRONMENT') ? GRAV_ENVIRONMENT : (getenv('GRAV_ENVIRONMENT') ?: null));
|
||||
|
||||
// If no environment is set, make sure we get one (CLI or hostname).
|
||||
if (!static::$environment) {
|
||||
if (\defined('GRAV_CLI')) {
|
||||
static::$environment = 'cli';
|
||||
if (null === $environment) {
|
||||
if (defined('GRAV_CLI')) {
|
||||
$environment = 'cli';
|
||||
} else {
|
||||
/** @var ServerRequestInterface $request */
|
||||
$request = $container['request'];
|
||||
$host = $request->getUri()->getHost();
|
||||
|
||||
static::$environment = Utils::substrToString($host, ':');
|
||||
$environment = Utils::substrToString($host, ':');
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve server aliases to the proper environment.
|
||||
$environment = $this->environments[static::$environment] ?? static::$environment;
|
||||
static::$environment = static::$environments[$environment] ?? $environment;
|
||||
|
||||
// Pre-load setup.php which contains our initial configuration.
|
||||
// Configuration may contain dynamic parts, which is why we need to always load it.
|
||||
// If "GRAV_SETUP_PATH" has been defined, use it, otherwise use defaults.
|
||||
$file = \defined('GRAV_SETUP_PATH') ? GRAV_SETUP_PATH : GRAV_ROOT . '/setup.php';
|
||||
$setup = is_file($file) ? (array) include $file : [];
|
||||
// If GRAV_SETUP_PATH has been defined, use it, otherwise use defaults.
|
||||
$setupFile = defined('GRAV_SETUP_PATH') ? GRAV_SETUP_PATH : (getenv('GRAV_SETUP_PATH') ?: null);
|
||||
if (null !== $setupFile) {
|
||||
// Make sure that the custom setup file exists. Terminates the script if not.
|
||||
if (!str_starts_with($setupFile, '/')) {
|
||||
$setupFile = GRAV_ROOT . '/' . $setupFile;
|
||||
}
|
||||
if (!is_file($setupFile)) {
|
||||
echo 'GRAV_SETUP_PATH is defined but does not point to existing setup file.';
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
$setupFile = GRAV_ROOT . '/setup.php';
|
||||
if (!is_file($setupFile)) {
|
||||
$setupFile = GRAV_ROOT . '/' . GRAV_USER_PATH . '/setup.php';
|
||||
}
|
||||
if (!is_file($setupFile)) {
|
||||
$setupFile = null;
|
||||
}
|
||||
}
|
||||
$setup = $setupFile ? (array) include $setupFile : [];
|
||||
|
||||
// Add default streams defined in beginning of the class.
|
||||
if (!isset($setup['streams']['schemes'])) {
|
||||
@@ -184,15 +223,37 @@ class Setup extends Data
|
||||
// Initialize class.
|
||||
parent::__construct($setup);
|
||||
|
||||
$this->def('environment', static::$environment);
|
||||
|
||||
// Figure out path for the current environment.
|
||||
$envPath = defined('GRAV_ENVIRONMENT_PATH') ? GRAV_ENVIRONMENT_PATH : (getenv('GRAV_ENVIRONMENT_PATH') ?: null);
|
||||
if (null === $envPath) {
|
||||
// Find common path for all environments and append current environment into it.
|
||||
$envPath = defined('GRAV_ENVIRONMENTS_PATH') ? GRAV_ENVIRONMENTS_PATH : (getenv('GRAV_ENVIRONMENTS_PATH') ?: null);
|
||||
if (null !== $envPath) {
|
||||
$envPath .= '/';
|
||||
} else {
|
||||
// Use default location. Start with Grav 1.7 default.
|
||||
$envPath = GRAV_ROOT. '/' . GRAV_USER_PATH . '/env';
|
||||
if (is_dir($envPath)) {
|
||||
$envPath = 'user://env/';
|
||||
} else {
|
||||
// Fallback to Grav 1.6 default.
|
||||
$envPath = 'user://';
|
||||
}
|
||||
}
|
||||
$envPath .= $this->get('environment');
|
||||
}
|
||||
|
||||
// Set up environment.
|
||||
$this->def('environment', $environment);
|
||||
$this->def('streams.schemes.environment.prefixes', ['' => ["user://{$this->get('environment')}"]]);
|
||||
$this->def('environment', static::$environment);
|
||||
$this->def('streams.schemes.environment.prefixes', ['' => [$envPath]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
* @throws \RuntimeException
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws RuntimeException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
@@ -220,7 +281,7 @@ class Setup extends Data
|
||||
} while (--$guard);
|
||||
|
||||
if (!$guard) {
|
||||
throw new \RuntimeException('Setup: Configuration reload loop detected!');
|
||||
throw new RuntimeException('Setup: Configuration reload loop detected!');
|
||||
}
|
||||
|
||||
// Make sure we have valid setup.
|
||||
@@ -233,7 +294,8 @@ class Setup extends Data
|
||||
* Initialize resource locator by using the configuration.
|
||||
*
|
||||
* @param UniformResourceLocator $locator
|
||||
* @throws \BadMethodCallException
|
||||
* @return void
|
||||
* @throws BadMethodCallException
|
||||
*/
|
||||
public function initializeLocator(UniformResourceLocator $locator)
|
||||
{
|
||||
@@ -279,24 +341,51 @@ class Setup extends Data
|
||||
|
||||
/**
|
||||
* @param UniformResourceLocator $locator
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \BadMethodCallException
|
||||
* @throws \RuntimeException
|
||||
* @return void
|
||||
* @throws InvalidArgumentException
|
||||
* @throws BadMethodCallException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function check(UniformResourceLocator $locator)
|
||||
{
|
||||
$streams = $this->items['streams']['schemes'] ?? null;
|
||||
if (!\is_array($streams)) {
|
||||
throw new \InvalidArgumentException('Configuration is missing streams.schemes!');
|
||||
if (!is_array($streams)) {
|
||||
throw new InvalidArgumentException('Configuration is missing streams.schemes!');
|
||||
}
|
||||
$diff = array_keys(array_diff_key($this->streams, $streams));
|
||||
if ($diff) {
|
||||
throw new \InvalidArgumentException(
|
||||
throw new InvalidArgumentException(
|
||||
sprintf('Configuration is missing keys %s from streams.schemes!', implode(', ', $diff))
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// If environment is found, remove all missing override locations (B/C compatibility).
|
||||
if ($locator->findResource('environment://', true)) {
|
||||
$force = $this->get('streams.schemes.environment.force', false);
|
||||
if (!$force) {
|
||||
$prefixes = $this->get('streams.schemes.environment.prefixes.');
|
||||
$update = false;
|
||||
foreach ($prefixes as $i => $prefix) {
|
||||
if ($locator->isStream($prefix)) {
|
||||
if ($locator->findResource($prefix, true)) {
|
||||
break;
|
||||
}
|
||||
} elseif (file_exists($prefix)) {
|
||||
break;
|
||||
}
|
||||
|
||||
unset($prefixes[$i]);
|
||||
$update = true;
|
||||
}
|
||||
|
||||
if ($update) {
|
||||
$this->set('streams.schemes.environment.prefixes', ['' => array_values($prefixes)]);
|
||||
$this->initializeLocator($locator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$locator->findResource('environment://config', true)) {
|
||||
// If environment does not have its own directory, remove it from the lookup.
|
||||
$this->set('streams.schemes.environment.prefixes', ['config' => []]);
|
||||
@@ -314,8 +403,8 @@ class Setup extends Data
|
||||
$security_file->save();
|
||||
$security_file->free();
|
||||
}
|
||||
} catch (\RuntimeException $e) {
|
||||
throw new \RuntimeException(sprintf('Grav failed to initialize: %s', $e->getMessage()), 500, $e);
|
||||
} catch (RuntimeException $e) {
|
||||
throw new RuntimeException(sprintf('Grav failed to initialize: %s', $e->getMessage()), 500, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -14,22 +14,44 @@ use Grav\Common\Grav;
|
||||
use Grav\Common\User\Interfaces\UserInterface;
|
||||
use RocketTheme\Toolbox\Blueprints\BlueprintForm;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RuntimeException;
|
||||
use function call_user_func_array;
|
||||
use function count;
|
||||
use function function_exists;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Class Blueprint
|
||||
* @package Grav\Common\Data
|
||||
*/
|
||||
class Blueprint extends BlueprintForm
|
||||
{
|
||||
/** @var string */
|
||||
protected $context = 'blueprints://';
|
||||
|
||||
/** @var string|null */
|
||||
protected $scope;
|
||||
|
||||
/** @var BlueprintSchema */
|
||||
protected $blueprintSchema;
|
||||
|
||||
/** @var array */
|
||||
/** @var object|null */
|
||||
protected $object;
|
||||
|
||||
/** @var array|null */
|
||||
protected $defaults;
|
||||
|
||||
/** @var array */
|
||||
protected $handlers = [];
|
||||
|
||||
/**
|
||||
* Clone blueprint.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
if ($this->blueprintSchema) {
|
||||
@@ -37,11 +59,24 @@ class Blueprint extends BlueprintForm
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $scope
|
||||
* @return void
|
||||
*/
|
||||
public function setScope($scope)
|
||||
{
|
||||
$this->scope = $scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $object
|
||||
* @return void
|
||||
*/
|
||||
public function setObject($object)
|
||||
{
|
||||
$this->object = $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default values for field types.
|
||||
*
|
||||
@@ -57,6 +92,29 @@ class Blueprint extends BlueprintForm
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return array|mixed|null
|
||||
* @since 1.7
|
||||
*/
|
||||
public function getDefaultValue(string $name)
|
||||
{
|
||||
$path = explode('.', $name) ?: [];
|
||||
$current = $this->getDefaults();
|
||||
|
||||
foreach ($path as $field) {
|
||||
if (is_object($current) && isset($current->{$field})) {
|
||||
$current = $current->{$field};
|
||||
} elseif (is_array($current) && isset($current[$field])) {
|
||||
$current = $current[$field];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get nested structure containing default values defined in the blueprints.
|
||||
*
|
||||
@@ -88,7 +146,7 @@ class Blueprint extends BlueprintForm
|
||||
$current = &$this->items;
|
||||
|
||||
foreach ($path as $field) {
|
||||
if (\is_object($current)) {
|
||||
if (is_object($current)) {
|
||||
// Handle objects.
|
||||
if (!isset($current->{$field})) {
|
||||
$current->{$field} = [];
|
||||
@@ -97,7 +155,7 @@ class Blueprint extends BlueprintForm
|
||||
$current = &$current->{$field};
|
||||
} else {
|
||||
// Handle arrays and scalars.
|
||||
if (!\is_array($current)) {
|
||||
if (!is_array($current)) {
|
||||
$current = [$field => []];
|
||||
} elseif (!isset($current[$field])) {
|
||||
$current[$field] = [];
|
||||
@@ -111,6 +169,7 @@ class Blueprint extends BlueprintForm
|
||||
foreach ($data as $property => $call) {
|
||||
$action = $call['action'];
|
||||
$method = 'dynamic' . ucfirst($action);
|
||||
$call['object'] = $this->object;
|
||||
|
||||
if (isset($this->handlers[$action])) {
|
||||
$callable = $this->handlers[$action];
|
||||
@@ -124,12 +183,44 @@ class Blueprint extends BlueprintForm
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend blueprint with another blueprint.
|
||||
*
|
||||
* @param BlueprintForm|array $extends
|
||||
* @param bool $append
|
||||
* @return $this
|
||||
*/
|
||||
public function extend($extends, $append = false)
|
||||
{
|
||||
parent::extend($extends, $append);
|
||||
|
||||
$this->deepInit($this->items);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @param string $separator
|
||||
* @param bool $append
|
||||
* @return $this
|
||||
*/
|
||||
public function embed($name, $value, $separator = '/', $append = false)
|
||||
{
|
||||
parent::embed($name, $value, $separator, $append);
|
||||
|
||||
$this->deepInit($this->items);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two arrays by using blueprints.
|
||||
*
|
||||
* @param array $data1
|
||||
* @param array $data2
|
||||
* @param string $name Optional
|
||||
* @param string|null $name Optional
|
||||
* @param string $separator Optional
|
||||
* @return array
|
||||
*/
|
||||
@@ -172,13 +263,15 @@ class Blueprint extends BlueprintForm
|
||||
* Validate data against blueprints.
|
||||
*
|
||||
* @param array $data
|
||||
* @throws \RuntimeException
|
||||
* @param array $options
|
||||
* @return void
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function validate(array $data)
|
||||
public function validate(array $data, array $options = [])
|
||||
{
|
||||
$this->initInternals();
|
||||
|
||||
$this->blueprintSchema->validate($data);
|
||||
$this->blueprintSchema->validate($data, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +286,7 @@ class Blueprint extends BlueprintForm
|
||||
{
|
||||
$this->initInternals();
|
||||
|
||||
return $this->blueprintSchema->filter($data, $missingValuesAsNull, $keepEmptyValues);
|
||||
return $this->blueprintSchema->filter($data, $missingValuesAsNull, $keepEmptyValues) ?? [];
|
||||
}
|
||||
|
||||
|
||||
@@ -223,6 +316,11 @@ class Blueprint extends BlueprintForm
|
||||
return $this->blueprintSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param callable $callable
|
||||
* @return void
|
||||
*/
|
||||
public function addDynamicHandler(string $name, callable $callable): void
|
||||
{
|
||||
$this->handlers[$name] = $callable;
|
||||
@@ -230,6 +328,8 @@ class Blueprint extends BlueprintForm
|
||||
|
||||
/**
|
||||
* Initialize validator.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function initInternals()
|
||||
{
|
||||
@@ -250,12 +350,12 @@ class Blueprint extends BlueprintForm
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return string
|
||||
* @return array
|
||||
*/
|
||||
protected function loadFile($filename)
|
||||
{
|
||||
$file = CompiledYamlFile::instance($filename);
|
||||
$content = $file->content();
|
||||
$content = (array)$file->content();
|
||||
$file->free();
|
||||
|
||||
return $content;
|
||||
@@ -263,7 +363,7 @@ class Blueprint extends BlueprintForm
|
||||
|
||||
/**
|
||||
* @param string|array $path
|
||||
* @param string $context
|
||||
* @param string|null $context
|
||||
* @return array
|
||||
*/
|
||||
protected function getFiles($path, $context = null)
|
||||
@@ -271,16 +371,24 @@ class Blueprint extends BlueprintForm
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
if (\is_string($path) && !$locator->isStream($path)) {
|
||||
if (is_string($path) && !$locator->isStream($path)) {
|
||||
if (is_file($path)) {
|
||||
return [$path];
|
||||
}
|
||||
|
||||
// Find path overrides.
|
||||
$paths = (array) ($this->overrides[$path] ?? null);
|
||||
if (null === $context) {
|
||||
$paths = (array) ($this->overrides[$path] ?? null);
|
||||
} else {
|
||||
$paths = [];
|
||||
}
|
||||
|
||||
// Add path pointing to default context.
|
||||
if ($context === null) {
|
||||
$context = $this->context;
|
||||
}
|
||||
|
||||
if ($context && $context[\strlen($context)-1] !== '/') {
|
||||
if ($context && $context[strlen($context)-1] !== '/') {
|
||||
$context .= '/';
|
||||
}
|
||||
|
||||
@@ -297,7 +405,7 @@ class Blueprint extends BlueprintForm
|
||||
|
||||
$files = [];
|
||||
foreach ($paths as $lookup) {
|
||||
if (\is_string($lookup) && strpos($lookup, '://')) {
|
||||
if (is_string($lookup) && strpos($lookup, '://')) {
|
||||
$files = array_merge($files, $locator->findResources($lookup));
|
||||
} else {
|
||||
$files[] = $lookup;
|
||||
@@ -311,12 +419,13 @@ class Blueprint extends BlueprintForm
|
||||
* @param array $field
|
||||
* @param string $property
|
||||
* @param array $call
|
||||
* @return void
|
||||
*/
|
||||
protected function dynamicData(array &$field, $property, array &$call)
|
||||
{
|
||||
$params = $call['params'];
|
||||
|
||||
if (\is_array($params)) {
|
||||
if (is_array($params)) {
|
||||
$function = array_shift($params);
|
||||
} else {
|
||||
$function = $params;
|
||||
@@ -327,18 +436,18 @@ class Blueprint extends BlueprintForm
|
||||
|
||||
$data = null;
|
||||
if (!$f) {
|
||||
if (\function_exists($o)) {
|
||||
$data = \call_user_func_array($o, $params);
|
||||
if (function_exists($o)) {
|
||||
$data = call_user_func_array($o, $params);
|
||||
}
|
||||
} else {
|
||||
if (method_exists($o, $f)) {
|
||||
$data = \call_user_func_array([$o, $f], $params);
|
||||
$data = call_user_func_array([$o, $f], $params);
|
||||
}
|
||||
}
|
||||
|
||||
// If function returns a value,
|
||||
if (null !== $data) {
|
||||
if (\is_array($data) && isset($field[$property]) && \is_array($field[$property])) {
|
||||
if (is_array($data) && isset($field[$property]) && is_array($field[$property])) {
|
||||
// Combine field and @data-field together.
|
||||
$field[$property] += $data;
|
||||
} else {
|
||||
@@ -352,15 +461,33 @@ class Blueprint extends BlueprintForm
|
||||
* @param array $field
|
||||
* @param string $property
|
||||
* @param array $call
|
||||
* @return void
|
||||
*/
|
||||
protected function dynamicConfig(array &$field, $property, array &$call)
|
||||
{
|
||||
$value = $call['params'];
|
||||
$params = $call['params'];
|
||||
if (is_array($params)) {
|
||||
$value = array_shift($params);
|
||||
$params = array_shift($params);
|
||||
} else {
|
||||
$value = $params;
|
||||
$params = [];
|
||||
}
|
||||
|
||||
$default = $field[$property] ?? null;
|
||||
$config = Grav::instance()['config']->get($value, $default);
|
||||
if (!empty($field['value_only'])) {
|
||||
$config = array_combine($config, $config);
|
||||
}
|
||||
|
||||
if (null !== $config) {
|
||||
$field[$property] = $config;
|
||||
if (!empty($params['append']) && is_array($config) && isset($field[$property]) && is_array($field[$property])) {
|
||||
// Combine field and @config-field together.
|
||||
$field[$property] += $config;
|
||||
} else {
|
||||
// Or create/replace field with @config-field.
|
||||
$field[$property] = $config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,6 +495,7 @@ class Blueprint extends BlueprintForm
|
||||
* @param array $field
|
||||
* @param string $property
|
||||
* @param array $call
|
||||
* @return void
|
||||
*/
|
||||
protected function dynamicSecurity(array &$field, $property, array &$call)
|
||||
{
|
||||
@@ -378,22 +506,46 @@ class Blueprint extends BlueprintForm
|
||||
$grav = Grav::instance();
|
||||
$actions = (array)$call['params'];
|
||||
|
||||
/** @var UserInterface $user */
|
||||
if (isset($grav['user'])) {
|
||||
$user = Grav::instance()['user'];
|
||||
foreach ($actions as $action) {
|
||||
if (!$user->authorize($action)) {
|
||||
$this->addPropertyRecursive($field, 'validate', ['ignore' => true]);
|
||||
return;
|
||||
}
|
||||
/** @var UserInterface|null $user */
|
||||
$user = $grav['user'] ?? null;
|
||||
$success = null !== $user;
|
||||
if ($success) {
|
||||
$success = $this->resolveActions($user, $actions);
|
||||
}
|
||||
if (!$success) {
|
||||
$this->addPropertyRecursive($field, 'validate', ['ignore' => true]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserInterface|null $user
|
||||
* @param array $actions
|
||||
* @param string $op
|
||||
* @return bool
|
||||
*/
|
||||
protected function resolveActions(UserInterface $user, array $actions, string $op = 'and')
|
||||
{
|
||||
$c = $i = count($actions);
|
||||
foreach ($actions as $key => $action) {
|
||||
if (!is_int($key) && is_array($actions)) {
|
||||
$i -= $this->resolveActions($user, $action, $key);
|
||||
} elseif ($user->authorize($action)) {
|
||||
$i--;
|
||||
}
|
||||
}
|
||||
|
||||
if ($op === 'and') {
|
||||
return $i === 0;
|
||||
}
|
||||
|
||||
return $c !== $i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $field
|
||||
* @param string $property
|
||||
* @param array $call
|
||||
* @return void
|
||||
*/
|
||||
protected function dynamicScope(array &$field, $property, array &$call)
|
||||
{
|
||||
@@ -402,7 +554,7 @@ class Blueprint extends BlueprintForm
|
||||
}
|
||||
|
||||
$scopes = (array)$call['params'];
|
||||
$matches = \in_array($this->scope, $scopes, true);
|
||||
$matches = in_array($this->scope, $scopes, true);
|
||||
if ($this->scope && $property !== 'ignore') {
|
||||
$matches = !$matches;
|
||||
}
|
||||
@@ -413,9 +565,15 @@ class Blueprint extends BlueprintForm
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $field
|
||||
* @param string $property
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
protected function addPropertyRecursive(array &$field, $property, $value)
|
||||
{
|
||||
if (\is_array($value) && isset($field[$property]) && \is_array($field[$property])) {
|
||||
if (is_array($value) && isset($field[$property]) && is_array($field[$property])) {
|
||||
$field[$property] = array_merge_recursive($field[$property], $value);
|
||||
} else {
|
||||
$field[$property] = $value;
|
||||
|
||||
@@ -3,21 +3,33 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
|
||||
use RocketTheme\Toolbox\Blueprints\BlueprintSchema as BlueprintSchemaBase;
|
||||
use RuntimeException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
* Class BlueprintSchema
|
||||
* @package Grav\Common\Data
|
||||
*/
|
||||
class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
{
|
||||
use Export;
|
||||
|
||||
/** @var array */
|
||||
protected $filter = ['validation' => true, 'xss_check' => true];
|
||||
|
||||
/** @var array */
|
||||
protected $ignoreFormKeys = [
|
||||
'title' => true,
|
||||
'help' => true,
|
||||
@@ -48,14 +60,16 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
* Validate data against blueprints.
|
||||
*
|
||||
* @param array $data
|
||||
* @throws \RuntimeException
|
||||
* @param array $options
|
||||
* @return void
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function validate(array $data)
|
||||
public function validate(array $data, array $options = [])
|
||||
{
|
||||
try {
|
||||
$messages = $this->validateArray($data, $this->nested);
|
||||
|
||||
} catch (\RuntimeException $e) {
|
||||
$validation = $this->items['']['form']['validation'] ?? 'loose';
|
||||
$messages = $this->validateArray($data, $this->nested, $validation === 'strict', $options['xss_check'] ?? true);
|
||||
} catch (RuntimeException $e) {
|
||||
throw (new ValidationException($e->getMessage(), $e->getCode(), $e))->setMessages();
|
||||
}
|
||||
|
||||
@@ -71,7 +85,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
*/
|
||||
public function processForm(array $data, array $toggles = [])
|
||||
{
|
||||
return $this->processFormRecursive($data, $toggles, $this->nested);
|
||||
return $this->processFormRecursive($data, $toggles, $this->nested) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +98,9 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
*/
|
||||
public function filter(array $data, $missingValuesAsNull = false, $keepEmptyValues = false)
|
||||
{
|
||||
return $this->filterArray($data, $this->nested, $missingValuesAsNull, $keepEmptyValues);
|
||||
$this->buildIgnoreNested($this->nested);
|
||||
|
||||
return $this->filterArray($data, $this->nested, '', $missingValuesAsNull, $keepEmptyValues) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,16 +145,19 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
/**
|
||||
* @param array $data
|
||||
* @param array $rules
|
||||
* @param bool $strict
|
||||
* @param bool $xss
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function validateArray(array $data, array $rules)
|
||||
protected function validateArray(array $data, array $rules, bool $strict, bool $xss = true)
|
||||
{
|
||||
$messages = $this->checkRequired($data, $rules);
|
||||
|
||||
foreach ($data as $key => $field) {
|
||||
foreach ($data as $key => $child) {
|
||||
$val = $rules[$key] ?? $rules['*'] ?? null;
|
||||
$rule = \is_string($val) ? $this->items[$val] : null;
|
||||
$rule = is_string($val) ? $this->items[$val] : null;
|
||||
$checkXss = $xss;
|
||||
|
||||
if ($rule) {
|
||||
// Item has been defined in blueprints.
|
||||
@@ -147,13 +166,26 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
continue;
|
||||
}
|
||||
|
||||
$messages += Validation::validate($field, $rule);
|
||||
} elseif (\is_array($field) && \is_array($val)) {
|
||||
$messages += Validation::validate($child, $rule);
|
||||
|
||||
} elseif (is_array($child) && is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$messages += $this->validateArray($field, $val);
|
||||
} elseif (isset($rules['validation']) && $rules['validation'] === 'strict') {
|
||||
// Undefined/extra item.
|
||||
throw new \RuntimeException(sprintf('%s is not defined in blueprints', $key));
|
||||
$messages += $this->validateArray($child, $val, $strict);
|
||||
$checkXss = false;
|
||||
|
||||
} elseif ($strict) {
|
||||
// Undefined/extra item in strict mode.
|
||||
/** @var Config $config */
|
||||
$config = Grav::instance()['config'];
|
||||
if (!$config->get('system.strict_mode.blueprint_strict_compat', true)) {
|
||||
throw new RuntimeException(sprintf('%s is not defined in blueprints', $key));
|
||||
}
|
||||
|
||||
user_error(sprintf('Having extra key %s in your data is deprecated with blueprint having \'validation: strict\'', $key), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if ($checkXss) {
|
||||
$messages += Validation::checkSafety($child, $rule ?: ['name' => $key]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,51 +195,55 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
/**
|
||||
* @param array $data
|
||||
* @param array $rules
|
||||
* @param string $parent
|
||||
* @param bool $missingValuesAsNull
|
||||
* @param bool $keepEmptyValues
|
||||
* @return array
|
||||
* @return array|null
|
||||
*/
|
||||
protected function filterArray(array $data, array $rules, $missingValuesAsNull, $keepEmptyValues)
|
||||
protected function filterArray(array $data, array $rules, string $parent, bool $missingValuesAsNull, bool $keepEmptyValues)
|
||||
{
|
||||
$results = [];
|
||||
|
||||
if ($missingValuesAsNull) {
|
||||
// First pass is to fill up all the fields with null. This is done to lock the ordering of the fields.
|
||||
foreach ($rules as $key => $rule) {
|
||||
if ($key && !isset($results[$key])) {
|
||||
$val = $rules[$key] ?? $rules['*'] ?? null;
|
||||
$rule = \is_string($val) ? $this->items[$val] : null;
|
||||
|
||||
if (empty($rule['disabled']) && empty($rule['validate']['ignore'])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $key => $field) {
|
||||
$val = $rules[$key] ?? $rules['*'] ?? null;
|
||||
$rule = \is_string($val) ? $this->items[$val] : null;
|
||||
$rule = is_string($val) ? $this->items[$val] : $this->items[$parent . $key] ?? null;
|
||||
|
||||
if ($rule) {
|
||||
// Item has been defined in blueprints.
|
||||
if (!empty($rule['disabled']) || !empty($rule['validate']['ignore'])) {
|
||||
// Skip any data in the ignored field.
|
||||
if (!empty($rule['disabled']) || !empty($rule['validate']['ignore'])) {
|
||||
// Skip any data in the ignored field.
|
||||
unset($results[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (null === $field) {
|
||||
if ($missingValuesAsNull) {
|
||||
$results[$key] = null;
|
||||
} else {
|
||||
unset($results[$key]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$isParent = isset($val['*']);
|
||||
$type = $rule['type'] ?? null;
|
||||
|
||||
if (!$isParent && $type && $type !== '_parent') {
|
||||
$field = Validation::filter($field, $rule);
|
||||
} elseif (is_array($field) && is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$k = $isParent ? '*' : $key;
|
||||
$field = $this->filterArray($field, $val, $parent . $k . '.', $missingValuesAsNull, $keepEmptyValues);
|
||||
|
||||
if (null === $field) {
|
||||
// Nested parent has no values.
|
||||
unset($results[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$field = Validation::filter($field, $rule);
|
||||
} elseif (\is_array($field) && \is_array($val)) {
|
||||
// Array has been defined in blueprints.
|
||||
$field = $this->filterArray($field, $val, $missingValuesAsNull, $keepEmptyValues);
|
||||
|
||||
} elseif (isset($rules['validation']) && $rules['validation'] === 'strict') {
|
||||
// Skip any extra data.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($keepEmptyValues || (null !== $field && (!\is_array($field) || !empty($field)))) {
|
||||
if ($keepEmptyValues || (null !== $field && (!is_array($field) || !empty($field)))) {
|
||||
$results[$key] = $field;
|
||||
}
|
||||
}
|
||||
@@ -215,6 +251,31 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
return $results ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $nested
|
||||
* @param string $parent
|
||||
* @return bool
|
||||
*/
|
||||
protected function buildIgnoreNested(array $nested, $parent = '')
|
||||
{
|
||||
$ignore = true;
|
||||
foreach ($nested as $key => $val) {
|
||||
$key = $parent . $key;
|
||||
if (is_array($val)) {
|
||||
$ignore = $this->buildIgnoreNested($val, $key . '.') && $ignore; // Keep the order!
|
||||
} else {
|
||||
$child = $this->items[$key] ?? null;
|
||||
$ignore = $ignore && (!$child || !empty($child['disabled']) || !empty($child['validate']['ignore']));
|
||||
}
|
||||
}
|
||||
if ($ignore) {
|
||||
$key = trim($parent, '.');
|
||||
$this->items[$key]['validate']['ignore'] = true;
|
||||
}
|
||||
|
||||
return $ignore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|null $data
|
||||
* @param array $toggles
|
||||
@@ -232,8 +293,19 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
continue;
|
||||
}
|
||||
if (is_array($value)) {
|
||||
// Special toggle handling for all the nested data.
|
||||
$toggle = $toggles[$key] ?? [];
|
||||
if (!is_array($toggle)) {
|
||||
if (!$toggle) {
|
||||
$data[$key] = null;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$toggle = [];
|
||||
}
|
||||
// Recursively fetch the items.
|
||||
$data[$key] = $this->processFormRecursive($data[$key] ?? null, $toggles[$key] ?? [], $value);
|
||||
$data[$key] = $this->processFormRecursive($data[$key] ?? null, $toggle, $value);
|
||||
} else {
|
||||
$field = $this->get($value);
|
||||
// Do not add the field if:
|
||||
@@ -244,8 +316,8 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
|| !empty($field['disabled'])
|
||||
// Field validation is set to be ignored
|
||||
|| !empty($field['validate']['ignore'])
|
||||
// Field is toggleable and the toggle is turned off
|
||||
|| (!empty($field['toggleable']) && empty($toggles[$key]))
|
||||
// Field is overridable and the toggle is turned off
|
||||
|| (!empty($field['overridable']) && empty($toggles[$key]))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -268,7 +340,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
$messages = [];
|
||||
|
||||
foreach ($fields as $name => $field) {
|
||||
if (!\is_string($field)) {
|
||||
if (!is_string($field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -279,10 +351,15 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip overridable fields without value.
|
||||
// TODO: We need better overridable support, which is not just ignoring required values but also looking if defaults are good.
|
||||
if (!empty($field['overridable']) && !isset($data[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if required.
|
||||
if (isset($field['validate']['required'])
|
||||
&& $field['validate']['required'] === true) {
|
||||
|
||||
if (isset($data[$name])) {
|
||||
continue;
|
||||
}
|
||||
@@ -304,6 +381,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|
||||
* @param array $field
|
||||
* @param string $property
|
||||
* @param array $call
|
||||
* @return void
|
||||
*/
|
||||
protected function dynamicConfig(array &$field, $property, array &$call)
|
||||
{
|
||||
|
||||
@@ -3,15 +3,23 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use DirectoryIterator;
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RuntimeException;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
|
||||
/**
|
||||
* Class Blueprints
|
||||
* @package Grav\Common\Data
|
||||
*/
|
||||
class Blueprints
|
||||
{
|
||||
/** @var array|string */
|
||||
@@ -34,12 +42,13 @@ class Blueprints
|
||||
*
|
||||
* @param string $type Blueprint type.
|
||||
* @return Blueprint
|
||||
* @throws \RuntimeException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function get($type)
|
||||
{
|
||||
if (!isset($this->instances[$type])) {
|
||||
$this->instances[$type] = $this->loadFile($type);
|
||||
$blueprint = $this->loadFile($type);
|
||||
$this->instances[$type] = $blueprint;
|
||||
}
|
||||
|
||||
return $this->instances[$type];
|
||||
@@ -64,10 +73,9 @@ class Blueprints
|
||||
if ($locator->isStream($this->search)) {
|
||||
$iterator = $locator->getIterator($this->search);
|
||||
} else {
|
||||
$iterator = new \DirectoryIterator($this->search);
|
||||
$iterator = new DirectoryIterator($this->search);
|
||||
}
|
||||
|
||||
/** @var \DirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
if (!$file->isFile() || '.' . $file->getExtension() !== YAML_EXT) {
|
||||
continue;
|
||||
@@ -91,7 +99,7 @@ class Blueprints
|
||||
{
|
||||
$blueprint = new Blueprint($name);
|
||||
|
||||
if (\is_array($this->search) || \is_object($this->search)) {
|
||||
if (is_array($this->search) || is_object($this->search)) {
|
||||
// Page types.
|
||||
$blueprint->setOverrides($this->search);
|
||||
$blueprint->setContext('blueprints://pages');
|
||||
@@ -99,6 +107,15 @@ class Blueprints
|
||||
$blueprint->setContext($this->search);
|
||||
}
|
||||
|
||||
return $blueprint->load()->init();
|
||||
try {
|
||||
$blueprint->load()->init();
|
||||
} catch (RuntimeException $e) {
|
||||
$log = Grav::instance()['log'];
|
||||
$log->error(sprintf('Blueprint %s cannot be loaded: %s', $name, $e->getMessage()));
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $blueprint;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,43 +3,80 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use ArrayAccess;
|
||||
use Exception;
|
||||
use JsonSerializable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
|
||||
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
|
||||
use RocketTheme\Toolbox\File\File;
|
||||
use RocketTheme\Toolbox\File\FileInterface;
|
||||
use RuntimeException;
|
||||
use function func_get_args;
|
||||
use function is_array;
|
||||
use function is_callable;
|
||||
use function is_object;
|
||||
|
||||
class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable, ExportInterface
|
||||
/**
|
||||
* Class Data
|
||||
* @package Grav\Common\Data
|
||||
*/
|
||||
class Data implements DataInterface, ArrayAccess, \Countable, JsonSerializable, ExportInterface
|
||||
{
|
||||
use NestedArrayAccessWithGetters, Countable, Export;
|
||||
|
||||
/** @var string */
|
||||
protected $gettersVariable = 'items';
|
||||
|
||||
/** @var array */
|
||||
protected $items;
|
||||
|
||||
/** @var Blueprint */
|
||||
/** @var Blueprint|callable|null */
|
||||
protected $blueprints;
|
||||
|
||||
/** @var File */
|
||||
/** @var FileInterface|null */
|
||||
protected $storage;
|
||||
|
||||
/** @var bool */
|
||||
private $missingValuesAsNull = false;
|
||||
/** @var bool */
|
||||
private $keepEmptyValues = true;
|
||||
|
||||
/**
|
||||
* @param array $items
|
||||
* @param Blueprint|callable $blueprints
|
||||
* @param Blueprint|callable|null $blueprints
|
||||
*/
|
||||
public function __construct(array $items = [], $blueprints = null)
|
||||
{
|
||||
$this->items = $items;
|
||||
$this->blueprints = $blueprints;
|
||||
if (null !== $blueprints) {
|
||||
$this->blueprints = $blueprints;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
* @return $this
|
||||
*/
|
||||
public function setKeepEmptyValues(bool $value)
|
||||
{
|
||||
$this->keepEmptyValues = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
* @return $this
|
||||
*/
|
||||
public function setMissingValuesAsNull(bool $value)
|
||||
{
|
||||
$this->missingValuesAsNull = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,20 +101,20 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
* @param mixed $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return $this
|
||||
* @throws \RuntimeException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function join($name, $value, $separator = '.')
|
||||
{
|
||||
$old = $this->get($name, null, $separator);
|
||||
if ($old !== null) {
|
||||
if (!\is_array($old)) {
|
||||
throw new \RuntimeException('Value ' . $old);
|
||||
if (!is_array($old)) {
|
||||
throw new RuntimeException('Value ' . $old);
|
||||
}
|
||||
|
||||
if (\is_object($value)) {
|
||||
if (is_object($value)) {
|
||||
$value = (array) $value;
|
||||
} elseif (!\is_array($value)) {
|
||||
throw new \RuntimeException('Value ' . $value);
|
||||
} elseif (!is_array($value)) {
|
||||
throw new RuntimeException('Value ' . $value);
|
||||
}
|
||||
|
||||
$value = $this->blueprints()->mergeData($old, $value, $name, $separator);
|
||||
@@ -110,7 +147,7 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
*/
|
||||
public function joinDefaults($name, $value, $separator = '.')
|
||||
{
|
||||
if (\is_object($value)) {
|
||||
if (is_object($value)) {
|
||||
$value = (array) $value;
|
||||
}
|
||||
|
||||
@@ -131,14 +168,14 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
* @param array|object $value Value to be joined.
|
||||
* @param string $separator Separator, defaults to '.'
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getJoined($name, $value, $separator = '.')
|
||||
{
|
||||
if (\is_object($value)) {
|
||||
if (is_object($value)) {
|
||||
$value = (array) $value;
|
||||
} elseif (!\is_array($value)) {
|
||||
throw new \RuntimeException('Value ' . $value);
|
||||
} elseif (!is_array($value)) {
|
||||
throw new RuntimeException('Value ' . $value);
|
||||
}
|
||||
|
||||
$old = $this->get($name, null, $separator);
|
||||
@@ -148,8 +185,8 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (!\is_array($old)) {
|
||||
throw new \RuntimeException('Value ' . $old);
|
||||
if (!is_array($old)) {
|
||||
throw new RuntimeException('Value ' . $old);
|
||||
}
|
||||
|
||||
// Return joined data.
|
||||
@@ -187,7 +224,7 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
* Validate by blueprints.
|
||||
*
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
@@ -202,8 +239,8 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
public function filter()
|
||||
{
|
||||
$args = func_get_args();
|
||||
$missingValuesAsNull = (bool)(array_shift($args) ?: false);
|
||||
$keepEmptyValues = (bool)(array_shift($args) ?: false);
|
||||
$missingValuesAsNull = (bool)(array_shift($args) ?? $this->missingValuesAsNull);
|
||||
$keepEmptyValues = (bool)(array_shift($args) ?? $this->keepEmptyValues);
|
||||
|
||||
$this->items = $this->blueprints()->filter($this->items, $missingValuesAsNull, $keepEmptyValues);
|
||||
|
||||
@@ -227,19 +264,22 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
*/
|
||||
public function blueprints()
|
||||
{
|
||||
if (!$this->blueprints){
|
||||
$this->blueprints = new Blueprint;
|
||||
} elseif (\is_callable($this->blueprints)) {
|
||||
if (!$this->blueprints) {
|
||||
$this->blueprints = new Blueprint();
|
||||
} elseif (is_callable($this->blueprints)) {
|
||||
// Lazy load blueprints.
|
||||
$blueprints = $this->blueprints;
|
||||
$this->blueprints = $blueprints();
|
||||
}
|
||||
|
||||
return $this->blueprints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save data if storage has been defined.
|
||||
* @throws \RuntimeException
|
||||
*
|
||||
* @return void
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
@@ -280,8 +320,8 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
/**
|
||||
* Set or get the data storage.
|
||||
*
|
||||
* @param FileInterface $storage Optionally enter a new storage.
|
||||
* @return FileInterface
|
||||
* @param FileInterface|null $storage Optionally enter a new storage.
|
||||
* @return FileInterface|null
|
||||
*/
|
||||
public function file(FileInterface $storage = null)
|
||||
{
|
||||
@@ -292,6 +332,9 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
|
||||
return $this->storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->items;
|
||||
|
||||
@@ -3,14 +3,19 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Exception;
|
||||
use RocketTheme\Toolbox\File\FileInterface;
|
||||
|
||||
/**
|
||||
* Interface DataInterface
|
||||
* @package Grav\Common\Data
|
||||
*/
|
||||
interface DataInterface
|
||||
{
|
||||
/**
|
||||
@@ -35,35 +40,44 @@ interface DataInterface
|
||||
|
||||
/**
|
||||
* Return blueprints.
|
||||
*
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function blueprints();
|
||||
|
||||
/**
|
||||
* Validate by blueprints.
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return $this
|
||||
* @throws Exception
|
||||
*/
|
||||
public function validate();
|
||||
|
||||
/**
|
||||
* Filter all items by using blueprints.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function filter();
|
||||
|
||||
/**
|
||||
* Get extra items which haven't been defined in blueprints.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extra();
|
||||
|
||||
/**
|
||||
* Save data into the file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save();
|
||||
|
||||
/**
|
||||
* Set or get the data storage.
|
||||
*
|
||||
* @param FileInterface $storage Optionally enter a new storage.
|
||||
* @param FileInterface|null $storage Optionally enter a new storage.
|
||||
* @return FileInterface
|
||||
*/
|
||||
public function file(FileInterface $storage = null);
|
||||
|
||||
@@ -3,16 +3,36 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use ArrayAccess;
|
||||
use Countable;
|
||||
use DateTime;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Security;
|
||||
use Grav\Common\User\Interfaces\UserInterface;
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Common\Yaml;
|
||||
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
|
||||
use Traversable;
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_float;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Class Validation
|
||||
* @package Grav\Common\Data
|
||||
*/
|
||||
class Validation
|
||||
{
|
||||
/**
|
||||
@@ -27,8 +47,9 @@ class Validation
|
||||
if (!isset($field['type'])) {
|
||||
$field['type'] = 'text';
|
||||
}
|
||||
$type = $validate['type'] ?? $field['type'];
|
||||
|
||||
$validate = (array)($field['validate'] ?? null);
|
||||
$type = $validate['type'] ?? $field['type'];
|
||||
$required = $validate['required'] ?? false;
|
||||
|
||||
// If value isn't required, we will stop validation if empty value is given.
|
||||
@@ -43,7 +64,7 @@ class Validation
|
||||
$name = ucfirst($field['label'] ?? $field['name']);
|
||||
$message = (string) isset($field['validate']['message'])
|
||||
? $language->translate($field['validate']['message'])
|
||||
: $language->translate('GRAV.FORM.INVALID_INPUT', null, true) . ' "' . $language->translate($name) . '"';
|
||||
: $language->translate('GRAV.FORM.INVALID_INPUT') . ' "' . $language->translate($name) . '"';
|
||||
|
||||
|
||||
// Validate type with fallback type text.
|
||||
@@ -77,6 +98,92 @@ class Validation
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $field
|
||||
* @return array
|
||||
*/
|
||||
public static function checkSafety($value, array $field)
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
$type = $field['validate']['type'] ?? $field['type'] ?? 'text';
|
||||
$options = $field['xss_check'] ?? [];
|
||||
if ($options === false || $type === 'unset') {
|
||||
return $messages;
|
||||
}
|
||||
if (!is_array($options)) {
|
||||
$options = [];
|
||||
}
|
||||
|
||||
$name = ucfirst($field['label'] ?? $field['name'] ?? 'UNKNOWN');
|
||||
|
||||
/** @var UserInterface $user */
|
||||
$user = Grav::instance()['user'] ?? null;
|
||||
/** @var Config $config */
|
||||
$config = Grav::instance()['config'];
|
||||
|
||||
$xss_whitelist = $config->get('security.xss_whitelist', 'admin.super');
|
||||
|
||||
// Get language class.
|
||||
/** @var Language $language */
|
||||
$language = Grav::instance()['language'];
|
||||
|
||||
if (!static::authorize($xss_whitelist, $user)) {
|
||||
$defaults = Security::getXssDefaults();
|
||||
$options += $defaults;
|
||||
$options['enabled_rules'] += $defaults['enabled_rules'];
|
||||
if (!empty($options['safe_protocols'])) {
|
||||
$options['invalid_protocols'] = array_diff($options['invalid_protocols'], $options['safe_protocols']);
|
||||
}
|
||||
if (!empty($options['safe_tags'])) {
|
||||
$options['dangerous_tags'] = array_diff($options['dangerous_tags'], $options['safe_tags']);
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$violation = Security::detectXss($value, $options);
|
||||
if ($violation) {
|
||||
$messages[$name][] = $language->translate(['GRAV.FORM.XSS_ISSUES', $language->translate($name)], null, true);
|
||||
}
|
||||
} elseif (is_array($value)) {
|
||||
$violations = Security::detectXssFromArray($value, "{$name}.", $options);
|
||||
if ($violations) {
|
||||
$messages[$name][] = $language->translate(['GRAV.FORM.XSS_ISSUES', $language->translate($name)], null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks user authorisation to the action.
|
||||
*
|
||||
* @param string|string[] $action
|
||||
* @param UserInterface|null $user
|
||||
* @return bool
|
||||
*/
|
||||
public static function authorize($action, UserInterface $user = null)
|
||||
{
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$action = (array)$action;
|
||||
foreach ($action as $a) {
|
||||
// Ignore 'admin.super' if it's not the only value to be checked.
|
||||
if ($a === 'admin.super' && count($action) > 1 && $user instanceof FlexObjectInterface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($user->authorize($a)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter value against a blueprint field definition.
|
||||
*
|
||||
@@ -122,7 +229,7 @@ class Validation
|
||||
*/
|
||||
public static function typeText($value, array $params, array $field)
|
||||
{
|
||||
if (!\is_string($value) && !is_numeric($value)) {
|
||||
if (!is_string($value) && !is_numeric($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -132,16 +239,16 @@ class Validation
|
||||
$value = trim($value);
|
||||
}
|
||||
|
||||
if (isset($params['min']) && \strlen($value) < $params['min']) {
|
||||
if (isset($params['min']) && strlen($value) < $params['min']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($params['max']) && \strlen($value) > $params['max']) {
|
||||
if (isset($params['max']) && strlen($value) > $params['max']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$min = $params['min'] ?? 0;
|
||||
if (isset($params['step']) && (\strlen($value) - $min) % $params['step'] === 0) {
|
||||
if (isset($params['step']) && (strlen($value) - $min) % $params['step'] === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -152,9 +259,15 @@ class Validation
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return string
|
||||
*/
|
||||
protected static function filterText($value, array $params, array $field)
|
||||
{
|
||||
if (!\is_string($value) && !is_numeric($value)) {
|
||||
if (!is_string($value) && !is_numeric($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -165,21 +278,68 @@ class Validation
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return string|null
|
||||
*/
|
||||
protected static function filterCheckbox($value, array $params, array $field)
|
||||
{
|
||||
$value = (string)$value;
|
||||
$field_value = (string)($field['value'] ?? '1');
|
||||
|
||||
return $value === $field_value ? $value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array|array[]|false|string[]
|
||||
*/
|
||||
protected static function filterCommaList($value, array $params, array $field)
|
||||
{
|
||||
return \is_array($value) ? $value : preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
return is_array($value) ? $value : preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return bool
|
||||
*/
|
||||
public static function typeCommaList($value, array $params, array $field)
|
||||
{
|
||||
return \is_array($value) ? true : self::typeText($value, $params, $field);
|
||||
return is_array($value) ? true : self::typeText($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array|array[]|false|string[]
|
||||
*/
|
||||
protected static function filterLines($value, array $params, array $field)
|
||||
{
|
||||
return is_array($value) ? $value : preg_split('/\s*[\r\n]+\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @return string
|
||||
*/
|
||||
protected static function filterLower($value, array $params)
|
||||
{
|
||||
return strtolower($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @return string
|
||||
*/
|
||||
protected static function filterUpper($value, array $params)
|
||||
{
|
||||
return strtoupper($value);
|
||||
@@ -245,6 +405,12 @@ class Validation
|
||||
return self::typeArray((array) $value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array|null
|
||||
*/
|
||||
protected static function filterCheckboxes($value, array $params, array $field)
|
||||
{
|
||||
return self::filterArray($value, $params, $field);
|
||||
@@ -289,7 +455,7 @@ class Validation
|
||||
*/
|
||||
public static function typeToggle($value, array $params, array $field)
|
||||
{
|
||||
if (\is_bool($value)) {
|
||||
if (is_bool($value)) {
|
||||
$value = (int)$value;
|
||||
}
|
||||
|
||||
@@ -309,6 +475,12 @@ class Validation
|
||||
return self::typeArray((array)$value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array
|
||||
*/
|
||||
protected static function filterFile($value, array $params, array $field)
|
||||
{
|
||||
return (array)$value;
|
||||
@@ -354,22 +526,33 @@ class Validation
|
||||
return !(isset($params['step']) && fmod($value - $min, $params['step']) === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return float|int
|
||||
*/
|
||||
protected static function filterNumber($value, array $params, array $field)
|
||||
{
|
||||
return (string)(int)$value !== (string)(float)$value ? (float) $value : (int) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return string
|
||||
*/
|
||||
protected static function filterDateTime($value, array $params, array $field)
|
||||
{
|
||||
$format = Grav::instance()['config']->get('system.pages.dateformat.default');
|
||||
if ($format) {
|
||||
$converted = new \DateTime($value);
|
||||
$converted = new DateTime($value);
|
||||
return $converted->format($format);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* HTML5 input: range
|
||||
*
|
||||
@@ -383,6 +566,12 @@ class Validation
|
||||
return self::typeNumber($value, $params, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return float|int
|
||||
*/
|
||||
protected static function filterRange($value, array $params, array $field)
|
||||
{
|
||||
return self::filterNumber($value, $params, $field);
|
||||
@@ -411,7 +600,7 @@ class Validation
|
||||
*/
|
||||
public static function typeEmail($value, array $params, array $field)
|
||||
{
|
||||
$values = !\is_array($value) ? explode(',', preg_replace('/\s+/', '', $value)) : $value;
|
||||
$values = !is_array($value) ? explode(',', preg_replace('/\s+/', '', $value)) : $value;
|
||||
|
||||
foreach ($values as $val) {
|
||||
if (!(self::typeText($val, $params, $field) && filter_var($val, FILTER_VALIDATE_EMAIL))) {
|
||||
@@ -430,7 +619,6 @@ class Validation
|
||||
* @param array $field Blueprint for the field.
|
||||
* @return bool True if validation succeeded.
|
||||
*/
|
||||
|
||||
public static function typeUrl($value, array $params, array $field)
|
||||
{
|
||||
return self::typeText($value, $params, $field) && filter_var($value, FILTER_VALIDATE_URL);
|
||||
@@ -446,17 +634,17 @@ class Validation
|
||||
*/
|
||||
public static function typeDatetime($value, array $params, array $field)
|
||||
{
|
||||
if ($value instanceof \DateTime) {
|
||||
if ($value instanceof DateTime) {
|
||||
return true;
|
||||
}
|
||||
if (!\is_string($value)) {
|
||||
if (!is_string($value)) {
|
||||
return false;
|
||||
}
|
||||
if (!isset($params['format'])) {
|
||||
return false !== strtotime($value);
|
||||
}
|
||||
|
||||
$dateFromFormat = \DateTime::createFromFormat($params['format'], $value);
|
||||
$dateFromFormat = DateTime::createFromFormat($params['format'], $value);
|
||||
|
||||
return $dateFromFormat && $value === date($params['format'], $dateFromFormat->getTimestamp());
|
||||
}
|
||||
@@ -552,25 +740,30 @@ class Validation
|
||||
*/
|
||||
public static function typeArray($value, array $params, array $field)
|
||||
{
|
||||
if (!\is_array($value)) {
|
||||
if (!is_array($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($field['multiple'])) {
|
||||
if (isset($params['min']) && \count($value) < $params['min']) {
|
||||
if (isset($params['min']) && count($value) < $params['min']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($params['max']) && \count($value) > $params['max']) {
|
||||
if (isset($params['max']) && count($value) > $params['max']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$min = $params['min'] ?? 0;
|
||||
if (isset($params['step']) && (\count($value) - $min) % $params['step'] === 0) {
|
||||
if (isset($params['step']) && (count($value) - $min) % $params['step'] === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If creating new values is allowed, no further checks are needed.
|
||||
if (!empty($field['selectize']['create'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$options = $field['options'] ?? [];
|
||||
$use = $field['use'] ?? 'values';
|
||||
|
||||
@@ -584,13 +777,32 @@ class Validation
|
||||
return !($options && array_diff($value, $options));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array|null
|
||||
*/
|
||||
protected static function filterFlatten_array($value, $params, $field)
|
||||
{
|
||||
$value = static::filterArray($value, $params, $field);
|
||||
|
||||
return Utils::arrayUnflattenDotNotation($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array|null
|
||||
*/
|
||||
protected static function filterArray($value, $params, $field)
|
||||
{
|
||||
$values = (array) $value;
|
||||
$options = isset($field['options']) ? array_keys($field['options']) : [];
|
||||
$multi = $field['multiple'] ?? false;
|
||||
|
||||
if (\count($values) === 1 && isset($values[0]) && $values[0] === '') {
|
||||
if (count($values) === 1 && isset($values[0]) && $values[0] === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -604,7 +816,7 @@ class Validation
|
||||
|
||||
if ($multi) {
|
||||
foreach ($values as $key => $val) {
|
||||
if (\is_array($val)) {
|
||||
if (is_array($val)) {
|
||||
$val = implode(',', $val);
|
||||
$values[$key] = array_map('trim', explode(',', $val));
|
||||
} else {
|
||||
@@ -613,28 +825,90 @@ class Validation
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($field['ignore_empty']) && Utils::isPositive($field['ignore_empty'])) {
|
||||
foreach ($values as $key => $val) {
|
||||
if ($val === '') {
|
||||
$ignoreEmpty = isset($field['ignore_empty']) && Utils::isPositive($field['ignore_empty']);
|
||||
$valueType = $params['value_type'] ?? null;
|
||||
$keyType = $params['key_type'] ?? null;
|
||||
if ($ignoreEmpty || $valueType || $keyType) {
|
||||
$values = static::arrayFilterRecurse($values, ['value_type' => $valueType, 'key_type' => $keyType, 'ignore_empty' => $ignoreEmpty]);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
protected static function arrayFilterRecurse(array $values, array $params): array
|
||||
{
|
||||
foreach ($values as $key => &$val) {
|
||||
if ($params['key_type']) {
|
||||
switch ($params['key_type']) {
|
||||
case 'int':
|
||||
$result = is_int($key);
|
||||
break;
|
||||
case 'string':
|
||||
$result = is_string($key);
|
||||
break;
|
||||
default:
|
||||
$result = false;
|
||||
}
|
||||
if (!$result) {
|
||||
unset($values[$key]);
|
||||
} elseif (\is_array($val)) {
|
||||
foreach ($val as $inner_key => $inner_value) {
|
||||
if ($inner_value === '') {
|
||||
unset($val[$inner_key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_array($val)) {
|
||||
$val = static::arrayFilterRecurse($val, $params);
|
||||
if ($params['ignore_empty'] && empty($val)) {
|
||||
unset($values[$key]);
|
||||
}
|
||||
} else {
|
||||
if ($params['value_type'] && $val !== '' && $val !== null) {
|
||||
switch ($params['value_type']) {
|
||||
case 'bool':
|
||||
if (Utils::isPositive($val)) {
|
||||
$val = true;
|
||||
} elseif (Utils::isNegative($val)) {
|
||||
$val = false;
|
||||
} else {
|
||||
// Ignore invalid bool values.
|
||||
$val = null;
|
||||
}
|
||||
break;
|
||||
case 'int':
|
||||
$val = (int)$val;
|
||||
break;
|
||||
case 'float':
|
||||
$val = (float)$val;
|
||||
break;
|
||||
case 'string':
|
||||
$val = (string)$val;
|
||||
break;
|
||||
case 'trim':
|
||||
$val = trim($val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$values[$key] = $val;
|
||||
if ($params['ignore_empty'] && ($val === '' || $val === null)) {
|
||||
unset($values[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return bool
|
||||
*/
|
||||
public static function typeList($value, array $params, array $field)
|
||||
{
|
||||
if (!\is_array($value)) {
|
||||
if (!is_array($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -651,19 +925,29 @@ class Validation
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return array
|
||||
*/
|
||||
protected static function filterList($value, array $params, array $field)
|
||||
{
|
||||
return (array) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function filterYaml($value, $params)
|
||||
{
|
||||
if (!\is_string($value)) {
|
||||
if (!is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return (array) Yaml::parse($value);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -679,6 +963,12 @@ class Validation
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return mixed
|
||||
*/
|
||||
public static function filterIgnore($value, array $params, array $field)
|
||||
{
|
||||
return $value;
|
||||
@@ -697,6 +987,12 @@ class Validation
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $params
|
||||
* @param array $field
|
||||
* @return null
|
||||
*/
|
||||
public static function filterUnset($value, array $params, array $field)
|
||||
{
|
||||
return null;
|
||||
@@ -704,6 +1000,11 @@ class Validation
|
||||
|
||||
// HTML5 attributes (min, max and range are handled inside the types)
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param bool $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateRequired($value, $params)
|
||||
{
|
||||
if (is_scalar($value)) {
|
||||
@@ -713,79 +1014,155 @@ class Validation
|
||||
return (bool) $params !== true || !empty($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param string $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validatePattern($value, $params)
|
||||
{
|
||||
return (bool) preg_match("`^{$params}$`u", $value);
|
||||
}
|
||||
|
||||
|
||||
// Internal types
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateAlpha($value, $params)
|
||||
{
|
||||
return ctype_alpha($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateAlnum($value, $params)
|
||||
{
|
||||
return ctype_alnum($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function typeBool($value, $params)
|
||||
{
|
||||
return \is_bool($value) || $value == 1 || $value == 0;
|
||||
return is_bool($value) || $value == 1 || $value == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateBool($value, $params)
|
||||
{
|
||||
return \is_bool($value) || $value == 1 || $value == 0;
|
||||
return is_bool($value) || $value == 1 || $value == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
protected static function filterBool($value, $params)
|
||||
{
|
||||
return (bool) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateDigit($value, $params)
|
||||
{
|
||||
return ctype_digit($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateFloat($value, $params)
|
||||
{
|
||||
return \is_float(filter_var($value, FILTER_VALIDATE_FLOAT));
|
||||
return is_float(filter_var($value, FILTER_VALIDATE_FLOAT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return float
|
||||
*/
|
||||
protected static function filterFloat($value, $params)
|
||||
{
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateHex($value, $params)
|
||||
{
|
||||
return ctype_xdigit($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateInt($value, $params)
|
||||
{
|
||||
return is_numeric($value) && (int)$value == $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return int
|
||||
*/
|
||||
protected static function filterInt($value, $params)
|
||||
{
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateArray($value, $params)
|
||||
{
|
||||
return \is_array($value) || ($value instanceof \ArrayAccess && $value instanceof \Traversable && $value instanceof \Countable);
|
||||
return is_array($value) || ($value instanceof ArrayAccess && $value instanceof Traversable && $value instanceof Countable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return array
|
||||
*/
|
||||
public static function filterItem_List($value, $params)
|
||||
{
|
||||
return array_values(array_filter($value, function($v) { return !empty($v); } ));
|
||||
return array_values(array_filter($value, function ($v) {
|
||||
return !empty($v);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $params
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateJson($value, $params)
|
||||
{
|
||||
return (bool) (@json_decode($value));
|
||||
|
||||
@@ -3,19 +3,30 @@
|
||||
/**
|
||||
* @package Grav\Common\Data
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Data;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use RuntimeException;
|
||||
|
||||
class ValidationException extends \RuntimeException
|
||||
/**
|
||||
* Class ValidationException
|
||||
* @package Grav\Common\Data
|
||||
*/
|
||||
class ValidationException extends RuntimeException
|
||||
{
|
||||
/** @var array */
|
||||
protected $messages = [];
|
||||
|
||||
public function setMessages(array $messages = []) {
|
||||
/**
|
||||
* @param array $messages
|
||||
* @return $this
|
||||
*/
|
||||
public function setMessages(array $messages = [])
|
||||
{
|
||||
$this->messages = $messages;
|
||||
|
||||
$language = Grav::instance()['language'];
|
||||
@@ -31,6 +42,9 @@ class ValidationException extends \RuntimeException
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMessages()
|
||||
{
|
||||
return $this->messages;
|
||||
|
||||
@@ -3,12 +3,19 @@
|
||||
/**
|
||||
* @package Grav\Common
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (C) 2015 - 2020 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common;
|
||||
|
||||
use Clockwork\Clockwork;
|
||||
use Clockwork\DataSource\MonologDataSource;
|
||||
use Clockwork\DataSource\PsrMessageDataSource;
|
||||
use Clockwork\DataSource\XdebugDataSource;
|
||||
use Clockwork\Helpers\ServerTiming;
|
||||
use Clockwork\Request\UserData;
|
||||
use Clockwork\Storage\FileStorage;
|
||||
use DebugBar\DataCollector\ConfigCollector;
|
||||
use DebugBar\DataCollector\DataCollectorInterface;
|
||||
use DebugBar\DataCollector\ExceptionsCollector;
|
||||
@@ -18,76 +25,106 @@ use DebugBar\DataCollector\PhpInfoCollector;
|
||||
use DebugBar\DataCollector\RequestDataCollector;
|
||||
use DebugBar\DataCollector\TimeDataCollector;
|
||||
use DebugBar\DebugBar;
|
||||
use DebugBar\DebugBarException;
|
||||
use DebugBar\JavascriptRenderer;
|
||||
use DebugBar\StandardDebugBar;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Processors\ProcessorInterface;
|
||||
use Grav\Common\Twig\TwigClockworkDataSource;
|
||||
use Grav\Framework\Psr7\Response;
|
||||
use Monolog\Logger;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use ReflectionObject;
|
||||
use SplFileInfo;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Throwable;
|
||||
use Twig\Environment;
|
||||
use Twig\Template;
|
||||
use Twig\TemplateWrapper;
|
||||
use function array_slice;
|
||||
use function call_user_func;
|
||||
use function count;
|
||||
use function define;
|
||||
use function defined;
|
||||
use function extension_loaded;
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_object;
|
||||
use function is_scalar;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
* Class Debugger
|
||||
* @package Grav\Common
|
||||
*/
|
||||
class Debugger
|
||||
{
|
||||
/** @var Grav $grav */
|
||||
/** @var static */
|
||||
protected static $instance;
|
||||
/** @var Grav|null */
|
||||
protected $grav;
|
||||
|
||||
/** @var Config $config */
|
||||
/** @var Config|null */
|
||||
protected $config;
|
||||
|
||||
/** @var JavascriptRenderer $renderer */
|
||||
/** @var JavascriptRenderer|null */
|
||||
protected $renderer;
|
||||
|
||||
/** @var StandardDebugBar $debugbar */
|
||||
/** @var DebugBar|null */
|
||||
protected $debugbar;
|
||||
|
||||
/** @var Clockwork|null */
|
||||
protected $clockwork;
|
||||
/** @var bool */
|
||||
protected $enabled = false;
|
||||
/** @var bool */
|
||||
protected $enabled;
|
||||
|
||||
protected $initialized = false;
|
||||
|
||||
/** @var array */
|
||||
protected $timers = [];
|
||||
|
||||
/** @var array $deprecations */
|
||||
/** @var array */
|
||||
protected $deprecations = [];
|
||||
|
||||
/** @var callable */
|
||||
/** @var callable|null */
|
||||
protected $errorHandler;
|
||||
/** @var float */
|
||||
protected $requestTime;
|
||||
/** @var float */
|
||||
protected $currentTime;
|
||||
/** @var int */
|
||||
protected $profiling = 0;
|
||||
/** @var bool */
|
||||
protected $censored = false;
|
||||
|
||||
/**
|
||||
* Debugger constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$currentTime = microtime(true);
|
||||
static::$instance = $this;
|
||||
|
||||
if (!\defined('GRAV_REQUEST_TIME')) {
|
||||
\define('GRAV_REQUEST_TIME', $currentTime);
|
||||
$this->currentTime = microtime(true);
|
||||
|
||||
if (!defined('GRAV_REQUEST_TIME')) {
|
||||
define('GRAV_REQUEST_TIME', $this->currentTime);
|
||||
}
|
||||
|
||||
// Enable debugger until $this->init() gets called.
|
||||
$this->enabled = true;
|
||||
|
||||
$debugbar = new DebugBar();
|
||||
$debugbar->addCollector(new PhpInfoCollector());
|
||||
$debugbar->addCollector(new MessagesCollector());
|
||||
$debugbar->addCollector(new RequestDataCollector());
|
||||
$debugbar->addCollector(new TimeDataCollector($_SERVER['REQUEST_TIME_FLOAT'] ?? GRAV_REQUEST_TIME));
|
||||
|
||||
$debugbar['time']->addMeasure('Server', $debugbar['time']->getRequestStartTime(), GRAV_REQUEST_TIME);
|
||||
$debugbar['time']->addMeasure('Loading', GRAV_REQUEST_TIME, $currentTime);
|
||||
$debugbar['time']->addMeasure('Debugger', $currentTime, microtime(true));
|
||||
|
||||
$this->debugbar = $debugbar;
|
||||
$this->requestTime = $_SERVER['REQUEST_TIME_FLOAT'] ?? GRAV_REQUEST_TIME;
|
||||
|
||||
// Set deprecation collector.
|
||||
$this->setErrorHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Clockwork|null
|
||||
*/
|
||||
public function getClockwork(): ?Clockwork
|
||||
{
|
||||
return $this->enabled ? $this->clockwork : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the debugger
|
||||
*
|
||||
* @return $this
|
||||
* @throws \DebugBar\DebugBarException
|
||||
* @throws DebugBarException
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
@@ -100,30 +137,242 @@ class Debugger
|
||||
|
||||
// Enable/disable debugger based on configuration.
|
||||
$this->enabled = (bool)$this->config->get('system.debugger.enabled');
|
||||
$this->censored = (bool)$this->config->get('system.debugger.censored', false);
|
||||
|
||||
if ($this->enabled()) {
|
||||
if ($this->enabled) {
|
||||
$this->initialized = true;
|
||||
|
||||
$plugins_config = (array)$this->config->get('plugins');
|
||||
$clockwork = $debugbar = null;
|
||||
|
||||
switch ($this->config->get('system.debugger.provider', 'debugbar')) {
|
||||
case 'clockwork':
|
||||
$this->clockwork = $clockwork = new Clockwork();
|
||||
break;
|
||||
default:
|
||||
$this->debugbar = $debugbar = new DebugBar();
|
||||
}
|
||||
|
||||
$plugins_config = (array)$this->config->get('plugins');
|
||||
ksort($plugins_config);
|
||||
|
||||
$debugbar = $this->debugbar;
|
||||
$debugbar->addCollector(new MemoryCollector());
|
||||
$debugbar->addCollector(new ExceptionsCollector());
|
||||
$debugbar->addCollector(new ConfigCollector((array)$this->config->get('system'), 'Config'));
|
||||
$debugbar->addCollector(new ConfigCollector($plugins_config, 'Plugins'));
|
||||
$this->addMessage('Grav v' . GRAV_VERSION);
|
||||
if ($clockwork) {
|
||||
$log = $this->grav['log'];
|
||||
$clockwork->setStorage(new FileStorage('cache://clockwork'));
|
||||
if (extension_loaded('xdebug')) {
|
||||
$clockwork->addDataSource(new XdebugDataSource());
|
||||
}
|
||||
if ($log instanceof Logger) {
|
||||
$clockwork->addDataSource(new MonologDataSource($log));
|
||||
}
|
||||
|
||||
$timeline = $clockwork->timeline();
|
||||
if ($this->requestTime !== GRAV_REQUEST_TIME) {
|
||||
$event = $timeline->event('Server');
|
||||
$event->finalize($this->requestTime, GRAV_REQUEST_TIME);
|
||||
}
|
||||
if ($this->currentTime !== GRAV_REQUEST_TIME) {
|
||||
$event = $timeline->event('Loading');
|
||||
$event->finalize(GRAV_REQUEST_TIME, $this->currentTime);
|
||||
}
|
||||
$event = $timeline->event('Site Setup');
|
||||
$event->finalize($this->currentTime, microtime(true));
|
||||
}
|
||||
|
||||
if ($this->censored) {
|
||||
$censored = ['CENSORED' => true];
|
||||
}
|
||||
|
||||
if ($debugbar) {
|
||||
$debugbar->addCollector(new PhpInfoCollector());
|
||||
$debugbar->addCollector(new MessagesCollector());
|
||||
if (!$this->censored) {
|
||||
$debugbar->addCollector(new RequestDataCollector());
|
||||
}
|
||||
$debugbar->addCollector(new TimeDataCollector($this->requestTime));
|
||||
$debugbar->addCollector(new MemoryCollector());
|
||||
$debugbar->addCollector(new ExceptionsCollector());
|
||||
$debugbar->addCollector(new ConfigCollector($censored ?? (array)$this->config->get('system'), 'Config'));
|
||||
$debugbar->addCollector(new ConfigCollector($censored ?? $plugins_config, 'Plugins'));
|
||||
$debugbar->addCollector(new ConfigCollector($this->config->get('streams.schemes'), 'Streams'));
|
||||
|
||||
if ($this->requestTime !== GRAV_REQUEST_TIME) {
|
||||
$debugbar['time']->addMeasure('Server', $debugbar['time']->getRequestStartTime(), GRAV_REQUEST_TIME);
|
||||
}
|
||||
if ($this->currentTime !== GRAV_REQUEST_TIME) {
|
||||
$debugbar['time']->addMeasure('Loading', GRAV_REQUEST_TIME, $this->currentTime);
|
||||
}
|
||||
$debugbar['time']->addMeasure('Site Setup', $this->currentTime, microtime(true));
|
||||
}
|
||||
|
||||
$this->addMessage('Grav v' . GRAV_VERSION . ' - PHP ' . PHP_VERSION);
|
||||
$this->config->debug();
|
||||
|
||||
if ($clockwork) {
|
||||
$clockwork->info('System Configuration', $censored ?? $this->config->get('system'));
|
||||
$clockwork->info('Plugins Configuration', $censored ?? $plugins_config);
|
||||
$clockwork->info('Streams', $this->config->get('streams.schemes'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function finalize(): void
|
||||
{
|
||||
if ($this->clockwork && $this->enabled) {
|
||||
$this->stopProfiling('Profiler Analysis');
|
||||
$this->addMeasures();
|
||||
|
||||
$deprecations = $this->getDeprecations();
|
||||
$count = count($deprecations);
|
||||
if (!$count) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var UserData $userData */
|
||||
$userData = $this->clockwork->userData('Deprecated');
|
||||
$userData->counters([
|
||||
'Deprecated' => count($deprecations)
|
||||
]);
|
||||
/*
|
||||
foreach ($deprecations as &$deprecation) {
|
||||
$d = $deprecation;
|
||||
unset($d['message']);
|
||||
$this->clockwork->log('deprecated', $deprecation['message'], $d);
|
||||
}
|
||||
unset($deprecation);
|
||||
*/
|
||||
|
||||
$userData->table('Your site is using following deprecated features', $deprecations);
|
||||
}
|
||||
}
|
||||
|
||||
public function logRequest(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
if (!$this->enabled || !$this->clockwork) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$clockwork = $this->clockwork;
|
||||
|
||||
$this->finalize();
|
||||
|
||||
$clockwork->timeline()->finalize($request->getAttribute('request_time'));
|
||||
|
||||
if ($this->censored) {
|
||||
$censored = 'CENSORED';
|
||||
$request = $request
|
||||
->withCookieParams([$censored => ''])
|
||||
->withUploadedFiles([])
|
||||
->withHeader('cookie', $censored);
|
||||
$request = $request->withParsedBody([$censored => '']);
|
||||
}
|
||||
|
||||
$clockwork->addDataSource(new PsrMessageDataSource($request, $response));
|
||||
|
||||
$clockwork->resolveRequest();
|
||||
$clockwork->storeRequest();
|
||||
|
||||
$clockworkRequest = $clockwork->getRequest();
|
||||
|
||||
$response = $response
|
||||
->withHeader('X-Clockwork-Id', $clockworkRequest->id)
|
||||
->withHeader('X-Clockwork-Version', $clockwork::VERSION);
|
||||
|
||||
$grav = Grav::instance();
|
||||
$basePath = $this->grav['base_url_relative'] . $grav['pages']->base();
|
||||
if ($basePath) {
|
||||
$response = $response->withHeader('X-Clockwork-Path', $basePath . '/__clockwork/');
|
||||
}
|
||||
|
||||
return $response->withHeader('Server-Timing', ServerTiming::fromRequest($clockworkRequest)->value());
|
||||
}
|
||||
|
||||
|
||||
public function debuggerRequest(RequestInterface $request): Response
|
||||
{
|
||||
$clockwork = $this->clockwork;
|
||||
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'Grav-Internal-SkipShutdown' => 1
|
||||
];
|
||||
|
||||
$path = $request->getUri()->getPath();
|
||||
$clockworkDataUri = '#/__clockwork(?:/(?<id>[0-9-]+))?(?:/(?<direction>(?:previous|next)))?(?:/(?<count>\d+))?#';
|
||||
if (preg_match($clockworkDataUri, $path, $matches) === false) {
|
||||
$response = ['message' => 'Bad Input'];
|
||||
|
||||
return new Response(400, $headers, json_encode($response));
|
||||
}
|
||||
|
||||
$id = $matches['id'] ?? null;
|
||||
$direction = $matches['direction'] ?? null;
|
||||
$count = $matches['count'] ?? null;
|
||||
|
||||
$storage = $clockwork->getStorage();
|
||||
|
||||
if ($direction === 'previous') {
|
||||
$data = $storage->previous($id, $count);
|
||||
} elseif ($direction === 'next') {
|
||||
$data = $storage->next($id, $count);
|
||||
} elseif ($id === 'latest') {
|
||||
$data = $storage->latest();
|
||||
} else {
|
||||
$data = $storage->find($id);
|
||||
}
|
||||
|
||||
if (preg_match('#(?<id>[0-9-]+|latest)/extended#', $path)) {
|
||||
$clockwork->extendRequest($data);
|
||||
}
|
||||
|
||||
if (!$data) {
|
||||
$response = ['message' => 'Not Found'];
|
||||
|
||||
return new Response(404, $headers, json_encode($response));
|
||||
}
|
||||
|
||||
$data = is_array($data) ? array_map(function ($item) {
|
||||
return $item->toArray();
|
||||
}, $data) : $data->toArray();
|
||||
|
||||
return new Response(200, $headers, json_encode($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function addMeasures(): void
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nowTime = microtime(true);
|
||||
$clkTimeLine = $this->clockwork ? $this->clockwork->timeline() : null;
|
||||
$debTimeLine = $this->debugbar ? $this->debugbar['time'] : null;
|
||||
foreach ($this->timers as $name => $data) {
|
||||
$description = $data[0];
|
||||
$startTime = $data[1] ?? null;
|
||||
$endTime = $data[2] ?? $nowTime;
|
||||
if ($clkTimeLine) {
|
||||
$event = $clkTimeLine->event($description);
|
||||
$event->finalize($startTime, $endTime);
|
||||
} elseif ($debTimeLine) {
|
||||
if ($endTime - $startTime < 0.001) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$debTimeLine->addMeasure($description ?? $name, $startTime, $endTime);
|
||||
}
|
||||
}
|
||||
$this->timers = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set/get the enabled state of the debugger
|
||||
*
|
||||
* @param bool $state If null, the method returns the enabled value. If set, the method sets the enabled state
|
||||
*
|
||||
* @param bool|null $state If null, the method returns the enabled value. If set, the method sets the enabled state
|
||||
* @return bool
|
||||
*/
|
||||
public function enabled($state = null)
|
||||
@@ -142,8 +391,7 @@ class Debugger
|
||||
*/
|
||||
public function addAssets()
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
|
||||
if ($this->enabled) {
|
||||
// Only add assets if Page is HTML
|
||||
$page = $this->grav['page'];
|
||||
if ($page->templateFormat() !== 'html') {
|
||||
@@ -153,28 +401,42 @@ class Debugger
|
||||
/** @var Assets $assets */
|
||||
$assets = $this->grav['assets'];
|
||||
|
||||
// Add jquery library
|
||||
$assets->add('jquery', 101);
|
||||
|
||||
$this->renderer = $this->debugbar->getJavascriptRenderer();
|
||||
$this->renderer->setIncludeVendors(false);
|
||||
|
||||
// Get the required CSS files
|
||||
list($css_files, $js_files) = $this->renderer->getAssets(null, JavascriptRenderer::RELATIVE_URL);
|
||||
foreach ((array)$css_files as $css) {
|
||||
$assets->addCss($css);
|
||||
// Clockwork specific assets
|
||||
if ($this->clockwork) {
|
||||
$assets->addCss('/system/assets/debugger/clockwork.css', ['loading' => 'inline']);
|
||||
$assets->addJs('/system/assets/debugger/clockwork.js', ['loading' => 'inline']);
|
||||
}
|
||||
|
||||
$assets->addCss('/system/assets/debugger.css');
|
||||
|
||||
foreach ((array)$js_files as $js) {
|
||||
$assets->addJs($js);
|
||||
// Debugbar specific assets
|
||||
if ($this->debugbar) {
|
||||
// Add jquery library
|
||||
$assets->add('jquery', 101);
|
||||
|
||||
$this->renderer = $this->debugbar->getJavascriptRenderer();
|
||||
$this->renderer->setIncludeVendors(false);
|
||||
|
||||
[$css_files, $js_files] = $this->renderer->getAssets(null, JavascriptRenderer::RELATIVE_URL);
|
||||
|
||||
foreach ((array)$css_files as $css) {
|
||||
$assets->addCss($css);
|
||||
}
|
||||
|
||||
$assets->addCss('/system/assets/debugger/phpdebugbar.css', ['loading' => 'inline']);
|
||||
|
||||
foreach ((array)$js_files as $js) {
|
||||
$assets->addJs($js);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getCaller($limit = 2)
|
||||
{
|
||||
$trace = debug_backtrace(false, $limit);
|
||||
@@ -186,13 +448,14 @@ class Debugger
|
||||
* Adds a data collector
|
||||
*
|
||||
* @param DataCollectorInterface $collector
|
||||
*
|
||||
* @return $this
|
||||
* @throws \DebugBar\DebugBarException
|
||||
* @throws DebugBarException
|
||||
*/
|
||||
public function addCollector($collector)
|
||||
{
|
||||
$this->debugbar->addCollector($collector);
|
||||
if ($this->debugbar && !$this->debugbar->hasCollector($collector->getName())) {
|
||||
$this->debugbar->addCollector($collector);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -200,14 +463,17 @@ class Debugger
|
||||
/**
|
||||
* Returns a data collector
|
||||
*
|
||||
* @param DataCollectorInterface $collector
|
||||
*
|
||||
* @return DataCollectorInterface
|
||||
* @throws \DebugBar\DebugBarException
|
||||
* @param string $name
|
||||
* @return DataCollectorInterface|null
|
||||
* @throws DebugBarException
|
||||
*/
|
||||
public function getCollector($collector)
|
||||
public function getCollector($name)
|
||||
{
|
||||
return $this->debugbar->getCollector($collector);
|
||||
if ($this->debugbar && $this->debugbar->hasCollector($name)) {
|
||||
return $this->debugbar->getCollector($name);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,13 +483,14 @@ class Debugger
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
if ($this->enabled && $this->debugbar) {
|
||||
// Only add assets if Page is HTML
|
||||
$page = $this->grav['page'];
|
||||
if (!$this->renderer || $page->templateFormat() !== 'html') {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->addMeasures();
|
||||
$this->addDeprecations();
|
||||
|
||||
echo $this->renderer->render();
|
||||
@@ -239,7 +506,8 @@ class Debugger
|
||||
*/
|
||||
public function sendDataInHeaders()
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
if ($this->enabled && $this->debugbar) {
|
||||
$this->addMeasures();
|
||||
$this->addDeprecations();
|
||||
$this->debugbar->sendDataInHeaders();
|
||||
}
|
||||
@@ -250,34 +518,182 @@ class Debugger
|
||||
/**
|
||||
* Returns collected debugger data.
|
||||
*
|
||||
* @return array
|
||||
* @return array|null
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
if (!$this->enabled()) {
|
||||
if (!$this->enabled || !$this->debugbar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->addMeasures();
|
||||
$this->addDeprecations();
|
||||
$this->timers = [];
|
||||
|
||||
return $this->debugbar->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hierarchical Profiler support.
|
||||
*
|
||||
* @param callable $callable
|
||||
* @param string|null $message
|
||||
* @return mixed
|
||||
*/
|
||||
public function profile(callable $callable, string $message = null)
|
||||
{
|
||||
$this->startProfiling();
|
||||
$response = $callable();
|
||||
$this->stopProfiling($message);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function addTwigProfiler(Environment $twig): void
|
||||
{
|
||||
$clockwork = $this->getClockwork();
|
||||
if ($clockwork) {
|
||||
$source = new TwigClockworkDataSource($twig);
|
||||
$source->listenToEvents();
|
||||
$clockwork->addDataSource($source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start profiling code.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startProfiling(): void
|
||||
{
|
||||
if ($this->enabled && extension_loaded('tideways_xhprof')) {
|
||||
$this->profiling++;
|
||||
if ($this->profiling === 1) {
|
||||
// @phpstan-ignore-next-line
|
||||
\tideways_xhprof_enable(TIDEWAYS_XHPROF_FLAGS_NO_BUILTINS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop profiling code. Returns profiling array or null if profiling couldn't be done.
|
||||
*
|
||||
* @param string|null $message
|
||||
* @return array|null
|
||||
*/
|
||||
public function stopProfiling(string $message = null): ?array
|
||||
{
|
||||
$timings = null;
|
||||
if ($this->enabled && extension_loaded('tideways_xhprof')) {
|
||||
$profiling = $this->profiling - 1;
|
||||
if ($profiling === 0) {
|
||||
// @phpstan-ignore-next-line
|
||||
$timings = \tideways_xhprof_disable();
|
||||
$timings = $this->buildProfilerTimings($timings);
|
||||
|
||||
if ($this->clockwork) {
|
||||
/** @var UserData $userData */
|
||||
$userData = $this->clockwork->userData('Profiler');
|
||||
$userData->counters([
|
||||
'Calls' => count($timings)
|
||||
]);
|
||||
$userData->table('Profiler', $timings);
|
||||
} else {
|
||||
$this->addMessage($message ?? 'Profiler Analysis', 'debug', $timings);
|
||||
}
|
||||
}
|
||||
$this->profiling = max(0, $profiling);
|
||||
}
|
||||
|
||||
return $timings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $timings
|
||||
* @return array
|
||||
*/
|
||||
protected function buildProfilerTimings(array $timings): array
|
||||
{
|
||||
// Filter method calls which take almost no time.
|
||||
$timings = array_filter($timings, function ($value) {
|
||||
return $value['wt'] > 50;
|
||||
});
|
||||
|
||||
uasort($timings, function (array $a, array $b) {
|
||||
return $b['wt'] <=> $a['wt'];
|
||||
});
|
||||
|
||||
$table = [];
|
||||
foreach ($timings as $key => $timing) {
|
||||
$parts = explode('==>', $key);
|
||||
$method = $this->parseProfilerCall(array_pop($parts));
|
||||
$context = $this->parseProfilerCall(array_pop($parts));
|
||||
|
||||
// Skip redundant method calls.
|
||||
if ($context === 'Grav\Framework\RequestHandler\RequestHandler::handle()') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not profile library calls.
|
||||
if (strpos($context, 'Grav\\') !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$table[] = [
|
||||
'Context' => $context,
|
||||
'Method' => $method,
|
||||
'Calls' => $timing['ct'],
|
||||
'Time (ms)' => $timing['wt'] / 1000,
|
||||
];
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $call
|
||||
* @return mixed|string|null
|
||||
*/
|
||||
protected function parseProfilerCall(?string $call)
|
||||
{
|
||||
if (null === $call) {
|
||||
return '';
|
||||
}
|
||||
if (strpos($call, '@')) {
|
||||
[$call,] = explode('@', $call);
|
||||
}
|
||||
if (strpos($call, '::')) {
|
||||
[$class, $call] = explode('::', $call);
|
||||
}
|
||||
|
||||
if (!isset($class)) {
|
||||
return $call;
|
||||
}
|
||||
|
||||
// It is also possible to display twig files, but they are being logged in views.
|
||||
/*
|
||||
if (strpos($class, '__TwigTemplate_') === 0 && class_exists($class)) {
|
||||
$env = new Environment();
|
||||
/ ** @var Template $template * /
|
||||
$template = new $class($env);
|
||||
|
||||
return $template->getTemplateName();
|
||||
}
|
||||
*/
|
||||
|
||||
return "{$class}::{$call}()";
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a timer with an associated name and description
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|null $description
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function startTimer($name, $description = null)
|
||||
{
|
||||
if (strpos($name, '_') === 0 || $this->enabled()) {
|
||||
$this->debugbar['time']->startMeasure($name, $description);
|
||||
$this->timers[] = $name;
|
||||
}
|
||||
$this->timers[$name] = [$description, microtime(true)];
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -286,13 +702,13 @@ class Debugger
|
||||
* Stop the named timer
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function stopTimer($name)
|
||||
{
|
||||
if (\in_array($name, $this->timers, true) && (strpos($name, '_') === 0 || $this->enabled())) {
|
||||
$this->debugbar['time']->stopMeasure($name);
|
||||
if (isset($this->timers[$name])) {
|
||||
$endTime = microtime(true);
|
||||
$this->timers[$name][] = $endTime;
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -303,14 +719,76 @@ class Debugger
|
||||
*
|
||||
* @param mixed $message
|
||||
* @param string $label
|
||||
* @param bool $isString
|
||||
*
|
||||
* @param mixed|bool $isString
|
||||
* @return $this
|
||||
*/
|
||||
public function addMessage($message, $label = 'info', $isString = true)
|
||||
{
|
||||
if ($this->enabled()) {
|
||||
$this->debugbar['messages']->addMessage($message, $label, $isString);
|
||||
if ($this->enabled) {
|
||||
if ($this->censored) {
|
||||
if (!is_scalar($message)) {
|
||||
$message = 'CENSORED';
|
||||
}
|
||||
if (!is_scalar($isString)) {
|
||||
$isString = ['CENSORED'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->debugbar) {
|
||||
if (is_array($isString)) {
|
||||
$message = $isString;
|
||||
$isString = false;
|
||||
} elseif (is_string($isString)) {
|
||||
$message = $isString;
|
||||
$isString = true;
|
||||
}
|
||||
$this->debugbar['messages']->addMessage($message, $label, $isString);
|
||||
}
|
||||
|
||||
if ($this->clockwork) {
|
||||
$context = $isString;
|
||||
if (!is_scalar($message)) {
|
||||
$context = $message;
|
||||
$message = gettype($context);
|
||||
}
|
||||
if (is_bool($context)) {
|
||||
$context = [];
|
||||
} elseif (!is_array($context)) {
|
||||
$type = gettype($context);
|
||||
$context = [$type => $context];
|
||||
}
|
||||
|
||||
$this->clockwork->log($label, $message, $context);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param object $event
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @param float|null $time
|
||||
* @return $this
|
||||
*/
|
||||
public function addEvent(string $name, $event, EventDispatcherInterface $dispatcher, float $time = null)
|
||||
{
|
||||
if ($this->enabled && $this->clockwork) {
|
||||
$time = $time ?? microtime(true);
|
||||
$duration = (microtime(true) - $time) * 1000;
|
||||
|
||||
$data = null;
|
||||
if ($event && method_exists($event, '__debugInfo')) {
|
||||
$data = $event;
|
||||
}
|
||||
|
||||
$listeners = [];
|
||||
foreach ($dispatcher->getListeners($name) as $listener) {
|
||||
$listeners[] = $this->resolveCallable($listener);
|
||||
}
|
||||
|
||||
$this->clockwork->addEvent($name, $data, $time, ['listeners' => $listeners, 'duration' => $duration]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -319,18 +797,31 @@ class Debugger
|
||||
/**
|
||||
* Dump exception into the Messages tab of the Debug Bar
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @param Throwable $e
|
||||
* @return Debugger
|
||||
*/
|
||||
public function addException(\Exception $e)
|
||||
public function addException(Throwable $e)
|
||||
{
|
||||
if ($this->initialized && $this->enabled()) {
|
||||
$this->debugbar['exceptions']->addException($e);
|
||||
if ($this->initialized && $this->enabled) {
|
||||
if ($this->debugbar) {
|
||||
$this->debugbar['exceptions']->addThrowable($e);
|
||||
}
|
||||
|
||||
if ($this->clockwork) {
|
||||
/** @var UserData $exceptions */
|
||||
$exceptions = $this->clockwork->userData('Exceptions');
|
||||
$exceptions->data(['message' => $e->getMessage()]);
|
||||
|
||||
$this->clockwork->alert($e->getMessage(), ['exception' => $e]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setErrorHandler()
|
||||
{
|
||||
$this->errorHandler = set_error_handler(
|
||||
@@ -347,15 +838,15 @@ class Debugger
|
||||
*/
|
||||
public function deprecatedErrorHandler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
if ($errno !== E_USER_DEPRECATED) {
|
||||
if ($errno !== E_USER_DEPRECATED && $errno !== E_DEPRECATED) {
|
||||
if ($this->errorHandler) {
|
||||
return \call_user_func($this->errorHandler, $errno, $errstr, $errfile, $errline);
|
||||
return call_user_func($this->errorHandler, $errno, $errstr, $errfile, $errline);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$this->enabled()) {
|
||||
if (!$this->enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -382,10 +873,10 @@ class Debugger
|
||||
foreach ($backtrace as $current) {
|
||||
if (isset($current['args'])) {
|
||||
foreach ($current['args'] as $arg) {
|
||||
if ($arg instanceof \SplFileInfo) {
|
||||
if ($arg instanceof SplFileInfo) {
|
||||
$arg = $arg->getPathname();
|
||||
}
|
||||
if (\is_string($arg) && preg_match('/.+\.(yaml|md)$/i', $arg)) {
|
||||
if (is_string($arg) && preg_match('/.+\.(yaml|md)$/i', $arg)) {
|
||||
$errfile = $arg;
|
||||
$errline = 0;
|
||||
|
||||
@@ -403,18 +894,18 @@ class Debugger
|
||||
if (isset($current['args'])) {
|
||||
$args = [];
|
||||
foreach ($current['args'] as $arg) {
|
||||
if (\is_string($arg)) {
|
||||
if (is_string($arg)) {
|
||||
$arg = "'" . $arg . "'";
|
||||
if (mb_strlen($arg) > 100) {
|
||||
$arg = 'string';
|
||||
}
|
||||
} elseif (\is_bool($arg)) {
|
||||
} elseif (is_bool($arg)) {
|
||||
$arg = $arg ? 'true' : 'false';
|
||||
} elseif (\is_scalar($arg)) {
|
||||
} elseif (is_scalar($arg)) {
|
||||
$arg = $arg;
|
||||
} elseif (\is_object($arg)) {
|
||||
} elseif (is_object($arg)) {
|
||||
$arg = get_class($arg) . ' $object';
|
||||
} elseif (\is_array($arg)) {
|
||||
} elseif (is_array($arg)) {
|
||||
$arg = '$array';
|
||||
} else {
|
||||
$arg = '$object';
|
||||
@@ -430,7 +921,7 @@ class Debugger
|
||||
|
||||
$reflection = null;
|
||||
if ($object instanceof TemplateWrapper) {
|
||||
$reflection = new \ReflectionObject($object);
|
||||
$reflection = new ReflectionObject($object);
|
||||
$property = $reflection->getProperty('template');
|
||||
$property->setAccessible(true);
|
||||
$object = $property->getValue($object);
|
||||
@@ -540,6 +1031,28 @@ class Debugger
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getDeprecations(): array
|
||||
{
|
||||
if (!$this->deprecations) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$list = [];
|
||||
/** @var array $deprecated */
|
||||
foreach ($this->deprecations as $deprecated) {
|
||||
$list[] = $this->getDepracatedMessage($deprecated)[0];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws DebugBarException
|
||||
*/
|
||||
protected function addDeprecations()
|
||||
{
|
||||
if (!$this->deprecations) {
|
||||
@@ -558,6 +1071,10 @@ class Debugger
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $deprecated
|
||||
* @return array
|
||||
*/
|
||||
protected function getDepracatedMessage($deprecated)
|
||||
{
|
||||
$scope = $deprecated['scope'];
|
||||
@@ -595,6 +1112,10 @@ class Debugger
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $trace
|
||||
* @return string
|
||||
*/
|
||||
protected function getFunction($trace)
|
||||
{
|
||||
if (!isset($trace['function'])) {
|
||||
@@ -603,4 +1124,17 @@ class Debugger
|
||||
|
||||
return $trace['function'] . '(' . implode(', ', $trace['args'] ?? []) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $callable
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveCallable(callable $callable)
|
||||
{
|
||||
if (is_array($callable)) {
|
||||
return get_class($callable[0]) . '->' . $callable[1] . '()';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user