mirror of
https://github.com/getgrav/grav.git
synced 2025-12-16 05:09:42 +01:00
Compare commits
1296 Commits
1.7.6
...
15cb068f95
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15cb068f95 | ||
|
|
d34213232b | ||
|
|
7a6b8a90d4 | ||
|
|
306f33f4ae | ||
|
|
6cb8229806 | ||
|
|
95e285efa4 | ||
|
|
80410dae13 | ||
|
|
fae70e5fc9 | ||
|
|
9d9247a32f | ||
|
|
94d85cd873 | ||
|
|
0f879bd1d4 | ||
|
|
fd828d452e | ||
|
|
63bbc1cac6 | ||
|
|
528032b11a | ||
|
|
a4c3a3af6d | ||
|
|
b7e1958a6e | ||
|
|
0c38968c58 | ||
|
|
9d11094e41 | ||
|
|
ed640a1314 | ||
|
|
e37259527d | ||
|
|
3462d94d57 | ||
|
|
19c2f8da76 | ||
|
|
a161399c84 | ||
|
|
5f120c328b | ||
|
|
db924c4a26 | ||
|
|
9fc1b42d59 | ||
|
|
c8878dfc80 | ||
|
|
779661ab8a | ||
|
|
3985638a8f | ||
|
|
a78789b291 | ||
|
|
caa127cd53 | ||
|
|
5f087d3a43 | ||
|
|
1bc6e5e13a | ||
|
|
f339bb83c5 | ||
|
|
27789991ae | ||
|
|
114aebae7c | ||
|
|
370dfd6016 | ||
|
|
1d05e6bdc4 | ||
|
|
3acff8a9f8 | ||
|
|
ea59bdb1d4 | ||
|
|
02330b96d9 | ||
|
|
2b1d73fd26 | ||
|
|
4e11ca7c8e | ||
|
|
591e2e4563 | ||
|
|
2161ffeb5e | ||
|
|
b856978211 | ||
|
|
19ee2d883e | ||
|
|
93089241c3 | ||
|
|
3b1c332932 | ||
|
|
7fd614f8b6 | ||
|
|
5567a5a1cd | ||
|
|
334e1dcabc | ||
|
|
cbf5ec57c6 | ||
|
|
9f33e247cf | ||
|
|
8c7e970603 | ||
|
|
360b418c97 | ||
|
|
af0db0c2a1 | ||
|
|
4c74192191 | ||
|
|
ee5fccd2c8 | ||
|
|
5bc89bf32b | ||
|
|
0b021e2114 | ||
|
|
15c1b1cc06 | ||
|
|
ee1b55e929 | ||
|
|
73d3a90c0b | ||
|
|
0764e37c8b | ||
|
|
bd5b2633f7 | ||
|
|
6b0c0486aa | ||
|
|
07ac3d3bb9 | ||
|
|
72e9d57e2e | ||
|
|
07965c6c61 | ||
|
|
72cc8e91a2 | ||
|
|
678eacaae5 | ||
|
|
cb7a3ccfdf | ||
|
|
076c10d34b | ||
|
|
2d75649a08 | ||
|
|
c8acc9a499 | ||
|
|
af499184ea | ||
|
|
ebac0a082c | ||
|
|
4d31bbb43a | ||
|
|
be20cf2e2c | ||
|
|
c33a1f57bc | ||
|
|
83817428c7 | ||
|
|
d2970a92b5 | ||
|
|
7b1bcf7789 | ||
|
|
44bdd1283d | ||
|
|
32dafbb1cb | ||
|
|
e622326285 | ||
|
|
d0287043c2 | ||
|
|
6c5b801c6f | ||
|
|
460bf241a5 | ||
|
|
ee179e19e5 | ||
|
|
3618a129df | ||
|
|
787146cc2c | ||
|
|
a1fe19f465 | ||
|
|
f2c26c116a | ||
|
|
d1d70c4d0c | ||
|
|
e5a659d445 | ||
|
|
39c4ecfe6a | ||
|
|
3e3aa00a1b | ||
|
|
9c2497460b | ||
|
|
f2f58d11d6 | ||
|
|
2d8be2f859 | ||
|
|
f6c57a44de | ||
|
|
0d2d0bdc11 | ||
|
|
e110701079 | ||
|
|
c10acd1837 | ||
|
|
f9f3b9a8ba | ||
|
|
e5b7449483 | ||
|
|
7077b0b71a | ||
|
|
57a446862f | ||
|
|
b2f2e7bd45 | ||
|
|
3fbd6771e9 | ||
|
|
8a10d6bc54 | ||
|
|
0bdde9dec2 | ||
|
|
348fa04c47 | ||
|
|
52f0d5f1d7 | ||
|
|
9c6111c368 | ||
|
|
9806533f56 | ||
|
|
e30245789c | ||
|
|
20b95c4585 | ||
|
|
6d0fc78462 | ||
|
|
5420ca2200 | ||
|
|
942f523f18 | ||
|
|
c812def317 | ||
|
|
9b2d352f8a | ||
|
|
d932875e66 | ||
|
|
7a2c151a4b | ||
|
|
81b0f0ec04 | ||
|
|
70ddb549b7 | ||
|
|
be3cb77f28 | ||
|
|
345b5e9577 | ||
|
|
e88f38bd10 | ||
|
|
bdc06afea2 | ||
|
|
f9348a4d9d | ||
|
|
44fd1172b8 | ||
|
|
c9c1267284 | ||
|
|
4fa5996414 | ||
|
|
920642411c | ||
|
|
2999c06a3a | ||
|
|
d97b2d70bd | ||
|
|
5e7b482972 | ||
|
|
9230a5a40f | ||
|
|
c3d1d4ae26 | ||
|
|
286b5a5179 | ||
|
|
c79d2ecfc4 | ||
|
|
70d6aec1a7 | ||
|
|
60a97dcf56 | ||
|
|
679a6db61d | ||
|
|
b70ae844a8 | ||
|
|
9dd507b717 | ||
|
|
e6de9db77e | ||
|
|
b6a37cfff3 | ||
|
|
42e37c1d02 | ||
|
|
09aa2fb8fd | ||
|
|
e764d2ce1c | ||
|
|
3f0b204728 | ||
|
|
f711cb3208 | ||
|
|
f10894fe47 | ||
|
|
b68872e3fd | ||
|
|
43126b09e4 | ||
|
|
6751d28839 | ||
|
|
8118d6b980 | ||
|
|
2c4b69f9ec | ||
|
|
d6cbc263e7 | ||
|
|
ba2536136b | ||
|
|
c56d24c0d7 | ||
|
|
ee49305053 | ||
|
|
b4d664fcb0 | ||
|
|
7192cfe549 | ||
|
|
7fcb1d1cb7 | ||
|
|
dbeaa8ad46 | ||
|
|
a3da588829 | ||
|
|
a3387c106b | ||
|
|
d9d241d806 | ||
|
|
bb5cdad333 | ||
|
|
44f90cbce0 | ||
|
|
a5c6f1dbe9 | ||
|
|
c8227b38fc | ||
|
|
77114ecdd0 | ||
|
|
23da92d0ff | ||
|
|
f88c09adca | ||
|
|
7dd5c8a0ba | ||
|
|
cf2ac28be2 | ||
|
|
43ddf45057 | ||
|
|
57212ec9a5 | ||
|
|
b55e86a8ba | ||
|
|
2b1a7d3fb6 | ||
|
|
250568bae5 | ||
|
|
cc97e2ff45 | ||
|
|
d92c430b8a | ||
|
|
184cdea75d | ||
|
|
7b9567ec28 | ||
|
|
75d8356f1b | ||
|
|
c82645a42a | ||
|
|
9e84d5d004 | ||
|
|
fd0d3dc463 | ||
|
|
eb985e875d | ||
|
|
ba3493adce | ||
|
|
d785042a0d | ||
|
|
49096b61f3 | ||
|
|
70e986074c | ||
|
|
4af22edd36 | ||
|
|
5bc7d6943f | ||
|
|
8eb4085bcd | ||
|
|
b47758e3c7 | ||
|
|
972ec26035 | ||
|
|
9116079e97 | ||
|
|
51ddb3984c | ||
|
|
22de638e52 | ||
|
|
365ab93e7e | ||
|
|
c172964025 | ||
|
|
cb0bbcdb8b | ||
|
|
35f5d2f329 | ||
|
|
03849923d4 | ||
|
|
3664096550 | ||
|
|
6664d98de8 | ||
|
|
17323ec76c | ||
|
|
9b9079bdd7 | ||
|
|
db3df738e8 | ||
|
|
38075f9c86 | ||
|
|
b0dac5f4b4 | ||
|
|
f7c77d1173 | ||
|
|
0fd734c8df | ||
|
|
b642b2c999 | ||
|
|
ae147fa53b | ||
|
|
9e6df39bff | ||
|
|
8ac6076f88 | ||
|
|
cef7812472 | ||
|
|
6f461395a7 | ||
|
|
162fe1acc2 | ||
|
|
332748a1f9 | ||
|
|
1a9a60115d | ||
|
|
a55052b8b0 | ||
|
|
dc7354e7e1 | ||
|
|
afc1513aad | ||
|
|
466b2a16e8 | ||
|
|
a21640ace6 | ||
|
|
09920deabc | ||
|
|
0bd72f4bb9 | ||
|
|
ec55a80183 | ||
|
|
d07ac5b6ea | ||
|
|
fa29f6672a | ||
|
|
006d8c85a0 | ||
|
|
c608ed10cf | ||
|
|
9d71de8e54 | ||
|
|
89764a51fb | ||
|
|
b851d9bf9d | ||
|
|
a0679fc050 | ||
|
|
e497a93da6 | ||
|
|
56cc894c1d | ||
|
|
d07f3770bc | ||
|
|
3baaf19c31 | ||
|
|
7236862a15 | ||
|
|
639be5ac0d | ||
|
|
8811b7aad0 | ||
|
|
4a22f3dc8d | ||
|
|
d3e32799ab | ||
|
|
3bebfc6dac | ||
|
|
9d80a4d992 | ||
|
|
fc7f72f89d | ||
|
|
fa56984dc3 | ||
|
|
90fd68d4a5 | ||
|
|
7613e38b6d | ||
|
|
830a442faa | ||
|
|
8d7b658aa6 | ||
|
|
83d098b891 | ||
|
|
d798859acd | ||
|
|
53391466b0 | ||
|
|
2fadc14c01 | ||
|
|
08d74df6e3 | ||
|
|
de2af9e470 | ||
|
|
edfb1a0868 | ||
|
|
9893a605a6 | ||
|
|
2620e836d4 | ||
|
|
83d291c24b | ||
|
|
7e723eb7f5 | ||
|
|
1d1d8da431 | ||
|
|
ca1b05ba57 | ||
|
|
4097d85daa | ||
|
|
350e4c04cd | ||
|
|
d8123a3662 | ||
|
|
1e430f635e | ||
|
|
17548131d3 | ||
|
|
4a27bd780c | ||
|
|
2e975dfa90 | ||
|
|
5666d0f211 | ||
|
|
d17ab9e06c | ||
|
|
a15fe29f43 | ||
|
|
3126fa8388 | ||
|
|
b0c339c9eb | ||
|
|
f812ee8555 | ||
|
|
a1e583f657 | ||
|
|
8c6388bb74 | ||
|
|
d7aaef986e | ||
|
|
ab9363c478 | ||
|
|
7f2da96c0b | ||
|
|
5cf7ef864b | ||
|
|
199cdd4364 | ||
|
|
2896aea30a | ||
|
|
3952491ce9 | ||
|
|
6b07088189 | ||
|
|
b16db4b9c0 | ||
|
|
42c0682dd8 | ||
|
|
1969ec1876 | ||
|
|
c30661736c | ||
|
|
3d2dfa2faf | ||
|
|
f06bbfc563 | ||
|
|
37e5526a4f | ||
|
|
85c4b8279e | ||
|
|
46736ce256 | ||
|
|
800b2e1ecb | ||
|
|
4f065b95a7 | ||
|
|
b59a3adc80 | ||
|
|
4ec9a3a489 | ||
|
|
a0614dc3eb | ||
|
|
346d194125 | ||
|
|
173d08243a | ||
|
|
62c60b8ba1 | ||
|
|
59031a8711 | ||
|
|
5cd859865f | ||
|
|
16eafbbb04 | ||
|
|
3947bb03aa | ||
|
|
ee55d097f2 | ||
|
|
ae567469b7 | ||
|
|
f6decaab15 | ||
|
|
964e37c6f0 | ||
|
|
c55f2bed4a | ||
|
|
ecf664c8e6 | ||
|
|
10d36a10bc | ||
|
|
574a430a10 | ||
|
|
302f02ca5d | ||
|
|
c334479e4c | ||
|
|
e2257a9783 | ||
|
|
6fa63197a0 | ||
|
|
f686e0ac64 | ||
|
|
b96483c49a | ||
|
|
cccce836f6 | ||
|
|
5a741d9b10 | ||
|
|
16a50767dd | ||
|
|
d72fca121f | ||
|
|
bdcb77d429 | ||
|
|
e0ff168cf3 | ||
|
|
e15cc86716 | ||
|
|
dcfbd73d43 | ||
|
|
1967910789 | ||
|
|
4f1f9a7755 | ||
|
|
bbfc3b5658 | ||
|
|
106dc58329 | ||
|
|
ca1e5ebb8a | ||
|
|
6a585857b0 | ||
|
|
f81a4ca008 | ||
|
|
8c1b4448e9 | ||
|
|
f73813103d | ||
|
|
9c697f178d | ||
|
|
20ca44fd53 | ||
|
|
24cd0e133f | ||
|
|
9f4a86317b | ||
|
|
25ace6458b | ||
|
|
856a478bd6 | ||
|
|
faa8ee5fe1 | ||
|
|
785f641ea5 | ||
|
|
013ff7ee1b | ||
|
|
c97a0ffb16 | ||
|
|
51623ee0da | ||
|
|
8c941cc6d3 | ||
|
|
b6bba9eb99 | ||
|
|
77adfcb831 | ||
|
|
ee8d783d05 | ||
|
|
d184e25f05 | ||
|
|
04f9385aa8 | ||
|
|
afb5b02e57 | ||
|
|
4187a04235 | ||
|
|
26a6cb75ad | ||
|
|
37d0498e1b | ||
|
|
dd8d610ae0 | ||
|
|
b9529d0010 | ||
|
|
4149c81339 | ||
|
|
2da91d9c8b | ||
|
|
d69adcf347 | ||
|
|
45e2c27c66 | ||
|
|
f77df43d7a | ||
|
|
de1ccfa12d | ||
|
|
5928411b86 | ||
|
|
15dc7568a5 | ||
|
|
b435d2b884 | ||
|
|
dbedb60634 | ||
|
|
f9f5781af8 | ||
|
|
ad8b1b79bd | ||
|
|
cd2a7d8d98 | ||
|
|
1dc6866eab | ||
|
|
0b16401a91 | ||
|
|
78b8125eae | ||
|
|
0d7cd64d0d | ||
|
|
3ea86e1794 | ||
|
|
6df03063c8 | ||
|
|
e5990f431d | ||
|
|
b3d55ca81a | ||
|
|
a0e728b540 | ||
|
|
171a5c074c | ||
|
|
f33e89fa45 | ||
|
|
e33d71e4b9 | ||
|
|
ddbb1362dc | ||
|
|
a71403f158 | ||
|
|
88eb9f915a | ||
|
|
70e5262512 | ||
|
|
a1c116dd82 | ||
|
|
cc08da0c74 | ||
|
|
f7eab6b163 | ||
|
|
f59fa9a291 | ||
|
|
458c64086e | ||
|
|
345086538c | ||
|
|
c62e173955 | ||
|
|
1b8e267d0a | ||
|
|
eb72cb32bb | ||
|
|
4e01398545 | ||
|
|
b0dd2358f4 | ||
|
|
0c9333e60d | ||
|
|
0b53609fa0 | ||
|
|
cfa510e7f7 | ||
|
|
6d5f0ff9ba | ||
|
|
71939e18be | ||
|
|
45f8fe4d0b | ||
|
|
2179ef33a7 | ||
|
|
d0ae677e61 | ||
|
|
6a9b1f2214 | ||
|
|
b1117e45c9 | ||
|
|
382a836d80 | ||
|
|
db3e39f0cb | ||
|
|
80ce87e4a9 | ||
|
|
f0f29891d6 | ||
|
|
c66da5bedb | ||
|
|
1f21d259ea | ||
|
|
21b218e464 | ||
|
|
3b2fb023b8 | ||
|
|
92babda742 | ||
|
|
3cdbc5890a | ||
|
|
a8042a666c | ||
|
|
79f9640b12 | ||
|
|
65aeb82e21 | ||
|
|
e3b0aa0c50 | ||
|
|
7e617a632e | ||
|
|
fb5dd14875 | ||
|
|
490bdd6ce7 | ||
|
|
893b1dd1db | ||
|
|
1146959806 | ||
|
|
45103f81b4 | ||
|
|
c426f4a9cc | ||
|
|
0d27f2d77e | ||
|
|
b4c62101a4 | ||
|
|
950cd0854f | ||
|
|
4cd137830b | ||
|
|
aa19bcdcbe | ||
|
|
cf6bf7d1ec | ||
|
|
47665dbddb | ||
|
|
dc209453d0 | ||
|
|
5b89091f13 | ||
|
|
50ee844759 | ||
|
|
244758d438 | ||
|
|
71bbed12f9 | ||
|
|
8c2c1cb726 | ||
|
|
9d01140a63 | ||
|
|
259e775db8 | ||
|
|
d4c617ff19 | ||
|
|
c7680bb50a | ||
|
|
722ce55ccb | ||
|
|
5b950ce73f | ||
|
|
8dfa2110bf | ||
|
|
31aeaf6309 | ||
|
|
e4f483998d | ||
|
|
7a393101ee | ||
|
|
d96b023d72 | ||
|
|
4de3cab522 | ||
|
|
b34f70f91d | ||
|
|
9da8cad7fe | ||
|
|
e4a30f5966 | ||
|
|
814a050858 | ||
|
|
b6179bd2de | ||
|
|
e5ac37e3cf | ||
|
|
66463ddff3 | ||
|
|
956c2993ae | ||
|
|
3cf67cb2fd | ||
|
|
36afa9d848 | ||
|
|
694ab76d1e | ||
|
|
369c2e9ffa | ||
|
|
95ae35216a | ||
|
|
9c0477fa52 | ||
|
|
e1ab15e323 | ||
|
|
ff77d58acb | ||
|
|
adfbd5730b | ||
|
|
bf175983ec | ||
|
|
470b69c775 | ||
|
|
60648c43db | ||
|
|
75cd4f4306 | ||
|
|
2412115f41 | ||
|
|
598836d656 | ||
|
|
e1019c4420 | ||
|
|
a8a6c0c520 | ||
|
|
685d76231a | ||
|
|
0f9b9f780f | ||
|
|
1e2792874d | ||
|
|
0a061ce95e | ||
|
|
d82ee029e1 | ||
|
|
3b83c8204d | ||
|
|
9ab7a4759a | ||
|
|
c261d0d3f7 | ||
|
|
940415dddb | ||
|
|
60506e6f34 | ||
|
|
bd7a74d79e | ||
|
|
5fcf690918 | ||
|
|
904ec46a9f | ||
|
|
259c148edb | ||
|
|
0ae980062f | ||
|
|
a888f19ad1 | ||
|
|
479b89134d | ||
|
|
d0d083d985 | ||
|
|
396b412dda | ||
|
|
8be02e44c6 | ||
|
|
ec115a6a64 | ||
|
|
3b92c1aca4 | ||
|
|
2d9df03766 | ||
|
|
f086f84ff2 | ||
|
|
a2b23ad80e | ||
|
|
88350d9090 | ||
|
|
b2f27fbdf2 | ||
|
|
72b769aa63 | ||
|
|
8a7e38751a | ||
|
|
3e6c719441 | ||
|
|
8efb000801 | ||
|
|
6d6e92048e | ||
|
|
8c365d45a4 | ||
|
|
ee6448c307 | ||
|
|
f8c9e9ada4 | ||
|
|
87ab3ae4a7 | ||
|
|
68dc461bc0 | ||
|
|
4dd98610a4 | ||
|
|
0358e55aed | ||
|
|
9e5ed10925 | ||
|
|
3b7eb198cf | ||
|
|
81ed7379a9 | ||
|
|
e1950e985b | ||
|
|
84c61af807 | ||
|
|
93755c7329 | ||
|
|
5329918e2f | ||
|
|
efd7726646 | ||
|
|
4c762c0ac3 | ||
|
|
81a911572c | ||
|
|
c56bb86b61 | ||
|
|
ea010f19f0 | ||
|
|
1fae4504a2 | ||
|
|
d99c84d9f8 | ||
|
|
c732bfaeef | ||
|
|
4f0fee684a | ||
|
|
884faa91bb | ||
|
|
8c261a05cc | ||
|
|
95aa57ca50 | ||
|
|
0bc0e58707 | ||
|
|
a86e0d4b96 | ||
|
|
44c819b021 | ||
|
|
3f13d81c6f | ||
|
|
720a965c7e | ||
|
|
f0e263a404 | ||
|
|
ad33a63ad2 | ||
|
|
f7b7f3337d | ||
|
|
983fcc5e40 | ||
|
|
9c5b8b6496 | ||
|
|
71fc4eb16b | ||
|
|
ea2858ea2b | ||
|
|
d080578e83 | ||
|
|
8427eb6d3e | ||
|
|
e3a342dabd | ||
|
|
00b13d1093 | ||
|
|
7e5ff71623 | ||
|
|
72ba7ccab6 | ||
|
|
ea9c9fdca8 | ||
|
|
5e379bfa39 | ||
|
|
fb1e31d0e4 | ||
|
|
8e6b823833 | ||
|
|
6fcc4ec5d0 | ||
|
|
3e9866920b | ||
|
|
3a00ca0457 | ||
|
|
2f6e4b1ad8 | ||
|
|
01d627e8f7 | ||
|
|
51a386e252 | ||
|
|
30502e8042 | ||
|
|
01264ef70d | ||
|
|
de642df06e | ||
|
|
dd8945124e | ||
|
|
0b6428ec29 | ||
|
|
840862c26d | ||
|
|
f799a71001 | ||
|
|
3f10c05840 | ||
|
|
dbca0b451c | ||
|
|
6882037b85 | ||
|
|
10b15bedf2 | ||
|
|
8c14a9907e | ||
|
|
5d2dc6c329 | ||
|
|
e9f28ab824 | ||
|
|
186eb8ae6c | ||
|
|
2258adcb05 | ||
|
|
20c4cdefe8 | ||
|
|
aba6382f2e | ||
|
|
32dd550178 | ||
|
|
f490d9a7e1 | ||
|
|
81ca0c2e25 | ||
|
|
fbfac9f8f4 | ||
|
|
ec3175fc89 | ||
|
|
dac1614306 | ||
|
|
37e92e4f55 | ||
|
|
1b0c3d1fce | ||
|
|
00cb9c3540 | ||
|
|
b9800b7c35 | ||
|
|
d6e72708bf | ||
|
|
2aff274c31 | ||
|
|
b5ab00639b | ||
|
|
cf38cccda1 | ||
|
|
024964eafb | ||
|
|
f8d736b24a | ||
|
|
62c4f3c768 | ||
|
|
31920bb153 | ||
|
|
b18b49a239 | ||
|
|
9d6a2dba09 | ||
|
|
de4af5dbcc | ||
|
|
f31541250f | ||
|
|
f570cea3bd | ||
|
|
0b54a7b4bc | ||
|
|
f464492582 | ||
|
|
81aa4601e4 | ||
|
|
6b57f29511 | ||
|
|
74296a80d0 | ||
|
|
74c1dfa433 | ||
|
|
f51a9d9d87 | ||
|
|
8dbc394ae7 | ||
|
|
89709a7190 | ||
|
|
e6320fa327 | ||
|
|
3bf979bd0f | ||
|
|
6fa96ca554 | ||
|
|
60ce105fa5 | ||
|
|
ec16b5184e | ||
|
|
a7a9b5d132 | ||
|
|
460ca49f9b | ||
|
|
32a486f1d4 | ||
|
|
1237f0a6d6 | ||
|
|
9ec3e7d731 | ||
|
|
d6dcd96301 | ||
|
|
2957077935 | ||
|
|
6ba1cff114 | ||
|
|
6218a4b366 | ||
|
|
28c4305d46 | ||
|
|
a2413718f3 | ||
|
|
767a17bf50 | ||
|
|
d75c87ca5e | ||
|
|
c2a8145f2c | ||
|
|
26e4768bc5 | ||
|
|
e5498f58e6 | ||
|
|
8f58a4494c | ||
|
|
4464f29169 | ||
|
|
5bfd43256d | ||
|
|
2ea39309b6 | ||
|
|
69bc3f7f25 | ||
|
|
782ceada80 | ||
|
|
1c0ed43afa | ||
|
|
a092aed4ed | ||
|
|
7c2b21fb60 | ||
|
|
e1ca3c2f70 | ||
|
|
0da5ccb3e1 | ||
|
|
2830ba9120 | ||
|
|
5a355fb94e | ||
|
|
b0add67cdd | ||
|
|
236c38e65d | ||
|
|
c9c23c6c4f | ||
|
|
8f0443a73d | ||
|
|
36392acbea | ||
|
|
4b564df38f | ||
|
|
c9271c80a7 | ||
|
|
62d9db7650 | ||
|
|
4f7dad2872 | ||
|
|
57d6be0f78 | ||
|
|
ae55b4794f | ||
|
|
d82851af10 | ||
|
|
730231b8b7 | ||
|
|
82015d4ae7 | ||
|
|
4376c8ce57 | ||
|
|
aa1e1bc0ad | ||
|
|
35d4d00429 | ||
|
|
3e7f67f589 | ||
|
|
c08341046b | ||
|
|
010753bdd6 | ||
|
|
d36bc4b8a2 | ||
|
|
847f09a2ac | ||
|
|
28469a6bf6 | ||
|
|
0d19bc6e97 | ||
|
|
492cc1d2f1 | ||
|
|
9d179e5b2a | ||
|
|
6032bd07dc | ||
|
|
4d4efb31e3 | ||
|
|
c7bc5f5b59 | ||
|
|
41b8fbb0e0 | ||
|
|
e60ba13d75 | ||
|
|
e0deeeb551 | ||
|
|
499b25aad8 | ||
|
|
9fd580c49b | ||
|
|
e09bae918c | ||
|
|
0abde01442 | ||
|
|
03f71fa49d | ||
|
|
4f92568171 | ||
|
|
b80fcca0cf | ||
|
|
879eb27540 | ||
|
|
7b7235297e | ||
|
|
34ab8408fa | ||
|
|
f19297d5f7 | ||
|
|
78b8051627 | ||
|
|
3dd0cabeac | ||
|
|
9a87a509b0 | ||
|
|
b992d7f185 | ||
|
|
a7e82f279a | ||
|
|
6d0d6c22d3 | ||
|
|
46f2a81b21 | ||
|
|
f5e21645f6 | ||
|
|
12c8cf9c40 | ||
|
|
93bb929b38 | ||
|
|
85eaf308d5 | ||
|
|
d9ede28b99 | ||
|
|
2e65b0eea4 | ||
|
|
7e52112b21 | ||
|
|
a900b89795 | ||
|
|
cad8510dae | ||
|
|
7cafeb2870 | ||
|
|
7660a80ef7 | ||
|
|
b3b5fca16c | ||
|
|
6d0a436834 | ||
|
|
176fd8f1d8 | ||
|
|
2f85fe9c99 | ||
|
|
d93d297dc4 | ||
|
|
fcd9093f84 | ||
|
|
58b54a70bd | ||
|
|
9daa0a9041 | ||
|
|
ec884997ef | ||
|
|
3a45748ce6 | ||
|
|
c4e10cf59f | ||
|
|
2c252c43b4 | ||
|
|
382756f79b | ||
|
|
11013cbb57 | ||
|
|
2377be9a2b | ||
|
|
b1938c9790 | ||
|
|
965e03daf2 | ||
|
|
b6c3db082a | ||
|
|
26b68953c4 | ||
|
|
fba015c5d9 | ||
|
|
5d842349e5 | ||
|
|
0323a898cb | ||
|
|
d275532cf8 | ||
|
|
7306f164e9 | ||
|
|
3ca58e9ae1 | ||
|
|
88219fbf0e | ||
|
|
5eefc91781 | ||
|
|
67ef9b221a | ||
|
|
c82a35cea0 | ||
|
|
840441bc9b | ||
|
|
9556e47874 | ||
|
|
45fd533186 | ||
|
|
5248519c37 | ||
|
|
5b9a0fb237 | ||
|
|
389ffe1c7a | ||
|
|
7b39a1bd8f | ||
|
|
4c5c5925b2 | ||
|
|
96df7deaf4 | ||
|
|
7666651324 | ||
|
|
b9db2f3322 | ||
|
|
84a1ab6ca5 | ||
|
|
3b89377570 | ||
|
|
c3c31880b0 | ||
|
|
52449246e5 | ||
|
|
b03de18304 | ||
|
|
9bb6f5366e | ||
|
|
d126d1b656 | ||
|
|
6f2fa9311a | ||
|
|
61061220d7 | ||
|
|
aa7e64f2b7 | ||
|
|
b58210de1a | ||
|
|
0bb52c75ce | ||
|
|
da6b41f8a6 | ||
|
|
1ab2f7083c | ||
|
|
707710d679 | ||
|
|
bab83ed977 | ||
|
|
c103efea22 | ||
|
|
3e784a2f15 | ||
|
|
21f76a1e34 | ||
|
|
a8c4e730f6 | ||
|
|
c1a9a7d648 | ||
|
|
d329df2bb0 | ||
|
|
f90232e511 | ||
|
|
57bd3c9cb5 | ||
|
|
419692b9e4 | ||
|
|
b422d9585c | ||
|
|
67b9623d6c | ||
|
|
bda6b26896 | ||
|
|
9b2af98b0b | ||
|
|
b07bf96ad9 | ||
|
|
fb2c4f48f7 | ||
|
|
308383b093 | ||
|
|
f8f5502c40 | ||
|
|
55b45fcf2f | ||
|
|
bd471cb61b | ||
|
|
2ac38d39ea | ||
|
|
7bb8d10b22 | ||
|
|
f9fcdd6c51 | ||
|
|
3febaaa802 | ||
|
|
178cc9c5ea | ||
|
|
8d51911873 | ||
|
|
99ceb40c5f | ||
|
|
8767bfb9b0 | ||
|
|
756b088ada | ||
|
|
c962201bae | ||
|
|
2335271472 | ||
|
|
47d7927bac | ||
|
|
d15542e553 | ||
|
|
e6447f7203 | ||
|
|
fbcaf991aa | ||
|
|
cc8ec10098 | ||
|
|
92824f44e6 | ||
|
|
c4eefc13a7 | ||
|
|
d541808604 | ||
|
|
231c8a0f4c | ||
|
|
ea39587329 | ||
|
|
8f3ac75afd | ||
|
|
b40b543790 | ||
|
|
3b7921b698 | ||
|
|
6af1ee48a5 | ||
|
|
4519971a76 | ||
|
|
26295d5cf2 | ||
|
|
bfc9e26f26 | ||
|
|
d939b1e563 | ||
|
|
8343cfb278 | ||
|
|
290a28109e | ||
|
|
5bec5db5e1 | ||
|
|
a6fb0a14f0 | ||
|
|
7cb62ddc75 | ||
|
|
0f85b831b5 | ||
|
|
530e6a4399 | ||
|
|
c7700c2e16 | ||
|
|
89acc59ac3 | ||
|
|
9a25c88471 | ||
|
|
4ccbdafe8a | ||
|
|
74096e836f | ||
|
|
1c51bf8a66 | ||
|
|
a4beb9b8bd | ||
|
|
484a41e42a | ||
|
|
def62ec2a2 | ||
|
|
fae431bc39 | ||
|
|
25bc1edf31 | ||
|
|
945b90fa46 | ||
|
|
420a4e3566 | ||
|
|
f2a7833933 | ||
|
|
491252476d | ||
|
|
6ed453890d | ||
|
|
432f0eb9e5 | ||
|
|
236c068d70 | ||
|
|
3f3503e0f3 | ||
|
|
bc70c9b93c | ||
|
|
90f5ff7c74 | ||
|
|
3bfbb1a4ef | ||
|
|
1f3f1828c2 | ||
|
|
6c7064db93 | ||
|
|
9df7b35c65 | ||
|
|
5552ea2d70 | ||
|
|
6079562c90 | ||
|
|
a56453cf0a | ||
|
|
7223c177c4 | ||
|
|
c2f394de17 | ||
|
|
878de339e5 | ||
|
|
86cfba3bc9 | ||
|
|
ad1e20abb7 | ||
|
|
2165743810 | ||
|
|
f3789bb17e | ||
|
|
8f2f5e3373 | ||
|
|
70c0dc6419 | ||
|
|
5b84213fce | ||
|
|
3ad68d6d5a | ||
|
|
627a1510dc | ||
|
|
042d4a4603 | ||
|
|
b2cfc4ef5f | ||
|
|
1c148ab6fb | ||
|
|
cf6159ffe3 | ||
|
|
f04f13723d | ||
|
|
5fd4f5f3eb | ||
|
|
512c2e5d9d | ||
|
|
da3e32f945 | ||
|
|
ae74f29b69 | ||
|
|
ecb2d31df0 | ||
|
|
2355d799f2 | ||
|
|
589f75b60d | ||
|
|
6aebbc2be4 | ||
|
|
fb4bce36cb | ||
|
|
3321f49253 | ||
|
|
5c6fd0453b | ||
|
|
179dec4c3b | ||
|
|
d59a4c63db | ||
|
|
4c3daf6e6f | ||
|
|
c220b6e0c2 | ||
|
|
e4f79dbfce | ||
|
|
d220812f5e | ||
|
|
d4a23c1fbe | ||
|
|
a57c18f63f | ||
|
|
8256af5cfa | ||
|
|
eae3668aee | ||
|
|
8d04330dc5 | ||
|
|
b7ab1df4e3 | ||
|
|
a68bdd2b75 | ||
|
|
38e4624506 | ||
|
|
ad9287ee0f | ||
|
|
664c95e95a | ||
|
|
a8d292a0d9 | ||
|
|
6e9f6e8f7a | ||
|
|
d0604a055f | ||
|
|
193475a0b6 | ||
|
|
47b444a742 | ||
|
|
3c3b44d6de | ||
|
|
88c2b40ec7 | ||
|
|
88ffa163c4 | ||
|
|
956ed013cf | ||
|
|
061ebf06c6 | ||
|
|
e6911ce24a | ||
|
|
d9c9f6a5eb | ||
|
|
2a04628459 | ||
|
|
33b473c290 | ||
|
|
58b48c2f26 | ||
|
|
ad003a0fc4 | ||
|
|
8f9c417c04 | ||
|
|
8bbf7a849b | ||
|
|
a723bcdb46 | ||
|
|
3d0d836d92 | ||
|
|
67ea6c8066 | ||
|
|
cd1dc5b43d | ||
|
|
1d1c10f0a6 | ||
|
|
125f2a8662 | ||
|
|
4046fed60f | ||
|
|
78ca2f68cc | ||
|
|
18fcf80b4f | ||
|
|
4db28bf47e | ||
|
|
917aa9407d | ||
|
|
6fc2bc4f91 | ||
|
|
7dc6b71252 | ||
|
|
53c7f4c119 | ||
|
|
af4243aff2 | ||
|
|
afc69a3229 | ||
|
|
17dfd130b6 | ||
|
|
05101650ce | ||
|
|
715477586c | ||
|
|
fd61f82f5a | ||
|
|
777ac119de | ||
|
|
076c64841e | ||
|
|
62cb40fef5 | ||
|
|
8b0a6906c7 | ||
|
|
be44bf0b55 | ||
|
|
d2536379e5 | ||
|
|
08d7ad80df | ||
|
|
33a5709903 | ||
|
|
280cbc2330 | ||
|
|
746e75b9e4 | ||
|
|
11decd5889 | ||
|
|
be136d3ce4 | ||
|
|
60fd4ec516 | ||
|
|
b136480669 | ||
|
|
07bd1e03d0 | ||
|
|
75ef1341eb | ||
|
|
ed6b60429c | ||
|
|
2edb12bc18 | ||
|
|
5843e226c3 | ||
|
|
4fc73fdc35 | ||
|
|
78fbc787a4 | ||
|
|
8af71742a0 | ||
|
|
22e550820d | ||
|
|
372f81a09e | ||
|
|
22f5ba4bb1 | ||
|
|
028e247df8 | ||
|
|
4038c0649c | ||
|
|
8aee574069 | ||
|
|
c220aa746a | ||
|
|
da00dd9eec | ||
|
|
30b55ae150 | ||
|
|
6b70826961 | ||
|
|
ae8dfde69d | ||
|
|
63661a40e3 | ||
|
|
b76ee25d49 | ||
|
|
3f7c7692ab | ||
|
|
e34d896278 | ||
|
|
3e90baef02 | ||
|
|
7ed87f87e0 | ||
|
|
35af0aa4b0 | ||
|
|
370b0674bd | ||
|
|
e0c3e28809 | ||
|
|
53302ac082 | ||
|
|
9ff605e08b | ||
|
|
5593327dbc | ||
|
|
31b0510bbd | ||
|
|
beba9c029d | ||
|
|
243053659c | ||
|
|
f2d30e3680 | ||
|
|
76d881bac1 | ||
|
|
c5fd282653 | ||
|
|
d276af6fa9 | ||
|
|
ba19ce4919 | ||
|
|
d86a7a1653 | ||
|
|
f3c82f85c8 | ||
|
|
b17eaba8bf | ||
|
|
0600d6a4d8 | ||
|
|
c51fb1779b | ||
|
|
34b7a28fbe | ||
|
|
a446152631 | ||
|
|
61c2abee35 | ||
|
|
c1d520f1cf | ||
|
|
3bd9e44155 | ||
|
|
7311517d65 | ||
|
|
4568a197e7 | ||
|
|
350134b256 | ||
|
|
1350cf5675 | ||
|
|
21db2e7d4a | ||
|
|
cda08242f1 | ||
|
|
fc8936986f | ||
|
|
ec37fd065f | ||
|
|
47875a4525 | ||
|
|
61adb1e6cf | ||
|
|
907e46631c | ||
|
|
aedf8cda47 | ||
|
|
7a1f5539ed | ||
|
|
49087e9a53 | ||
|
|
a128c7f18d | ||
|
|
c6704d8129 | ||
|
|
c43b375d3b | ||
|
|
9523bab910 | ||
|
|
a8fe62a829 | ||
|
|
4708a46ec9 | ||
|
|
7a99aaa53f | ||
|
|
e54e488f80 | ||
|
|
bf471cc3fa | ||
|
|
fd8c44ba90 | ||
|
|
f9e7f1c08e | ||
|
|
8042caee57 | ||
|
|
3f3f63f411 | ||
|
|
292687ea00 | ||
|
|
aa47cb7b97 | ||
|
|
794237bf30 | ||
|
|
de3aa16aca | ||
|
|
34d001cbef | ||
|
|
21bd51aef9 | ||
|
|
f45afd1f54 | ||
|
|
c975f894ae | ||
|
|
a9b59596d8 | ||
|
|
9333fcc1d6 | ||
|
|
b3426f86a3 | ||
|
|
b3e9682511 | ||
|
|
b3af6c9920 | ||
|
|
2e9fe80e33 | ||
|
|
def389356e | ||
|
|
c5dfa65994 | ||
|
|
c9159695aa | ||
|
|
17d1786e5c | ||
|
|
5437d2db1a | ||
|
|
e390e9901e | ||
|
|
7c946c59f8 | ||
|
|
506c74de55 | ||
|
|
ab9783102e | ||
|
|
a8a8cce25f | ||
|
|
d62e869044 | ||
|
|
6dd5e0fd20 | ||
|
|
c57a29c23f | ||
|
|
2866a51326 | ||
|
|
21f5488d3b | ||
|
|
7b1a188cfe | ||
|
|
fdcf7026d2 | ||
|
|
b8ada23e2b | ||
|
|
5def813a2e | ||
|
|
551a8251f9 | ||
|
|
951ce6f9f8 | ||
|
|
c9448870fa | ||
|
|
1d552ab603 | ||
|
|
e39d01e139 | ||
|
|
d4805bc709 | ||
|
|
35db2f61f7 | ||
|
|
8af1229f65 | ||
|
|
9aa6f5b1f7 | ||
|
|
da8e374443 | ||
|
|
95851e8f52 | ||
|
|
d2350b6786 | ||
|
|
08a2abb713 | ||
|
|
ac62f54aa5 | ||
|
|
fb189a3ce4 | ||
|
|
7e41938317 | ||
|
|
d90b28a399 | ||
|
|
90f5635478 | ||
|
|
acf8724402 | ||
|
|
f1c623c14b | ||
|
|
ee40ad59f2 | ||
|
|
845fac8adf | ||
|
|
896695b30f | ||
|
|
fbfa88739d | ||
|
|
ea191602da | ||
|
|
564287eb21 | ||
|
|
28790197aa | ||
|
|
1db66fd43d | ||
|
|
8d506db73c | ||
|
|
c288d4bd0b | ||
|
|
39247ac7ef | ||
|
|
47b5b10bf4 | ||
|
|
ad1cf15d7c | ||
|
|
6339d9f3cd | ||
|
|
fc36a76fc0 | ||
|
|
18d7fd4c7d | ||
|
|
76e44a1043 | ||
|
|
4c0d107562 | ||
|
|
d359120d81 | ||
|
|
278671deec | ||
|
|
e28360f86a | ||
|
|
75cef03644 | ||
|
|
33be6946f7 | ||
|
|
7f23b088a4 | ||
|
|
c56f9f3277 | ||
|
|
6198d5abf3 | ||
|
|
3f8c51cc01 | ||
|
|
b693ed4071 | ||
|
|
5621f5cdb0 | ||
|
|
83f2097f40 | ||
|
|
879de1d95e | ||
|
|
ec018f40aa | ||
|
|
0866753617 | ||
|
|
5f66f2c4a9 | ||
|
|
40f08a7f8b | ||
|
|
c274337fed | ||
|
|
7d01977a89 | ||
|
|
d058c1d4fc | ||
|
|
1a28155f1c | ||
|
|
7ca7d8e045 | ||
|
|
ec98ddc2df | ||
|
|
72b520745a | ||
|
|
9b4f0ca951 | ||
|
|
a761df80db | ||
|
|
9059904c1a | ||
|
|
568e728d20 | ||
|
|
e56d414357 | ||
|
|
668f8ccdbf | ||
|
|
9281be57fc | ||
|
|
d1e58eb95e | ||
|
|
9f5a15f00a | ||
|
|
3aa47043c9 | ||
|
|
8532db70d2 | ||
|
|
f8106a48ae | ||
|
|
2813934d21 | ||
|
|
defb793b0b | ||
|
|
1fd2162d4f | ||
|
|
094a1bd5ee | ||
|
|
2cf7a5f281 | ||
|
|
2ed451130c | ||
|
|
4d1f88627a | ||
|
|
ed7e51480b | ||
|
|
3e91be9a4d | ||
|
|
3e9bfad78f | ||
|
|
fd34fce3c1 | ||
|
|
0cf684300d | ||
|
|
fe1c808dfd | ||
|
|
aa8c67061c | ||
|
|
8e8de1eeec | ||
|
|
b9fb284a52 | ||
|
|
5efe447861 | ||
|
|
b9ab491cbb | ||
|
|
fb8d76922a | ||
|
|
1a97986bd2 | ||
|
|
a2bb650518 | ||
|
|
f4398a0867 | ||
|
|
b44a1ce939 | ||
|
|
59fb9b2202 | ||
|
|
86ba7333ad | ||
|
|
7e7eac0c75 | ||
|
|
ad50017235 | ||
|
|
aeb2e3a715 | ||
|
|
3d10282567 | ||
|
|
9e89666a24 | ||
|
|
953cf65223 | ||
|
|
371eb76b2b | ||
|
|
75c63704f6 | ||
|
|
16dab2c9f1 | ||
|
|
7fdaf62684 | ||
|
|
312c11b8da | ||
|
|
2ef4c28401 | ||
|
|
27dc8ffb45 | ||
|
|
c5296cd7e0 | ||
|
|
029b408915 | ||
|
|
4fdff81762 | ||
|
|
f3e4f9d311 | ||
|
|
74a667bbef | ||
|
|
7f8263318e | ||
|
|
e0fc832621 | ||
|
|
34aea14ff9 | ||
|
|
284d9ea22b | ||
|
|
1e2d86f054 | ||
|
|
aa81350b6f | ||
|
|
e02d298ff4 | ||
|
|
67f172b741 | ||
|
|
e97fe9de86 | ||
|
|
770bf15342 | ||
|
|
38b1556ee0 | ||
|
|
0af33850a6 | ||
|
|
edafa68414 | ||
|
|
f78debe768 | ||
|
|
4f0f7876a8 | ||
|
|
2a4376b7cb | ||
|
|
1d3d2a0bf9 | ||
|
|
bbddb0a036 | ||
|
|
a8e6aedd04 | ||
|
|
1b9fd6276e | ||
|
|
e229ab191f | ||
|
|
42084ea0cb | ||
|
|
35468bb417 | ||
|
|
9eb20e37c8 | ||
|
|
fa819064ef | ||
|
|
eb89c00bd5 | ||
|
|
0491bd1a76 | ||
|
|
802ae5b712 | ||
|
|
38a393d605 | ||
|
|
ba8b91ad29 | ||
|
|
4b88122ab2 | ||
|
|
0a9b0c107f | ||
|
|
ff1bc03361 | ||
|
|
3967862f10 | ||
|
|
70ab9b4b1b | ||
|
|
50682d73dc | ||
|
|
3926a61528 | ||
|
|
9082cd5b17 | ||
|
|
1c24f9f473 | ||
|
|
0d1c63f0fd | ||
|
|
29a4c66364 | ||
|
|
73bb1f3046 | ||
|
|
86169bbf7c | ||
|
|
bde65bf056 | ||
|
|
0ab7d3ca63 | ||
|
|
644a54e441 | ||
|
|
62ff25f96d | ||
|
|
dabb4402a7 | ||
|
|
4ae9c42cc6 | ||
|
|
be3d49163d | ||
|
|
0cf39a07f5 | ||
|
|
8697a79df0 | ||
|
|
f5e6a1f3de | ||
|
|
7a0ec5f51c | ||
|
|
dfcda95642 | ||
|
|
047ae7b033 | ||
|
|
fad681430e | ||
|
|
4f1af3709e | ||
|
|
2681a8196b | ||
|
|
25e7f6c5e8 | ||
|
|
a67a538dfd | ||
|
|
6dd7044786 | ||
|
|
16eaba1774 | ||
|
|
054805349c | ||
|
|
ec661d2c15 | ||
|
|
58533c495c | ||
|
|
5c1c68c553 | ||
|
|
a9c01926e8 | ||
|
|
8ef52d87f5 | ||
|
|
35296f9f82 | ||
|
|
898759b3b9 | ||
|
|
7cbd067daf | ||
|
|
3b6f5353fd | ||
|
|
47bc6b0411 | ||
|
|
b56ff0c31c | ||
|
|
5376ce0909 | ||
|
|
807df2ac55 | ||
|
|
2727f92a53 | ||
|
|
1297114b3e | ||
|
|
1ba35f0bc3 | ||
|
|
f5a6f51abc | ||
|
|
8431452f73 | ||
|
|
27aff76b20 | ||
|
|
e767e00a82 | ||
|
|
aab17ca79a | ||
|
|
94876d1019 | ||
|
|
7d7726e345 | ||
|
|
d16a65ebd7 | ||
|
|
e802d0098f | ||
|
|
62fd24bcfb | ||
|
|
02185145c9 | ||
|
|
4acb0f15ad | ||
|
|
c8d0b4dd3f | ||
|
|
4802bce06d | ||
|
|
5bec0ee6b6 | ||
|
|
a938a3a350 | ||
|
|
04567817b2 | ||
|
|
061a65f730 | ||
|
|
470894577d | ||
|
|
7db4da4154 | ||
|
|
78b5f6ef5c | ||
|
|
340f478bfa | ||
|
|
2108af8f36 | ||
|
|
1728b6c8bf | ||
|
|
bbfd514342 | ||
|
|
e42ae65355 | ||
|
|
0e8e00ddd7 |
@@ -13,5 +13,5 @@ indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# 2 space indentation
|
||||
[*.{yaml,yml}]
|
||||
[*.{yaml,yml,vue,js,css}]
|
||||
indent_size = 2
|
||||
|
||||
35
.github/workflows/build.yaml
vendored
35
.github/workflows/build.yaml
vendored
@@ -4,19 +4,30 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: "!github.event.release.prerelease"
|
||||
permissions:
|
||||
contents: write # for release creation (svenstaro/upload-release-action)
|
||||
|
||||
#if: "!github.event.release.prerelease"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- name: Extract Tag
|
||||
run: echo "PACKAGE_VERSION=${{ github.ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 7.3
|
||||
php-version: 8.3
|
||||
extensions: opcache, gd
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
env:
|
||||
COMPOSER_TOKEN: ${{ secrets.GLOBAL_TOKEN }}
|
||||
@@ -29,24 +40,28 @@ jobs:
|
||||
- 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
|
||||
curl --silent -H "Authorization: token ${{ secrets.GLOBAL_TOKEN }}" -H "Accept: application/vnd.github.v3.raw" ${{ secrets.BUILD_SCRIPT_URL_18 }} --output build-grav.sh
|
||||
|
||||
# Development Local URL
|
||||
# curl ${{ secrets.BUILD_SCRIPT_URL }} --output build-grav.sh
|
||||
# curl ${{ secrets.BUILD_SCRIPT_URL_18 }} --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 }}
|
||||
- name: Upload packages to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
asset_paths: '["./grav-dist/*.zip"]'
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ env.PACKAGE_VERSION }}
|
||||
file: ./grav-dist/*.zip
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
|
||||
slack:
|
||||
permissions:
|
||||
actions: read # to list jobs for workflow run (technote-space/workflow-conclusion-action)
|
||||
|
||||
name: Slack
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
36
.github/workflows/tests.yaml
vendored
36
.github/workflows/tests.yaml
vendored
@@ -2,45 +2,41 @@ name: PHP Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
branches: [ develop, 1.8 ]
|
||||
pull_request:
|
||||
branches: [ develop ]
|
||||
branches: [ develop, 1.8 ]
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
|
||||
unit-tests:
|
||||
strategy:
|
||||
matrix:
|
||||
php: [8.5, 8.4, 8.3]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php: [ 8.0, 7.4, 7.3]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
- name: Setup PHP ${{ matrix.php }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: opcache, gd
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
env:
|
||||
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# - name: Update composer
|
||||
# run: composer update
|
||||
#
|
||||
# - name: Validate composer.json and composer.lock
|
||||
# run: composer validate
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
@@ -50,7 +46,7 @@ jobs:
|
||||
run: composer install --prefer-dist --no-progress
|
||||
|
||||
- name: Run test suite
|
||||
run: vendor/bin/codecept run
|
||||
run: php -d register_argc_argv=On vendor/bin/codecept run
|
||||
|
||||
# slack:
|
||||
# name: Slack
|
||||
|
||||
48
.github/workflows/trigger-skeletons.yml
vendored
Normal file
48
.github/workflows/trigger-skeletons.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Trigger Skeletons Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Which Grav release to use'
|
||||
required: true
|
||||
default: 'latest'
|
||||
admin:
|
||||
description: 'Create also a package with Admin'
|
||||
required: true
|
||||
default: 'true'
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
WORKFLOW: "build-skeleton.yml"
|
||||
AUTH: ":${{secrets.GLOBAL_TOKEN}}"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Make it rain ☔️
|
||||
run: |
|
||||
SKELETONS=`curl -s "${{secrets.SKELETONS_JSON_LIST}}"`
|
||||
echo "$SKELETONS" | jq -cr '.[]' | while read SKELETON; do
|
||||
KEY=$(echo "$SKELETON" | jq -cr 'keys[0]')
|
||||
VERSION=$(echo "$SKELETON" | jq -cr '.[]')
|
||||
URL="https://api.github.com/repos/${KEY}/actions/workflows/${WORKFLOW}/dispatches"
|
||||
|
||||
curl -X POST \
|
||||
-u "${AUTH}" \
|
||||
-H "Accept: application/vnd.github.everest-preview+json" \
|
||||
-H "Content-Type: application/json" \
|
||||
-sS \
|
||||
${URL} \
|
||||
--data '{ "ref": "develop",
|
||||
"inputs": {
|
||||
"tag": "'"$VERSION"'",
|
||||
"version": "'"$INPUT_VERSION"'",
|
||||
"admin": "'"$INPUT_ADMIN"'"
|
||||
}
|
||||
}' > /dev/null
|
||||
echo "Dispatched Worfklow for ${KEY}@$VERSION"
|
||||
done
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -25,8 +25,11 @@ user/plugins/*
|
||||
!user/plugins/.*
|
||||
user/themes/*
|
||||
!user/themes/.*
|
||||
user/localhost/config/security.yaml
|
||||
user/config/security.yaml
|
||||
user/**/config/security.yaml
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.gravenv
|
||||
|
||||
# OS Generated
|
||||
.DS_Store*
|
||||
@@ -45,3 +48,7 @@ tests/cache/*
|
||||
tests/error.log
|
||||
system/templates/testing/*
|
||||
/user/config/versions.yaml
|
||||
/user/data/recovery.window
|
||||
tmp/*
|
||||
/AGENTS.md
|
||||
/.claude
|
||||
|
||||
@@ -59,9 +59,9 @@ RewriteRule .* index.php [L]
|
||||
# Block all direct access for these folders
|
||||
RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
# Block access to specific file types for these system folders
|
||||
RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
# Block access to specific file types for these user folders
|
||||
RewriteRule ^(user)/(.*)\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
# Block all direct access to .md files:
|
||||
RewriteRule \.md$ error [F]
|
||||
# Block all direct access to files and folders beginning with a dot
|
||||
|
||||
1044
CHANGELOG.md
1044
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
57
README.md
57
README.md
@@ -1,27 +1,26 @@
|
||||
#  Grav
|
||||
|
||||
[](https://github.com/phpstan/phpstan)
|
||||
[](https://insight.sensiolabs.com/projects/cfd20465-d0f8-4a0a-8444-467f5b5f16ad)
|
||||
[](https://chat.getgrav.org)
|
||||
[](https://github.com/getgrav/grav/actions?query=workflow%3A%22PHP+Tests%22) [](#backers) [](#sponsors)
|
||||
[](https://github.com/getgrav/grav/actions?query=workflow%3A%22PHP+Tests%22) [](#backers) [](#supporters) [](#sponsors)
|
||||
|
||||
Grav is a **Fast**, **Simple**, and **Flexible**, file-based Web-platform. There is **Zero** installation required. Just extract the ZIP archive, and you are already up and running. It follows similar principles to other flat-file CMS platforms, but has a different design philosophy than most. Grav comes with a powerful **Package Management System** to allow for simple installation and upgrading of plugins and themes, as well as simple updating of Grav itself.
|
||||
|
||||
The underlying architecture of Grav is designed to use well-established and _best-in-class_ technologies to ensure that Grav is simple to use and easy to extend. Some of these key technologies include:
|
||||
|
||||
* [Twig Templating](https://twig.sensiolabs.org/): for powerful control of the user interface
|
||||
* [Twig Templating](https://twig.symfony.com/): for powerful control of the user interface
|
||||
* [Markdown](https://en.wikipedia.org/wiki/Markdown): for easy content creation
|
||||
* [YAML](https://yaml.org): for simple configuration
|
||||
* [Parsedown](https://parsedown.org/): for fast Markdown and Markdown Extra support
|
||||
* [Doctrine Cache](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/caching.html): layer for performance
|
||||
* [Pimple Dependency Injection Container](https://pimple.sensiolabs.org/): for extensibility and maintainability
|
||||
* [Symfony Cache](https://symfony.com/doc/current/components/cache.html): backend layer for performance
|
||||
* [Pimple Dependency Injection Container](https://github.com/silexphp/Pimple): for extensibility and maintainability
|
||||
* [Symfony Event Dispatcher](https://symfony.com/doc/current/components/event_dispatcher/introduction.html): for plugin event handling
|
||||
* [Symfony Console](https://symfony.com/doc/current/components/console/introduction.html): for CLI interface
|
||||
* [Gregwar Image Library](https://github.com/Gregwar/Image): for dynamic image manipulation
|
||||
|
||||
# Requirements
|
||||
|
||||
- PHP 7.3.6 or higher. Check the [required modules list](https://learn.getgrav.org/basics/requirements#php-requirements)
|
||||
- PHP 8.3 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
|
||||
@@ -40,22 +39,22 @@ You can download a **ready-built** package from the [Downloads page on https://g
|
||||
|
||||
You can create a new project with the latest **stable** Grav release with the following command:
|
||||
|
||||
```
|
||||
$ composer create-project getgrav/grav ~/webroot/grav
|
||||
```bash
|
||||
composer create-project getgrav/grav ~/webroot/grav
|
||||
```
|
||||
|
||||
### From GitHub
|
||||
|
||||
1. Clone the Grav repository from [https://github.com/getgrav/grav]() to a folder in the webroot of your server, e.g. `~/webroot/grav`. Launch a **terminal** or **console** and navigate to the webroot folder:
|
||||
```
|
||||
$ cd ~/webroot
|
||||
$ git clone https://github.com/getgrav/grav.git
|
||||
```bash
|
||||
cd ~/webroot
|
||||
git clone https://github.com/getgrav/grav.git
|
||||
```
|
||||
|
||||
2. Install the **plugin** and **theme dependencies** by using the [Grav CLI application](https://learn.getgrav.org/advanced/grav-cli) `bin/grav`:
|
||||
```
|
||||
$ cd ~/webroot/grav
|
||||
$ bin/grav install
|
||||
```bash
|
||||
cd ~/webroot/grav
|
||||
bin/grav install
|
||||
```
|
||||
|
||||
Check out the [install procedures](https://learn.getgrav.org/basics/installation) for more information.
|
||||
@@ -64,35 +63,36 @@ Check out the [install procedures](https://learn.getgrav.org/basics/installation
|
||||
|
||||
You can download [plugins](https://getgrav.org/downloads/plugins) or [themes](https://getgrav.org/downloads/themes) manually from the appropriate tab on the [Downloads page on https://getgrav.org](https://getgrav.org/downloads), but the preferred solution is to use the [Grav Package Manager](https://learn.getgrav.org/advanced/grav-gpm) or `GPM`:
|
||||
|
||||
```
|
||||
$ bin/gpm index
|
||||
```bash
|
||||
bin/gpm index
|
||||
```
|
||||
|
||||
This will display all the available plugins and then you can install one or more with:
|
||||
|
||||
```
|
||||
$ bin/gpm install <plugin/theme>
|
||||
```bash
|
||||
bin/gpm install <plugin/theme>
|
||||
```
|
||||
|
||||
# Updating
|
||||
|
||||
To update Grav you should use the [Grav Package Manager](https://learn.getgrav.org/advanced/grav-gpm) or `GPM`:
|
||||
|
||||
```
|
||||
$ bin/gpm selfupgrade
|
||||
```bash
|
||||
bin/gpm selfupgrade
|
||||
```
|
||||
|
||||
To update plugins and themes:
|
||||
|
||||
```
|
||||
$ bin/gpm update
|
||||
```bash
|
||||
bin/gpm update
|
||||
```
|
||||
|
||||
## Upgrading from older version
|
||||
|
||||
* [Upgrading to Grav 1.8](https://learn.getgrav.org/16/advanced/grav-development/grav-18-upgrade-guide)
|
||||
* [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)
|
||||
* [Upgrading from Grav before 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.
|
||||
@@ -118,12 +118,19 @@ If you discover a possible security issue related to Grav or one of its plugins,
|
||||
* More [Awesome Grav Stuff](https://github.com/getgrav/awesome-grav)
|
||||
|
||||
# Backers
|
||||
Support Grav with a monthly donation to help us continue development. [[Become a backer](https://opencollective.com/grav#backer)]
|
||||
Support Grav with a monthly donation to help us continue development. [[Become a backer](https://opencollective.com/grav/contribute)]
|
||||
|
||||
<img src="https://opencollective.com/grav/tiers/backers.svg?avatarHeight=36&width=600" />
|
||||
|
||||
|
||||
# Supporters
|
||||
Support Grav with a monthly donation to help us continue development. [[Become a supporter](https://opencollective.com/grav/contribute)]
|
||||
|
||||
<img src="https://opencollective.com/grav/tiers/supporters.svg?avatarHeight=36&width=600" />
|
||||
|
||||
|
||||
# Sponsors
|
||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/grav#sponsor)]
|
||||
Support Grav with a yearly donation to help us continue development. [[Become a sponsor](https://opencollective.com/grav/contribute)]
|
||||
|
||||
<img src="https://opencollective.com/grav/tiers/sponsors.svg?avatarHeight=36&width=600" />
|
||||
|
||||
|
||||
29
SECURITY.md
29
SECURITY.md
@@ -7,9 +7,32 @@ 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 | :x: |
|
||||
| < 1.6 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
## :pushpin: Note on Security Severity
|
||||
|
||||
> NOTE: Please use the following guidelines when selecting a **Severity**. Submitted advisories that are marked **High** or **Critical** that don't meet the guidelines below will be closed.
|
||||
|
||||
* **CRITICAL** - no account required, can modify content, or run malicious code or nefarious activity without any access.
|
||||
* **HIGH** - publisher level account able to run malicious code or nefarious activity, or other high level security things.
|
||||
* **MODERATE** - admin level account able to run malicious code or do nefarious things. other moderate security things.
|
||||
* **LOW** - super admin level account able to run malicious code or do nefarious things. other minor security things.
|
||||
|
||||
## :warning: Versions
|
||||
|
||||
Versions with :warning: will be supported for security issues, however you won't be able to update to them, you will need to manually update through the [`direct-install` command](https://learn.getgrav.org/17/admin-panel/tools).
|
||||
|
||||
If you cannot update to the latest stable version available because, for example, your server does not meet the minimum PHP requirements, you can manually install a previous version by downloading the package from our Releases directory (https://github.com/getgrav/grav/releases).
|
||||
|
||||
## :pencil: Reporting a Vulnerability
|
||||
|
||||
Please contact security@getgrav.org with a detailed explanation of the security issue found. If it appears to be a legitimate issues, please submit an **advisory via GitHub Security**: https://github.com/getgrav/grav/security/advisories
|
||||
|
||||
> NOTE: Please do not use 3rd party security issue reporting services, we like to keep everything in the GitHub ecosystem for easier manageability.
|
||||
|
||||
## :bug: Bug Bounties
|
||||
|
||||
We do greatly appreciate your efforts to improve Grav, but unfortunately because we are a small open source project, we **do not have the resources to offer bounties** for security issues found.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
@@ -1 +1 @@
|
||||
/* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. */
|
||||
/* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved. */
|
||||
|
||||
@@ -1 +1 @@
|
||||
/* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. */
|
||||
/* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved. */
|
||||
|
||||
209
bin/build-test-update.php
Executable file
209
bin/build-test-update.php
Executable file
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
if (!\defined('GRAV_ROOT')) {
|
||||
\define('GRAV_ROOT', realpath(__DIR__ . '/..') ?: getcwd());
|
||||
}
|
||||
|
||||
if (!\extension_loaded('zip')) {
|
||||
fwrite(STDERR, "The PHP zip extension is required.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$options = getopt('', [
|
||||
'version:',
|
||||
'output::',
|
||||
'port::',
|
||||
'base-url::',
|
||||
'serve',
|
||||
]);
|
||||
|
||||
if (!isset($options['version'])) {
|
||||
fwrite(
|
||||
STDERR,
|
||||
"Usage: php bin/build-test-update.php --version=1.7.999 [--output=tmp/test-gpm] [--port=8043] [--base-url=http://127.0.0.1:8043] [--serve]\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$version = trim((string) $options['version']);
|
||||
if ($version === '') {
|
||||
fwrite(STDERR, "A non-empty --version value is required.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$root = GRAV_ROOT;
|
||||
|
||||
$output = $options['output'] ?? $root . '/tmp/test-gpm';
|
||||
if (!str_starts_with($output, DIRECTORY_SEPARATOR)) {
|
||||
$output = $root . '/' . ltrim($output, '/');
|
||||
}
|
||||
$output = rtrim($output, DIRECTORY_SEPARATOR);
|
||||
|
||||
$defaultPort = isset($options['port']) ? (int) $options['port'] : 8043;
|
||||
$baseUrl = $options['base-url'] ?? sprintf('http://127.0.0.1:%d', $defaultPort);
|
||||
$serve = array_key_exists('serve', $options);
|
||||
|
||||
Folder::create($output);
|
||||
|
||||
$downloadName = sprintf('grav-update-%s.zip', $version);
|
||||
$zipPath = $output . '/' . $downloadName;
|
||||
$jsonPath = $output . '/grav.json';
|
||||
$zipPrefix = 'grav-update/';
|
||||
|
||||
$excludeDirs = [
|
||||
'.build',
|
||||
'.crush',
|
||||
'.ddev',
|
||||
'.git',
|
||||
'.github',
|
||||
'.gitlab',
|
||||
'.circleci',
|
||||
'.idea',
|
||||
'.vscode',
|
||||
'.pytest_cache',
|
||||
'backup',
|
||||
'cache',
|
||||
'images',
|
||||
'logs',
|
||||
'node_modules',
|
||||
'tests',
|
||||
'tmp',
|
||||
'user',
|
||||
];
|
||||
|
||||
$excludeFiles = [
|
||||
'.htaccess',
|
||||
'.DS_Store',
|
||||
'robots.txt',
|
||||
];
|
||||
|
||||
$directory = new RecursiveDirectoryIterator($root, RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$filtered = new RecursiveCallbackFilterIterator(
|
||||
$directory,
|
||||
function (SplFileInfo $current) use ($root, $excludeDirs, $excludeFiles): bool {
|
||||
$relative = ltrim(str_replace($root, '', $current->getPathname()), DIRECTORY_SEPARATOR);
|
||||
$relative = str_replace('\\', '/', $relative);
|
||||
|
||||
if ($relative === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (str_contains($relative, '..')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($excludeDirs as $prefix) {
|
||||
$prefix = trim($prefix, '/');
|
||||
if ($prefix === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($relative === $prefix || str_starts_with($relative, $prefix . '/')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($current->getFilename(), $excludeFiles, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
|
||||
throw new RuntimeException(sprintf('Unable to open archive at %s', $zipPath));
|
||||
}
|
||||
|
||||
$zip->addEmptyDir($zipPrefix);
|
||||
|
||||
$iterator = new RecursiveIteratorIterator($filtered, RecursiveIteratorIterator::SELF_FIRST);
|
||||
/** @var SplFileInfo $fileInfo */
|
||||
foreach ($iterator as $fileInfo) {
|
||||
$fullPath = $fileInfo->getPathname();
|
||||
$relative = ltrim(str_replace($root, '', $fullPath), DIRECTORY_SEPARATOR);
|
||||
$relative = str_replace('\\', '/', $relative);
|
||||
$targetPath = $zipPrefix . $relative;
|
||||
|
||||
if ($fileInfo->isDir()) {
|
||||
$zip->addEmptyDir(rtrim($targetPath, '/') . '/');
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($fileInfo->isLink()) {
|
||||
$target = readlink($fullPath);
|
||||
$zip->addFromString($targetPath, $target === false ? '' : $target);
|
||||
$zip->setExternalAttributesName($targetPath, ZipArchive::OPSYS_UNIX, 0120000 << 16);
|
||||
continue;
|
||||
}
|
||||
|
||||
$zip->addFile($fullPath, $targetPath);
|
||||
|
||||
$perms = @fileperms($fullPath);
|
||||
if ($perms !== false) {
|
||||
$zip->setExternalAttributesName($targetPath, ZipArchive::OPSYS_UNIX, ($perms & 0xFFFF) << 16);
|
||||
}
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
|
||||
$size = filesize($zipPath);
|
||||
$sha256 = hash_file('sha256', $zipPath);
|
||||
$timestamp = date('c');
|
||||
$downloadUrl = rtrim($baseUrl, '/') . '/' . rawurlencode($downloadName);
|
||||
|
||||
$manifest = [
|
||||
'version' => $version,
|
||||
'date' => $timestamp,
|
||||
'min_php' => '8.3.0',
|
||||
'assets' => [
|
||||
'grav-update' => [
|
||||
'name' => $downloadName,
|
||||
'slug' => 'grav-update',
|
||||
'version' => $version,
|
||||
'date' => $timestamp,
|
||||
'testing' => false,
|
||||
'description' => 'Local test update package generated for safe-upgrade validation.',
|
||||
'download' => $downloadUrl,
|
||||
'size' => $size,
|
||||
'checksum' => 'sha256:' . $sha256,
|
||||
'sha256' => $sha256,
|
||||
'host' => parse_url($downloadUrl, PHP_URL_HOST),
|
||||
],
|
||||
],
|
||||
'changelog' => [
|
||||
$version => [
|
||||
'date' => $timestamp,
|
||||
'content' => "- Local test update package generated by build-test-update.\n",
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
file_put_contents($jsonPath, json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL);
|
||||
|
||||
$manifestUrl = rtrim($baseUrl, '/') . '/grav.json';
|
||||
|
||||
echo "Update package created at: {$zipPath}\n";
|
||||
echo "Manifest written to: {$jsonPath}\n";
|
||||
echo "Manifest URL: {$manifestUrl}\n";
|
||||
echo "Download URL: {$downloadUrl}\n";
|
||||
echo "Archive size: {$size} bytes\n";
|
||||
echo "SHA256: {$sha256}\n";
|
||||
|
||||
if ($serve) {
|
||||
$host = parse_url($baseUrl, PHP_URL_HOST) ?: '127.0.0.1';
|
||||
$port = parse_url($baseUrl, PHP_URL_PORT) ?: $defaultPort;
|
||||
$command = sprintf('php -S %s:%d -t %s', $host, $port, escapeshellarg($output));
|
||||
echo "\nServing files using PHP built-in server. Press Ctrl+C to stop.\n";
|
||||
echo $command . "\n\n";
|
||||
passthru($command);
|
||||
}
|
||||
Binary file not shown.
14
bin/gpm
14
bin/gpm
@@ -2,7 +2,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -25,18 +25,10 @@ if (!file_exists(__DIR__ . '/../vendor/autoload.php')){
|
||||
|
||||
$autoload = require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
// 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');
|
||||
|
||||
|
||||
14
bin/grav
14
bin/grav
@@ -2,7 +2,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -25,18 +25,10 @@ if (!file_exists(__DIR__ . '/../vendor/autoload.php')){
|
||||
|
||||
$autoload = require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
// 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');
|
||||
|
||||
|
||||
14
bin/plugin
14
bin/plugin
@@ -2,7 +2,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -25,18 +25,10 @@ if (!file_exists(__DIR__ . '/../vendor/autoload.php')){
|
||||
|
||||
$autoload = require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
// 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');
|
||||
|
||||
|
||||
634
bin/restore
Executable file
634
bin/restore
Executable file
@@ -0,0 +1,634 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Grav Snapshot Restore Utility
|
||||
*
|
||||
* Lightweight CLI that can list and apply safe-upgrade snapshots without
|
||||
* bootstrapping the full Grav application (or any plugins).
|
||||
*/
|
||||
|
||||
$root = dirname(__DIR__);
|
||||
|
||||
define('GRAV_CLI', true);
|
||||
define('GRAV_REQUEST_TIME', microtime(true));
|
||||
|
||||
if (!file_exists($root . '/vendor/autoload.php')) {
|
||||
fwrite(STDERR, "Unable to locate vendor/autoload.php. Run composer install first.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$autoload = require $root . '/vendor/autoload.php';
|
||||
|
||||
if (!file_exists($root . '/index.php')) {
|
||||
fwrite(STDERR, "FATAL: Must be run from Grav root directory.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Recovery\RecoveryManager;
|
||||
use Grav\Common\Upgrade\SafeUpgradeService;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
const RESTORE_USAGE = <<<USAGE
|
||||
Grav Restore Utility
|
||||
|
||||
Usage:
|
||||
bin/restore list [--staging-root=/absolute/path]
|
||||
Lists all available snapshots (most recent first).
|
||||
|
||||
bin/restore apply <snapshot-id> [--staging-root=/absolute/path]
|
||||
Restores the specified snapshot created by safe-upgrade.
|
||||
|
||||
bin/restore remove [<snapshot-id> ...] [--staging-root=/absolute/path]
|
||||
Deletes one or more snapshots (interactive selection when no id provided).
|
||||
|
||||
bin/restore snapshot [--label=\"optional description\"] [--staging-root=/absolute/path]
|
||||
Creates a manual snapshot of the current Grav core files.
|
||||
|
||||
bin/restore recovery [status|clear]
|
||||
Shows the recovery flag context or clears it.
|
||||
|
||||
Options:
|
||||
--staging-root Overrides the staging directory (defaults to configured value).
|
||||
--label Optional label to store with the manual snapshot.
|
||||
|
||||
Examples:
|
||||
bin/restore list
|
||||
bin/restore apply stage-68eff31cc4104
|
||||
bin/restore apply stage-68eff31cc4104 --staging-root=/var/grav-backups
|
||||
bin/restore snapshot --label=\"Before plugin install\"
|
||||
bin/restore recovery status
|
||||
bin/restore recovery clear
|
||||
USAGE;
|
||||
|
||||
/**
|
||||
* @param array $args
|
||||
* @return array{command:string,arguments:array,options:array}
|
||||
*/
|
||||
function parseArguments(array $args): array
|
||||
{
|
||||
array_shift($args); // remove script name
|
||||
|
||||
$command = null;
|
||||
$arguments = [];
|
||||
$options = [];
|
||||
|
||||
while ($args) {
|
||||
$arg = array_shift($args);
|
||||
if (strncmp($arg, '--', 2) === 0) {
|
||||
$parts = explode('=', substr($arg, 2), 2);
|
||||
$name = $parts[0] ?? '';
|
||||
if ($name === '') {
|
||||
continue;
|
||||
}
|
||||
$value = $parts[1] ?? null;
|
||||
if ($value === null && $args && substr($args[0], 0, 2) !== '--') {
|
||||
$value = array_shift($args);
|
||||
}
|
||||
$options[$name] = $value ?? true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (null === $command) {
|
||||
$command = $arg;
|
||||
} else {
|
||||
$arguments[] = $arg;
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $command) {
|
||||
$command = 'interactive';
|
||||
}
|
||||
|
||||
return [
|
||||
'command' => $command,
|
||||
'arguments' => $arguments,
|
||||
'options' => $options,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return SafeUpgradeService
|
||||
*/
|
||||
function createUpgradeService(array $options): SafeUpgradeService
|
||||
{
|
||||
$serviceOptions = ['root' => GRAV_ROOT];
|
||||
|
||||
if (isset($options['staging-root']) && is_string($options['staging-root']) && $options['staging-root'] !== '') {
|
||||
$serviceOptions['staging_root'] = $options['staging-root'];
|
||||
}
|
||||
|
||||
return new SafeUpgradeService($serviceOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{id:string,label:?string,source_version:?string,target_version:?string,created_at:int}>
|
||||
*/
|
||||
function loadSnapshots(): array
|
||||
{
|
||||
$manifestDir = GRAV_ROOT . '/user/data/upgrades';
|
||||
if (!is_dir($manifestDir)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$files = glob($manifestDir . '/*.json') ?: [];
|
||||
rsort($files);
|
||||
|
||||
$snapshots = [];
|
||||
foreach ($files as $file) {
|
||||
$decoded = json_decode(file_get_contents($file) ?: '', true);
|
||||
if (!is_array($decoded) || empty($decoded['id'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$snapshots[] = [
|
||||
'id' => $decoded['id'],
|
||||
'label' => $decoded['label'] ?? null,
|
||||
'source_version' => $decoded['source_version'] ?? null,
|
||||
'target_version' => $decoded['target_version'] ?? null,
|
||||
'created_at' => (int)($decoded['created_at'] ?? 0),
|
||||
];
|
||||
}
|
||||
|
||||
return $snapshots;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array{id:string,label:?string,source_version:?string,target_version:?string,created_at:int}> $snapshots
|
||||
* @return string
|
||||
*/
|
||||
function formatSnapshotListLine(array $snapshot): string
|
||||
{
|
||||
$restoreVersion = $snapshot['source_version'] ?? $snapshot['target_version'] ?? 'unknown';
|
||||
$timeLabel = formatSnapshotTimestamp($snapshot['created_at']);
|
||||
$label = $snapshot['label'] ?? null;
|
||||
$display = $label ? sprintf('%s [%s]', $label, $snapshot['id']) : $snapshot['id'];
|
||||
|
||||
return sprintf('%s (restore to Grav %s, %s)', $display, $restoreVersion, $timeLabel);
|
||||
}
|
||||
|
||||
function formatSnapshotTimestamp(int $timestamp): string
|
||||
{
|
||||
if ($timestamp <= 0) {
|
||||
return 'time unknown';
|
||||
}
|
||||
|
||||
try {
|
||||
$timezone = resolveTimezone();
|
||||
$dt = new DateTime('@' . $timestamp);
|
||||
$dt->setTimezone($timezone);
|
||||
$formatted = $dt->format('Y-m-d H:i:s T');
|
||||
} catch (\Throwable $e) {
|
||||
$formatted = date('Y-m-d H:i:s T', $timestamp);
|
||||
}
|
||||
|
||||
return $formatted . ' (' . formatRelative(time() - $timestamp) . ')';
|
||||
}
|
||||
|
||||
function resolveTimezone(): DateTimeZone
|
||||
{
|
||||
static $resolved = null;
|
||||
if ($resolved instanceof DateTimeZone) {
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
$timezone = null;
|
||||
$configFile = GRAV_ROOT . '/user/config/system.yaml';
|
||||
if (is_file($configFile)) {
|
||||
try {
|
||||
$data = Yaml::parse(file_get_contents($configFile) ?: '') ?: [];
|
||||
if (!empty($data['system']['timezone']) && is_string($data['system']['timezone'])) {
|
||||
$timezone = $data['system']['timezone'];
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// ignore parse errors, fallback below
|
||||
}
|
||||
}
|
||||
|
||||
if (!$timezone) {
|
||||
$timezone = ini_get('date.timezone') ?: 'UTC';
|
||||
}
|
||||
|
||||
try {
|
||||
$resolved = new DateTimeZone($timezone);
|
||||
} catch (\Throwable $e) {
|
||||
$resolved = new DateTimeZone('UTC');
|
||||
}
|
||||
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
function formatRelative(int $seconds): string
|
||||
{
|
||||
if ($seconds < 5) {
|
||||
return 'just now';
|
||||
}
|
||||
$negative = $seconds < 0;
|
||||
$seconds = abs($seconds);
|
||||
$units = [
|
||||
31536000 => 'y',
|
||||
2592000 => 'mo',
|
||||
604800 => 'w',
|
||||
86400 => 'd',
|
||||
3600 => 'h',
|
||||
60 => 'm',
|
||||
1 => 's',
|
||||
];
|
||||
foreach ($units as $size => $label) {
|
||||
if ($seconds >= $size) {
|
||||
$value = (int)floor($seconds / $size);
|
||||
$suffix = $label === 'mo' ? 'month' : ($label === 'y' ? 'year' : ($label === 'w' ? 'week' : ($label === 'd' ? 'day' : ($label === 'h' ? 'hour' : ($label === 'm' ? 'minute' : 'second')))));
|
||||
if ($value !== 1) {
|
||||
$suffix .= 's';
|
||||
}
|
||||
$phrase = $value . ' ' . $suffix;
|
||||
return $negative ? 'in ' . $phrase : $phrase . ' ago';
|
||||
}
|
||||
}
|
||||
|
||||
return $negative ? 'in 0 seconds' : '0 seconds ago';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $snapshotId
|
||||
* @param array $options
|
||||
* @return void
|
||||
*/
|
||||
function applySnapshot(string $snapshotId, array $options): void
|
||||
{
|
||||
try {
|
||||
$service = createUpgradeService($options);
|
||||
$manifest = $service->rollback($snapshotId);
|
||||
} catch (\Throwable $e) {
|
||||
fwrite(STDERR, "Restore failed: " . $e->getMessage() . "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!$manifest) {
|
||||
fwrite(STDERR, "Snapshot {$snapshotId} not found.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$version = $manifest['source_version'] ?? $manifest['target_version'] ?? 'unknown';
|
||||
echo "Restored snapshot {$snapshotId} (Grav {$version}).\n";
|
||||
if (!empty($manifest['id'])) {
|
||||
echo "Snapshot manifest: {$manifest['id']}\n";
|
||||
}
|
||||
if (!empty($manifest['backup_path'])) {
|
||||
echo "Snapshot path: {$manifest['backup_path']}\n";
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return void
|
||||
*/
|
||||
function createManualSnapshot(array $options): void
|
||||
{
|
||||
$label = null;
|
||||
if (isset($options['label']) && is_string($options['label'])) {
|
||||
$label = trim($options['label']);
|
||||
if ($label === '') {
|
||||
$label = null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$service = createUpgradeService($options);
|
||||
$manifest = $service->createSnapshot($label);
|
||||
} catch (\Throwable $e) {
|
||||
fwrite(STDERR, "Snapshot creation failed: " . $e->getMessage() . "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$snapshotId = $manifest['id'] ?? null;
|
||||
if (!$snapshotId) {
|
||||
$snapshotId = 'unknown';
|
||||
}
|
||||
$version = $manifest['source_version'] ?? $manifest['target_version'] ?? 'unknown';
|
||||
|
||||
echo "Created snapshot {$snapshotId} (Grav {$version}).\n";
|
||||
if ($label) {
|
||||
echo "Label: {$label}\n";
|
||||
}
|
||||
if (!empty($manifest['backup_path'])) {
|
||||
echo "Snapshot path: {$manifest['backup_path']}\n";
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array{id:string,source_version:?string,target_version:?string,created_at:int}> $snapshots
|
||||
* @return string|null
|
||||
*/
|
||||
function promptSnapshotSelection(array $snapshots): ?string
|
||||
{
|
||||
echo "Available snapshots:\n";
|
||||
foreach ($snapshots as $index => $snapshot) {
|
||||
$line = formatSnapshotListLine($snapshot);
|
||||
$number = $index + 1;
|
||||
echo sprintf(" [%d] %s\n", $number, $line);
|
||||
}
|
||||
|
||||
$default = $snapshots[0]['id'];
|
||||
echo "\nSelect a snapshot to restore [1]: ";
|
||||
$input = trim((string)fgets(STDIN));
|
||||
|
||||
if ($input === '') {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if (ctype_digit($input)) {
|
||||
$idx = (int)$input - 1;
|
||||
if (isset($snapshots[$idx])) {
|
||||
return $snapshots[$idx]['id'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($snapshots as $snapshot) {
|
||||
if (strcasecmp($snapshot['id'], $input) === 0) {
|
||||
return $snapshot['id'];
|
||||
}
|
||||
}
|
||||
|
||||
echo "Invalid selection. Aborting.\n";
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array{id:string,source_version:?string,target_version:?string,created_at:int}> $snapshots
|
||||
* @return array<string>
|
||||
*/
|
||||
function promptSnapshotsRemoval(array $snapshots): array
|
||||
{
|
||||
echo "Available snapshots:\n";
|
||||
foreach ($snapshots as $index => $snapshot) {
|
||||
$line = formatSnapshotListLine($snapshot);
|
||||
$number = $index + 1;
|
||||
echo sprintf(" [%d] %s\n", $number, $line);
|
||||
}
|
||||
|
||||
echo "\nSelect snapshots to remove (comma or space separated numbers / ids, 'all' for everything, empty to cancel): ";
|
||||
$input = trim((string)fgets(STDIN));
|
||||
|
||||
if ($input === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$inputLower = strtolower($input);
|
||||
if ($inputLower === 'all' || $inputLower === '*') {
|
||||
return array_values(array_unique(array_column($snapshots, 'id')));
|
||||
}
|
||||
|
||||
$tokens = preg_split('/[\\s,]+/', $input, -1, PREG_SPLIT_NO_EMPTY) ?: [];
|
||||
$selected = [];
|
||||
foreach ($tokens as $token) {
|
||||
if (ctype_digit($token)) {
|
||||
$idx = (int)$token - 1;
|
||||
if (isset($snapshots[$idx])) {
|
||||
$selected[] = $snapshots[$idx]['id'];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($snapshots as $snapshot) {
|
||||
if (strcasecmp($snapshot['id'], $token) === 0) {
|
||||
$selected[] = $snapshot['id'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique(array_filter($selected)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $snapshotId
|
||||
* @return array{success:bool,message:string}
|
||||
*/
|
||||
function removeSnapshot(string $snapshotId): array
|
||||
{
|
||||
$manifestDir = GRAV_ROOT . '/user/data/upgrades';
|
||||
$manifestPath = $manifestDir . '/' . $snapshotId . '.json';
|
||||
if (!is_file($manifestPath)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => "Snapshot {$snapshotId} not found."
|
||||
];
|
||||
}
|
||||
|
||||
$manifest = json_decode(file_get_contents($manifestPath) ?: '', true);
|
||||
if (!is_array($manifest)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => "Snapshot {$snapshotId} manifest is invalid."
|
||||
];
|
||||
}
|
||||
|
||||
$pathsToDelete = [];
|
||||
foreach (['package_path', 'backup_path'] as $key) {
|
||||
if (!empty($manifest[$key]) && is_string($manifest[$key])) {
|
||||
$pathsToDelete[] = $manifest[$key];
|
||||
}
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
|
||||
foreach ($pathsToDelete as $path) {
|
||||
if (!$path) {
|
||||
continue;
|
||||
}
|
||||
if (!file_exists($path)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (is_dir($path)) {
|
||||
Folder::delete($path);
|
||||
} else {
|
||||
@unlink($path);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$errors[] = "Unable to remove {$path}: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
if (!@unlink($manifestPath)) {
|
||||
$errors[] = "Unable to delete manifest file {$manifestPath}.";
|
||||
}
|
||||
|
||||
if ($errors) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => implode(' ', $errors)
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => "Removed snapshot {$snapshotId}."
|
||||
];
|
||||
}
|
||||
|
||||
$cli = parseArguments($argv);
|
||||
$command = $cli['command'];
|
||||
$arguments = $cli['arguments'];
|
||||
$options = $cli['options'];
|
||||
|
||||
switch ($command) {
|
||||
case 'interactive':
|
||||
$snapshots = loadSnapshots();
|
||||
if (!$snapshots) {
|
||||
echo "No snapshots found. Run bin/gpm self-upgrade (with safe upgrade enabled) to create one.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$selection = promptSnapshotSelection($snapshots);
|
||||
if (!$selection) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
applySnapshot($selection, $options);
|
||||
break;
|
||||
|
||||
case 'list':
|
||||
$snapshots = loadSnapshots();
|
||||
if (!$snapshots) {
|
||||
echo "No snapshots found. Run bin/gpm self-upgrade (with safe upgrade enabled) to create one.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
echo "Available snapshots:\n";
|
||||
foreach ($snapshots as $snapshot) {
|
||||
echo ' - ' . formatSnapshotListLine($snapshot) . "\n";
|
||||
}
|
||||
exit(0);
|
||||
|
||||
case 'remove':
|
||||
$snapshots = loadSnapshots();
|
||||
if (!$snapshots) {
|
||||
echo "No snapshots found. Nothing to remove.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$selectedIds = [];
|
||||
if ($arguments) {
|
||||
foreach ($arguments as $arg) {
|
||||
if (!$arg) {
|
||||
continue;
|
||||
}
|
||||
$selectedIds[] = $arg;
|
||||
}
|
||||
} else {
|
||||
$selectedIds = promptSnapshotsRemoval($snapshots);
|
||||
if (!$selectedIds) {
|
||||
echo "No snapshots selected. Aborting.\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
$selectedIds = array_values(array_unique($selectedIds));
|
||||
echo "Snapshots selected for removal:\n";
|
||||
foreach ($selectedIds as $id) {
|
||||
echo " - {$id}\n";
|
||||
}
|
||||
|
||||
$autoConfirm = isset($options['yes']) || isset($options['y']);
|
||||
if (!$autoConfirm) {
|
||||
echo "\nThis action cannot be undone. Proceed? [y/N] ";
|
||||
$confirmation = strtolower(trim((string)fgets(STDIN)));
|
||||
if (!in_array($confirmation, ['y', 'yes'], true)) {
|
||||
echo "Aborted.\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
$success = 0;
|
||||
foreach ($selectedIds as $id) {
|
||||
$result = removeSnapshot($id);
|
||||
echo $result['message'] . "\n";
|
||||
if ($result['success']) {
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
|
||||
exit($success > 0 ? 0 : 1);
|
||||
|
||||
case 'apply':
|
||||
$snapshotId = $arguments[0] ?? null;
|
||||
if (!$snapshotId) {
|
||||
echo "Missing snapshot id.\n\n" . RESTORE_USAGE . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
applySnapshot($snapshotId, $options);
|
||||
break;
|
||||
|
||||
case 'snapshot':
|
||||
createManualSnapshot($options);
|
||||
break;
|
||||
|
||||
case 'recovery':
|
||||
$action = strtolower($arguments[0] ?? 'status');
|
||||
$manager = new RecoveryManager(GRAV_ROOT);
|
||||
|
||||
switch ($action) {
|
||||
case 'clear':
|
||||
if ($manager->isActive()) {
|
||||
$manager->clear();
|
||||
echo "Recovery flag cleared.\n";
|
||||
} else {
|
||||
echo "Recovery mode is not active.\n";
|
||||
}
|
||||
exit(0);
|
||||
|
||||
case 'status':
|
||||
if (!$manager->isActive()) {
|
||||
echo "Recovery mode is not active.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$context = $manager->getContext();
|
||||
if (!$context) {
|
||||
echo "Recovery flag present but context could not be parsed.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$created = isset($context['created_at']) ? date('c', (int)$context['created_at']) : 'unknown';
|
||||
$token = $context['token'] ?? '(missing)';
|
||||
$message = $context['message'] ?? '(no message)';
|
||||
$plugin = $context['plugin'] ?? '(none detected)';
|
||||
$file = $context['file'] ?? '(unknown file)';
|
||||
$line = $context['line'] ?? '(unknown line)';
|
||||
|
||||
echo "Recovery flag context:\n";
|
||||
echo " Token: {$token}\n";
|
||||
echo " Message: {$message}\n";
|
||||
echo " Plugin: {$plugin}\n";
|
||||
echo " File: {$file}\n";
|
||||
echo " Line: {$line}\n";
|
||||
echo " Created: {$created}\n";
|
||||
|
||||
$window = $manager->getUpgradeWindow();
|
||||
if ($window) {
|
||||
$expires = isset($window['expires_at']) ? date('c', (int)$window['expires_at']) : 'unknown';
|
||||
$reason = $window['reason'] ?? '(unknown)';
|
||||
echo " Window: active ({$reason}, expires {$expires})\n";
|
||||
} else {
|
||||
echo " Window: inactive\n";
|
||||
}
|
||||
exit(0);
|
||||
|
||||
default:
|
||||
echo "Unknown recovery action: {$action}\n\n" . RESTORE_USAGE . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
case 'help':
|
||||
default:
|
||||
echo RESTORE_USAGE . "\n";
|
||||
exit($command === 'help' ? 0 : 1);
|
||||
}
|
||||
2
cache/.gitkeep
vendored
2
cache/.gitkeep
vendored
@@ -1 +1 @@
|
||||
/* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. */
|
||||
/* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved. */
|
||||
|
||||
@@ -2,7 +2,7 @@ actor: Tester
|
||||
bootstrap: _bootstrap.php
|
||||
paths:
|
||||
tests: tests
|
||||
log: tests/_output
|
||||
output: tests/_output
|
||||
data: tests/_data
|
||||
support: tests/_support
|
||||
envs: tests/_envs
|
||||
|
||||
155
composer.json
155
composer.json
@@ -12,67 +12,86 @@
|
||||
"homepage": "https://getgrav.org",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.3.6 || ^8.0",
|
||||
"php": "^8.3",
|
||||
"ext-json": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-dom": "*",
|
||||
"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",
|
||||
"ext-gd": "*",
|
||||
"symfony/polyfill-mbstring": "^1.24",
|
||||
"symfony/polyfill-iconv": "^1.24",
|
||||
"symfony/polyfill-php80": "^1.24",
|
||||
"symfony/polyfill-php81": "^1.24",
|
||||
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
|
||||
"psr/http-message": "^1.1 || ^2.0",
|
||||
"psr/http-server-middleware": "^1.0",
|
||||
"kodus/psr7-server": "*",
|
||||
"nyholm/psr7": "^1.3",
|
||||
"twig/twig": "~1.44",
|
||||
"psr/container": "^1.1 || ^2.0",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0",
|
||||
"symfony/cache": "^6.4 || ^7.0",
|
||||
"symfony/yaml": "^6.4 || ^7.0",
|
||||
"symfony/console": "^6.4 || ^7.0",
|
||||
"symfony/event-dispatcher": "^6.4 || ^7.0",
|
||||
"symfony/var-exporter": "^6.4 || ^7.0",
|
||||
"symfony/var-dumper": "^6.4 || ^7.0",
|
||||
"symfony/process": "^6.4 || ^7.0",
|
||||
"symfony/http-client": "^6.4 || ^7.0",
|
||||
"twig/twig": "3.x-dev",
|
||||
"monolog/monolog": "^3.0",
|
||||
"doctrine/collections": "^2.2",
|
||||
"nyholm/psr7-server": "^1.1",
|
||||
"nyholm/psr7": "^1.8",
|
||||
"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.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",
|
||||
"rockettheme/toolbox": "v2.x-dev",
|
||||
"composer/ca-bundle": "^1.5",
|
||||
"composer/semver": "^3.4",
|
||||
"dragonmantank/cron-expression": "^3.3",
|
||||
"willdurand/negotiation": "^3.1",
|
||||
"rhukster/dom-sanitizer": "^1.0",
|
||||
"tubalmartin/cssmin": "^4.1",
|
||||
"tedivm/jshrink": "^1.7",
|
||||
"donatj/phpuseragentparser": "~1.9",
|
||||
"guzzlehttp/psr7": "^2.7",
|
||||
"filp/whoops": "~2.16",
|
||||
"itsgoingd/clockwork": "^5.3",
|
||||
"php-debugbar/php-debugbar": "~2.1",
|
||||
"getgrav/image": "^4.0",
|
||||
"getgrav/cache": "^2.0",
|
||||
"antoligy/dom-string-iterators": "^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": "^3.0",
|
||||
"itsgoingd/clockwork": "^5.0",
|
||||
"enshrined/svg-sanitize": "~0.13",
|
||||
"symfony/http-client": "^4.4",
|
||||
"composer/semver": "^1.4"
|
||||
"league/climate": "^3.10",
|
||||
"multiavatar/multiavatar-php": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^4.1",
|
||||
"phpstan/phpstan": "^0.12",
|
||||
"phpstan/phpstan-deprecation-rules": "^0.12",
|
||||
"phpunit/php-code-coverage": "~9.2",
|
||||
"victorjonsson/markdowndocs": "dev-master",
|
||||
"codeception/module-asserts": "^1.3",
|
||||
"codeception/module-phpbrowser": "^1.0"
|
||||
"codeception/codeception": "^5.1",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0",
|
||||
"phpunit/php-code-coverage": "^11.0",
|
||||
"getgrav/markdowndocs": "^2.0",
|
||||
"codeception/module-asserts": "*",
|
||||
"codeception/module-phpbrowser": "*",
|
||||
"rector/rector": "^2.1"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/rockettheme/toolbox"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/getgrav/twig"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/getgrav/parsedown"
|
||||
}
|
||||
],
|
||||
"replace": {
|
||||
"symfony/polyfill-php72": "*",
|
||||
"symfony/polyfill-php73": "*"
|
||||
"symfony/polyfill-php73": "*",
|
||||
"symfony/polyfill-php74": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "Recommended for better performance",
|
||||
@@ -81,49 +100,49 @@
|
||||
"ext-intl": "Recommended for multi-language sites",
|
||||
"ext-memcache": "Needed to support Memcache servers",
|
||||
"ext-memcached": "Needed to support Memcached servers",
|
||||
"ext-redis": "Needed to support Redis servers"
|
||||
"ext-redis": "Needed to support Redis servers",
|
||||
"ext-exif": "Needed to use exif data from images."
|
||||
},
|
||||
"config": {
|
||||
"apcu-autoloader": true,
|
||||
"platform": {
|
||||
"php": "7.3.6"
|
||||
"php": "8.3"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
"Grav\\": "system/src/Grav",
|
||||
"Doctrine\\": "system/src/Doctrine",
|
||||
"RocketTheme\\": "system/src/RocketTheme",
|
||||
"Twig\\": "system/src/Twig",
|
||||
"Pimple\\": "system/src/Pimple"
|
||||
},
|
||||
"files": [
|
||||
"system/defines.php"
|
||||
"system/defines.php",
|
||||
"system/src/DOMLettersIterator.php",
|
||||
"system/src/DOMWordsIterator.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"PHPStan\\": "tests/phpstan/classes"
|
||||
}
|
||||
},
|
||||
"archive": {
|
||||
"exclude": [
|
||||
"VERSION"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"api-17": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.17.md",
|
||||
"api-18": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.18.md",
|
||||
"post-create-project-cmd": "bin/grav install",
|
||||
"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",
|
||||
"rector": "vendor/bin/rector",
|
||||
"rector:php-compat": "@php vendor/bin/rector process --config=system/rector.php --ansi --no-progress-bar",
|
||||
"phpstan": "vendor/bin/phpstan analyse -l 2 -c ./tests/phpstan/phpstan.neon --memory-limit=720M system/src",
|
||||
"phpstan-framework": "vendor/bin/phpstan analyse -l 6 -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"
|
||||
"test": "php -d register_argc_argv=On vendor/bin/codecept run unit",
|
||||
"test-windows": "php -d register_argc_argv=On vendor\\bin\\codecept run unit"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
|
||||
5205
composer.lock
generated
5205
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
/* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. */
|
||||
/* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved. */
|
||||
|
||||
109
index.php
109
index.php
@@ -3,36 +3,63 @@
|
||||
/**
|
||||
* @package Grav.Core
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2024 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav;
|
||||
|
||||
\define('GRAV_REQUEST_TIME', microtime(true));
|
||||
\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));
|
||||
}
|
||||
\define('GRAV_PHP_MIN', '8.3.0');
|
||||
|
||||
if (PHP_SAPI === 'cli-server') {
|
||||
$symfony_server = stripos(getenv('_'), 'symfony') !== false || stripos($_SERVER['SERVER_SOFTWARE'], 'symfony
|
||||
') !== false;
|
||||
$symfony_server = stripos(getenv('_'), 'symfony') !== false || stripos($_SERVER['SERVER_SOFTWARE'] ?? '', 'symfony') !== false || stripos($_ENV['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());
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
if (!isset($_SERVER['argv']) && !ini_get('register_argc_argv')) {
|
||||
$queryString = $_SERVER['QUERY_STRING'] ?? '';
|
||||
$_SERVER['argv'] = $queryString !== '' ? [$queryString] : [];
|
||||
$_SERVER['argc'] = $queryString !== '' ? 1 : 0;
|
||||
}
|
||||
|
||||
// Set internal encoding.
|
||||
if (!\extension_loaded('mbstring')) {
|
||||
die("'mbstring' extension is not loaded. This is required for Grav to run correctly");
|
||||
$requestUri = $_SERVER['REQUEST_URI'] ?? '';
|
||||
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
|
||||
$path = parse_url($requestUri, PHP_URL_PATH) ?? '/';
|
||||
$path = str_replace('\\', '/', $path);
|
||||
|
||||
$scriptDir = str_replace('\\', '/', dirname($scriptName));
|
||||
if ($scriptDir && $scriptDir !== '/' && $scriptDir !== '.') {
|
||||
if (strpos($path, $scriptDir) === 0) {
|
||||
$path = substr($path, strlen($scriptDir));
|
||||
$path = $path === '' ? '/' : $path;
|
||||
}
|
||||
}
|
||||
|
||||
if ($path === '/___safe-upgrade-status') {
|
||||
$statusEndpoint = __DIR__ . '/user/plugins/admin/safe-upgrade-status.php';
|
||||
if (!\defined('GRAV_ROOT')) {
|
||||
// Minimal bootstrap so the status script has the expected constants.
|
||||
require_once __DIR__ . '/system/defines.php';
|
||||
}
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
if (is_file($statusEndpoint)) {
|
||||
require $statusEndpoint;
|
||||
} else {
|
||||
http_response_code(404);
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'Safe upgrade status endpoint unavailable.',
|
||||
]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@ini_set('default_charset', 'UTF-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
// Ensure vendor libraries exist
|
||||
$autoload = __DIR__ . '/vendor/autoload.php';
|
||||
@@ -43,23 +70,57 @@ if (!is_file($autoload)) {
|
||||
// Register the auto-loader.
|
||||
$loader = require $autoload;
|
||||
|
||||
// Set timezone to default, falls back to system if php.ini not set
|
||||
date_default_timezone_set(@date_default_timezone_get());
|
||||
|
||||
// Set internal encoding.
|
||||
@ini_set('default_charset', 'UTF-8');
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
// Use getcwd() for paths to support symlinked index.php (GRAV_ROOT uses getcwd())
|
||||
$gravRoot = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', getenv('GRAV_ROOT') ?: getcwd()), '/');
|
||||
|
||||
// Helper function to check if recovery mode is enabled in config (updates.recovery_mode)
|
||||
$isRecoveryEnabled = static function () use ($gravRoot) {
|
||||
$userConfig = $gravRoot . '/user/config/system.yaml';
|
||||
if (!is_file($userConfig)) {
|
||||
return true; // Default enabled
|
||||
}
|
||||
$content = file_get_contents($userConfig);
|
||||
if ($content === false) {
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/^\s*updates:\s*\n(?:\s+\w+:.*\n)*?\s+recovery_mode:\s*(true|false|1|0)\s*$/m', $content, $matches)) {
|
||||
return in_array(strtolower($matches[1]), ['true', '1'], true);
|
||||
}
|
||||
return true; // Default enabled
|
||||
};
|
||||
|
||||
$recoveryFlag = $gravRoot . '/user/data/recovery.flag';
|
||||
if (PHP_SAPI !== 'cli' && is_file($recoveryFlag) && $isRecoveryEnabled()) {
|
||||
if (!defined('GRAV_ROOT')) {
|
||||
define('GRAV_ROOT', $gravRoot);
|
||||
}
|
||||
require __DIR__ . '/system/recovery.php';
|
||||
return 0;
|
||||
}
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
// Get the Grav instance
|
||||
$grav = Grav::instance(
|
||||
array(
|
||||
'loader' => $loader
|
||||
)
|
||||
);
|
||||
$grav = Grav::instance(['loader' => $loader]);
|
||||
|
||||
// Process the page
|
||||
try {
|
||||
$grav->process();
|
||||
} catch (\Error $e) {
|
||||
$grav->fireEvent('onFatalException', new Event(array('exception' => $e)));
|
||||
throw $e;
|
||||
} catch (\Exception $e) {
|
||||
$grav->fireEvent('onFatalException', new Event(array('exception' => $e)));
|
||||
} catch (\Error|\Exception $e) {
|
||||
$grav->fireEvent('onFatalException', new Event(['exception' => $e]));
|
||||
|
||||
if (PHP_SAPI !== 'cli' && is_file($recoveryFlag) && $isRecoveryEnabled()) {
|
||||
require __DIR__ . '/system/recovery.php';
|
||||
return 0;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
/* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. */
|
||||
/* @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved. */
|
||||
|
||||
505
needs_fixing.txt
Normal file
505
needs_fixing.txt
Normal file
@@ -0,0 +1,505 @@
|
||||
------ ----------------------------------------------------
|
||||
Line src/Grav/Common/GPM/Response.php
|
||||
------ ----------------------------------------------------
|
||||
3 Class Grav\Common\GPM\Response not found.
|
||||
🪪 class.notFound
|
||||
💡 Learn more at
|
||||
https://phpstan.org/user-guide/discovering-symbols
|
||||
------ ----------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Common/Grav.php
|
||||
------ -----------------------------------------------------------
|
||||
148 No error to ignore is reported on line 148.
|
||||
681 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Common/Page/Collection.php
|
||||
------ -----------------------------------------------------------
|
||||
112 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
209 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Common/Processors/InitializeProcessor.php
|
||||
------ -----------------------------------------------------------
|
||||
58 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -------------------------------------------------------
|
||||
Line src/Grav/Common/Scheduler/Job.php
|
||||
------ -------------------------------------------------------
|
||||
574 Call to static method sendEmail() on an unknown class
|
||||
Grav\Plugin\Email\Utils.
|
||||
🪪 class.notFound
|
||||
💡 Learn more at
|
||||
https://phpstan.org/user-guide/discovering-symbols
|
||||
------ -------------------------------------------------------
|
||||
|
||||
------ ----------------------------------------------------------
|
||||
Line src/Grav/Common/Scheduler/SchedulerController.php
|
||||
------ ----------------------------------------------------------
|
||||
41 Class Grav\Common\Scheduler\ModernScheduler not found.
|
||||
🪪 class.notFound
|
||||
💡 Learn more at
|
||||
https://phpstan.org/user-guide/discovering-symbols
|
||||
45 Instantiated class Grav\Common\Scheduler\ModernScheduler
|
||||
not found.
|
||||
🪪 class.notFound
|
||||
💡 Learn more at
|
||||
https://phpstan.org/user-guide/discovering-symbols
|
||||
------ ----------------------------------------------------------
|
||||
|
||||
------ --------------------------------------------------------
|
||||
Line src/Grav/Common/Service/SchedulerServiceProvider.php
|
||||
------ --------------------------------------------------------
|
||||
55 Instantiated class Grav\Common\Scheduler\JobWorker not
|
||||
found.
|
||||
🪪 class.notFound
|
||||
💡 Learn more at
|
||||
https://phpstan.org/user-guide/discovering-symbols
|
||||
------ --------------------------------------------------------
|
||||
|
||||
------ ---------------------------------------------
|
||||
Line src/Grav/Common/Session.php
|
||||
------ ---------------------------------------------
|
||||
132 No error to ignore is reported on line 132.
|
||||
137 No error to ignore is reported on line 137.
|
||||
------ ---------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Common/Uri.php
|
||||
------ -----------------------------------------------------------
|
||||
1131 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ --------------------------------------------------------
|
||||
Line src/Grav/Console/Application/Application.php
|
||||
------ --------------------------------------------------------
|
||||
125 Return type mixed of method
|
||||
Grav\Console\Application\Application::configureIO() is
|
||||
not covariant with return type void of method
|
||||
Symfony\Component\Console\Application::configureIO().
|
||||
------ --------------------------------------------------------
|
||||
|
||||
------ ---------------------------------------------------------
|
||||
Line src/Grav/Console/ConsoleCommand.php
|
||||
------ ---------------------------------------------------------
|
||||
29 Return type mixed of method
|
||||
Grav\Console\ConsoleCommand::execute() is not covariant
|
||||
with return type int of method
|
||||
Symfony\Component\Console\Command\Command::execute().
|
||||
------ ---------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Console/ConsoleTrait.php (in context of class
|
||||
Grav\Console\ConsoleCommand)
|
||||
------ -----------------------------------------------------------
|
||||
89 Method Grav\Console\ConsoleCommand::addOption() overrides
|
||||
method
|
||||
Symfony\Component\Console\Command\Command::addOption()
|
||||
but misses parameter #6 $suggestedValues.
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ --------------------------------------------------------
|
||||
Line src/Grav/Console/ConsoleTrait.php (in context of class
|
||||
Grav\Console\GpmCommand)
|
||||
------ --------------------------------------------------------
|
||||
89 Method Grav\Console\GpmCommand::addOption() overrides
|
||||
method
|
||||
Symfony\Component\Console\Command\Command::addOption()
|
||||
but misses parameter #6 $suggestedValues.
|
||||
------ --------------------------------------------------------
|
||||
|
||||
------ --------------------------------------------------------
|
||||
Line src/Grav/Console/ConsoleTrait.php (in context of class
|
||||
Grav\Console\GravCommand)
|
||||
------ --------------------------------------------------------
|
||||
89 Method Grav\Console\GravCommand::addOption() overrides
|
||||
method
|
||||
Symfony\Component\Console\Command\Command::addOption()
|
||||
but misses parameter #6 $suggestedValues.
|
||||
------ --------------------------------------------------------
|
||||
|
||||
------ ----------------------------------------------------------
|
||||
Line src/Grav/Console/GpmCommand.php
|
||||
------ ----------------------------------------------------------
|
||||
31 Return type mixed of method
|
||||
Grav\Console\GpmCommand::execute() is not covariant with
|
||||
return type int of method
|
||||
Symfony\Component\Console\Command\Command::execute().
|
||||
39 No error to ignore is reported on line 39.
|
||||
------ ----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Console/GravCommand.php
|
||||
------ -----------------------------------------------------------
|
||||
29 Return type mixed of method
|
||||
Grav\Console\GravCommand::execute() is not covariant with
|
||||
return type int of method
|
||||
Symfony\Component\Console\Command\Command::execute().
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Acl/RecursiveActionIterator.php
|
||||
------ -----------------------------------------------------------
|
||||
62 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ ----------------------------------------------------------
|
||||
Line src/Grav/Framework/Cache/CacheTrait.php (in context of
|
||||
class Grav\Framework\Cache\AbstractCache)
|
||||
------ ----------------------------------------------------------
|
||||
87 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::get() is not
|
||||
covariant with return type mixed of method
|
||||
Psr\SimpleCache\CacheInterface::get().
|
||||
102 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::set() is not
|
||||
covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::set().
|
||||
117 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::delete() is not
|
||||
covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::delete().
|
||||
127 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::clear() is not
|
||||
covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::clear().
|
||||
138 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::getMultiple() is not
|
||||
covariant with return type iterable of method
|
||||
Psr\SimpleCache\CacheInterface::getMultiple().
|
||||
181 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::setMultiple() is not
|
||||
covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::setMultiple().
|
||||
214 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::deleteMultiple() is
|
||||
not covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::deleteMultiple().
|
||||
242 Return type mixed of method
|
||||
Grav\Framework\Cache\AbstractCache::has() is not
|
||||
covariant with return type bool of method
|
||||
Psr\SimpleCache\CacheInterface::has().
|
||||
------ ----------------------------------------------------------
|
||||
|
||||
------ ----------------------------------------------------------
|
||||
Line src/Grav/Framework/Collection/AbstractFileCollection.php
|
||||
------ ----------------------------------------------------------
|
||||
95 No error to ignore is reported on line 95.
|
||||
------ ----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Collection/AbstractIndexCollection.php
|
||||
------ -----------------------------------------------------------
|
||||
154 No error to ignore is reported on line 154.
|
||||
168 No error to ignore is reported on line 168.
|
||||
185 No error to ignore is reported on line 185.
|
||||
201 No error to ignore is reported on line 201.
|
||||
507 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Collection/AbstractLazyCollection.php
|
||||
------ -----------------------------------------------------------
|
||||
29 Property
|
||||
Grav\Framework\Collection\AbstractLazyCollection::$collec
|
||||
tion overriding property
|
||||
Doctrine\Common\Collections\AbstractLazyCollection<TKey o
|
||||
f (int|string),T>::$collection (Doctrine\Common\Collectio
|
||||
ns\Collection|null) should also have native type
|
||||
Doctrine\Common\Collections\Collection|null.
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Contracts/Relationships/RelationshipIn
|
||||
terface.php
|
||||
------ -----------------------------------------------------------
|
||||
80 Return type iterable of method
|
||||
Grav\Framework\Contracts\Relationships\RelationshipInterf
|
||||
ace::getIterator() is not covariant with tentative return
|
||||
type Traversable of method IteratorAggregate<string,T of
|
||||
Grav\Framework\Contracts\Object\IdentifierInterface>::get
|
||||
Iterator().
|
||||
💡 Make it covariant, or use the #[\ReturnTypeWillChange]
|
||||
attribute to temporarily suppress the error.
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Filesystem/Filesystem.php
|
||||
------ -----------------------------------------------------------
|
||||
51 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
252 No error to ignore is reported on line 252.
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ ---------------------------------------------
|
||||
Line src/Grav/Framework/Flex/FlexCollection.php
|
||||
------ ---------------------------------------------
|
||||
102 No error to ignore is reported on line 102.
|
||||
------ ---------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Flex/FlexIdentifier.php
|
||||
------ -----------------------------------------------------------
|
||||
27 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ ----------------------------------------------------------
|
||||
Line src/Grav/Framework/Flex/FlexIndex.php
|
||||
------ ----------------------------------------------------------
|
||||
109 No error to ignore is reported on line 109.
|
||||
934 Method Grav\Framework\Flex\FlexIndex::reduce() should
|
||||
return TInitial|TReturn but return statement is missing.
|
||||
🪪 return.missing
|
||||
------ ----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Form/FormFlashFile.php
|
||||
------ -----------------------------------------------------------
|
||||
62 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::getStream() is not
|
||||
covariant with return type
|
||||
Psr\Http\Message\StreamInterface of method
|
||||
Psr\Http\Message\UploadedFileInterface::getStream().
|
||||
83 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::moveTo() is not
|
||||
covariant with return type void of method
|
||||
Psr\Http\Message\UploadedFileInterface::moveTo().
|
||||
123 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::getSize() is not
|
||||
covariant with return type int|null of method
|
||||
Psr\Http\Message\UploadedFileInterface::getSize().
|
||||
131 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::getError() is not
|
||||
covariant with return type int of method
|
||||
Psr\Http\Message\UploadedFileInterface::getError().
|
||||
139 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::getClientFilename() is
|
||||
not covariant with return type string|null of method
|
||||
Psr\Http\Message\UploadedFileInterface::getClientFilename
|
||||
().
|
||||
147 Return type mixed of method
|
||||
Grav\Framework\Form\FormFlashFile::getClientMediaType()
|
||||
is not covariant with return type string|null of method
|
||||
Psr\Http\Message\UploadedFileInterface::getClientMediaTyp
|
||||
e().
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Logger/Processors/UserProcessor.php
|
||||
------ -----------------------------------------------------------
|
||||
24 Parameter #1 $record (array) of method
|
||||
Grav\Framework\Logger\Processors\UserProcessor::__invoke(
|
||||
) is not contravariant with parameter #1 $record
|
||||
(Monolog\LogRecord) of method
|
||||
Monolog\Processor\ProcessorInterface::__invoke().
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Media/MediaIdentifier.php
|
||||
------ -----------------------------------------------------------
|
||||
30 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Media/UploadedMediaObject.php
|
||||
------ -----------------------------------------------------------
|
||||
36 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Mime/MimeTypes.php
|
||||
------ -----------------------------------------------------------
|
||||
42 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ ------------------------------------------------
|
||||
Line src/Grav/Framework/Object/ObjectCollection.php
|
||||
------ ------------------------------------------------
|
||||
96 No error to ignore is reported on line 96.
|
||||
------ ------------------------------------------------
|
||||
|
||||
------ ---------------------------------------------
|
||||
Line src/Grav/Framework/Object/ObjectIndex.php
|
||||
------ ---------------------------------------------
|
||||
193 No error to ignore is reported on line 193.
|
||||
------ ---------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Psr7/Stream.php
|
||||
------ -----------------------------------------------------------
|
||||
31 Unsafe usage of new static().
|
||||
🪪 new.static
|
||||
💡 See:
|
||||
https://phpstan.org/blog/solving-phpstan-error-unsafe-usa
|
||||
ge-of-new-static
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Psr7/Traits/ServerRequestDecoratorTrai
|
||||
t.php (in context of class
|
||||
Grav\Framework\Psr7\ServerRequest)
|
||||
------ -----------------------------------------------------------
|
||||
51 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::getAttributes() is not
|
||||
covariant with return type array of method
|
||||
Psr\Http\Message\ServerRequestInterface::getAttributes().
|
||||
60 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::getCookieParams() is
|
||||
not covariant with return type array of method
|
||||
Psr\Http\Message\ServerRequestInterface::getCookieParams(
|
||||
).
|
||||
76 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::getQueryParams() is
|
||||
not covariant with return type array of method
|
||||
Psr\Http\Message\ServerRequestInterface::getQueryParams()
|
||||
.
|
||||
84 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::getServerParams() is
|
||||
not covariant with return type array of method
|
||||
Psr\Http\Message\ServerRequestInterface::getServerParams(
|
||||
).
|
||||
92 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::getUploadedFiles() is
|
||||
not covariant with return type array of method
|
||||
Psr\Http\Message\ServerRequestInterface::getUploadedFiles
|
||||
().
|
||||
100 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withAttribute() is not
|
||||
covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withAttribute().
|
||||
125 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withoutAttribute() is
|
||||
not covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withoutAttribute
|
||||
().
|
||||
136 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withCookieParams() is
|
||||
not covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withCookieParams
|
||||
().
|
||||
147 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withParsedBody() is
|
||||
not covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withParsedBody()
|
||||
.
|
||||
158 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withQueryParams() is
|
||||
not covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withQueryParams(
|
||||
).
|
||||
169 Return type mixed of method
|
||||
Grav\Framework\Psr7\ServerRequest::withUploadedFiles() is
|
||||
not covariant with return type
|
||||
Psr\Http\Message\ServerRequestInterface of method
|
||||
Psr\Http\Message\ServerRequestInterface::withUploadedFile
|
||||
s().
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Grav/Framework/Relationships/Relationships.php
|
||||
------ -----------------------------------------------------------
|
||||
107 Return type mixed of method
|
||||
Grav\Framework\Relationships\Relationships::offsetSet()
|
||||
is not covariant with tentative return type void of
|
||||
method
|
||||
ArrayAccess<string,Grav\Framework\Contracts\Relationships
|
||||
\RelationshipInterface<T of Grav\Framework\Contracts\Obje
|
||||
ct\IdentifierInterface, P of
|
||||
Grav\Framework\Contracts\Object\IdentifierInterface>>::of
|
||||
fsetSet().
|
||||
💡 Make it covariant, or use the #[\ReturnTypeWillChange]
|
||||
attribute to temporarily suppress the error.
|
||||
116 Return type mixed of method
|
||||
Grav\Framework\Relationships\Relationships::offsetUnset()
|
||||
is not covariant with tentative return type void of
|
||||
method
|
||||
ArrayAccess<string,Grav\Framework\Contracts\Relationships
|
||||
\RelationshipInterface<T of Grav\Framework\Contracts\Obje
|
||||
ct\IdentifierInterface, P of
|
||||
Grav\Framework\Contracts\Object\IdentifierInterface>>::of
|
||||
fsetUnset().
|
||||
💡 Make it covariant, or use the #[\ReturnTypeWillChange]
|
||||
attribute to temporarily suppress the error.
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
------ -----------------------------------------------------------
|
||||
Line src/Twig/DeferredExtension/DeferredNodeVisitorCompat.php
|
||||
------ -----------------------------------------------------------
|
||||
30 Parameter #1 $node (Twig_NodeInterface) of method
|
||||
Twig\DeferredExtension\DeferredNodeVisitorCompat::enterNo
|
||||
de() is not contravariant with parameter #1 $node
|
||||
(Twig\Node\Node) of method
|
||||
Twig\NodeVisitor\NodeVisitorInterface::enterNode().
|
||||
30 Parameter $node of method
|
||||
Twig\DeferredExtension\DeferredNodeVisitorCompat::enterNo
|
||||
de() has invalid type Twig_NodeInterface.
|
||||
🪪 class.notFound
|
||||
46 Parameter #1 $node (Twig_NodeInterface) of method
|
||||
Twig\DeferredExtension\DeferredNodeVisitorCompat::leaveNo
|
||||
de() is not contravariant with parameter #1 $node
|
||||
(Twig\Node\Node) of method
|
||||
Twig\NodeVisitor\NodeVisitorInterface::leaveNode().
|
||||
46 Parameter $node of method
|
||||
Twig\DeferredExtension\DeferredNodeVisitorCompat::leaveNo
|
||||
de() has invalid type Twig_NodeInterface.
|
||||
🪪 class.notFound
|
||||
------ -----------------------------------------------------------
|
||||
|
||||
[ERROR] Found 74 errors
|
||||
|
||||
11
robots.txt
11
robots.txt
@@ -1,16 +1,21 @@
|
||||
User-agent: *
|
||||
Disallow: /.github/
|
||||
Disallow: /.phan/
|
||||
Disallow: /assets/
|
||||
Disallow: /backup/
|
||||
Disallow: /bin/
|
||||
Disallow: /cache/
|
||||
Disallow: /grav/
|
||||
Disallow: /logs/
|
||||
Disallow: /system/
|
||||
Disallow: /vendor/
|
||||
Disallow: /tests/
|
||||
Disallow: /tmp/
|
||||
Disallow: /user/
|
||||
Disallow: /vendor/
|
||||
Disallow: /webserver-configs/
|
||||
Allow: /user/pages/
|
||||
Allow: /user/themes/
|
||||
Allow: /user/images/
|
||||
Allow: /
|
||||
Allow: *.css$
|
||||
Allow: *.js$
|
||||
Allow: /system/*.js$
|
||||
Allow: /system/*.js$
|
||||
|
||||
@@ -1,2 +1,61 @@
|
||||
/** 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==)}
|
||||
.clockwork-badge {
|
||||
position: fixed;
|
||||
z-index: 1000; /* Increased z-index for better visibility */
|
||||
bottom: 0; /* Added some spacing from the bottom */
|
||||
left: 0; /* Added some spacing from the left */
|
||||
padding: 5px;
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom: 0;
|
||||
border-left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 0 4px 0 0; /* Rounded top corners */
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.clockwork-badge:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.clockwork-badge i {
|
||||
display: block;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
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==);
|
||||
}
|
||||
|
||||
.clockwork-badge .tooltip {
|
||||
display: none; /* Hidden by default */
|
||||
position: absolute;
|
||||
bottom: 35px; /* Position above the badge */
|
||||
left: 0;
|
||||
width: 450px;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||||
z-index: 1001; /* Ensure it appears above other elements */
|
||||
}
|
||||
|
||||
.clockwork-badge:hover .tooltip {
|
||||
display: block; /* Show tooltip on hover */
|
||||
}
|
||||
|
||||
.clockwork-badge .tooltip a {
|
||||
color: #007BFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.clockwork-badge .tooltip a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,37 @@
|
||||
/** 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)});
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Directly select the script tag by its id
|
||||
var currentScript = document.getElementById('clockwork-script');
|
||||
|
||||
if (!currentScript) {
|
||||
console.error("Clockwork Debugger: Script tag with id 'clockwork-script' not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
var route = currentScript.getAttribute('data-route') || '/clockwork'; // Default route if not specified
|
||||
|
||||
// Debugging: Log the route to verify
|
||||
console.log("Clockwork Debugger Route:", route);
|
||||
|
||||
// Create the badge container
|
||||
var badge = document.createElement("div");
|
||||
badge.className = "clockwork-badge";
|
||||
badge.setAttribute('aria-label', 'Clockwork Debugger Enabled');
|
||||
badge.setAttribute('role', 'button');
|
||||
|
||||
// Create the icon element
|
||||
var icon = document.createElement("i");
|
||||
badge.appendChild(icon);
|
||||
|
||||
// Create the tooltip element
|
||||
var tooltip = document.createElement("div");
|
||||
tooltip.className = "tooltip";
|
||||
tooltip.innerHTML = `
|
||||
<b>Grav Clockwork Debugger Enabled.</b><br>
|
||||
Install the <b>Clockwork Browser extension</b> (Chrome or Firefox) or use the <b>"Clockwork Web"</b> Grav plugin to <a href="${route}" target="_blank">View Debug Info 🔗</a>.
|
||||
`;
|
||||
badge.appendChild(tooltip);
|
||||
|
||||
// Append the badge to the body
|
||||
document.body.appendChild(badge);
|
||||
});
|
||||
@@ -1,70 +1,5 @@
|
||||
div.phpdebugbar {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
div.phpdebugbar a.phpdebugbar-restore-btn::after {
|
||||
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==) !important;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
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
@@ -122,4 +122,13 @@ form:
|
||||
default: '* 3 * * *'
|
||||
validate:
|
||||
required: true
|
||||
.schedule_environment:
|
||||
type: select
|
||||
label: PLUGIN_ADMIN.BACKUPS_PROFILE_ENVIRONMENT
|
||||
help: PLUGIN_ADMIN.BACKUPS_PROFILE_ENVIRONMENT_HELP
|
||||
default: ''
|
||||
options:
|
||||
'': 'Default (cli)'
|
||||
localhost: 'Localhost'
|
||||
cli: 'CLI'
|
||||
|
||||
|
||||
@@ -4,73 +4,788 @@ form:
|
||||
validation: loose
|
||||
|
||||
fields:
|
||||
scheduler_tabs:
|
||||
type: tabs
|
||||
active: 1
|
||||
|
||||
status_title:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SCHEDULER_STATUS
|
||||
underline: true
|
||||
fields:
|
||||
status_tab:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.SCHEDULER_STATUS
|
||||
|
||||
status:
|
||||
type: cronstatus
|
||||
validate:
|
||||
type: commalist
|
||||
fields:
|
||||
status_title:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SCHEDULER_STATUS
|
||||
underline: true
|
||||
|
||||
jobs_title:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SCHEDULER_JOBS
|
||||
underline: true
|
||||
status:
|
||||
type: cronstatus
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
webhook_status_override:
|
||||
type: display
|
||||
label:
|
||||
content: |
|
||||
<script>
|
||||
(function() {
|
||||
function updateSchedulerStatus() {
|
||||
// Find all notice bars
|
||||
var notices = document.querySelectorAll('.notice');
|
||||
var webhookStatusChecked = false;
|
||||
|
||||
// Check for modern scheduler and webhook settings
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.webhook_enabled) {
|
||||
notices.forEach(function(notice) {
|
||||
if (notice.textContent.includes('Not Enabled for user:')) {
|
||||
// This is the cron status notice - replace it
|
||||
notice.className = 'notice info';
|
||||
notice.innerHTML = '<i class="fa fa-fw fa-check-circle"></i> <strong>Webhook Active</strong> - Scheduler can be triggered via webhook. Cron is not configured.';
|
||||
}
|
||||
});
|
||||
|
||||
// Also update the main status if it exists
|
||||
var statusDiv = document.querySelector('.cronstatus-status');
|
||||
if (statusDiv && statusDiv.textContent.includes('Not Enabled')) {
|
||||
statusDiv.className = 'cronstatus-status success';
|
||||
statusDiv.innerHTML = '<i class="fa fa-fw fa-check"></i> Webhook Ready';
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('Webhook status check failed:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// Run on page load
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', updateSchedulerStatus);
|
||||
} else {
|
||||
updateSchedulerStatus();
|
||||
}
|
||||
|
||||
// Also run after a short delay to catch any late-rendered elements
|
||||
setTimeout(updateSchedulerStatus, 500);
|
||||
})();
|
||||
</script>
|
||||
markdown: false
|
||||
|
||||
status_enhanced:
|
||||
type: display
|
||||
label:
|
||||
content: |
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Check if webhook is enabled
|
||||
var webhookEnabled = document.querySelector('[name="data[scheduler][modern][webhook][enabled]"]:checked');
|
||||
var statusDiv = document.querySelector('.cronstatus-status');
|
||||
|
||||
// Also find the parent notice bar
|
||||
var noticeBar = document.querySelector('.notice.alert');
|
||||
|
||||
if (statusDiv) {
|
||||
var currentStatus = statusDiv.textContent || statusDiv.innerText;
|
||||
var cronReady = currentStatus.includes('Ready');
|
||||
var cronNotEnabled = currentStatus.includes('Not Enabled');
|
||||
|
||||
// Check if scheduler-webhook plugin exists
|
||||
var webhookPluginInstalled = false;
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
webhookPluginInstalled = true;
|
||||
updateStatusDisplay(data);
|
||||
})
|
||||
.catch(error => {
|
||||
updateStatusDisplay(null);
|
||||
});
|
||||
|
||||
function updateStatusDisplay(healthData) {
|
||||
var isWebhookEnabled = webhookEnabled && webhookEnabled.value == '1';
|
||||
var isWebhookReady = webhookPluginInstalled && isWebhookEnabled && healthData && healthData.webhook_enabled;
|
||||
|
||||
// Update the main status text
|
||||
var mainStatusText = '';
|
||||
var mainStatusClass = '';
|
||||
|
||||
if (cronReady && isWebhookReady) {
|
||||
mainStatusText = 'Cron and Webhook Ready';
|
||||
mainStatusClass = 'success';
|
||||
} else if (cronReady) {
|
||||
mainStatusText = 'Cron Ready';
|
||||
mainStatusClass = 'success';
|
||||
} else if (isWebhookReady) {
|
||||
mainStatusText = 'Webhook Ready (No Cron)';
|
||||
mainStatusClass = 'success'; // Changed from warning to success
|
||||
} else if (cronNotEnabled && !isWebhookReady) {
|
||||
mainStatusText = 'Not Configured';
|
||||
mainStatusClass = 'error';
|
||||
} else {
|
||||
mainStatusText = 'Configuration Pending';
|
||||
mainStatusClass = 'warning';
|
||||
}
|
||||
|
||||
// Update the notice bar if webhooks are ready
|
||||
if (noticeBar && isWebhookReady) {
|
||||
// Change from error (red) to success (green) or info (blue)
|
||||
noticeBar.classList.remove('alert');
|
||||
noticeBar.classList.add('info');
|
||||
|
||||
var noticeIcon = noticeBar.querySelector('i.fa');
|
||||
if (noticeIcon) {
|
||||
noticeIcon.classList.remove('fa-times-circle');
|
||||
noticeIcon.classList.add('fa-check-circle');
|
||||
}
|
||||
|
||||
var noticeText = noticeBar.querySelector('strong') || noticeBar;
|
||||
var username = noticeText.textContent.match(/user:\s*(\w+)/);
|
||||
if (username) {
|
||||
noticeText.innerHTML = 'Webhook Ready for user: <b>' + username[1] + '</b> (Cron not configured)';
|
||||
} else {
|
||||
noticeText.innerHTML = mainStatusText;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the main status div
|
||||
if (statusDiv) {
|
||||
statusDiv.innerHTML = '<i class="fa fa-fw fa-' +
|
||||
(mainStatusClass === 'success' ? 'check' : mainStatusClass === 'warning' ? 'exclamation' : 'times') +
|
||||
'"></i> ' + mainStatusText;
|
||||
statusDiv.className = 'cronstatus-status ' + mainStatusClass;
|
||||
}
|
||||
|
||||
// Update install instructions button/content
|
||||
var installButton = document.querySelector('.cronstatus-install-button');
|
||||
var installDiv = document.querySelector('.cronstatus-install');
|
||||
|
||||
if (installDiv) {
|
||||
var installHtml = '<div class="alert alert-info">';
|
||||
installHtml += '<h4>Setup Instructions:</h4>';
|
||||
|
||||
var hasInstructions = false;
|
||||
|
||||
// Cron setup
|
||||
if (!cronReady) {
|
||||
installHtml += '<p><strong>Option 1: Traditional Cron</strong><br>';
|
||||
installHtml += 'Run: <code>bin/grav scheduler --install</code><br>';
|
||||
installHtml += 'This will add a cron job that runs every minute.</p>';
|
||||
hasInstructions = true;
|
||||
}
|
||||
|
||||
// Webhook setup
|
||||
if (!webhookPluginInstalled) {
|
||||
installHtml += '<p><strong>Option 2: Webhook Support</strong><br>';
|
||||
installHtml += '1. Install plugin: <code>bin/gpm install scheduler-webhook</code><br>';
|
||||
installHtml += '2. Configure webhook token in Advanced Features tab<br>';
|
||||
installHtml += '3. Use webhook URL in your CI/CD or cloud scheduler</p>';
|
||||
hasInstructions = true;
|
||||
} else if (!isWebhookEnabled) {
|
||||
installHtml += '<p><strong>Webhook Plugin Installed</strong><br>';
|
||||
installHtml += 'Enable webhooks in Advanced Features tab and set a secure token.</p>';
|
||||
hasInstructions = true;
|
||||
} else if (isWebhookReady) {
|
||||
installHtml += '<p><strong>✅ Webhook is Active!</strong><br>';
|
||||
installHtml += 'Trigger URL: <code>' + window.location.origin + '/grav-editor-pro/scheduler/webhook</code><br>';
|
||||
installHtml += 'Use with Authorization header: <code>Bearer YOUR_TOKEN</code></p>';
|
||||
|
||||
if (!cronReady) {
|
||||
installHtml += '<p class="text-muted"><small>Note: No cron job configured. Scheduler runs only via webhook triggers.</small></p>';
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasInstructions && cronReady) {
|
||||
installHtml += '<p><strong>✅ Cron is configured and ready!</strong><br>';
|
||||
installHtml += 'The scheduler runs automatically every minute via system cron.</p>';
|
||||
|
||||
}
|
||||
|
||||
installHtml += '</div>';
|
||||
installDiv.innerHTML = installHtml;
|
||||
|
||||
// Update button text based on status
|
||||
if (installButton) {
|
||||
if (cronReady && isWebhookReady) {
|
||||
installButton.innerHTML = '<i class="fa fa-info-circle"></i> Configuration Details';
|
||||
} else if (cronReady || isWebhookReady) {
|
||||
installButton.innerHTML = '<i class="fa fa-plus-circle"></i> Add More Triggers';
|
||||
} else {
|
||||
installButton.innerHTML = '<i class="fa fa-exclamation-triangle"></i> Install Instructions';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
custom_jobs:
|
||||
type: list
|
||||
style: vertical
|
||||
label:
|
||||
classes: cron-job-list compact
|
||||
key: id
|
||||
fields:
|
||||
.id:
|
||||
type: key
|
||||
label: ID
|
||||
placeholder: 'process-name'
|
||||
validate:
|
||||
required: true
|
||||
pattern: '[a-zа-я0-9_\-]+'
|
||||
max: 20
|
||||
message: 'ID must be lowercase with dashes/underscores only and less than 20 characters'
|
||||
.command:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.COMMAND
|
||||
placeholder: 'ls'
|
||||
validate:
|
||||
required: true
|
||||
.args:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.EXTRA_ARGUMENTS
|
||||
placeholder: '-lah'
|
||||
.at:
|
||||
type: cron
|
||||
label: PLUGIN_ADMIN.SCHEDULER_RUNAT
|
||||
help: PLUGIN_ADMIN.SCHEDULER_RUNAT_HELP
|
||||
placeholder: '* * * * *'
|
||||
validate:
|
||||
required: true
|
||||
.output:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.SCHEDULER_OUTPUT
|
||||
help: PLUGIN_ADMIN.SCHEDULER_OUTPUT_HELP
|
||||
placeholder: 'logs/ls-cron.out'
|
||||
.output_mode:
|
||||
type: select
|
||||
label: PLUGIN_ADMIN.SCHEDULER_OUTPUT_TYPE
|
||||
help: PLUGIN_ADMIN.SCHEDULER_OUTPUT_TYPE_HELP
|
||||
default: append
|
||||
options:
|
||||
append: Append
|
||||
overwrite: Overwrite
|
||||
.email:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.SCHEDULER_EMAIL
|
||||
help: PLUGIN_ADMIN.SCHEDULER_EMAIL_HELP
|
||||
placeholder: 'notifications@yoursite.com'
|
||||
modern_health:
|
||||
type: display
|
||||
label: Health Status
|
||||
content: |
|
||||
<div id="scheduler-health-status">
|
||||
<div class="text-muted">Checking health...</div>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
function loadHealthStatus() {
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
var statusEl = document.getElementById('scheduler-health-status');
|
||||
if (!statusEl) return;
|
||||
|
||||
// Modern card-based layout
|
||||
var statusColor = '#6c757d';
|
||||
var statusLabel = data.status || 'unknown';
|
||||
if (data.status === 'healthy') statusColor = '#28a745';
|
||||
else if (data.status === 'warning') statusColor = '#ffc107';
|
||||
else if (data.status === 'critical') statusColor = '#dc3545';
|
||||
|
||||
var html = '<div style="display: flex; flex-direction: column; gap: 1rem;">';
|
||||
|
||||
// Status card
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: linear-gradient(135deg, #f8f9fa 0%, #fff 100%); border-radius: 6px; border: 1px solid #e9ecef; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">';
|
||||
html += '<span style="font-weight: 500; color: #495057;">Status:</span>';
|
||||
html += '<span style="background: ' + statusColor + '; color: white; padding: 0.375rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 4px; text-transform: uppercase; letter-spacing: 0.025em;">' + statusLabel + '</span>';
|
||||
html += '</div>';
|
||||
|
||||
// Info grid
|
||||
html += '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem;">';
|
||||
|
||||
// Last run card
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<div style="color: #6c757d; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.25rem;">Last Run</div>';
|
||||
if (data.last_run) {
|
||||
var age = data.last_run_age;
|
||||
var ageText = 'just now';
|
||||
if (age > 86400) {
|
||||
ageText = Math.floor(age / 86400) + ' day(s) ago';
|
||||
} else if (age > 3600) {
|
||||
ageText = Math.floor(age / 3600) + ' hour(s) ago';
|
||||
} else if (age > 60) {
|
||||
ageText = Math.floor(age / 60) + ' minute(s) ago';
|
||||
} else if (age > 0) {
|
||||
ageText = age + ' second(s) ago';
|
||||
}
|
||||
html += '<div style="font-size: 1rem; color: #212529; font-weight: 500;">' + ageText + '</div>';
|
||||
} else {
|
||||
html += '<div style="font-size: 1rem; color: #6c757d;">Never</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
// Jobs count card
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<div style="color: #6c757d; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.25rem;">Scheduled Jobs</div>';
|
||||
html += '<div style="font-size: 1rem; color: #212529; font-weight: 500;">' + (data.scheduled_jobs || 0) + '</div>';
|
||||
html += '</div>';
|
||||
|
||||
html += '</div>'; // Close grid
|
||||
|
||||
// Additional info if available
|
||||
if (data.modern_features && data.queue_size !== undefined) {
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<span style="color: #6c757d; font-size: 0.875rem;">Queue Size: </span>';
|
||||
html += '<span style="font-weight: 500;">' + data.queue_size + '</span>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// Failed jobs warning
|
||||
if (data.failed_jobs_24h > 0) {
|
||||
html += '<div style="background: #fff5f5; border: 1px solid #feb2b2; border-radius: 6px; padding: 0.75rem; color: #c53030;">';
|
||||
html += '<strong>⚠️ Failed Jobs (24h):</strong> ' + data.failed_jobs_24h;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>'; // Close main container
|
||||
statusEl.innerHTML = html;
|
||||
})
|
||||
.catch(error => {
|
||||
var statusEl = document.getElementById('scheduler-health-status');
|
||||
if (statusEl) {
|
||||
statusEl.innerHTML = '<div class="alert alert-warning">Unable to fetch health status. Ensure scheduler-webhook plugin is installed.</div>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Load on page ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', loadHealthStatus);
|
||||
} else {
|
||||
loadHealthStatus();
|
||||
}
|
||||
|
||||
// Refresh every 30 seconds
|
||||
setInterval(loadHealthStatus, 30000);
|
||||
})();
|
||||
</script>
|
||||
markdown: false
|
||||
|
||||
trigger_methods:
|
||||
type: display
|
||||
label: Active Triggers
|
||||
content: |
|
||||
<div id="scheduler-triggers">
|
||||
<div class="text-muted">Checking triggers...</div>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
function loadTriggers() {
|
||||
// Check cron status from the main status field
|
||||
var cronReady = false;
|
||||
var statusDiv = document.querySelector('.cronstatus-status');
|
||||
if (statusDiv) {
|
||||
var statusText = statusDiv.textContent || statusDiv.innerText;
|
||||
cronReady = statusText.includes('Ready');
|
||||
}
|
||||
|
||||
// Check webhook status
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
var triggersEl = document.getElementById('scheduler-triggers');
|
||||
if (!triggersEl) return;
|
||||
|
||||
var html = '<div style="display: flex; flex-direction: column; gap: 0.5rem;">';
|
||||
|
||||
// Cron trigger card
|
||||
var cronIcon = cronReady ? '✅' : '❌';
|
||||
var cronStatus = cronReady ? 'Active' : 'Not Configured';
|
||||
var cronStatusColor = cronReady ? '#28a745' : '#6c757d';
|
||||
var cardBg = cronReady ? '#f8f9fa' : '#fff';
|
||||
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: ' + cardBg + '; border: 1px solid #e9ecef; border-radius: 4px;">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;">' + cronIcon + '</span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Cron:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: ' + cronStatusColor + '; color: white; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">' + cronStatus + '</span>';
|
||||
html += '</div>';
|
||||
|
||||
// Webhook trigger card
|
||||
if (data.webhook_enabled) {
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 4px;">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;">✅</span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Webhook:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: #28a745; color: white; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">ACTIVE</span>';
|
||||
html += '</div>';
|
||||
} else {
|
||||
// Show webhook as not configured/disabled
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: #fff; border: 1px solid #e9ecef; border-radius: 4px;">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;">⚠️</span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Webhook:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: #ffc107; color: #212529; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">DISABLED</span>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
// Add warning if no triggers active
|
||||
if (!cronReady && !data.webhook_enabled) {
|
||||
html += '<div class="alert alert-warning" style="margin-top: 1rem;"><i class="fa fa-exclamation-triangle"></i> No triggers active! Configure cron or enable webhooks.</div>';
|
||||
}
|
||||
|
||||
triggersEl.innerHTML = html;
|
||||
})
|
||||
.catch(error => {
|
||||
var triggersEl = document.getElementById('scheduler-triggers');
|
||||
if (triggersEl) {
|
||||
// Show just cron status if health endpoint not available
|
||||
var html = '<ul class="list-unstyled">';
|
||||
if (cronReady) {
|
||||
html += '<li>✅ <strong>Cron:</strong> <span class="badge badge-success">Active</span></li>';
|
||||
} else {
|
||||
html += '<li>❌ <strong>Cron:</strong> <span class="badge badge-secondary">Not Configured</span></li>';
|
||||
}
|
||||
html += '<li>⚠️ <strong>Webhook:</strong> <span class="badge badge-secondary">Plugin Not Installed</span></li>';
|
||||
html += '</ul>';
|
||||
triggersEl.innerHTML = html;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Load on page ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', loadTriggers);
|
||||
} else {
|
||||
loadTriggers();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
markdown: false
|
||||
|
||||
jobs_tab:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.SCHEDULER_JOBS
|
||||
|
||||
fields:
|
||||
jobs_title:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.SCHEDULER_JOBS
|
||||
underline: true
|
||||
|
||||
custom_jobs:
|
||||
type: list
|
||||
style: vertical
|
||||
label:
|
||||
classes: cron-job-list compact
|
||||
key: id
|
||||
fields:
|
||||
.id:
|
||||
type: key
|
||||
label: ID
|
||||
placeholder: 'process-name'
|
||||
validate:
|
||||
required: true
|
||||
pattern: '[a-zа-я0-9_\-]+'
|
||||
max: 20
|
||||
message: 'ID must be lowercase with dashes/underscores only and less than 20 characters'
|
||||
.command:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.COMMAND
|
||||
placeholder: 'ls'
|
||||
validate:
|
||||
required: true
|
||||
.args:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.EXTRA_ARGUMENTS
|
||||
placeholder: '-lah'
|
||||
.at:
|
||||
type: text
|
||||
wrapper_classes: cron-selector
|
||||
label: PLUGIN_ADMIN.SCHEDULER_RUNAT
|
||||
help: PLUGIN_ADMIN.SCHEDULER_RUNAT_HELP
|
||||
placeholder: '* * * * *'
|
||||
validate:
|
||||
required: true
|
||||
.output:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.SCHEDULER_OUTPUT
|
||||
help: PLUGIN_ADMIN.SCHEDULER_OUTPUT_HELP
|
||||
placeholder: 'logs/ls-cron.out'
|
||||
.output_mode:
|
||||
type: select
|
||||
label: PLUGIN_ADMIN.SCHEDULER_OUTPUT_TYPE
|
||||
help: PLUGIN_ADMIN.SCHEDULER_OUTPUT_TYPE_HELP
|
||||
default: append
|
||||
options:
|
||||
append: Append
|
||||
overwrite: Overwrite
|
||||
.email:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.SCHEDULER_EMAIL
|
||||
help: PLUGIN_ADMIN.SCHEDULER_EMAIL_HELP
|
||||
placeholder: 'notifications@yoursite.com'
|
||||
|
||||
modern_tab:
|
||||
type: tab
|
||||
title: Advanced Features
|
||||
|
||||
fields:
|
||||
workers_section:
|
||||
type: section
|
||||
title: Worker Configuration
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
modern.workers:
|
||||
type: number
|
||||
label: Concurrent Workers
|
||||
help: Number of jobs that can run simultaneously (1 = sequential)
|
||||
default: 4
|
||||
size: x-small
|
||||
append: workers
|
||||
validate:
|
||||
type: int
|
||||
min: 1
|
||||
max: 10
|
||||
|
||||
retry_section:
|
||||
type: section
|
||||
title: Retry Configuration
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
modern.retry.enabled:
|
||||
type: toggle
|
||||
label: Enable Job Retry
|
||||
help: Automatically retry failed jobs
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
modern.retry.max_attempts:
|
||||
type: number
|
||||
label: Maximum Retry Attempts
|
||||
help: Maximum number of times to retry a failed job
|
||||
default: 3
|
||||
size: x-small
|
||||
append: retries
|
||||
validate:
|
||||
type: int
|
||||
min: 1
|
||||
max: 10
|
||||
|
||||
modern.retry.backoff:
|
||||
type: select
|
||||
label: Retry Backoff Strategy
|
||||
help: How to calculate delay between retries
|
||||
default: exponential
|
||||
options:
|
||||
linear: Linear (fixed delay)
|
||||
exponential: Exponential (increasing delay)
|
||||
|
||||
queue_section:
|
||||
type: section
|
||||
title: Queue Configuration
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
modern.queue.path:
|
||||
type: text
|
||||
label: Queue Storage Path
|
||||
help: Where to store queued jobs
|
||||
default: 'user-data://scheduler/queue'
|
||||
placeholder: 'user-data://scheduler/queue'
|
||||
|
||||
modern.queue.max_size:
|
||||
type: number
|
||||
label: Maximum Queue Size
|
||||
help: Maximum number of jobs that can be queued
|
||||
default: 1000
|
||||
size: x-small
|
||||
append: jobs
|
||||
validate:
|
||||
type: int
|
||||
min: 100
|
||||
max: 10000
|
||||
|
||||
history_section:
|
||||
type: section
|
||||
title: Job History
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
modern.history.enabled:
|
||||
type: toggle
|
||||
label: Enable Job History
|
||||
help: Track execution history for all jobs
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
modern.history.retention_days:
|
||||
type: number
|
||||
label: History Retention (days)
|
||||
help: How long to keep job history
|
||||
default: 30
|
||||
size: x-small
|
||||
append: days
|
||||
validate:
|
||||
type: int
|
||||
min: 1
|
||||
max: 365
|
||||
|
||||
webhook_section:
|
||||
type: section
|
||||
title: Webhook Configuration
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
webhook_plugin_status:
|
||||
type: webhook-status
|
||||
label:
|
||||
modern.webhook.enabled:
|
||||
type: toggle
|
||||
label: Enable Webhook Triggers
|
||||
help: Allow triggering scheduler via HTTP webhook
|
||||
highlight: 0
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
modern.webhook.token:
|
||||
type: text
|
||||
label: Webhook Security Token
|
||||
help: Secret token for authenticating webhook requests. Keep this secret!
|
||||
placeholder: 'Click Generate to create a secure token'
|
||||
autocomplete: 'off'
|
||||
|
||||
webhook_token_generate:
|
||||
type: display
|
||||
label:
|
||||
content: |
|
||||
<div style="margin-top: -10px; margin-bottom: 15px;">
|
||||
<button type="button" class="button button-primary" onclick="generateWebhookToken()">
|
||||
<i class="fa fa-refresh"></i> Generate Token
|
||||
</button>
|
||||
</div>
|
||||
<script>
|
||||
function generateWebhookToken() {
|
||||
try {
|
||||
// Generate token
|
||||
const array = new Uint8Array(32);
|
||||
crypto.getRandomValues(array);
|
||||
const token = Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
||||
|
||||
// Try multiple selectors to find the field
|
||||
let field = document.querySelector('[name="data[scheduler][modern][webhook][token]"]');
|
||||
if (!field) {
|
||||
field = document.querySelector('input[name*="webhook][token"]');
|
||||
}
|
||||
if (!field) {
|
||||
field = document.getElementById('scheduler-modern-webhook-token');
|
||||
}
|
||||
if (!field) {
|
||||
// Look for any text input in the webhook section
|
||||
const webhookSection = document.querySelector('.webhook_section');
|
||||
if (webhookSection) {
|
||||
const inputs = webhookSection.querySelectorAll('input[type="text"]');
|
||||
// Find the token field by checking for the placeholder
|
||||
for (let input of inputs) {
|
||||
if (input.placeholder && input.placeholder.includes('Generate')) {
|
||||
field = input;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (field) {
|
||||
field.value = token;
|
||||
field.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
field.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
// Flash the field to show it was updated
|
||||
field.style.backgroundColor = '#d4edda';
|
||||
setTimeout(function() {
|
||||
field.style.backgroundColor = '';
|
||||
}, 500);
|
||||
// Also try to trigger Grav's form change detection
|
||||
if (window.jQuery) {
|
||||
jQuery(field).trigger('change');
|
||||
}
|
||||
} else {
|
||||
// Log more debugging info
|
||||
console.error('Token field not found. Looking for input fields...');
|
||||
console.log('All inputs:', document.querySelectorAll('input[type="text"]'));
|
||||
alert('Could not find the token field. Please ensure you are in the Advanced Features tab and the Webhook Configuration section is visible.');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error generating token:', e);
|
||||
alert('Error generating token: ' + e.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
markdown: false
|
||||
|
||||
modern.webhook.path:
|
||||
type: text
|
||||
label: Webhook Path
|
||||
help: URL path for webhook endpoint
|
||||
default: '/scheduler/webhook'
|
||||
placeholder: '/scheduler/webhook'
|
||||
|
||||
health_section:
|
||||
type: section
|
||||
title: Health Check Configuration
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
modern.health.enabled:
|
||||
type: toggle
|
||||
label: Enable Health Check
|
||||
help: Provide health status endpoint for monitoring
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
modern.health.path:
|
||||
type: text
|
||||
label: Health Check Path
|
||||
help: URL path for health check endpoint
|
||||
default: '/scheduler/health'
|
||||
placeholder: '/scheduler/health'
|
||||
|
||||
webhook_usage:
|
||||
type: section
|
||||
title: Usage Examples
|
||||
underline: true
|
||||
|
||||
fields:
|
||||
webhook_examples:
|
||||
type: display
|
||||
label:
|
||||
content: |
|
||||
<script src="{{ url('plugin://admin/themes/grav/js/clipboard-helper.js') }}"></script>
|
||||
<div class="webhook-examples">
|
||||
<script>
|
||||
// Initialize webhook commands when page loads
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (typeof GravClipboard !== 'undefined') {
|
||||
GravClipboard.initWebhookCommands();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<h4>How to use webhooks:</h4>
|
||||
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<label style="display: block; margin-bottom: 0.25rem; font-weight: 500;">Trigger all due jobs (respects schedule):</label>
|
||||
<div class="form-input-wrapper form-input-addon-wrapper">
|
||||
<textarea id="webhook-all-cmd" readonly rows="2" style="font-family: monospace; background: #f5f5f5; resize: none;">Loading...</textarea>
|
||||
<div class="form-input-addon form-input-append" style="cursor: pointer;" onclick="GravClipboard.copy(this)"><i class="fa fa-copy"></i> Copy</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<label style="display: block; margin-bottom: 0.25rem; font-weight: 500;">Force-run specific job (ignores schedule):</label>
|
||||
<div class="form-input-wrapper form-input-addon-wrapper">
|
||||
<textarea id="webhook-job-cmd" readonly rows="2" style="font-family: monospace; background: #f5f5f5; resize: none;">Loading...</textarea>
|
||||
<div class="form-input-addon form-input-append" style="cursor: pointer;" onclick="GravClipboard.copy(this)"><i class="fa fa-copy"></i> Copy</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<label style="display: block; margin-bottom: 0.25rem; font-weight: 500;">Check health status:</label>
|
||||
<div class="form-input-wrapper form-input-addon-wrapper">
|
||||
<input type="text" id="webhook-health-cmd" readonly value="Loading..." style="font-family: monospace; background: #f5f5f5;">
|
||||
<div class="form-input-addon form-input-append" style="cursor: pointer;" onclick="GravClipboard.copy(this)"><i class="fa fa-copy"></i> Copy</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 1rem;">
|
||||
<p><strong>GitHub Actions example:</strong></p>
|
||||
<pre>- name: Trigger Scheduler
|
||||
run: |
|
||||
curl -X POST ${{ secrets.SITE_URL }}/scheduler/webhook \
|
||||
-H "Authorization: Bearer ${{ secrets.WEBHOOK_TOKEN }}"</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
markdown: false
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ form:
|
||||
"D, d M Y G:i:s": Date3
|
||||
"d-m-y G:i": Date4
|
||||
"jS M Y": Date5
|
||||
"Y-m-d G:i": Date6
|
||||
|
||||
pages.dateformat.long:
|
||||
type: dateformat
|
||||
@@ -116,6 +117,7 @@ form:
|
||||
"D, d M Y G:i:s": Date3
|
||||
"d-m-y G:i": Date4
|
||||
"jS M Y": Date5
|
||||
"Y-m-d G:i:s": Date6
|
||||
|
||||
pages.order.by:
|
||||
type: select
|
||||
@@ -177,39 +179,47 @@ form:
|
||||
label: PLUGIN_ADMIN.APPEND_URL_EXT
|
||||
help: PLUGIN_ADMIN.APPEND_URL_EXT_HELP
|
||||
|
||||
pages.redirect_default_route:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.REDIRECT_DEFAULT_ROUTE
|
||||
help: PLUGIN_ADMIN.REDIRECT_DEFAULT_ROUTE_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
pages.redirect_default_code:
|
||||
type: select
|
||||
size: medium
|
||||
classes: fancy
|
||||
label: PLUGIN_ADMIN.REDIRECT_DEFAULT_CODE
|
||||
help: PLUGIN_ADMIN.REDIRECT_DEFAULT_CODE_HELP
|
||||
default: 302
|
||||
options:
|
||||
301: 301 - Permanent
|
||||
302: 302 - Found
|
||||
303: 303 - Other
|
||||
304: 304 - Not Modified
|
||||
301: PLUGIN_ADMIN.REDIRECT_OPTION_301
|
||||
302: PLUGIN_ADMIN.REDIRECT_OPTION_302
|
||||
303: PLUGIN_ADMIN.REDIRECT_OPTION_303
|
||||
|
||||
pages.redirect_default_route:
|
||||
type: select
|
||||
size: medium
|
||||
classes: fancy
|
||||
label: PLUGIN_ADMIN.REDIRECT_DEFAULT_ROUTE
|
||||
help: PLUGIN_ADMIN.REDIRECT_DEFAULT_ROUTE_HELP
|
||||
default: 0
|
||||
options:
|
||||
0: PLUGIN_ADMIN.REDIRECT_OPTION_NO_REDIRECT
|
||||
1: PLUGIN_ADMIN.REDIRECT_OPTION_DEFAULT_REDIRECT
|
||||
301: PLUGIN_ADMIN.REDIRECT_OPTION_301
|
||||
302: PLUGIN_ADMIN.REDIRECT_OPTION_302
|
||||
validate:
|
||||
type: int
|
||||
|
||||
pages.redirect_trailing_slash:
|
||||
type: toggle
|
||||
type: select
|
||||
size: medium
|
||||
classes: fancy
|
||||
label: PLUGIN_ADMIN.REDIRECT_TRAILING_SLASH
|
||||
help: PLUGIN_ADMIN.REDIRECT_TRAILING_SLASH_HELP
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
0: PLUGIN_ADMIN.REDIRECT_OPTION_NO_REDIRECT
|
||||
1: PLUGIN_ADMIN.REDIRECT_OPTION_DEFAULT_REDIRECT
|
||||
301: PLUGIN_ADMIN.REDIRECT_OPTION_301
|
||||
302: PLUGIN_ADMIN.REDIRECT_OPTION_302
|
||||
validate:
|
||||
type: bool
|
||||
type: int
|
||||
|
||||
pages.ignore_hidden:
|
||||
type: toggle
|
||||
@@ -440,6 +450,17 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
languages.debug:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.LANGUAGE_DEBUG
|
||||
help: PLUGIN_ADMIN.LANGUAGE_DEBUG_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
http_headers:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.HTTP_HEADERS
|
||||
@@ -589,6 +610,15 @@ form:
|
||||
hash: All files timestamps
|
||||
none: No timestamp checking
|
||||
|
||||
cache.check.interval:
|
||||
type: number
|
||||
size: x-small
|
||||
label: Cache Check Interval
|
||||
help: Seconds to reuse the previously computed filesystem hash before checking again. Zero keeps existing per-request checks.
|
||||
validate:
|
||||
type: int
|
||||
min: 0
|
||||
|
||||
cache.driver:
|
||||
type: select
|
||||
size: small
|
||||
@@ -598,12 +628,9 @@ form:
|
||||
options:
|
||||
auto: Auto detect
|
||||
file: File
|
||||
apc: APC
|
||||
apcu: APCu
|
||||
xcache: Xcache
|
||||
memcache: Memcache
|
||||
memcached: Memcached
|
||||
wincache: WinCache
|
||||
redis: Redis
|
||||
|
||||
cache.prefix:
|
||||
@@ -613,6 +640,19 @@ form:
|
||||
help: PLUGIN_ADMIN.CACHE_PREFIX_HELP
|
||||
placeholder: PLUGIN_ADMIN.CACHE_PREFIX_PLACEHOLDER
|
||||
|
||||
cache.purge_max_age_days:
|
||||
type: text
|
||||
size: x-small
|
||||
append: GRAV.NICETIME.DAY_PLURAL
|
||||
label: PLUGIN_ADMIN.CACHE_PURGE_AGE
|
||||
help: PLUGIN_ADMIN.CACHE_PURGE_AGE_HELP
|
||||
validate:
|
||||
type: number
|
||||
min: 1
|
||||
max: 365
|
||||
step: 1
|
||||
default: 30
|
||||
|
||||
cache.purge_at:
|
||||
type: cron
|
||||
label: PLUGIN_ADMIN.CACHE_PURGE_JOB
|
||||
@@ -638,7 +678,7 @@ form:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.CLEAR_IMAGES_BY_DEFAULT
|
||||
help: PLUGIN_ADMIN.CLEAR_IMAGES_BY_DEFAULT_HELP
|
||||
highlight: 1
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
@@ -687,20 +727,6 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
cache.memcache.server:
|
||||
type: text
|
||||
size: medium
|
||||
label: PLUGIN_ADMIN.MEMCACHE_SERVER
|
||||
help: PLUGIN_ADMIN.MEMCACHE_SERVER_HELP
|
||||
placeholder: "localhost"
|
||||
|
||||
cache.memcache.port:
|
||||
type: text
|
||||
size: small
|
||||
label: PLUGIN_ADMIN.MEMCACHE_PORT
|
||||
help: PLUGIN_ADMIN.MEMCACHE_PORT_HELP
|
||||
placeholder: "11211"
|
||||
|
||||
cache.memcached.server:
|
||||
type: text
|
||||
size: medium
|
||||
@@ -758,8 +784,8 @@ form:
|
||||
flex.cache.index.enabled:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.FLEX_INDEX_CACHE_ENABLED
|
||||
highlight: 1
|
||||
default: 1
|
||||
highlight: 0
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
@@ -776,8 +802,8 @@ form:
|
||||
flex.cache.object.enabled:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.FLEX_OBJECT_CACHE_ENABLED
|
||||
highlight: 1
|
||||
default: 1
|
||||
highlight: 0
|
||||
default: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.ENABLED
|
||||
0: PLUGIN_ADMIN.DISABLED
|
||||
@@ -864,10 +890,20 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twig.umask_fix:
|
||||
assets:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.ASSETS
|
||||
|
||||
fields:
|
||||
general_config_section:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.GENERAL_CONFIG
|
||||
underline: true
|
||||
|
||||
assets.enable_asset_timestamp:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.TWIG_UMASK_FIX
|
||||
help: PLUGIN_ADMIN.TWIG_UMASK_FIX_HELP
|
||||
label: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS
|
||||
help: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
@@ -875,14 +911,29 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.ASSETS
|
||||
assets.enable_asset_sri:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.ENABLED_SRI_ON_ASSETS
|
||||
help: PLUGIN_ADMIN.ENABLED_SRI_ON_ASSETS_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
fields:
|
||||
assets_section:
|
||||
assets.collections:
|
||||
type: multilevel
|
||||
label: PLUGIN_ADMIN.COLLECTIONS
|
||||
placeholder_key: collection_name
|
||||
placeholder_value: collection_path
|
||||
validate:
|
||||
type: array
|
||||
|
||||
|
||||
css_assets_section:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.ASSETS
|
||||
title: PLUGIN_ADMIN.CSS_ASSETS
|
||||
underline: true
|
||||
|
||||
assets.css_pipeline:
|
||||
@@ -951,6 +1002,11 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
js_assets_section:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.JS_ASSETS
|
||||
underline: true
|
||||
|
||||
assets.js_pipeline:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.JAVASCRIPT_PIPELINE
|
||||
@@ -995,10 +1051,15 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.enable_asset_timestamp:
|
||||
js_module_assets_section:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.JS_MODULE_ASSETS
|
||||
underline: true
|
||||
|
||||
assets.js_module_pipeline:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS
|
||||
help: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS_HELP
|
||||
label: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE
|
||||
help: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
@@ -1006,13 +1067,29 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
assets.collections:
|
||||
type: multilevel
|
||||
label: PLUGIN_ADMIN.COLLECTIONS
|
||||
placeholder_key: collection_name
|
||||
placeholder_value: collection_path
|
||||
assets.js_module_pipeline_include_externals:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_INCLUDE_EXTERNALS
|
||||
help: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_INCLUDE_EXTERNALS_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: array
|
||||
type: bool
|
||||
|
||||
assets.js_module_pipeline_before_excludes:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_BEFORE_EXCLUDES
|
||||
help: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_BEFORE_EXCLUDES_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
|
||||
|
||||
errors:
|
||||
type: tab
|
||||
@@ -1086,6 +1163,13 @@ form:
|
||||
local6: local6
|
||||
local7: local7
|
||||
|
||||
log.syslog.tag:
|
||||
type: text
|
||||
size: small
|
||||
label: PLUGIN_ADMIN.SYSLOG_TAG
|
||||
help: PLUGIN_ADMIN.SYSLOG_TAG_HELP
|
||||
placeholder: "grav"
|
||||
|
||||
debugger:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.DEBUGGER
|
||||
@@ -1149,6 +1233,16 @@ form:
|
||||
title: PLUGIN_ADMIN.MEDIA
|
||||
underline: true
|
||||
|
||||
images.adapter:
|
||||
type: select
|
||||
size: small
|
||||
label: PLUGIN_ADMIN.IMAGE_ADAPTER
|
||||
help: PLUGIN_ADMIN.IMAGE_ADAPTER_HELP
|
||||
highlight: gd
|
||||
options:
|
||||
gd: GD (PHP built-in)
|
||||
imagick: Imagick
|
||||
|
||||
images.default_image_quality:
|
||||
type: range
|
||||
append: '%'
|
||||
@@ -1211,6 +1305,28 @@ form:
|
||||
auto: Auto
|
||||
lazy: Lazy
|
||||
eager: Eager
|
||||
|
||||
images.defaults.decoding:
|
||||
type: select
|
||||
size: small
|
||||
label: PLUGIN_ADMIN.IMAGES_DECODING
|
||||
help: PLUGIN_ADMIN.IMAGES_DECODING_HELP
|
||||
highlight: auto
|
||||
options:
|
||||
auto: Auto
|
||||
sync: Sync
|
||||
async: Async
|
||||
|
||||
images.defaults.fetchpriority:
|
||||
type: select
|
||||
size: small
|
||||
label: PLUGIN_ADMIN.IMAGES_FETCHPRIORITY
|
||||
help: PLUGIN_ADMIN.IMAGES_FETCHPRIORITY_HELP
|
||||
highlight: auto
|
||||
options:
|
||||
auto: Auto
|
||||
high: High
|
||||
low: Low
|
||||
|
||||
images.seofriendly:
|
||||
type: toggle
|
||||
@@ -1263,6 +1379,45 @@ form:
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
section_images_cls:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.IMAGES_CLS_TITLE
|
||||
underline: true
|
||||
|
||||
images.cls.auto_sizes:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.IMAGES_CLS_AUTO_SIZES
|
||||
help: PLUGIN_ADMIN.IMAGES_CLS_AUTO_SIZES_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
images.cls.aspect_ratio:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.IMAGES_CLS_ASPECT_RATIO
|
||||
help: PLUGIN_ADMIN.IMAGES_CLS_ASPECT_RATIO_HELP
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
images.cls.retina_scale:
|
||||
type: select
|
||||
label: PLUGIN_ADMIN.IMAGES_CLS_RETINA_SCALE
|
||||
help: PLUGIN_ADMIN.IMAGES_CLS_RETINA_SCALE_HELP
|
||||
size: small
|
||||
highlight: 1
|
||||
options:
|
||||
1: 1X
|
||||
2: 2X
|
||||
3: 3X
|
||||
4: 4X
|
||||
|
||||
session:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.SESSION
|
||||
@@ -1336,6 +1491,18 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
session.secure_https:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.SESSION_SECURE_HTTPS
|
||||
help: PLUGIN_ADMIN.SESSION_SECURE_HTTPS_HELP
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
default: true
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
session.httponly:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.SESSION_HTTPONLY
|
||||
@@ -1388,6 +1555,10 @@ form:
|
||||
title: PLUGIN_ADMIN.ADVANCED
|
||||
underline: true
|
||||
|
||||
gpm_section:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.GPM_SECTION
|
||||
|
||||
gpm.releases:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.GPM_RELEASES
|
||||
@@ -1397,23 +1568,6 @@ form:
|
||||
stable: PLUGIN_ADMIN.STABLE
|
||||
testing: PLUGIN_ADMIN.TESTING
|
||||
|
||||
gpm.proxy_url:
|
||||
type: text
|
||||
size: medium
|
||||
placeholder: "e.g. 127.0.0.1:3128"
|
||||
label: PLUGIN_ADMIN.PROXY_URL
|
||||
help: PLUGIN_ADMIN.PROXY_URL_HELP
|
||||
|
||||
gpm.method:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.GPM_METHOD
|
||||
highlight: auto
|
||||
help: PLUGIN_ADMIN.GPM_METHOD_HELP
|
||||
options:
|
||||
auto: PLUGIN_ADMIN.AUTO
|
||||
fopen: PLUGIN_ADMIN.FOPEN
|
||||
curl: PLUGIN_ADMIN.CURL
|
||||
|
||||
gpm.official_gpm_only:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY
|
||||
@@ -1426,17 +1580,117 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
gpm.verify_peer:
|
||||
updates_section:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.UPDATES_SECTION
|
||||
|
||||
updates.safe_upgrade:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.GPM_VERIFY_PEER
|
||||
label: PLUGIN_ADMIN.SAFE_UPGRADE
|
||||
help: PLUGIN_ADMIN.SAFE_UPGRADE_HELP
|
||||
highlight: 1
|
||||
help: PLUGIN_ADMIN.GPM_VERIFY_PEER_HELP
|
||||
default: true
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
updates.safe_upgrade_snapshot_limit:
|
||||
type: number
|
||||
label: PLUGIN_ADMIN.SAFE_UPGRADE_SNAPSHOT_LIMIT
|
||||
help: PLUGIN_ADMIN.SAFE_UPGRADE_SNAPSHOT_LIMIT_HELP
|
||||
default: 5
|
||||
validate:
|
||||
type: int
|
||||
min: 0
|
||||
|
||||
updates.recovery_mode:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.RECOVERY_MODE
|
||||
help: PLUGIN_ADMIN.RECOVERY_MODE_HELP
|
||||
highlight: 1
|
||||
default: true
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
http_section:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.HTTP_SECTION
|
||||
|
||||
http.method:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.GPM_METHOD
|
||||
highlight: auto
|
||||
help: PLUGIN_ADMIN.GPM_METHOD_HELP
|
||||
options:
|
||||
auto: PLUGIN_ADMIN.AUTO
|
||||
fopen: PLUGIN_ADMIN.FOPEN
|
||||
curl: PLUGIN_ADMIN.CURL
|
||||
|
||||
http.enable_proxy:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.SSL_ENABLE_PROXY
|
||||
highlight: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
default: false
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
http.proxy_url:
|
||||
type: text
|
||||
size: medium
|
||||
placeholder: "e.g. 127.0.0.1:3128"
|
||||
label: PLUGIN_ADMIN.PROXY_URL
|
||||
help: PLUGIN_ADMIN.PROXY_URL_HELP
|
||||
|
||||
http.proxy_cert_path:
|
||||
type: text
|
||||
size: medium
|
||||
placeholder: "e.g. /Users/bob/certs/"
|
||||
label: PLUGIN_ADMIN.PROXY_CERT
|
||||
help: PLUGIN_ADMIN.PROXY_CERT_HELP
|
||||
|
||||
http.verify_peer:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.SSL_VERIFY_PEER
|
||||
highlight: 1
|
||||
help: PLUGIN_ADMIN.SSL_VERIFY_PEER_HELP
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
http.verify_host:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.SSL_VERIFY_HOST
|
||||
highlight: 1
|
||||
help: PLUGIN_ADMIN.SSL_VERIFY_HOST_HELP
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
http.concurrent_connections:
|
||||
type: number
|
||||
size: x-small
|
||||
label: PLUGIN_ADMIN.HTTP_CONNECTIONS
|
||||
help: PLUGIN_ADMIN.HTTP_CONNECTIONS_HELP
|
||||
validate:
|
||||
min: 1
|
||||
max: 20
|
||||
|
||||
misc_section:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.MISC_SECTION
|
||||
|
||||
reverse_proxy_setup:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.REVERSE_PROXY
|
||||
@@ -1548,8 +1802,8 @@ form:
|
||||
http_x_forwarded.host:
|
||||
type: toggle
|
||||
label: HTTP_X_FORWARDED_HOST Enabled
|
||||
highlight: 0
|
||||
default: 0
|
||||
highlight: 1
|
||||
default: 1
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
@@ -1582,8 +1836,8 @@ form:
|
||||
strict_mode.blueprint_compat:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.STRICT_BLUEPRINT_COMPAT
|
||||
highlight: 0
|
||||
default: 0
|
||||
highlight: 1
|
||||
default: 1
|
||||
help: PLUGIN_ADMIN.STRICT_BLUEPRINT_COMPAT_HELP
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
@@ -1603,7 +1857,7 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
strict_mode.twig_compat:
|
||||
strict_mode.twig2_compat:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.STRICT_TWIG_COMPAT
|
||||
highlight: 0
|
||||
@@ -1615,35 +1869,27 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
experimental:
|
||||
strict_mode.twig3_compat:
|
||||
type: toggle
|
||||
label: Twig 3 Compatibility
|
||||
highlight: 0
|
||||
default: 0
|
||||
help: Enable automatic rewrites for legacy Twig 1/2 syntax that breaks on Twig 3 (e.g. `for ... if ...` guards)
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
|
||||
accounts:
|
||||
type: tab
|
||||
title: PLUGIN_ADMIN.EXPERIMENTAL
|
||||
title: PLUGIN_ADMIN.ACCOUNTS
|
||||
|
||||
fields:
|
||||
experimental_section:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.EXPERIMENTAL
|
||||
underline: true
|
||||
|
||||
# flex_pages:
|
||||
# type: section
|
||||
# title: Flex Pages
|
||||
#
|
||||
# pages.type:
|
||||
# type: select
|
||||
# label: PLUGIN_ADMIN.PAGES_TYPE
|
||||
# highlight: regular
|
||||
# help: PLUGIN_ADMIN.PAGES_TYPE_HELP
|
||||
# options:
|
||||
# regular: PLUGIN_ADMIN.REGULAR
|
||||
# flex: PLUGIN_ADMIN.FLEX
|
||||
|
||||
pages.type:
|
||||
type: hidden
|
||||
|
||||
flex_accounts:
|
||||
type: section
|
||||
title: Flex Accounts
|
||||
title: User Accounts
|
||||
|
||||
accounts.type:
|
||||
type: select
|
||||
@@ -1662,3 +1908,38 @@ form:
|
||||
options:
|
||||
file: PLUGIN_ADMIN.FILE
|
||||
folder: PLUGIN_ADMIN.FOLDER
|
||||
|
||||
accounts.avatar:
|
||||
type: select
|
||||
label: PLUGIN_ADMIN.AVATAR
|
||||
default: gravatar
|
||||
help: PLUGIN_ADMIN.AVATAR_HELP
|
||||
options:
|
||||
multiavatar: Multiavatar [local]
|
||||
gravatar: Gravatar [external]
|
||||
|
||||
# experimental:
|
||||
# type: tab
|
||||
# title: PLUGIN_ADMIN.EXPERIMENTAL
|
||||
#
|
||||
# fields:
|
||||
# experimental_section:
|
||||
# type: section
|
||||
# title: PLUGIN_ADMIN.EXPERIMENTAL
|
||||
# underline: true
|
||||
#
|
||||
# flex_pages:
|
||||
# type: section
|
||||
# title: Flex Pages
|
||||
#
|
||||
# pages.type:
|
||||
# type: select
|
||||
# label: PLUGIN_ADMIN.PAGES_TYPE
|
||||
# highlight: regular
|
||||
# help: PLUGIN_ADMIN.PAGES_TYPE_HELP
|
||||
# options:
|
||||
# regular: PLUGIN_ADMIN.REGULAR
|
||||
# flex: PLUGIN_ADMIN.FLEX
|
||||
#
|
||||
# pages.type:
|
||||
# type: hidden
|
||||
|
||||
@@ -104,7 +104,7 @@ config:
|
||||
|
||||
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 %}"
|
||||
template: "{% if object.root %}Root <small>( <root> )</small>{% else %}{{ (form.value('header.title') ?? form.value('folder'))|e }} <small>( {{ (object.getRoute().toString(false) ?: '/')|e }} )</small>{% endif %}"
|
||||
|
||||
# TODO: not used yet
|
||||
buttons:
|
||||
@@ -176,7 +176,7 @@ config:
|
||||
indexed: true
|
||||
# Set default ordering of the pages
|
||||
ordering:
|
||||
key: ASC
|
||||
storage_key: ASC
|
||||
search:
|
||||
# Search options
|
||||
options:
|
||||
@@ -184,9 +184,9 @@ config:
|
||||
# Fields to be searched
|
||||
fields:
|
||||
- key
|
||||
- slug
|
||||
- menu
|
||||
- title
|
||||
- name
|
||||
|
||||
blueprints:
|
||||
configure:
|
||||
|
||||
@@ -72,7 +72,7 @@ config:
|
||||
# Edit view
|
||||
edit:
|
||||
title:
|
||||
template: "{{ form.value('fullname') ?? form.value('username') }} <{{ form.value('email') }}>"
|
||||
template: "{{ form.value('fullname') ?? form.value('username') }}"
|
||||
|
||||
# Configure view
|
||||
configure:
|
||||
@@ -122,6 +122,19 @@ config:
|
||||
fields:
|
||||
- key
|
||||
- email
|
||||
- username
|
||||
- fullname
|
||||
|
||||
relationships:
|
||||
media:
|
||||
type: media
|
||||
cardinality: to-many
|
||||
avatar:
|
||||
type: media
|
||||
cardinality: to-one
|
||||
# roles:
|
||||
# type: user-groups
|
||||
# cardinality: to-many
|
||||
|
||||
blueprints:
|
||||
configure:
|
||||
|
||||
@@ -18,6 +18,7 @@ config:
|
||||
configure:
|
||||
path: '/accounts/configure'
|
||||
redirects:
|
||||
'/groups': '/accounts/groups'
|
||||
'/accounts': '/accounts/groups'
|
||||
|
||||
# Permissions
|
||||
@@ -112,6 +113,7 @@ config:
|
||||
fields:
|
||||
- key
|
||||
- groupname
|
||||
- readableName
|
||||
- description
|
||||
|
||||
blueprints:
|
||||
|
||||
@@ -121,7 +121,7 @@ form:
|
||||
underline: true
|
||||
|
||||
folder:
|
||||
type: text
|
||||
type: folder-slug
|
||||
label: PLUGIN_ADMIN.FOLDER_NAME
|
||||
validate:
|
||||
rule: slug
|
||||
@@ -320,6 +320,18 @@ form:
|
||||
|
||||
fields:
|
||||
|
||||
header.redirect_default_route:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: PLUGIN_ADMIN.REDIRECT_DEFAULT_ROUTE
|
||||
help: PLUGIN_ADMIN.REDIRECT_DEFAULT_ROUTE_HELP
|
||||
config-highlight@: system.pages.redirect_default_route
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
header.routes.default:
|
||||
type: text
|
||||
toggleable: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
title: PLUGIN_ADMIN:EXTERNAL
|
||||
title: PLUGIN_ADMIN.EXTERNAL
|
||||
extends@:
|
||||
type: default
|
||||
context: blueprints://pages
|
||||
type: default
|
||||
context: blueprints://pages
|
||||
|
||||
form:
|
||||
validation: loose
|
||||
@@ -29,16 +29,16 @@ form:
|
||||
unset@: true
|
||||
|
||||
header.external_url:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.EXTERNAL_URL
|
||||
placeholder: https://getgrav.org
|
||||
validate:
|
||||
required: true
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.EXTERNAL_URL
|
||||
placeholder: https://getgrav.org
|
||||
validate:
|
||||
required: true
|
||||
|
||||
options:
|
||||
fields:
|
||||
|
||||
publishing:
|
||||
|
||||
fields:
|
||||
|
||||
header.date:
|
||||
|
||||
@@ -51,17 +51,13 @@ form:
|
||||
type: bool
|
||||
|
||||
header.permissions.authors:
|
||||
type: list
|
||||
type: array
|
||||
toggleable: true
|
||||
value_only: true
|
||||
placeholder_value: PLUGIN_ADMIN.USERNAME
|
||||
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
|
||||
|
||||
@@ -11,10 +11,21 @@ form:
|
||||
avatar:
|
||||
type: file
|
||||
size: large
|
||||
destination: 'user://accounts/avatars'
|
||||
destination: 'account://avatars'
|
||||
multiple: false
|
||||
random_name: true
|
||||
|
||||
multiavatar_only:
|
||||
type: conditional
|
||||
condition: config.system.accounts.avatar == 'multiavatar'
|
||||
fields:
|
||||
avatar_hash:
|
||||
type: text
|
||||
label: ''
|
||||
placeholder: 'e.g. dceaadcfda491f4e45'
|
||||
description: PLUGIN_ADMIN.AVATAR_HASH
|
||||
size: large
|
||||
|
||||
content:
|
||||
type: section
|
||||
title: PLUGIN_ADMIN.ACCOUNT
|
||||
@@ -107,6 +118,12 @@ form:
|
||||
label: PLUGIN_ADMIN.2FA_SECRET
|
||||
sublabel: PLUGIN_ADMIN.2FA_SECRET_HELP
|
||||
|
||||
yubikey_id:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.YUBIKEY_ID
|
||||
description: PLUGIN_ADMIN.YUBIKEY_HELP
|
||||
size: small
|
||||
maxlength: 12
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,6 @@ form:
|
||||
label: PLUGIN_ADMIN.USERNAME
|
||||
help: PLUGIN_ADMIN.USERNAME_HELP
|
||||
unset-disabled@: true
|
||||
unset-readonly@: true
|
||||
unset-readonly@: true
|
||||
validate:
|
||||
required: true
|
||||
|
||||
@@ -10,6 +10,7 @@ profiles:
|
||||
root: '/'
|
||||
schedule: false
|
||||
schedule_at: '0 3 * * *'
|
||||
schedule_environment: ''
|
||||
exclude_paths: "/backup\r\n/cache\r\n/images\r\n/logs\r\n/tmp"
|
||||
exclude_files: ".DS_Store\r\n.git\r\n.svn\r\n.hg\r\n.idea\r\n.vscode\r\nnode_modules"
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@ types:
|
||||
type: image
|
||||
thumb: media/thumb-webp.png
|
||||
mime: image/webp
|
||||
avif:
|
||||
type: image
|
||||
thumb: media/thumb.png
|
||||
mime: image/avif
|
||||
gif:
|
||||
type: animated
|
||||
thumb: media/thumb-gif.png
|
||||
@@ -91,7 +95,7 @@ types:
|
||||
aif:
|
||||
type: audio
|
||||
thumb: media/thumb-aif.png
|
||||
mime: audio/aif
|
||||
mime: audio/aiff
|
||||
txt:
|
||||
type: file
|
||||
thumb: media/thumb-txt.png
|
||||
@@ -195,7 +199,7 @@ types:
|
||||
gz:
|
||||
type: file
|
||||
thumb: media/thumb-gz.png
|
||||
mime: application/gzip
|
||||
mime: application/x-gzip
|
||||
tar:
|
||||
type: file
|
||||
thumb: media/thumb-tar.png
|
||||
@@ -207,8 +211,13 @@ types:
|
||||
js:
|
||||
type: file
|
||||
thumb: media/thumb-js.png
|
||||
mime: application/javascript
|
||||
mime: text/javascript
|
||||
json:
|
||||
type: file
|
||||
thumb: media/thumb-json.png
|
||||
mime: application/json
|
||||
vcf:
|
||||
type: file
|
||||
thumb: media/thumb-vcf.png
|
||||
mime: text/x-vcard
|
||||
|
||||
|
||||
1988
system/config/mime.yaml
Normal file
1988
system/config/mime.yaml
Normal file
File diff suppressed because it is too large
Load Diff
68
system/config/scheduler.yaml
Normal file
68
system/config/scheduler.yaml
Normal file
@@ -0,0 +1,68 @@
|
||||
# Grav Scheduler Configuration
|
||||
|
||||
# Default scheduler settings (backward compatible)
|
||||
defaults:
|
||||
output: true
|
||||
output_type: file
|
||||
email: null
|
||||
|
||||
# Status of individual jobs (enabled/disabled)
|
||||
status: {}
|
||||
|
||||
# Custom scheduled jobs
|
||||
custom_jobs: {}
|
||||
|
||||
# Modern scheduler features (disabled by default for backward compatibility)
|
||||
modern:
|
||||
# Enable modern scheduler features
|
||||
enabled: false
|
||||
|
||||
# Number of concurrent workers (1 = sequential execution like legacy)
|
||||
workers: 1
|
||||
|
||||
# Job retry configuration
|
||||
retry:
|
||||
enabled: true
|
||||
max_attempts: 3
|
||||
backoff: exponential # 'linear' or 'exponential'
|
||||
|
||||
# Job queue configuration
|
||||
queue:
|
||||
path: user-data://scheduler/queue
|
||||
max_size: 1000
|
||||
|
||||
# Webhook trigger configuration
|
||||
webhook:
|
||||
enabled: false
|
||||
token: null # Set a secure token to enable webhook triggers
|
||||
path: /scheduler/webhook
|
||||
|
||||
# Health check endpoint
|
||||
health:
|
||||
enabled: true
|
||||
path: /scheduler/health
|
||||
|
||||
# Job execution history
|
||||
history:
|
||||
enabled: true
|
||||
retention_days: 30
|
||||
path: user-data://scheduler/history
|
||||
|
||||
# Performance settings
|
||||
performance:
|
||||
job_timeout: 300 # Default timeout in seconds
|
||||
lock_timeout: 10 # Lock acquisition timeout in seconds
|
||||
|
||||
# Monitoring and alerts
|
||||
monitoring:
|
||||
enabled: false
|
||||
alert_on_failure: true
|
||||
alert_email: null
|
||||
webhook_url: null
|
||||
|
||||
# Trigger detection methods
|
||||
triggers:
|
||||
check_cron: true
|
||||
check_systemd: true
|
||||
check_webhook: true
|
||||
check_external: true
|
||||
@@ -1,39 +1,50 @@
|
||||
xss_whitelist: [admin.super] # Whitelist of user access that should 'skip' XSS checking
|
||||
xss_whitelist:
|
||||
- admin.super
|
||||
xss_enabled:
|
||||
on_events: true
|
||||
invalid_protocols: true
|
||||
moz_binding: true
|
||||
html_inline_styles: true
|
||||
dangerous_tags: true
|
||||
on_events: true
|
||||
invalid_protocols: true
|
||||
moz_binding: true
|
||||
html_inline_styles: true
|
||||
dangerous_tags: true
|
||||
xss_invalid_protocols:
|
||||
- javascript
|
||||
- livescript
|
||||
- vbscript
|
||||
- mocha
|
||||
- feed
|
||||
- data
|
||||
- javascript
|
||||
- livescript
|
||||
- vbscript
|
||||
- mocha
|
||||
- feed
|
||||
- data
|
||||
xss_dangerous_tags:
|
||||
- applet
|
||||
- meta
|
||||
- xml
|
||||
- blink
|
||||
- link
|
||||
- style
|
||||
- script
|
||||
- embed
|
||||
- object
|
||||
- iframe
|
||||
- frame
|
||||
- frameset
|
||||
- ilayer
|
||||
- layer
|
||||
- bgsound
|
||||
- title
|
||||
- base
|
||||
- applet
|
||||
- meta
|
||||
- xml
|
||||
- blink
|
||||
- link
|
||||
- style
|
||||
- script
|
||||
- embed
|
||||
- object
|
||||
- iframe
|
||||
- frame
|
||||
- frameset
|
||||
- ilayer
|
||||
- layer
|
||||
- bgsound
|
||||
- title
|
||||
- base
|
||||
- isindex
|
||||
uploads_dangerous_extensions:
|
||||
- php
|
||||
- html
|
||||
- htm
|
||||
- js
|
||||
- exe
|
||||
- php
|
||||
- php2
|
||||
- php3
|
||||
- php4
|
||||
- php5
|
||||
- phar
|
||||
- phtml
|
||||
- html
|
||||
- htm
|
||||
- shtml
|
||||
- shtm
|
||||
- js
|
||||
- exe
|
||||
sanitize_svg: true
|
||||
salt: SbmgUJQ62MqNc0
|
||||
|
||||
@@ -25,7 +25,7 @@ routes:
|
||||
# '/new/(.*)': '/blog/$1' # Regex any /new/my-page URL to /blog/my-page Route
|
||||
|
||||
blog:
|
||||
route: '/blog' # Custom value added (accessible via system.blog.route)
|
||||
route: '/blog' # Custom value added (accessible via site.blog.route)
|
||||
|
||||
#menu: # Menu Example
|
||||
# - text: Source
|
||||
|
||||
@@ -28,6 +28,7 @@ languages:
|
||||
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
|
||||
debug: false # Debug language detection
|
||||
|
||||
home:
|
||||
alias: '/home' # Default path for home, ie /
|
||||
@@ -35,6 +36,7 @@ home:
|
||||
|
||||
pages:
|
||||
type: regular # EXPERIMENTAL: Page type: regular or flex
|
||||
dirs: ['page://'] # Advanced functionality, allows for multiple page paths
|
||||
theme: quark # Default theme (defaults to "quark" theme)
|
||||
order:
|
||||
by: default # Order pages by "default", "alpha" or "date"
|
||||
@@ -75,9 +77,9 @@ pages:
|
||||
last_modified: false # Set the last modified date header based on file modification timestamp
|
||||
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
|
||||
redirect_trailing_slash: true # Handle automatically or 302 redirect a trailing / URL
|
||||
redirect_default_code: 302 # Default code to use for redirects: 301|302|303
|
||||
redirect_trailing_slash: 1 # Always redirect trailing slash with redirect code 0|1|301|302 (0: no redirect, 1: use default code)
|
||||
redirect_default_route: 0 # Always redirect to page's default route using code 0|1|301|302, also removes .htm and .html extensions
|
||||
ignore_files: [.DS_Store] # Files to ignore in Pages
|
||||
ignore_folders: [.git, .idea] # Folders to ignore in Pages
|
||||
ignore_hidden: true # Ignore all Hidden files and folders
|
||||
@@ -91,14 +93,16 @@ cache:
|
||||
enabled: true # Set to true to enable caching
|
||||
check:
|
||||
method: file # Method to check for updates in pages: file|folder|hash|none
|
||||
driver: auto # One of: auto|file|apcu|memcache|wincache
|
||||
interval: 0 # Seconds to reuse previous filesystem hash before rechecking (0 = every request)
|
||||
driver: auto # One of: auto|file|apcu|memcached|redis
|
||||
prefix: 'g' # Cache prefix string (prevents cache conflicts)
|
||||
purge_at: '0 4 * * *' # How often to purge old file cache (using new scheduler)
|
||||
clear_at: '0 3 * * *' # How often to clear cache (using new scheduler)
|
||||
clear_job_type: 'standard' # Type to clear when processing the scheduled clear job `standard`|`all`
|
||||
clear_images_by_default: true # By default grav will include processed images in cache clear, this can be disabled
|
||||
cli_compatibility: false # Ensures only non-volatile drivers are used (file, redis, memcache, etc.)
|
||||
clear_images_by_default: false # By default grav does not include processed images in cache clear, this can be enabled
|
||||
cli_compatibility: false # Ensures only non-volatile drivers are used (file, redis, memcached, etc.)
|
||||
lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite)
|
||||
purge_max_age_days: 30 # Maximum age of cache items in days before they are purged
|
||||
gzip: false # GZip compress the page output
|
||||
allow_webserver_gzip: false # If true, `content-encoding: identity` but connection isn't closed before `onShutDown()` event
|
||||
redis:
|
||||
@@ -113,7 +117,8 @@ twig:
|
||||
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
|
||||
safe_functions: [] # List of PHP functions which are allowed to be used as Twig functions
|
||||
safe_filters: [] # List of PHP functions which are allowed to be used as Twig filters
|
||||
|
||||
assets: # Configuration for Assets Manager (JS, CSS)
|
||||
css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file
|
||||
@@ -125,10 +130,14 @@ assets: # Configuration for Assets Mana
|
||||
js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file
|
||||
js_pipeline_include_externals: true # Include external URLs in the pipeline by default
|
||||
js_pipeline_before_excludes: true # Render the pipeline before any excluded files
|
||||
js_module_pipeline: false # The JS Module pipeline is the unification of multiple JS Module resources into one file
|
||||
js_module_pipeline_include_externals: true # Include external URLs in the pipeline by default
|
||||
js_module_pipeline_before_excludes: true # Render the pipeline before any excluded files
|
||||
js_minify: true # Minify the JS during pipelining
|
||||
enable_asset_timestamp: false # Enable asset timestamps
|
||||
enable_asset_sri: false # Enable asset SRI
|
||||
collections:
|
||||
jquery: system://assets/jquery/jquery-2.x.min.js
|
||||
jquery: system://assets/jquery/jquery-3.x.min.js
|
||||
|
||||
errors:
|
||||
display: 0 # Display either (1) Full backtrace | (0) Simple Error | (-1) System Error
|
||||
@@ -138,6 +147,7 @@ log:
|
||||
handler: file # Log handler. Currently supported: file | syslog
|
||||
syslog:
|
||||
facility: local6 # Syslog facilities output
|
||||
tag: grav # Syslog tag. Default: "grav".
|
||||
|
||||
debugger:
|
||||
enabled: false # Enable Grav debugger and following settings
|
||||
@@ -147,14 +157,27 @@ debugger:
|
||||
close_connection: true # Close the connection before calling onShutdown(). false for debugging
|
||||
|
||||
images:
|
||||
adapter: gd # Image adapter to use: gd | imagick
|
||||
default_image_quality: 85 # Default image quality to use when resampling images (85%)
|
||||
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: true # Automatically fix the image orientation based on the Exif data
|
||||
seofriendly: false # SEO-friendly processed image names
|
||||
cls: # Cumulative Layout Shift: See https://web.dev/optimize-cls/
|
||||
auto_sizes: false # Automatically add height/width to image
|
||||
aspect_ratio: false # Reserve space with aspect ratio style
|
||||
retina_scale: 1 # scale to adjust auto-sizes for better handling of HiDPI resolutions
|
||||
defaults:
|
||||
loading: auto # Let browser pick [auto|lazy|eager]
|
||||
decoding: auto # Let browser pick [auto|sync|async]
|
||||
fetchpriority: auto # Let browser pick [auto|high|low]
|
||||
watermark:
|
||||
image: 'system://images/watermark.png' # Path to a watermark image
|
||||
position_y: 'center' # top|center|bottom
|
||||
position_x: 'center' # left|center|right
|
||||
scale: 33 # percentage of watermark scale
|
||||
watermark_all: false # automatically watermark all images
|
||||
|
||||
media:
|
||||
enable_media_timestamp: false # Enable media timestamps
|
||||
@@ -169,6 +192,7 @@ session:
|
||||
name: grav-site # Name prefix of the session cookie. Use alphanumeric, dashes or underscores only. Do not use dots in the session name
|
||||
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
|
||||
secure_https: true # Set session secure on HTTPS but not on HTTP. Has no effect if you have `session.secure: true`. Set to false if your site jumps between HTTP and 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)
|
||||
@@ -177,14 +201,26 @@ session:
|
||||
|
||||
gpm:
|
||||
releases: stable # Set to either 'stable' or 'testing'
|
||||
proxy_url: # Configure a manual proxy URL for GPM (eg 127.0.0.1:3128)
|
||||
method: 'auto' # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL
|
||||
verify_peer: true # Sometimes on some systems (Windows most commonly) GPM is unable to connect because the SSL certificate cannot be verified. Disabling this setting might help.
|
||||
official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security
|
||||
|
||||
updates:
|
||||
safe_upgrade: true # Enable guarded staging+rollback pipeline for Grav self-updates
|
||||
safe_upgrade_snapshot_limit: 5 # Maximum number of safe-upgrade snapshots to retain (0 = unlimited)
|
||||
recovery_mode: true # Enable recovery mode when fatal errors occur during upgrades
|
||||
|
||||
http:
|
||||
method: auto # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL
|
||||
enable_proxy: true # Enable proxy server configuration
|
||||
proxy_url: # Configure a manual proxy URL for GPM (eg 127.0.0.1:3128)
|
||||
proxy_cert_path: # Local path to proxy certificate folder containing pem files
|
||||
concurrent_connections: 5 # Concurrent HTTP connections when multiplexing
|
||||
verify_peer: true # Enable/Disable SSL verification of peer certificates
|
||||
verify_host: true # Enable/Disable SSL verification of host certificates
|
||||
|
||||
accounts:
|
||||
type: regular # EXPERIMENTAL: Account type: regular or flex
|
||||
storage: file # EXPERIMENTAL: Flex storage type: file or folder
|
||||
avatar: gravatar # Avatar generator [multiavatar|gravatar]
|
||||
|
||||
flex:
|
||||
cache:
|
||||
@@ -200,5 +236,6 @@ flex:
|
||||
|
||||
strict_mode:
|
||||
yaml_compat: false # Set to true to enable YAML backwards compatibility
|
||||
twig_compat: false # Set to true to enable deprecated Twig settings (autoescape: false)
|
||||
twig2_compat: false # Set to true to enable deprecated Twig settings (autoescape: false)
|
||||
twig3_compat: true # Set to true to enable automatic fixes for Twig 3 syntax changes
|
||||
blueprint_compat: false # Set to true to enable backward compatible strict support for blueprints
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Grav\Core
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '1.7.6');
|
||||
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
|
||||
define('GRAV_TESTING', false);
|
||||
define('GRAV_VERSION', '1.8.0-beta.28');
|
||||
define('GRAV_SCHEMA', '1.8.0_2025-09-21_0');
|
||||
define('GRAV_TESTING', true);
|
||||
|
||||
// PHP minimum requirement
|
||||
if (!defined('GRAV_PHP_MIN')) {
|
||||
define('GRAV_PHP_MIN', '7.3.6');
|
||||
define('GRAV_PHP_MIN', '8.3.0');
|
||||
}
|
||||
|
||||
// Directory separator
|
||||
@@ -22,51 +23,68 @@ if (!defined('DS')) {
|
||||
define('DS', '/');
|
||||
}
|
||||
|
||||
// Directories and Paths
|
||||
// Absolute path to Grav root. This is where Grav is installed into.
|
||||
if (!defined('GRAV_ROOT')) {
|
||||
$path = rtrim(str_replace(DIRECTORY_SEPARATOR, DS, getenv('GRAV_ROOT') ?: getcwd()), DS);
|
||||
define('GRAV_ROOT', $path);
|
||||
define('GRAV_ROOT', $path ?: DS);
|
||||
}
|
||||
// Absolute path to Grav webroot. This is the path where your site is located in.
|
||||
if (!defined('GRAV_WEBROOT')) {
|
||||
$path = rtrim(getenv('GRAV_WEBROOT') ?: GRAV_ROOT, DS);
|
||||
define('GRAV_WEBROOT', $path ?: DS);
|
||||
}
|
||||
// Relative path to user folder. This path needs to be located under GRAV_WEBROOT.
|
||||
if (!defined('GRAV_USER_PATH')) {
|
||||
$path = rtrim(getenv('GRAV_USER_PATH') ?: 'user', DS);
|
||||
define('GRAV_USER_PATH', $path);
|
||||
}
|
||||
// Absolute or relative path to system folder. Defaults to GRAV_ROOT/system
|
||||
// If system folder is outside of webroot, see https://github.com/getgrav/grav/issues/3297#issuecomment-810294972
|
||||
if (!defined('GRAV_SYSTEM_PATH')) {
|
||||
$path = rtrim(getenv('GRAV_SYSTEM_PATH') ?: 'system', DS);
|
||||
define('GRAV_SYSTEM_PATH', $path);
|
||||
}
|
||||
// Absolute or relative path to cache folder. Defaults to GRAV_ROOT/cache
|
||||
if (!defined('GRAV_CACHE_PATH')) {
|
||||
$path = rtrim(getenv('GRAV_CACHE_PATH') ?: 'cache', DS);
|
||||
define('GRAV_CACHE_PATH', $path);
|
||||
}
|
||||
// Absolute or relative path to logs folder. Defaults to GRAV_ROOT/logs
|
||||
if (!defined('GRAV_LOG_PATH')) {
|
||||
$path = rtrim(getenv('GRAV_LOG_PATH') ?: 'logs', DS);
|
||||
define('GRAV_LOG_PATH', $path);
|
||||
}
|
||||
// Absolute or relative path to tmp folder. Defaults to GRAV_ROOT/tmp
|
||||
if (!defined('GRAV_TMP_PATH')) {
|
||||
$path = rtrim(getenv('GRAV_TMP_PATH') ?: 'tmp', DS);
|
||||
define('GRAV_TMP_PATH', $path);
|
||||
}
|
||||
// Absolute or relative path to backup folder. Defaults to GRAV_ROOT/backup
|
||||
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_PATH);
|
||||
// INTERNAL: Do not use!
|
||||
define('USER_DIR', GRAV_WEBROOT . '/' . GRAV_USER_PATH . '/');
|
||||
define('CACHE_DIR', (!preg_match('`^(/|[a-z]:[\\\/])`ui', GRAV_CACHE_PATH) ? GRAV_ROOT . '/' : '') . GRAV_CACHE_PATH . '/');
|
||||
|
||||
// DEPRECATED: Do not use!
|
||||
define('ASSETS_DIR', ROOT_DIR . 'assets/');
|
||||
define('IMAGES_DIR', ROOT_DIR . 'images/');
|
||||
define('ACCOUNTS_DIR', USER_DIR .'accounts/');
|
||||
define('PAGES_DIR', USER_DIR .'pages/');
|
||||
define('DATA_DIR', USER_DIR .'data/');
|
||||
define('SYSTEM_DIR', ROOT_DIR .'system/');
|
||||
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 . GRAV_LOG_PATH . DS);
|
||||
define('CACHE_PATH', GRAV_CACHE_PATH . DS);
|
||||
define('USER_PATH', GRAV_USER_PATH . DS);
|
||||
define('ROOT_DIR', GRAV_ROOT . DS);
|
||||
define('ASSETS_DIR', GRAV_WEBROOT . '/assets/');
|
||||
define('IMAGES_DIR', GRAV_WEBROOT . '/images/');
|
||||
define('ACCOUNTS_DIR', USER_DIR . 'accounts/');
|
||||
define('PAGES_DIR', USER_DIR . 'pages/');
|
||||
define('DATA_DIR', USER_DIR . 'data/');
|
||||
define('PLUGINS_DIR', USER_DIR . 'plugins/');
|
||||
define('THEMES_DIR', USER_DIR . 'themes/');
|
||||
define('SYSTEM_DIR', (!preg_match('`^(/|[a-z]:[\\\/])`ui', GRAV_SYSTEM_PATH) ? GRAV_ROOT . '/' : '') . GRAV_SYSTEM_PATH . '/');
|
||||
define('LIB_DIR', SYSTEM_DIR . 'src/');
|
||||
define('VENDOR_DIR', GRAV_ROOT . '/vendor/');
|
||||
define('LOG_DIR', (!preg_match('`^(/|[a-z]:[\\\/])`ui', GRAV_LOG_PATH) ? GRAV_ROOT . '/' : '') . GRAV_LOG_PATH . '/');
|
||||
// END DEPRECATED
|
||||
|
||||
// Some extensions
|
||||
@@ -81,3 +99,6 @@ define('RAW_CONTENT', 1);
|
||||
define('TWIG_CONTENT', 2);
|
||||
define('TWIG_CONTENT_LIST', 3);
|
||||
define('TWIG_TEMPLATES', 4);
|
||||
|
||||
// Filters
|
||||
define('GRAV_SANITIZE_STRING', 5001);
|
||||
|
||||
BIN
system/images/watermark.png
Normal file
BIN
system/images/watermark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 94 KiB |
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* @package Grav\Core
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,43 @@ if (!defined('GRAV_ROOT')) {
|
||||
die();
|
||||
}
|
||||
|
||||
// Check if Install class is already loaded (from an older Grav version)
|
||||
// This happens when upgrading from older versions where the OLD Install class
|
||||
// was loaded via autoloader before extracting the update package (e.g., via Install::forceSafeUpgrade())
|
||||
$logInstallerSource = static function ($install, string $source) {
|
||||
$sourceLabel = $source === 'extracted update package' ? 'update package' : 'existing installation';
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
echo sprintf(" |- Using installer from %s\n", $sourceLabel);
|
||||
}
|
||||
};
|
||||
|
||||
if (class_exists('Grav\\Installer\\Install', false)) {
|
||||
// OLD Install class is already loaded. We cannot load the NEW one due to PHP limitations.
|
||||
// However, we can work around this by:
|
||||
// 1. Using a different class name for the NEW installer
|
||||
// 2. Or, accepting that the OLD Install class will run but ensuring it can still upgrade properly
|
||||
|
||||
// For now, use the OLD Install class but set its location to this extracted package
|
||||
// so it processes files from here
|
||||
$install = Grav\Installer\Install::instance();
|
||||
|
||||
// Use reflection to update the location property to point to this package
|
||||
$reflection = new \ReflectionClass($install);
|
||||
if ($reflection->hasProperty('location')) {
|
||||
$locationProp = $reflection->getProperty('location');
|
||||
$locationProp->setAccessible(true);
|
||||
$locationProp->setValue($install, __DIR__ . '/..');
|
||||
}
|
||||
|
||||
$logInstallerSource($install, 'existing installation');
|
||||
|
||||
return $install;
|
||||
}
|
||||
|
||||
// Normal case: Install class not yet loaded, load the NEW one
|
||||
require_once __DIR__ . '/src/Grav/Installer/Install.php';
|
||||
|
||||
return Grav\Installer\Install::instance();
|
||||
$install = Grav\Installer\Install::instance();
|
||||
$logInstallerSource($install, 'extracted update package');
|
||||
|
||||
return $install;
|
||||
|
||||
@@ -51,6 +51,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>فشل التحقق من صحة:</b>'
|
||||
INVALID_INPUT: 'إدخال غير صحيح في'
|
||||
MISSING_REQUIRED_FIELD: 'حقل مطلوب مفقود:'
|
||||
XSS_ISSUES: "مشاكل XSS محتملة تم اكتشافها في حقل '%s' '"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'كانون الثاني'
|
||||
- 'شباط'
|
||||
@@ -72,6 +73,8 @@ GRAV:
|
||||
- 'الجمعة'
|
||||
- 'السبت'
|
||||
- 'الأحد'
|
||||
YES: "نعم"
|
||||
NO: "لا"
|
||||
CRON:
|
||||
EVERY: كل
|
||||
EVERY_HOUR: كل ساعة
|
||||
@@ -80,3 +83,11 @@ GRAV:
|
||||
EVERY_DAY_OF_MONTH: كل يوم في الشهر
|
||||
EVERY_MONTH: ' كل شهر'
|
||||
TEXT_PERIOD: كل <b />
|
||||
TEXT_MINS: ' في <b /> دقيقة(دقائق) بعد الساعة'
|
||||
TEXT_TIME: ' في <b />:<b />'
|
||||
TEXT_DOW: ' في <b />'
|
||||
TEXT_MONTH: ' من <b />'
|
||||
TEXT_DOM: ' في <b />'
|
||||
ERROR1: الوسم %s غير مدعوم!
|
||||
ERROR2: عدد عناصر غير صالح.
|
||||
ERROR4: تعبير غير معروف
|
||||
|
||||
@@ -5,6 +5,7 @@ GRAV:
|
||||
BAD_DATE: Невалидна дата
|
||||
AGO: преди
|
||||
FROM_NOW: от сега
|
||||
JUST_NOW: току що
|
||||
SECOND: секунда
|
||||
MINUTE: минута
|
||||
HOUR: час
|
||||
@@ -60,3 +61,12 @@ GRAV:
|
||||
- 'петък'
|
||||
- 'събота'
|
||||
- 'неделя'
|
||||
YES: "Да"
|
||||
NO: "Не"
|
||||
CRON:
|
||||
EVERY: всеки
|
||||
EVERY_HOUR: Всеки час
|
||||
EVERY_MINUTE: Всяка минута
|
||||
EVERY_DAY_OF_WEEK: Всеки ден от седмицата
|
||||
EVERY_DAY_OF_MONTH: Всеки ден от месеца
|
||||
EVERY_MONTH: Всеки месец
|
||||
|
||||
@@ -2,19 +2,20 @@
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# S'ha produït un error: Frontmatter invàlid\n\nRuta: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- ''
|
||||
- 'informació'
|
||||
- 'rice'
|
||||
- 'money'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- 'fish'
|
||||
- 'sheep'
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: No s'ha proporcionat data
|
||||
BAD_DATE: Data invàlida
|
||||
AGO: abans
|
||||
FROM_NOW: des d'ara
|
||||
JUST_NOW: Ara mateix
|
||||
SECOND: segon
|
||||
MINUTE: minut
|
||||
HOUR: hora
|
||||
@@ -48,6 +49,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Ha fallat la validació:</b>'
|
||||
INVALID_INPUT: 'Entrada no vàlida a'
|
||||
MISSING_REQUIRED_FIELD: 'Falta camp obligatori:'
|
||||
XSS_ISSUES: "Detectats potencials problemes XSS al camp '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Gener'
|
||||
- 'Febrer'
|
||||
@@ -69,3 +71,17 @@ GRAV:
|
||||
- 'Divendres'
|
||||
- 'Dissabte'
|
||||
- 'Diumenge'
|
||||
YES: "Sí"
|
||||
NO: "No"
|
||||
CRON:
|
||||
EVERY: cada
|
||||
EVERY_HOUR: cada hora
|
||||
EVERY_MINUTE: cada minut
|
||||
EVERY_DAY_OF_WEEK: cada dia de la setmana
|
||||
EVERY_DAY_OF_MONTH: cada dia del mes
|
||||
EVERY_MONTH: cada mes
|
||||
TEXT_PERIOD: Cada <b />
|
||||
ERROR1: L'etiqueta %s no està suportada!
|
||||
ERROR2: Nombre d'elements incorrecte
|
||||
ERROR3: El jquery_element s'ha d'establir a la configuració de jqCron
|
||||
ERROR4: Expressió no reconeguda
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
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```"
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Chyba: Chybná hlavička\n\nCesta: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_PLURALS:
|
||||
'/(quiz)$/i': '\1zes'
|
||||
'/^(ox)$/i': '\1en'
|
||||
|
||||
@@ -119,3 +119,10 @@ GRAV:
|
||||
ERROR2: Bad number of elements
|
||||
ERROR3: The jquery_element should be set into jqCron settings
|
||||
ERROR4: Unrecognized expression
|
||||
|
||||
PLUGIN_ADMIN:
|
||||
UPDATES_SECTION: Updates
|
||||
SAFE_UPGRADE: Safe self-upgrade
|
||||
SAFE_UPGRADE_HELP: When enabled, Grav core updates use staged installation with automatic rollback support.
|
||||
SAFE_UPGRADE_SNAPSHOT_LIMIT: Safe-upgrade snapshots to keep
|
||||
SAFE_UPGRADE_SNAPSHOT_LIMIT_HELP: Maximum number of snapshots to retain for safe upgrades (0 disables pruning).
|
||||
|
||||
40
system/languages/eo.yaml
Normal file
40
system/languages/eo.yaml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Eraro: Nevalida Frontmatter\n\nVojo: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_PLURALS:
|
||||
'/sis$/i': 'j'
|
||||
NICETIME:
|
||||
FROM_NOW: ekde nun
|
||||
JUST_NOW: Ĝuste nun
|
||||
SECOND: sekundo
|
||||
MINUTE: minuto
|
||||
HOUR: horo
|
||||
DAY: tago
|
||||
WEEK: semajno
|
||||
MONTH: monato
|
||||
YEAR: jaro
|
||||
DECADE: jardeko
|
||||
SEC: sek.
|
||||
MIN: min.
|
||||
HR: horo
|
||||
SECOND_PLURAL: sekundoj
|
||||
MINUTE_PLURAL: minutoj
|
||||
HOUR_PLURAL: horoj
|
||||
DAY_PLURAL: tagoj
|
||||
WEEK_PLURAL: semajnoj
|
||||
MONTH_PLURAL: monatoj
|
||||
YEAR_PLURAL: jaroj
|
||||
DECADE_PLURAL: jardekoj
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'januaro'
|
||||
- 'februaro'
|
||||
- 'marto'
|
||||
- 'aprilo'
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
@@ -21,9 +21,9 @@ GRAV:
|
||||
'sex': 'sexos'
|
||||
'move': 'movido'
|
||||
INFLECTOR_ORDINALS:
|
||||
'first': 'ro'
|
||||
'second': 'do'
|
||||
'third': 'ro'
|
||||
'first': '.º'
|
||||
'second': '.º'
|
||||
'third': '.º'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: No se proporcionó fecha
|
||||
BAD_DATE: Fecha errónea
|
||||
@@ -44,7 +44,7 @@ GRAV:
|
||||
WK: sem
|
||||
MO: mes
|
||||
YR: año
|
||||
DEC: dic
|
||||
DEC: déc
|
||||
SECOND_PLURAL: segundos
|
||||
MINUTE_PLURAL: minutos
|
||||
HOUR_PLURAL: horas
|
||||
@@ -64,7 +64,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Falló la validación: </b>'
|
||||
INVALID_INPUT: 'Dato inválido en: '
|
||||
MISSING_REQUIRED_FIELD: 'Falta el campo requerido: '
|
||||
XSS_ISSUES: "Se detectaron problemas XSS potenciales en el campo '%s'"
|
||||
XSS_ISSUES: "Se detectaron potenciales problemas XSS en el campo '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Enero'
|
||||
- 'Febrero'
|
||||
@@ -86,7 +86,7 @@ GRAV:
|
||||
- 'Viernes'
|
||||
- 'Sábado'
|
||||
- 'Domingo'
|
||||
YES: "Si"
|
||||
YES: "Sí"
|
||||
NO: "No"
|
||||
CRON:
|
||||
EVERY: cada
|
||||
@@ -96,12 +96,12 @@ GRAV:
|
||||
EVERY_DAY_OF_MONTH: cada día del mes
|
||||
EVERY_MONTH: cada mes
|
||||
TEXT_PERIOD: Cada <b />
|
||||
TEXT_MINS: ' a <b /> minuto(s) despues de la hora'
|
||||
TEXT_MINS: ' a <b /> minuto(s) después de la hora'
|
||||
TEXT_TIME: ' a <b />:<b />'
|
||||
TEXT_DOW: ' en <b />'
|
||||
TEXT_MONTH: ' de<b />'
|
||||
TEXT_DOM: ' en<b />'
|
||||
ERROR1: La etiqueta %s no está soportada!
|
||||
ERROR2: El número de elementos es erroneo
|
||||
ERROR1: No se admite la etiqueta %s.
|
||||
ERROR2: El número de elementos es erróneo
|
||||
ERROR3: El jquery_element debería establecerse en la configuración del jqCron
|
||||
ERROR4: Expresión no reconocida
|
||||
|
||||
@@ -13,12 +13,12 @@ GRAV:
|
||||
'/(tive)s$/i': '\1'
|
||||
'/(hive)s$/i': '\1'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- ''
|
||||
- 'informatsioon'
|
||||
- 'riis'
|
||||
- 'raha'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- ''
|
||||
- ''
|
||||
- 'kala'
|
||||
- 'lammas'
|
||||
INFLECTOR_IRREGULAR:
|
||||
@@ -70,6 +70,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Kinnitamine nurjus:</b>'
|
||||
INVALID_INPUT: 'Vigane sisend:'
|
||||
MISSING_REQUIRED_FIELD: 'Nõutud väli puudub:'
|
||||
XSS_ISSUES: "Tuvastasime '%s' väljal võimaliku XSS-riski"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'jaanuar'
|
||||
- 'veebruar'
|
||||
@@ -91,11 +92,14 @@ GRAV:
|
||||
- 'reede'
|
||||
- 'laupäev'
|
||||
- 'pühapäev'
|
||||
YES: "Jah"
|
||||
NO: "Ei"
|
||||
CRON:
|
||||
EVERY: iga
|
||||
EVERY_HOUR: iga tund
|
||||
EVERY_MINUTE: iga minut
|
||||
EVERY_DAY_OF_WEEK: iga nädala päev
|
||||
EVERY_DAY_OF_WEEK: nädala igal päeval
|
||||
EVERY_DAY_OF_MONTH: kuu igal päeval
|
||||
EVERY_MONTH: iga kuu
|
||||
TEXT_PERIOD: Iga <b />
|
||||
ERROR1: Silt %s pole toetatud!
|
||||
|
||||
@@ -45,12 +45,12 @@ GRAV:
|
||||
'/([ti])a$/i': '\1um'
|
||||
'/(n)ews$/i': '\1ews'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- 'information'
|
||||
- ''
|
||||
- ''
|
||||
- 'riisi'
|
||||
- 'raha'
|
||||
- 'lajit'
|
||||
- 'series'
|
||||
- ''
|
||||
- 'kala'
|
||||
- 'lammas'
|
||||
INFLECTOR_IRREGULAR:
|
||||
|
||||
@@ -22,8 +22,28 @@ GRAV:
|
||||
'/$/': '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': '\1ouvelles'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'équipement'
|
||||
- 'information'
|
||||
@@ -58,10 +78,10 @@ GRAV:
|
||||
MONTH: mois
|
||||
YEAR: année
|
||||
DECADE: décennie
|
||||
SEC: s
|
||||
MIN: m
|
||||
HR: h
|
||||
WK: sem
|
||||
SEC: sec.
|
||||
MIN: min.
|
||||
HR: hr.
|
||||
WK: sem.
|
||||
MO: m
|
||||
YR: an
|
||||
DEC: déc
|
||||
@@ -84,6 +104,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>La validation a échoué :</b>'
|
||||
INVALID_INPUT: 'Saisie non valide'
|
||||
MISSING_REQUIRED_FIELD: 'Champ obligatoire manquant :'
|
||||
XSS_ISSUES: "Erreurs XSS probablement détectées dans le champ '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'janvier'
|
||||
- 'février'
|
||||
@@ -105,6 +126,8 @@ GRAV:
|
||||
- 'vendredi'
|
||||
- 'samedi'
|
||||
- 'dimanche'
|
||||
YES: "Oui"
|
||||
NO: "Non"
|
||||
CRON:
|
||||
EVERY: chaque
|
||||
EVERY_HOUR: toutes les heures
|
||||
@@ -118,7 +141,7 @@ GRAV:
|
||||
TEXT_DOW: ' sur <b/>'
|
||||
TEXT_MONTH: ' de <b />'
|
||||
TEXT_DOM: ' sur <b/>'
|
||||
ERROR1: La balise %s n'est pas supportée!
|
||||
ERROR1: La balise %s n'est pas prise en charge !
|
||||
ERROR2: Nombre invalide d'éléments
|
||||
ERROR3: L'élément jquery_element doit être défini dans les paramètres jqCron
|
||||
ERROR4: Expression non reconnue
|
||||
|
||||
@@ -104,6 +104,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Fallou a validación:</b>'
|
||||
INVALID_INPUT: 'Entrada incorrecta en'
|
||||
MISSING_REQUIRED_FIELD: 'Falta un campo requirido:'
|
||||
XSS_ISSUES: "Detectáronse posibles problemas XSS no campo '% s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'xaneiro'
|
||||
- 'febreiro'
|
||||
@@ -125,6 +126,8 @@ GRAV:
|
||||
- 'venres'
|
||||
- 'sábado'
|
||||
- 'domingo'
|
||||
YES: "Si"
|
||||
NO: "Non"
|
||||
CRON:
|
||||
EVERY: cada
|
||||
EVERY_HOUR: Cada hora
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nכותרת: %1$s\n---\n# שגיאה: Fronmatter לא חוקי\nנתיב: `%2$s`\n**%3$s**\n```\n%4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'ציוד'
|
||||
- 'מידע'
|
||||
- 'אורז'
|
||||
- 'כסף'
|
||||
- 'מינים'
|
||||
- 'סדרה'
|
||||
- 'דג'
|
||||
- 'כבשה'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'אנשים'
|
||||
'man': 'גברים'
|
||||
'child': 'ילדים'
|
||||
'sex': 'מינים'
|
||||
'move': 'מהלכים'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: לא סופק תאריך
|
||||
BAD_DATE: תאריך פגום
|
||||
AGO: לפני
|
||||
FROM_NOW: כרגע
|
||||
JUST_NOW: כרגע
|
||||
SECOND: שנייה
|
||||
MINUTE: דקה
|
||||
HOUR: שעה
|
||||
@@ -40,6 +56,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>האימות נכשל:</b>'
|
||||
INVALID_INPUT: 'קלט לא חוקי'
|
||||
MISSING_REQUIRED_FIELD: 'שדות חובה חסרים:'
|
||||
XSS_ISSUES: "בעיות XSS פוטנציאליות זוהו בשדה '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'ינואר'
|
||||
- 'פברואר'
|
||||
@@ -61,3 +78,22 @@ GRAV:
|
||||
- 'שישי'
|
||||
- 'שבת'
|
||||
- 'ראשון'
|
||||
YES: "כן"
|
||||
NO: "לא"
|
||||
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: 'ב <b />'
|
||||
TEXT_MONTH: 'של <b />'
|
||||
TEXT_DOM: 'ב <b />'
|
||||
ERROR1: התגית %s אינו נתמכת
|
||||
ERROR2: מספר לא חוקי של משתנים.
|
||||
ERROR3: יש להגדיר את ה-jquery_element להגדרות jqCron
|
||||
ERROR4: ביטוי לא מזוהה
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nnaslov: %1$s\n---\n\n# Pogreška: nevažeći frontmatter\n\nPutanja datoteke: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'oprema'
|
||||
- 'informacije'
|
||||
- 'informacija'
|
||||
- 'riža'
|
||||
- 'novac'
|
||||
- 'vrsta'
|
||||
@@ -15,11 +16,17 @@ GRAV:
|
||||
'child': 'djeca'
|
||||
'sex': 'spolovi'
|
||||
'move': 'Pomakni'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': '.'
|
||||
'first': '.'
|
||||
'second': '.'
|
||||
'third': '.'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Datum nije upisan
|
||||
BAD_DATE: Pogrešan datum
|
||||
AGO: prije
|
||||
FROM_NOW: od sada
|
||||
JUST_NOW: upravo sad
|
||||
SECOND: sekunda
|
||||
MINUTE: minuta
|
||||
HOUR: sat
|
||||
@@ -29,6 +36,7 @@ GRAV:
|
||||
YEAR: godina
|
||||
DECADE: desetljeće
|
||||
SEC: sek
|
||||
MIN: min
|
||||
HR: sat
|
||||
WK: t
|
||||
MO: m
|
||||
@@ -53,6 +61,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Validacija nije uspjela:</b>'
|
||||
INVALID_INPUT: 'Pogrešan unos u'
|
||||
MISSING_REQUIRED_FIELD: 'Nedostaje obavezno polje:'
|
||||
XSS_ISSUES: "Potencijalni XSS problemi otkriveni u polju '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Siječanj'
|
||||
- 'Veljača'
|
||||
@@ -74,3 +83,22 @@ GRAV:
|
||||
- 'Petak'
|
||||
- 'Subota'
|
||||
- 'Nedjelja'
|
||||
YES: "Da"
|
||||
NO: "Ne"
|
||||
CRON:
|
||||
EVERY: svaki
|
||||
EVERY_HOUR: svaki sat
|
||||
EVERY_MINUTE: svake minute
|
||||
EVERY_DAY_OF_WEEK: svaki dan u tjednu
|
||||
EVERY_DAY_OF_MONTH: svaki dan u mjesecu
|
||||
EVERY_MONTH: svaki mjesec
|
||||
TEXT_PERIOD: Svakih <b />
|
||||
TEXT_MINS: ' u <b /> minut(e) nakon sata'
|
||||
TEXT_TIME: ' u <b />:<b />'
|
||||
TEXT_DOW: ' na <b />'
|
||||
TEXT_MONTH: ' <b />'
|
||||
TEXT_DOM: ' na <b />'
|
||||
ERROR1: Oznaka %s nije podržana!
|
||||
ERROR2: Pogrešan broj elemenata.
|
||||
ERROR3: jquery_element treba postaviti u postavke jqCron
|
||||
ERROR4: Izraz nije prepoznat
|
||||
|
||||
@@ -3,26 +3,72 @@ GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Error: Frontmatter tidak valid\n\nLokasi: `%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:
|
||||
- 'peralatan'
|
||||
- 'informasi'
|
||||
- 'nasi'
|
||||
- 'uang'
|
||||
- 'spesies'
|
||||
- 'rangkaian'
|
||||
- 'ikan'
|
||||
- 'domba'
|
||||
- 'Peralatan'
|
||||
- 'Informasi '
|
||||
- 'Nasi'
|
||||
- 'Uang'
|
||||
- 'Jenis'
|
||||
- 'Seri'
|
||||
- 'Ikan'
|
||||
- 'Domba'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'orang-orang'
|
||||
'man': 'laki-laki'
|
||||
'child': 'anak-anak'
|
||||
'sex': 'jenis kelamin'
|
||||
'person': 'Orang-orang'
|
||||
'man': 'Pria'
|
||||
'child': 'Balita'
|
||||
'sex': 'Jenis Kelamin'
|
||||
'move': 'pindahkan'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': 'ke'
|
||||
'first': 'pertama'
|
||||
'second': 'nd'
|
||||
'third': 'rd'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Tanggal tidak tersedia
|
||||
NO_DATE_PROVIDED: Tidak ada tanggal yang disediakan
|
||||
BAD_DATE: Format tanggal salah
|
||||
AGO: yang lalu
|
||||
FROM_NOW: dari saat ini
|
||||
FROM_NOW: dari sekarang
|
||||
JUST_NOW: baru saja
|
||||
SECOND: detik
|
||||
MINUTE: menit
|
||||
@@ -32,12 +78,12 @@ GRAV:
|
||||
MONTH: bulan
|
||||
YEAR: tahun
|
||||
DECADE: dekade
|
||||
SEC: dtk
|
||||
MIN: mnt
|
||||
HR: j
|
||||
WK: mng
|
||||
MO: bln
|
||||
YR: thn
|
||||
SEC: detik
|
||||
MIN: menit
|
||||
HR: ' jam'
|
||||
WK: minggu
|
||||
MO: bulan
|
||||
YR: tahun
|
||||
DEC: desimal
|
||||
SECOND_PLURAL: detik
|
||||
MINUTE_PLURAL: menit
|
||||
@@ -47,17 +93,18 @@ GRAV:
|
||||
MONTH_PLURAL: bulan
|
||||
YEAR_PLURAL: tahun
|
||||
DECADE_PLURAL: dekade
|
||||
SEC_PLURAL: dtk
|
||||
MIN_PLURAL: mnt
|
||||
HR_PLURAL: j
|
||||
WK_PLURAL: mgg
|
||||
MO_PLURAL: bln
|
||||
YR_PLURAL: thn
|
||||
SEC_PLURAL: detik
|
||||
MIN_PLURAL: menit
|
||||
HR_PLURAL: jam
|
||||
WK_PLURAL: minggu
|
||||
MO_PLURAL: bulan
|
||||
YR_PLURAL: tahun
|
||||
DEC_PLURAL: dekade
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b>Validasi gagal:</b>'
|
||||
INVALID_INPUT: 'Input tidak valid di'
|
||||
MISSING_REQUIRED_FIELD: 'Data yang diperlukan belum terisi:'
|
||||
XSS_ISSUES: "Isu berpotensial XSS terdeteksi dalam baris %s"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Januari'
|
||||
- 'Februari'
|
||||
@@ -76,22 +123,25 @@ GRAV:
|
||||
- 'Selasa'
|
||||
- 'Rabu'
|
||||
- 'Kamis'
|
||||
- 'Jumat'
|
||||
- 'Jum''at'
|
||||
- 'Sabtu'
|
||||
- 'Minggu'
|
||||
YES: "Ya"
|
||||
NO: "Tidak"
|
||||
CRON:
|
||||
EVERY: Setiap
|
||||
EVERY_HOUR: Setiap jam
|
||||
EVERY_MINUTE: Setiap menit
|
||||
EVERY_DAY_OF_WEEK: Setiap hari selama seminggu
|
||||
EVERY_DAY_OF_MONTH: pada tanggal setiap bulannya
|
||||
EVERY_DAY_OF_MONTH: Setiap hari dalam sebulan
|
||||
EVERY_MONTH: setiap bulan
|
||||
TEXT_PERIOD: Setiap <b />
|
||||
TEXT_MINS: 'dalam <b /> menit setelah jam yang lalu'
|
||||
TEXT_TIME: ' pada <b />:<b />'
|
||||
TEXT_DOW: ' pada <b />'
|
||||
TEXT_MONTH: ' pada <b />'
|
||||
TEXT_DOM: ' pada <b />'
|
||||
ERROR1: Tag %s tidak didukung!
|
||||
ERROR2: Jumlah elemen tidak valid
|
||||
ERROR3: jquery_element harus ditetapkan ke pengaturan jqCron
|
||||
ERROR4: Ekspresi tidak dikenali
|
||||
ERROR2: Jumlah elemen yang buruk
|
||||
ERROR3: jquery_element harus diatur ke dalam pengaturan jqCron
|
||||
ERROR4: Ekspresi tidak dikenal
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitill: %1$s\n---\n\n# Villa: Ógilt efni á forsíðu\n\nSlóð: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- ''
|
||||
- 'upplýsingar'
|
||||
- 'rice'
|
||||
- 'money'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- 'fish'
|
||||
- 'sheep'
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Engin dagsetning gefin
|
||||
BAD_DATE: Röng dagsetning
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
GRAV:
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- ''
|
||||
- '情報'
|
||||
- 'rice'
|
||||
- ''
|
||||
- 'お金'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- ''
|
||||
- ''
|
||||
- '魚'
|
||||
- 'ヒツジ'
|
||||
INFLECTOR_IRREGULAR:
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
---
|
||||
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```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- '장비'
|
||||
- '정보'
|
||||
- ''
|
||||
- ''
|
||||
- ''
|
||||
- '시리즈'
|
||||
- '물고기'
|
||||
- ''
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': '사람들'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: 제공된 날짜가 없습니다
|
||||
BAD_DATE: 잘못된 날짜
|
||||
AGO: 전
|
||||
FROM_NOW: 후
|
||||
JUST_NOW: 방금
|
||||
SECOND: 초
|
||||
MINUTE: 분
|
||||
HOUR: 시간
|
||||
@@ -40,6 +52,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>유효성 검사 실패:</b>'
|
||||
INVALID_INPUT: '잘못된 입력'
|
||||
MISSING_REQUIRED_FIELD: '누락 된 필수 필드:'
|
||||
XSS_ISSUES: "'%s' 필드에서 잠재적인 XSS 문제가 감지되었습니다."
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- '일월'
|
||||
- '이월'
|
||||
@@ -61,3 +74,17 @@ GRAV:
|
||||
- '금요일'
|
||||
- '토요일'
|
||||
- '일요일'
|
||||
YES: "네"
|
||||
NO: "아니요"
|
||||
CRON:
|
||||
EVERY: 모두
|
||||
EVERY_HOUR: 매 시간
|
||||
EVERY_MINUTE: 매 분
|
||||
EVERY_DAY_OF_WEEK: 일주일간 매일
|
||||
EVERY_DAY_OF_MONTH: 일개월간 매일
|
||||
EVERY_MONTH: 매달
|
||||
TEXT_PERIOD: 모든 <b />
|
||||
ERROR1: '%s 태그는 지원되지 않습니다. '
|
||||
ERROR2: 잘못된 요소 수
|
||||
ERROR3: jquery_element는 jqCron 설정에서 설정할 수 있습니다.
|
||||
ERROR4: 인식할 수 없는 표현
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Klaida: klaidinga įžanginė konfigūracija\n\nPath: `%2$s`\n\n**%3$s**\n\n```\n %4$s\n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'equipment'
|
||||
- 'information'
|
||||
- ''
|
||||
- ''
|
||||
- 'ryžiai'
|
||||
- 'pinigai'
|
||||
- 'prieskoniai'
|
||||
|
||||
84
system/languages/lv.yaml
Normal file
84
system/languages/lv.yaml
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nNosaukums: %1$s\n---\n\n# Kļūda: Nederīgs Frontmatter\n\nCeļš: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': '.'
|
||||
'first': '.'
|
||||
'second': '.'
|
||||
'third': '.'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Nav norādīts datums
|
||||
BAD_DATE: Nederīgs datums
|
||||
AGO: iepriekš
|
||||
FROM_NOW: no šī brīža
|
||||
JUST_NOW: tikko
|
||||
SECOND: sekundes
|
||||
MINUTE: minūte
|
||||
HOUR: stunda
|
||||
DAY: diena
|
||||
WEEK: nedēļa
|
||||
MONTH: mēnesis
|
||||
YEAR: gads
|
||||
DECADE: dekāde
|
||||
SEC: s
|
||||
MIN: m
|
||||
HR: st
|
||||
WK: ned
|
||||
MO: mēn.
|
||||
YR: g.
|
||||
DEC: dec
|
||||
SECOND_PLURAL: sekundes
|
||||
MINUTE_PLURAL: minūtes
|
||||
HOUR_PLURAL: stundas
|
||||
DAY_PLURAL: dienas
|
||||
WEEK_PLURAL: nedēļas
|
||||
MONTH_PLURAL: mēneši
|
||||
YEAR_PLURAL: gadi
|
||||
DECADE_PLURAL: desmitgades
|
||||
SEC_PLURAL: s
|
||||
MIN_PLURAL: m
|
||||
HR_PLURAL: st.
|
||||
WK_PLURAL: ned.
|
||||
MO_PLURAL: mēn.
|
||||
YR_PLURAL: g.
|
||||
DEC_PLURAL: d
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b>Validācija neizdevās:</b>'
|
||||
INVALID_INPUT: 'Nederīga ievade'
|
||||
MISSING_REQUIRED_FIELD: 'Laukā trūkst datu'
|
||||
XSS_ISSUES: "Atrastas iespējamas XSS problēmas laukā '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Janvāris'
|
||||
- 'Februāris'
|
||||
- 'Marts'
|
||||
- 'Aprīlis'
|
||||
- 'Maijs'
|
||||
- 'Jūnijs'
|
||||
- 'Jūlijs'
|
||||
- 'Augusts'
|
||||
- 'Septembris'
|
||||
- 'Oktobris'
|
||||
- 'Novembris'
|
||||
- 'Decembris'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- 'Pirmdiena'
|
||||
- 'Otrdiena'
|
||||
- 'Trešdiena'
|
||||
- 'Ceturtdiena'
|
||||
- 'Piektdiena'
|
||||
- 'Sestdiena'
|
||||
- 'Svētdiena'
|
||||
YES: "Jā"
|
||||
NO: "Nē"
|
||||
CRON:
|
||||
EVERY: katru
|
||||
EVERY_HOUR: katru stundu
|
||||
EVERY_MINUTE: katru minūti
|
||||
EVERY_DAY_OF_WEEK: katru nedēļas dienu
|
||||
EVERY_DAY_OF_MONTH: katru mēneša dienu
|
||||
EVERY_MONTH: katru mēnesi
|
||||
TEXT_PERIOD: Katru <b />
|
||||
ERROR1: Marķieris %s nav atbalstīts!
|
||||
ERROR2: Nederīgs elementu skaits
|
||||
ERROR3: jquery_element nevajadzētu definēt jqCron iestatījumos
|
||||
ERROR4: Neatpazīta izteiksme
|
||||
147
system/languages/mn.yaml
Normal file
147
system/languages/mn.yaml
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
GRAV:
|
||||
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': '\1зүүд'
|
||||
'/^(ox)$/i': '\1ууд'
|
||||
'/([m|l])ouse$/i': '\1ууд'
|
||||
'/(matr|vert|ind)ix|ex$/i': '\1иксүүд'
|
||||
'/(x|ch|ss|sh)$/i': '\1үүд'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1үүд'
|
||||
'/([^aeiouy]|qu)y$/i': '\1үүд'
|
||||
'/(hive)$/i': '\1үүд'
|
||||
'/(?:([^f])fe|([lr])f)$/i': '\1\2үүд'
|
||||
'/sis$/i': 'үүд'
|
||||
'/([ti])um$/i': '\1үүд'
|
||||
'/(buffal|tomat)o$/i': '\1үүд'
|
||||
'/(bu)s$/i': '\1үүд'
|
||||
'/(alias|status)/i': '\1үүд'
|
||||
'/(octop|vir)us$/i': '\1үүд'
|
||||
'/(ax|test)is$/i': '\1үүд'
|
||||
'/s$/i': 'үүд'
|
||||
'/$/': 'үүд'
|
||||
INFLECTOR_SINGULAR:
|
||||
'/(quiz)zes$/i': '\1'
|
||||
'/(matr)ices$/i': '\1икс'
|
||||
'/(vert|ind)ices$/i': '\1икс'
|
||||
'/^(ox)en/i': '\1'
|
||||
'/(alias|status)es$/i': '\1'
|
||||
'/([octop|vir])i$/i': '\1'
|
||||
'/(cris|ax|test)es$/i': '\1'
|
||||
'/(shoe)s$/i': '\1'
|
||||
'/(o)es$/i': '\1'
|
||||
'/(bus)es$/i': '\1'
|
||||
'/([m|l])ice$/i': '\1'
|
||||
'/(x|ch|ss|sh)es$/i': '\1'
|
||||
'/(m)ovies$/i': '\1'
|
||||
'/(s)eries$/i': '\1'
|
||||
'/([^aeiouy]|qu)ies$/i': '\1үүд'
|
||||
'/([lr])ves$/i': '\1'
|
||||
'/(tive)s$/i': '\1'
|
||||
'/(hive)s$/i': '\1'
|
||||
'/([^f])ves$/i': '\1'
|
||||
'/(^analy)ses$/i': '\1'
|
||||
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2үүд'
|
||||
'/([ti])a$/i': '\1'
|
||||
'/(n)ews$/i': '\1'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'тоног төхөөрөмж'
|
||||
- 'Мэдээлэл'
|
||||
- 'будаа'
|
||||
- 'мөнгө'
|
||||
- 'төрөл зүйл'
|
||||
- 'цуврал'
|
||||
- 'загас'
|
||||
- 'хонь'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'хүмүүс'
|
||||
'man': 'эрчүүд'
|
||||
'child': 'хүүхэд'
|
||||
'sex': 'хүйс'
|
||||
'move': 'хөдөлгөөн'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': 'th'
|
||||
'first': 'st'
|
||||
'second': 'nd'
|
||||
'third': 'rd'
|
||||
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: долоо хоногууд
|
||||
MONTH_PLURAL: сарууд
|
||||
YEAR_PLURAL: онууд
|
||||
DECADE_PLURAL: арван жилүүд
|
||||
SEC_PLURAL: сек.-үүд
|
||||
MIN_PLURAL: мин.-ууд
|
||||
HR_PLURAL: цагууд
|
||||
WK_PLURAL: д.х.-ууд
|
||||
MO_PLURAL: сарууд
|
||||
YR_PLURAL: жилүүд
|
||||
DEC_PLURAL: арван жилүүд
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b>Баталгаажуулалт амжилтгүй боллоо:</b>'
|
||||
INVALID_INPUT: 'Буруу өгөгдөл дараахид'
|
||||
MISSING_REQUIRED_FIELD: 'Шаардлагатай талбар дутуу байна:'
|
||||
XSS_ISSUES: "'%s' талбарт XSS -ийн болзошгүй асуудлууд илэрсэн"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- '1-р сар'
|
||||
- '2-р сар'
|
||||
- '3-р сар'
|
||||
- '4-р сар'
|
||||
- '5 сар'
|
||||
- '6 сар'
|
||||
- '7 сар'
|
||||
- '8 сар'
|
||||
- '9 сар'
|
||||
- '10 сар'
|
||||
- '11 сар'
|
||||
- '12 сар'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- 'Даваа гараг'
|
||||
- 'Мягмар гараг'
|
||||
- 'Лхагва гараг'
|
||||
- 'Пүрэв гараг'
|
||||
- 'Баасан гараг'
|
||||
- 'Бямба гараг'
|
||||
- 'Ням гараг'
|
||||
YES: "Тийм"
|
||||
NO: "Үгүй"
|
||||
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: ' <b /> -д'
|
||||
TEXT_MONTH: ' <b /> -ын'
|
||||
TEXT_DOM: ' <b /> -т'
|
||||
ERROR1: '%s -н утга нь дэмжигддэггүй!'
|
||||
ERROR2: Элементүүдийн тоо хэмжээ буруу
|
||||
ERROR3: jquery_element нь jqCron тохиргоонд хийгдсэн байх ёстой
|
||||
ERROR4: Танигдаагүй илэрхийлэл
|
||||
147
system/languages/my.yaml
Normal file
147
system/languages/my.yaml
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nခေါင်းစဥ်: %1$s\n---\n\n# အမှား - Frontmatter မမှန်ကန်ပါ\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': 'nd'
|
||||
'third': 'rd'
|
||||
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: ရက်သတ္တပတ်
|
||||
MONTH_PLURAL: လ
|
||||
YEAR_PLURAL: နှစ်
|
||||
DECADE_PLURAL: ဆယ်စုနှစ်များစွ
|
||||
SEC_PLURAL: စက္ကန့်
|
||||
MIN_PLURAL: မိနစ်
|
||||
HR_PLURAL: နာရီ
|
||||
WK_PLURAL: အပတ်
|
||||
MO_PLURAL: လ
|
||||
YR_PLURAL: နှစ်
|
||||
DEC_PLURAL: ဆယ်စုနှစ်
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b> အတည်ပြုခြင်းမအောင်မြင်ပါ: </b>'
|
||||
INVALID_INPUT: 'ထည့်သွင်းမှုမမှန်ပါ'
|
||||
MISSING_REQUIRED_FIELD: 'လိုအပ်သောအကွက်ပျောက်နေသည်'
|
||||
XSS_ISSUES: "XSS ပြဿနာ ဖြစ်နိုင်ချေ ကို '%s' အကွက်တွင် တွေ့"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'ဇန်နဝါရီ'
|
||||
- 'ဖေဖော်ဝါရီ'
|
||||
- 'မတ်'
|
||||
- 'ဧပြီ'
|
||||
- 'မေ'
|
||||
- 'ဇွန်'
|
||||
- 'ဇူလိုင်'
|
||||
- 'သြဂုတ်'
|
||||
- 'စက်တင်ဘာ'
|
||||
- 'အောက်တိုဘာ'
|
||||
- 'နိုဝင်ဘာ'
|
||||
- 'ဒီဇင်ဘာ'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- 'တနင်္လာ'
|
||||
- ' အင်္ဂါ'
|
||||
- 'ဗုဒ္ဓဟူး'
|
||||
- 'ကြာသပတေး'
|
||||
- 'သောကြာ'
|
||||
- 'စနေ'
|
||||
- 'တနင်္ဂနွေ'
|
||||
YES: "လုပ်"
|
||||
NO: "မလုပ်"
|
||||
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: '<b /> ပေါ်တွင် '
|
||||
TEXT_MONTH: '<b />၏ '
|
||||
TEXT_DOM: '<b /> တွင် '
|
||||
ERROR1: ဤ %s တက် ကိုပံ့ပိုးမထားပါ။
|
||||
ERROR2: လိုအပ်သောထည့်သွင်း နာပတ် အမှားဖြစ်နေသည်
|
||||
ERROR3: jquery_element ကို jqCron ဆက်တင် တွင်ထားရမည်
|
||||
ERROR4: အသိအမှတ်မပြုသော အသုံးအနှုန်း
|
||||
@@ -104,6 +104,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Validatie mislukt:</b>'
|
||||
INVALID_INPUT: 'Ongeldige invoer in'
|
||||
MISSING_REQUIRED_FIELD: 'Ontbrekend verplicht veld:'
|
||||
XSS_ISSUES: "Mogelijke XSS-problemen ontdekt in '%s' veld"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Januari'
|
||||
- 'Februari'
|
||||
@@ -125,6 +126,8 @@ GRAV:
|
||||
- 'Vrijdag'
|
||||
- 'Zaterdag'
|
||||
- 'Zondag'
|
||||
YES: "Ja"
|
||||
NO: "Nee"
|
||||
CRON:
|
||||
EVERY: elke
|
||||
EVERY_HOUR: elk uur
|
||||
|
||||
@@ -6,10 +6,10 @@ GRAV:
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'wyposażenie'
|
||||
- 'informacja'
|
||||
- 'rice'
|
||||
- ''
|
||||
- 'pieniądze'
|
||||
- 'species'
|
||||
- 'series'
|
||||
- ''
|
||||
- ''
|
||||
- 'ryba'
|
||||
- 'owca'
|
||||
INFLECTOR_IRREGULAR:
|
||||
|
||||
@@ -67,7 +67,7 @@ GRAV:
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Nenhuma data fornecida
|
||||
BAD_DATE: Data inválida
|
||||
AGO: atrás
|
||||
AGO: há
|
||||
FROM_NOW: a partir de agora
|
||||
JUST_NOW: mesmo agora
|
||||
SECOND: segundo
|
||||
@@ -104,6 +104,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Falha na validação:</b>'
|
||||
INVALID_INPUT: 'Dados inseridos são inválidos em'
|
||||
MISSING_REQUIRED_FIELD: 'Campo obrigatório em falta:'
|
||||
XSS_ISSUES: "Potenciais problemas de XSS detectados no campo '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Janeiro'
|
||||
- 'Fevereiro'
|
||||
@@ -125,6 +126,8 @@ GRAV:
|
||||
- 'Sexta-feira'
|
||||
- 'Sábado'
|
||||
- 'Domingo'
|
||||
YES: "Sim"
|
||||
NO: "Não"
|
||||
CRON:
|
||||
EVERY: cada
|
||||
EVERY_HOUR: cada hora
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
---
|
||||
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```"
|
||||
INFLECTOR_SINGULAR:
|
||||
'/([octop|vir])i$/i': '\1us'
|
||||
'/(cris|ax|test)es$/i': '\1is'
|
||||
'/(shoe)s$/i': '\1'
|
||||
'/([lr])ves$/i': '\1f'
|
||||
'/(tive)s$/i': "\\1\n"
|
||||
'/(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'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'экипировка'
|
||||
- 'информация'
|
||||
|
||||
120
system/languages/si.yaml
Normal file
120
system/languages/si.yaml
Normal file
@@ -0,0 +1,120 @@
|
||||
---
|
||||
GRAV:
|
||||
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:
|
||||
'/([m|l])ouse$/i': '\1අයිස්'
|
||||
'/(matr|vert|ind)ix|ex$/i': '\1අයිස්'
|
||||
'/(?:([^f])fe|([lr])f)$/i': '\1\2වෙස්'
|
||||
'/([ti])um$/i': '\1අ'
|
||||
'/(buffal|tomat)o$/i': '\1ඕඑස්'
|
||||
'/(bu)s$/i': '\1සෙස්'
|
||||
INFLECTOR_SINGULAR:
|
||||
'/(quiz)zes$/i': '\1'
|
||||
'/^(ox)en/i': '\1'
|
||||
'/(alias|status)es$/i': '\1'
|
||||
'/([octop|vir])i$/i': '\1 අප'
|
||||
'/(cris|ax|test)es$/i': '\1 වේ'
|
||||
'/(o)es$/i': '\1'
|
||||
'/(bus)es$/i': '\1'
|
||||
'/([m|l])ice$/i': '\1 භාවිතා කරන්න'
|
||||
'/(x|ch|ss|sh)es$/i': '\1'
|
||||
'/(m)ovies$/i': '\1ඕවී'
|
||||
'/(s)eries$/i': '\1මාලා'
|
||||
'/(^analy)ses$/i': '\1සිස්'
|
||||
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2සිස්'
|
||||
'/([ti])a$/i': '\1ම්'
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'උපකරණ'
|
||||
- 'විස්තර'
|
||||
- 'සහල්'
|
||||
- 'මුදල'
|
||||
- 'විශේෂ'
|
||||
- 'මාලාවක්'
|
||||
- 'මාළු'
|
||||
- 'බැටළුවන්'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'මහජන'
|
||||
'man': 'මිනිසුන්'
|
||||
'child': 'දරුවන්'
|
||||
'sex': 'ලිංගිකත්වය'
|
||||
'move': 'චලනය කරයි'
|
||||
INFLECTOR_ORDINALS:
|
||||
'first': 'ශාන්ත'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: දිනයක් සපයා නැත
|
||||
BAD_DATE: නරක දිනය
|
||||
AGO: පෙර
|
||||
FROM_NOW: මෙතැන් සිට
|
||||
JUST_NOW: මේ දැන්
|
||||
SECOND: දෙවැනි
|
||||
MINUTE: මිනිත්තුව
|
||||
HOUR: පැය
|
||||
DAY: දින
|
||||
WEEK: සතිය
|
||||
MONTH: මස
|
||||
YEAR: වර්ෂය
|
||||
DECADE: දශකය
|
||||
SEC: තත්පර
|
||||
MIN: මිනි
|
||||
HR: පැය
|
||||
YR: වසර
|
||||
DEC: දෙසැ
|
||||
SECOND_PLURAL: තත්පර
|
||||
MINUTE_PLURAL: මිනිත්තු
|
||||
HOUR_PLURAL: පැය
|
||||
DAY_PLURAL: දින
|
||||
WEEK_PLURAL: සති
|
||||
MONTH_PLURAL: මාස
|
||||
YEAR_PLURAL: වසර
|
||||
DECADE_PLURAL: දශක
|
||||
SEC_PLURAL: තත්පර
|
||||
MIN_PLURAL: මිනිත්තු
|
||||
HR_PLURAL: පැය
|
||||
WK_PLURAL: සති
|
||||
YR_PLURAL: වසර
|
||||
DEC_PLURAL: දෙසැ
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b>වලංගු කිරීම අසාර්ථක විය:</b>'
|
||||
INVALID_INPUT: 'වලංගු නොවන ආදානය'
|
||||
MISSING_REQUIRED_FIELD: 'අවශ්ය ක්ෂේත්රය අස්ථානගත වී ඇත:'
|
||||
XSS_ISSUES: "විභව XSS ගැටළු '%s' ක්ෂේත්රයේ අනාවරණය විය"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'ජනවාරි'
|
||||
- 'පෙබරවාරි'
|
||||
- 'මාර්තු'
|
||||
- 'අප්රේල්'
|
||||
- 'මැයි'
|
||||
- 'ජූනි'
|
||||
- 'ජුලි'
|
||||
- 'අගෝස්තු'
|
||||
- 'සැප්තැම්බර්'
|
||||
- 'ඔක්තෝම්බර්'
|
||||
- 'නොවැම්බර්'
|
||||
- 'දෙසැම්බර්'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- 'සඳුදා'
|
||||
- 'අඟහරුවාදා'
|
||||
- 'බදාදා'
|
||||
- 'බ්රහස්පතින්දා'
|
||||
- 'සිකුරාදා'
|
||||
- 'සෙනසුරාදා'
|
||||
- 'ඉරිදා'
|
||||
YES: "ඔව්"
|
||||
NO: "නැත"
|
||||
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: ' <b />මත'
|
||||
TEXT_MONTH: ' <b />'
|
||||
TEXT_DOM: ' <b />මත'
|
||||
ERROR1: ටැගය %s සහාය නොදක්වයි!
|
||||
ERROR2: නරක මූලද්රව්ය සංඛ්යාව
|
||||
ERROR3: jquery_element jqCron සැකසුම් වලට සැකසිය යුතුය
|
||||
ERROR4: හඳුනා නොගත් ප්රකාශනය
|
||||
@@ -1,6 +1,17 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Napaka: Neveljavna Frontmatter\n\nPath: `%2$s`\n\n**%3$s ** \n\n```\n%4$s \n```"
|
||||
INFLECTOR_UNCOUNTABLE:
|
||||
- 'oprema'
|
||||
- 'informacija'
|
||||
- 'riž'
|
||||
- 'denar'
|
||||
- 'vrste'
|
||||
- 'serija'
|
||||
- 'riba'
|
||||
- 'ovca'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'ljudje'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Datum ni na voljo
|
||||
BAD_DATE: Neveljaven datum
|
||||
@@ -43,15 +54,15 @@ GRAV:
|
||||
- 'Januar'
|
||||
- 'Februar'
|
||||
- 'Marec'
|
||||
- 'April'
|
||||
- 'april'
|
||||
- 'Maj'
|
||||
- 'Junij'
|
||||
- 'Julij'
|
||||
- 'Avgust'
|
||||
- 'September'
|
||||
- 'september'
|
||||
- 'Oktober'
|
||||
- 'November'
|
||||
- 'December'
|
||||
- 'november'
|
||||
- 'december'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- 'Ponedeljek'
|
||||
- 'Torek'
|
||||
@@ -60,3 +71,15 @@ GRAV:
|
||||
- 'Petek'
|
||||
- 'Sobota'
|
||||
- 'Nedelja'
|
||||
YES: "Da"
|
||||
NO: "Ne"
|
||||
CRON:
|
||||
EVERY: vsak
|
||||
EVERY_HOUR: vsako uro
|
||||
EVERY_MINUTE: vsako minuto
|
||||
EVERY_DAY_OF_WEEK: vsak dan v tednu
|
||||
EVERY_DAY_OF_MONTH: vsak dan v mesecu
|
||||
EVERY_MONTH: vsak mesec
|
||||
ERROR1: Oznaka %s ni podprta!
|
||||
ERROR2: Napačno število elementov.
|
||||
ERROR4: Neznan izraz
|
||||
|
||||
@@ -104,6 +104,7 @@ GRAV:
|
||||
VALIDATION_FAIL: '<b>Провера неуспела:</b>'
|
||||
INVALID_INPUT: 'Неисправан унос у'
|
||||
MISSING_REQUIRED_FIELD: 'Недостаје обавезн поље:'
|
||||
XSS_ISSUES: "Потенцијална грешка у XSS-у детектована у пољу '%s' "
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Јануар'
|
||||
- 'Фебруар'
|
||||
@@ -125,6 +126,8 @@ GRAV:
|
||||
- 'Петак'
|
||||
- 'Субота'
|
||||
- 'Недеља'
|
||||
YES: "Да"
|
||||
NO: "Не"
|
||||
CRON:
|
||||
EVERY: сваки
|
||||
EVERY_HOUR: сваки сат
|
||||
|
||||
147
system/languages/sw.yaml
Normal file
147
system/languages/sw.yaml
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nkichwa: %1$s\n---\n\n# Kosa: Mbele ya Mbele\n\nNjia: `%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:
|
||||
- 'vifaa'
|
||||
- 'habari'
|
||||
- 'mchele'
|
||||
- 'pesa'
|
||||
- 'spishi'
|
||||
- 'mfululizo'
|
||||
- 'samaki'
|
||||
- 'kondoo'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'watu'
|
||||
'man': 'wanaume'
|
||||
'child': 'watoto'
|
||||
'sex': 'jinsia'
|
||||
'move': 'songa'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': 'th'
|
||||
'first': 'st'
|
||||
'second': 'nd'
|
||||
'third': 'rd'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: Hakuna tarehe iliyotolewa
|
||||
BAD_DATE: Tarehe mbaya
|
||||
AGO: zilizopita
|
||||
FROM_NOW: kuanzia sasa
|
||||
JUST_NOW: sasa hivi
|
||||
SECOND: pili
|
||||
MINUTE: dakika
|
||||
HOUR: saa
|
||||
DAY: siku
|
||||
WEEK: wiki
|
||||
MONTH: mwezi
|
||||
YEAR: mwaka
|
||||
DECADE: muongo
|
||||
SEC: sec
|
||||
MIN: min
|
||||
HR: hr
|
||||
WK: wk
|
||||
MO: mo
|
||||
YR: yr
|
||||
DEC: dec
|
||||
SECOND_PLURAL: sekunde
|
||||
MINUTE_PLURAL: dakika
|
||||
HOUR_PLURAL: masaa
|
||||
DAY_PLURAL: siku
|
||||
WEEK_PLURAL: wiki
|
||||
MONTH_PLURAL: miezi
|
||||
YEAR_PLURAL: miaka
|
||||
DECADE_PLURAL: miongo
|
||||
SEC_PLURAL: secs
|
||||
MIN_PLURAL: mins
|
||||
HR_PLURAL: hrs
|
||||
WK_PLURAL: wks
|
||||
MO_PLURAL: mos
|
||||
YR_PLURAL: yrs
|
||||
DEC_PLURAL: decs
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b> Uthibitishaji umeshindwa: </b>'
|
||||
INVALID_INPUT: 'Ingizo batili katika'
|
||||
MISSING_REQUIRED_FIELD: 'Sehemu inayokosekana inahitajika:'
|
||||
XSS_ISSUES: "Masuala yanayowezekana ya XSS yamegunduliwa katika uwanja wa '% s"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'Januari'
|
||||
- 'Februari'
|
||||
- 'Machi'
|
||||
- 'Aprili'
|
||||
- 'Mei'
|
||||
- 'Juni'
|
||||
- 'Julai'
|
||||
- 'Agosti'
|
||||
- 'Septemba'
|
||||
- 'Oktoba'
|
||||
- 'Novemba'
|
||||
- 'Desemba'
|
||||
DAYS_OF_THE_WEEK:
|
||||
- 'Jumatatu'
|
||||
- 'Jumanne'
|
||||
- 'Jumatano'
|
||||
- 'Alhamisi'
|
||||
- 'Ijumaa'
|
||||
- 'Jumamosi'
|
||||
- 'Jumapili'
|
||||
YES: "Ndiyo"
|
||||
NO: "Hapana"
|
||||
CRON:
|
||||
EVERY: kila
|
||||
EVERY_HOUR: kila saa
|
||||
EVERY_MINUTE: kila dakika
|
||||
EVERY_DAY_OF_WEEK: kila siku ya juma
|
||||
EVERY_DAY_OF_MONTH: kila siku ya mwezi
|
||||
EVERY_MONTH: kila mwezi
|
||||
TEXT_PERIOD: Kila <b />
|
||||
TEXT_MINS: ' saa <b /> dakika (saa) zilizopita saa'
|
||||
TEXT_TIME: ' saa <b />: <b />'
|
||||
TEXT_DOW: ' kwenye <b />'
|
||||
TEXT_MONTH: ' ya <b />'
|
||||
TEXT_DOM: ' kwenye <b />'
|
||||
ERROR1: Lebo% s haitumiki!
|
||||
ERROR2: Idadi mbaya ya vitu
|
||||
ERROR3: Jquery_element inapaswa kuwekwa kwenye mipangilio ya jqCron
|
||||
ERROR4: Maneno yasiyotambulika
|
||||
@@ -1,11 +1,75 @@
|
||||
---
|
||||
GRAV:
|
||||
FRONTMATTER_ERROR_PAGE: "---\nชื่อเรื่อง: %1$s\n---\n\n# ข้อผิดพลาด: Invalid Frontmatter\n\nPath: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
|
||||
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Error: Invalid 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:
|
||||
- 'อุปกรณ์'
|
||||
- 'ข้อมูล'
|
||||
- 'ข้าว'
|
||||
- 'เงิน'
|
||||
- 'สายพันธุ์'
|
||||
- 'ซีรีส์'
|
||||
- 'ปลา'
|
||||
- 'แกะ'
|
||||
INFLECTOR_IRREGULAR:
|
||||
'person': 'คน'
|
||||
'man': 'ผู้ชาย'
|
||||
'child': 'เด็กเด็ก'
|
||||
'sex': 'เพศ'
|
||||
'move': 'ย้าย'
|
||||
INFLECTOR_ORDINALS:
|
||||
'default': 'th'
|
||||
'first': 'st'
|
||||
'second': 'nd'
|
||||
'third': 'rd'
|
||||
NICETIME:
|
||||
NO_DATE_PROVIDED: ไม่มีวันที่ให้
|
||||
BAD_DATE: รูปแบบวันที่ผิด
|
||||
AGO: ที่ผ่านมา
|
||||
FROM_NOW: จากตอนนี้
|
||||
JUST_NOW: เมื่อกี้
|
||||
SECOND: วินาที
|
||||
MINUTE: นาที
|
||||
HOUR: ชั่วโมง
|
||||
@@ -17,6 +81,10 @@ GRAV:
|
||||
SEC: วิ
|
||||
MIN: นาที
|
||||
HR: ชม.
|
||||
WK: wk
|
||||
MO: mo
|
||||
YR: yr
|
||||
DEC: dec
|
||||
SECOND_PLURAL: วินาที
|
||||
MINUTE_PLURAL: นาที
|
||||
HOUR_PLURAL: ชั่วโมง
|
||||
@@ -28,11 +96,15 @@ GRAV:
|
||||
SEC_PLURAL: วินาที
|
||||
MIN_PLURAL: นาที
|
||||
HR_PLURAL: ชั่วโมง
|
||||
WK_PLURAL: wks
|
||||
MO_PLURAL: mos
|
||||
YR_PLURAL: ปี
|
||||
DEC_PLURAL: decs
|
||||
FORM:
|
||||
VALIDATION_FAIL: '<b>ตรวจสอบล้มเหลว: </b>'
|
||||
INVALID_INPUT: 'ป้อนข้อมูลไม่ถูกต้องใน'
|
||||
MISSING_REQUIRED_FIELD: 'ขาดข้อมูลที่จำเป็น:'
|
||||
XSS_ISSUES: "ตรวจพบปัญหา XSS ที่เป็นไปได้ในฟิลด์ '%s'"
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- 'มกราคม'
|
||||
- 'กุมภาพันธ์'
|
||||
@@ -54,3 +126,22 @@ GRAV:
|
||||
- 'ศุกร์'
|
||||
- 'เสาร์'
|
||||
- 'อาทิตย์'
|
||||
YES: "ใช่"
|
||||
NO: "ไม่"
|
||||
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: ' บน <b />'
|
||||
TEXT_MONTH: ' จาก <b />'
|
||||
TEXT_DOM: ' บน <b />'
|
||||
ERROR1: ไม่รองรับแท็ก %s!
|
||||
ERROR2: จำนวนองค์ประกอบไม่ดี
|
||||
ERROR3: ควรตั้งค่า jquery_element เป็นการตั้งค่า jqCron
|
||||
ERROR4: นิพจน์ที่ไม่รู้จัก
|
||||
|
||||
@@ -82,6 +82,8 @@ GRAV:
|
||||
- 'Cuma'
|
||||
- 'Cumartesi'
|
||||
- 'Pazar'
|
||||
YES: "Evet"
|
||||
NO: "Hayır"
|
||||
CRON:
|
||||
EVERY: her
|
||||
EVERY_HOUR: saatte bir
|
||||
|
||||
@@ -125,6 +125,8 @@ GRAV:
|
||||
- '星期五'
|
||||
- '星期六'
|
||||
- '星期日'
|
||||
YES: "是"
|
||||
NO: "否"
|
||||
CRON:
|
||||
EVERY: 每隔
|
||||
EVERY_HOUR: 每小时
|
||||
|
||||
@@ -38,7 +38,9 @@ GRAV:
|
||||
YR_PLURAL: 年
|
||||
DEC_PLURAL: 十年
|
||||
FORM:
|
||||
MISSING_REQUIRED_FIELD: 遺漏必填欄位:
|
||||
VALIDATION_FAIL: '<b>確驗證失敗:</b>'
|
||||
INVALID_INPUT: '無效輸入:'
|
||||
MISSING_REQUIRED_FIELD: '遺漏必填欄位:'
|
||||
MONTHS_OF_THE_YEAR:
|
||||
- '一月'
|
||||
- '二月'
|
||||
@@ -60,3 +62,18 @@ GRAV:
|
||||
- '星期五'
|
||||
- '星期六'
|
||||
- '星期日'
|
||||
YES: "是"
|
||||
NO: "否"
|
||||
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: ' 的 <b />'
|
||||
TEXT_MONTH: ' 的 <b />'
|
||||
TEXT_DOM: ' 的 <b />'
|
||||
|
||||
@@ -125,6 +125,8 @@ GRAV:
|
||||
- '星期五'
|
||||
- '星期六'
|
||||
- '星期日'
|
||||
YES: "是"
|
||||
NO: "否"
|
||||
CRON:
|
||||
EVERY: 每隔
|
||||
EVERY_HOUR: 每小时
|
||||
|
||||
547
system/recovery.php
Normal file
547
system/recovery.php
Normal file
@@ -0,0 +1,547 @@
|
||||
<?php
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Recovery\RecoveryManager;
|
||||
use Grav\Common\Upgrade\SafeUpgradeService;
|
||||
|
||||
if (!\defined('GRAV_ROOT')) {
|
||||
\define('GRAV_ROOT', dirname(__DIR__));
|
||||
}
|
||||
|
||||
session_start([
|
||||
'name' => 'grav-recovery',
|
||||
'cookie_httponly' => true,
|
||||
'cookie_secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off',
|
||||
'cookie_samesite' => 'Lax',
|
||||
]);
|
||||
|
||||
$manager = new RecoveryManager();
|
||||
$context = $manager->getContext() ?? [];
|
||||
$token = $context['token'] ?? null;
|
||||
$authenticated = $token && isset($_SESSION['grav_recovery_authenticated']) && hash_equals($_SESSION['grav_recovery_authenticated'], $token);
|
||||
$errorMessage = null;
|
||||
$notice = null;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
if ($action === 'authenticate') {
|
||||
$provided = trim($_POST['token'] ?? '');
|
||||
if ($token && hash_equals($token, $provided)) {
|
||||
$_SESSION['grav_recovery_authenticated'] = $token;
|
||||
header('Location: ' . $_SERVER['REQUEST_URI']);
|
||||
exit;
|
||||
}
|
||||
$errorMessage = 'Invalid recovery token.';
|
||||
} elseif ($action === 'clear-flag') {
|
||||
// Clear recovery flag - allowed without authentication
|
||||
$manager->clear();
|
||||
$_SESSION['grav_recovery_authenticated'] = null;
|
||||
$notice = 'Recovery flag cleared. <a href="' . htmlspecialchars($_SERVER['REQUEST_URI'], ENT_QUOTES, 'UTF-8') . '">Reload the page</a> to continue.';
|
||||
} elseif ($action === 'disable-recovery') {
|
||||
// Disable recovery mode in config (updates.recovery_mode) - allowed without authentication
|
||||
$configDir = GRAV_ROOT . '/user/config';
|
||||
$configFile = $configDir . '/system.yaml';
|
||||
Folder::create($configDir);
|
||||
|
||||
$config = [];
|
||||
if (is_file($configFile)) {
|
||||
$content = file_get_contents($configFile);
|
||||
if ($content !== false) {
|
||||
// Simple YAML parsing for this specific case
|
||||
$config = \Symfony\Component\Yaml\Yaml::parse($content) ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($config['updates'])) {
|
||||
$config['updates'] = [];
|
||||
}
|
||||
$config['updates']['recovery_mode'] = false;
|
||||
$yaml = \Symfony\Component\Yaml\Yaml::dump($config, 4, 2);
|
||||
file_put_contents($configFile, $yaml);
|
||||
|
||||
// Also clear the recovery flag
|
||||
$manager->clear();
|
||||
$_SESSION['grav_recovery_authenticated'] = null;
|
||||
$notice = 'Recovery mode has been disabled. <a href="' . htmlspecialchars($_SERVER['REQUEST_URI'], ENT_QUOTES, 'UTF-8') . '">Reload the page</a> to continue.';
|
||||
} elseif ($authenticated) {
|
||||
$service = new SafeUpgradeService();
|
||||
try {
|
||||
if ($action === 'rollback' && !empty($_POST['manifest'])) {
|
||||
$service->rollback(trim($_POST['manifest']));
|
||||
$manager->clear();
|
||||
$_SESSION['grav_recovery_authenticated'] = null;
|
||||
$notice = 'Rollback complete. Please reload Grav.';
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$errorMessage = 'Authentication required for this action.';
|
||||
}
|
||||
}
|
||||
|
||||
$quarantineFile = GRAV_ROOT . '/user/data/upgrades/quarantine.json';
|
||||
$quarantine = [];
|
||||
if (is_file($quarantineFile)) {
|
||||
$decoded = json_decode(file_get_contents($quarantineFile), true);
|
||||
if (is_array($decoded)) {
|
||||
$quarantine = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
$manifestDir = GRAV_ROOT . '/user/data/upgrades';
|
||||
$snapshots = [];
|
||||
if (is_dir($manifestDir)) {
|
||||
$files = glob($manifestDir . '/*.json');
|
||||
if ($files) {
|
||||
foreach ($files as $file) {
|
||||
$decoded = json_decode(file_get_contents($file), true);
|
||||
if (!is_array($decoded)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$id = $decoded['id'] ?? pathinfo($file, PATHINFO_FILENAME);
|
||||
if (!is_string($id) || $id === '' || strncmp($id, 'snapshot-', 9) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$decoded['id'] = $id;
|
||||
$decoded['file'] = basename($file);
|
||||
$decoded['created_at'] = (int)($decoded['created_at'] ?? filemtime($file) ?: 0);
|
||||
$snapshots[] = $decoded;
|
||||
}
|
||||
|
||||
if ($snapshots) {
|
||||
usort($snapshots, static function (array $a, array $b): int {
|
||||
return ($b['created_at'] ?? 0) <=> ($a['created_at'] ?? 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$latestSnapshot = $snapshots[0] ?? null;
|
||||
|
||||
// Determine base URL for assets
|
||||
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '/index.php';
|
||||
$baseUrl = rtrim(dirname($scriptName), '/\\');
|
||||
if ($baseUrl === '.' || $baseUrl === '') {
|
||||
$baseUrl = '';
|
||||
}
|
||||
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
|
||||
?><!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Grav Recovery Mode</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||
min-height: 100vh;
|
||||
color: #e8e8e8;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
padding: 30px 0;
|
||||
}
|
||||
.header img {
|
||||
width: 80px;
|
||||
height: auto;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
margin: 0 0 8px 0;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.header .subtitle {
|
||||
color: #a0a0a0;
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
.alert {
|
||||
padding: 16px 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
.alert-error {
|
||||
background: rgba(239, 68, 68, 0.15);
|
||||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||
color: #fca5a5;
|
||||
}
|
||||
.alert-warning {
|
||||
background: rgba(245, 158, 11, 0.15);
|
||||
border: 1px solid rgba(245, 158, 11, 0.3);
|
||||
color: #fcd34d;
|
||||
}
|
||||
.alert-success {
|
||||
background: rgba(34, 197, 94, 0.15);
|
||||
border: 1px solid rgba(34, 197, 94, 0.3);
|
||||
color: #86efac;
|
||||
}
|
||||
.alert-success a { color: #4ade80; }
|
||||
.alert-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
.alert-content { flex: 1; }
|
||||
.alert-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.card {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.card h2 {
|
||||
font-size: 1.1rem;
|
||||
margin: 0 0 16px 0;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.card h3 {
|
||||
font-size: 1rem;
|
||||
margin: 0 0 12px 0;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.error-summary {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
font-size: 0.9rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.error-summary .error-message {
|
||||
color: #f87171;
|
||||
word-break: break-word;
|
||||
}
|
||||
.error-summary .error-location {
|
||||
color: #94a3b8;
|
||||
margin-top: 8px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 16px;
|
||||
}
|
||||
button, .btn {
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
text-decoration: none;
|
||||
}
|
||||
.btn-primary {
|
||||
background: #3b82f6;
|
||||
color: #fff;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: #2563eb;
|
||||
}
|
||||
.btn-secondary {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #e8e8e8;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
.btn-danger {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: #fca5a5;
|
||||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||
}
|
||||
.btn-danger:hover {
|
||||
background: rgba(239, 68, 68, 0.3);
|
||||
}
|
||||
details {
|
||||
margin-top: 16px;
|
||||
}
|
||||
summary {
|
||||
cursor: pointer;
|
||||
color: #94a3b8;
|
||||
font-size: 0.9rem;
|
||||
padding: 8px 0;
|
||||
user-select: none;
|
||||
}
|
||||
summary:hover {
|
||||
color: #cbd5e1;
|
||||
}
|
||||
.stack-trace {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-top: 12px;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
font-size: 0.8rem;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
color: #94a3b8;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.info-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.info-list li {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
.info-list li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.info-list .label {
|
||||
color: #94a3b8;
|
||||
min-width: 120px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.info-list .value {
|
||||
color: #e8e8e8;
|
||||
word-break: break-word;
|
||||
}
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
color: #fff;
|
||||
font-size: 0.95rem;
|
||||
margin-top: 8px;
|
||||
}
|
||||
input[type="text"]:focus {
|
||||
outline: none;
|
||||
border-color: #3b82f6;
|
||||
}
|
||||
label {
|
||||
color: #94a3b8;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.help-text {
|
||||
color: #64748b;
|
||||
font-size: 0.85rem;
|
||||
margin-top: 8px;
|
||||
}
|
||||
code {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.quarantine-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.quarantine-list li {
|
||||
padding: 12px;
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.quarantine-list .plugin-name {
|
||||
font-weight: 600;
|
||||
color: #fcd34d;
|
||||
}
|
||||
.quarantine-list .plugin-time {
|
||||
color: #94a3b8;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.snapshot-info {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
margin: 12px 0;
|
||||
}
|
||||
.snapshot-info code {
|
||||
color: #60a5fa;
|
||||
}
|
||||
.snapshot-info small {
|
||||
color: #64748b;
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
body { padding: 12px; }
|
||||
.card { padding: 16px; }
|
||||
.btn-group { flex-direction: column; }
|
||||
button, .btn { width: 100%; justify-content: center; }
|
||||
.info-list li { flex-direction: column; gap: 4px; }
|
||||
.info-list .label { min-width: auto; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<img src="<?php echo htmlspecialchars($baseUrl, ENT_QUOTES, 'UTF-8'); ?>/system/assets/grav.png" alt="Grav" onerror="this.style.display='none'">
|
||||
<h1>Recovery Mode</h1>
|
||||
<p class="subtitle">Grav has encountered an error during a recent update</p>
|
||||
</div>
|
||||
|
||||
<?php if ($notice): ?>
|
||||
<div class="alert alert-success">
|
||||
<span class="alert-icon">✓</span>
|
||||
<div class="alert-content"><?php echo $notice; ?></div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($errorMessage): ?>
|
||||
<div class="alert alert-error">
|
||||
<span class="alert-icon">⚠</span>
|
||||
<div class="alert-content">
|
||||
<div class="alert-title">Action Failed</div>
|
||||
<?php echo htmlspecialchars($errorMessage, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<span class="alert-icon">⚠</span>
|
||||
<div class="alert-content">
|
||||
<div class="alert-title">A Fatal Error Occurred</div>
|
||||
Grav detected a fatal error after a recent upgrade and has entered recovery mode to protect your site.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Error Details</h2>
|
||||
<div class="error-summary">
|
||||
<div class="error-message"><?php echo htmlspecialchars($context['message'] ?? 'Unknown error', ENT_QUOTES, 'UTF-8'); ?></div>
|
||||
<?php if (!empty($context['file'])): ?>
|
||||
<div class="error-location">
|
||||
<?php echo htmlspecialchars($context['file'], ENT_QUOTES, 'UTF-8'); ?><?php if (!empty($context['line'])): ?>:<?php echo htmlspecialchars((string)$context['line'], ENT_QUOTES, 'UTF-8'); ?><?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($context['trace'])): ?>
|
||||
<details>
|
||||
<summary>View Stack Trace</summary>
|
||||
<div class="stack-trace"><?php echo htmlspecialchars($context['trace'], ENT_QUOTES, 'UTF-8'); ?></div>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($context['plugin'])): ?>
|
||||
<details open>
|
||||
<summary>Affected Plugin</summary>
|
||||
<ul class="info-list" style="margin-top: 12px;">
|
||||
<li>
|
||||
<span class="label">Plugin</span>
|
||||
<span class="value"><strong><?php echo htmlspecialchars($context['plugin'], ENT_QUOTES, 'UTF-8'); ?></strong> (has been automatically disabled)</span>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($quarantine): ?>
|
||||
<div class="card">
|
||||
<h2>Quarantined Plugins</h2>
|
||||
<p class="help-text" style="margin-top: 0;">These plugins have been automatically disabled due to errors:</p>
|
||||
<ul class="quarantine-list">
|
||||
<?php foreach ($quarantine as $entry): ?>
|
||||
<li>
|
||||
<span class="plugin-name"><?php echo htmlspecialchars($entry['slug'], ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
<span class="plugin-time">Disabled at <?php echo date('Y-m-d H:i:s', $entry['disabled_at']); ?></span>
|
||||
<?php if (!empty($entry['message'])): ?>
|
||||
<div style="margin-top: 4px; font-size: 0.85rem; color: #94a3b8;"><?php echo htmlspecialchars($entry['message'], ENT_QUOTES, 'UTF-8'); ?></div>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card">
|
||||
<h2>What would you like to do?</h2>
|
||||
<p style="margin-top: 0; color: #94a3b8;">Choose an action to resolve this issue:</p>
|
||||
|
||||
<div class="btn-group">
|
||||
<form method="post" style="display: contents;">
|
||||
<input type="hidden" name="action" value="clear-flag">
|
||||
<button type="submit" class="btn btn-primary">Clear Recovery & Continue</button>
|
||||
</form>
|
||||
<form method="post" style="display: contents;">
|
||||
<input type="hidden" name="action" value="disable-recovery">
|
||||
<button type="submit" class="btn btn-secondary" title="Prevents recovery mode from activating in the future">Disable Recovery Mode</button>
|
||||
</form>
|
||||
</div>
|
||||
<p class="help-text">
|
||||
<strong>Clear Recovery & Continue:</strong> Clears the recovery flag and attempts to load your site normally.<br>
|
||||
<strong>Disable Recovery Mode:</strong> Sets <code>updates.recovery_mode: false</code> in your configuration so recovery mode won't trigger again.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<?php if ($latestSnapshot): ?>
|
||||
<div class="card">
|
||||
<h2>Rollback to Previous Version</h2>
|
||||
<p style="margin-top: 0; color: #94a3b8;">If the error persists, you can rollback to a previous Grav version.</p>
|
||||
|
||||
<div class="snapshot-info">
|
||||
<code><?php echo htmlspecialchars($latestSnapshot['id'], ENT_QUOTES, 'UTF-8'); ?></code>
|
||||
<?php if (!empty($latestSnapshot['label'])): ?>
|
||||
<small><?php echo htmlspecialchars($latestSnapshot['label'], ENT_QUOTES, 'UTF-8'); ?></small>
|
||||
<?php endif; ?>
|
||||
<small>Grav <?php echo htmlspecialchars($latestSnapshot['target_version'] ?? 'unknown', ENT_QUOTES, 'UTF-8'); ?> — Created <?php echo date('Y-m-d H:i:s', (int)$latestSnapshot['created_at']); ?></small>
|
||||
</div>
|
||||
|
||||
<?php if (!$authenticated): ?>
|
||||
<p class="help-text">To rollback, enter the recovery token found in <code>user/data/recovery.flag</code></p>
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="authenticate">
|
||||
<label for="token">Recovery Token</label>
|
||||
<input id="token" name="token" type="text" autocomplete="one-time-code" placeholder="Enter token from recovery.flag" required>
|
||||
<div class="btn-group">
|
||||
<button type="submit" class="btn btn-secondary">Authenticate for Rollback</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="rollback">
|
||||
<input type="hidden" name="manifest" value="<?php echo htmlspecialchars($latestSnapshot['id'], ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<div class="btn-group">
|
||||
<button type="submit" class="btn btn-danger">Rollback to This Snapshot</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
16
system/rector.php
Normal file
16
system/rector.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use Rector\Config\RectorConfig;
|
||||
|
||||
return RectorConfig::configure()
|
||||
->withSkip([
|
||||
__DIR__ . '/vendor',
|
||||
])
|
||||
->withPaths([
|
||||
__DIR__
|
||||
])
|
||||
->withPhpSets(php82: true)
|
||||
->withPhpVersion(Rector\ValueObject\PhpVersion::PHP_84)
|
||||
->withRules([
|
||||
Rector\Php84\Rector\Param\ExplicitNullableParamTypeRector::class,
|
||||
]);
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Core
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -13,8 +13,25 @@ if (PHP_SAPI !== 'cli-server') {
|
||||
|
||||
$_SERVER['PHP_CLI_ROUTER'] = true;
|
||||
|
||||
if (is_file($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $_SERVER['SCRIPT_NAME'])) {
|
||||
return false;
|
||||
$root = $_SERVER['DOCUMENT_ROOT'];
|
||||
$path = $_SERVER['SCRIPT_NAME'];
|
||||
if ($path !== '/index.php' && is_file($root . $path)) {
|
||||
if (!(
|
||||
// Block all direct access to files and folders beginning with a dot
|
||||
str_contains((string) $path, '/.')
|
||||
// Block all direct access for these folders
|
||||
|| preg_match('`^/(\.git|cache|bin|logs|backup|webserver-configs|tests)/`ui', (string) $path)
|
||||
// Block access to specific file types for these system folders
|
||||
|| preg_match('`^/(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', (string) $path)
|
||||
// Block access to specific file types for these user folders
|
||||
|| preg_match('`^/(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', (string) $path)
|
||||
// Block all direct access to .md files
|
||||
|| preg_match('`\.md$`ui', (string) $path)
|
||||
// Block access to specific files in the root folder
|
||||
|| preg_match('`^/(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$`ui', (string) $path)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$grav_index = 'index.php';
|
||||
|
||||
165
system/src/DOMLettersIterator.php
Normal file
165
system/src/DOMLettersIterator.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Iterates individual characters (Unicode codepoints) of DOM text and CDATA nodes
|
||||
* while keeping track of their position in the document.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $doc = new DOMDocument();
|
||||
* $doc->load('example.xml');
|
||||
* foreach(new DOMLettersIterator($doc) as $letter) echo $letter;
|
||||
*
|
||||
* NB: If you only need characters without their position
|
||||
* in the document, use DOMNode->textContent instead.
|
||||
*
|
||||
* @author porneL http://pornel.net
|
||||
* @license Public Domain
|
||||
* @url https://github.com/antoligy/dom-string-iterators
|
||||
*
|
||||
* @implements Iterator<int,string>
|
||||
*/
|
||||
final class DOMLettersIterator implements Iterator
|
||||
{
|
||||
/** @var DOMElement */
|
||||
private $start;
|
||||
/** @var DOMElement|null */
|
||||
private $current;
|
||||
/** @var int */
|
||||
private $offset = -1;
|
||||
/** @var int|null */
|
||||
private $key;
|
||||
/** @var array<int,string>|null */
|
||||
private $letters;
|
||||
|
||||
/**
|
||||
* expects DOMElement or DOMDocument (see DOMDocument::load and DOMDocument::loadHTML)
|
||||
*
|
||||
* @param DOMNode $el
|
||||
*/
|
||||
public function __construct(DOMNode $el)
|
||||
{
|
||||
if ($el instanceof DOMDocument) {
|
||||
$el = $el->documentElement;
|
||||
}
|
||||
|
||||
if (!$el instanceof DOMElement) {
|
||||
throw new InvalidArgumentException('Invalid arguments, expected DOMElement or DOMDocument');
|
||||
}
|
||||
|
||||
$this->start = $el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns position in text as DOMText node and character offset.
|
||||
* (it's NOT a byte offset, you must use mb_substr() or similar to use this offset properly).
|
||||
* node may be NULL if iterator has finished.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function currentTextPosition(): array
|
||||
{
|
||||
return [$this->current, $this->offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns DOMElement that is currently being iterated or NULL if iterator has finished.
|
||||
*
|
||||
* @return DOMElement|null
|
||||
*/
|
||||
public function currentElement(): ?DOMElement
|
||||
{
|
||||
return $this->current ? $this->current->parentNode : null;
|
||||
}
|
||||
|
||||
// Implementation of Iterator interface
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function key(): ?int
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function next(): void
|
||||
{
|
||||
if (null === $this->current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) {
|
||||
if ($this->offset === -1) {
|
||||
preg_match_all('/./us', $this->current->textContent, $m);
|
||||
$this->letters = $m[0];
|
||||
}
|
||||
|
||||
$this->offset++;
|
||||
$this->key++;
|
||||
if ($this->letters && $this->offset < count($this->letters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->offset = -1;
|
||||
}
|
||||
|
||||
while ($this->current->nodeType === XML_ELEMENT_NODE && $this->current->firstChild) {
|
||||
$this->current = $this->current->firstChild;
|
||||
if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) {
|
||||
$this->next();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (!$this->current->nextSibling && $this->current->parentNode) {
|
||||
$this->current = $this->current->parentNode;
|
||||
if ($this->current === $this->start) {
|
||||
$this->current = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->current = $this->current->nextSibling;
|
||||
|
||||
$this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element
|
||||
* @link https://php.net/manual/en/iterator.current.php
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function current(): ?string
|
||||
{
|
||||
return $this->letters ? $this->letters[$this->offset] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid
|
||||
* @link https://php.net/manual/en/iterator.valid.php
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
return (bool)$this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->current = $this->start;
|
||||
$this->offset = -1;
|
||||
$this->key = 0;
|
||||
$this->letters = [];
|
||||
|
||||
$this->next();
|
||||
}
|
||||
}
|
||||
|
||||
158
system/src/DOMWordsIterator.php
Normal file
158
system/src/DOMWordsIterator.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Iterates individual words of DOM text and CDATA nodes
|
||||
* while keeping track of their position in the document.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $doc = new DOMDocument();
|
||||
* $doc->load('example.xml');
|
||||
* foreach(new DOMWordsIterator($doc) as $word) echo $word;
|
||||
*
|
||||
* @author pjgalbraith http://www.pjgalbraith.com
|
||||
* @author porneL http://pornel.net (based on DOMLettersIterator available at http://pornel.net/source/domlettersiterator.php)
|
||||
* @license Public Domain
|
||||
* @url https://github.com/antoligy/dom-string-iterators
|
||||
*
|
||||
* @implements Iterator<int,string>
|
||||
*/
|
||||
|
||||
final class DOMWordsIterator implements Iterator
|
||||
{
|
||||
/** @var DOMElement */
|
||||
private $start;
|
||||
/** @var DOMElement|null */
|
||||
private $current;
|
||||
/** @var int */
|
||||
private $offset = -1;
|
||||
/** @var int|null */
|
||||
private $key;
|
||||
/** @var array<int,array<int,int|string>>|null */
|
||||
private $words;
|
||||
|
||||
/**
|
||||
* expects DOMElement or DOMDocument (see DOMDocument::load and DOMDocument::loadHTML)
|
||||
*
|
||||
* @param DOMNode $el
|
||||
*/
|
||||
public function __construct(DOMNode $el)
|
||||
{
|
||||
if ($el instanceof DOMDocument) {
|
||||
$el = $el->documentElement;
|
||||
}
|
||||
|
||||
if (!$el instanceof DOMElement) {
|
||||
throw new InvalidArgumentException('Invalid arguments, expected DOMElement or DOMDocument');
|
||||
}
|
||||
|
||||
$this->start = $el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns position in text as DOMText node and character offset.
|
||||
* (it's NOT a byte offset, you must use mb_substr() or similar to use this offset properly).
|
||||
* node may be NULL if iterator has finished.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function currentWordPosition(): array
|
||||
{
|
||||
return [$this->current, $this->offset, $this->words];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns DOMElement that is currently being iterated or NULL if iterator has finished.
|
||||
*
|
||||
* @return DOMElement|null
|
||||
*/
|
||||
public function currentElement(): ?DOMElement
|
||||
{
|
||||
return $this->current ? $this->current->parentNode : null;
|
||||
}
|
||||
|
||||
// Implementation of Iterator interface
|
||||
|
||||
/**
|
||||
* Return the key of the current element
|
||||
* @link https://php.net/manual/en/iterator.key.php
|
||||
* @return int|null
|
||||
*/
|
||||
public function key(): ?int
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function next(): void
|
||||
{
|
||||
if (null === $this->current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) {
|
||||
if ($this->offset === -1) {
|
||||
$this->words = preg_split("/[\n\r\t ]+/", $this->current->textContent, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_OFFSET_CAPTURE) ?: [];
|
||||
}
|
||||
$this->offset++;
|
||||
|
||||
if ($this->words && $this->offset < count($this->words)) {
|
||||
$this->key++;
|
||||
return;
|
||||
}
|
||||
$this->offset = -1;
|
||||
}
|
||||
|
||||
while ($this->current->nodeType === XML_ELEMENT_NODE && $this->current->firstChild) {
|
||||
$this->current = $this->current->firstChild;
|
||||
if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) {
|
||||
$this->next();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (!$this->current->nextSibling && $this->current->parentNode) {
|
||||
$this->current = $this->current->parentNode;
|
||||
if ($this->current === $this->start) {
|
||||
$this->current = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->current = $this->current->nextSibling;
|
||||
|
||||
$this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element
|
||||
* @link https://php.net/manual/en/iterator.current.php
|
||||
* @return string|null
|
||||
*/
|
||||
public function current(): ?string
|
||||
{
|
||||
return $this->words ? (string)$this->words[$this->offset][0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid
|
||||
* @link https://php.net/manual/en/iterator.valid.php
|
||||
* @return bool
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
return (bool)$this->current;
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->current = $this->start;
|
||||
$this->offset = -1;
|
||||
$this->key = 0;
|
||||
$this->words = [];
|
||||
|
||||
$this->next();
|
||||
}
|
||||
}
|
||||
79
system/src/Doctrine/Common/Cache/Cache.php
Normal file
79
system/src/Doctrine/Common/Cache/Cache.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file provides a lightweight replacement for the legacy Doctrine Cache
|
||||
* interfaces so that existing Grav extensions depending on the Doctrine
|
||||
* namespace continue to function without the abandoned package.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Interface for cache drivers.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
interface Cache
|
||||
{
|
||||
public const STATS_HITS = 'hits';
|
||||
public const STATS_MISSES = 'misses';
|
||||
public const STATS_UPTIME = 'uptime';
|
||||
public const STATS_MEMORY_USAGE = 'memory_usage';
|
||||
public const STATS_MEMORY_AVAILABLE = 'memory_available';
|
||||
/**
|
||||
* Only for backward compatibility (may be removed in next major release)
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public const STATS_MEMORY_AVAILIABLE = 'memory_available';
|
||||
|
||||
/**
|
||||
* Fetches an entry from the cache.
|
||||
*
|
||||
* @param string $id The id of the cache entry to fetch.
|
||||
*
|
||||
* @return mixed The cached data or FALSE, if no cache entry exists for the given id.
|
||||
*/
|
||||
public function fetch($id);
|
||||
|
||||
/**
|
||||
* Tests if an entry exists in the cache.
|
||||
*
|
||||
* @param string $id The cache id of the entry to check for.
|
||||
*
|
||||
* @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise.
|
||||
*/
|
||||
public function contains($id);
|
||||
|
||||
/**
|
||||
* Puts data into the cache.
|
||||
*
|
||||
* If a cache entry with the given id already exists, its data will be replaced.
|
||||
*
|
||||
* @param string $id The cache id.
|
||||
* @param mixed $data The cache entry/data.
|
||||
* @param int $lifeTime The lifetime in number of seconds for this cache entry.
|
||||
* If zero (the default), the entry never expires (although it may be deleted from the cache
|
||||
* to make place for other entries).
|
||||
*
|
||||
* @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
|
||||
*/
|
||||
public function save($id, $data, $lifeTime = 0);
|
||||
|
||||
/**
|
||||
* Deletes a cache entry.
|
||||
*
|
||||
* @param string $id The cache id.
|
||||
*
|
||||
* @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise.
|
||||
* Deleting a non-existing entry is considered successful.
|
||||
*/
|
||||
public function delete($id);
|
||||
|
||||
/**
|
||||
* Retrieves cached information from the data store.
|
||||
*
|
||||
* @return mixed[]|null An associative array with server's statistics if available, NULL otherwise.
|
||||
*/
|
||||
public function getStats();
|
||||
}
|
||||
329
system/src/Doctrine/Common/Cache/CacheProvider.php
Normal file
329
system/src/Doctrine/Common/Cache/CacheProvider.php
Normal file
@@ -0,0 +1,329 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Lightweight compatibility layer for the abandoned Doctrine Cache package.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
use function array_combine;
|
||||
use function array_key_exists;
|
||||
use function array_map;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Base class for cache provider implementations.
|
||||
*/
|
||||
abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiOperationCache
|
||||
{
|
||||
public const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]';
|
||||
|
||||
/**
|
||||
* The namespace to prefix all cache ids with.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $namespace = '';
|
||||
|
||||
/**
|
||||
* The namespace version.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
private $namespaceVersion;
|
||||
|
||||
/**
|
||||
* Sets the namespace to prefix all cache ids with.
|
||||
*
|
||||
* @param string $namespace
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
$this->namespace = (string) $namespace;
|
||||
$this->namespaceVersion = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the namespace that prefixes all cache ids.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($id)
|
||||
{
|
||||
return $this->doFetch($this->getNamespacedId($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchMultiple(array $keys)
|
||||
{
|
||||
if (empty($keys)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys
|
||||
$namespacedKeys = array_combine($keys, array_map([$this, 'getNamespacedId'], $keys));
|
||||
$items = $this->doFetchMultiple($namespacedKeys);
|
||||
$foundItems = [];
|
||||
|
||||
// no internal array function supports this sort of mapping: needs to be iterative
|
||||
// this filters and combines keys in one pass
|
||||
foreach ($namespacedKeys as $requestedKey => $namespacedKey) {
|
||||
if (! isset($items[$namespacedKey]) && ! array_key_exists($namespacedKey, $items)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$foundItems[$requestedKey] = $items[$namespacedKey];
|
||||
}
|
||||
|
||||
return $foundItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveMultiple(array $keysAndValues, $lifetime = 0)
|
||||
{
|
||||
$namespacedKeysAndValues = [];
|
||||
foreach ($keysAndValues as $key => $value) {
|
||||
$namespacedKeysAndValues[$this->getNamespacedId($key)] = $value;
|
||||
}
|
||||
|
||||
return $this->doSaveMultiple($namespacedKeysAndValues, $lifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function contains($id)
|
||||
{
|
||||
return $this->doContains($this->getNamespacedId($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($id, $data, $lifeTime = 0)
|
||||
{
|
||||
return $this->doSave($this->getNamespacedId($id), $data, $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteMultiple(array $keys)
|
||||
{
|
||||
return $this->doDeleteMultiple(array_map([$this, 'getNamespacedId'], $keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
return $this->doDelete($this->getNamespacedId($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStats()
|
||||
{
|
||||
return $this->doGetStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function flushAll()
|
||||
{
|
||||
return $this->doFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function deleteAll()
|
||||
{
|
||||
$namespaceCacheKey = $this->getNamespaceCacheKey();
|
||||
$namespaceVersion = $this->getNamespaceVersion() + 1;
|
||||
|
||||
if ($this->doSave($namespaceCacheKey, $namespaceVersion)) {
|
||||
$this->namespaceVersion = $namespaceVersion;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefixes the passed id with the configured namespace value.
|
||||
*
|
||||
* @param string $id The id to namespace.
|
||||
*
|
||||
* @return string The namespaced id.
|
||||
*/
|
||||
private function getNamespacedId(string $id): string
|
||||
{
|
||||
$namespaceVersion = $this->getNamespaceVersion();
|
||||
|
||||
return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace cache key.
|
||||
*/
|
||||
private function getNamespaceCacheKey(): string
|
||||
{
|
||||
return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace version.
|
||||
*/
|
||||
private function getNamespaceVersion(): int
|
||||
{
|
||||
if ($this->namespaceVersion !== null) {
|
||||
return $this->namespaceVersion;
|
||||
}
|
||||
|
||||
$namespaceCacheKey = $this->getNamespaceCacheKey();
|
||||
$this->namespaceVersion = (int) $this->doFetch($namespaceCacheKey) ?: 1;
|
||||
|
||||
return $this->namespaceVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of doFetchMultiple. Each driver that supports multi-get should overwrite it.
|
||||
*
|
||||
* @param string[] $keys Array of keys to retrieve from cache
|
||||
*
|
||||
* @return mixed[] Array of values retrieved for the given keys.
|
||||
*/
|
||||
protected function doFetchMultiple(array $keys)
|
||||
{
|
||||
$returnValues = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$item = $this->doFetch($key);
|
||||
if ($item === false && ! $this->doContains($key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$returnValues[$key] = $item;
|
||||
}
|
||||
|
||||
return $returnValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an entry from the cache.
|
||||
*
|
||||
* @param string $id The id of the cache entry to fetch.
|
||||
*
|
||||
* @return mixed|false The cached data or FALSE, if no cache entry exists for the given id.
|
||||
*/
|
||||
abstract protected function doFetch($id);
|
||||
|
||||
/**
|
||||
* Tests if an entry exists in the cache.
|
||||
*
|
||||
* @param string $id The cache id of the entry to check for.
|
||||
*
|
||||
* @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise.
|
||||
*/
|
||||
abstract protected function doContains($id);
|
||||
|
||||
/**
|
||||
* Default implementation of doSaveMultiple. Each driver that supports multi-put should override it.
|
||||
*
|
||||
* @param mixed[] $keysAndValues Array of keys and values to save in cache
|
||||
* @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these
|
||||
* cache entries (0 => infinite lifeTime).
|
||||
*
|
||||
* @return bool TRUE if the operation was successful, FALSE if it wasn't.
|
||||
*/
|
||||
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
foreach ($keysAndValues as $key => $value) {
|
||||
if ($this->doSave($key, $value, $lifetime)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts data into the cache.
|
||||
*
|
||||
* @param string $id The cache id.
|
||||
* @param string $data The cache entry/data.
|
||||
* @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this
|
||||
* cache entry (0 => infinite lifeTime).
|
||||
*
|
||||
* @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
|
||||
*/
|
||||
abstract protected function doSave($id, $data, $lifeTime = 0);
|
||||
|
||||
/**
|
||||
* Default implementation of doDeleteMultiple. Each driver that supports multi-delete should override it.
|
||||
*
|
||||
* @param string[] $keys Array of keys to delete from cache
|
||||
*
|
||||
* @return bool TRUE if the operation was successful, FALSE if it wasn't
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if ($this->doDelete($key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$success = false;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a cache entry.
|
||||
*
|
||||
* @param string $id The cache id.
|
||||
*
|
||||
* @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise.
|
||||
*/
|
||||
abstract protected function doDelete($id);
|
||||
|
||||
/**
|
||||
* Flushes all cache entries.
|
||||
*
|
||||
* @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise.
|
||||
*/
|
||||
abstract protected function doFlush();
|
||||
|
||||
/**
|
||||
* Retrieves cached information from the data store.
|
||||
*
|
||||
* @return mixed[]|null An associative array with server's statistics if available, NULL otherwise.
|
||||
*/
|
||||
abstract protected function doGetStats();
|
||||
}
|
||||
25
system/src/Doctrine/Common/Cache/ClearableCache.php
Normal file
25
system/src/Doctrine/Common/Cache/ClearableCache.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Lightweight compatibility layer for the abandoned Doctrine Cache package.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Interface for cache that can be flushed.
|
||||
*
|
||||
* Intended to be used for partial clearing of a cache namespace. For a more
|
||||
* global "flushing", see {@see FlushableCache}.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
interface ClearableCache
|
||||
{
|
||||
/**
|
||||
* Deletes all cache entries in the current cache namespace.
|
||||
*
|
||||
* @return bool TRUE if the cache entries were successfully deleted, FALSE otherwise.
|
||||
*/
|
||||
public function deleteAll();
|
||||
}
|
||||
24
system/src/Doctrine/Common/Cache/FilesystemCache.php
Normal file
24
system/src/Doctrine/Common/Cache/FilesystemCache.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
use Grav\Common\Cache\SymfonyCacheProvider;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
|
||||
/**
|
||||
* Filesystem cache driver (backwards compatibility).
|
||||
*/
|
||||
class FilesystemCache extends SymfonyCacheProvider
|
||||
{
|
||||
public const EXTENSION = '.doctrinecache.data';
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @param string $extension
|
||||
* @param int $umask
|
||||
*/
|
||||
public function __construct($directory, $extension = self::EXTENSION, $umask = 0002)
|
||||
{
|
||||
parent::__construct(new FilesystemAdapter('', 0, $directory));
|
||||
}
|
||||
}
|
||||
22
system/src/Doctrine/Common/Cache/FlushableCache.php
Normal file
22
system/src/Doctrine/Common/Cache/FlushableCache.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Lightweight compatibility layer for the abandoned Doctrine Cache package.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Interface for cache that can be flushed.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
interface FlushableCache
|
||||
{
|
||||
/**
|
||||
* Flushes all cache entries, globally.
|
||||
*
|
||||
* @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise.
|
||||
*/
|
||||
public function flushAll();
|
||||
}
|
||||
26
system/src/Doctrine/Common/Cache/MultiDeleteCache.php
Normal file
26
system/src/Doctrine/Common/Cache/MultiDeleteCache.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Lightweight compatibility layer for the abandoned Doctrine Cache package.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Interface for cache drivers that allows to delete many items at once.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
interface MultiDeleteCache
|
||||
{
|
||||
/**
|
||||
* Deletes several cache entries.
|
||||
*
|
||||
* @param string[] $keys Array of keys to delete from cache
|
||||
*
|
||||
* @return bool TRUE if the operation was successful, FALSE if it wasn't.
|
||||
*/
|
||||
public function deleteMultiple(array $keys);
|
||||
}
|
||||
27
system/src/Doctrine/Common/Cache/MultiGetCache.php
Normal file
27
system/src/Doctrine/Common/Cache/MultiGetCache.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Lightweight compatibility layer for the abandoned Doctrine Cache package.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Interface for cache drivers that allows to get many items at once.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
interface MultiGetCache
|
||||
{
|
||||
/**
|
||||
* Returns an associative array of values for keys is found in cache.
|
||||
*
|
||||
* @param string[] $keys Array of keys to retrieve from cache
|
||||
*
|
||||
* @return mixed[] Array of retrieved values, indexed by the specified keys.
|
||||
* Values that couldn't be retrieved are not contained in this array.
|
||||
*/
|
||||
public function fetchMultiple(array $keys);
|
||||
}
|
||||
16
system/src/Doctrine/Common/Cache/MultiOperationCache.php
Normal file
16
system/src/Doctrine/Common/Cache/MultiOperationCache.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Lightweight compatibility layer for the abandoned Doctrine Cache package.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Interface for cache drivers that supports multiple items manipulation.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
interface MultiOperationCache extends MultiGetCache, MultiDeleteCache, MultiPutCache
|
||||
{
|
||||
}
|
||||
28
system/src/Doctrine/Common/Cache/MultiPutCache.php
Normal file
28
system/src/Doctrine/Common/Cache/MultiPutCache.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Lightweight compatibility layer for the abandoned Doctrine Cache package.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Interface for cache drivers that allows to put many items at once.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
interface MultiPutCache
|
||||
{
|
||||
/**
|
||||
* Returns a boolean value indicating if the operation succeeded.
|
||||
*
|
||||
* @param mixed[] $keysAndValues Array of keys and values to save in cache
|
||||
* @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these
|
||||
* cache entries (0 => infinite lifeTime).
|
||||
*
|
||||
* @return bool TRUE if the operation was successful, FALSE if it wasn't.
|
||||
*/
|
||||
public function saveMultiple(array $keysAndValues, $lifetime = 0);
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @package Grav\Common
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
|
||||
* @copyright Copyright (c) 2015 - 2025 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
@@ -16,8 +16,8 @@ use Grav\Common\Assets\Traits\TestingAssetsTrait;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Framework\Object\PropertyObject;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use function array_slice;
|
||||
use function call_user_func_array;
|
||||
use function count;
|
||||
use function func_get_args;
|
||||
use function is_array;
|
||||
|
||||
@@ -30,14 +30,21 @@ class Assets extends PropertyObject
|
||||
use TestingAssetsTrait;
|
||||
use LegacyAssetsTrait;
|
||||
|
||||
const LINK = 'link';
|
||||
const CSS = 'css';
|
||||
const JS = 'js';
|
||||
const JS_MODULE = 'js_module';
|
||||
const LINK_COLLECTION = 'assets_link';
|
||||
const CSS_COLLECTION = 'assets_css';
|
||||
const JS_COLLECTION = 'assets_js';
|
||||
const JS_MODULE_COLLECTION = 'assets_js_module';
|
||||
const LINK_TYPE = Assets\Link::class;
|
||||
const CSS_TYPE = Assets\Css::class;
|
||||
const JS_TYPE = Assets\Js::class;
|
||||
const JS_MODULE_TYPE = Assets\JsModule::class;
|
||||
const INLINE_CSS_TYPE = Assets\InlineCss::class;
|
||||
const INLINE_JS_TYPE = Assets\InlineJs::class;
|
||||
const INLINE_JS_MODULE_TYPE = Assets\InlineJsModule::class;
|
||||
|
||||
/** @const Regex to match CSS and JavaScript files */
|
||||
const DEFAULT_REGEX = '/.\.(css|js)$/i';
|
||||
@@ -48,15 +55,24 @@ class Assets extends PropertyObject
|
||||
/** @const Regex to match JavaScript files */
|
||||
const JS_REGEX = '/.\.js$/i';
|
||||
|
||||
/** @const Regex to match JavaScriptModyle files */
|
||||
const JS_MODULE_REGEX = '/.\.mjs$/i';
|
||||
|
||||
/** @var string */
|
||||
protected $assets_dir;
|
||||
/** @var string */
|
||||
protected $assets_url;
|
||||
|
||||
/** @var array */
|
||||
protected $assets_link = [];
|
||||
/** @var array */
|
||||
protected $assets_css = [];
|
||||
/** @var array */
|
||||
protected $assets_js = [];
|
||||
/** @var array */
|
||||
protected $assets_js_module = [];
|
||||
|
||||
|
||||
|
||||
// Following variables come from the configuration:
|
||||
/** @var bool */
|
||||
@@ -66,19 +82,17 @@ class Assets extends PropertyObject
|
||||
/** @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;
|
||||
protected $js_module_pipeline;
|
||||
/** @var bool */
|
||||
protected $inlinejs_pipeline_before_excludes;
|
||||
protected $js_module_pipeline_include_externals;
|
||||
/** @var bool */
|
||||
protected $js_module_pipeline_before_excludes;
|
||||
/** @var array */
|
||||
protected $pipeline_options = [];
|
||||
|
||||
@@ -110,7 +124,7 @@ class Assets extends PropertyObject
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
$this->assets_dir = $locator->findResource('asset://') . DS;
|
||||
$this->assets_dir = $locator->findResource('asset://');
|
||||
$this->assets_url = $locator->findResource('asset://', false);
|
||||
|
||||
$this->config($asset_config);
|
||||
@@ -160,30 +174,46 @@ class Assets extends PropertyObject
|
||||
*/
|
||||
public function add($asset)
|
||||
{
|
||||
if (!$asset) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$args = func_get_args();
|
||||
|
||||
// More than one asset
|
||||
if (is_array($asset)) {
|
||||
foreach ($asset as $a) {
|
||||
array_shift($args);
|
||||
$args = array_merge([$a], $args);
|
||||
call_user_func_array([$this, 'add'], $args);
|
||||
foreach ($asset as $index => $location) {
|
||||
$params = array_slice($args, 1);
|
||||
if (is_array($location)) {
|
||||
$params = array_shift($params);
|
||||
if (is_numeric($params)) {
|
||||
$params = [ 'priority' => $params ];
|
||||
}
|
||||
$params = [array_replace_recursive([], $location, $params)];
|
||||
$location = $index;
|
||||
}
|
||||
|
||||
$params = array_merge([$location], $params);
|
||||
call_user_func_array($this->add(...), $params);
|
||||
}
|
||||
} 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);
|
||||
$path = parse_url($asset, PHP_URL_PATH);
|
||||
$extension = $path ? Utils::pathinfo($path, PATHINFO_EXTENSION) : '';
|
||||
|
||||
// JavaScript or CSS
|
||||
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);
|
||||
} elseif ($extension === 'mjs') {
|
||||
call_user_func_array($this->addJsModule(...), $args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,14 +231,19 @@ class Assets extends PropertyObject
|
||||
protected function addType($collection, $type, $asset, $options)
|
||||
{
|
||||
if (is_array($asset)) {
|
||||
foreach ($asset as $a) {
|
||||
$this->addType($collection, $type, $a, $options);
|
||||
foreach ($asset as $index => $location) {
|
||||
$assetOptions = $options;
|
||||
if (is_array($location)) {
|
||||
$assetOptions = array_replace_recursive([], $options, $location);
|
||||
$location = $index;
|
||||
}
|
||||
$this->addType($collection, $type, $location, $assetOptions);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (($type === $this::CSS_TYPE || $type === $this::JS_TYPE) && isset($this->collections[$asset])) {
|
||||
if ($this->isValidType($type) && isset($this->collections[$asset])) {
|
||||
$this->addType($collection, $type, $this->collections[$asset], $options);
|
||||
return $this;
|
||||
}
|
||||
@@ -216,7 +251,9 @@ 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 : $this::CSS;
|
||||
|
||||
$exclude_type = $this->getBaseType($type);
|
||||
|
||||
$excludes = strtolower($exclude_type . '_pipeline_before_excludes');
|
||||
if ($this->{$excludes}) {
|
||||
$default = 'after';
|
||||
@@ -224,14 +261,20 @@ class Assets extends PropertyObject
|
||||
$default = 'before';
|
||||
}
|
||||
|
||||
$options['position'] = $options['position'] ?? $default;
|
||||
$options['position'] ??= $default;
|
||||
}
|
||||
|
||||
unset($options['pipeline']);
|
||||
}
|
||||
|
||||
// Add timestamp
|
||||
$options['timestamp'] = $this->timestamp;
|
||||
$timestamp_override = $options['timestamp'] ?? true;
|
||||
|
||||
if (filter_var($timestamp_override, FILTER_VALIDATE_BOOLEAN)) {
|
||||
$options['timestamp'] = $this->timestamp;
|
||||
} else {
|
||||
$options['timestamp'] = null;
|
||||
}
|
||||
|
||||
// Set order
|
||||
$group = $options['group'] ?? 'head';
|
||||
@@ -255,6 +298,16 @@ class Assets extends PropertyObject
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CSS asset or a collection of assets.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addLink($asset)
|
||||
{
|
||||
return $this->addType($this::LINK_COLLECTION, $this::LINK_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::LINK_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CSS asset or a collection of assets.
|
||||
*
|
||||
@@ -295,6 +348,25 @@ class Assets extends PropertyObject
|
||||
return $this->addType($this::JS_COLLECTION, $this::INLINE_JS_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::INLINE_JS_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a JS asset or a collection of assets.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addJsModule($asset)
|
||||
{
|
||||
return $this->addType($this::JS_MODULE_COLLECTION, $this::JS_MODULE_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::JS_MODULE_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an Inline JS asset or a collection of assets.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addInlineJsModule($asset)
|
||||
{
|
||||
return $this->addType($this::JS_MODULE_COLLECTION, $this::INLINE_JS_MODULE_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::INLINE_JS_MODULE_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/replace collection.
|
||||
@@ -326,6 +398,9 @@ class Assets extends PropertyObject
|
||||
|
||||
if ($key === 'position' && $value === 'pipeline') {
|
||||
$type = $asset->getType();
|
||||
if ($type === 'jsmodule') {
|
||||
$type = 'js_module';
|
||||
}
|
||||
|
||||
if ($asset->getRemote() && $this->{strtolower($type) . '_pipeline_include_externals'} === false && $asset['position'] === 'pipeline') {
|
||||
if ($this->{strtolower($type) . '_pipeline_before_excludes'}) {
|
||||
@@ -357,9 +432,7 @@ class Assets extends PropertyObject
|
||||
*/
|
||||
protected function sortAssets($assets)
|
||||
{
|
||||
uasort($assets, static function ($a, $b) {
|
||||
return $b['priority'] <=> $a['priority'] ?: $a['order'] <=> $b['order'];
|
||||
});
|
||||
uasort($assets, static fn($a, $b) => $b['priority'] <=> $a['priority'] ?: $a['order'] <=> $b['order']);
|
||||
|
||||
return $assets;
|
||||
}
|
||||
@@ -386,11 +459,37 @@ class Assets extends PropertyObject
|
||||
$after_assets = $this->filterAssets($group_assets, 'position', 'after', true);
|
||||
|
||||
// Pipeline
|
||||
if ($this->{$pipeline_enabled}) {
|
||||
if ($this->{$pipeline_enabled} ?? false) {
|
||||
$options = array_merge($this->pipeline_options, ['timestamp' => $this->timestamp]);
|
||||
|
||||
$pipeline = new Pipeline($options);
|
||||
$pipeline_output = $pipeline->$render_pipeline($pipeline_assets, $group, $attributes);
|
||||
$grouped_pipeline_assets = $this->splitPipelineAssetsByAttribute($pipeline_assets, 'loading');
|
||||
|
||||
foreach ($grouped_pipeline_assets as $pipeline_group) {
|
||||
if (empty($pipeline_group['assets'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$group_attributes = array_merge($attributes, $pipeline_group['attributes']);
|
||||
|
||||
$pipeline = new Pipeline($options);
|
||||
$result = $pipeline->$render_pipeline($pipeline_group['assets'], $group, $group_attributes);
|
||||
|
||||
// Handle different return types from pipeline
|
||||
if ($result === false) {
|
||||
// No assets to render
|
||||
continue;
|
||||
} elseif (is_array($result)) {
|
||||
// Array result contains pipelined output and any failed assets
|
||||
$pipeline_output .= $result['output'];
|
||||
// Render failed assets individually (they couldn't be minified)
|
||||
foreach ($result['failed'] as $asset) {
|
||||
$pipeline_output .= $asset->render();
|
||||
}
|
||||
} else {
|
||||
// String result (no minification or CSS)
|
||||
$pipeline_output .= $result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($pipeline_assets as $asset) {
|
||||
$pipeline_output .= $asset->render();
|
||||
@@ -418,9 +517,29 @@ class Assets extends PropertyObject
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function css($group = 'head', $attributes = [])
|
||||
public function css($group = 'head', $attributes = [], $include_link = true)
|
||||
{
|
||||
return $this->render('css', $group, $attributes);
|
||||
$output = '';
|
||||
|
||||
if ($include_link) {
|
||||
$output = $this->link($group, $attributes);
|
||||
}
|
||||
|
||||
$output .= $this->render(self::CSS, $group, $attributes);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the CSS link tags.
|
||||
*
|
||||
* @param string $group name of the group
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function link($group = 'head', $attributes = [])
|
||||
{
|
||||
return $this->render(self::LINK, $group, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -430,8 +549,131 @@ class Assets extends PropertyObject
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function js($group = 'head', $attributes = [])
|
||||
public function js($group = 'head', $attributes = [], $include_js_module = true)
|
||||
{
|
||||
return $this->render('js', $group, $attributes);
|
||||
$output = $this->render(self::JS, $group, $attributes);
|
||||
|
||||
if ($include_js_module) {
|
||||
$output .= $this->jsModule($group, $attributes);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Javascript Modules tags
|
||||
*
|
||||
* @param string $group
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function jsModule($group = 'head', $attributes = [])
|
||||
{
|
||||
return $this->render(self::JS_MODULE, $group, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $group
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function all($group = 'head', $attributes = [])
|
||||
{
|
||||
$output = $this->css($group, $attributes, false);
|
||||
$output .= $this->link($group, $attributes);
|
||||
$output .= $this->js($group, $attributes, false);
|
||||
$output .= $this->jsModule($group, $attributes);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $type
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValidType($type)
|
||||
{
|
||||
return in_array($type, [self::CSS_TYPE, self::JS_TYPE, self::JS_MODULE_TYPE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $type
|
||||
* @return string
|
||||
*/
|
||||
protected function getBaseType($type)
|
||||
{
|
||||
$base_type = match ($type) {
|
||||
$this::JS_TYPE, $this::INLINE_JS_TYPE => $this::JS,
|
||||
$this::JS_MODULE_TYPE, $this::INLINE_JS_MODULE_TYPE => $this::JS_MODULE,
|
||||
default => $this::CSS,
|
||||
};
|
||||
|
||||
return $base_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split pipeline assets into ordered groups based on the value of a given attribute.
|
||||
*
|
||||
* This preserves the original order of the assets while ensuring assets that require
|
||||
* special handling (such as different loading strategies) are rendered separately.
|
||||
*
|
||||
* @param array $assets
|
||||
* @param string $attribute
|
||||
* @return array<int, array{assets: array, attributes: array}>
|
||||
*/
|
||||
protected function splitPipelineAssetsByAttribute(array $assets, string $attribute): array
|
||||
{
|
||||
$groups = [];
|
||||
$currentAssets = [];
|
||||
$currentValue = null;
|
||||
$hasCurrentGroup = false;
|
||||
|
||||
foreach ($assets as $key => $asset) {
|
||||
$value = null;
|
||||
|
||||
if (method_exists($asset, 'hasNestedProperty')) {
|
||||
if ($asset->hasNestedProperty($attribute)) {
|
||||
$value = $asset->getNestedProperty($attribute);
|
||||
} elseif ($asset->hasNestedProperty('attributes.' . $attribute)) {
|
||||
$value = $asset->getNestedProperty('attributes.' . $attribute);
|
||||
}
|
||||
}
|
||||
|
||||
if ($value === null && isset($asset[$attribute])) {
|
||||
$value = $asset[$attribute];
|
||||
}
|
||||
|
||||
if ($value === '' || $value === false) {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
if (!$hasCurrentGroup) {
|
||||
$currentAssets = [$key => $asset];
|
||||
$currentValue = $value;
|
||||
$hasCurrentGroup = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value === $currentValue) {
|
||||
$currentAssets[$key] = $asset;
|
||||
continue;
|
||||
}
|
||||
|
||||
$groups[] = [
|
||||
'assets' => $currentAssets,
|
||||
'attributes' => $currentValue !== null ? [$attribute => $currentValue] : []
|
||||
];
|
||||
|
||||
$currentAssets = [$key => $asset];
|
||||
$currentValue = $value;
|
||||
}
|
||||
|
||||
if ($hasCurrentGroup) {
|
||||
$groups[] = [
|
||||
'assets' => $currentAssets,
|
||||
'attributes' => $currentValue !== null ? [$attribute => $currentValue] : []
|
||||
];
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user