forked from leftypol/leftypol
Compare commits
1271 Commits
block_mass
...
config
Author | SHA1 | Date | |
---|---|---|---|
b0b684bdb1 | |||
8626ab7571 | |||
0ce203f1d2 | |||
9efc35e441 | |||
0d38349a10 | |||
295cde61f6 | |||
cfdbcfcad9 | |||
2030b60acf | |||
428d9e9001 | |||
7dcdbc065d | |||
47d704ed01 | |||
9723bb8f4a | |||
217f52ec69 | |||
aa99d10f1a | |||
003152095a | |||
940ea3f4b5 | |||
f6cc9a2f9f | |||
f5aa60627e | |||
909c2040da | |||
86fc44d2f3 | |||
1da97d77ca | |||
8bc9a22920 | |||
9084588fa0 | |||
3351715795 | |||
6863db49ef | |||
21d0a3a585 | |||
2509be645d | |||
28c8ff15c1 | |||
a74a9e41e3 | |||
5f1f7319a3 | |||
94f6a1f366 | |||
159b9c9737 | |||
92a50f60cd | |||
49457dfd9f | |||
9799d7e515 | |||
8fcad42ba2 | |||
e84480764a | |||
43a5a33cbd | |||
bdd80b6160 | |||
b942250a16 | |||
4ca2f5f3a3 | |||
c09a85ca65 | |||
05952a6222 | |||
295d1751b1 | |||
45e4153d5b | |||
e5c8923c1c | |||
6a0613d4a8 | |||
36419ab9a7 | |||
ac3fc9518b | |||
677a1e0210 | |||
919a6d0d2f | |||
c6f0dc5f4e | |||
040bf21c47 | |||
651cc9edb4 | |||
1682352b66 | |||
4edb0b5563 | |||
ef98a2aa7e | |||
0d2a441eb0 | |||
ffaad8dbb8 | |||
745bd5fc5d | |||
cb686abbba | |||
e1e6a5ce35 | |||
0354b1a3cd | |||
5873987599 | |||
06214a1e26 | |||
df7d7e1d3c | |||
6ba2c7cf42 | |||
e76dbfd7a0 | |||
5bf2634bf6 | |||
726de817b1 | |||
20b7ed7829 | |||
e3dc1a1a65 | |||
faf546ab02 | |||
928593dad9 | |||
f3b2793954 | |||
9de120201d | |||
7805530e41 | |||
2092661af4 | |||
b50578ffa9 | |||
fd890cefd7 | |||
ef936d60a9 | |||
2e6a0aa06a | |||
9c978fd5f8 | |||
![]() |
46e61a71cf | ||
cb5b465c57 | |||
97e41d1c7f | |||
8e5b09c687 | |||
0a1412b74f | |||
e496fb10a5 | |||
5709513cb7 | |||
1de62d4ddd | |||
48347ae1cd | |||
98ef473a9a | |||
e8f589f6c1 | |||
a8ff605571 | |||
0b7161a588 | |||
7e3a531aa5 | |||
c696ad4fa2 | |||
cb4726e076 | |||
b742445f50 | |||
aca9ca0d7b | |||
91e41aa522 | |||
6fcd4f4779 | |||
6ac0e47f15 | |||
39412b4427 | |||
3191ef3105 | |||
17820b31c4 | |||
57a6154287 | |||
64ba328c3b | |||
dcc936b35d | |||
ea99ddc6f6 | |||
2efd67ef85 | |||
264c10cf2a | |||
c559d2cd41 | |||
45f310e4e3 | |||
41016b1ca2 | |||
38afe37733 | |||
b16e891768 | |||
a4b5bf341b | |||
fd9baaea5f | |||
854cc6cd23 | |||
a9211d23b5 | |||
df2fe0e60f | |||
56b6d6b1aa | |||
df78f0e79f | |||
799072d692 | |||
8faf20f7ff | |||
5c0d0a8ca1 | |||
1b00660d33 | |||
1adcb13cc1 | |||
![]() |
fdaa4ebdd1 | ||
a3a1e8e55e | |||
61a3aa8fe1 | |||
d694f2aa38 | |||
![]() |
56bbaf881f | ||
![]() |
b833e2743a | ||
![]() |
845ff14e52 | ||
![]() |
e2bef8c2f2 | ||
ea67272e87 | |||
![]() |
0cffd52b02 | ||
![]() |
fc2357e6f2 | ||
![]() |
5b01f34973 | ||
![]() |
f1a48042c2 | ||
57df86557a | |||
e3ea750c85 | |||
bf4fb6d0e8 | |||
3725d27e72 | |||
2b5365b572 | |||
9c70a1f880 | |||
37edb0cf3d | |||
6c2e958a77 | |||
![]() |
ad0af8e082 | ||
a1f64335f9 | |||
bc7fa47aa2 | |||
a1f8f4adca | |||
ca1ce0103f | |||
231321f2c1 | |||
2f64d7edff | |||
![]() |
c388081617 | ||
![]() |
15a099deb3 | ||
e39f342008 | |||
a1b6ca71b4 | |||
a385be3f4c | |||
d66b9d6208 | |||
cfaea67381 | |||
9bf3c9a0ae | |||
2605a6ff5d | |||
c9ef4e80ac | |||
19bbde2639 | |||
c6cfa4e04d | |||
2f84986401 | |||
bf51d4ed89 | |||
b055b1101b | |||
4efe45fe23 | |||
a09180ed05 | |||
90e033fb4c | |||
18d96f5376 | |||
78ab5de8f7 | |||
c20db2988d | |||
dd1b6b0276 | |||
0b56719008 | |||
30e179552a | |||
1db5bd1a6e | |||
011bb40ebe | |||
badfb60161 | |||
135604386a | |||
66097406b8 | |||
795b54bf61 | |||
9115f631ab | |||
9638c00f39 | |||
773f0e1842 | |||
16d6448204 | |||
4fcbad3fb1 | |||
3ae859d6e6 | |||
ae65959a48 | |||
6235c12275 | |||
b3adcfe98b | |||
e6212c1a51 | |||
78d4fd6ee1 | |||
667b271a27 | |||
569f5f5876 | |||
0250d59fb7 | |||
bfb51c66a4 | |||
6aa992d6a2 | |||
43268fba76 | |||
1d8828896d | |||
7e61b7d077 | |||
2dc55ab236 | |||
926b377032 | |||
![]() |
807adaff90 | ||
2654af07c2 | |||
9efcec3573 | |||
59e614a8f0 | |||
3112d6486b | |||
d5704ec4eb | |||
10c772efad | |||
d1db003472 | |||
34f178d53e | |||
67232c5c41 | |||
fabc83ebfb | |||
cb1fd189a6 | |||
aa9341c51d | |||
07f1abfc3a | |||
55457cf07c | |||
bba5e1bff3 | |||
00d8d50210 | |||
44e632a430 | |||
6042d8b49a | |||
![]() |
e6106e04e0 | ||
220896b535 | |||
![]() |
bc691addff | ||
39074e10fa | |||
dbca2948ae | |||
ed6fab9088 | |||
aa3c8fb6e9 | |||
132b7ace54 | |||
![]() |
888977a96a | ||
![]() |
afdd7419b3 | ||
![]() |
7ad0f7e635 | ||
![]() |
704ba15ced | ||
8fe2499e58 | |||
![]() |
7d2bc99953 | ||
![]() |
88070b3c4a | ||
![]() |
26a729e9d8 | ||
![]() |
e642af7ac0 | ||
![]() |
d7248c357d | ||
6f47518ae9 | |||
![]() |
d867e97340 | ||
![]() |
6d685d00fb | ||
1accda2a18 | |||
8e2de03447 | |||
b518cc94c3 | |||
8acc328c91 | |||
79156cbbe4 | |||
b227982390 | |||
99493b054d | |||
9c17b19ded | |||
a19a283c91 | |||
73794e5719 | |||
f45636065d | |||
c909b31cd7 | |||
3379b9c72e | |||
21be83eb73 | |||
e55784eff2 | |||
e69e18ae26 | |||
96babb9229 | |||
0acca678a7 | |||
d41b1230e1 | |||
f69d524a59 | |||
cb280cd5e0 | |||
c35c6a5dbe | |||
191c7a5c66 | |||
4e4d3a0d29 | |||
d758b5b5f2 | |||
76911be92a | |||
1f9891006a | |||
02452955ec | |||
2625f6ede5 | |||
bb34acedf6 | |||
fa4f543a2d | |||
0806e39c03 | |||
52cd74a01d | |||
1864530b56 | |||
46d62584d0 | |||
9c7239f2d4 | |||
37bfc3beeb | |||
5bb8505dfd | |||
bbf7ced5db | |||
2dee1c1f38 | |||
9ca34d6ed5 | |||
2532d08331 | |||
1d97b825d3 | |||
![]() |
843ab6f55e | ||
4a5944795c | |||
![]() |
52d6de5681 | ||
![]() |
5abdabe115 | ||
f2e398305d | |||
29e0906022 | |||
![]() |
4a4bb4e856 | ||
![]() |
7c4d74aeee | ||
![]() |
89c5246e3c | ||
![]() |
3eee68e0db | ||
![]() |
7d23e14cb9 | ||
![]() |
eee71e58a3 | ||
![]() |
9b749a6c81 | ||
![]() |
b0e3448139 | ||
78b3a63311 | |||
![]() |
c841179934 | ||
![]() |
4a7240eebb | ||
![]() |
43aa9eee5f | ||
![]() |
75a59ece64 | ||
![]() |
a8b80c7037 | ||
f575691fc4 | |||
af889e8941 | |||
![]() |
ed55ea23ea | ||
55a9caefa1 | |||
819b520e37 | |||
![]() |
bd71b3598d | ||
7b0564ca33 | |||
344f8371c0 | |||
68eaa26ddd | |||
49ec94d11f | |||
![]() |
b97ec18275 | ||
![]() |
ca8f6baa36 | ||
72b716269e | |||
![]() |
178df81467 | ||
baef16208c | |||
6701456e81 | |||
4379135437 | |||
![]() |
e0cec8ee5a | ||
![]() |
c39009f0e7 | ||
![]() |
7e9afa9356 | ||
![]() |
b992a926cc | ||
![]() |
3bae2c65a2 | ||
![]() |
2584ed9d1a | ||
![]() |
5581b68aed | ||
![]() |
d3ff13d8ca | ||
![]() |
780c64e31e | ||
![]() |
fadc80ff2e | ||
![]() |
88c4e7ef47 | ||
![]() |
040305c60c | ||
74b1859972 | |||
8b3df89863 | |||
8a505322d6 | |||
2475a192a1 | |||
da2b2567b0 | |||
c98738684a | |||
e93e613535 | |||
![]() |
d112885ca5 | ||
e418f1e3aa | |||
f4ae0196bd | |||
57bd7ec25b | |||
8398c78093 | |||
![]() |
b792bd7b07 | ||
![]() |
07a7971822 | ||
![]() |
08c4e0723d | ||
![]() |
50546fa3d5 | ||
![]() |
8727f1faeb | ||
![]() |
5a3d55f731 | ||
![]() |
327ad190ea | ||
![]() |
610e62bfa0 | ||
![]() |
b4b5d4aff7 | ||
![]() |
e118c166de | ||
![]() |
8b8d4b12b9 | ||
![]() |
beede38c6e | ||
![]() |
5f542c03fc | ||
![]() |
e14df481a6 | ||
![]() |
953baa22ac | ||
![]() |
186e122b42 | ||
![]() |
6797d447f2 | ||
![]() |
f1dd6fbab7 | ||
![]() |
e4be20c8c3 | ||
8ad44a39eb | |||
![]() |
55e6735024 | ||
![]() |
c64a4bf701 | ||
![]() |
47a9fa507e | ||
f48a1b71c2 | |||
![]() |
d7a108f881 | ||
![]() |
1685e13753 | ||
![]() |
4bec221fdb | ||
![]() |
ac5e6fb5a9 | ||
![]() |
96957142ac | ||
![]() |
f31d6c8e83 | ||
9a8aa66eb7 | |||
436886e4d9 | |||
daf702fa3e | |||
9f3b919b44 | |||
![]() |
03b00f29fc | ||
0cf01da7fe | |||
![]() |
de1f163ed2 | ||
2ae0dc8b56 | |||
b12f96952a | |||
![]() |
1073803c74 | ||
![]() |
2ec45088ea | ||
d90ae4b789 | |||
bef84432f8 | |||
a86d0f7a9d | |||
2a9191b52c | |||
0be3f8f1f8 | |||
c82ad0ed68 | |||
d05d62d599 | |||
cf0a326e3d | |||
19251b466d | |||
![]() |
f4a785100d | ||
![]() |
c44bbde511 | ||
6380b071aa | |||
3a6fe72d70 | |||
c1d27702ef | |||
![]() |
aacc92199a | ||
![]() |
3096438faa | ||
5ef675e578 | |||
8a05aff581 | |||
![]() |
fc66da1d14 | ||
![]() |
27dfef9393 | ||
![]() |
959414ca46 | ||
dd425e3ef0 | |||
8ea261f6ca | |||
b4ef8e8381 | |||
![]() |
b11487a8a0 | ||
![]() |
53f82b295d | ||
![]() |
acbe91df84 | ||
![]() |
5ea0255fea | ||
33bc63a354 | |||
![]() |
76cfc7f76c | ||
9c3bd4ce63 | |||
98579209db | |||
6b2583c839 | |||
056d53adf5 | |||
7cc76fa0ad | |||
8938989efb | |||
![]() |
3d52266e98 | ||
![]() |
a09369bfdd | ||
![]() |
d68c90f2be | ||
782b886252 | |||
40475ac830 | |||
4e0e4f720e | |||
8daf9d79ef | |||
b2c7aa42f3 | |||
3b05c7bc00 | |||
b09a932211 | |||
8d4815921d | |||
305e698e32 | |||
19acf84cb3 | |||
![]() |
ee8107f775 | ||
7993602665 | |||
![]() |
7aabc15867 | ||
![]() |
a271a9e4b0 | ||
![]() |
d737a047f8 | ||
![]() |
1ac46ff57e | ||
![]() |
55c729f574 | ||
![]() |
7edb471379 | ||
f13e4dfa2b | |||
![]() |
eaa6125f46 | ||
3893e8e331 | |||
22acb3e039 | |||
![]() |
653dbc7cf0 | ||
f2ac2d2b8a | |||
![]() |
30cb2951fc | ||
1f848ca437 | |||
![]() |
38ffb75d8a | ||
![]() |
5992e57eda | ||
![]() |
c9eae4bd69 | ||
![]() |
c994ae7f23 | ||
6b62058172 | |||
![]() |
ec1041e095 | ||
993af46670 | |||
8485dad4ae | |||
f979567b10 | |||
f0b3c0fcb6 | |||
11bab0a9ad | |||
![]() |
661837a4b1 | ||
![]() |
012a08dda6 | ||
df98d2e66c | |||
16cb2ab75c | |||
a4906098a6 | |||
68046d4a26 | |||
![]() |
be1e0bb2be | ||
![]() |
094e102269 | ||
![]() |
95c939dd22 | ||
![]() |
f7af183f86 | ||
5adc4eb33e | |||
3d2ea677b4 | |||
![]() |
c3ee0b8fb5 | ||
![]() |
f625b2e656 | ||
![]() |
b68394b075 | ||
26e510ab1a | |||
![]() |
28a5dc1d72 | ||
![]() |
4d998f7a95 | ||
![]() |
334a88248c | ||
![]() |
063c11bb1f | ||
![]() |
1123b4009d | ||
![]() |
2efaafd639 | ||
![]() |
11a5344a87 | ||
![]() |
635537fd74 | ||
![]() |
b3c881231f | ||
![]() |
2c6628b7cd | ||
![]() |
5216f2e189 | ||
![]() |
7c7e4cca70 | ||
![]() |
e7752273f9 | ||
![]() |
3393bd649c | ||
![]() |
67a37ccf69 | ||
![]() |
8c67103c16 | ||
![]() |
afc3d158b4 | ||
![]() |
04e68d220c | ||
d65cab7270 | |||
9b2313e39c | |||
73c189bdea | |||
![]() |
314eb340e4 | ||
![]() |
f65a588760 | ||
![]() |
6cbd1b3dab | ||
![]() |
7839422a45 | ||
![]() |
81a2b2f324 | ||
8f25717239 | |||
![]() |
c384ac12f5 | ||
![]() |
e0bc0e106d | ||
![]() |
ba8d243ca2 | ||
![]() |
6fae2cd102 | ||
5c6d09ce1e | |||
![]() |
a00962c6d8 | ||
936b518efd | |||
11bb4a8019 | |||
75af4aaa47 | |||
c1d736c713 | |||
cc569aa22f | |||
fc3854594d | |||
f68fed678b | |||
3c5b8193b6 | |||
5083f3caeb | |||
29433c989f | |||
735a9a6249 | |||
![]() |
e9d03ef243 | ||
![]() |
91b6a1e854 | ||
![]() |
b929ed8d55 | ||
![]() |
a7324dae9c | ||
![]() |
041ed36019 | ||
![]() |
1f5d04cfd6 | ||
![]() |
02119c5c58 | ||
7a86b9f092 | |||
17c7fb3db0 | |||
f544dde952 | |||
![]() |
ddd2a4843b | ||
![]() |
ec9d144c67 | ||
![]() |
4fd6506069 | ||
![]() |
25725d4eb6 | ||
![]() |
d9ee367ff2 | ||
![]() |
c5b62f18e9 | ||
![]() |
66b6682952 | ||
![]() |
f71bc3337b | ||
9ee06c9e6d | |||
6b21692146 | |||
816094ce96 | |||
1afb5a5f31 | |||
![]() |
e188887731 | ||
![]() |
f00faf0b62 | ||
![]() |
325d442f2a | ||
![]() |
fc08073309 | ||
![]() |
a2619087cf | ||
![]() |
53cb7ac5f0 | ||
![]() |
60070f9e0c | ||
![]() |
65d82ff3f0 | ||
![]() |
4d4f87c40d | ||
![]() |
73a67be553 | ||
![]() |
b243f5ddce | ||
![]() |
23c55aa9f1 | ||
![]() |
0b86471076 | ||
221ff92826 | |||
614c1971f3 | |||
6f87892a4b | |||
6cc047100a | |||
fabbd15c86 | |||
1ef960f2aa | |||
caadfdff2e | |||
adb4112564 | |||
7ccc9067a9 | |||
![]() |
bb6b4b868c | ||
![]() |
22d5ee3fa6 | ||
![]() |
4c7fbead86 | ||
![]() |
cb26f537f3 | ||
a1d2f0c701 | |||
![]() |
801d5d1459 | ||
![]() |
6d67ca34f0 | ||
![]() |
4d28c956ab | ||
![]() |
6f2545e716 | ||
![]() |
5ebfb22a5e | ||
![]() |
5672300267 | ||
![]() |
a6c4f5a1ec | ||
![]() |
b26c2a0256 | ||
![]() |
9f309c7362 | ||
![]() |
f914297501 | ||
![]() |
aee880aae8 | ||
![]() |
e1ac470df0 | ||
d1d0dd2f76 | |||
![]() |
c9b736e1c7 | ||
![]() |
376d934c61 | ||
![]() |
1eb75f1cee | ||
54a0c74aaf | |||
aebc63b09a | |||
b5ebf71a2b | |||
40ae49f82e | |||
e5947b87c7 | |||
1337b233e7 | |||
568b194f21 | |||
1998822e9c | |||
a3dee77f47 | |||
1bd809b9f0 | |||
40012ef81c | |||
4bf8ba7731 | |||
![]() |
068e130a71 | ||
b4bbe54510 | |||
4ac284d744 | |||
2dd5826e2e | |||
1acb060694 | |||
![]() |
48650d8ddd | ||
909c5f0a25 | |||
![]() |
28c68d7369 | ||
![]() |
598ca1bb1d | ||
4bc9e91591 | |||
3a22d6a61d | |||
4e520ad5a4 | |||
4cea6567eb | |||
75ac5dccb0 | |||
![]() |
92d00c2aa9 | ||
![]() |
88c5ecc67e | ||
![]() |
bb2739f93c | ||
![]() |
68080f90bf | ||
![]() |
91efbfd418 | ||
![]() |
a4094a37f9 | ||
![]() |
6208e76816 | ||
![]() |
b56670daa0 | ||
![]() |
d17d35c35d | ||
![]() |
565b3807bd | ||
![]() |
891329946e | ||
![]() |
8d120212b7 | ||
![]() |
6acff64f17 | ||
![]() |
0a404925ea | ||
![]() |
b66ef17262 | ||
![]() |
662b02a336 | ||
![]() |
a037d8b5e0 | ||
![]() |
f030ce8215 | ||
![]() |
64a53e16e6 | ||
a7e2bbbd87 | |||
![]() |
3a5081e73d | ||
![]() |
dc63ff61ee | ||
![]() |
0d80e600b5 | ||
![]() |
eb5dee5283 | ||
![]() |
e57b760de8 | ||
![]() |
2ca72c6a7c | ||
![]() |
20135fd1db | ||
![]() |
0bdbe0f927 | ||
06542ec421 | |||
d8120b3b18 | |||
![]() |
bde8b2980c | ||
![]() |
ae93e745df | ||
![]() |
ca5fde5f83 | ||
8bbd4a014b | |||
33ae50b63f | |||
2d1a712a20 | |||
5eea6c4ac7 | |||
03dc4db644 | |||
73ee83c9c6 | |||
399391116b | |||
13efc37bdd | |||
93d0784a5e | |||
![]() |
d5a9127c83 | ||
![]() |
c9b67ec5ca | ||
![]() |
fdd91c0fd7 | ||
![]() |
42916c9356 | ||
![]() |
6d1406b2f0 | ||
![]() |
84f6e25adf | ||
![]() |
8b8e3bc0ed | ||
ed9d6eb30d | |||
55020d2c7f | |||
fd0aa9c70d | |||
3f1ef0e0fe | |||
46ecd710b5 | |||
118eed2eb4 | |||
8cb33ade7f | |||
a2636ce0d6 | |||
dd37756f24 | |||
9378d247b9 | |||
e48982cf41 | |||
9768dd58df | |||
3f57632d44 | |||
8c6d284989 | |||
295eb11117 | |||
![]() |
f9a6b584e1 | ||
ddc35369c5 | |||
b27a838c2a | |||
![]() |
95ce162d77 | ||
![]() |
7ab8a576e0 | ||
![]() |
96b457af11 | ||
![]() |
f3dcd08881 | ||
![]() |
2657cb7cf0 | ||
182ad6fea8 | |||
e132e1d663 | |||
![]() |
d0ca66fa49 | ||
2a37ad3b5d | |||
![]() |
454ad4381a | ||
![]() |
b256157c40 | ||
![]() |
4e7351394a | ||
![]() |
a1f5986c21 | ||
![]() |
b03d4f7c54 | ||
![]() |
0446ade568 | ||
![]() |
029a4408d1 | ||
![]() |
1ffd3c4740 | ||
![]() |
98a69a3188 | ||
![]() |
38f40d3d06 | ||
![]() |
53dd21f5bf | ||
![]() |
7ecd87765a | ||
![]() |
f0d7c24c97 | ||
![]() |
6011a31e03 | ||
8258ea609d | |||
5a6bc5f3fb | |||
![]() |
708debe4e4 | ||
![]() |
b09a73135a | ||
8e77b1d9fd | |||
4f2b47fdd2 | |||
7785736d53 | |||
![]() |
8ab6219db2 | ||
![]() |
271e4f1f5d | ||
![]() |
971acea884 | ||
![]() |
e4d36ba86a | ||
![]() |
77385bffcd | ||
![]() |
1201a5da47 | ||
c2a2c667d6 | |||
492f1b6474 | |||
54e085fbdc | |||
![]() |
b7f30ed4d1 | ||
![]() |
742390fb41 | ||
![]() |
85e5fa2a04 | ||
![]() |
254ca6e2c1 | ||
![]() |
7f6a152b03 | ||
![]() |
5dbf11ad88 | ||
![]() |
8db5a4f4da | ||
![]() |
c28a59f6d8 | ||
![]() |
a4a1ec8f41 | ||
![]() |
eb34ba0d57 | ||
![]() |
540ff0dc12 | ||
![]() |
04933a3383 | ||
![]() |
70a8b3e516 | ||
![]() |
e17e080b25 | ||
![]() |
e67b8dd962 | ||
![]() |
361276b797 | ||
![]() |
433c9417c1 | ||
![]() |
525d50ff66 | ||
aedcbcfd83 | |||
![]() |
4f1b8e0bee | ||
![]() |
b7e71762b9 | ||
![]() |
8a9f862bcc | ||
![]() |
bc1c493620 | ||
3ae4066f77 | |||
![]() |
7eecea6739 | ||
![]() |
fa9b9186ac | ||
![]() |
af25f25092 | ||
![]() |
bb07d33611 | ||
![]() |
9d398254f9 | ||
![]() |
b1996628ac | ||
![]() |
9791aa8113 | ||
![]() |
1cfe33a67f | ||
![]() |
72a8e82e32 | ||
![]() |
f466d47f42 | ||
![]() |
cf02e82fc3 | ||
![]() |
ca56ce0b57 | ||
![]() |
e82012d6d6 | ||
![]() |
35eb148b21 | ||
3f79f16c62 | |||
![]() |
f86b715df6 | ||
![]() |
d464850de1 | ||
![]() |
6425580b00 | ||
![]() |
d54be24b37 | ||
![]() |
7e4da93330 | ||
![]() |
ea4adff8c9 | ||
![]() |
5fe6ddacb3 | ||
![]() |
0b02ca4424 | ||
![]() |
dff2aaa9d0 | ||
![]() |
ae0bd09d0e | ||
![]() |
8b70517f3d | ||
![]() |
dcbd20ab3d | ||
![]() |
470223d84c | ||
![]() |
e4f0b54cd5 | ||
![]() |
297628dffa | ||
![]() |
9dbbdbe4b8 | ||
![]() |
aa1757da29 | ||
![]() |
e3b3c469b7 | ||
![]() |
a25cdddb2b | ||
![]() |
d9749ea66d | ||
![]() |
1d2533442c | ||
![]() |
889d9c759d | ||
![]() |
9ccfb6f33d | ||
![]() |
7e661c79dd | ||
![]() |
a5d933bcc2 | ||
![]() |
51a8d62aca | ||
![]() |
3080fdd5b2 | ||
![]() |
adb0c6067f | ||
![]() |
430c5e0766 | ||
![]() |
c3e83ec0ee | ||
![]() |
bb7f9a29c9 | ||
![]() |
d09ae6e588 | ||
![]() |
0f1d7f3894 | ||
![]() |
64413e8bae | ||
![]() |
cebf9f2876 | ||
![]() |
3023317764 | ||
![]() |
735b2fe07b | ||
![]() |
2f105e7d85 | ||
![]() |
a682a756c9 | ||
![]() |
709a47a88e | ||
![]() |
644460e079 | ||
![]() |
1a24115485 | ||
![]() |
9b11495e99 | ||
![]() |
3e9c52cec4 | ||
![]() |
f75d42d9c4 | ||
![]() |
195a3914d1 | ||
![]() |
4b61aaabbf | ||
![]() |
f7fcb010ce | ||
![]() |
1760f98a0c | ||
![]() |
6bb0d4bbee | ||
![]() |
c9c4273d4b | ||
![]() |
3a2ff71f60 | ||
![]() |
e373746cc5 | ||
![]() |
cd9515397c | ||
![]() |
05a0274f1f | ||
![]() |
e6e00582f2 | ||
![]() |
cc8b8bf369 | ||
![]() |
049c325ef3 | ||
![]() |
c68af19e11 | ||
![]() |
2b741c7477 | ||
![]() |
eefa9e3790 | ||
![]() |
bbf201bc7b | ||
![]() |
a632d6a299 | ||
![]() |
9147862d7e | ||
![]() |
41915f120b | ||
![]() |
2d569c386f | ||
![]() |
917c05ae1a | ||
![]() |
22cc429cf9 | ||
![]() |
44f99c5f0c | ||
![]() |
7dba466f73 | ||
![]() |
a5650df053 | ||
![]() |
878b9aa420 | ||
![]() |
82335ef2f5 | ||
![]() |
d900279707 | ||
![]() |
3527e5703b | ||
![]() |
bd73afc32a | ||
![]() |
a68d9bb120 | ||
![]() |
1e3a255b93 | ||
![]() |
59e8120375 | ||
![]() |
b83106d832 | ||
![]() |
83ee85c7dc | ||
![]() |
2bb2a6d3d7 | ||
![]() |
e62f8912fc | ||
![]() |
242ad5fec5 | ||
![]() |
dece1287c4 | ||
![]() |
a3695ef2a8 | ||
![]() |
5b89b6e27f | ||
![]() |
06f23a5988 | ||
![]() |
22b17a391f | ||
![]() |
2f024d8b83 | ||
![]() |
b52804fa65 | ||
![]() |
15234ed711 | ||
![]() |
4b9061b21c | ||
![]() |
3bd497d6c4 | ||
![]() |
151bbe7b53 | ||
![]() |
75f419b948 | ||
![]() |
5e86f53b88 | ||
![]() |
681292ef10 | ||
![]() |
e6c07544da | ||
![]() |
4ecd84f81d | ||
![]() |
6960140e66 | ||
![]() |
85b6d63b8d | ||
![]() |
389b826179 | ||
![]() |
cca756bf92 | ||
![]() |
402b001247 | ||
![]() |
ad310fc721 | ||
![]() |
a82a3698ad | ||
![]() |
3af7b22a08 | ||
![]() |
f85e18f1ed | ||
![]() |
c9745e7110 | ||
![]() |
a9a0b39360 | ||
![]() |
438a47b9f2 | ||
![]() |
5645fd5f47 | ||
![]() |
3c0dbdc615 | ||
![]() |
fcd982e9c8 | ||
![]() |
3501aab7a9 | ||
![]() |
5f70dcd124 | ||
![]() |
b4a28e9fa9 | ||
![]() |
b0db28237b | ||
![]() |
d2429e05d1 | ||
![]() |
2fecc4a6a8 | ||
![]() |
8346795054 | ||
![]() |
6fbc2a2a8b | ||
![]() |
02266e082e | ||
![]() |
0b3872dc93 | ||
![]() |
0bc5a876b4 | ||
![]() |
998386fe8a | ||
![]() |
a2a8b58612 | ||
![]() |
5e78bae4ff | ||
![]() |
af7f8fbcc2 | ||
![]() |
43d5359b37 | ||
![]() |
c9ffa4890c | ||
![]() |
dee919b76c | ||
![]() |
6182d7ac5e | ||
![]() |
82f0e1f3ab | ||
![]() |
b949d0157d | ||
![]() |
b316476684 | ||
![]() |
b2ea634e15 | ||
![]() |
7859d7c951 | ||
![]() |
e90b5dd470 | ||
![]() |
e470eba5f9 | ||
![]() |
b43d937c9c | ||
![]() |
22bbc4147e | ||
![]() |
f0d73f9952 | ||
![]() |
4a0c9a4d85 | ||
![]() |
41cc49a8c2 | ||
![]() |
20f26ecf90 | ||
![]() |
3e3b0b8c95 | ||
![]() |
bf04995748 | ||
![]() |
22b8dc6312 | ||
![]() |
24bc0d2d2f | ||
![]() |
d9b7252397 | ||
![]() |
490efdc062 | ||
![]() |
103fd27032 | ||
![]() |
967a969373 | ||
![]() |
990816da11 | ||
![]() |
166914898a | ||
![]() |
05058773f1 | ||
![]() |
41bb60c934 | ||
![]() |
68f3916fed | ||
![]() |
cc559d0b1e | ||
![]() |
27a1c19ddb | ||
![]() |
5fd29d8323 | ||
![]() |
ce62a34b7a | ||
![]() |
04c5e2371c | ||
![]() |
2e5eb4fdec | ||
![]() |
a4f37fdf2b | ||
![]() |
39be872353 | ||
![]() |
18bcadfe90 | ||
![]() |
8adae94906 | ||
![]() |
23d079313c | ||
![]() |
4b4921cf53 | ||
![]() |
16e35bc5b1 | ||
![]() |
3e1f5738fb | ||
![]() |
6f7dc038f3 | ||
![]() |
9e57b5c30d | ||
![]() |
e6992804d1 | ||
![]() |
c9ece2d968 | ||
![]() |
cefd46ec2a | ||
![]() |
4c604fdf7e | ||
![]() |
ca4aa67acb | ||
![]() |
54d9d5b80f | ||
![]() |
e4bcd58c19 | ||
![]() |
7043eec8ad | ||
![]() |
1e294a5cd7 | ||
![]() |
465c4f7562 | ||
![]() |
1d1b15e143 | ||
![]() |
cca271aec3 | ||
![]() |
3344be60b4 | ||
![]() |
4cdf575515 | ||
![]() |
3980c34ea1 | ||
![]() |
07a158fd38 | ||
![]() |
7882227492 | ||
![]() |
eaced8531a | ||
![]() |
d36a488245 | ||
![]() |
9de786f327 | ||
![]() |
69fefaadc3 | ||
![]() |
62c0d8c6b6 | ||
![]() |
ac3ddd5101 | ||
![]() |
a90732eb95 | ||
![]() |
7a4e74533f | ||
![]() |
bd9d2bd44f | ||
![]() |
455bc946fa | ||
![]() |
e780032e3e | ||
![]() |
c3bbb9fe6d | ||
![]() |
7e97946dc7 | ||
![]() |
359a50169a | ||
![]() |
b2d2edc404 | ||
![]() |
86f65afd14 | ||
![]() |
e644020f06 | ||
![]() |
d7afd900c4 | ||
![]() |
54f8da322d | ||
![]() |
165938b30d | ||
![]() |
9d5e98a9a3 | ||
![]() |
040803bbc2 | ||
![]() |
bdaf941d34 | ||
![]() |
18a208e873 | ||
![]() |
9a5d521397 | ||
![]() |
cfd036c7aa | ||
![]() |
1f3effba04 | ||
![]() |
f2b73e1dff | ||
![]() |
9ac3381891 | ||
![]() |
f4af336760 | ||
![]() |
936c087637 | ||
![]() |
19c0f384a5 | ||
![]() |
aa8eb39dab | ||
![]() |
4139277cff | ||
![]() |
1e13d730b4 | ||
![]() |
4c7e4881e4 | ||
![]() |
e69657065e | ||
![]() |
b68e749876 | ||
![]() |
afd749415b | ||
![]() |
ac07ce6220 | ||
![]() |
ea418e2741 | ||
![]() |
ed162554db | ||
![]() |
045825e64b | ||
![]() |
687024ab60 | ||
![]() |
77e8dfc02a | ||
![]() |
f69f306430 | ||
![]() |
82e7228393 | ||
![]() |
e170624b22 | ||
![]() |
46ce8c7024 | ||
![]() |
e3302d0723 | ||
![]() |
56dadd6c5b | ||
![]() |
6dd8258163 | ||
![]() |
dfe7e51d9c | ||
![]() |
c6686f1658 | ||
![]() |
54279249a4 | ||
![]() |
a247a3950b | ||
![]() |
08f5412458 | ||
![]() |
45954b5144 | ||
![]() |
47273d6887 | ||
![]() |
5ee7954f95 | ||
![]() |
332cc15810 | ||
![]() |
0cc6eb2f79 | ||
![]() |
bc9e94d3d1 | ||
![]() |
baacee3fd3 | ||
![]() |
3ef22877cc | ||
![]() |
3f236c3f25 | ||
![]() |
d4e460c5e9 | ||
![]() |
44e8b9615c | ||
![]() |
d76a981dfb | ||
![]() |
ef22020403 | ||
![]() |
db64e74f7d | ||
![]() |
eafb464a77 | ||
![]() |
65d96528d7 | ||
![]() |
4b83baa6cc | ||
![]() |
7d0c867544 | ||
![]() |
b8dec91885 | ||
![]() |
93e3b76b97 | ||
![]() |
d23355ce48 | ||
![]() |
590178e77d | ||
![]() |
eb968a7d01 | ||
![]() |
fc3956a7fc | ||
![]() |
1164bf56ef | ||
![]() |
a75427254a | ||
![]() |
7245722254 | ||
![]() |
e01a6d19f3 | ||
![]() |
35c0152f6d | ||
![]() |
e93e954bad | ||
![]() |
cb6a6eb5e3 | ||
![]() |
735e12b414 | ||
![]() |
c59a5edb49 | ||
![]() |
cd56fec0e8 | ||
![]() |
8135c800e4 | ||
![]() |
c777b88521 | ||
![]() |
ecc28576ae | ||
![]() |
358769deb5 | ||
![]() |
b121ccbe7f | ||
![]() |
b2d8e2df22 | ||
![]() |
32aa3e3d8f | ||
![]() |
b70ef95b39 | ||
![]() |
307af0246b | ||
![]() |
f91739389f | ||
![]() |
a1b0ffdfdc | ||
![]() |
f32153b99e | ||
![]() |
ccfa70ad78 | ||
![]() |
7e40653e26 | ||
![]() |
f5ea58426d | ||
![]() |
da450d7088 | ||
![]() |
4503de147b | ||
![]() |
49311e5e4a | ||
![]() |
00e429a22f | ||
![]() |
14bb931567 | ||
![]() |
7e406d2280 | ||
![]() |
b2df573207 | ||
![]() |
025bb70a58 | ||
![]() |
2172abf9dd | ||
![]() |
10cae1981c | ||
![]() |
809455d05d | ||
![]() |
197cd5028c | ||
![]() |
8c620bb55d | ||
![]() |
006821f5d4 | ||
![]() |
72a207b4c0 | ||
![]() |
b85104821e | ||
![]() |
cf0587d9f0 | ||
![]() |
a1a83f50ba | ||
![]() |
f469416073 | ||
![]() |
bb34304614 | ||
![]() |
e5aa42860d | ||
![]() |
03075e7a8c | ||
![]() |
c58e37ce39 | ||
![]() |
0bd63149b7 | ||
![]() |
ec5dc28d55 | ||
![]() |
e09105cc79 | ||
![]() |
a5e7b3da6f | ||
![]() |
9dedb7829d | ||
![]() |
57be2cdf41 | ||
![]() |
0b19051891 | ||
![]() |
a779b96370 | ||
![]() |
1c24c69999 | ||
![]() |
5e335a8564 | ||
![]() |
a209216656 | ||
![]() |
8548a4ff70 | ||
![]() |
7bec8a0a85 | ||
![]() |
a55760299c | ||
![]() |
11cecf8452 | ||
![]() |
8f4aa27329 | ||
![]() |
fed9065cf1 | ||
![]() |
356f46237c | ||
![]() |
e230f1472c | ||
![]() |
c9ef21bff9 | ||
![]() |
f23d11be60 | ||
![]() |
4f3cc7f316 | ||
![]() |
f27c26907d | ||
![]() |
aa0d92a2b4 | ||
![]() |
04f42b3802 | ||
![]() |
cdd963e79e | ||
![]() |
d2bb4a776f | ||
![]() |
8a46c7a0d5 | ||
![]() |
36d762514c | ||
![]() |
94c91db097 | ||
![]() |
6a7be4a058 | ||
![]() |
d285a79667 | ||
![]() |
52fe9bc873 | ||
![]() |
4fe2da2fcd | ||
![]() |
018dd48a66 | ||
![]() |
4479fc7681 | ||
![]() |
1cff10fd95 | ||
![]() |
ccc9cff23d | ||
![]() |
bb9aaad899 | ||
![]() |
f24e0f9814 | ||
![]() |
12e6aba5d4 | ||
![]() |
e265375475 | ||
![]() |
b6f0317bde | ||
![]() |
644f227ab3 | ||
![]() |
a5e22f6d63 | ||
![]() |
deef54fe13 | ||
![]() |
ce9f9eec25 | ||
![]() |
3f405b3484 | ||
![]() |
3571670b98 | ||
![]() |
a5bd39dc4a | ||
![]() |
505adffcdc | ||
![]() |
ab02a42725 | ||
![]() |
d788131202 | ||
![]() |
91c02c3ec4 | ||
![]() |
d726eaf195 | ||
![]() |
95b1e103cb | ||
![]() |
7911c374e8 | ||
![]() |
6dd1420f91 | ||
![]() |
ce3ce4f1b6 | ||
![]() |
7831da83fc | ||
![]() |
b476b66007 | ||
![]() |
2fa37278db | ||
![]() |
6e33de568d | ||
![]() |
8496b021a9 | ||
![]() |
65ea7b78c5 | ||
![]() |
3515fdabe7 | ||
![]() |
aa98ca337e | ||
![]() |
126ee42b9d | ||
![]() |
d069a4c9fd | ||
![]() |
33ef3f9b01 | ||
![]() |
33ef1d2123 | ||
![]() |
6644ff666a | ||
![]() |
7a7574bdca | ||
![]() |
6da7f4d25a | ||
![]() |
ae4eb4d3d9 | ||
![]() |
632d0a76d0 | ||
![]() |
cb97029d0d | ||
![]() |
3f29170f1b | ||
![]() |
ce62673a2c | ||
![]() |
aa0d3395b1 | ||
![]() |
b6f3d44080 | ||
![]() |
293543878a | ||
![]() |
6c334a3b44 | ||
![]() |
d46bf4e2f2 | ||
![]() |
02c3c28a16 | ||
![]() |
6991ca270e | ||
![]() |
129eb154b3 | ||
![]() |
633c223282 | ||
![]() |
cad8019068 | ||
![]() |
5f043d0a29 | ||
![]() |
913420e040 | ||
![]() |
130b32d08b | ||
![]() |
2712235f15 | ||
![]() |
6cb3039b71 | ||
![]() |
71fde35938 | ||
![]() |
c2e3ff162f | ||
![]() |
7cf3fccda5 | ||
![]() |
e64b01b690 | ||
![]() |
6b04b3c671 | ||
![]() |
5f10badee9 | ||
![]() |
8412299fa5 | ||
![]() |
4e635229b4 | ||
![]() |
f02226449a | ||
![]() |
cac428b30c | ||
![]() |
5267098cb8 | ||
![]() |
fd2e9df30c | ||
![]() |
01446aad12 | ||
![]() |
6f301505e3 | ||
![]() |
0b1c67574a | ||
![]() |
8943bb0bb3 | ||
![]() |
95a9b7b72b | ||
![]() |
4e39262223 | ||
![]() |
81daf934fb | ||
![]() |
f6b4b2ac18 | ||
![]() |
1663efcf9d | ||
![]() |
1b0f5fd24c | ||
![]() |
69a6631742 | ||
![]() |
86ddb4ecbb | ||
![]() |
9265ebea43 | ||
![]() |
59ee8a990f | ||
![]() |
4e27112147 | ||
![]() |
a9b29c7232 | ||
![]() |
513c8f7b68 | ||
![]() |
dc725641c3 | ||
![]() |
8f6ea6dd94 | ||
![]() |
f84d0b9027 | ||
![]() |
b469855126 | ||
![]() |
af91ddf637 | ||
![]() |
e3dbca5616 | ||
![]() |
84bc3b0f7b | ||
![]() |
3e579ee1d4 | ||
![]() |
deefe7225d | ||
![]() |
5176377045 | ||
![]() |
88f6088a42 | ||
![]() |
7160cd650e | ||
![]() |
c3146e1794 | ||
![]() |
63491b0b9a | ||
![]() |
fd2b41c1f0 | ||
![]() |
bc2257be08 | ||
![]() |
4767a63178 | ||
![]() |
99ee2e36ba | ||
![]() |
a723ff8e66 | ||
![]() |
26130c43ea | ||
![]() |
c8eaebce9a | ||
![]() |
709a248d10 | ||
![]() |
2808b1b8b2 | ||
![]() |
dd27026618 | ||
![]() |
e8b530a783 | ||
![]() |
01207dfcbb | ||
![]() |
b056124e49 | ||
![]() |
4f96263e3b | ||
![]() |
e2016340e1 | ||
![]() |
cd01191072 | ||
![]() |
3eb755ee7e | ||
![]() |
93f748e6a8 | ||
![]() |
d310abc95c | ||
![]() |
186ad5ca86 | ||
![]() |
abe4bdd6ae | ||
![]() |
19b70663d7 | ||
![]() |
4c827cf105 | ||
![]() |
c4b98e94ce | ||
![]() |
77176faece | ||
![]() |
38bf3276e4 | ||
![]() |
89fe3db556 | ||
![]() |
985c113190 | ||
![]() |
8dac72e924 | ||
![]() |
a42256b296 | ||
![]() |
36b78e5f98 | ||
![]() |
295f597699 | ||
![]() |
dcf5d699bd | ||
![]() |
9768161327 | ||
![]() |
c898f97493 | ||
![]() |
7c3126866c | ||
![]() |
2caad90755 | ||
![]() |
199931dc1a | ||
![]() |
f7a47f1b9e | ||
![]() |
e0a4b479f9 | ||
![]() |
48726950d9 | ||
![]() |
caaf741691 | ||
![]() |
c53f13bf90 | ||
![]() |
028fd3df15 | ||
![]() |
d2de4419bd | ||
![]() |
fe66c51a19 |
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
@ -0,0 +1,4 @@
|
|||
**/.git
|
||||
**/.gitignore
|
||||
/local-instances
|
||||
**/.gitkeep
|
44
.gitignore
vendored
44
.gitignore
vendored
|
@ -7,6 +7,8 @@
|
|||
/*/config.php
|
||||
/*.html
|
||||
/*.xml
|
||||
/vendor/*
|
||||
/vendor
|
||||
|
||||
# include some files though
|
||||
!/templates/*.html
|
||||
|
@ -18,6 +20,9 @@
|
|||
# instance-config
|
||||
/inc/instance-config.php
|
||||
|
||||
# captcha-config
|
||||
/inc/captchaconfig.php
|
||||
|
||||
# .installed
|
||||
/.installed
|
||||
|
||||
|
@ -39,6 +44,7 @@ Thumbs.db
|
|||
*.orig
|
||||
*~
|
||||
~*/
|
||||
php_errors.log
|
||||
|
||||
# tmp filesystem
|
||||
/tmp/cache/*
|
||||
|
@ -49,6 +55,7 @@ Thumbs.db
|
|||
#vichan custom
|
||||
favicon.ico
|
||||
/static/spoiler.png
|
||||
/local-instances
|
||||
|
||||
piwik/
|
||||
jwplayer/
|
||||
|
@ -59,10 +66,37 @@ tf/
|
|||
[._]s[a-w][a-z]
|
||||
|
||||
# special boards
|
||||
all/
|
||||
mod/
|
||||
random/
|
||||
/all/
|
||||
/mod/
|
||||
/random/
|
||||
|
||||
# Banners
|
||||
banners/*
|
||||
!banners/lain-bottom.png
|
||||
static/banners/*
|
||||
|
||||
#Fonts
|
||||
stylesheets/fonts
|
||||
|
||||
#Images from twemoji
|
||||
js/twemoji/16x16/
|
||||
|
||||
#Boards
|
||||
AKM/
|
||||
alt/
|
||||
anime/
|
||||
dead/
|
||||
draw/
|
||||
edu/
|
||||
games/
|
||||
hobby/
|
||||
leftypol/
|
||||
leftypol_archive/
|
||||
meta/
|
||||
music/
|
||||
overboard/
|
||||
roulette/
|
||||
roulette_archive/
|
||||
sfw/
|
||||
siberia/
|
||||
siberia_archive/
|
||||
tech/
|
||||
vi/
|
||||
|
|
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -1,3 +1,9 @@
|
|||
[submodule "js/wPaint"]
|
||||
path = js/wPaint
|
||||
url = https://github.com/vichan-devel/wPaint.git
|
||||
branch = master
|
||||
|
||||
[submodule "inc/lib/parsedown"]
|
||||
path = inc/lib/parsedown
|
||||
url = https://github.com/vichan-devel/parsedown
|
||||
branch = master
|
||||
|
|
17
403.php
17
403.php
|
@ -1,17 +0,0 @@
|
|||
<title>403</title>
|
||||
</head>
|
||||
<!-- <body style="background: black"> -->
|
||||
<body style="background-image:url(/static/system.gif)">
|
||||
<center><img height=480 width=640 src="/static/403.jpg"/>
|
||||
<marquee scrollamount="40"><h1><p style="font-family: sans-serif; font-size:30px; color: black;">WHOOPS</p></h1></marquee>
|
||||
<p style="color: blue;background:black">this isn't for you</p>
|
||||
<p style="color: red;background:black">it's a 403</p>
|
||||
<br /> <br />
|
||||
<param name="movie" value="/static/congrats.swf">
|
||||
</center>
|
||||
<audio autoplay loop>
|
||||
<source src="/static/cyberia.ogg" />
|
||||
</audio>
|
||||
</body>
|
||||
</html>
|
||||
|
21
404.html
Normal file
21
404.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>404 - Page Not Found</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: #1E1E1E;
|
||||
color: #999999;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<center>
|
||||
<h1>Page not found</h1>
|
||||
<img style="width:750px;height:420px" src="/static/404.webp"/>
|
||||
<br/>
|
||||
<p>It may have been pruned, moved, merged, removed, or never existed.</p>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
|
32
404.php
32
404.php
|
@ -1,32 +0,0 @@
|
|||
<title>404</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 0 !important;
|
||||
padding: 0px !important;
|
||||
overflow-x:hidden!important;
|
||||
background-size: cover !important;
|
||||
background-color: #222 !important;
|
||||
background-image:
|
||||
url(https://gs1.wac.edgecastcdn.net/8019B6/data.tumblr.com/tumblr_mc0xu6C22f1qbj9bko1_500.gif);
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: 0!important;
|
||||
background-attachment: fixed !important;
|
||||
background-size: cover !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<center><!--<img src="/static/lain_2_090.jpg"/>-->
|
||||
<marquee scrollamount="40"><h1><p style="font-family: sans-serif; font-size:30px; color: white;">WHOOPS</p></h1></marquee>
|
||||
<p style="color: blue;background:black">There's nothing here</p>
|
||||
<p style="color: red;background:black">it's a 404</p>
|
||||
<br /> <br />
|
||||
<audio autoplay loop>
|
||||
<source src="/static/duvet.ogg" type="audio/ogg" />
|
||||
<source src="/static/duvet.mp3" type="audio/mpeg" />
|
||||
</audio>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
|
29
Dockerfile
Normal file
29
Dockerfile
Normal file
|
@ -0,0 +1,29 @@
|
|||
FROM php:8.1.8-fpm
|
||||
|
||||
COPY . /code
|
||||
|
||||
RUN docker-php-ext-install pdo pdo_mysql
|
||||
RUN apt-get update -y && apt-get install -y libpng-dev libjpeg-dev libonig-dev
|
||||
RUN docker-php-ext-install mbstring
|
||||
RUN apt-get update -y && apt-get install -y libmcrypt-dev
|
||||
# RUN docker-php-ext-install -j$(nproc) mcrypt
|
||||
RUN docker-php-ext-install iconv
|
||||
RUN apt-get update -y && apt-get install -y imagemagick
|
||||
RUN apt-get update -y && apt-get install -y graphicsmagick
|
||||
RUN apt-get update -y && apt-get install -y gifsicle
|
||||
# RUN docker-php-ext-configure gd
|
||||
# --with-jpeg=/usr/include
|
||||
# --with-png-dir=/usr \
|
||||
RUN docker-php-ext-install gd
|
||||
RUN apt-get update -y \
|
||||
&& apt-get install -y libmemcached11 libmemcachedutil2 build-essential libmemcached-dev libz-dev git \
|
||||
&& pecl install memcached \
|
||||
&& echo extension=memcached.so >> /usr/local/etc/php/conf.d/memcached.ini \
|
||||
&& apt-get remove -y build-essential libmemcached-dev libz-dev \
|
||||
&& apt-get autoremove -y \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /tmp/pear \
|
||||
&& curl -sS https://getcomposer.org/installer -o composer-setup.php \
|
||||
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
|
||||
&& docker-php-ext-install bcmath \
|
||||
&& cd /code && composer install
|
82
README.md
82
README.md
|
@ -1,97 +1,89 @@
|
|||
Lainchan - A fork of vichan
|
||||
leftypol - vichan/lainchan based imageboard software
|
||||
========================================================
|
||||
|
||||
About
|
||||
------------
|
||||
Lainchan is a fork of [vichan](http://github.com/vichan-devel/vichan),
|
||||
a great imageboard package, actively building on it and adding a lot of features and other
|
||||
The leftypol imageboard is a fork of [lainchan](http://github.com/lainchan/lainchan), a fork of [vichan](http://github.com/vichan-devel/vichan), actively building on it and adding features, bug-fixes and other
|
||||
improvements.
|
||||
|
||||
We highly recommend you read the [vichan GitHub wiki](http://github.com/vichan-devel/vichan/wiki) for a basic guide to features.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
1. PHP >= 5.4 (we still try to keep compatibility with php 5.3 as much as possible)
|
||||
PHP 7.0 is explicitly supported.
|
||||
2. MySQL/MariaDB server
|
||||
3. [mbstring](http://www.php.net/manual/en/mbstring.installation.php)
|
||||
4. [PHP GD](http://www.php.net/manual/en/intro.image.php)
|
||||
5. [PHP PDO](http://www.php.net/manual/en/intro.pdo.php)
|
||||
1. PHP >= 5.4 (we do not actively check if this is still supported)
|
||||
PHP 8.0 is explicitly supported. PHP 7.x should be compatable.
|
||||
2. MySQL/MariaDB server >= 5.5.3
|
||||
3. [Composer](https://getcomposer.org/) (To install various packages)
|
||||
4. [mbstring](http://www.php.net/manual/en/mbstring.installation.php)
|
||||
5. [PHP GD](http://www.php.net/manual/en/intro.image.php)
|
||||
6. [PHP PDO](http://www.php.net/manual/en/intro.pdo.php)
|
||||
|
||||
We try to make sure lainchan is compatible with all major web servers and
|
||||
operating systems. lainchan does not include an Apache ```.htaccess``` file nor does
|
||||
This should be compatible with all major web servers and
|
||||
operating systems. This code does not include an Apache ```.htaccess``` file nor does
|
||||
it need one.
|
||||
|
||||
### Recommended
|
||||
1. MySQL/MariaDB server >= 5.5.3
|
||||
2. ImageMagick (command-line ImageMagick or GraphicsMagick preferred).
|
||||
3. [APC (Alternative PHP Cache)](http://php.net/manual/en/book.apc.php),
|
||||
1. ImageMagick (command-line ImageMagick or GraphicsMagick preferred).
|
||||
2. [APC (Alternative PHP Cache)](http://php.net/manual/en/book.apc.php),
|
||||
Redis,
|
||||
[XCache](http://xcache.lighttpd.net/) or
|
||||
[Memcached](http://www.php.net/manual/en/intro.memcached.php)
|
||||
|
||||
Contributing
|
||||
------------
|
||||
You can contribute to lainchan by:
|
||||
* Developing patches/improvements/translations and using GitHub to submit pull requests
|
||||
You can contribute to leftypol, and upstream imageboard softwares, by:
|
||||
* Developing patches/improvements/translations and using this repo to submit pull requests
|
||||
* Providing feedback and suggestions
|
||||
* Writing/editing documentation
|
||||
|
||||
If you need help developing a patch, please join our IRC channel.
|
||||
|
||||
> irc.freenode.net @ #lainchan-dev
|
||||
If you need help developing a patch, please reply to the sticky on our [/tech/](https://leftypol.org/tech/) board.
|
||||
|
||||
Installation
|
||||
-------------
|
||||
1. Download and extract lainchan to your web directory or get the latest
|
||||
1. Download and extract leftypol to your web directory or get the latest
|
||||
development version with:
|
||||
|
||||
git clone git://github.com/lainchan/lainchan.git
|
||||
git clone git://git.leftypol.org/leftypol/leftypol.git
|
||||
|
||||
2. Navigate to ```install.php``` in your web browser and follow the
|
||||
2. run ```composer install``` inside the directory
|
||||
3. Navigate to ```install.php``` in your web browser and follow the
|
||||
prompts.
|
||||
3. lainchan should now be installed. Log in to ```mod.php``` with the
|
||||
4. leftypol should now be installed. Log in to ```mod.php``` with the
|
||||
default username and password combination: **admin / password**.
|
||||
|
||||
Please remember to change the administrator account password.
|
||||
|
||||
See also: [Configuration Basics](http://tinyboard.org/docs/?p=Config).
|
||||
See also: [Configuration Basics](https://web.archive.org/web/20121003095922/http://tinyboard.org/docs/?p=Config).
|
||||
|
||||
Upgrade
|
||||
-------
|
||||
To upgrade from any version of Tinyboard or vichan:
|
||||
This probably will break if you try and upgrade from vichan or the older (<= August 2021) leftypol versions. Ask us for migration advice if you intend to do so.
|
||||
|
||||
To upgrade from this repo:
|
||||
|
||||
Either run ```git pull``` to update your files, if you used git, or
|
||||
backup your ```inc/instance-config.php```, replace all your files in place
|
||||
(don't remove boards etc.), then put ```inc/instance-config.php``` back and
|
||||
finally run ```install.php```.
|
||||
|
||||
To migrate from a Kusaba X board, use http://github.com/vichan-devel/Tinyboard-Migration
|
||||
To migrate from a Kusaba X board, use http://github.com/vichan-devel/Tinyboard-Migration (untested)
|
||||
|
||||
Support
|
||||
--------
|
||||
If you find a bug, please report it.
|
||||
|
||||
If you need assistance with installing, configuring, or using lainchan, you may
|
||||
If you need assistance with installing, configuring, or using leftypol, you may
|
||||
find support from a variety of sources:
|
||||
|
||||
* If you're unsure about how to enable or configure certain features, make
|
||||
sure you have read the comments in ```inc/config.php```.
|
||||
* You can join lainchan's IRC channel for support
|
||||
[irc.freenode.net #lainchan](irc://irc.freenode.net/lainchan)
|
||||
* For support, reply to the sticky on our [/tech/](https://leftypol.org/tech/) board.
|
||||
|
||||
### Tinyboard support
|
||||
vichan, and by extension lainchan, is based on a Tinyboard, so both engines have very much in common. These
|
||||
links may be helpful for you as well:
|
||||
vichan, and by extension lainchan and leftypol, is based on a Tinyboard, so both engines have very much in common. These links may be helpful for you as well:
|
||||
|
||||
* Tinyboard documentation can be found [here](https://web.archive.org/web/20121016074303/http://tinyboard.org/docs/?p=Main_Page).
|
||||
|
||||
Donations
|
||||
---------
|
||||
Do you like our work? You can motivate us financially to do better ;)
|
||||
* Bitcoin: 18CshTkxW6HRFoBhXo9mLJcjkReMmjvuq9
|
||||
|
||||
You can also ask us to develop some feature specially for you <3. Join our IRC
|
||||
channel and ask for a quote (there are a few of us, who work with the codebase
|
||||
and are skilled enough to develop such features pretty quickly).
|
||||
|
||||
CLI tools
|
||||
-----------------
|
||||
There are a few command line interface tools, based on Tinyboard-Tools. These need
|
||||
|
@ -104,13 +96,13 @@ at the power users. You won't be able to run these from shared hosting accounts
|
|||
|
||||
Localisation
|
||||
------------
|
||||
Wanting to have lainchan in your language? You can contribute your translations to vichan at this URL:
|
||||
Wanting to have leftypol in your language? You can contribute your translations to vichan at this URL:
|
||||
|
||||
https://www.transifex.com/projects/p/tinyboard-vichan-devel/
|
||||
|
||||
Oekaki
|
||||
------
|
||||
lainchan makes use of [wPaint](https://github.com/websanova/wPaint) for oekaki. After you pull the repository, however, you will need to download wPaint separately using git's `submodule` feature. Use the following commands:
|
||||
leftypol makes use of [wPaint](https://github.com/websanova/wPaint) for oekaki. After you pull the repository, however, you will need to download wPaint separately using git's `submodule` feature. Use the following commands:
|
||||
|
||||
```
|
||||
git submodule init
|
||||
|
@ -123,12 +115,12 @@ WebM support
|
|||
------------
|
||||
Read `inc/lib/webm/README.md` for information about enabling webm.
|
||||
|
||||
lainchan API
|
||||
leftypol API
|
||||
----------
|
||||
lainchan provides by default a 4chan-compatible JSON API, just like vichan. For documentation on this, see:
|
||||
leftypol provides by default a 4chan-compatible JSON API, just like vichan. For documentation on this, see:
|
||||
https://github.com/vichan-devel/vichan-API/ .
|
||||
|
||||
License
|
||||
--------
|
||||
See [LICENSE.md](http://github.com/lainchan/lainchan/blob/master/LICENSE.md).
|
||||
See LICENSE.md.
|
||||
|
||||
|
|
47
b.php
47
b.php
|
@ -1,47 +0,0 @@
|
|||
<?php
|
||||
// This script assumes there is at least one normal (non-priority)
|
||||
// banner!
|
||||
|
||||
// Get the files in a directory, returns null if the directory does
|
||||
// not exist.
|
||||
function getFilesInDirectory($dir) {
|
||||
if (! is_dir($dir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_diff(scandir($dir), array('.', '..'));
|
||||
}
|
||||
|
||||
// Serve a random banner and exit.
|
||||
function serveRandomBanner($dir, $files) {
|
||||
$name = $files[array_rand($files)];
|
||||
|
||||
// snags the extension
|
||||
$ext = pathinfo($name, PATHINFO_EXTENSION);
|
||||
|
||||
// send the right headers
|
||||
header('Cache-Control: no-cache, no-store, must-revalidate'); // HTTP 1.1
|
||||
header('Pragma: no-cache'); // HTTP 1.0
|
||||
header('Expires: 0'); // Proxies
|
||||
header("Content-type: image/" . $ext);
|
||||
header("Content-Disposition: inline; filename=" . $name);
|
||||
|
||||
// readfile displays the image, passthru seems to spits stream.
|
||||
readfile($dir.$name);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get all the banners
|
||||
$bannerDir = "banners/";
|
||||
$priorityDir = "banners_priority/";
|
||||
|
||||
$banners = getFilesInDirectory($bannerDir);
|
||||
$priority = getFilesInDirectory($priorityDir);
|
||||
|
||||
// If there are priority banners, serve 1/3rd of the time.
|
||||
if($priority !== null && count($priority) !== 0 && rand(0,2) === 0) {
|
||||
serveRandomBanner($priorityDir, $priority);
|
||||
}
|
||||
|
||||
serveRandomBanner($bannerDir, $banners);
|
||||
?>
|
|
@ -1,6 +1,5 @@
|
|||
<?php
|
||||
require_once 'inc/functions.php';
|
||||
require_once 'inc/bans.php';
|
||||
require_once 'inc/bootstrap.php';
|
||||
checkBan();
|
||||
|
||||
//If the user is not banned, show the "not banned" page.
|
||||
|
|
26
banners.php
26
banners.php
|
@ -1,22 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Lainchan Banners</title>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
function listBannersInDir($dir) {
|
||||
if ($handle = opendir($dir)) {
|
||||
while (false !== ($entry = readdir($handle))) {
|
||||
if ($entry != "." && $entry != "..") {
|
||||
echo "<a href=\"$dir/$entry\"><img src=\"$dir/$entry\" alt=\"$entry\" style=\"width:348px;height:128px\"></a> ";
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
}
|
||||
}
|
||||
|
||||
listBannersInDir("banners_priority");
|
||||
listBannersInDir("banners");
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
$files = scandir(__dir__ . '/static/banners/', SCANDIR_SORT_NONE);
|
||||
$files = array_diff($files, ['.', '..']);
|
||||
|
||||
$filename = $files[array_rand($files)];
|
||||
header("Location: /static/banners/$filename", true, 307);
|
||||
header('Cache-Control: no-cache');
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 116 KiB |
32
captcha.php
Normal file
32
captcha.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
require_once 'inc/functions.php';
|
||||
require_once 'inc/lib/securimage/securimage.php';
|
||||
|
||||
if(!isset($config['securimage']) || !$config['securimage']){
|
||||
error('Securimage captcha not enabled.'); //TODO error image
|
||||
}
|
||||
|
||||
$image=new Securimage(array('config_file'=>__DIR__ . '/inc/captchaconfig.php'));
|
||||
|
||||
$image->show();
|
||||
|
||||
$code=$image->getCode(false, true);
|
||||
|
||||
$ip=$_SERVER['REMOTE_ADDR'];
|
||||
|
||||
$query=prepare('INSERT INTO captchas(ip, code, time) VALUES(:ip, :code, NOW())');
|
||||
$query->bindValue(':ip', $ip);
|
||||
$query->bindValue(':code', $code);
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
$query=prepare('SELECT count(*) from captchas where ip=:ip');
|
||||
$query->bindValue(':ip', $ip);
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
$count=$query->fetch()[0];
|
||||
if($count>10){
|
||||
$query=prepare('DELETE from captchas where ip=:ip ORDER BY time asc LIMIT 1');
|
||||
$query->bindValue(':ip', $ip);
|
||||
$query->execute()or error(db_error($query));
|
||||
}
|
48
composer.json
Normal file
48
composer.json
Normal file
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"name": "leftypol/leftypol",
|
||||
"description": "leftypol imageboard",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"twig/twig": "^1.44.2",
|
||||
"lifo/ip": "^1.0",
|
||||
"gettext/gettext": "^1.0",
|
||||
"mrclay/minify": "^2.1.6"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["inc/"],
|
||||
"files": [
|
||||
"inc/bootstrap.php",
|
||||
"inc/display.php",
|
||||
"inc/template.php",
|
||||
"inc/database.php",
|
||||
"inc/events.php",
|
||||
"inc/api.php",
|
||||
"inc/mod/auth.php",
|
||||
"inc/lock.php",
|
||||
"inc/queue.php",
|
||||
"inc/polyfill.php",
|
||||
"inc/error.php",
|
||||
"inc/functions.php",
|
||||
"inc/functions/net.php"
|
||||
]
|
||||
},
|
||||
"license": "Tinyboard + vichan",
|
||||
"authors": [
|
||||
{
|
||||
"name": "tinyboard contributors",
|
||||
"homepage": "https://github.com/savetheinternet/Tinyboard"
|
||||
},
|
||||
{
|
||||
"name": "vichan contributors",
|
||||
"homepage": "https://github.com/vichan-devel/vichan/"
|
||||
},
|
||||
{
|
||||
"name": "lainchan contributors",
|
||||
"homepage": "https://github.com/lainchan/lainchan/"
|
||||
},
|
||||
{
|
||||
"name": "leftypol contributors",
|
||||
"homepage": "https://git.leftypol.org/leftypol/leftypol/"
|
||||
}
|
||||
]
|
||||
}
|
330
composer.lock
generated
Normal file
330
composer.lock
generated
Normal file
|
@ -0,0 +1,330 @@
|
|||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "346d80deda89b0298a414b565213f312",
|
||||
"packages": [
|
||||
{
|
||||
"name": "gettext/gettext",
|
||||
"version": "v1.1.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-gettext/Gettext.git",
|
||||
"reference": "1bdf755a1b49f0614d6fc29f446df567eb62cd5c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-gettext/Gettext/zipball/1bdf755a1b49f0614d6fc29f446df567eb62cd5c",
|
||||
"reference": "1bdf755a1b49f0614d6fc29f446df567eb62cd5c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Gettext": ""
|
||||
},
|
||||
"files": [
|
||||
"Gettext/translator_functions.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"AGPL-3.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Oscar Otero",
|
||||
"email": "oom@oscarotero.com",
|
||||
"homepage": "http://oscarotero.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "PHP - JS gettext conversor",
|
||||
"homepage": "https://github.com/oscarotero/Gettext",
|
||||
"keywords": [
|
||||
"JS",
|
||||
"gettext",
|
||||
"i18n",
|
||||
"translation"
|
||||
],
|
||||
"support": {
|
||||
"email": "oom@oscarotero.com",
|
||||
"issues": "https://github.com/oscarotero/Gettext/issues",
|
||||
"source": "https://github.com/php-gettext/Gettext/tree/v1.1.5"
|
||||
},
|
||||
"time": "2014-10-22T15:53:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lifo/ip",
|
||||
"version": "v1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lifo101/ip.git",
|
||||
"reference": "b6a36dab288d7aea155698808bfc6649799fe413"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/lifo101/ip/zipball/b6a36dab288d7aea155698808bfc6649799fe413",
|
||||
"reference": "b6a36dab288d7aea155698808bfc6649799fe413",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-bcmath": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Lifo\\IP\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jason Morriss",
|
||||
"email": "lifo2013@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "IP address helper PHP library for working with IPv4 and IPv6 addresses",
|
||||
"keywords": [
|
||||
"IP",
|
||||
"ip address",
|
||||
"ipv4",
|
||||
"ipv6"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/lifo101/ip/issues",
|
||||
"source": "https://github.com/lifo101/ip/tree/master"
|
||||
},
|
||||
"time": "2020-04-02T11:09:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mrclay/minify",
|
||||
"version": "2.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mrclay/minify.git",
|
||||
"reference": "1928e89208d28e91427b2f13b67acdbd8cd01ac9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mrclay/minify/zipball/1928e89208d28e91427b2f13b67acdbd8cd01ac9",
|
||||
"reference": "1928e89208d28e91427b2f13b67acdbd8cd01ac9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pcre": "*",
|
||||
"php": ">=5.2.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"tubalmartin/cssmin": "~2.4.8"
|
||||
},
|
||||
"suggest": {
|
||||
"tubalmartin/cssmin": "Support minify with CSSMin (YUI PHP port)"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"min/lib/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Stephen Clay",
|
||||
"email": "steve@mrclay.org",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Minify is a PHP5 app that helps you follow several rules for client-side performance. It combines multiple CSS or Javascript files, removes unnecessary whitespace and comments, and serves them with gzip encoding and optimal client-side cache headers",
|
||||
"homepage": "http://code.google.com/p/minify/",
|
||||
"support": {
|
||||
"email": "minify@googlegroups.com",
|
||||
"issues": "http://code.google.com/p/minify/issues/list",
|
||||
"source": "https://github.com/mrclay/minify/tree/2.x",
|
||||
"wiki": "http://code.google.com/p/minify/w/list"
|
||||
},
|
||||
"time": "2017-11-03T21:04:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.24.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Ctype\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gert de Pagter",
|
||||
"email": "BackEndTea@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for ctype functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"ctype",
|
||||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-10-20T20:35:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v1.44.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "ae39480f010ef88adc7938503c9b02d3baf2f3b3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/ae39480f010ef88adc7938503c9b02d3baf2f3b3",
|
||||
"reference": "ae39480f010ef88adc7938503c9b02d3baf2f3b3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/container": "^1.0",
|
||||
"symfony/phpunit-bridge": "^4.4.9|^5.0.9"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.44-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Twig_": "lib/"
|
||||
},
|
||||
"psr-4": {
|
||||
"Twig\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com",
|
||||
"homepage": "http://fabien.potencier.org",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Twig Team",
|
||||
"role": "Contributors"
|
||||
},
|
||||
{
|
||||
"name": "Armin Ronacher",
|
||||
"email": "armin.ronacher@active-4.com",
|
||||
"role": "Project Founder"
|
||||
}
|
||||
],
|
||||
"description": "Twig, the flexible, fast, and secure template language for PHP",
|
||||
"homepage": "https://twig.symfony.com",
|
||||
"keywords": [
|
||||
"templating"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v1.44.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/twig/twig",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-25T13:31:46+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
40
docker-compose.yml
Normal file
40
docker-compose.yml
Normal file
|
@ -0,0 +1,40 @@
|
|||
services:
|
||||
#nginx webserver + php 8.x
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./docker/nginx/Dockerfile
|
||||
ports:
|
||||
- "9091:80"
|
||||
depends_on:
|
||||
- leftypol-db
|
||||
volumes:
|
||||
- ./local-instances/1/www:/var/www/html
|
||||
- ./docker/nginx/leftypol.conf:/etc/nginx/conf.d/default.conf
|
||||
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./docker/nginx/proxy.conf:/etc/nginx/conf.d/proxy.conf
|
||||
links:
|
||||
- php
|
||||
|
||||
php:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./docker/php/Dockerfile
|
||||
volumes:
|
||||
- ./local-instances/1/www:/var/www
|
||||
- ./docker/php/www.conf:/usr/local/etc/php-fpm.d/www.conf
|
||||
|
||||
#MySQL Service
|
||||
leftypol-db:
|
||||
image: mysql:8.0.35
|
||||
container_name: leftypol-db
|
||||
restart: unless-stopped
|
||||
tty: true
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
MYSQL_DATABASE: vichan
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
command: "--default-authentication-plugin=mysql_native_password"
|
||||
volumes:
|
||||
- ./local-instances/1/mysql:/var/lib/mysql
|
16
docker/doc.md
Normal file
16
docker/doc.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
The `php-fpm` process runs containerized.
|
||||
The php application always uses `/var/www` as it's work directory and home folder, and if `/var/www` is bind mounted it
|
||||
is necessary to adjust the path passed via FastCGI to `php-fpm` by changing the root directory to `/var/www`.
|
||||
This can achieved in nginx by setting the `fastcgi_param SCRIPT_FILENAME` to `/var/www/$fastcgi_script_name;`
|
||||
|
||||
The default docker compose settings are intended for development and testing purposes.
|
||||
The folder structure expected by compose is as follows
|
||||
|
||||
```
|
||||
<vichan-project>
|
||||
└── local-instances
|
||||
└── 1
|
||||
├── mysql
|
||||
└── www
|
||||
```
|
||||
The vichan container is by itself much less rigid.
|
8
docker/nginx/Dockerfile
Normal file
8
docker/nginx/Dockerfile
Normal file
|
@ -0,0 +1,8 @@
|
|||
FROM nginx:1.25.3-alpine
|
||||
|
||||
COPY . /code
|
||||
RUN adduser --system www-data \
|
||||
&& adduser www-data www-data
|
||||
|
||||
CMD [ "nginx", "-g", "daemon off;" ]
|
||||
EXPOSE 80
|
65
docker/nginx/leftypol.conf
Normal file
65
docker/nginx/leftypol.conf
Normal file
|
@ -0,0 +1,65 @@
|
|||
upstream php-upstream {
|
||||
server php:9000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server ipv6only=on;
|
||||
server_name leftypol;
|
||||
root /var/www/html;
|
||||
add_header X-Frame-Options "SAMEORIGIN";
|
||||
add_header X-Content-Type-Options "nosniff";
|
||||
|
||||
index index.html index.php;
|
||||
|
||||
charset utf-8;
|
||||
|
||||
location ~ ^([^.\?]*[^\/])$ {
|
||||
try_files $uri @addslash;
|
||||
}
|
||||
|
||||
# Expire rules for static content
|
||||
# Media: images, icons, video, audio, HTC
|
||||
location ~* \.(?:jpg|jpeg|gif|png|webp|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
|
||||
log_not_found off;
|
||||
# Public cache, never changes until max-age expires, max-age of 1 month, can still be served while being
|
||||
# revalidated or if the server is erroring for 1 day.
|
||||
add_header Cache-Control "public, immutable, max-age=2592000, stale-while-revalidate=86400, stale-if-error=86400";
|
||||
}
|
||||
# CSS and Javascript
|
||||
location ~* \.(?:css|js)$ {
|
||||
log_not_found off;
|
||||
# Public cache, max-age of 1 year, can still be served while being revalidated or if the server is erroring for 1 day.
|
||||
add_header Cache-Control "public, max-age=31536000, stale-while-revalidate=86400, stale-if-error=86400";
|
||||
}
|
||||
|
||||
location ~* \.(html)$ {
|
||||
expires -1;
|
||||
}
|
||||
|
||||
location @addslash {
|
||||
return 301 $uri/;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
client_max_body_size 2G;
|
||||
|
||||
location ~ \.php$ {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Request-Id $x_request_id;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header Forwarded-Request-Id $x_request_id;
|
||||
fastcgi_pass php-upstream;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/$fastcgi_script_name;
|
||||
fastcgi_read_timeout 600;
|
||||
include fastcgi_params;
|
||||
}
|
||||
|
||||
location = /favicon.ico { access_log off; log_not_found off; }
|
||||
location = /robots.txt { access_log off; log_not_found off; }
|
||||
}
|
35
docker/nginx/nginx.conf
Normal file
35
docker/nginx/nginx.conf
Normal file
|
@ -0,0 +1,35 @@
|
|||
# This and proxy.conf are based on
|
||||
# https://github.com/dead-guru/devichan/blob/master/nginx/nginx.conf
|
||||
|
||||
user www-data;
|
||||
worker_processes auto;
|
||||
# daemon off;
|
||||
# error_log /var/log/nginx/error.log warn;
|
||||
error_log /dev/stdout warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
#access_log /var/log/nginx/access.log;
|
||||
# Switch logging to console out to view via Docker
|
||||
access_log /dev/stdout;
|
||||
error_log /dev/stdout warn;
|
||||
sendfile on;
|
||||
keepalive_timeout 5;
|
||||
|
||||
gzip on;
|
||||
gzip_http_version 1.0;
|
||||
gzip_vary on;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/xml text/plain text/css application/xhtml+xml application/xml application/rss+xml application/atom_xml application/x-javascript application/x-httpd-php;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-available/*.conf;
|
||||
}
|
40
docker/nginx/proxy.conf
Normal file
40
docker/nginx/proxy.conf
Normal file
|
@ -0,0 +1,40 @@
|
|||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=czone:4m max_size=50m inactive=120m;
|
||||
proxy_temp_path /var/tmp/nginx;
|
||||
proxy_cache_key "$scheme://$host$request_uri";
|
||||
|
||||
|
||||
map $http_forwarded_request_id $x_request_id {
|
||||
"" $request_id;
|
||||
default $http_forwarded_request_id;
|
||||
}
|
||||
|
||||
map $http_forwarded_forwarded_host $forwardedhost {
|
||||
"" $host;
|
||||
default $http_forwarded_forwarded_host;
|
||||
}
|
||||
|
||||
|
||||
map $http_x_forwarded_proto $fcgi_https {
|
||||
default "";
|
||||
https on;
|
||||
}
|
||||
|
||||
map $http_x_forwarded_proto $real_scheme {
|
||||
default $scheme;
|
||||
https https;
|
||||
}
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Server $host;
|
||||
|
||||
real_ip_header X-Forwarded-For;
|
||||
|
||||
set_real_ip_from 10.0.0.0/8;
|
||||
set_real_ip_from 172.16.0.0/12;
|
||||
set_real_ip_from 172.18.0.0;
|
||||
set_real_ip_from 192.168.0.0/24;
|
||||
set_real_ip_from 127.0.0.0/8;
|
||||
|
||||
real_ip_recursive on;
|
87
docker/php/Dockerfile
Normal file
87
docker/php/Dockerfile
Normal file
|
@ -0,0 +1,87 @@
|
|||
# Based on https://github.com/dead-guru/devichan/blob/master/php-fpm/Dockerfile
|
||||
|
||||
FROM composer AS composer
|
||||
FROM php:7.2-fpm-alpine
|
||||
|
||||
RUN apk add --no-cache \
|
||||
zlib \
|
||||
zlib-dev \
|
||||
libpng \
|
||||
libpng-dev \
|
||||
libjpeg-turbo \
|
||||
libjpeg-turbo-dev \
|
||||
libwebp \
|
||||
libwebp-dev \
|
||||
libcurl \
|
||||
curl-dev \
|
||||
imagemagick \
|
||||
graphicsmagick \
|
||||
gifsicle \
|
||||
ffmpeg \
|
||||
bind-tools \
|
||||
gettext \
|
||||
gettext-dev \
|
||||
icu-dev \
|
||||
oniguruma \
|
||||
oniguruma-dev \
|
||||
libmcrypt \
|
||||
libmcrypt-dev \
|
||||
lz4-libs \
|
||||
lz4-dev \
|
||||
imagemagick-dev \
|
||||
pcre-dev \
|
||||
$PHPIZE_DEPS \
|
||||
&& docker-php-ext-configure gd \
|
||||
--with-webp-dir=/usr/include/webp \
|
||||
--with-jpeg-dir=/usr/include \
|
||||
&& docker-php-ext-install -j$(nproc) \
|
||||
gd \
|
||||
curl \
|
||||
bcmath \
|
||||
opcache \
|
||||
pdo_mysql \
|
||||
gettext \
|
||||
intl \
|
||||
mbstring \
|
||||
&& pecl update-channels \
|
||||
&& pecl install -o -f igbinary \
|
||||
&& pecl install redis \
|
||||
&& pecl install imagick \
|
||||
$$ docker-php-ext-enable \
|
||||
igbinary \
|
||||
redis \
|
||||
imagick \
|
||||
&& apk del \
|
||||
zlib-dev \
|
||||
libpng-dev \
|
||||
libjpeg-turbo-dev \
|
||||
libwebp-dev \
|
||||
curl-dev \
|
||||
gettext-dev \
|
||||
oniguruma-dev \
|
||||
libmcrypt-dev \
|
||||
lz4-dev \
|
||||
imagemagick-dev \
|
||||
pcre-dev \
|
||||
$PHPIZE_DEPS \
|
||||
&& rm -rf /var/cache/*
|
||||
RUN rmdir /var/www/html \
|
||||
&& install -d -m 744 -o www-data -g www-data /var/www \
|
||||
&& install -d -m 700 -o www-data -g www-data /var/tmp/vichan \
|
||||
&& install -d -m 700 -o www-data -g www-data /var/cache/gen-cache \
|
||||
&& install -d -m 700 -o www-data -g www-data /var/cache/template-cache
|
||||
|
||||
# Copy the bootstrap script.
|
||||
COPY ./docker/php/bootstrap.sh /usr/local/bin/bootstrap.sh
|
||||
|
||||
COPY --from=composer /usr/bin/composer /usr/local/bin/composer
|
||||
|
||||
# Copy the actual project (use .dockerignore to exclude stuff).
|
||||
COPY . /code
|
||||
|
||||
# Install the compose depedencies.
|
||||
RUN cd /code && composer install
|
||||
|
||||
WORKDIR "/var/www"
|
||||
CMD [ "bootstrap.sh" ]
|
||||
EXPOSE 9000
|
79
docker/php/bootstrap.sh
Executable file
79
docker/php/bootstrap.sh
Executable file
|
@ -0,0 +1,79 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
function set_cfg() {
|
||||
if [ ! -f "/var/www/inc/$1" ]; then
|
||||
echo "INFO: Resetting $1"
|
||||
touch "/var/www/inc/$1"
|
||||
chown www-data "/var/www/inc/$1"
|
||||
chgrp www-data "/var/www/inc/$1"
|
||||
chmod 600 "/var/www/inc/$1"
|
||||
else
|
||||
echo "INFO: Using existing $1"
|
||||
fi
|
||||
}
|
||||
|
||||
if ! mountpoint -q /var/www; then
|
||||
echo "WARNING: '/var/www' is not a mountpoint. All the data will remain inside the container!"
|
||||
fi
|
||||
|
||||
if [ ! -w /var/www ] ; then
|
||||
echo "ERROR: '/var/www' is not writable. Closing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Link the entrypoints from the exposed directory.
|
||||
ln -nfs \
|
||||
/code/tools/ \
|
||||
/code/walls/ \
|
||||
/code/*.php \
|
||||
/code/LICENSE.* \
|
||||
/code/404.html \
|
||||
/code/install.sql \
|
||||
/var/www/
|
||||
# Static files accessible from the webserver must be copied.
|
||||
cp -ur /code/static /var/www/
|
||||
cp -ur /code/stylesheets /var/www/
|
||||
|
||||
# Ensure correct permissions are set, since this might be bind mount.
|
||||
chown www-data /var/www
|
||||
chgrp www-data /var/www
|
||||
|
||||
# Initialize an empty robots.txt with the default if it doesn't exist.
|
||||
touch /var/www/robots.txt
|
||||
|
||||
# Link the cache and tmp files directory.
|
||||
ln -nfs /var/tmp/vichan /var/www/tmp
|
||||
|
||||
# Link the javascript directory.
|
||||
ln -nfs /code/js /var/www/
|
||||
|
||||
# Link the html templates directory and it's cache.
|
||||
ln -nfs /code/templates /var/www/
|
||||
ln -nfs -T /var/cache/template-cache /var/www/templates/cache
|
||||
chown -h www-data /var/www/templates/cache
|
||||
chgrp -h www-data /var/www/templates/cache
|
||||
|
||||
# Link the generic cache.
|
||||
ln -nfs -T /var/cache/gen-cache /var/www/tmp/cache
|
||||
chown -h www-data /var/www/tmp/cache
|
||||
chgrp -h www-data /var/www/tmp/cache
|
||||
|
||||
# Create the included files directory and link them
|
||||
install -d -m 700 -o www-data -g www-data /var/www/inc
|
||||
for file in /code/inc/*; do
|
||||
file="${file##*/}"
|
||||
if [ ! -e /var/www/inc/$file ]; then
|
||||
ln -s /code/inc/$file /var/www/inc/
|
||||
fi
|
||||
done
|
||||
|
||||
# Copy an empty instance configuration if the file is a link (it was linked because it did not exist before).
|
||||
set_cfg 'instance-config.php'
|
||||
|
||||
# Link the composer dependencies.
|
||||
ln -nfs /code/vendor /var/www/
|
||||
|
||||
# Start the php-fpm server.
|
||||
exec php-fpm
|
15
docker/php/www.conf
Normal file
15
docker/php/www.conf
Normal file
|
@ -0,0 +1,15 @@
|
|||
[www]
|
||||
access.log = /proc/self/fd/2
|
||||
|
||||
; Ensure worker stdout and stderr are sent to the main error log.
|
||||
catch_workers_output = yes
|
||||
|
||||
user = www-data
|
||||
group = www-data
|
||||
|
||||
listen = 127.0.0.1:9000
|
||||
pm = static
|
||||
pm.max_children = 16
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
|
@ -220,63 +220,3 @@ function _create_antibot($board, $thread) {
|
|||
return $antibot;
|
||||
}
|
||||
|
||||
function checkSpam(array $extra_salt = array()) {
|
||||
global $config, $pdo;
|
||||
|
||||
if (!isset($_POST['hash']))
|
||||
return true;
|
||||
|
||||
$hash = $_POST['hash'];
|
||||
|
||||
if (!empty($extra_salt)) {
|
||||
// create a salted hash of the "extra salt"
|
||||
$extra_salt = implode(':', $extra_salt);
|
||||
} else {
|
||||
$extra_salt = '';
|
||||
}
|
||||
|
||||
// Reconsturct the $inputs array
|
||||
$inputs = array();
|
||||
|
||||
foreach ($_POST as $name => $value) {
|
||||
if (in_array($name, $config['spam']['valid_inputs']))
|
||||
continue;
|
||||
|
||||
$inputs[$name] = $value;
|
||||
}
|
||||
|
||||
// Sort the inputs in alphabetical order (A-Z)
|
||||
ksort($inputs);
|
||||
|
||||
$_hash = '';
|
||||
|
||||
// Iterate through each input
|
||||
foreach ($inputs as $name => $value) {
|
||||
$_hash .= $name . '=' . $value;
|
||||
}
|
||||
|
||||
// Add a salt to the hash
|
||||
$_hash .= $config['cookies']['salt'];
|
||||
|
||||
// Use SHA1 for the hash
|
||||
$_hash = sha1($_hash . $extra_salt);
|
||||
|
||||
if ($hash != $_hash)
|
||||
return true;
|
||||
|
||||
$query = prepare('SELECT `passed` FROM ``antispam`` WHERE `hash` = :hash');
|
||||
$query->bindValue(':hash', $hash);
|
||||
$query->execute() or error(db_error($query));
|
||||
if ((($passed = $query->fetchColumn(0)) === false) || ($passed > $config['spam']['hidden_inputs_max_pass'])) {
|
||||
// there was no database entry for this hash. most likely expired.
|
||||
return true;
|
||||
}
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
function incrementSpamHash($hash) {
|
||||
$query = prepare('UPDATE ``antispam`` SET `passed` = `passed` + 1 WHERE `hash` = :hash');
|
||||
$query->bindValue(':hash', $hash);
|
||||
$query->execute() or error(db_error($query));
|
||||
}
|
||||
|
|
83
inc/api.php
83
inc/api.php
|
@ -32,18 +32,22 @@ class Api {
|
|||
'images' => 'images',
|
||||
'sticky' => 'sticky',
|
||||
'locked' => 'locked',
|
||||
'cycle' => 'cyclical',
|
||||
'bump' => 'last_modified',
|
||||
'embed' => 'embed',
|
||||
'board' => 'board',
|
||||
);
|
||||
|
||||
$this->threadsPageFields = array(
|
||||
'id' => 'no',
|
||||
'bump' => 'last_modified'
|
||||
'bump' => 'last_modified',
|
||||
'board' => 'board',
|
||||
);
|
||||
|
||||
$this->fileFields = array(
|
||||
'thumbheight' => 'tn_h',
|
||||
'thumbwidth' => 'tn_w',
|
||||
'file_id' => 'id',
|
||||
'type' => 'mime',
|
||||
'extension' => 'ext',
|
||||
'height' => 'h',
|
||||
'width' => 'w',
|
||||
'size' => 'fsize',
|
||||
|
@ -87,14 +91,43 @@ class Api {
|
|||
}
|
||||
|
||||
private function translateFile($file, $post, &$apiPost) {
|
||||
global $config;
|
||||
|
||||
$this->translateFields($this->fileFields, $file, $apiPost);
|
||||
$apiPost['filename'] = @substr($file->name, 0, strrpos($file->name, '.'));
|
||||
$dotPos = strrpos($file->file, '.');
|
||||
$apiPost['ext'] = substr($file->file, $dotPos);
|
||||
$apiPost['tim'] = substr($file->file, 0, $dotPos);
|
||||
if (isset ($file->thumb) && $file->thumb) {
|
||||
$apiPost['spoiler'] = $file->thumb === 'spoiler';
|
||||
}
|
||||
if (isset ($file->hash) && $file->hash) {
|
||||
$apiPost['md5'] = base64_encode(hex2bin($file->hash));
|
||||
}
|
||||
else if (isset ($post->filehash) && $post->filehash) {
|
||||
$apiPost['md5'] = base64_encode(hex2bin($post->filehash));
|
||||
}
|
||||
|
||||
$apiPost['file_path'] = $config['uri_img'] . $file->file;
|
||||
|
||||
// Pick the correct thumbnail
|
||||
if (isset($file->thumb) && $file->thumb === 'spoiler') {
|
||||
// Spoiler
|
||||
$apiPost['thumb_path'] = $config['root'] . $config['spoiler_image'];
|
||||
} else if (!isset($file->thumb) || $file->thumb === 'file') {
|
||||
// Default file format image
|
||||
$thumbFile = $config['file_icons']['default'];
|
||||
if (isset($file->extension) && isset($config['file_icons'][$file->extension])) {
|
||||
$thumbFile = $config['file_icons'][$file->extension];
|
||||
}
|
||||
|
||||
$apiPost['thumb_path'] = $config['root'] . sprintf($config['file_thumb'], $thumbFile);
|
||||
} else {
|
||||
// The file's own thumbnail
|
||||
$apiPost['thumb_path'] = $config['uri_thumb'] . $file->thumb;
|
||||
}
|
||||
}
|
||||
|
||||
private function translatePost($post, $threadsPage = false) {
|
||||
global $config, $board;
|
||||
$apiPost = array();
|
||||
|
@ -104,16 +137,27 @@ class Api {
|
|||
if (isset($config['poster_ids']) && $config['poster_ids']) $apiPost['id'] = poster_id($post->ip, $post->thread, $board['uri']);
|
||||
if ($threadsPage) return $apiPost;
|
||||
|
||||
// Handle country field
|
||||
if (isset($post->body_nomarkup) && $this->config['country_flags']) {
|
||||
// Load board info
|
||||
if (isset($post->board)) {
|
||||
openBoard($post->board);
|
||||
}
|
||||
|
||||
// Handle special fields
|
||||
if (isset($post->body_nomarkup) && ($this->config['country_flags'] || $this->config['user_flag'])) {
|
||||
$modifiers = extract_modifiers($post->body_nomarkup);
|
||||
if (isset($modifiers['flag']) && isset($modifiers['flag alt']) && preg_match('/^[a-z]{2}$/', $modifiers['flag'])) {
|
||||
$country = strtoupper($modifiers['flag']);
|
||||
if (isset($modifiers['flag']) && isset($modifiers['flag alt']) && preg_match('/^[1-9a-z_-]{2,}$/', $modifiers['flag'])) {
|
||||
$country = strtolower($modifiers['flag']);
|
||||
if ($country) {
|
||||
$apiPost['country'] = $country;
|
||||
$apiPost['country_name'] = $modifiers['flag alt'];
|
||||
}
|
||||
}
|
||||
if (isset($modifiers['warning message'])) {
|
||||
$apiPost['warning_msg'] = $modifiers['warning message'];
|
||||
}
|
||||
if (isset($modifiers['ban message'])) {
|
||||
$apiPost['ban_msg'] = $modifiers['ban message'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($config['slugify'] && !$post->thread) {
|
||||
|
@ -121,21 +165,13 @@ class Api {
|
|||
}
|
||||
|
||||
// Handle files
|
||||
// Note: 4chan only supports one file, so only the first file is taken into account for 4chan-compatible API.
|
||||
if (isset($post->files) && $post->files && !$threadsPage) {
|
||||
$file = $post->files[0];
|
||||
$this->translateFile($file, $post, $apiPost);
|
||||
if (sizeof($post->files) > 1) {
|
||||
$extra_files = array();
|
||||
foreach ($post->files as $i => $f) {
|
||||
if ($i == 0) continue;
|
||||
$apiPost['files'] = [];
|
||||
foreach ($post->files as $f) {
|
||||
$file = array();
|
||||
$this->translateFile($f, $post, $file);
|
||||
|
||||
$extra_file = array();
|
||||
$this->translateFile($f, $post, $extra_file);
|
||||
|
||||
$extra_files[] = $extra_file;
|
||||
}
|
||||
$apiPost['extra_files'] = $extra_files;
|
||||
$apiPost['files'][] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,6 +188,13 @@ class Api {
|
|||
$apiPosts['posts'][] = $this->translatePost($p, $threadsPage);
|
||||
}
|
||||
|
||||
// Count unique IPs
|
||||
$ips = array($thread->ip);
|
||||
foreach ($thread->posts as $p) {
|
||||
$ips[] = $p->ip;
|
||||
}
|
||||
$apiPosts['posts'][0]['unique_ips'] = count(array_unique($ips));
|
||||
|
||||
return $apiPosts;
|
||||
}
|
||||
|
||||
|
|
88
inc/bans.php
88
inc/bans.php
|
@ -1,9 +1,5 @@
|
|||
<?php
|
||||
|
||||
require 'inc/lib/IP/Lifo/IP/IP.php';
|
||||
require 'inc/lib/IP/Lifo/IP/BC.php';
|
||||
require 'inc/lib/IP/Lifo/IP/CIDR.php';
|
||||
|
||||
use Lifo\IP\CIDR;
|
||||
|
||||
class Bans {
|
||||
|
@ -16,12 +12,14 @@ class Bans {
|
|||
return $ipstr;
|
||||
}
|
||||
|
||||
if (strlen($ipstart) != strlen($ipend))
|
||||
if (strlen($ipstart) != strlen($ipend)) {
|
||||
return '???'; // What the fuck are you doing, son?
|
||||
}
|
||||
|
||||
$range = CIDR::range_to_cidr(inet_ntop($ipstart), inet_ntop($ipend));
|
||||
if ($range !== false)
|
||||
if ($range !== false) {
|
||||
return $range;
|
||||
}
|
||||
|
||||
return '???';
|
||||
}
|
||||
|
@ -47,31 +45,31 @@ class Bans {
|
|||
|
||||
if (isset($matches[2])) {
|
||||
// Years
|
||||
$expire += $matches[2]*60*60*24*365;
|
||||
$expire += (int)$matches[2]*60*60*24*365;
|
||||
}
|
||||
if (isset($matches[4])) {
|
||||
// Months
|
||||
$expire += $matches[4]*60*60*24*30;
|
||||
$expire += (int)$matches[4]*60*60*24*30;
|
||||
}
|
||||
if (isset($matches[6])) {
|
||||
// Weeks
|
||||
$expire += $matches[6]*60*60*24*7;
|
||||
$expire += (int)$matches[6]*60*60*24*7;
|
||||
}
|
||||
if (isset($matches[8])) {
|
||||
// Days
|
||||
$expire += $matches[8]*60*60*24;
|
||||
$expire += (int)$matches[8]*60*60*24;
|
||||
}
|
||||
if (isset($matches[10])) {
|
||||
// Hours
|
||||
$expire += $matches[10]*60*60;
|
||||
$expire += (int)$matches[10]*60*60;
|
||||
}
|
||||
if (isset($matches[12])) {
|
||||
// Minutes
|
||||
$expire += $matches[12]*60;
|
||||
$expire += (int)$matches[12]*60;
|
||||
}
|
||||
if (isset($matches[14])) {
|
||||
// Seconds
|
||||
$expire += $matches[14];
|
||||
$expire += (int)$matches[14];
|
||||
}
|
||||
|
||||
return time() + $expire;
|
||||
|
@ -105,12 +103,12 @@ class Bans {
|
|||
list($ipstart, $ipend) = self::calc_cidr($mask);
|
||||
} elseif (preg_match('@^[:a-z\d]+/\d+$@i', $mask)) {
|
||||
list($ipv6, $bits) = explode('/', $mask);
|
||||
if ($bits > 128)
|
||||
if ($bits > 128) {
|
||||
return false;
|
||||
}
|
||||
|
||||
list($ipstart, $ipend) = self::calc_cidr($mask);
|
||||
} else {
|
||||
if (($ipstart = @inet_pton($mask)) === false)
|
||||
} elseif (($ipstart = @inet_pton($mask)) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -139,8 +137,9 @@ class Bans {
|
|||
if ($ban['expires'] && ($ban['seen'] || !$config['require_ban_view']) && $ban['expires'] < time()) {
|
||||
self::delete($ban['id']);
|
||||
} else {
|
||||
if ($ban['post'])
|
||||
if ($ban['post']) {
|
||||
$ban['post'] = json_decode($ban['post'], true);
|
||||
}
|
||||
$ban['mask'] = self::range_to_string(array($ban['ipstart'], $ban['ipend']));
|
||||
$ban_list[] = $ban;
|
||||
}
|
||||
|
@ -155,7 +154,9 @@ class Bans {
|
|||
ORDER BY `created` DESC") or error(db_error());
|
||||
$bans = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($board_access && $board_access[0] == '*') $board_access = false;
|
||||
if ($board_access && $board_access[0] == '*') {
|
||||
$board_access = false;
|
||||
}
|
||||
|
||||
$out ? fputs($out, "[") : print("[");
|
||||
|
||||
|
@ -174,7 +175,7 @@ class Bans {
|
|||
|
||||
if ($ban['post'] && !$hide_message) {
|
||||
$post = json_decode($ban['post']);
|
||||
$ban['message'] = $post->body;
|
||||
$ban['message'] = isset($post->body) ? $post->body : 0;
|
||||
}
|
||||
unset($ban['ipstart'], $ban['ipend'], $ban['post'], $ban['creator']);
|
||||
|
||||
|
@ -209,23 +210,24 @@ class Bans {
|
|||
}
|
||||
|
||||
$out ? fputs($out, "]") : print("]");
|
||||
|
||||
}
|
||||
|
||||
static public function seen($ban_id) {
|
||||
$query = query("UPDATE ``bans`` SET `seen` = 1 WHERE `id` = " . (int)$ban_id) or error(db_error());
|
||||
query("UPDATE ``bans`` SET `seen` = 1 WHERE `id` = " . (int)$ban_id) or error(db_error());
|
||||
rebuildThemes('bans');
|
||||
}
|
||||
|
||||
static public function purge() {
|
||||
$query = query("DELETE FROM ``bans`` WHERE `expires` IS NOT NULL AND `expires` < " . time() . " AND `seen` = 1") or error(db_error());
|
||||
query("DELETE FROM ``bans`` WHERE `expires` IS NOT NULL AND `expires` < " . time() . " AND `seen` = 1") or error(db_error());
|
||||
rebuildThemes('bans');
|
||||
}
|
||||
|
||||
static public function delete($ban_id, $modlog = false, $boards = false, $dont_rebuild = false) {
|
||||
global $config;
|
||||
|
||||
if ($boards && $boards[0] == '*') $boards = false;
|
||||
if ($boards && $boards[0] == '*') {
|
||||
$boards = false;
|
||||
}
|
||||
|
||||
if ($modlog) {
|
||||
$query = query("SELECT `ipstart`, `ipend`, `board` FROM ``bans`` WHERE `id` = " . (int)$ban_id) or error(db_error());
|
||||
|
@ -234,8 +236,9 @@ class Bans {
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($boards !== false && !in_array($ban['board'], $boards))
|
||||
if ($boards !== false && !in_array($ban['board'], $boards)) {
|
||||
error($config['error']['noaccess']);
|
||||
}
|
||||
|
||||
$mask = self::range_to_string(array($ban['ipstart'], $ban['ipend']));
|
||||
|
||||
|
@ -245,7 +248,9 @@ class Bans {
|
|||
|
||||
query("DELETE FROM ``bans`` WHERE `id` = " . (int)$ban_id) or error(db_error());
|
||||
|
||||
if (!$dont_rebuild) rebuildThemes('bans');
|
||||
if (!$dont_rebuild) {
|
||||
rebuildThemes('bans');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -263,10 +268,11 @@ class Bans {
|
|||
$query = prepare("INSERT INTO ``bans`` VALUES (NULL, :ipstart, :ipend, :time, :expires, :board, :mod, :reason, 0, :post)");
|
||||
|
||||
$query->bindValue(':ipstart', $range[0]);
|
||||
if ($range[1] !== false && $range[1] != $range[0])
|
||||
if ($range[1] !== false && $range[1] != $range[0]) {
|
||||
$query->bindValue(':ipend', $range[1]);
|
||||
else
|
||||
} else {
|
||||
$query->bindValue(':ipend', null, PDO::PARAM_NULL);
|
||||
}
|
||||
|
||||
$query->bindValue(':mod', $mod_id);
|
||||
$query->bindValue(':time', time());
|
||||
|
@ -275,8 +281,9 @@ class Bans {
|
|||
$reason = escape_markup_modifiers($reason);
|
||||
markup($reason);
|
||||
$query->bindValue(':reason', $reason);
|
||||
} else
|
||||
} else {
|
||||
$query->bindValue(':reason', null, PDO::PARAM_NULL);
|
||||
}
|
||||
|
||||
if ($length) {
|
||||
if (is_int($length) || ctype_digit($length)) {
|
||||
|
@ -289,29 +296,28 @@ class Bans {
|
|||
$query->bindValue(':expires', null, PDO::PARAM_NULL);
|
||||
}
|
||||
|
||||
if ($ban_board)
|
||||
if ($ban_board) {
|
||||
$query->bindValue(':board', $ban_board);
|
||||
else
|
||||
} else {
|
||||
$query->bindValue(':board', null, PDO::PARAM_NULL);
|
||||
}
|
||||
|
||||
if ($post) {
|
||||
$post['board'] = $board['uri'];
|
||||
$query->bindValue(':post', json_encode($post));
|
||||
} else
|
||||
} else {
|
||||
$query->bindValue(':post', null, PDO::PARAM_NULL);
|
||||
}
|
||||
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
if (isset($mod['id']) && $mod['id'] == $mod_id) {
|
||||
modLog('Created a new ' .
|
||||
($length > 0 ? preg_replace('/^(\d+) (\w+?)s?$/', '$1-$2', until($length)) : 'permanent') .
|
||||
' ban on ' .
|
||||
($ban_board ? '/' . $ban_board . '/' : 'all boards') .
|
||||
' for ' .
|
||||
(filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$mask\">$mask</a>" : $mask) .
|
||||
' (<small>#' . $pdo->lastInsertId() . '</small>)' .
|
||||
' with ' . ($reason ? 'reason: ' . utf8tohtml($reason) . '' : 'no reason'));
|
||||
}
|
||||
$ban_len = $length > 0 ? preg_replace('/^(\d+) (\w+?)s?$/', '$1-$2', until($length)) : 'permanent';
|
||||
$ban_board = $ban_board ? "/$ban_board/" : 'all boards';
|
||||
$ban_ip = filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$mask\">$mask</a>" : $mask;
|
||||
$ban_id = $pdo->lastInsertId();
|
||||
$ban_reason = $reason ? 'reason: ' . utf8tohtml($reason) : 'no reason';
|
||||
|
||||
modLog("Created a new $ban_len ban on $ban_board for $ban_ip (<small># $ban_id </small>) with $ban_reason");
|
||||
|
||||
rebuildThemes('bans');
|
||||
|
||||
|
|
3
inc/bootstrap.php
Normal file
3
inc/bootstrap.php
Normal file
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
@define('TINYBOARD', 'xD');
|
||||
require_once('vendor/autoload.php');
|
|
@ -118,11 +118,15 @@ class Cache {
|
|||
|
||||
switch ($config['cache']['enabled']) {
|
||||
case 'memcached':
|
||||
case 'redis':
|
||||
if (!self::$cache)
|
||||
self::init();
|
||||
self::$cache->delete($key);
|
||||
break;
|
||||
case 'redis':
|
||||
if (!self::$cache)
|
||||
self::init();
|
||||
self::$cache->del($key);
|
||||
break;
|
||||
case 'apc':
|
||||
apc_delete($key);
|
||||
break;
|
||||
|
|
90
inc/captchaconfig.php.sample
Normal file
90
inc/captchaconfig.php.sample
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
Securimage sample config file (rename to config.inc.php to activate)
|
||||
Place your custom configuration in this file to make settings global so they
|
||||
are applied to the captcha image, audio playback, and validation.
|
||||
Using this file is optional but makes settings managing settings easier,
|
||||
especially when upgrading to a new version.
|
||||
When a new Securimage object is created, if config.inc.php is found in the
|
||||
Securimage directory, these settings will be applied *before* any settings
|
||||
passed to the constructor (so options passed in will override these).
|
||||
This file is especially useful if you use a custom database or session
|
||||
configuration and is easier than modifying securimage.php directly.
|
||||
Any class property from securimage.php can be used here.
|
||||
*/
|
||||
|
||||
return array(
|
||||
/**** CAPTCHA Appearance Options ****/
|
||||
|
||||
'image_width' => 275, // width of captcha image in pixels
|
||||
'image_height' => 100, // height of captcha image in pixels
|
||||
'code_length' => 6, // # of characters for captcha code
|
||||
'image_bg_color' => '#770000', // hex color for image background
|
||||
'text_color' => '#DDDD64', // hex color for captcha text
|
||||
'line_color' => '#DDDD64', // hex color for lines over text
|
||||
'noise_color' => '#DDDD64', // color of random noise to draw under text
|
||||
'num_lines' => 5, // # of lines to draw over text
|
||||
'noise_level' => 0.5, // how much random noise to add (0-10)
|
||||
'perturbation' => 0.75, // distoration level
|
||||
|
||||
'use_random_spaces' => true,
|
||||
'use_random_baseline' => true,
|
||||
'use_text_angles' => true,
|
||||
'use_random_boxes' => false,
|
||||
'use_transparent_text' => false,
|
||||
|
||||
'wordlist_file' => 'words/words.txt', // text file for word captcha
|
||||
'use_wordlist' => false, // true to use word list
|
||||
'wordlist_file_encoding' => null, // character encoding of word file if other than ASCII (e.g. UTF-8, GB2312)
|
||||
|
||||
// example UTF-8 charset (TTF file must support symbols being used
|
||||
// 'charset' => "абвгдeжзийклмнопрстуфхцчшщъьюяАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯ",
|
||||
'charset' => "2345689abdfgkmnpqsuwxyz", // capitals are more effort to type, removed confusable characters like o,O,0,1,l
|
||||
|
||||
// 'ttf_file' => './AHGBold.ttf', // TTF file for captcha text
|
||||
|
||||
//'captcha_type' => Securimage::SI_CAPTCHA_WORDS, // Securimage::SI_CAPTCHA_STRING || Securimage:: SI_CAPTCHA_MATHEMATIC || Securimage::SI_CAPTCHA_WORDS
|
||||
|
||||
//'display_value' => 'ABC 123', // Draws custom text on captcha
|
||||
|
||||
|
||||
/**** Code Storage & Database Options ****/
|
||||
|
||||
// true if you *DO NOT* want to use PHP sessions at all, false to use PHP sessions
|
||||
'no_session' => true,
|
||||
|
||||
// the PHP session name to use (null for default PHP session name)
|
||||
// do not change unless you know what you are doing
|
||||
'session_name' => null,
|
||||
|
||||
// change to true to store codes in a database
|
||||
'use_database' => true,
|
||||
|
||||
// database engine to use for storing codes. must have the PDO extension loaded
|
||||
// Values choices are:
|
||||
// Securimage::SI_DRIVER_MYSQL, Securimage::SI_DRIVER_SQLITE3, Securimage::SI_DRIVER_PGSQL
|
||||
'database_driver' => Securimage::SI_DRIVER_MYSQL,
|
||||
|
||||
'database_host' => 'localhost', // database server host to connect to
|
||||
'database_user' => 'lainchan', // database user to connect as
|
||||
'database_pass' => '', // database user password
|
||||
'database_name' => 'lainchan', // name of database to select (you must create this first or use an existing database)
|
||||
'database_table' => 'captcha_codes', // database table for storing codes, will be created automatically
|
||||
|
||||
// Securimage will automatically create the database table if it is not found
|
||||
// change to true for performance reasons once database table is up and running
|
||||
'skip_table_check' => false,
|
||||
|
||||
/**** Audio Options ****/
|
||||
|
||||
//'audio_path' => __DIR__ . '/audio/en/',
|
||||
//'audio_use_noise' => true,
|
||||
//'audio_noise_path' => __DIR__ . '/audio/noise/',
|
||||
//'degrade_audio' => true,
|
||||
|
||||
'no_exit'=>true,
|
||||
|
||||
|
||||
'log_file'=>'/dev/null', //This should be placed somewhere sensible.
|
||||
);
|
422
inc/config.php
422
inc/config.php
|
@ -31,13 +31,16 @@
|
|||
* =======================
|
||||
*/
|
||||
|
||||
// Enables captcha. This may require board rebuilds to allow on mobile (unconfirmed)
|
||||
$config['securimage'] = false;
|
||||
|
||||
// Global announcement -- the very simple version.
|
||||
// This used to be wrongly named $config['blotter'] (still exists as an alias).
|
||||
// $config['global_message'] = 'This is an important announcement!';
|
||||
$config['blotter'] = &$config['global_message'];
|
||||
|
||||
// Automatically check if a newer version of Tinyboard is available when an administrator logs in.
|
||||
$config['check_updates'] = true;
|
||||
$config['check_updates'] = false;
|
||||
// How often to check for updates
|
||||
$config['check_updates_time'] = 43200; // 12 hours
|
||||
|
||||
|
@ -103,7 +106,7 @@
|
|||
|
||||
/*
|
||||
* ====================
|
||||
* Cache settings
|
||||
* Cache, lock and queue settings
|
||||
* ====================
|
||||
*/
|
||||
|
||||
|
@ -120,6 +123,7 @@
|
|||
// $config['cache']['enabled'] = 'apc';
|
||||
// $config['cache']['enabled'] = 'memcached';
|
||||
// $config['cache']['enabled'] = 'redis';
|
||||
// $config['cache']['enabled'] = 'fs';
|
||||
|
||||
// Timeout for cached objects such as posts and HTML.
|
||||
$config['cache']['timeout'] = 60 * 60 * 48; // 48 hours
|
||||
|
@ -142,6 +146,12 @@
|
|||
// (this file will be explicitly loaded during cache hit, but not during cache miss).
|
||||
$config['cache_config'] = false;
|
||||
|
||||
// Define a lock driver.
|
||||
$config['lock']['enabled'] = 'fs';
|
||||
|
||||
// Define a queue driver.
|
||||
$config['queue']['enabled'] = 'fs'; // xD
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* Cookie settings
|
||||
|
@ -162,7 +172,7 @@
|
|||
|
||||
// How long should the cookies last (in seconds). Defines how long should moderators should remain logged
|
||||
// in (0 = browser session).
|
||||
$config['cookies']['expire'] = 60 * 60 * 24 * 30 * 6; // ~6 months
|
||||
$config['cookies']['expire'] = 60 * 60 * 24 * 7; // 1 week.
|
||||
|
||||
// Make this something long and random for security.
|
||||
$config['cookies']['salt'] = 'abcdefghijklmnopqrstuvwxyz09123456789!@#$%^&*()';
|
||||
|
@ -170,6 +180,10 @@
|
|||
// Whether or not you can access the mod cookie in JavaScript. Most users should not need to change this.
|
||||
$config['cookies']['httponly'] = true;
|
||||
|
||||
// Do not allow logins via unencrypted HTTP. Should only be changed in testing environments or if you connect to a
|
||||
// load-balancer without encryption.
|
||||
$config['cookies']['secure_login_only'] = true;
|
||||
|
||||
// Used to salt secure tripcodes ("##trip") and poster IDs (if enabled).
|
||||
$config['secure_trip_salt'] = ')(*&^%$#@!98765432190zyxwvutsrqponmlkjihgfedcba';
|
||||
|
||||
|
@ -189,7 +203,9 @@
|
|||
|
||||
// Prevents most Tor exit nodes from making posts. Recommended, as a lot of abuse comes from Tor because
|
||||
// of the strong anonymity associated with it.
|
||||
$config['dnsbl'][] = array('tor.dnsbl.sectoor.de', 1);
|
||||
$config['dnsbl'][] = 'rbl.efnetrbl.org';
|
||||
$config['dnsbl'][] = 'tor.efnet.org';
|
||||
|
||||
|
||||
// http://www.sorbs.net/using.shtml
|
||||
// $config['dnsbl'][] = array('dnsbl.sorbs.net', array(2, 3, 4, 5, 6, 7, 8, 9));
|
||||
|
@ -212,6 +228,9 @@
|
|||
// Skip checking certain IP addresses against blacklists (for troubleshooting or whatever)
|
||||
$config['dnsbl_exceptions'][] = '127.0.0.1';
|
||||
|
||||
// To prevent bump atacks; returns the thread to last position after the last post is deleted.
|
||||
$config['anti_bump_flood'] = false;
|
||||
|
||||
/*
|
||||
* Introduction to Tinyboard's spam filter:
|
||||
*
|
||||
|
@ -232,6 +251,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
$config['spam']['enabled'] = true;
|
||||
|
||||
// Number of hidden fields to generate.
|
||||
$config['spam']['hidden_inputs_min'] = 4;
|
||||
$config['spam']['hidden_inputs_max'] = 12;
|
||||
|
@ -275,17 +296,33 @@
|
|||
'lock',
|
||||
'raw',
|
||||
'embed',
|
||||
'recaptcha_challenge_field',
|
||||
'recaptcha_response_field',
|
||||
'g-recaptcha-response',
|
||||
'spoiler',
|
||||
'page',
|
||||
'file_url',
|
||||
'file_url1',
|
||||
'file_url2',
|
||||
'file_url3',
|
||||
'file_url4',
|
||||
'file_url5',
|
||||
'file_url6',
|
||||
'file_url7',
|
||||
'file_url8',
|
||||
'file_url9',
|
||||
'json_response',
|
||||
'user_flag',
|
||||
'no_country',
|
||||
'tag'
|
||||
'tag',
|
||||
'simple_spam'
|
||||
);
|
||||
|
||||
// Enable simple anti-spam measure.
|
||||
/*
|
||||
$config['simple_spam'] = array (
|
||||
'prompt' => 'What is 2 + 2?',
|
||||
'answer' => '4'
|
||||
);
|
||||
*/
|
||||
// Enable reCaptcha to make spam even harder. Rarely necessary.
|
||||
$config['recaptcha'] = false;
|
||||
|
||||
|
@ -319,38 +356,66 @@
|
|||
*/
|
||||
|
||||
// Minimum time between between each post by the same IP address.
|
||||
$config['flood_time'] = 10;
|
||||
$config['flood_time_ip'] = 10;
|
||||
// Minimum time between between each post with the exact same content
|
||||
$config['flood_time_repost'] = 30;
|
||||
// Minimum time between between each post with the exact same content AND same IP address.
|
||||
$config['flood_time_ip'] = 120;
|
||||
// Same as above but by a different IP address. (Same content, not necessarily same IP address.)
|
||||
$config['flood_time_same'] = 30;
|
||||
$config['flood_time_ip_repost'] = 120;
|
||||
// Minimum time between between any opening post on the same board.
|
||||
$config['flood_time_board_op'] = 30;
|
||||
// Minimum time between between opening posts by the same IP address.
|
||||
$config['flood_time_ip_op'] = 180;
|
||||
|
||||
// Minimum time between posts by the same IP address (all boards).
|
||||
$config['filters'][] = array(
|
||||
'condition' => array(
|
||||
'flood-match' => array('ip'), // Only match IP address
|
||||
'flood-time' => &$config['flood_time']
|
||||
'flood-time' => &$config['flood_time_ip']
|
||||
),
|
||||
'action' => 'reject',
|
||||
'message' => &$config['error']['flood']
|
||||
);
|
||||
|
||||
// Minimum time between posts by the same IP address with the same text.
|
||||
// Minimum time between between each post with the exact same content (all boards)
|
||||
$config['filters'][] = array(
|
||||
'condition' => array(
|
||||
'flood-match' => array('ip', 'body'), // Match IP address and post body
|
||||
'flood-time' => &$config['flood_time_ip'],
|
||||
'flood-match' => array('body'), // Only match post body
|
||||
'flood-time' => &$config['flood_time_repost']
|
||||
),
|
||||
'action' => 'reject',
|
||||
'message' => &$config['error']['flood']
|
||||
);
|
||||
|
||||
// Minimum time between posts by the same IP address with the same text (all boards)
|
||||
$config['filters'][] = array(
|
||||
'condition' => array(
|
||||
'flood-match' => array('ip', 'body'), // Only match IP address and post body
|
||||
'flood-time' => &$config['flood_time_ip_repost'],
|
||||
'!body' => '/^$/', // Post body is NOT empty
|
||||
),
|
||||
'action' => 'reject',
|
||||
'message' => &$config['error']['flood']
|
||||
);
|
||||
|
||||
// Minimum time between posts with the same text. (Same content, but not always the same IP address.)
|
||||
// Minimum time between between each opening post (same board)
|
||||
$config['filters'][] = array(
|
||||
'condition' => array(
|
||||
'flood-match' => array('body'), // Match only post body
|
||||
'flood-time' => &$config['flood_time_same']
|
||||
'OP' => true,
|
||||
'flood-match' => array('board'), // Only match OPs on the same board
|
||||
'flood-match' => array('isop'),
|
||||
'flood-time' => &$config['flood_time_board_op']
|
||||
),
|
||||
'action' => 'reject',
|
||||
'message' => 'New threads are being created too quickly.'
|
||||
);
|
||||
|
||||
// Minimum time between opening posts by the same IP address (all boards)
|
||||
$config['filters'][] = array(
|
||||
'condition' => array(
|
||||
'OP' => true,
|
||||
'flood-match' => array('ip'), // Only match IP address of OPs
|
||||
'flood-match' => array('isop'),
|
||||
'flood-time' => &$config['flood_time_ip_op']
|
||||
),
|
||||
'action' => 'reject',
|
||||
'message' => &$config['error']['flood']
|
||||
|
@ -445,11 +510,16 @@
|
|||
|
||||
// Strip superfluous new lines at the end of a post.
|
||||
$config['strip_superfluous_returns'] = true;
|
||||
// Strip combining characters from Unicode strings (eg. "Zalgo").
|
||||
// Strip combining characters from Unicode strings (eg. "Zalgo"). This will impact some non-English languages.
|
||||
$config['strip_combining_chars'] = true;
|
||||
// Maximum number of combining characters in a row allowed in Unicode strings so that they can still be used in moderation.
|
||||
// Requires $config['strip_combining_chars'] = true;
|
||||
$config['max_combining_chars'] = 0;
|
||||
|
||||
// Maximum post body length.
|
||||
$config['max_body'] = 1800;
|
||||
// Minimum post body length.
|
||||
$config['min_body'] = 0;
|
||||
// Maximum number of post body lines to show on the index page.
|
||||
$config['body_truncate'] = 15;
|
||||
// Maximum number of characters to show on the index page.
|
||||
|
@ -505,11 +575,12 @@
|
|||
$config['link_prefix'] = '';
|
||||
$config['url_ads'] = &$config['link_prefix']; // leave alias
|
||||
|
||||
// Allow "uploading" images via URL as well. Users can enter the URL of the image and then Tinyboard will
|
||||
// download it. Not usually recommended.
|
||||
$config['allow_upload_by_url'] = false;
|
||||
// The timeout for the above, in seconds.
|
||||
$config['upload_by_url_timeout'] = 15;
|
||||
// Enable early 404? With default settings, a thread would 404 if it was to leave page 3, if it had less
|
||||
// than 3 replies.
|
||||
$config['early_404'] = false;
|
||||
|
||||
$config['early_404_page'] = 3;
|
||||
$config['early_404_replies'] = 5;
|
||||
|
||||
// A wordfilter (sometimes referred to as just a "filter" or "censor") automatically scans users’ posts
|
||||
// as they are submitted and changes or censors particular words or phrases.
|
||||
|
@ -550,6 +621,9 @@
|
|||
// When true, the sage won't be displayed
|
||||
$config['hide_sage'] = false;
|
||||
|
||||
// Don't display user's email when it's not "sage"
|
||||
$config['hide_email'] = false;
|
||||
|
||||
// Attach country flags to posts.
|
||||
$config['country_flags'] = false;
|
||||
|
||||
|
@ -601,6 +675,19 @@
|
|||
// a link to an email address or IRC chat room to appeal the ban.
|
||||
$config['ban_page_extra'] = '';
|
||||
|
||||
// Pre-configured ban reasons that pre-fill the ban form when clicked.
|
||||
// To disable, set $config['ban_reasons'] = false;
|
||||
$config['ban_reasons'] = array(
|
||||
array( 'reason' => 'Low-quality posting',
|
||||
'length' => '1d'),
|
||||
array( 'reason' => 'Off-topic',
|
||||
'length' => '1d'),
|
||||
array( 'reason' => 'Ban evasion',
|
||||
'length' => '30d'),
|
||||
array( 'reason' => 'Illegal content',
|
||||
'length' => ''),
|
||||
);
|
||||
|
||||
// Allow users to appeal bans through Tinyboard.
|
||||
$config['ban_appeals'] = false;
|
||||
|
||||
|
@ -610,6 +697,9 @@
|
|||
// How many ban appeals can be made for a single ban?
|
||||
$config['ban_appeals_max'] = 1;
|
||||
|
||||
// Maximum character length of appeal.
|
||||
$config['ban_appeal_max_chars'] = 120;
|
||||
|
||||
// Show moderator name on ban page.
|
||||
$config['show_modname'] = false;
|
||||
|
||||
|
@ -623,7 +713,7 @@
|
|||
$config['markup'][] = array("/'''(.+?)'''/", "<strong>\$1</strong>");
|
||||
$config['markup'][] = array("/''(.+?)''/", "<em>\$1</em>");
|
||||
$config['markup'][] = array("/\*\*(.+?)\*\*/", "<span class=\"spoiler\">\$1</span>");
|
||||
$config['markup'][] = array("/^[ |\t]*==(.+?)==[ |\t]*$/m", "<span class=\"heading\">\$1</span>");
|
||||
$config['markup'][] = array("/==(.+?)==/", "<span class=\"heading\">\$1</span>");
|
||||
|
||||
// Code markup. This should be set to a regular expression, using tags you want to use. Examples:
|
||||
// "/\[code\](.*?)\[\/code\]/is"
|
||||
|
@ -646,8 +736,10 @@
|
|||
* ====================
|
||||
*/
|
||||
// Maximum number of images allowed. Increasing this number enabled multi image.
|
||||
// If you make it more than 1, make sure to enable the below script for the post form to change.
|
||||
// $config['additional_javascript'][] = 'js/multi_image.js';
|
||||
// If you make it more than 1, make sure to enable one of the below scripts for the post form to change.
|
||||
// $config['additional_javascript'][] = 'js/multi-image.js';
|
||||
// or
|
||||
// $config['additional_javascript'][] = 'js/file-selector.js';
|
||||
$config['max_images'] = 1;
|
||||
|
||||
// Method to use for determing the max filesize.
|
||||
|
@ -763,7 +855,7 @@
|
|||
// Location of thumbnail to use for spoiler images.
|
||||
$config['spoiler_image'] = 'static/spoiler.png';
|
||||
// Location of thumbnail to use for deleted images.
|
||||
// $config['image_deleted'] = 'static/deleted.png';
|
||||
$config['image_deleted'] = 'static/deleted.png';
|
||||
|
||||
// When a thumbnailed image is going to be the same (in dimension), just copy the entire file and use
|
||||
// that as a thumbnail instead of resizing/redrawing.
|
||||
|
@ -804,8 +896,17 @@
|
|||
// Set this to true if you're using a BSD
|
||||
$config['bsd_md5'] = false;
|
||||
|
||||
// Set this to true if you're having problems with image duplicated error and bsd_md5 doesn't help.
|
||||
$config['php_md5'] = false;
|
||||
// Set this to true if you're using Linux and you can execute `md5sum` binary.
|
||||
$config['gnu_md5'] = false;
|
||||
|
||||
// Use Tesseract OCR to retrieve text from images, so you can use it as a spamfilter.
|
||||
$config['tesseract_ocr'] = false;
|
||||
|
||||
// Tesseract parameters
|
||||
$config['tesseract_params'] = '';
|
||||
|
||||
// Tesseract preprocess command
|
||||
$config['tesseract_preprocess_command'] = 'convert -monochrome %s -';
|
||||
|
||||
// Number of posts in a "View Last X Posts" page
|
||||
$config['noko50_count'] = 50;
|
||||
|
@ -838,6 +939,9 @@
|
|||
// Number of reports you can create at once.
|
||||
$config['report_limit'] = 3;
|
||||
|
||||
// Maximum character length of report.
|
||||
$config['report_max_length'] = 50;
|
||||
|
||||
// Allow unfiltered HTML in board subtitle. This is useful for placing icons and links.
|
||||
$config['allow_subtitle_html'] = false;
|
||||
|
||||
|
@ -928,8 +1032,8 @@
|
|||
// Show page navigation links at the top as well.
|
||||
$config['page_nav_top'] = false;
|
||||
|
||||
// Show "Catalog" link in page navigation. Use with the Catalog theme.
|
||||
// $config['catalog_link'] = 'catalog.html';
|
||||
// Show "Catalog" link in page navigation. Use with the Catalog theme. Set to false to disable.
|
||||
$config['catalog_link'] = 'catalog.html';
|
||||
|
||||
// Board categories. Only used in the "Categories" theme.
|
||||
// $config['categories'] = array(
|
||||
|
@ -975,8 +1079,6 @@
|
|||
*/
|
||||
|
||||
// Additional Javascript files to include on board index and thread pages. See js/ for available scripts.
|
||||
$config['additional_javascript'][] = 'js/inline-expanding.js';
|
||||
// $config['additional_javascript'][] = 'js/local-time.js';
|
||||
|
||||
// Some scripts require jQuery. Check the comments in script files to see what's needed. When enabling
|
||||
// jQuery, you should first empty the array so that "js/query.min.js" can be the first, and then re-add
|
||||
|
@ -989,6 +1091,16 @@
|
|||
// $config['additional_javascript'][] = 'js/post-hover.js';
|
||||
// $config['additional_javascript'][] = 'js/style-select.js';
|
||||
|
||||
// Defer some additional Javascript for faster initial page load times. Defering may break some scripts.
|
||||
// To enable, set this to true and add the scripts you want to defer to $config['additional_javascript_defer'].
|
||||
// If using $config['additional_javascript_compile'], this will be ignored and no files will be deferred.
|
||||
// eg.
|
||||
// inc/instance-config.php:
|
||||
// $config['deferred_javascript'] = true;
|
||||
// $config['additional_javascript_defer'][] = 'js/style-select.js';
|
||||
$config['additional_javascript_defer'] = [];
|
||||
$config['deferred_javascript'] = false;
|
||||
|
||||
// Where these script files are located on the web. Defaults to $config['root'].
|
||||
// $config['additional_javascript_url'] = 'http://static.example.org/tinyboard-javascript-stuff/';
|
||||
|
||||
|
@ -998,6 +1110,10 @@
|
|||
// Minify Javascript using http://code.google.com/p/minify/.
|
||||
$config['minify_js'] = false;
|
||||
|
||||
// Dispatch thumbnail loading and image configuration with JavaScript. It will need a certain javascript
|
||||
// code to work.
|
||||
$config['javascript_image_dispatch'] = false;
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* Video embedding
|
||||
|
@ -1016,7 +1132,7 @@
|
|||
),
|
||||
array(
|
||||
'/^https?:\/\/(\w+\.)?vimeo\.com\/(\d{2,10})(\?.+)?$/i',
|
||||
'<object style="float: left;margin: 10px 20px;" width="%%tb_width%%" height="%%tb_height%%"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=$2&server=vimeo.com&show_title=0&show_byline=0&show_portrait=0&color=00adef&fullscreen=1&autoplay=0&loop=0" /><embed src="http://vimeo.com/moogaloop.swf?clip_id=$2&server=vimeo.com&show_title=0&show_byline=0&show_portrait=0&color=00adef&fullscreen=1&autoplay=0&loop=0" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="%%tb_width%%" height="%%tb_height%%"></object>'
|
||||
'<iframe src="https://player.vimeo.com/video/$2" style="float: left;margin: 10px 20px;" width="%%tb_width%%" height="%%tb_height%%" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>'
|
||||
),
|
||||
array(
|
||||
'/^https?:\/\/(\w+\.)?dailymotion\.com\/video\/([a-zA-Z0-9]{2,10})(_.+)?$/i',
|
||||
|
@ -1058,12 +1174,14 @@
|
|||
$config['error']['fileext'] = _('Unsupported image format.');
|
||||
$config['error']['noboard'] = _('Invalid board!');
|
||||
$config['error']['nonexistant'] = _('Thread specified does not exist.');
|
||||
$config['error']['nopost'] = _('Post specified does not exist.');
|
||||
$config['error']['locked'] = _('Thread locked. You may not reply at this time.');
|
||||
$config['error']['reply_hard_limit'] = _('Thread has reached its maximum reply limit.');
|
||||
$config['error']['image_hard_limit'] = _('Thread has reached its maximum image limit.');
|
||||
$config['error']['nopost'] = _('You didn\'t make a post.');
|
||||
$config['error']['flood'] = _('Flood detected; Post discarded.');
|
||||
$config['error']['spam'] = _('Your request looks automated; Post discarded.');
|
||||
$config['error']['simple_spam'] = _('You must answer the question to make a new thread. See the last field.');
|
||||
$config['error']['unoriginal'] = _('Unoriginal content!');
|
||||
$config['error']['muted'] = _('Unoriginal content! You have been muted for %d seconds.');
|
||||
$config['error']['youaremuted'] = _('You are muted! Expires in %d seconds.');
|
||||
|
@ -1072,8 +1190,15 @@
|
|||
$config['error']['toomanycites'] = _('Too many cites; post discarded.');
|
||||
$config['error']['toomanycross'] = _('Too many cross-board links; post discarded.');
|
||||
$config['error']['nodelete'] = _('You didn\'t select anything to delete.');
|
||||
$config['error']['nodeletethread'] = _('You are not allowed to delete threads.');
|
||||
$config['error']['noreport'] = _('You didn\'t select anything to report.');
|
||||
$config['error']['toolongreport'] = _('The reason was too long.');
|
||||
$config['error']['toomanyreports'] = _('You can\'t report that many posts at once.');
|
||||
$config['error']['noban'] = _('That ban doesn\'t exist or is not for you.');
|
||||
$config['error']['tooshortban'] = _('You cannot appeal a ban of this length.');
|
||||
$config['error']['toolongappeal'] = _('The appeal was too long.');
|
||||
$config['error']['toomanyappeals'] = _('You cannot appeal this ban again.');
|
||||
$config['error']['pendingappeal'] = _('There is already a pending appeal for this ban.');
|
||||
$config['error']['invalidpassword'] = _('Wrong password…');
|
||||
$config['error']['invalidimg'] = _('Invalid image.');
|
||||
$config['error']['unknownext'] = _('Unknown file extension.');
|
||||
|
@ -1083,7 +1208,7 @@
|
|||
$config['error']['webmerror'] = _('There was a problem processing your webm.');//Is this error used anywhere ?
|
||||
$config['error']['invalidwebm'] = _('Invalid webm uploaded.');
|
||||
$config['error']['webmhasaudio'] = _('The uploaded webm contains an audio or another type of additional stream.');
|
||||
$config['error']['webmtoolong'] = _('The uploaded webm is longer than ' . $config['webm']['max_length'] . ' seconds.');
|
||||
$config['error']['webmtoolong'] = _('The uploaded webm is longer than ' . $config['webm']['max_length'] . ' seconds.'); // note that this will sub the initial value from this file, not instance-config.php
|
||||
$config['error']['fileexists'] = _('That file <a href="%s">already exists</a>!');
|
||||
$config['error']['fileexistsinthread'] = _('That file <a href="%s">already exists</a> in this thread!');
|
||||
$config['error']['delete_too_soon'] = _('You\'ll have to wait another %s before deleting that.');
|
||||
|
@ -1095,6 +1220,7 @@
|
|||
// Moderator errors
|
||||
$config['error']['toomanyunban'] = _('You are only allowed to unban %s users at a time. You tried to unban %u users.');
|
||||
$config['error']['invalid'] = _('Invalid username and/or password.');
|
||||
$config['error']['insecure'] = _('Login on insecure connections is disabled.');
|
||||
$config['error']['notamod'] = _('You are not a mod…');
|
||||
$config['error']['invalidafter'] = _('Invalid username and/or password. Your user may have been deleted or changed.');
|
||||
$config['error']['malformed'] = _('Invalid/malformed cookies.');
|
||||
|
@ -1140,6 +1266,7 @@
|
|||
// Location of files.
|
||||
$config['file_index'] = 'index.html';
|
||||
$config['file_page'] = '%d.html'; // NB: page is both an index page and a thread
|
||||
$config['file_catalog'] = 'catalog.html';
|
||||
$config['file_page50'] = '%d+50.html';
|
||||
$config['file_page_slug'] = '%d-%s.html';
|
||||
$config['file_page50_slug'] = '%d-%s+50.html';
|
||||
|
@ -1174,7 +1301,8 @@
|
|||
// $config['font_awesome'] is false (default).
|
||||
// $config['image_sticky'] = 'static/sticky.png';
|
||||
// $config['image_locked'] = 'static/locked.gif';
|
||||
// $config['image_bumplocked'] = 'static/sage.png'.
|
||||
// $config['image_bumplocked'] = 'static/sage.png';
|
||||
// $config['image_cycled'] = 'static/cycled.png';
|
||||
|
||||
// If you want to put images and other dynamic-static stuff on another (preferably cookieless) domain.
|
||||
// This will override $config['root'] and $config['dir']['...'] directives. "%s" will get replaced with
|
||||
|
@ -1190,19 +1318,80 @@
|
|||
// Website favicon.
|
||||
// $config['url_favicon'] = '/favicon.gif';
|
||||
|
||||
// Website Apple touch icon.
|
||||
// $config['url_appletouchicon'] = '/favicon.gif';
|
||||
|
||||
// Try not to build pages when we shouldn't have to.
|
||||
$config['try_smarter'] = true;
|
||||
|
||||
// EXPERIMENTAL: Defer static HTML building to a moment, when a given file is actually accessed.
|
||||
// Warning: This option won't run out of the box. You need to tell your webserver, that a file
|
||||
// for serving 403 and 404 pages is /smart_build.php. Also, you need to turn off indexes.
|
||||
/*
|
||||
* ====================
|
||||
* Advanced build
|
||||
* ====================
|
||||
*/
|
||||
|
||||
// Strategies for file generation. Also known as an "advanced build". If you don't have performance
|
||||
// issues, you can safely ignore that part, because it's hard to configure and won't even work on
|
||||
// your free webhosting.
|
||||
//
|
||||
// A strategy is a function, that given the PHP environment and ($fun, $array) variable pair, returns
|
||||
// an $action array or false.
|
||||
//
|
||||
// $fun - a controller function name, see inc/controller.php. This is named after functions, so that
|
||||
// we can generate the files in daemon.
|
||||
//
|
||||
// $array - arguments to be passed
|
||||
//
|
||||
// $action - action to be taken. It's an array, and the first element of it is one of the following:
|
||||
// * "immediate" - generate the page immediately
|
||||
// * "defer" - defer page generation to a moment a worker daemon gets to build it (serving a stale
|
||||
// page in the meantime). The remaining arguments are daemon-specific. Daemon isn't
|
||||
// implemented yet :DDDD inb4 while(true) { generate(Queue::Get()) }; (which is probably it).
|
||||
// * "build_on_load" - defer page generation to a moment, when the user actually accesses the page.
|
||||
// This is a smart_build behaviour. You shouldn't use this one too much, if you
|
||||
// use it for active boards, the server may choke due to a possible race condition.
|
||||
// See my blog post: https://engine.vichan.net/blog/res/2.html
|
||||
//
|
||||
// So, let's assume we want to build a thread 1324 on board /b/, because a new post appeared there.
|
||||
// We try the first strategy, giving it arguments: 'sb_thread', array('b', 1324). The strategy will
|
||||
// now return a value $action, denoting an action to do. If $action is false, we try another strategy.
|
||||
//
|
||||
// As I said, configuration isn't easy.
|
||||
//
|
||||
// 1. chmod 0777 directories: tmp/locks/ and tmp/queue/.
|
||||
// 2. serve 403 and 404 requests to go thru smart_build.php
|
||||
// for nginx, this blog post contains config snippets: https://engine.vichan.net/blog/res/2.html
|
||||
// 3. disable indexes in your webserver
|
||||
// 4. launch any number of daemons (eg. twice your number of threads?) using the command:
|
||||
// $ tools/worker.php
|
||||
// You don't need to do that step if you are not going to use the "defer" option.
|
||||
// 5. enable smart_build_helper (see below)
|
||||
// 6. edit the strategies (see inc/functions.php for the builtin ones). You can use lambdas. I will test
|
||||
// various ones and include one that works best for me.
|
||||
$config['generation_strategies'] = array();
|
||||
// Add a sane strategy. It forces to immediately generate a page user is about to land on. Otherwise,
|
||||
// it has no opinion, so it needs a fallback strategy.
|
||||
$config['generation_strategies'][] = 'strategy_sane';
|
||||
// Add an immediate catch-all strategy. This is the default function of imageboards: generate all pages
|
||||
// on post time.
|
||||
$config['generation_strategies'][] = 'strategy_immediate';
|
||||
// NOT RECOMMENDED: Instead of an all-"immediate" strategy, you can use an all-"build_on_load" one (used
|
||||
// to be initialized using $config['smart_build']; ) for all pages instead of those to be build
|
||||
// immediately. A rebuild done in this mode should remove all your static files
|
||||
// $config['generation_strategies'][1] = 'strategy_smart_build';
|
||||
|
||||
// Deprecated. Leave it false. See above.
|
||||
$config['smart_build'] = false;
|
||||
|
||||
// Smart build related: when a file doesn't exist, where should we redirect?
|
||||
// Use smart_build.php for dispatching missing requests. It may be useful without smart_build or advanced
|
||||
// build, for example it will regenerate the missing files.
|
||||
$config['smart_build_helper'] = true;
|
||||
|
||||
// smart_build.php: when a file doesn't exist, where should we redirect?
|
||||
$config['page_404'] = '/404.html';
|
||||
|
||||
// Smart build related: extra entrypoints.
|
||||
$config['smart_build_entrypoints'] = array();
|
||||
// Extra controller entrypoints. Controller is used only by smart_build and advanced build.
|
||||
$config['controller_entrypoints'] = array();
|
||||
|
||||
/*
|
||||
* ====================
|
||||
|
@ -1222,6 +1411,7 @@
|
|||
// Mod links (full HTML).
|
||||
$config['mod']['link_delete'] = '[D]';
|
||||
$config['mod']['link_ban'] = '[B]';
|
||||
$config['mod']['link_warning'] = '[W]';
|
||||
$config['mod']['link_bandelete'] = '[B&D]';
|
||||
$config['mod']['link_deletefile'] = '[F]';
|
||||
$config['mod']['link_spoilerimage'] = '[S]';
|
||||
|
@ -1235,6 +1425,9 @@
|
|||
$config['mod']['link_bumpunlock'] = '[-Sage]';
|
||||
$config['mod']['link_editpost'] = '[Edit]';
|
||||
$config['mod']['link_move'] = '[Move]';
|
||||
$config['mod']['link_merge'] = '[Merge]';
|
||||
$config['mod']['link_cycle'] = '[Cycle]';
|
||||
$config['mod']['link_uncycle'] = '[-Cycle]';
|
||||
|
||||
// Moderator capcodes.
|
||||
$config['capcode'] = ' <span class="capcode">## %s</span>';
|
||||
|
@ -1288,9 +1481,11 @@
|
|||
// Default public ban message. In public ban messages, %length% is replaced with "for x days" or
|
||||
// "permanently" (with %LENGTH% being the uppercase equivalent).
|
||||
$config['mod']['default_ban_message'] = _('USER WAS BANNED FOR THIS POST');
|
||||
$config['mod']['default_warning_message'] = _('USER WAS WARNED FOR THIS POST');
|
||||
// $config['mod']['default_ban_message'] = 'USER WAS BANNED %LENGTH% FOR THIS POST';
|
||||
// HTML to append to post bodies for public bans messages (where "%s" is the message).
|
||||
$config['mod']['ban_message'] = '<span class="public_ban">(%s)</span>';
|
||||
$config['mod']['warning_message'] = '<span class="public_warning">(%s)</span>';
|
||||
|
||||
// When moving a thread to another board and choosing to keep a "shadow thread", an automated post (with
|
||||
// a capcode) will be made, linking to the new location for the thread. "%s" will be replaced with a
|
||||
|
@ -1342,7 +1537,7 @@
|
|||
|
||||
// Capcode permissions.
|
||||
$config['mod']['capcode'] = array(
|
||||
// JANITOR => array('Janitor'),
|
||||
JANITOR => array('Janitor'),
|
||||
MOD => array('Mod'),
|
||||
ADMIN => true
|
||||
);
|
||||
|
@ -1362,6 +1557,8 @@
|
|||
$config['mod']['show_ip'] = MOD;
|
||||
// Delete a post
|
||||
$config['mod']['delete'] = JANITOR;
|
||||
// Publicly warn a user for a post
|
||||
$config['mod']['warning'] = JANITOR;
|
||||
// Ban a user for a post
|
||||
$config['mod']['ban'] = MOD;
|
||||
// Ban and delete (one click; instant)
|
||||
|
@ -1378,6 +1575,9 @@
|
|||
$config['mod']['deletebyip_global'] = ADMIN;
|
||||
// Sticky a thread
|
||||
$config['mod']['sticky'] = MOD;
|
||||
// Cycle a thread
|
||||
$config['mod']['cycle'] = MOD;
|
||||
$config['cycle_limit'] = 250;
|
||||
// Lock a thread
|
||||
$config['mod']['lock'] = MOD;
|
||||
// Post in a locked thread
|
||||
|
@ -1390,13 +1590,16 @@
|
|||
$config['mod']['editpost'] = ADMIN;
|
||||
// "Move" a thread to another board (EXPERIMENTAL; has some known bugs)
|
||||
$config['mod']['move'] = DISABLED;
|
||||
// "Merge" a thread to same board or another board
|
||||
$config['mod']['merge'] = MOD;
|
||||
// Bypass "field_disable_*" (forced anonymity, etc.)
|
||||
$config['mod']['bypass_field_disable'] = MOD;
|
||||
// Post bypass unoriginal content check on robot-enabled boards
|
||||
$config['mod']['postunoriginal'] = ADMIN;
|
||||
// Bypass flood check
|
||||
$config['mod']['bypass_filters'] = ADMIN;
|
||||
$config['mod']['flood'] = &$config['mod']['bypass_filters'];
|
||||
//$config['mod']['flood'] = &$config['mod']['bypass_filters'];
|
||||
$config['mod']['flood'] = MOD;
|
||||
// Raw HTML posting
|
||||
$config['mod']['rawhtml'] = ADMIN;
|
||||
|
||||
|
@ -1443,7 +1646,7 @@
|
|||
// Create a user
|
||||
$config['mod']['createusers'] = ADMIN;
|
||||
// View the moderation log
|
||||
$config['mod']['modlog'] = ADMIN;
|
||||
$config['mod']['modlog'] = MOD;
|
||||
// View IP addresses of other mods in ?/log
|
||||
$config['mod']['show_ip_modlog'] = ADMIN;
|
||||
// View relevant moderation log entries on IP address pages (ie. ban history, etc.) Warning: Can be
|
||||
|
@ -1488,6 +1691,9 @@
|
|||
$config['mod']['ban_appeals'] = MOD;
|
||||
// View the recent posts page
|
||||
$config['mod']['recent'] = MOD;
|
||||
// Create pages
|
||||
$config['mod']['edit_pages'] = MOD;
|
||||
$config['pages_max'] = 10;
|
||||
|
||||
// Config editor permissions
|
||||
$config['mod']['config'] = array();
|
||||
|
@ -1534,14 +1740,19 @@
|
|||
|
||||
/*
|
||||
* ====================
|
||||
* Public post search
|
||||
* Public pages
|
||||
* ====================
|
||||
*/
|
||||
|
||||
// Public post search settings
|
||||
$config['search'] = array();
|
||||
|
||||
// Enable the search form
|
||||
$config['search']['enable'] = false;
|
||||
|
||||
// Enable search in the board index.
|
||||
$config['board_search'] = false;
|
||||
|
||||
// Maximal number of queries per IP address per minutes
|
||||
$config['search']['queries_per_minutes'] = Array(15, 2);
|
||||
|
||||
|
@ -1554,6 +1765,9 @@
|
|||
// Boards for searching
|
||||
//$config['search']['boards'] = array('a', 'b', 'c', 'd', 'e');
|
||||
|
||||
// Enable public logs? 0: NO, 1: YES, 2: YES, but drop names
|
||||
$config['public_logs'] = 0;
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* Events (PHP 5.3.0+)
|
||||
|
@ -1588,12 +1802,69 @@
|
|||
// Example: Adding the pre-markup post body to the API as "com_nomarkup".
|
||||
// $config['api']['extra_fields'] = array('body_nomarkup' => 'com_nomarkup');
|
||||
|
||||
/*
|
||||
* ==================
|
||||
* NNTPChan settings
|
||||
* ==================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Please keep in mind that NNTPChan support in vichan isn't finished yet / is in an experimental
|
||||
* state. Please join #nntpchan on Rizon in order to peer with someone.
|
||||
*/
|
||||
|
||||
$config['nntpchan'] = array();
|
||||
|
||||
// Enable NNTPChan integration
|
||||
$config['nntpchan']['enabled'] = false;
|
||||
|
||||
// NNTP server
|
||||
$config['nntpchan']['server'] = "localhost:1119";
|
||||
|
||||
// Global dispatch array. Add your boards to it to enable them. Please make
|
||||
// sure that this setting is set in a global context.
|
||||
$config['nntpchan']['dispatch'] = array(); // 'overchan.test' => 'test'
|
||||
|
||||
// Trusted peer - an IP address of your NNTPChan instance. This peer will have
|
||||
// increased capabilities, eg.: will evade spamfilter.
|
||||
$config['nntpchan']['trusted_peer'] = '127.0.0.1';
|
||||
|
||||
// Salt for message ID generation. Keep it long and secure.
|
||||
$config['nntpchan']['salt'] = 'change_me+please';
|
||||
|
||||
// A local message ID domain. Make sure to change it.
|
||||
$config['nntpchan']['domain'] = 'example.vichan.net';
|
||||
|
||||
// An NNTPChan group name.
|
||||
// Please set this setting in your board/config.php, not globally.
|
||||
$config['nntpchan']['group'] = false; // eg. 'overchan.test'
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* Other/uncategorized
|
||||
* ====================
|
||||
*/
|
||||
|
||||
// Matrix integration for reports
|
||||
// $config['matrix'] = array(
|
||||
// 'access_token' => 'ACCESS_TOKEN',
|
||||
// 'room_id' => '%21askjdlkajsdlka:matrix.org',
|
||||
// 'host' => 'https://matrix.org',
|
||||
// 'max_message_length' => 240
|
||||
// );
|
||||
|
||||
//Securimage captcha
|
||||
//Note from lainchan PR: "TODO move a bunch of things here"
|
||||
|
||||
$config['spam']['valid_inputs'][]='captcha';
|
||||
$config['error']['securimage']=array(
|
||||
'missing'=>'The captcha field was missing. Please try again',
|
||||
'empty'=>'Please fill out the captcha',
|
||||
'bad'=>'Incorrect or expired captcha',
|
||||
);
|
||||
|
||||
// Meta keywords. It's probably best to include these in per-board configurations.
|
||||
// $config['meta_keywords'] = 'chan,anonymous discussion,imageboard,tinyboard';
|
||||
|
||||
|
@ -1671,7 +1942,58 @@
|
|||
'</a></div>';
|
||||
|
||||
// Slack Report Notification
|
||||
$config['slack'] = true;
|
||||
$config['slack_channel'] = "reports";
|
||||
$config['slack_incoming_webhook_endpoint'] = "https://hooks.slack.com/services/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";
|
||||
$config['slack'] = false;
|
||||
$config['slack_channel'] = "";
|
||||
$config['slack_incoming_webhook_endpoint'] = "https://hooks.slack.com/services/";
|
||||
|
||||
// Password hashing function
|
||||
//
|
||||
// $5$ <- SHA256
|
||||
// $6$ <- SHA512
|
||||
//
|
||||
// 25000 rounds make for ~0.05s on my 2015 Core i3 computer.
|
||||
//
|
||||
// https://secure.php.net/manual/en/function.crypt.php
|
||||
$config['password_crypt'] = '$6$rounds=25000$';
|
||||
|
||||
// Password hashing method version
|
||||
// If set to 0, it won't upgrade hashes using old password encryption schema, only create new.
|
||||
// You can set it to a higher value, to further migrate to other password hashing function.
|
||||
$config['password_crypt_version'] = 1;
|
||||
|
||||
// Use CAPTCHA for reports?
|
||||
$config['report_captcha'] = false;
|
||||
|
||||
// Allowed HTML tags in ?/edit_pages.
|
||||
$config['allowed_html'] = 'a[href|title],p,br,li,ol,ul,strong,em,u,h2,b,i,tt,div,img[src|alt|title],hr';
|
||||
|
||||
// Allow joke capcode
|
||||
$config['joke_capcode'] = false;
|
||||
|
||||
// Show "Home" link in page navigation. Use with the Catalog theme. Set to false to disable.
|
||||
$config['home_link'] = false;
|
||||
|
||||
// Enable posting from overboards
|
||||
$config['overboard_post_form'] = false;
|
||||
|
||||
// Enable auto IP note generation of moderator deleted posts
|
||||
$config['autotagging'] = false;
|
||||
|
||||
// Enable PDF file thumbnail generation
|
||||
$config['pdf_file_thumbnail'] = false;
|
||||
|
||||
// Enable TXT file thumbnail
|
||||
$config['txt_file_thumbnail'] = false;
|
||||
|
||||
// Enable SCeditor WYSIWIG and CSS
|
||||
$config['sc_editor'] = false;
|
||||
$config['sc_editor_theme'] = 'transparent.min';
|
||||
|
||||
// Show "Home" link in page navigation. Use with the Catalog theme. Set to false to disable.
|
||||
$config['home_link'] = true;
|
||||
|
||||
//Empty board alias
|
||||
$config['boards_alias'] = array();
|
||||
|
||||
//Logo location for themes
|
||||
$config['logo'] = 'static/logo.png';
|
||||
|
|
108
inc/controller.php
Normal file
108
inc/controller.php
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
// This file contains the controller part of vichan
|
||||
|
||||
// don't bother with that unless you use smart build or advanced build
|
||||
// you can use those parts for your own implementations though :^)
|
||||
|
||||
defined('TINYBOARD') or exit;
|
||||
|
||||
function sb_board($b, $page = 1) { global $config, $build_pages; $page = (int)$page;
|
||||
if ($page < 1) return false;
|
||||
if (!openBoard($b)) return false;
|
||||
if ($page > $config['max_pages']) return false;
|
||||
$config['try_smarter'] = true;
|
||||
$build_pages = array($page);
|
||||
buildIndex("skip");
|
||||
return true;
|
||||
}
|
||||
|
||||
function sb_api_board($b, $page = 0) { $page = (int)$page;
|
||||
return sb_board($b, $page + 1);
|
||||
}
|
||||
|
||||
function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = (int)$thread;
|
||||
if ($thread < 1) return false;
|
||||
|
||||
if (!preg_match('/^'.$config['board_regex'].'$/u', $b)) return false;
|
||||
|
||||
if (Cache::get("thread_exists_".$b."_".$thread) == "no") return false;
|
||||
|
||||
$query = prepare(sprintf("SELECT MAX(`id`) AS `max` FROM ``posts_%s``", $b));
|
||||
if (!$query->execute()) return false;
|
||||
|
||||
$s = $query->fetch(PDO::FETCH_ASSOC);
|
||||
$max = $s['max'];
|
||||
|
||||
if ($thread > $max) return false;
|
||||
|
||||
$query = prepare(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL", $b));
|
||||
$query->bindValue(':id', $thread);
|
||||
|
||||
if (!$query->execute() || !$query->fetch(PDO::FETCH_ASSOC) ) {
|
||||
Cache::set("thread_exists_".$b."_".$thread, "no", 3600);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($slugcheck && $config['slugify']) {
|
||||
global $request;
|
||||
|
||||
$link = link_for(array("id" => $thread), $slugcheck === 50, array("uri" => $b));
|
||||
$link = "/".$b."/".$config['dir']['res'].$link;
|
||||
|
||||
if ($link != $request) {
|
||||
header("Location: $link", true, 301);
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
if ($slugcheck == 50) { // Should we really generate +50 page? Maybe there are not enough posts anyway
|
||||
global $request;
|
||||
$r = str_replace("+50", "", $request);
|
||||
$r = substr($r, 1); // Cut the slash
|
||||
|
||||
if (file_exists($r)) return false;
|
||||
}
|
||||
|
||||
if (!openBoard($b)) return false;
|
||||
buildThread($thread);
|
||||
return true;
|
||||
}
|
||||
|
||||
function sb_thread_slugcheck($b, $thread) {
|
||||
return sb_thread($b, $thread, true);
|
||||
}
|
||||
function sb_thread_slugcheck50($b, $thread) {
|
||||
return sb_thread($b, $thread, 50);
|
||||
}
|
||||
|
||||
function sb_api($b) { global $config, $build_pages;
|
||||
if (!openBoard($b)) return false;
|
||||
$config['try_smarter'] = true;
|
||||
$build_pages = array(-1);
|
||||
buildIndex();
|
||||
return true;
|
||||
}
|
||||
|
||||
function sb_ukko() {
|
||||
rebuildTheme("ukko", "post-thread");
|
||||
return true;
|
||||
}
|
||||
|
||||
function sb_catalog($b) {
|
||||
if (!openBoard($b)) return false;
|
||||
|
||||
rebuildTheme("catalog", "post-thread", $b);
|
||||
return true;
|
||||
}
|
||||
|
||||
function sb_recent() {
|
||||
rebuildTheme("recent", "post-thread");
|
||||
return true;
|
||||
}
|
||||
|
||||
function sb_sitemap() {
|
||||
rebuildTheme("sitemap", "all");
|
||||
return true;
|
||||
}
|
||||
|
|
@ -69,19 +69,23 @@ function sql_open() {
|
|||
try {
|
||||
$options = array(
|
||||
PDO::ATTR_TIMEOUT => $config['db']['timeout'],
|
||||
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
|
||||
);
|
||||
|
||||
if ($config['db']['type'] == "mysql")
|
||||
$options[PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true;
|
||||
|
||||
if ($config['db']['persistent'])
|
||||
$options[PDO::ATTR_PERSISTENT] = true;
|
||||
$pdo = new PDO($dsn, $config['db']['user'], $config['db']['password'], $options);
|
||||
|
||||
if ($config['debug'])
|
||||
$debug['time']['db_connect'] = '~' . round((microtime(true) - $start) * 1000, 2) . 'ms';
|
||||
|
||||
if ($config['db']['type'] == "mysql"){
|
||||
if (mysql_version() >= 50503)
|
||||
query('SET NAMES utf8mb4') or error(db_error());
|
||||
else
|
||||
query('SET NAMES utf8') or error(db_error());
|
||||
}
|
||||
return $pdo;
|
||||
} catch(PDOException $e) {
|
||||
$message = $e->getMessage();
|
||||
|
@ -95,15 +99,10 @@ function sql_open() {
|
|||
}
|
||||
}
|
||||
|
||||
// 5.6.10 becomes 50610
|
||||
// 5.6.10 becomes 50610 HACK: hardcoded to be above critical value 50803 due to laziness
|
||||
function mysql_version() {
|
||||
global $pdo;
|
||||
|
||||
$version = $pdo->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
$v = explode('.', $version);
|
||||
if (count($v) != 3)
|
||||
return false;
|
||||
return (int) sprintf("%02d%02d%02d", $v[0], $v[1], $v[2]);
|
||||
// TODO delete all references of this function everywhere
|
||||
return 80504;
|
||||
}
|
||||
|
||||
function prepare($query) {
|
||||
|
|
|
@ -31,6 +31,16 @@ function doBoardListPart($list, $root, &$boards) {
|
|||
$body .= ' <a href="' . $board . '">' . $key . '</a> /';
|
||||
} else {
|
||||
$title = '';
|
||||
if (array_key_exists($board,$config['boards_alias'])){
|
||||
$actual_board = $config['boards_alias'][$board];
|
||||
if (isset ($boards[$actual_board])) {
|
||||
$title = ' title="'.$boards[$actual_board].'"';
|
||||
}
|
||||
|
||||
$body .= ' <a href="' . $root . $actual_board . '/' . $config['file_index'] . '"'.$title.'>' . $board . '</a> /';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset ($boards[$board])) {
|
||||
$title = ' title="'.$boards[$board].'"';
|
||||
}
|
||||
|
@ -39,6 +49,7 @@ function doBoardListPart($list, $root, &$boards) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$body = preg_replace('/\/$/', '', $body);
|
||||
|
||||
return $body;
|
||||
|
@ -71,7 +82,6 @@ function createBoardlist($mod=false) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
function loginForm($error=false, $username=false, $redirect=false) {
|
||||
global $config;
|
||||
|
||||
|
@ -98,6 +108,12 @@ function pm_snippet($body, $len=null) {
|
|||
// Replace line breaks with some whitespace
|
||||
$body = preg_replace('@<br/?>@i', ' ', $body);
|
||||
|
||||
// Strip tags but leave span tags which contain spoiler
|
||||
$body_with_spoiler_tags = strip_tags($body,'<span>');
|
||||
|
||||
// Check for spoiler tags
|
||||
$spoiler = preg_match("/spoiler/", $body_with_spoiler_tags);
|
||||
|
||||
// Strip tags
|
||||
$body = strip_tags($body);
|
||||
|
||||
|
@ -109,6 +125,13 @@ function pm_snippet($body, $len=null) {
|
|||
|
||||
$body = mb_substr($body, 0, $len);
|
||||
|
||||
if ($spoiler){
|
||||
$value = "<span class=\"spoiler\">" . utf8tohtml($body) . "</span>";
|
||||
}
|
||||
else {
|
||||
$value = utf8tohtml($body);
|
||||
}
|
||||
|
||||
// Re-escape the characters.
|
||||
return '<em>' . utf8tohtml($body) . ($strlen > $len ? '…' : '') . '</em>';
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?PHP
|
||||
<?php
|
||||
|
||||
function error_handler($errno,$errstr,$errfile, $errline, $errcontext){
|
||||
if(error_reporting() & $errno){
|
||||
|
@ -8,7 +8,7 @@ function error_handler($errno,$errstr,$errfile, $errline, $errcontext){
|
|||
return false;
|
||||
}
|
||||
|
||||
function exception_handler(Exception $e){
|
||||
function exception_handler($e){
|
||||
error($e->getMessage());
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,8 @@ function fatal_error_handler(){
|
|||
|
||||
register_shutdown_function('fatal_error_handler');
|
||||
|
||||
// Due to composer autoload, this isn't implicitly global anymore
|
||||
global $error_recursion;
|
||||
$error_recursion=false;
|
||||
|
||||
function error($message, $priority = true, $debug_stuff = false) {
|
||||
|
|
|
@ -55,8 +55,12 @@ class Filter {
|
|||
if ($flood_post['board'] != $post['board'])
|
||||
continue 3;
|
||||
break;
|
||||
case 'isop':
|
||||
if ($flood_post['isreply'] != '0')
|
||||
continue 3;
|
||||
break;
|
||||
case 'isreply':
|
||||
if ($flood_post['isreply'] == $post['op'])
|
||||
if ($flood_post['isreply'] != '1')
|
||||
continue 3;
|
||||
break;
|
||||
default:
|
||||
|
@ -66,6 +70,7 @@ class Filter {
|
|||
$flood_check_matched[] = $flood_post;
|
||||
}
|
||||
|
||||
// is there any reason for this assignment?
|
||||
$this->flood_check = $flood_check_matched;
|
||||
|
||||
return !empty($this->flood_check);
|
||||
|
@ -145,7 +150,7 @@ class Filter {
|
|||
}
|
||||
if (isset ($this->action)) switch($this->action) {
|
||||
case 'reject':
|
||||
error(isset($this->message) ? $this->message : 'Posting throttled by filter.');
|
||||
error(isset($this->message) ? $this->message : 'Posting blocked by filter.');
|
||||
case 'ban':
|
||||
if (!isset($this->reason))
|
||||
error('The ban action requires a reason.');
|
||||
|
@ -176,7 +181,9 @@ class Filter {
|
|||
if ($condition[0] == '!') {
|
||||
$NOT = true;
|
||||
$condition = substr($condition, 1);
|
||||
} else $NOT = false;
|
||||
} else {
|
||||
$NOT = false;
|
||||
}
|
||||
|
||||
if ($this->match($condition, $value) == $NOT)
|
||||
return false;
|
||||
|
@ -216,21 +223,10 @@ function do_filters(array $post) {
|
|||
foreach ($config['filters'] as $filter) {
|
||||
if (isset($filter['condition']['flood-match'])) {
|
||||
$has_flood = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($has_flood)) {
|
||||
if ($post['has_file']) {
|
||||
$query = prepare("SELECT * FROM ``flood`` WHERE `ip` = :ip OR `posthash` = :posthash OR `filehash` = :filehash");
|
||||
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
|
||||
$query->bindValue(':posthash', make_comment_hex($post['body_nomarkup']));
|
||||
$query->bindValue(':filehash', $post['filehash']);
|
||||
} else {
|
||||
$query = prepare("SELECT * FROM ``flood`` WHERE `ip` = :ip OR `posthash` = :posthash");
|
||||
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
|
||||
$query->bindValue(':posthash', make_comment_hex($post['body_nomarkup']));
|
||||
}
|
||||
$query = prepare("SELECT * FROM ``flood``");
|
||||
$query->execute() or error(db_error($query));
|
||||
$flood_check = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
|
@ -240,9 +236,10 @@ function do_filters(array $post) {
|
|||
foreach ($config['filters'] as $filter_array) {
|
||||
$filter = new Filter($filter_array);
|
||||
$filter->flood_check = $flood_check;
|
||||
if ($filter->check($post))
|
||||
if ($filter->check($post)) {
|
||||
$filter->action();
|
||||
}
|
||||
}
|
||||
|
||||
purge_flood_table();
|
||||
}
|
||||
|
|
555
inc/functions.php
Executable file → Normal file
555
inc/functions.php
Executable file → Normal file
|
@ -9,19 +9,9 @@ if (realpath($_SERVER['SCRIPT_FILENAME']) == str_replace('\\', '/', __FILE__)) {
|
|||
exit;
|
||||
}
|
||||
|
||||
define('TINYBOARD', null);
|
||||
|
||||
$microtime_start = microtime(true);
|
||||
require_once 'inc/error.php';
|
||||
require_once 'inc/cache.php';
|
||||
require_once 'inc/display.php';
|
||||
require_once 'inc/template.php';
|
||||
require_once 'inc/database.php';
|
||||
require_once 'inc/events.php';
|
||||
require_once 'inc/api.php';
|
||||
if (!extension_loaded('gettext')) {
|
||||
require_once 'inc/lib/gettext/gettext.inc';
|
||||
}
|
||||
|
||||
use Lifo\IP\IP; // for expanding IPv6 address in DNSBL()
|
||||
|
||||
// the user is not currently logged in as a moderator
|
||||
$mod = false;
|
||||
|
@ -33,6 +23,9 @@ function init_locale($locale) {
|
|||
if (extension_loaded('gettext')) {
|
||||
if (setlocale(LC_ALL, $locale) === false) {
|
||||
//$error('The specified locale (' . $locale . ') does not exist on your platform!');
|
||||
// Fall back to C.UTF-8 instead of normal C, so we support unicode instead of just ASCII
|
||||
setlocale(LC_ALL, "C.UTF-8");
|
||||
setlocale(LC_CTYPE, "C.UTF-8");
|
||||
}
|
||||
bindtextdomain('tinyboard', './inc/locale');
|
||||
bind_textdomain_codeset('tinyboard', 'UTF-8');
|
||||
|
@ -40,6 +33,9 @@ function init_locale($locale) {
|
|||
} else {
|
||||
if (_setlocale(LC_ALL, $locale) === false) {
|
||||
error('The specified locale (' . $locale . ') does not exist on your platform!');
|
||||
// Fall back to C.UTF-8 instead of normal C, so we support unicode instead of just ASCII
|
||||
_setlocale(LC_ALL, "C.UTF-8");
|
||||
_setlocale(LC_CTYPE, "C.UTF-8");
|
||||
}
|
||||
_bindtextdomain('tinyboard', './inc/locale');
|
||||
_bind_textdomain_codeset('tinyboard', 'UTF-8');
|
||||
|
@ -86,6 +82,8 @@ function loadConfig() {
|
|||
'db',
|
||||
'api',
|
||||
'cache',
|
||||
'lock',
|
||||
'queue',
|
||||
'cookies',
|
||||
'error',
|
||||
'dir',
|
||||
|
@ -123,7 +121,7 @@ function loadConfig() {
|
|||
// So, we may store the locale in a tmp/ filesystem.
|
||||
|
||||
if (file_exists($fn = 'tmp/cache/locale_' . $boardsuffix ) ) {
|
||||
$config['locale'] = file_get_contents($fn);
|
||||
$config['locale'] = @file_get_contents($fn);
|
||||
}
|
||||
else {
|
||||
$config['locale'] = 'en';
|
||||
|
@ -134,13 +132,13 @@ function loadConfig() {
|
|||
$configstr .= file_get_contents($board['dir'] . '/config.php');
|
||||
}
|
||||
$matches = array();
|
||||
preg_match_all('/[^\/*#]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/', $configstr, $matches);
|
||||
preg_match_all('/[^\/#*]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/', $configstr, $matches);
|
||||
if ($matches && isset ($matches[2]) && $matches[2]) {
|
||||
$matches = $matches[2];
|
||||
$config['locale'] = $matches[count($matches)-1];
|
||||
}
|
||||
|
||||
file_put_contents($fn, $config['locale']);
|
||||
@file_put_contents($fn, $config['locale']);
|
||||
}
|
||||
|
||||
if ($config['locale'] != $current_locale) {
|
||||
|
@ -214,6 +212,7 @@ function loadConfig() {
|
|||
if (!isset($config['image_deleted']))
|
||||
$config['image_deleted'] = $config['dir']['static'] . 'deleted.png';
|
||||
|
||||
if (isset($board)) {
|
||||
if (!isset($config['uri_thumb']))
|
||||
$config['uri_thumb'] = $config['root'] . $board['dir'] . $config['dir']['thumb'];
|
||||
elseif (isset($board['dir']))
|
||||
|
@ -223,6 +222,7 @@ function loadConfig() {
|
|||
$config['uri_img'] = $config['root'] . $board['dir'] . $config['dir']['img'];
|
||||
elseif (isset($board['dir']))
|
||||
$config['uri_img'] = sprintf($config['uri_img'], $board['dir']);
|
||||
}
|
||||
|
||||
if (!isset($config['uri_stylesheets']))
|
||||
$config['uri_stylesheets'] = $config['root'] . 'stylesheets/';
|
||||
|
@ -279,9 +279,6 @@ function loadConfig() {
|
|||
if ($config['syslog'])
|
||||
openlog('tinyboard', LOG_ODELAY, LOG_SYSLOG); // open a connection to sysem logger
|
||||
|
||||
if ($config['recaptcha'])
|
||||
require_once 'inc/lib/recaptcha/recaptchalib.php';
|
||||
|
||||
if ($config['cache']['enabled'])
|
||||
require_once 'inc/cache.php';
|
||||
|
||||
|
@ -306,8 +303,12 @@ function loadConfig() {
|
|||
Cache::set('events_'.$boardsuffix, $events);
|
||||
}
|
||||
|
||||
if (is_array($config['anonymous']))
|
||||
if (is_array($config['anonymous'])) {
|
||||
$config['anonymous'] = $config['anonymous'][array_rand($config['anonymous'])];
|
||||
}
|
||||
else if (is_callable($config['anonymous'])){
|
||||
$config['anonymous'] = $config['anonymous']($boardsuffix);
|
||||
}
|
||||
|
||||
if ($config['debug']) {
|
||||
if (!isset($debug)) {
|
||||
|
@ -346,7 +347,7 @@ function define_groups() {
|
|||
foreach ($config['mod']['groups'] as $group_value => $group_name) {
|
||||
$group_name = strtoupper($group_name);
|
||||
if(!defined($group_name)) {
|
||||
define($group_name, $group_value, true);
|
||||
define($group_name, $group_value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,7 +481,8 @@ function setupBoard($array) {
|
|||
$board = array(
|
||||
'uri' => $array['uri'],
|
||||
'title' => $array['title'],
|
||||
'subtitle' => $array['subtitle']
|
||||
'subtitle' => $array['subtitle'],
|
||||
#'indexed' => $array['indexed'],
|
||||
);
|
||||
|
||||
// older versions
|
||||
|
@ -505,14 +507,19 @@ function setupBoard($array) {
|
|||
}
|
||||
|
||||
function openBoard($uri) {
|
||||
global $config, $build_pages;
|
||||
global $config, $build_pages, $board;
|
||||
|
||||
if ($config['try_smarter'])
|
||||
$build_pages = array();
|
||||
|
||||
$board = getBoardInfo($uri);
|
||||
if ($board) {
|
||||
setupBoard($board);
|
||||
// And what if we don't really need to change a board we have opened?
|
||||
if (isset ($board) && isset ($board['uri']) && $board['uri'] == $uri) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$b = getBoardInfo($uri);
|
||||
if ($b) {
|
||||
setupBoard($b);
|
||||
|
||||
if (function_exists('after_open_board')) {
|
||||
after_open_board();
|
||||
|
@ -836,7 +843,7 @@ function displayBan($ban) {
|
|||
Element('page.html', array(
|
||||
'title' => _('Banned!'),
|
||||
'config' => $config,
|
||||
'nojavascript' => true,
|
||||
'boardlist' => createBoardlist(isset($mod) ? $mod : false),
|
||||
'body' => Element('banned.html', array(
|
||||
'config' => $config,
|
||||
'ban' => $ban,
|
||||
|
@ -973,8 +980,8 @@ function insertFloodPost(array $post) {
|
|||
}
|
||||
|
||||
function post(array $post) {
|
||||
global $pdo, $board;
|
||||
$query = prepare(sprintf("INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :files, :num_files, :filehash, :password, :ip, :sticky, :locked, 0, :embed, :slug)", $board['uri']));
|
||||
global $pdo, $board,$config;
|
||||
$query = prepare(sprintf("INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :files, :num_files, :filehash, :password, :ip, :sticky, :locked, :cycle, 0, :embed, :slug)", $board['uri']));
|
||||
|
||||
// Basic stuff
|
||||
if (!empty($post['subject'])) {
|
||||
|
@ -1014,11 +1021,22 @@ function post(array $post) {
|
|||
$query->bindValue(':locked', false, PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
if ($post['mod'] && isset($post['capcode']) && $post['capcode']) {
|
||||
$query->bindValue(':capcode', $post['capcode'], PDO::PARAM_INT);
|
||||
if ($post['op'] && $post['mod'] && isset($post['cycle']) && $post['cycle']) {
|
||||
$query->bindValue(':cycle', true, PDO::PARAM_INT);
|
||||
} else {
|
||||
$query->bindValue(':cycle', false, PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
if ($post['mod'] && isset($post['capcode']) && $post['capcode']) {
|
||||
$query->bindValue(':capcode', $post['capcode'], PDO::PARAM_STR);
|
||||
} else {
|
||||
if ($config['joke_capcode'] && isset($post['capcode']) && $post['capcode'] === 'joke') {
|
||||
$query->bindValue(':capcode', $post['capcode'], PDO::PARAM_STR);
|
||||
}
|
||||
else {
|
||||
$query->bindValue(':capcode', null, PDO::PARAM_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($post['embed'])) {
|
||||
$query->bindValue(':embed', $post['embed']);
|
||||
|
@ -1086,6 +1104,8 @@ function deleteFile($id, $remove_entirely_if_already=true, $file=null) {
|
|||
$files = json_decode($post['files']);
|
||||
$file_to_delete = $file !== false ? $files[(int)$file] : (object)array('file' => false);
|
||||
|
||||
if (!$files[0]) error(_('That post has no files.'));
|
||||
|
||||
if ($files[0]->file == 'deleted' && $post['num_files'] == 1 && !$post['thread'])
|
||||
return; // Can't delete OP's image completely.
|
||||
|
||||
|
@ -1097,8 +1117,10 @@ function deleteFile($id, $remove_entirely_if_already=true, $file=null) {
|
|||
foreach ($files as $i => $f) {
|
||||
if (($file !== false && $i == $file) || $file === null) {
|
||||
// Delete thumbnail
|
||||
if (isset ($f->thumb) && $f->thumb) {
|
||||
file_unlink($board['dir'] . $config['dir']['thumb'] . $f->thumb);
|
||||
unset($files[$i]->thumb);
|
||||
}
|
||||
|
||||
// Delete file
|
||||
file_unlink($board['dir'] . $config['dir']['img'] . $f->file);
|
||||
|
@ -1120,19 +1142,22 @@ function deleteFile($id, $remove_entirely_if_already=true, $file=null) {
|
|||
|
||||
// rebuild post (markup)
|
||||
function rebuildPost($id) {
|
||||
global $board;
|
||||
global $board, $mod;
|
||||
|
||||
$query = prepare(sprintf("SELECT `body_nomarkup`, `thread` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
|
||||
$query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
|
||||
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
if ((!$post = $query->fetch(PDO::FETCH_ASSOC)) || !$post['body_nomarkup'])
|
||||
return false;
|
||||
|
||||
markup($body = &$post['body_nomarkup']);
|
||||
markup($post['body'] = &$post['body_nomarkup']);
|
||||
$post = (object)$post;
|
||||
event('rebuildpost', $post);
|
||||
$post = (array)$post;
|
||||
|
||||
$query = prepare(sprintf("UPDATE ``posts_%s`` SET `body` = :body WHERE `id` = :id", $board['uri']));
|
||||
$query->bindValue(':body', $body);
|
||||
$query->bindValue(':body', $post['body']);
|
||||
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
|
@ -1162,6 +1187,7 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) {
|
|||
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||
event('delete', $post);
|
||||
|
||||
$thread_id = $post['thread'];
|
||||
if (!$post['thread']) {
|
||||
// Delete thread HTML page
|
||||
file_unlink($board['dir'] . $config['dir']['res'] . link_for($post) );
|
||||
|
@ -1213,6 +1239,26 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) {
|
|||
$query->bindValue(':board', $board['uri']);
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
// No need to run on OPs
|
||||
if ($config['anti_bump_flood'] && isset($thread_id)) {
|
||||
$query = prepare(sprintf("SELECT `sage` FROM ``posts_%s`` WHERE `id` = :thread", $board['uri']));
|
||||
$query->bindValue(':thread', $thread_id);
|
||||
$query->execute() or error(db_error($query));
|
||||
$bumplocked = (bool)$query->fetchColumn();
|
||||
|
||||
if (!$bumplocked) {
|
||||
$query = prepare(sprintf("SELECT `time` FROM ``posts_%s`` WHERE (`thread` = :thread AND NOT email <=> 'sage') OR `id` = :thread ORDER BY `time` DESC LIMIT 1", $board['uri']));
|
||||
$query->bindValue(':thread', $thread_id);
|
||||
$query->execute() or error(db_error($query));
|
||||
$bump = $query->fetchColumn();
|
||||
|
||||
$query = prepare(sprintf("UPDATE ``posts_%s`` SET `bump` = :bump WHERE `id` = :thread", $board['uri']));
|
||||
$query->bindValue(':bump', $bump);
|
||||
$query->bindValue(':thread', $thread_id);
|
||||
$query->execute() or error(db_error($query));
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($rebuild) && $rebuild_after) {
|
||||
buildThread($rebuild);
|
||||
buildIndex();
|
||||
|
@ -1221,7 +1267,7 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) {
|
|||
return true;
|
||||
}
|
||||
|
||||
function clean() {
|
||||
function clean($pid = false) {
|
||||
global $board, $config;
|
||||
$offset = round($config['max_pages']*$config['threads_per_page']);
|
||||
|
||||
|
@ -1232,6 +1278,22 @@ function clean() {
|
|||
$query->execute() or error(db_error($query));
|
||||
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||
deletePost($post['id'], false, false);
|
||||
if ($pid) modLog("Automatically deleting thread #{$post['id']} due to new thread #{$pid}");
|
||||
}
|
||||
|
||||
// Bump off threads with X replies earlier, spam prevention method
|
||||
if ($config['early_404']) {
|
||||
$offset = round($config['early_404_page']*$config['threads_per_page']);
|
||||
$query = prepare(sprintf("SELECT `id` AS `thread_id`, (SELECT COUNT(`id`) FROM ``posts_%s`` WHERE `thread` = `thread_id`) AS `reply_count` FROM ``posts_%s`` WHERE `thread` IS NULL ORDER BY `sticky` DESC, `bump` DESC LIMIT :offset, 9001", $board['uri'], $board['uri']));
|
||||
$query->bindValue(':offset', $offset, PDO::PARAM_INT);
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||
if ($post['reply_count'] < $config['early_404_replies']) {
|
||||
deletePost($post['thread_id'], false, false);
|
||||
if ($pid) modLog("Automatically deleting thread #{$post['thread_id']} due to new thread #{$pid} (early 404 is set, #{$post['thread_id']} had {$post['reply_count']} replies)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1245,13 +1307,14 @@ function thread_find_page($thread) {
|
|||
return floor(($config['threads_per_page'] + $index) / $config['threads_per_page']);
|
||||
}
|
||||
|
||||
function index($page, $mod=false) {
|
||||
// $brief means that we won't need to generate anything yet
|
||||
function index($page, $mod=false, $brief = false) {
|
||||
global $board, $config, $debug;
|
||||
|
||||
$body = '';
|
||||
$offset = round($page*$config['threads_per_page']-$config['threads_per_page']);
|
||||
|
||||
$query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` IS NULL ORDER BY `sticky` DESC, `bump` DESC LIMIT :offset,:threads_per_page", $board['uri']));
|
||||
$query = prepare(sprintf("SELECT *,'%s' as board FROM ``posts_%s`` WHERE `thread` IS NULL ORDER BY `sticky` DESC, `bump` DESC LIMIT :offset,:threads_per_page", $board['uri'], $board['uri']));
|
||||
$query->bindValue(':offset', $offset, PDO::PARAM_INT);
|
||||
$query->bindValue(':threads_per_page', $config['threads_per_page'], PDO::PARAM_INT);
|
||||
$query->execute() or error(db_error($query));
|
||||
|
@ -1276,6 +1339,7 @@ function index($page, $mod=false) {
|
|||
unset($cached);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($cached)) {
|
||||
$posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri']));
|
||||
$posts->bindValue(':id', $th['id']);
|
||||
|
@ -1315,8 +1379,11 @@ function index($page, $mod=false) {
|
|||
}
|
||||
|
||||
$threads[] = $thread;
|
||||
|
||||
if (!$brief) {
|
||||
$body .= $thread->build(true);
|
||||
}
|
||||
}
|
||||
|
||||
if ($config['file_board']) {
|
||||
$body = Element('fileboard.html', array('body' => $body, 'mod' => $mod));
|
||||
|
@ -1533,33 +1600,111 @@ function checkMute() {
|
|||
}
|
||||
}
|
||||
|
||||
function checkSpam(array $extra_salt = array()) {
|
||||
global $config, $pdo;
|
||||
|
||||
if (!isset($_POST['hash']))
|
||||
return true;
|
||||
|
||||
$hash = $_POST['hash'];
|
||||
|
||||
if (!empty($extra_salt)) {
|
||||
// create a salted hash of the "extra salt"
|
||||
$extra_salt = implode(':', $extra_salt);
|
||||
} else {
|
||||
$extra_salt = '';
|
||||
}
|
||||
|
||||
// Reconsturct the $inputs array
|
||||
$inputs = array();
|
||||
|
||||
foreach ($_POST as $name => $value) {
|
||||
if (in_array($name, $config['spam']['valid_inputs']))
|
||||
continue;
|
||||
|
||||
$inputs[$name] = $value;
|
||||
}
|
||||
|
||||
// Sort the inputs in alphabetical order (A-Z)
|
||||
ksort($inputs);
|
||||
|
||||
$_hash = '';
|
||||
|
||||
// Iterate through each input
|
||||
foreach ($inputs as $name => $value) {
|
||||
$_hash .= $name . '=' . $value;
|
||||
}
|
||||
|
||||
// Add a salt to the hash
|
||||
$_hash .= $config['cookies']['salt'];
|
||||
|
||||
// Use SHA1 for the hash
|
||||
$_hash = sha1($_hash . $extra_salt);
|
||||
|
||||
if ($hash != $_hash) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = prepare('SELECT `passed` FROM ``antispam`` WHERE `hash` = :hash');
|
||||
$query->bindValue(':hash', $hash);
|
||||
$query->execute() or error(db_error($query));
|
||||
if ((($passed = $query->fetchColumn(0)) === false) || ($passed > $config['spam']['hidden_inputs_max_pass'])) {
|
||||
// there was no database entry for this hash. most likely expired.
|
||||
return true;
|
||||
}
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
function incrementSpamHash($hash) {
|
||||
$query = prepare('UPDATE ``antispam`` SET `passed` = `passed` + 1 WHERE `hash` = :hash');
|
||||
$query->bindValue(':hash', $hash);
|
||||
$query->execute() or error(db_error($query));
|
||||
}
|
||||
|
||||
function buildIndex($global_api = "yes") {
|
||||
global $board, $config, $build_pages;
|
||||
|
||||
if (!$config['smart_build']) {
|
||||
$pages = getPages();
|
||||
if (!$config['try_smarter'])
|
||||
$antibot = create_antibot($board['uri']);
|
||||
$catalog_api_action = generation_strategy('sb_api', array($board['uri']));
|
||||
|
||||
$pages = null;
|
||||
$antibot = null;
|
||||
|
||||
if ($config['api']['enabled']) {
|
||||
$api = new Api();
|
||||
$catalog = array();
|
||||
}
|
||||
}
|
||||
|
||||
for ($page = 1; $page <= $config['max_pages']; $page++) {
|
||||
$filename = $board['dir'] . ($page == 1 ? $config['file_index'] : sprintf($config['file_page'], $page));
|
||||
$jsonFilename = $board['dir'] . ($page - 1) . '.json'; // pages should start from 0
|
||||
|
||||
if ((!$config['api']['enabled'] || $global_api == "skip" || $config['smart_build']) && $config['try_smarter']
|
||||
&& isset($build_pages) && !empty($build_pages) && !in_array($page, $build_pages) )
|
||||
$wont_build_this_page = $config['try_smarter'] && isset($build_pages) && !empty($build_pages) && !in_array($page, $build_pages);
|
||||
|
||||
if ((!$config['api']['enabled'] || $global_api == "skip") && $wont_build_this_page)
|
||||
continue;
|
||||
|
||||
if (!$config['smart_build']) {
|
||||
$content = index($page);
|
||||
$action = generation_strategy('sb_board', array($board['uri'], $page));
|
||||
if ($action == 'rebuild' || $catalog_api_action == 'rebuild') {
|
||||
$content = index($page, false, $wont_build_this_page);
|
||||
if (!$content)
|
||||
break;
|
||||
|
||||
// Tries to avoid rebuilding if the body is the same as the one in cache.
|
||||
if ($config['cache']['enabled']) {
|
||||
$contentHash = md5(json_encode($content['body']));
|
||||
$contentHashKey = '_index_hashed_'. $board['uri'] . '_' . $page;
|
||||
$cachedHash = cache::get($contentHashKey);
|
||||
if ($cachedHash == $contentHash){
|
||||
if ($config['api']['enabled']) {
|
||||
// this is needed for the thread.json and catalog.json rebuilding below, which includes all pages.
|
||||
$catalog[$page-1] = $content['threads'];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
cache::set($contentHashKey, $contentHash, 3600);
|
||||
}
|
||||
|
||||
// json api
|
||||
if ($config['api']['enabled']) {
|
||||
$threads = $content['threads'];
|
||||
|
@ -1567,17 +1712,21 @@ function buildIndex($global_api = "yes") {
|
|||
file_write($jsonFilename, $json);
|
||||
|
||||
$catalog[$page-1] = $threads;
|
||||
}
|
||||
|
||||
if ($config['api']['enabled'] && $global_api != "skip" && $config['try_smarter'] && isset($build_pages)
|
||||
&& !empty($build_pages) && !in_array($page, $build_pages) )
|
||||
continue;
|
||||
if ($wont_build_this_page) continue;
|
||||
}
|
||||
|
||||
if ($config['try_smarter']) {
|
||||
$antibot = create_antibot($board['uri'], 0 - $page);
|
||||
$content['current_page'] = $page;
|
||||
}
|
||||
elseif (!$antibot) {
|
||||
$antibot = create_antibot($board['uri']);
|
||||
}
|
||||
$antibot->reset();
|
||||
if (!$pages) {
|
||||
$pages = getPages();
|
||||
}
|
||||
$content['pages'] = $pages;
|
||||
$content['pages'][$page-1]['selected'] = true;
|
||||
$content['btn'] = getPageButtons($content['pages']);
|
||||
|
@ -1585,13 +1734,14 @@ function buildIndex($global_api = "yes") {
|
|||
|
||||
file_write($filename, Element('index.html', $content));
|
||||
}
|
||||
else {
|
||||
elseif ($action == 'delete' || $catalog_api_action == 'delete') {
|
||||
file_unlink($filename);
|
||||
file_unlink($jsonFilename);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$config['smart_build'] && $page < $config['max_pages']) {
|
||||
// $action is an action for our last page
|
||||
if (($catalog_api_action == 'rebuild' || $action == 'rebuild' || $action == 'delete') && $page < $config['max_pages']) {
|
||||
for (;$page<=$config['max_pages'];$page++) {
|
||||
$filename = $board['dir'] . ($page==1 ? $config['file_index'] : sprintf($config['file_page'], $page));
|
||||
file_unlink($filename);
|
||||
|
@ -1605,13 +1755,13 @@ function buildIndex($global_api = "yes") {
|
|||
|
||||
// json api catalog
|
||||
if ($config['api']['enabled'] && $global_api != "skip") {
|
||||
if ($config['smart_build']) {
|
||||
if ($catalog_api_action == 'delete') {
|
||||
$jsonFilename = $board['dir'] . 'catalog.json';
|
||||
file_unlink($jsonFilename);
|
||||
$jsonFilename = $board['dir'] . 'threads.json';
|
||||
file_unlink($jsonFilename);
|
||||
}
|
||||
else {
|
||||
elseif ($catalog_api_action == 'rebuild') {
|
||||
$json = json_encode($api->translateCatalog($catalog));
|
||||
$jsonFilename = $board['dir'] . 'catalog.json';
|
||||
file_write($jsonFilename, $json);
|
||||
|
@ -1656,13 +1806,12 @@ function buildJavascript() {
|
|||
}
|
||||
|
||||
if ($config['additional_javascript_compile']) {
|
||||
foreach ($config['additional_javascript'] as $file) {
|
||||
foreach (array_merge($config['additional_javascript'], $config['additional_javascript_defer']) as $file) {
|
||||
$script .= file_get_contents($file);
|
||||
}
|
||||
}
|
||||
|
||||
if ($config['minify_js']) {
|
||||
require_once 'inc/lib/minify/JSMin.php';
|
||||
$script = JSMin::minify($script);
|
||||
}
|
||||
|
||||
|
@ -1672,17 +1821,20 @@ function buildJavascript() {
|
|||
function checkDNSBL() {
|
||||
global $config;
|
||||
|
||||
|
||||
if (isIPv6())
|
||||
return; // No IPv6 support yet.
|
||||
|
||||
if (!isset($_SERVER['REMOTE_ADDR']))
|
||||
return; // Fix your web server configuration
|
||||
|
||||
if (preg_match("/^(::(ffff:)?)?(127\.|192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|0\.|255\.)/", $_SERVER['REMOTE_ADDR']))
|
||||
return; // It's pointless to check for local IP addresses in dnsbls, isn't it?
|
||||
|
||||
if (in_array($_SERVER['REMOTE_ADDR'], $config['dnsbl_exceptions']))
|
||||
return;
|
||||
|
||||
$ipaddr = ReverseIPOctets($_SERVER['REMOTE_ADDR']);
|
||||
if (isIPv6()) {
|
||||
$ipaddr = ReverseIPv6Octets($_SERVER['REMOTE_ADDR']);
|
||||
} else {
|
||||
$ipaddr = ReverseIPv4Octets($_SERVER['REMOTE_ADDR']);
|
||||
}
|
||||
|
||||
foreach ($config['dnsbl'] as $blacklist) {
|
||||
if (!is_array($blacklist))
|
||||
|
@ -1718,14 +1870,49 @@ function isIPv6() {
|
|||
return strstr($_SERVER['REMOTE_ADDR'], ':') !== false;
|
||||
}
|
||||
|
||||
function ReverseIPOctets($ip) {
|
||||
function ReverseIPv4Octets($ip) {
|
||||
return implode('.', array_reverse(explode('.', $ip)));
|
||||
}
|
||||
|
||||
function ReverseIPv6Octets($ip) {
|
||||
return strrev(implode(".", str_split(str_replace(':', '', IP::inet_expand($ip)))));
|
||||
}
|
||||
|
||||
function wordfilters(&$body) {
|
||||
global $config;
|
||||
|
||||
foreach ($config['wordfilters'] as $filter) {
|
||||
if (isset($filter[3]) && $filter[3]) {
|
||||
$refilter = $filter[0];
|
||||
if (strncmp($filter[0], "/", 1) !== 0)
|
||||
{
|
||||
$refilter = "/.*" . $filter[0] . "/";
|
||||
}
|
||||
$body = preg_replace_callback($refilter,
|
||||
function($matches) use ($filter , $body) {
|
||||
foreach ($matches as $match) {
|
||||
if (preg_match("/(http|https|ftp):\/\//i", $match)) {
|
||||
return $match;
|
||||
} else {
|
||||
if (isset($filter[2]) && $filter[2]) {
|
||||
$refilter = $filter[0];
|
||||
if (strncmp($filter[0], "/", 1) !== 0)
|
||||
{
|
||||
$refilter = "/.*" . $filter[0] . "/";
|
||||
}
|
||||
if (is_callable($filter[1]))
|
||||
return preg_replace_callback($refilter, $filter[1], $match);
|
||||
else
|
||||
return preg_replace($refilter, $filter[1], $match);
|
||||
} else {
|
||||
return str_ireplace($filter[0], $filter[1], $match);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
, $body);
|
||||
} else {
|
||||
if (isset($filter[2]) && $filter[2]) {
|
||||
if (is_callable($filter[1]))
|
||||
$body = preg_replace_callback($filter[0], $filter[1], $body);
|
||||
|
@ -1735,8 +1922,11 @@ function wordfilters(&$body) {
|
|||
$body = str_ireplace($filter[0], $filter[1], $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function quote($body, $quote=true) {
|
||||
global $config;
|
||||
|
||||
|
@ -1797,6 +1987,10 @@ function unicodify($body) {
|
|||
return $body;
|
||||
}
|
||||
|
||||
function newline_to_full_stop($body) {
|
||||
return str_replace("\n", '. ', $body);
|
||||
}
|
||||
|
||||
function extract_modifiers($body) {
|
||||
$modifiers = array();
|
||||
|
||||
|
@ -1811,7 +2005,22 @@ function extract_modifiers($body) {
|
|||
return $modifiers;
|
||||
}
|
||||
|
||||
function markup(&$body, $track_cites = false) {
|
||||
function remove_markup($body) {
|
||||
global $config;
|
||||
|
||||
foreach ($config['markup'] as $markup) {
|
||||
if (is_string($markup[1]))
|
||||
$body = preg_replace($markup[0], "$1", $body);
|
||||
}
|
||||
return $body;
|
||||
}
|
||||
|
||||
|
||||
function remove_modifiers($body) {
|
||||
return preg_replace('@<tinyboard ([\w\s]+)>(.+?)</tinyboard>@usm', '', $body);
|
||||
}
|
||||
|
||||
function markup(&$body, $track_cites = false, $op = false) {
|
||||
global $board, $config, $markup_urls;
|
||||
|
||||
$modifiers = extract_modifiers($body);
|
||||
|
@ -1850,15 +2059,16 @@ function markup(&$body, $track_cites = false) {
|
|||
$markup_urls = array();
|
||||
|
||||
$body = preg_replace_callback(
|
||||
'/((?:https?:\/\/|ftp:\/\/|irc:\/\/)[^\s<>()"]+?(?:\([^\s<>()"]*?\)[^\s<>()"]*?)*)((?:\s|<|>|"|\.||\]|!|\?|,|,|")*(?:[\s<>()"]|$))/',
|
||||
'/((?:https?:\/\/|ftp:\/\/|irc:\/\/|gopher:\/\/)[^\s<>()"]+?(?:\([^\s<>()"]*?\)[^\s<>()"]*?)*)((?:\s|<|>|"|\.||\]|!|\?|,|,|")*(?:[\s<>()"]|$))/',
|
||||
'markup_url',
|
||||
$body,
|
||||
-1,
|
||||
$num_links);
|
||||
|
||||
if ($num_links > $config['max_links'])
|
||||
if ($num_links > $config['max_links']) {
|
||||
error($config['error']['toomanylinks']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($config['markup_repair_tidy'])
|
||||
$body = str_replace(' ', ' ', $body);
|
||||
|
@ -1876,7 +2086,7 @@ function markup(&$body, $track_cites = false) {
|
|||
$tracked_cites = array();
|
||||
|
||||
// Cites
|
||||
if (isset($board) && preg_match_all('/(^|\s)>>(\d+?)([\s,.)?]|$)/m', $body, $cites, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
|
||||
if (isset($board) && preg_match_all('/(^|\s)>>(\d+?)((?=[\s,.:)?!])|$)/m', $body, $cites, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
|
||||
if (count($cites[0]) > $config['max_cites']) {
|
||||
error($config['error']['toomanycites']);
|
||||
}
|
||||
|
@ -1907,7 +2117,7 @@ function markup(&$body, $track_cites = false) {
|
|||
}
|
||||
|
||||
if (isset($cited_posts[$cite])) {
|
||||
$replacement = '<a onclick="highlightReply(\''.$cite.'\');" href="' .
|
||||
$replacement = '<a onclick="highlightReply(\''.$cite.'\', event);" href="' .
|
||||
$config['root'] . $board['dir'] . $config['dir']['res'] .
|
||||
link_for(array('id' => $cite, 'thread' => $cited_posts[$cite])) . '#' . $cite . '">' .
|
||||
'>>' . $cite .
|
||||
|
@ -1923,7 +2133,7 @@ function markup(&$body, $track_cites = false) {
|
|||
}
|
||||
|
||||
// Cross-board linking
|
||||
if (preg_match_all('/(^|\s)>>>\/(' . $config['board_regex'] . 'f?)\/(\d+)?([\s,.)?]|$)/um', $body, $cites, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
|
||||
if (preg_match_all('/(^|\s)>>>\/(' . $config['board_regex'] . 'f?)\/(\d+)?((?=[\s,.:)?!])|$)/um', $body, $cites, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
|
||||
if (count($cites[0]) > $config['max_cites']) {
|
||||
error($config['error']['toomanycross']);
|
||||
}
|
||||
|
@ -1968,9 +2178,22 @@ function markup(&$body, $track_cites = false) {
|
|||
$clauses = array_unique($clauses);
|
||||
|
||||
if ($board['uri'] != $_board) {
|
||||
if (!openBoard($_board))
|
||||
if (!openBoard($_board)){
|
||||
if (in_array($_board,array_keys($config['boards_alias']))){
|
||||
$_board = $config['boards_alias'][$_board];
|
||||
if (openBoard($_board)){
|
||||
|
||||
}
|
||||
else {
|
||||
continue; // Unknown board
|
||||
}
|
||||
}
|
||||
else {
|
||||
continue; // Unknown board
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($clauses)) {
|
||||
$cited_posts[$_board] = array();
|
||||
|
@ -1992,7 +2215,13 @@ function markup(&$body, $track_cites = false) {
|
|||
openBoard($tmp_board);
|
||||
|
||||
foreach ($cites as $matches) {
|
||||
$original_board = NULL;
|
||||
$_board = $matches[2][0];
|
||||
if (in_array($_board,array_keys($config['boards_alias']))){
|
||||
$original_board = $_board;
|
||||
$_board = $config['boards_alias'][$_board];
|
||||
|
||||
}
|
||||
$cite = @$matches[3][0];
|
||||
|
||||
// preg_match_all is not multibyte-safe
|
||||
|
@ -2003,14 +2232,25 @@ function markup(&$body, $track_cites = false) {
|
|||
if ($cite) {
|
||||
if (isset($cited_posts[$_board][$cite])) {
|
||||
$link = $cited_posts[$_board][$cite];
|
||||
|
||||
if (isset($original_board)){
|
||||
$replacement = '<a ' .
|
||||
($_board == $board['uri'] ?
|
||||
'onclick="highlightReply(\''.$cite.'\');" '
|
||||
'onclick="highlightReply(\''.$cite.'\', event);" '
|
||||
: '') . 'href="' . $link . '">' .
|
||||
'>>>/' . $original_board . '/' . $cite .
|
||||
'</a>';
|
||||
|
||||
}
|
||||
else {
|
||||
$replacement = '<a ' .
|
||||
($_board == $board['uri'] ?
|
||||
'onclick="highlightReply(\''.$cite.'\', event);" '
|
||||
: '') . 'href="' . $link . '">' .
|
||||
'>>>/' . $_board . '/' . $cite .
|
||||
'</a>';
|
||||
|
||||
}
|
||||
|
||||
$body = mb_substr_replace($body, $matches[1][0] . $replacement . $matches[4][0], $matches[0][1] + $skip_chars, mb_strlen($matches[0][0]));
|
||||
$skip_chars += mb_strlen($matches[1][0] . $replacement . $matches[4][0]) - mb_strlen($matches[0][0]);
|
||||
|
||||
|
@ -2044,7 +2284,7 @@ function markup(&$body, $track_cites = false) {
|
|||
|
||||
$code = rtrim(ltrim($code, "\r\n"));
|
||||
|
||||
$code = "<pre class='code lang-$code_lang'>".str_replace(array("\n","\t"), array(" ","	"), htmlspecialchars($code))."</pre>";
|
||||
$code = "<pre class='code lang-$code_lang'>".str_replace(array("\n","\t"), array(" ","	"), htmlspecialchars($code, ENT_COMPAT, "UTF-8", false))."</pre>";
|
||||
|
||||
$body = str_replace("<code $id>", $code, $body);
|
||||
}
|
||||
|
@ -2105,36 +2345,20 @@ function ordutf8($string, &$offset) {
|
|||
return $code;
|
||||
}
|
||||
|
||||
// Limit Non_Spacing_Mark and Enclosing_Mark characters
|
||||
function strip_combining_chars($str) {
|
||||
$chars = preg_split('//u', $str, -1, PREG_SPLIT_NO_EMPTY);
|
||||
$str = '';
|
||||
foreach ($chars as $char) {
|
||||
$o = 0;
|
||||
$ord = ordutf8($char, $o);
|
||||
|
||||
if ($ord >= 768 && $ord <= 879)
|
||||
continue;
|
||||
|
||||
if ($ord >= 7616 && $ord <= 7679)
|
||||
continue;
|
||||
|
||||
if ($ord >= 8400 && $ord <= 8447)
|
||||
continue;
|
||||
|
||||
if ($ord >= 65056 && $ord <= 65071)
|
||||
continue;
|
||||
|
||||
$str .= $char;
|
||||
}
|
||||
return $str;
|
||||
global $config;
|
||||
$limit = strval($config['max_combining_chars']+1);
|
||||
return preg_replace('/(\p{Me}|\p{Mn}){'.$limit.',}/u','', $str);
|
||||
}
|
||||
|
||||
function buildThread($id, $return = false, $mod = false) {
|
||||
global $board, $config, $build_pages;
|
||||
$id = round($id);
|
||||
|
||||
if (event('build-thread', $id))
|
||||
if (event('build-thread', $id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($config['cache']['enabled'] && !$mod) {
|
||||
// Clear cache
|
||||
|
@ -2145,8 +2369,10 @@ function buildThread($id, $return = false, $mod = false) {
|
|||
if ($config['try_smarter'] && !$mod)
|
||||
$build_pages[] = thread_find_page($id);
|
||||
|
||||
if (!$config['smart_build'] || $return || $mod) {
|
||||
$query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri']));
|
||||
$action = generation_strategy('sb_thread', array($board['uri'], $id));
|
||||
|
||||
if ($action == 'rebuild' || $return || $mod) {
|
||||
$query = prepare(sprintf("SELECT *,'%s' as board FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri'],$board['uri']));
|
||||
$query->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
|
@ -2180,26 +2406,26 @@ function buildThread($id, $return = false, $mod = false) {
|
|||
));
|
||||
|
||||
// json api
|
||||
if ($config['api']['enabled']) {
|
||||
if ($config['api']['enabled'] && !$mod) {
|
||||
$api = new Api();
|
||||
$json = json_encode($api->translateThread($thread));
|
||||
$jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json';
|
||||
file_write($jsonFilename, $json);
|
||||
}
|
||||
}
|
||||
else {
|
||||
elseif($action == 'delete') {
|
||||
$jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json';
|
||||
file_unlink($jsonFilename);
|
||||
}
|
||||
|
||||
if ($config['smart_build'] && !$return && !$mod) {
|
||||
if ($action == 'delete' && !$return && !$mod) {
|
||||
$noko50fn = $board['dir'] . $config['dir']['res'] . link_for(array('id' => $id), true);
|
||||
file_unlink($noko50fn);
|
||||
|
||||
file_unlink($board['dir'] . $config['dir']['res'] . link_for(array('id' => $id)));
|
||||
} else if ($return) {
|
||||
} elseif ($return) {
|
||||
return $body;
|
||||
} else {
|
||||
} elseif ($action == 'rebuild') {
|
||||
$noko50fn = $board['dir'] . $config['dir']['res'] . link_for($thread, true);
|
||||
if ($hasnoko50 || file_exists($noko50fn)) {
|
||||
buildThread50($id, $return, $mod, $thread, $antibot);
|
||||
|
@ -2340,7 +2566,7 @@ function generate_tripcode($name) {
|
|||
if (isset($config['custom_tripcode']["##{$trip}"]))
|
||||
$trip = $config['custom_tripcode']["##{$trip}"];
|
||||
else
|
||||
$trip = '!!' . substr(crypt($trip, '_..A.' . substr(base64_encode(sha1($trip . $config['secure_trip_salt'], true)), 0, 4)), -10);
|
||||
$trip = '!!' . substr(crypt($trip, str_replace('+', '.', '_..A.' . substr(base64_encode(sha1($trip . $config['secure_trip_salt'], true)), 0, 4))), -10);
|
||||
} else {
|
||||
if (isset($config['custom_tripcode']["#{$trip}"]))
|
||||
$trip = $config['custom_tripcode']["#{$trip}"];
|
||||
|
@ -2429,7 +2655,7 @@ function rDNS($ip_addr) {
|
|||
if (!$config['dns_system']) {
|
||||
$host = gethostbyaddr($ip_addr);
|
||||
} else {
|
||||
$resp = shell_exec_error('host -W 1 ' . $ip_addr);
|
||||
$resp = shell_exec_error('host -W 3 ' . $ip_addr);
|
||||
if (preg_match('/domain name pointer ([^\s]+)$/', $resp, $m))
|
||||
$host = $m[1];
|
||||
else
|
||||
|
@ -2565,13 +2791,14 @@ function slugify($post) {
|
|||
elseif (isset ($post['body_nomarkup']) && $post['body_nomarkup'])
|
||||
$slug = $post['body_nomarkup'];
|
||||
elseif (isset ($post['body']) && $post['body'])
|
||||
$slug = strip_html($post['body']);
|
||||
$slug = strip_tags($post['body']);
|
||||
|
||||
// Fix UTF-8 first
|
||||
$slug = mb_convert_encoding($slug, "UTF-8", "UTF-8");
|
||||
|
||||
// Transliterate local characters like ü, I wonder how would it work for weird alphabets :^)
|
||||
$slug = iconv("UTF-8", "ASCII//TRANSLIT//IGNORE", $slug);
|
||||
// $slug = iconv("UTF-8", "ASCII//TRANSLIT//IGNORE", $slug);
|
||||
$slug = mb_convert_encoding($slug, "ASCII", "UTF-8");
|
||||
|
||||
// Remove Tinyboard custom markup
|
||||
$slug = preg_replace("/<tinyboard [^>]+>.*?<\/tinyboard>/s", '', $slug);
|
||||
|
@ -2640,3 +2867,121 @@ function link_for($post, $page50 = false, $foreignlink = false, $thread = false)
|
|||
|
||||
return sprintf($tpl, $id, $slug);
|
||||
}
|
||||
|
||||
// Generate filename, extension, file id and file and thumb paths of a file
|
||||
function process_filenames($file, $board_dir, $multiple, $i){
|
||||
global $config;
|
||||
$file['filename'] = urldecode($file['name']);
|
||||
$file['extension'] = strtolower(mb_substr($file['filename'], mb_strrpos($file['filename'], '.') + 1));
|
||||
if (isset($config['filename_func']))
|
||||
$file['file_id'] = $config['filename_func']($file);
|
||||
else
|
||||
$file['file_id'] = time() . substr(microtime(), 2, 3);
|
||||
|
||||
if ($multiple)
|
||||
$file['file_id'] .= "-$i";
|
||||
|
||||
$file['file'] = $board_dir . $config['dir']['img'] . $file['file_id'] . '.' . $file['extension'];
|
||||
$file['thumb'] = $board_dir . $config['dir']['thumb'] . $file['file_id'] . '.' . ($config['thumb_ext'] ? $config['thumb_ext'] : $file['extension']);
|
||||
return $file;
|
||||
}
|
||||
|
||||
function prettify_textarea($s){
|
||||
return str_replace("\t", '	', str_replace("\n", ' ', htmlentities($s)));
|
||||
}
|
||||
|
||||
/*class HTMLPurifier_URIFilter_NoExternalImages extends HTMLPurifier_URIFilter {
|
||||
public $name = 'NoExternalImages';
|
||||
public function filter(&$uri, $c, $context) {
|
||||
global $config;
|
||||
$ct = $context->get('CurrentToken');
|
||||
|
||||
if (!$ct || $ct->name !== 'img') return true;
|
||||
|
||||
if (!isset($uri->host) && !isset($uri->scheme)) return true;
|
||||
|
||||
if (!in_array($uri->scheme . '://' . $uri->host . '/', $config['allowed_offsite_urls'])) {
|
||||
error('No off-site links in board announcement images.');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
|
||||
function purify_html($s) {
|
||||
global $config;
|
||||
|
||||
$c = HTMLPurifier_Config::createDefault();
|
||||
$c->set('HTML.Allowed', $config['allowed_html']);
|
||||
$uri = $c->getDefinition('URI');
|
||||
$uri->addFilter(new HTMLPurifier_URIFilter_NoExternalImages(), $c);
|
||||
$purifier = new HTMLPurifier($c);
|
||||
$clean_html = $purifier->purify($s);
|
||||
return $clean_html;
|
||||
}
|
||||
|
||||
function markdown($s) {
|
||||
$pd = new Parsedown();
|
||||
$pd->setMarkupEscaped(true);
|
||||
$pd->setimagesEnabled(false);
|
||||
|
||||
return $pd->text($s);
|
||||
}
|
||||
|
||||
function generation_strategy($fun, $array=array()) { global $config;
|
||||
$action = false;
|
||||
|
||||
foreach ($config['generation_strategies'] as $s) {
|
||||
if ($action = $s($fun, $array)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($action[0]) {
|
||||
case 'immediate':
|
||||
return 'rebuild';
|
||||
case 'defer':
|
||||
// Ok, it gets interesting here :)
|
||||
get_queue('generate')->push(serialize(array('build', $fun, $array, $action)));
|
||||
return 'ignore';
|
||||
case 'build_on_load':
|
||||
return 'delete';
|
||||
}
|
||||
}
|
||||
|
||||
function strategy_immediate($fun, $array) {
|
||||
return array('immediate');
|
||||
}
|
||||
|
||||
function strategy_smart_build($fun, $array) {
|
||||
return array('build_on_load');
|
||||
}
|
||||
|
||||
function strategy_sane($fun, $array) { global $config;
|
||||
if (php_sapi_name() == 'cli') return false;
|
||||
else if (isset($_POST['mod'])) return false;
|
||||
// Thread needs to be done instantly. Same with a board page, but only if posting a new thread.
|
||||
else if ($fun == 'sb_thread' || ($fun == 'sb_board' && $array[1] == 1 && isset ($_POST['page']))) return array('immediate');
|
||||
else return false;
|
||||
}
|
||||
|
||||
// My first, test strategy.
|
||||
function strategy_first($fun, $array) {
|
||||
switch ($fun) {
|
||||
case 'sb_thread':
|
||||
return array('defer');
|
||||
case 'sb_board':
|
||||
if ($array[1] > 8) return array('build_on_load');
|
||||
else return array('defer');
|
||||
case 'sb_api':
|
||||
return array('defer');
|
||||
case 'sb_catalog':
|
||||
return array('defer');
|
||||
case 'sb_recent':
|
||||
return array('build_on_load');
|
||||
case 'sb_sitemap':
|
||||
return array('build_on_load');
|
||||
case 'sb_ukko':
|
||||
return array('defer');
|
||||
}
|
||||
}
|
||||
|
|
17
inc/functions/net.php
Normal file
17
inc/functions/net.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
namespace Vichan\Functions\Net;
|
||||
|
||||
|
||||
/**
|
||||
* @return bool Returns if the client-server connection is an HTTPS one.
|
||||
*/
|
||||
function is_connection_https(): bool {
|
||||
return !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Returns if the client-server connection is an encrypted one (HTTPS or Tor loopback).
|
||||
*/
|
||||
function is_connection_secure(): bool {
|
||||
return is_connection_https() || (!empty($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] === '127.0.0.1');
|
||||
}
|
172
inc/image.php
172
inc/image.php
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
defined('TINYBOARD') or exit;
|
||||
require_once 'inc/polyfill.php';
|
||||
|
||||
class Image {
|
||||
public $src, $format, $image, $size;
|
||||
|
@ -311,7 +312,7 @@ class ImageConvert extends ImageBase {
|
|||
$this->destroy();
|
||||
}
|
||||
|
||||
$this->temp = tempnam($config['tmp'], 'convert');
|
||||
$this->temp = tempnam($config['tmp'], 'convert') . ($config['thumb_ext'] == '' ? '' : '.' . $config['thumb_ext']);
|
||||
|
||||
$config['thumb_keep_animation_frames'] = (int)$config['thumb_keep_animation_frames'];
|
||||
|
||||
|
@ -496,170 +497,11 @@ class ImageBMP extends ImageBase {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*********************************************/
|
||||
/* Fonction: imagecreatefrombmp */
|
||||
/* Author: DHKold */
|
||||
/* Contact: admin@dhkold.com */
|
||||
/* Date: The 15th of June 2005 */
|
||||
/* Version: 2.0B */
|
||||
/*********************************************/
|
||||
|
||||
function imagecreatefrombmp($filename) {
|
||||
if (! $f1 = fopen($filename,"rb")) return FALSE;
|
||||
$FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1,14));
|
||||
if ($FILE['file_type'] != 19778) return FALSE;
|
||||
$BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'.
|
||||
'/Vcompression/Vsize_bitmap/Vhoriz_resolution'.
|
||||
'/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1,40));
|
||||
$BMP['colors'] = pow(2,$BMP['bits_per_pixel']);
|
||||
if ($BMP['size_bitmap'] == 0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
|
||||
$BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8;
|
||||
$BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']);
|
||||
$BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4);
|
||||
$BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4);
|
||||
$BMP['decal'] = 4-(4*$BMP['decal']);
|
||||
if ($BMP['decal'] == 4) $BMP['decal'] = 0;
|
||||
|
||||
$PALETTE = array();
|
||||
if ($BMP['colors'] < 16777216)
|
||||
{
|
||||
$PALETTE = unpack('V'.$BMP['colors'], fread($f1,$BMP['colors']*4));
|
||||
class ImageWEBP extends ImageBase {
|
||||
public function from() {
|
||||
$this->image = @imagecreatefromwebp($this->src);
|
||||
}
|
||||
|
||||
$IMG = fread($f1,$BMP['size_bitmap']);
|
||||
$VIDE = chr(0);
|
||||
|
||||
$res = imagecreatetruecolor($BMP['width'],$BMP['height']);
|
||||
$P = 0;
|
||||
$Y = $BMP['height']-1;
|
||||
while ($Y >= 0)
|
||||
{
|
||||
$X=0;
|
||||
while ($X < $BMP['width'])
|
||||
{
|
||||
if ($BMP['bits_per_pixel'] == 24)
|
||||
$COLOR = unpack("V",substr($IMG,$P,3).$VIDE);
|
||||
elseif ($BMP['bits_per_pixel'] == 16)
|
||||
{
|
||||
$COLOR = unpack("n",substr($IMG,$P,2));
|
||||
$COLOR[1] = $PALETTE[$COLOR[1]+1];
|
||||
}
|
||||
elseif ($BMP['bits_per_pixel'] == 8)
|
||||
{
|
||||
$COLOR = unpack("n",$VIDE.substr($IMG,$P,1));
|
||||
$COLOR[1] = $PALETTE[$COLOR[1]+1];
|
||||
}
|
||||
elseif ($BMP['bits_per_pixel'] == 4)
|
||||
{
|
||||
$COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1));
|
||||
if (($P*2)%2 == 0) $COLOR[1] = ($COLOR[1] >> 4) ; else $COLOR[1] = ($COLOR[1] & 0x0F);
|
||||
$COLOR[1] = $PALETTE[$COLOR[1]+1];
|
||||
}
|
||||
elseif ($BMP['bits_per_pixel'] == 1)
|
||||
{
|
||||
$COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1));
|
||||
if (($P*8)%8 == 0) $COLOR[1] = $COLOR[1] >>7;
|
||||
elseif (($P*8)%8 == 1) $COLOR[1] = ($COLOR[1] & 0x40)>>6;
|
||||
elseif (($P*8)%8 == 2) $COLOR[1] = ($COLOR[1] & 0x20)>>5;
|
||||
elseif (($P*8)%8 == 3) $COLOR[1] = ($COLOR[1] & 0x10)>>4;
|
||||
elseif (($P*8)%8 == 4) $COLOR[1] = ($COLOR[1] & 0x8)>>3;
|
||||
elseif (($P*8)%8 == 5) $COLOR[1] = ($COLOR[1] & 0x4)>>2;
|
||||
elseif (($P*8)%8 == 6) $COLOR[1] = ($COLOR[1] & 0x2)>>1;
|
||||
elseif (($P*8)%8 == 7) $COLOR[1] = ($COLOR[1] & 0x1);
|
||||
$COLOR[1] = $PALETTE[$COLOR[1]+1];
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
imagesetpixel($res,$X,$Y,$COLOR[1]);
|
||||
$X++;
|
||||
$P += $BMP['bytes_per_pixel'];
|
||||
}
|
||||
$Y--;
|
||||
$P+=$BMP['decal'];
|
||||
}
|
||||
fclose($f1);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
function imagebmp(&$img, $filename='') {
|
||||
$widthOrig = imagesx($img);
|
||||
$widthFloor = ((floor($widthOrig/16))*16);
|
||||
$widthCeil = ((ceil($widthOrig/16))*16);
|
||||
$height = imagesy($img);
|
||||
|
||||
$size = ($widthCeil*$height*3)+54;
|
||||
|
||||
// Bitmap File Header
|
||||
$result = 'BM'; // header (2b)
|
||||
$result .= int_to_dword($size); // size of file (4b)
|
||||
$result .= int_to_dword(0); // reserved (4b)
|
||||
$result .= int_to_dword(54); // byte location in the file which is first byte of IMAGE (4b)
|
||||
// Bitmap Info Header
|
||||
$result .= int_to_dword(40); // Size of BITMAPINFOHEADER (4b)
|
||||
$result .= int_to_dword($widthCeil); // width of bitmap (4b)
|
||||
$result .= int_to_dword($height); // height of bitmap (4b)
|
||||
$result .= int_to_word(1); // biPlanes = 1 (2b)
|
||||
$result .= int_to_word(24); // biBitCount = {1 (mono) or 4 (16 clr ) or 8 (256 clr) or 24 (16 Mil)} (2b
|
||||
$result .= int_to_dword(0); // RLE COMPRESSION (4b)
|
||||
$result .= int_to_dword(0); // width x height (4b)
|
||||
$result .= int_to_dword(0); // biXPelsPerMeter (4b)
|
||||
$result .= int_to_dword(0); // biYPelsPerMeter (4b)
|
||||
$result .= int_to_dword(0); // Number of palettes used (4b)
|
||||
$result .= int_to_dword(0); // Number of important colour (4b)
|
||||
|
||||
// is faster than chr()
|
||||
$arrChr = array();
|
||||
for ($i=0; $i<256; $i++){
|
||||
$arrChr[$i] = chr($i);
|
||||
}
|
||||
|
||||
// creates image data
|
||||
$bgfillcolor = array('red'=>0, 'green'=>0, 'blue'=>0);
|
||||
|
||||
// bottom to top - left to right - attention blue green red !!!
|
||||
$y=$height-1;
|
||||
for ($y2=0; $y2<$height; $y2++) {
|
||||
for ($x=0; $x<$widthFloor; ) {
|
||||
$rgb = imagecolorsforindex($img, imagecolorat($img, $x++, $y));
|
||||
$result .= $arrChr[$rgb['blue']].$arrChr[$rgb['green']].$arrChr[$rgb['red']];
|
||||
$rgb = imagecolorsforindex($img, imagecolorat($img, $x++, $y));
|
||||
$result .= $arrChr[$rgb['blue']].$arrChr[$rgb['green']].$arrChr[$rgb['red']];
|
||||
$rgb = imagecolorsforindex($img, imagecolorat($img, $x++, $y));
|
||||
$result .= $arrChr[$rgb['blue']].$arrChr[$rgb['green']].$arrChr[$rgb['red']];
|
||||
$rgb = imagecolorsforindex($img, imagecolorat($img, $x++, $y));
|
||||
$result .= $arrChr[$rgb['blue']].$arrChr[$rgb['green']].$arrChr[$rgb['red']];
|
||||
$rgb = imagecolorsforindex($img, imagecolorat($img, $x++, $y));
|
||||
$result .= $arrChr[$rgb['blue']].$arrChr[$rgb['green']].$arrChr[$rgb['red']];
|
||||
$rgb = imagecolorsforindex($img, imagecolorat($img, $x++, $y));
|
||||
$result .= $arrChr[$rgb['blue']].$arrChr[$rgb['green']].$arrChr[$rgb['red']];
|
||||
$rgb = imagecolorsforindex($img, imagecolorat($img, $x++, $y));
|
||||
$result .= $arrChr[$rgb['blue']].$arrChr[$rgb['green']].$arrChr[$rgb['red']];
|
||||
$rgb = imagecolorsforindex($img, imagecolorat($img, $x++, $y));
|
||||
$result .= $arrChr[$rgb['blue']].$arrChr[$rgb['green']].$arrChr[$rgb['red']];
|
||||
}
|
||||
for ($x=$widthFloor; $x<$widthCeil; $x++) {
|
||||
$rgb = ($x<$widthOrig) ? imagecolorsforindex($img, imagecolorat($img, $x, $y)) : $bgfillcolor;
|
||||
$result .= $arrChr[$rgb['blue']].$arrChr[$rgb['green']].$arrChr[$rgb['red']];
|
||||
}
|
||||
$y--;
|
||||
}
|
||||
|
||||
// see imagegif
|
||||
if ($filename == '') {
|
||||
echo $result;
|
||||
} else {
|
||||
$file = fopen($filename, 'wb');
|
||||
fwrite($file, $result);
|
||||
fclose($file);
|
||||
public function to($src) {
|
||||
imagewebp($this->image, $src);
|
||||
}
|
||||
}
|
||||
// imagebmp helpers
|
||||
function int_to_dword($n) {
|
||||
return chr($n & 255).chr(($n >> 8) & 255).chr(($n >> 16) & 255).chr(($n >> 24) & 255);
|
||||
}
|
||||
function int_to_word($n) {
|
||||
return chr($n & 255).chr(($n >> 8) & 255);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Autoloads Twig classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Autoloader
|
||||
{
|
||||
/**
|
||||
* Registers Twig_Autoloader as an SPL autoloader.
|
||||
*
|
||||
* @param Boolean $prepend Whether to prepend the autoloader or not.
|
||||
*/
|
||||
public static function register($prepend = false)
|
||||
{
|
||||
if (version_compare(phpversion(), '5.3.0', '>=')) {
|
||||
spl_autoload_register(array(new self, 'autoload'), true, $prepend);
|
||||
} else {
|
||||
spl_autoload_register(array(new self, 'autoload'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles autoloading of classes.
|
||||
*
|
||||
* @param string $class A class name.
|
||||
*/
|
||||
public static function autoload($class)
|
||||
{
|
||||
if (0 !== strpos($class, 'Twig')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_file($file = dirname(__FILE__).'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php')) {
|
||||
require $file;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,268 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compiles a node to PHP code.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Compiler implements Twig_CompilerInterface
|
||||
{
|
||||
protected $lastLine;
|
||||
protected $source;
|
||||
protected $indentation;
|
||||
protected $env;
|
||||
protected $debugInfo;
|
||||
protected $sourceOffset;
|
||||
protected $sourceLine;
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Twig_Environment $env The twig environment instance
|
||||
*/
|
||||
public function __construct(Twig_Environment $env)
|
||||
{
|
||||
$this->env = $env;
|
||||
$this->debugInfo = array();
|
||||
}
|
||||
|
||||
public function getFilename()
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the environment instance related to this compiler.
|
||||
*
|
||||
* @return Twig_Environment The environment instance
|
||||
*/
|
||||
public function getEnvironment()
|
||||
{
|
||||
return $this->env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current PHP code after compilation.
|
||||
*
|
||||
* @return string The PHP code
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a node.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to compile
|
||||
* @param integer $indentation The current indentation
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function compile(Twig_NodeInterface $node, $indentation = 0)
|
||||
{
|
||||
$this->lastLine = null;
|
||||
$this->source = '';
|
||||
$this->sourceOffset = 0;
|
||||
// source code starts at 1 (as we then increment it when we encounter new lines)
|
||||
$this->sourceLine = 1;
|
||||
$this->indentation = $indentation;
|
||||
|
||||
if ($node instanceof Twig_Node_Module) {
|
||||
$this->filename = $node->getAttribute('filename');
|
||||
}
|
||||
|
||||
$node->compile($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function subcompile(Twig_NodeInterface $node, $raw = true)
|
||||
{
|
||||
if (false === $raw) {
|
||||
$this->addIndentation();
|
||||
}
|
||||
|
||||
$node->compile($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a raw string to the compiled code.
|
||||
*
|
||||
* @param string $string The string
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function raw($string)
|
||||
{
|
||||
$this->source .= $string;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to the compiled code by adding indentation.
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function write()
|
||||
{
|
||||
$strings = func_get_args();
|
||||
foreach ($strings as $string) {
|
||||
$this->addIndentation();
|
||||
$this->source .= $string;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an indentation to the current PHP code after compilation.
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function addIndentation()
|
||||
{
|
||||
$this->source .= str_repeat(' ', $this->indentation * 4);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a quoted string to the compiled code.
|
||||
*
|
||||
* @param string $value The string
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function string($value)
|
||||
{
|
||||
$this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\"));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a PHP representation of a given value.
|
||||
*
|
||||
* @param mixed $value The value to convert
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function repr($value)
|
||||
{
|
||||
if (is_int($value) || is_float($value)) {
|
||||
if (false !== $locale = setlocale(LC_NUMERIC, 0)) {
|
||||
setlocale(LC_NUMERIC, 'C');
|
||||
}
|
||||
|
||||
$this->raw($value);
|
||||
|
||||
if (false !== $locale) {
|
||||
setlocale(LC_NUMERIC, $locale);
|
||||
}
|
||||
} elseif (null === $value) {
|
||||
$this->raw('null');
|
||||
} elseif (is_bool($value)) {
|
||||
$this->raw($value ? 'true' : 'false');
|
||||
} elseif (is_array($value)) {
|
||||
$this->raw('array(');
|
||||
$first = true;
|
||||
foreach ($value as $key => $value) {
|
||||
if (!$first) {
|
||||
$this->raw(', ');
|
||||
}
|
||||
$first = false;
|
||||
$this->repr($key);
|
||||
$this->raw(' => ');
|
||||
$this->repr($value);
|
||||
}
|
||||
$this->raw(')');
|
||||
} else {
|
||||
$this->string($value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds debugging information.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The related twig node
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function addDebugInfo(Twig_NodeInterface $node)
|
||||
{
|
||||
if ($node->getLine() != $this->lastLine) {
|
||||
$this->write("// line {$node->getLine()}\n");
|
||||
|
||||
// when mbstring.func_overload is set to 2
|
||||
// mb_substr_count() replaces substr_count()
|
||||
// but they have different signatures!
|
||||
if (((int) ini_get('mbstring.func_overload')) & 2) {
|
||||
// this is much slower than the "right" version
|
||||
$this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n");
|
||||
} else {
|
||||
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
|
||||
}
|
||||
$this->sourceOffset = strlen($this->source);
|
||||
$this->debugInfo[$this->sourceLine] = $node->getLine();
|
||||
|
||||
$this->lastLine = $node->getLine();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDebugInfo()
|
||||
{
|
||||
return $this->debugInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indents the generated code.
|
||||
*
|
||||
* @param integer $step The number of indentation to add
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function indent($step = 1)
|
||||
{
|
||||
$this->indentation += $step;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outdents the generated code.
|
||||
*
|
||||
* @param integer $step The number of indentation to remove
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function outdent($step = 1)
|
||||
{
|
||||
// can't outdent by more steps than the current indentation level
|
||||
if ($this->indentation < $step) {
|
||||
throw new LogicException('Unable to call outdent() as the indentation would become negative');
|
||||
}
|
||||
|
||||
$this->indentation -= $step;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface implemented by compiler classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
interface Twig_CompilerInterface
|
||||
{
|
||||
/**
|
||||
* Compiles a node.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to compile
|
||||
*
|
||||
* @return Twig_CompilerInterface The current compiler instance
|
||||
*/
|
||||
public function compile(Twig_NodeInterface $node);
|
||||
|
||||
/**
|
||||
* Gets the current PHP code after compilation.
|
||||
*
|
||||
* @return string The PHP code
|
||||
*/
|
||||
public function getSource();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,243 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Twig base exception.
|
||||
*
|
||||
* This exception class and its children must only be used when
|
||||
* an error occurs during the loading of a template, when a syntax error
|
||||
* is detected in a template, or when rendering a template. Other
|
||||
* errors must use regular PHP exception classes (like when the template
|
||||
* cache directory is not writable for instance).
|
||||
*
|
||||
* To help debugging template issues, this class tracks the original template
|
||||
* name and line where the error occurred.
|
||||
*
|
||||
* Whenever possible, you must set these information (original template name
|
||||
* and line number) yourself by passing them to the constructor. If some or all
|
||||
* these information are not available from where you throw the exception, then
|
||||
* this class will guess them automatically (when the line number is set to -1
|
||||
* and/or the filename is set to null). As this is a costly operation, this
|
||||
* can be disabled by passing false for both the filename and the line number
|
||||
* when creating a new instance of this class.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Error extends Exception
|
||||
{
|
||||
protected $lineno;
|
||||
protected $filename;
|
||||
protected $rawMessage;
|
||||
protected $previous;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Set both the line number and the filename to false to
|
||||
* disable automatic guessing of the original template name
|
||||
* and line number.
|
||||
*
|
||||
* Set the line number to -1 to enable its automatic guessing.
|
||||
* Set the filename to null to enable its automatic guessing.
|
||||
*
|
||||
* By default, automatic guessing is enabled.
|
||||
*
|
||||
* @param string $message The error message
|
||||
* @param integer $lineno The template line where the error occurred
|
||||
* @param string $filename The template file name where the error occurred
|
||||
* @param Exception $previous The previous exception
|
||||
*/
|
||||
public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
|
||||
$this->previous = $previous;
|
||||
parent::__construct('');
|
||||
} else {
|
||||
parent::__construct('', 0, $previous);
|
||||
}
|
||||
|
||||
$this->lineno = $lineno;
|
||||
$this->filename = $filename;
|
||||
|
||||
if (-1 === $this->lineno || null === $this->filename) {
|
||||
$this->guessTemplateInfo();
|
||||
}
|
||||
|
||||
$this->rawMessage = $message;
|
||||
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw message.
|
||||
*
|
||||
* @return string The raw message
|
||||
*/
|
||||
public function getRawMessage()
|
||||
{
|
||||
return $this->rawMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the filename where the error occurred.
|
||||
*
|
||||
* @return string The filename
|
||||
*/
|
||||
public function getTemplateFile()
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filename where the error occurred.
|
||||
*
|
||||
* @param string $filename The filename
|
||||
*/
|
||||
public function setTemplateFile($filename)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the template line where the error occurred.
|
||||
*
|
||||
* @return integer The template line
|
||||
*/
|
||||
public function getTemplateLine()
|
||||
{
|
||||
return $this->lineno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the template line where the error occurred.
|
||||
*
|
||||
* @param integer $lineno The template line
|
||||
*/
|
||||
public function setTemplateLine($lineno)
|
||||
{
|
||||
$this->lineno = $lineno;
|
||||
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
public function guess()
|
||||
{
|
||||
$this->guessTemplateInfo();
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
/**
|
||||
* For PHP < 5.3.0, provides access to the getPrevious() method.
|
||||
*
|
||||
* @param string $method The method name
|
||||
* @param array $arguments The parameters to be passed to the method
|
||||
*
|
||||
* @return Exception The previous exception or null
|
||||
*
|
||||
* @throws BadMethodCallException
|
||||
*/
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
if ('getprevious' == strtolower($method)) {
|
||||
return $this->previous;
|
||||
}
|
||||
|
||||
throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method));
|
||||
}
|
||||
|
||||
protected function updateRepr()
|
||||
{
|
||||
$this->message = $this->rawMessage;
|
||||
|
||||
$dot = false;
|
||||
if ('.' === substr($this->message, -1)) {
|
||||
$this->message = substr($this->message, 0, -1);
|
||||
$dot = true;
|
||||
}
|
||||
|
||||
if ($this->filename) {
|
||||
if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) {
|
||||
$filename = sprintf('"%s"', $this->filename);
|
||||
} else {
|
||||
$filename = json_encode($this->filename);
|
||||
}
|
||||
$this->message .= sprintf(' in %s', $filename);
|
||||
}
|
||||
|
||||
if ($this->lineno && $this->lineno >= 0) {
|
||||
$this->message .= sprintf(' at line %d', $this->lineno);
|
||||
}
|
||||
|
||||
if ($dot) {
|
||||
$this->message .= '.';
|
||||
}
|
||||
}
|
||||
|
||||
protected function guessTemplateInfo()
|
||||
{
|
||||
$template = null;
|
||||
$templateClass = null;
|
||||
|
||||
if (version_compare(phpversion(), '5.3.6', '>=')) {
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
|
||||
} else {
|
||||
$backtrace = debug_backtrace();
|
||||
}
|
||||
|
||||
foreach ($backtrace as $trace) {
|
||||
if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) {
|
||||
$currentClass = get_class($trace['object']);
|
||||
$isEmbedContainer = 0 === strpos($templateClass, $currentClass);
|
||||
if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
|
||||
$template = $trace['object'];
|
||||
$templateClass = get_class($trace['object']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update template filename
|
||||
if (null !== $template && null === $this->filename) {
|
||||
$this->filename = $template->getTemplateName();
|
||||
}
|
||||
|
||||
if (null === $template || $this->lineno > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$r = new ReflectionObject($template);
|
||||
$file = $r->getFileName();
|
||||
|
||||
$exceptions = array($e = $this);
|
||||
while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) {
|
||||
$exceptions[] = $e;
|
||||
}
|
||||
|
||||
while ($e = array_pop($exceptions)) {
|
||||
$traces = $e->getTrace();
|
||||
while ($trace = array_shift($traces)) {
|
||||
if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($template->getDebugInfo() as $codeLine => $templateLine) {
|
||||
if ($codeLine <= $trace['line']) {
|
||||
// update template line
|
||||
$this->lineno = $templateLine;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when an error occurs during template loading.
|
||||
*
|
||||
* Automatic template information guessing is always turned off as
|
||||
* if a template cannot be loaded, there is nothing to guess.
|
||||
* However, when a template is loaded from another one, then, we need
|
||||
* to find the current context and this is automatically done by
|
||||
* Twig_Template::displayWithErrorHandling().
|
||||
*
|
||||
* This strategy makes Twig_Environment::resolveTemplate() much faster.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Error_Loader extends Twig_Error
|
||||
{
|
||||
public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, false, false, $previous);
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when an error occurs at runtime.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Error_Runtime extends Twig_Error
|
||||
{
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when a syntax error occurs during lexing or parsing of a template.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Error_Syntax extends Twig_Error
|
||||
{
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds an exists() method for loaders.
|
||||
*
|
||||
* @author Florin Patan <florinpatan@gmail.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
interface Twig_ExistsLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Check if we have the source code of a template, given its name.
|
||||
*
|
||||
* @param string $name The name of the template to check if we can load
|
||||
*
|
||||
* @return boolean If the template source code is handled by this loader or not
|
||||
*/
|
||||
public function exists($name);
|
||||
}
|
|
@ -1,611 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parses expressions.
|
||||
*
|
||||
* This parser implements a "Precedence climbing" algorithm.
|
||||
*
|
||||
* @see http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
|
||||
* @see http://en.wikipedia.org/wiki/Operator-precedence_parser
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_ExpressionParser
|
||||
{
|
||||
const OPERATOR_LEFT = 1;
|
||||
const OPERATOR_RIGHT = 2;
|
||||
|
||||
protected $parser;
|
||||
protected $unaryOperators;
|
||||
protected $binaryOperators;
|
||||
|
||||
public function __construct(Twig_Parser $parser, array $unaryOperators, array $binaryOperators)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
$this->unaryOperators = $unaryOperators;
|
||||
$this->binaryOperators = $binaryOperators;
|
||||
}
|
||||
|
||||
public function parseExpression($precedence = 0)
|
||||
{
|
||||
$expr = $this->getPrimary();
|
||||
$token = $this->parser->getCurrentToken();
|
||||
while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) {
|
||||
$op = $this->binaryOperators[$token->getValue()];
|
||||
$this->parser->getStream()->next();
|
||||
|
||||
if (isset($op['callable'])) {
|
||||
$expr = call_user_func($op['callable'], $this->parser, $expr);
|
||||
} else {
|
||||
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
|
||||
$class = $op['class'];
|
||||
$expr = new $class($expr, $expr1, $token->getLine());
|
||||
}
|
||||
|
||||
$token = $this->parser->getCurrentToken();
|
||||
}
|
||||
|
||||
if (0 === $precedence) {
|
||||
return $this->parseConditionalExpression($expr);
|
||||
}
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
||||
protected function getPrimary()
|
||||
{
|
||||
$token = $this->parser->getCurrentToken();
|
||||
|
||||
if ($this->isUnary($token)) {
|
||||
$operator = $this->unaryOperators[$token->getValue()];
|
||||
$this->parser->getStream()->next();
|
||||
$expr = $this->parseExpression($operator['precedence']);
|
||||
$class = $operator['class'];
|
||||
|
||||
return $this->parsePostfixExpression(new $class($expr, $token->getLine()));
|
||||
} elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$this->parser->getStream()->next();
|
||||
$expr = $this->parseExpression();
|
||||
$this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed');
|
||||
|
||||
return $this->parsePostfixExpression($expr);
|
||||
}
|
||||
|
||||
return $this->parsePrimaryExpression();
|
||||
}
|
||||
|
||||
protected function parseConditionalExpression($expr)
|
||||
{
|
||||
while ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '?')) {
|
||||
$this->parser->getStream()->next();
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$expr2 = $this->parseExpression();
|
||||
if ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$this->parser->getStream()->next();
|
||||
$expr3 = $this->parseExpression();
|
||||
} else {
|
||||
$expr3 = new Twig_Node_Expression_Constant('', $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
} else {
|
||||
$this->parser->getStream()->next();
|
||||
$expr2 = $expr;
|
||||
$expr3 = $this->parseExpression();
|
||||
}
|
||||
|
||||
$expr = new Twig_Node_Expression_Conditional($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
||||
protected function isUnary(Twig_Token $token)
|
||||
{
|
||||
return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]);
|
||||
}
|
||||
|
||||
protected function isBinary(Twig_Token $token)
|
||||
{
|
||||
return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]);
|
||||
}
|
||||
|
||||
public function parsePrimaryExpression()
|
||||
{
|
||||
$token = $this->parser->getCurrentToken();
|
||||
switch ($token->getType()) {
|
||||
case Twig_Token::NAME_TYPE:
|
||||
$this->parser->getStream()->next();
|
||||
switch ($token->getValue()) {
|
||||
case 'true':
|
||||
case 'TRUE':
|
||||
$node = new Twig_Node_Expression_Constant(true, $token->getLine());
|
||||
break;
|
||||
|
||||
case 'false':
|
||||
case 'FALSE':
|
||||
$node = new Twig_Node_Expression_Constant(false, $token->getLine());
|
||||
break;
|
||||
|
||||
case 'none':
|
||||
case 'NONE':
|
||||
case 'null':
|
||||
case 'NULL':
|
||||
$node = new Twig_Node_Expression_Constant(null, $token->getLine());
|
||||
break;
|
||||
|
||||
default:
|
||||
if ('(' === $this->parser->getCurrentToken()->getValue()) {
|
||||
$node = $this->getFunctionNode($token->getValue(), $token->getLine());
|
||||
} else {
|
||||
$node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Twig_Token::NUMBER_TYPE:
|
||||
$this->parser->getStream()->next();
|
||||
$node = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
break;
|
||||
|
||||
case Twig_Token::STRING_TYPE:
|
||||
case Twig_Token::INTERPOLATION_START_TYPE:
|
||||
$node = $this->parseStringExpression();
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($token->test(Twig_Token::PUNCTUATION_TYPE, '[')) {
|
||||
$node = $this->parseArrayExpression();
|
||||
} elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) {
|
||||
$node = $this->parseHashExpression();
|
||||
} else {
|
||||
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($token->getType(), $token->getLine()), $token->getValue()), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->parsePostfixExpression($node);
|
||||
}
|
||||
|
||||
public function parseStringExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$nodes = array();
|
||||
// a string cannot be followed by another string in a single expression
|
||||
$nextCanBeString = true;
|
||||
while (true) {
|
||||
if ($stream->test(Twig_Token::STRING_TYPE) && $nextCanBeString) {
|
||||
$token = $stream->next();
|
||||
$nodes[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
$nextCanBeString = false;
|
||||
} elseif ($stream->test(Twig_Token::INTERPOLATION_START_TYPE)) {
|
||||
$stream->next();
|
||||
$nodes[] = $this->parseExpression();
|
||||
$stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
|
||||
$nextCanBeString = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$expr = array_shift($nodes);
|
||||
foreach ($nodes as $node) {
|
||||
$expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getLine());
|
||||
}
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
||||
public function parseArrayExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '[', 'An array element was expected');
|
||||
|
||||
$node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine());
|
||||
$first = true;
|
||||
while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
|
||||
if (!$first) {
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma');
|
||||
|
||||
// trailing ,?
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$first = false;
|
||||
|
||||
$node->addElement($this->parseExpression());
|
||||
}
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed');
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parseHashExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '{', 'A hash element was expected');
|
||||
|
||||
$node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine());
|
||||
$first = true;
|
||||
while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) {
|
||||
if (!$first) {
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma');
|
||||
|
||||
// trailing ,?
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$first = false;
|
||||
|
||||
// a hash key can be:
|
||||
//
|
||||
// * a number -- 12
|
||||
// * a string -- 'a'
|
||||
// * a name, which is equivalent to a string -- a
|
||||
// * an expression, which must be enclosed in parentheses -- (1 + 2)
|
||||
if ($stream->test(Twig_Token::STRING_TYPE) || $stream->test(Twig_Token::NAME_TYPE) || $stream->test(Twig_Token::NUMBER_TYPE)) {
|
||||
$token = $stream->next();
|
||||
$key = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
} elseif ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$key = $this->parseExpression();
|
||||
} else {
|
||||
$current = $stream->getCurrent();
|
||||
|
||||
throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($current->getType(), $current->getLine()), $current->getValue()), $current->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
|
||||
$value = $this->parseExpression();
|
||||
|
||||
$node->addElement($value, $key);
|
||||
}
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed');
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parsePostfixExpression($node)
|
||||
{
|
||||
while (true) {
|
||||
$token = $this->parser->getCurrentToken();
|
||||
if ($token->getType() == Twig_Token::PUNCTUATION_TYPE) {
|
||||
if ('.' == $token->getValue() || '[' == $token->getValue()) {
|
||||
$node = $this->parseSubscriptExpression($node);
|
||||
} elseif ('|' == $token->getValue()) {
|
||||
$node = $this->parseFilterExpression($node);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function getFunctionNode($name, $line)
|
||||
{
|
||||
switch ($name) {
|
||||
case 'parent':
|
||||
$args = $this->parseArguments();
|
||||
if (!count($this->parser->getBlockStack())) {
|
||||
throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line, $this->parser->getFilename());
|
||||
}
|
||||
|
||||
if (!$this->parser->getParent() && !$this->parser->hasTraits()) {
|
||||
throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden', $line, $this->parser->getFilename());
|
||||
}
|
||||
|
||||
return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line);
|
||||
case 'block':
|
||||
return new Twig_Node_Expression_BlockReference($this->parseArguments()->getNode(0), false, $line);
|
||||
case 'attribute':
|
||||
$args = $this->parseArguments();
|
||||
if (count($args) < 2) {
|
||||
throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes)', $line, $this->parser->getFilename());
|
||||
}
|
||||
|
||||
return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : new Twig_Node_Expression_Array(array(), $line), Twig_Template::ANY_CALL, $line);
|
||||
default:
|
||||
$args = $this->parseArguments(true);
|
||||
if (null !== $alias = $this->parser->getImportedSymbol('macro', $name)) {
|
||||
return new Twig_Node_Expression_MacroCall($alias['node'], $alias['name'], $this->createArrayFromArguments($args), $line);
|
||||
}
|
||||
|
||||
try {
|
||||
$class = $this->getFunctionNodeClass($name, $line);
|
||||
} catch (Twig_Error_Syntax $e) {
|
||||
if (!$this->parser->hasMacro($name)) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return new Twig_Node_Expression_MacroCall(new Twig_Node_Expression_Name('_self', $line), $name, $this->createArrayFromArguments($args), $line);
|
||||
}
|
||||
|
||||
return new $class($name, $args, $line);
|
||||
}
|
||||
}
|
||||
|
||||
public function parseSubscriptExpression($node)
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$token = $stream->next();
|
||||
$lineno = $token->getLine();
|
||||
$arguments = new Twig_Node_Expression_Array(array(), $lineno);
|
||||
$type = Twig_Template::ANY_CALL;
|
||||
if ($token->getValue() == '.') {
|
||||
$token = $stream->next();
|
||||
if (
|
||||
$token->getType() == Twig_Token::NAME_TYPE
|
||||
||
|
||||
$token->getType() == Twig_Token::NUMBER_TYPE
|
||||
||
|
||||
($token->getType() == Twig_Token::OPERATOR_TYPE && preg_match(Twig_Lexer::REGEX_NAME, $token->getValue()))
|
||||
) {
|
||||
$arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
|
||||
} else {
|
||||
throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename());
|
||||
}
|
||||
|
||||
if ($node instanceof Twig_Node_Expression_Name && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
|
||||
if (!$arg instanceof Twig_Node_Expression_Constant) {
|
||||
throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s")', $node->getAttribute('name')), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
|
||||
$arguments = $this->createArrayFromArguments($this->parseArguments(true));
|
||||
|
||||
return new Twig_Node_Expression_MacroCall($node, $arg->getAttribute('value'), $arguments, $lineno);
|
||||
}
|
||||
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$type = Twig_Template::METHOD_CALL;
|
||||
$arguments = $this->createArrayFromArguments($this->parseArguments());
|
||||
}
|
||||
} else {
|
||||
$type = Twig_Template::ARRAY_CALL;
|
||||
|
||||
// slice?
|
||||
$slice = false;
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$slice = true;
|
||||
$arg = new Twig_Node_Expression_Constant(0, $token->getLine());
|
||||
} else {
|
||||
$arg = $this->parseExpression();
|
||||
}
|
||||
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$slice = true;
|
||||
$stream->next();
|
||||
}
|
||||
|
||||
if ($slice) {
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
|
||||
$length = new Twig_Node_Expression_Constant(null, $token->getLine());
|
||||
} else {
|
||||
$length = $this->parseExpression();
|
||||
}
|
||||
|
||||
$class = $this->getFilterNodeClass('slice', $token->getLine());
|
||||
$arguments = new Twig_Node(array($arg, $length));
|
||||
$filter = new $class($node, new Twig_Node_Expression_Constant('slice', $token->getLine()), $arguments, $token->getLine());
|
||||
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
|
||||
}
|
||||
|
||||
return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $type, $lineno);
|
||||
}
|
||||
|
||||
public function parseFilterExpression($node)
|
||||
{
|
||||
$this->parser->getStream()->next();
|
||||
|
||||
return $this->parseFilterExpressionRaw($node);
|
||||
}
|
||||
|
||||
public function parseFilterExpressionRaw($node, $tag = null)
|
||||
{
|
||||
while (true) {
|
||||
$token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE);
|
||||
|
||||
$name = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$arguments = new Twig_Node();
|
||||
} else {
|
||||
$arguments = $this->parseArguments(true);
|
||||
}
|
||||
|
||||
$class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine());
|
||||
|
||||
$node = new $class($node, $name, $arguments, $token->getLine(), $tag);
|
||||
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '|')) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->parser->getStream()->next();
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses arguments.
|
||||
*
|
||||
* @param Boolean $namedArguments Whether to allow named arguments or not
|
||||
* @param Boolean $definition Whether we are parsing arguments for a function definition
|
||||
*
|
||||
* @return Twig_Node
|
||||
*/
|
||||
public function parseArguments($namedArguments = false, $definition = false)
|
||||
{
|
||||
$args = array();
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
|
||||
while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ')')) {
|
||||
if (!empty($args)) {
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
|
||||
}
|
||||
|
||||
if ($definition) {
|
||||
$token = $stream->expect(Twig_Token::NAME_TYPE, null, 'An argument must be a name');
|
||||
$value = new Twig_Node_Expression_Name($token->getValue(), $this->parser->getCurrentToken()->getLine());
|
||||
} else {
|
||||
$value = $this->parseExpression();
|
||||
}
|
||||
|
||||
$name = null;
|
||||
if ($namedArguments && $stream->test(Twig_Token::OPERATOR_TYPE, '=')) {
|
||||
$token = $stream->next();
|
||||
if (!$value instanceof Twig_Node_Expression_Name) {
|
||||
throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given', get_class($value)), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
$name = $value->getAttribute('name');
|
||||
|
||||
if ($definition) {
|
||||
$value = $this->parsePrimaryExpression();
|
||||
|
||||
if (!$this->checkConstantExpression($value)) {
|
||||
throw new Twig_Error_Syntax('A default value for an argument must be a constant (a boolean, a string, a number, or an array).', $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
} else {
|
||||
$value = $this->parseExpression();
|
||||
}
|
||||
}
|
||||
|
||||
if ($definition && null === $name) {
|
||||
$name = $value->getAttribute('name');
|
||||
$value = new Twig_Node_Expression_Constant(null, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
if (null === $name) {
|
||||
$args[] = $value;
|
||||
} else {
|
||||
if ($definition && isset($args[$name])) {
|
||||
throw new Twig_Error_Syntax(sprintf('Arguments cannot contain the same argument name more than once ("%s" is defined twice).', $name), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
|
||||
$args[$name] = $value;
|
||||
}
|
||||
}
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
|
||||
|
||||
return new Twig_Node($args);
|
||||
}
|
||||
|
||||
public function parseAssignmentExpression()
|
||||
{
|
||||
$targets = array();
|
||||
while (true) {
|
||||
$token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to');
|
||||
if (in_array($token->getValue(), array('true', 'false', 'none'))) {
|
||||
throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s"', $token->getValue()), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
$targets[] = new Twig_Node_Expression_AssignName($token->getValue(), $token->getLine());
|
||||
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
$this->parser->getStream()->next();
|
||||
}
|
||||
|
||||
return new Twig_Node($targets);
|
||||
}
|
||||
|
||||
public function parseMultitargetExpression()
|
||||
{
|
||||
$targets = array();
|
||||
while (true) {
|
||||
$targets[] = $this->parseExpression();
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
$this->parser->getStream()->next();
|
||||
}
|
||||
|
||||
return new Twig_Node($targets);
|
||||
}
|
||||
|
||||
protected function getFunctionNodeClass($name, $line)
|
||||
{
|
||||
$env = $this->parser->getEnvironment();
|
||||
|
||||
if (false === $function = $env->getFunction($name)) {
|
||||
$message = sprintf('The function "%s" does not exist', $name);
|
||||
if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFunctions()))) {
|
||||
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
|
||||
}
|
||||
|
||||
throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename());
|
||||
}
|
||||
|
||||
if ($function instanceof Twig_SimpleFunction) {
|
||||
return $function->getNodeClass();
|
||||
}
|
||||
|
||||
return $function instanceof Twig_Function_Node ? $function->getClass() : 'Twig_Node_Expression_Function';
|
||||
}
|
||||
|
||||
protected function getFilterNodeClass($name, $line)
|
||||
{
|
||||
$env = $this->parser->getEnvironment();
|
||||
|
||||
if (false === $filter = $env->getFilter($name)) {
|
||||
$message = sprintf('The filter "%s" does not exist', $name);
|
||||
if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFilters()))) {
|
||||
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
|
||||
}
|
||||
|
||||
throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename());
|
||||
}
|
||||
|
||||
if ($filter instanceof Twig_SimpleFilter) {
|
||||
return $filter->getNodeClass();
|
||||
}
|
||||
|
||||
return $filter instanceof Twig_Filter_Node ? $filter->getClass() : 'Twig_Node_Expression_Filter';
|
||||
}
|
||||
|
||||
// checks that the node only contains "constant" elements
|
||||
protected function checkConstantExpression(Twig_NodeInterface $node)
|
||||
{
|
||||
if (!($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($node as $n) {
|
||||
if (!$this->checkConstantExpression($n)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function createArrayFromArguments(Twig_Node $arguments, $line = null)
|
||||
{
|
||||
$line = null === $line ? $arguments->getLine() : $line;
|
||||
$array = new Twig_Node_Expression_Array(array(), $line);
|
||||
foreach ($arguments as $key => $value) {
|
||||
$array->addElement($value, new Twig_Node_Expression_Constant($key, $value->getLine()));
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
abstract class Twig_Extension implements Twig_ExtensionInterface
|
||||
{
|
||||
/**
|
||||
* Initializes the runtime environment.
|
||||
*
|
||||
* This is where you can load some file that contains filter functions for instance.
|
||||
*
|
||||
* @param Twig_Environment $environment The current Twig_Environment instance
|
||||
*/
|
||||
public function initRuntime(Twig_Environment $environment)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of tests to add to the existing list.
|
||||
*
|
||||
* @return array An array of tests
|
||||
*/
|
||||
public function getTests()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of functions to add to the existing list.
|
||||
*
|
||||
* @return array An array of functions
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of operators to add to the existing list.
|
||||
*
|
||||
* @return array An array of operators
|
||||
*/
|
||||
public function getOperators()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of global variables to add to the existing list.
|
||||
*
|
||||
* @return array An array of global variables
|
||||
*/
|
||||
public function getGlobals()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,71 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2011 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Extension_Debug extends Twig_Extension
|
||||
{
|
||||
/**
|
||||
* Returns a list of global functions to add to the existing list.
|
||||
*
|
||||
* @return array An array of global functions
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
// dump is safe if var_dump is overridden by xdebug
|
||||
$isDumpOutputHtmlSafe = extension_loaded('xdebug')
|
||||
// false means that it was not set (and the default is on) or it explicitly enabled
|
||||
&& (false === ini_get('xdebug.overload_var_dump') || ini_get('xdebug.overload_var_dump'))
|
||||
// false means that it was not set (and the default is on) or it explicitly enabled
|
||||
// xdebug.overload_var_dump produces HTML only when html_errors is also enabled
|
||||
&& (false === ini_get('html_errors') || ini_get('html_errors'))
|
||||
|| 'cli' === php_sapi_name()
|
||||
;
|
||||
|
||||
return array(
|
||||
new Twig_SimpleFunction('dump', 'twig_var_dump', array('is_safe' => $isDumpOutputHtmlSafe ? array('html') : array(), 'needs_context' => true, 'needs_environment' => true)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'debug';
|
||||
}
|
||||
}
|
||||
|
||||
function twig_var_dump(Twig_Environment $env, $context)
|
||||
{
|
||||
if (!$env->isDebug()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
$count = func_num_args();
|
||||
if (2 === $count) {
|
||||
$vars = array();
|
||||
foreach ($context as $key => $value) {
|
||||
if (!$value instanceof Twig_Template) {
|
||||
$vars[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
var_dump($vars);
|
||||
} else {
|
||||
for ($i = 2; $i < $count; $i++) {
|
||||
var_dump(func_get_arg($i));
|
||||
}
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Extension_Escaper extends Twig_Extension
|
||||
{
|
||||
protected $defaultStrategy;
|
||||
|
||||
public function __construct($defaultStrategy = 'html')
|
||||
{
|
||||
$this->setDefaultStrategy($defaultStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array(new Twig_TokenParser_AutoEscape());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return array(new Twig_NodeVisitor_Escaper());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return array(
|
||||
new Twig_SimpleFilter('raw', 'twig_raw_filter', array('is_safe' => array('all'))),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default strategy to use when not defined by the user.
|
||||
*
|
||||
* The strategy can be a valid PHP callback that takes the template
|
||||
* "filename" as an argument and returns the strategy to use.
|
||||
*
|
||||
* @param mixed $defaultStrategy An escaping strategy
|
||||
*/
|
||||
public function setDefaultStrategy($defaultStrategy)
|
||||
{
|
||||
// for BC
|
||||
if (true === $defaultStrategy) {
|
||||
$defaultStrategy = 'html';
|
||||
}
|
||||
|
||||
$this->defaultStrategy = $defaultStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default strategy to use when not defined by the user.
|
||||
*
|
||||
* @param string $filename The template "filename"
|
||||
*
|
||||
* @return string The default strategy to use for the template
|
||||
*/
|
||||
public function getDefaultStrategy($filename)
|
||||
{
|
||||
// disable string callables to avoid calling a function named html or js,
|
||||
// or any other upcoming escaping strategy
|
||||
if (!is_string($this->defaultStrategy) && is_callable($this->defaultStrategy)) {
|
||||
return call_user_func($this->defaultStrategy, $filename);
|
||||
}
|
||||
|
||||
return $this->defaultStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'escaper';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a variable as being safe.
|
||||
*
|
||||
* @param string $string A PHP variable
|
||||
*/
|
||||
function twig_raw_filter($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Extension_Optimizer extends Twig_Extension
|
||||
{
|
||||
protected $optimizers;
|
||||
|
||||
public function __construct($optimizers = -1)
|
||||
{
|
||||
$this->optimizers = $optimizers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return array(new Twig_NodeVisitor_Optimizer($this->optimizers));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'optimizer';
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Extension_Sandbox extends Twig_Extension
|
||||
{
|
||||
protected $sandboxedGlobally;
|
||||
protected $sandboxed;
|
||||
protected $policy;
|
||||
|
||||
public function __construct(Twig_Sandbox_SecurityPolicyInterface $policy, $sandboxed = false)
|
||||
{
|
||||
$this->policy = $policy;
|
||||
$this->sandboxedGlobally = $sandboxed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array(new Twig_TokenParser_Sandbox());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return array(new Twig_NodeVisitor_Sandbox());
|
||||
}
|
||||
|
||||
public function enableSandbox()
|
||||
{
|
||||
$this->sandboxed = true;
|
||||
}
|
||||
|
||||
public function disableSandbox()
|
||||
{
|
||||
$this->sandboxed = false;
|
||||
}
|
||||
|
||||
public function isSandboxed()
|
||||
{
|
||||
return $this->sandboxedGlobally || $this->sandboxed;
|
||||
}
|
||||
|
||||
public function isSandboxedGlobally()
|
||||
{
|
||||
return $this->sandboxedGlobally;
|
||||
}
|
||||
|
||||
public function setSecurityPolicy(Twig_Sandbox_SecurityPolicyInterface $policy)
|
||||
{
|
||||
$this->policy = $policy;
|
||||
}
|
||||
|
||||
public function getSecurityPolicy()
|
||||
{
|
||||
return $this->policy;
|
||||
}
|
||||
|
||||
public function checkSecurity($tags, $filters, $functions)
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
$this->policy->checkSecurity($tags, $filters, $functions);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkMethodAllowed($obj, $method)
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
$this->policy->checkMethodAllowed($obj, $method);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkPropertyAllowed($obj, $method)
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
$this->policy->checkPropertyAllowed($obj, $method);
|
||||
}
|
||||
}
|
||||
|
||||
public function ensureToStringAllowed($obj)
|
||||
{
|
||||
if (is_object($obj)) {
|
||||
$this->policy->checkMethodAllowed($obj, '__toString');
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'sandbox';
|
||||
}
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2012 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Internal class.
|
||||
*
|
||||
* This class is used by Twig_Environment as a staging area and must not be used directly.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Extension_Staging extends Twig_Extension
|
||||
{
|
||||
protected $functions = array();
|
||||
protected $filters = array();
|
||||
protected $visitors = array();
|
||||
protected $tokenParsers = array();
|
||||
protected $globals = array();
|
||||
protected $tests = array();
|
||||
|
||||
public function addFunction($name, $function)
|
||||
{
|
||||
$this->functions[$name] = $function;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return $this->functions;
|
||||
}
|
||||
|
||||
public function addFilter($name, $filter)
|
||||
{
|
||||
$this->filters[$name] = $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
|
||||
{
|
||||
$this->visitors[] = $visitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return $this->visitors;
|
||||
}
|
||||
|
||||
public function addTokenParser(Twig_TokenParserInterface $parser)
|
||||
{
|
||||
$this->tokenParsers[] = $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return $this->tokenParsers;
|
||||
}
|
||||
|
||||
public function addGlobal($name, $value)
|
||||
{
|
||||
$this->globals[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getGlobals()
|
||||
{
|
||||
return $this->globals;
|
||||
}
|
||||
|
||||
public function addTest($name, $test)
|
||||
{
|
||||
$this->tests[$name] = $test;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTests()
|
||||
{
|
||||
return $this->tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'staging';
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2012 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Extension_StringLoader extends Twig_Extension
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return array(
|
||||
new Twig_SimpleFunction('template_from_string', 'twig_template_from_string', array('needs_environment' => true)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'string_loader';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a template from a string.
|
||||
*
|
||||
* <pre>
|
||||
* {{ include(template_from_string("Hello {{ name }}")) }}
|
||||
* </pre>
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param string $template A template as a string
|
||||
*
|
||||
* @return Twig_Template A Twig_Template instance
|
||||
*/
|
||||
function twig_template_from_string(Twig_Environment $env, $template)
|
||||
{
|
||||
$name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), true), false));
|
||||
|
||||
$loader = new Twig_Loader_Chain(array(
|
||||
new Twig_Loader_Array(array($name => $template)),
|
||||
$current = $env->getLoader(),
|
||||
));
|
||||
|
||||
$env->setLoader($loader);
|
||||
try {
|
||||
$template = $env->loadTemplate($name);
|
||||
} catch (Exception $e) {
|
||||
$env->setLoader($current);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
$env->setLoader($current);
|
||||
|
||||
return $template;
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface implemented by extension classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface Twig_ExtensionInterface
|
||||
{
|
||||
/**
|
||||
* Initializes the runtime environment.
|
||||
*
|
||||
* This is where you can load some file that contains filter functions for instance.
|
||||
*
|
||||
* @param Twig_Environment $environment The current Twig_Environment instance
|
||||
*/
|
||||
public function initRuntime(Twig_Environment $environment);
|
||||
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
|
||||
*/
|
||||
public function getTokenParsers();
|
||||
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors();
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getFilters();
|
||||
|
||||
/**
|
||||
* Returns a list of tests to add to the existing list.
|
||||
*
|
||||
* @return array An array of tests
|
||||
*/
|
||||
public function getTests();
|
||||
|
||||
/**
|
||||
* Returns a list of functions to add to the existing list.
|
||||
*
|
||||
* @return array An array of functions
|
||||
*/
|
||||
public function getFunctions();
|
||||
|
||||
/**
|
||||
* Returns a list of operators to add to the existing list.
|
||||
*
|
||||
* @return array An array of operators
|
||||
*/
|
||||
public function getOperators();
|
||||
|
||||
/**
|
||||
* Returns a list of global variables to add to the existing list.
|
||||
*
|
||||
* @return array An array of global variables
|
||||
*/
|
||||
public function getGlobals();
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName();
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Extensions_Extension_I18n extends Twig_Extension
|
||||
{
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array(new Twig_Extensions_TokenParser_Trans());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return array(
|
||||
new Twig_SimpleFilter('trans', 'gettext'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'i18n';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
<?php
|
||||
|
||||
class Twig_Extensions_Extension_Tinyboard extends Twig_Extension
|
||||
{
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return array(
|
||||
new Twig_SimpleFilter('filesize', 'format_bytes'),
|
||||
new Twig_SimpleFilter('truncate', 'twig_truncate_filter'),
|
||||
new Twig_SimpleFilter('truncate_body', 'truncate'),
|
||||
new Twig_SimpleFilter('truncate_filename', 'twig_filename_truncate_filter'),
|
||||
new Twig_SimpleFilter('extension', 'twig_extension_filter'),
|
||||
new Twig_SimpleFilter('sprintf', 'sprintf'),
|
||||
new Twig_SimpleFilter('capcode', 'capcode'),
|
||||
new Twig_SimpleFilter('hasPermission', 'twig_hasPermission_filter'),
|
||||
new Twig_SimpleFilter('date', 'twig_date_filter'),
|
||||
new Twig_SimpleFilter('poster_id', 'poster_id'),
|
||||
new Twig_SimpleFilter('remove_whitespace', 'twig_remove_whitespace_filter'),
|
||||
new Twig_SimpleFilter('count', 'count'),
|
||||
new Twig_SimpleFilter('ago', 'ago'),
|
||||
new Twig_SimpleFilter('until', 'until'),
|
||||
new Twig_SimpleFilter('push', 'twig_push_filter'),
|
||||
new Twig_SimpleFilter('bidi_cleanup', 'bidi_cleanup'),
|
||||
new Twig_SimpleFilter('addslashes', 'addslashes'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of functions to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return array(
|
||||
new Twig_SimpleFunction('time', 'time'),
|
||||
new Twig_SimpleFunction('floor', 'floor'),
|
||||
new Twig_SimpleFunction('timezone', 'twig_timezone_function'),
|
||||
new Twig_SimpleFunction('hiddenInputs', 'hiddenInputs'),
|
||||
new Twig_SimpleFunction('hiddenInputsHash', 'hiddenInputsHash'),
|
||||
new Twig_SimpleFunction('ratio', 'twig_ratio_function'),
|
||||
new Twig_SimpleFunction('secure_link_confirm', 'twig_secure_link_confirm'),
|
||||
new Twig_SimpleFunction('secure_link', 'twig_secure_link'),
|
||||
new Twig_SimpleFunction('link_for', 'link_for')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'tinyboard';
|
||||
}
|
||||
}
|
||||
|
||||
function twig_timezone_function() {
|
||||
return 'Z';
|
||||
}
|
||||
|
||||
function twig_push_filter($array, $value) {
|
||||
array_push($array, $value);
|
||||
return $array;
|
||||
}
|
||||
|
||||
function twig_remove_whitespace_filter($data) {
|
||||
return preg_replace('/[\t\r\n]/', '', $data);
|
||||
}
|
||||
|
||||
function twig_date_filter($date, $format) {
|
||||
return gmstrftime($format, $date);
|
||||
}
|
||||
|
||||
function twig_hasPermission_filter($mod, $permission, $board = null) {
|
||||
return hasPermission($permission, $board, $mod);
|
||||
}
|
||||
|
||||
function twig_extension_filter($value, $case_insensitive = true) {
|
||||
$ext = mb_substr($value, mb_strrpos($value, '.') + 1);
|
||||
if($case_insensitive)
|
||||
$ext = mb_strtolower($ext);
|
||||
return $ext;
|
||||
}
|
||||
|
||||
function twig_sprintf_filter( $value, $var) {
|
||||
return sprintf($value, $var);
|
||||
}
|
||||
|
||||
function twig_truncate_filter($value, $length = 30, $preserve = false, $separator = '…') {
|
||||
if (mb_strlen($value) > $length) {
|
||||
if ($preserve) {
|
||||
if (false !== ($breakpoint = mb_strpos($value, ' ', $length))) {
|
||||
$length = $breakpoint;
|
||||
}
|
||||
}
|
||||
return mb_substr($value, 0, $length) . $separator;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
function twig_filename_truncate_filter($value, $length = 30, $separator = '…') {
|
||||
if (mb_strlen($value) > $length) {
|
||||
$value = strrev($value);
|
||||
$array = array_reverse(explode(".", $value, 2));
|
||||
$array = array_map("strrev", $array);
|
||||
|
||||
$filename = &$array[0];
|
||||
$extension = isset($array[1]) ? $array[1] : false;
|
||||
|
||||
$filename = mb_substr($filename, 0, $length - ($extension ? mb_strlen($extension) + 1 : 0)) . $separator;
|
||||
|
||||
return implode(".", $array);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
function twig_ratio_function($w, $h) {
|
||||
return fraction($w, $h, ':');
|
||||
}
|
||||
function twig_secure_link_confirm($text, $title, $confirm_message, $href) {
|
||||
global $config;
|
||||
|
||||
return '<a onclick="if (event.which==2) return true;if (confirm(\'' . htmlentities(addslashes($confirm_message)) . '\')) document.location=\'?/' . htmlspecialchars(addslashes($href . '/' . make_secure_link_token($href))) . '\';return false;" title="' . htmlentities($title) . '" href="?/' . $href . '">' . $text . '</a>';
|
||||
}
|
||||
function twig_secure_link($href) {
|
||||
return $href . '/' . make_secure_link_token($href);
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a trans node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Extensions_Node_Trans extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $body, Twig_NodeInterface $plural = null, Twig_Node_Expression $count = null, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('count' => $count, 'body' => $body, 'plural' => $plural), array(), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
list($msg, $vars) = $this->compileString($this->getNode('body'));
|
||||
|
||||
if (null !== $this->getNode('plural')) {
|
||||
list($msg1, $vars1) = $this->compileString($this->getNode('plural'));
|
||||
|
||||
$vars = array_merge($vars, $vars1);
|
||||
}
|
||||
|
||||
$function = null === $this->getNode('plural') ? 'gettext' : 'ngettext';
|
||||
|
||||
if ($vars) {
|
||||
$compiler
|
||||
->write('echo strtr('.$function.'(')
|
||||
->subcompile($msg)
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('plural')) {
|
||||
$compiler
|
||||
->raw(', ')
|
||||
->subcompile($msg1)
|
||||
->raw(', abs(')
|
||||
->subcompile($this->getNode('count'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->raw('), array(');
|
||||
|
||||
foreach ($vars as $var) {
|
||||
if ('count' === $var->getAttribute('name')) {
|
||||
$compiler
|
||||
->string('%count%')
|
||||
->raw(' => abs(')
|
||||
->subcompile($this->getNode('count'))
|
||||
->raw('), ')
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->string('%'.$var->getAttribute('name').'%')
|
||||
->raw(' => ')
|
||||
->subcompile($var)
|
||||
->raw(', ')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
$compiler->raw("));\n");
|
||||
} else {
|
||||
$compiler
|
||||
->write('echo '.$function.'(')
|
||||
->subcompile($msg)
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('plural')) {
|
||||
$compiler
|
||||
->raw(', ')
|
||||
->subcompile($msg1)
|
||||
->raw(', abs(')
|
||||
->subcompile($this->getNode('count'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->raw(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected function compileString(Twig_NodeInterface $body)
|
||||
{
|
||||
if ($body instanceof Twig_Node_Expression_Name || $body instanceof Twig_Node_Expression_Constant || $body instanceof Twig_Node_Expression_TempName) {
|
||||
return array($body, array());
|
||||
}
|
||||
|
||||
$vars = array();
|
||||
if (count($body)) {
|
||||
$msg = '';
|
||||
|
||||
foreach ($body as $node) {
|
||||
if (get_class($node) === 'Twig_Node' && $node->getNode(0) instanceof Twig_Node_SetTemp) {
|
||||
$node = $node->getNode(1);
|
||||
}
|
||||
|
||||
if ($node instanceof Twig_Node_Print) {
|
||||
$n = $node->getNode('expr');
|
||||
while ($n instanceof Twig_Node_Expression_Filter) {
|
||||
$n = $n->getNode('node');
|
||||
}
|
||||
$msg .= sprintf('%%%s%%', $n->getAttribute('name'));
|
||||
$vars[] = new Twig_Node_Expression_Name($n->getAttribute('name'), $n->getLine());
|
||||
} else {
|
||||
$msg .= $node->getAttribute('data');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$msg = $body->getAttribute('data');
|
||||
}
|
||||
|
||||
return array(new Twig_Node(array(new Twig_Node_Expression_Constant(trim($msg), $body->getLine()))), $vars);
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Extensions_TokenParser_Trans extends Twig_TokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param Twig_Token $token A Twig_Token instance
|
||||
*
|
||||
* @return Twig_NodeInterface A Twig_NodeInterface instance
|
||||
*/
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
$count = null;
|
||||
$plural = null;
|
||||
|
||||
if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) {
|
||||
$body = $this->parser->getExpressionParser()->parseExpression();
|
||||
} else {
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
$body = $this->parser->subparse(array($this, 'decideForFork'));
|
||||
if ('plural' === $stream->next()->getValue()) {
|
||||
$count = $this->parser->getExpressionParser()->parseExpression();
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
$plural = $this->parser->subparse(array($this, 'decideForEnd'), true);
|
||||
}
|
||||
}
|
||||
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
|
||||
$this->checkTransString($body, $lineno);
|
||||
|
||||
return new Twig_Extensions_Node_Trans($body, $plural, $count, $lineno, $this->getTag());
|
||||
}
|
||||
|
||||
public function decideForFork(Twig_Token $token)
|
||||
{
|
||||
return $token->test(array('plural', 'endtrans'));
|
||||
}
|
||||
|
||||
public function decideForEnd(Twig_Token $token)
|
||||
{
|
||||
return $token->test('endtrans');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @param string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'trans';
|
||||
}
|
||||
|
||||
protected function checkTransString(Twig_NodeInterface $body, $lineno)
|
||||
{
|
||||
foreach ($body as $i => $node) {
|
||||
if (
|
||||
$node instanceof Twig_Node_Text
|
||||
||
|
||||
($node instanceof Twig_Node_Print && $node->getNode('expr') instanceof Twig_Node_Expression_Name)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Twig_Error_Syntax(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $lineno);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template filter.
|
||||
*
|
||||
* Use Twig_SimpleFilter instead.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
abstract class Twig_Filter implements Twig_FilterInterface, Twig_FilterCallableInterface
|
||||
{
|
||||
protected $options;
|
||||
protected $arguments = array();
|
||||
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->options = array_merge(array(
|
||||
'needs_environment' => false,
|
||||
'needs_context' => false,
|
||||
'pre_escape' => null,
|
||||
'preserves_safety' => null,
|
||||
'callable' => null,
|
||||
), $options);
|
||||
}
|
||||
|
||||
public function setArguments($arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
public function getArguments()
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
public function needsEnvironment()
|
||||
{
|
||||
return $this->options['needs_environment'];
|
||||
}
|
||||
|
||||
public function needsContext()
|
||||
{
|
||||
return $this->options['needs_context'];
|
||||
}
|
||||
|
||||
public function getSafe(Twig_Node $filterArgs)
|
||||
{
|
||||
if (isset($this->options['is_safe'])) {
|
||||
return $this->options['is_safe'];
|
||||
}
|
||||
|
||||
if (isset($this->options['is_safe_callback'])) {
|
||||
return call_user_func($this->options['is_safe_callback'], $filterArgs);
|
||||
}
|
||||
}
|
||||
|
||||
public function getPreservesSafety()
|
||||
{
|
||||
return $this->options['preserves_safety'];
|
||||
}
|
||||
|
||||
public function getPreEscape()
|
||||
{
|
||||
return $this->options['pre_escape'];
|
||||
}
|
||||
|
||||
public function getCallable()
|
||||
{
|
||||
return $this->options['callable'];
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a function template filter.
|
||||
*
|
||||
* Use Twig_SimpleFilter instead.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
class Twig_Filter_Function extends Twig_Filter
|
||||
{
|
||||
protected $function;
|
||||
|
||||
public function __construct($function, array $options = array())
|
||||
{
|
||||
$options['callable'] = $function;
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->function = $function;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
return $this->function;
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a method template filter.
|
||||
*
|
||||
* Use Twig_SimpleFilter instead.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
class Twig_Filter_Method extends Twig_Filter
|
||||
{
|
||||
protected $extension;
|
||||
protected $method;
|
||||
|
||||
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
|
||||
{
|
||||
$options['callable'] = array($extension, $method);
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->extension = $extension;
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2011 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template filter as a node.
|
||||
*
|
||||
* Use Twig_SimpleFilter instead.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
class Twig_Filter_Node extends Twig_Filter
|
||||
{
|
||||
protected $class;
|
||||
|
||||
public function __construct($class, array $options = array())
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
public function getClass()
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2012 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a callable template filter.
|
||||
*
|
||||
* Use Twig_SimpleFilter instead.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
interface Twig_FilterCallableInterface
|
||||
{
|
||||
public function getCallable();
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template filter.
|
||||
*
|
||||
* Use Twig_SimpleFilter instead.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
interface Twig_FilterInterface
|
||||
{
|
||||
/**
|
||||
* Compiles a filter.
|
||||
*
|
||||
* @return string The PHP code for the filter
|
||||
*/
|
||||
public function compile();
|
||||
|
||||
public function needsEnvironment();
|
||||
|
||||
public function needsContext();
|
||||
|
||||
public function getSafe(Twig_Node $filterArgs);
|
||||
|
||||
public function getPreservesSafety();
|
||||
|
||||
public function getPreEscape();
|
||||
|
||||
public function setArguments($arguments);
|
||||
|
||||
public function getArguments();
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template function.
|
||||
*
|
||||
* Use Twig_SimpleFunction instead.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
abstract class Twig_Function implements Twig_FunctionInterface, Twig_FunctionCallableInterface
|
||||
{
|
||||
protected $options;
|
||||
protected $arguments = array();
|
||||
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->options = array_merge(array(
|
||||
'needs_environment' => false,
|
||||
'needs_context' => false,
|
||||
'callable' => null,
|
||||
), $options);
|
||||
}
|
||||
|
||||
public function setArguments($arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
public function getArguments()
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
public function needsEnvironment()
|
||||
{
|
||||
return $this->options['needs_environment'];
|
||||
}
|
||||
|
||||
public function needsContext()
|
||||
{
|
||||
return $this->options['needs_context'];
|
||||
}
|
||||
|
||||
public function getSafe(Twig_Node $functionArgs)
|
||||
{
|
||||
if (isset($this->options['is_safe'])) {
|
||||
return $this->options['is_safe'];
|
||||
}
|
||||
|
||||
if (isset($this->options['is_safe_callback'])) {
|
||||
return call_user_func($this->options['is_safe_callback'], $functionArgs);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getCallable()
|
||||
{
|
||||
return $this->options['callable'];
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2010 Arnaud Le Blanc
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a function template function.
|
||||
*
|
||||
* Use Twig_SimpleFunction instead.
|
||||
*
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
class Twig_Function_Function extends Twig_Function
|
||||
{
|
||||
protected $function;
|
||||
|
||||
public function __construct($function, array $options = array())
|
||||
{
|
||||
$options['callable'] = $function;
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->function = $function;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
return $this->function;
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2010 Arnaud Le Blanc
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a method template function.
|
||||
*
|
||||
* Use Twig_SimpleFunction instead.
|
||||
*
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
class Twig_Function_Method extends Twig_Function
|
||||
{
|
||||
protected $extension;
|
||||
protected $method;
|
||||
|
||||
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
|
||||
{
|
||||
$options['callable'] = array($extension, $method);
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->extension = $extension;
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2011 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template function as a node.
|
||||
*
|
||||
* Use Twig_SimpleFunction instead.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
class Twig_Function_Node extends Twig_Function
|
||||
{
|
||||
protected $class;
|
||||
|
||||
public function __construct($class, array $options = array())
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
public function getClass()
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2012 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a callable template function.
|
||||
*
|
||||
* Use Twig_SimpleFunction instead.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
interface Twig_FunctionCallableInterface
|
||||
{
|
||||
public function getCallable();
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
* (c) 2010 Arnaud Le Blanc
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template function.
|
||||
*
|
||||
* Use Twig_SimpleFunction instead.
|
||||
*
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
interface Twig_FunctionInterface
|
||||
{
|
||||
/**
|
||||
* Compiles a function.
|
||||
*
|
||||
* @return string The PHP code for the function
|
||||
*/
|
||||
public function compile();
|
||||
|
||||
public function needsEnvironment();
|
||||
|
||||
public function needsContext();
|
||||
|
||||
public function getSafe(Twig_Node $filterArgs);
|
||||
|
||||
public function setArguments($arguments);
|
||||
|
||||
public function getArguments();
|
||||
}
|
|
@ -1,408 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Lexes a template string.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Lexer implements Twig_LexerInterface
|
||||
{
|
||||
protected $tokens;
|
||||
protected $code;
|
||||
protected $cursor;
|
||||
protected $lineno;
|
||||
protected $end;
|
||||
protected $state;
|
||||
protected $states;
|
||||
protected $brackets;
|
||||
protected $env;
|
||||
protected $filename;
|
||||
protected $options;
|
||||
protected $regexes;
|
||||
protected $position;
|
||||
protected $positions;
|
||||
protected $currentVarBlockLine;
|
||||
|
||||
const STATE_DATA = 0;
|
||||
const STATE_BLOCK = 1;
|
||||
const STATE_VAR = 2;
|
||||
const STATE_STRING = 3;
|
||||
const STATE_INTERPOLATION = 4;
|
||||
|
||||
const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
|
||||
const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A';
|
||||
const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
|
||||
const REGEX_DQ_STRING_DELIM = '/"/A';
|
||||
const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
|
||||
const PUNCTUATION = '()[]{}?:.,|';
|
||||
|
||||
public function __construct(Twig_Environment $env, array $options = array())
|
||||
{
|
||||
$this->env = $env;
|
||||
|
||||
$this->options = array_merge(array(
|
||||
'tag_comment' => array('{#', '#}'),
|
||||
'tag_block' => array('{%', '%}'),
|
||||
'tag_variable' => array('{{', '}}'),
|
||||
'whitespace_trim' => '-',
|
||||
'interpolation' => array('#{', '}'),
|
||||
), $options);
|
||||
|
||||
$this->regexes = array(
|
||||
'lex_var' => '/\s*'.preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A',
|
||||
'lex_block' => '/\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')\n?/A',
|
||||
'lex_raw_data' => '/('.preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim'], '/').'|'.preg_quote($this->options['tag_block'][0], '/').')\s*(?:end%s)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/s',
|
||||
'operator' => $this->getOperatorRegex(),
|
||||
'lex_comment' => '/(?:'.preg_quote($this->options['whitespace_trim'], '/').preg_quote($this->options['tag_comment'][1], '/').'\s*|'.preg_quote($this->options['tag_comment'][1], '/').')\n?/s',
|
||||
'lex_block_raw' => '/\s*(raw|verbatim)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/As',
|
||||
'lex_block_line' => '/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As',
|
||||
'lex_tokens_start' => '/('.preg_quote($this->options['tag_variable'][0], '/').'|'.preg_quote($this->options['tag_block'][0], '/').'|'.preg_quote($this->options['tag_comment'][0], '/').')('.preg_quote($this->options['whitespace_trim'], '/').')?/s',
|
||||
'interpolation_start' => '/'.preg_quote($this->options['interpolation'][0], '/').'\s*/A',
|
||||
'interpolation_end' => '/\s*'.preg_quote($this->options['interpolation'][1], '/').'/A',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes a source code.
|
||||
*
|
||||
* @param string $code The source code
|
||||
* @param string $filename A unique identifier for the source code
|
||||
*
|
||||
* @return Twig_TokenStream A token stream instance
|
||||
*/
|
||||
public function tokenize($code, $filename = null)
|
||||
{
|
||||
if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
|
||||
$mbEncoding = mb_internal_encoding();
|
||||
mb_internal_encoding('ASCII');
|
||||
}
|
||||
|
||||
$this->code = str_replace(array("\r\n", "\r"), "\n", $code);
|
||||
$this->filename = $filename;
|
||||
$this->cursor = 0;
|
||||
$this->lineno = 1;
|
||||
$this->end = strlen($this->code);
|
||||
$this->tokens = array();
|
||||
$this->state = self::STATE_DATA;
|
||||
$this->states = array();
|
||||
$this->brackets = array();
|
||||
$this->position = -1;
|
||||
|
||||
// find all token starts in one go
|
||||
preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE);
|
||||
$this->positions = $matches;
|
||||
|
||||
while ($this->cursor < $this->end) {
|
||||
// dispatch to the lexing functions depending
|
||||
// on the current state
|
||||
switch ($this->state) {
|
||||
case self::STATE_DATA:
|
||||
$this->lexData();
|
||||
break;
|
||||
|
||||
case self::STATE_BLOCK:
|
||||
$this->lexBlock();
|
||||
break;
|
||||
|
||||
case self::STATE_VAR:
|
||||
$this->lexVar();
|
||||
break;
|
||||
|
||||
case self::STATE_STRING:
|
||||
$this->lexString();
|
||||
break;
|
||||
|
||||
case self::STATE_INTERPOLATION:
|
||||
$this->lexInterpolation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->pushToken(Twig_Token::EOF_TYPE);
|
||||
|
||||
if (!empty($this->brackets)) {
|
||||
list($expect, $lineno) = array_pop($this->brackets);
|
||||
throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
|
||||
}
|
||||
|
||||
if (isset($mbEncoding)) {
|
||||
mb_internal_encoding($mbEncoding);
|
||||
}
|
||||
|
||||
return new Twig_TokenStream($this->tokens, $this->filename);
|
||||
}
|
||||
|
||||
protected function lexData()
|
||||
{
|
||||
// if no matches are left we return the rest of the template as simple text token
|
||||
if ($this->position == count($this->positions[0]) - 1) {
|
||||
$this->pushToken(Twig_Token::TEXT_TYPE, substr($this->code, $this->cursor));
|
||||
$this->cursor = $this->end;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the first token after the current cursor
|
||||
$position = $this->positions[0][++$this->position];
|
||||
while ($position[1] < $this->cursor) {
|
||||
if ($this->position == count($this->positions[0]) - 1) {
|
||||
return;
|
||||
}
|
||||
$position = $this->positions[0][++$this->position];
|
||||
}
|
||||
|
||||
// push the template text first
|
||||
$text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor);
|
||||
if (isset($this->positions[2][$this->position][0])) {
|
||||
$text = rtrim($text);
|
||||
}
|
||||
$this->pushToken(Twig_Token::TEXT_TYPE, $text);
|
||||
$this->moveCursor($textContent.$position[0]);
|
||||
|
||||
switch ($this->positions[1][$this->position][0]) {
|
||||
case $this->options['tag_comment'][0]:
|
||||
$this->lexComment();
|
||||
break;
|
||||
|
||||
case $this->options['tag_block'][0]:
|
||||
// raw data?
|
||||
if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->lexRawData($match[1]);
|
||||
// {% line \d+ %}
|
||||
} elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->lineno = (int) $match[1];
|
||||
} else {
|
||||
$this->pushToken(Twig_Token::BLOCK_START_TYPE);
|
||||
$this->pushState(self::STATE_BLOCK);
|
||||
$this->currentVarBlockLine = $this->lineno;
|
||||
}
|
||||
break;
|
||||
|
||||
case $this->options['tag_variable'][0]:
|
||||
$this->pushToken(Twig_Token::VAR_START_TYPE);
|
||||
$this->pushState(self::STATE_VAR);
|
||||
$this->currentVarBlockLine = $this->lineno;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexBlock()
|
||||
{
|
||||
if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->pushToken(Twig_Token::BLOCK_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexVar()
|
||||
{
|
||||
if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->pushToken(Twig_Token::VAR_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexExpression()
|
||||
{
|
||||
// whitespace
|
||||
if (preg_match('/\s+/A', $this->code, $match, null, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
|
||||
if ($this->cursor >= $this->end) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->currentVarBlockLine, $this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
// operators
|
||||
if (preg_match($this->regexes['operator'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->pushToken(Twig_Token::OPERATOR_TYPE, $match[0]);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// names
|
||||
elseif (preg_match(self::REGEX_NAME, $this->code, $match, null, $this->cursor)) {
|
||||
$this->pushToken(Twig_Token::NAME_TYPE, $match[0]);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// numbers
|
||||
elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, null, $this->cursor)) {
|
||||
$number = (float) $match[0]; // floats
|
||||
if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) {
|
||||
$number = (int) $match[0]; // integers lower than the maximum
|
||||
}
|
||||
$this->pushToken(Twig_Token::NUMBER_TYPE, $number);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// punctuation
|
||||
elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) {
|
||||
// opening bracket
|
||||
if (false !== strpos('([{', $this->code[$this->cursor])) {
|
||||
$this->brackets[] = array($this->code[$this->cursor], $this->lineno);
|
||||
}
|
||||
// closing bracket
|
||||
elseif (false !== strpos(')]}', $this->code[$this->cursor])) {
|
||||
if (empty($this->brackets)) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unexpected "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename);
|
||||
}
|
||||
|
||||
list($expect, $lineno) = array_pop($this->brackets);
|
||||
if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
$this->pushToken(Twig_Token::PUNCTUATION_TYPE, $this->code[$this->cursor]);
|
||||
++$this->cursor;
|
||||
}
|
||||
// strings
|
||||
elseif (preg_match(self::REGEX_STRING, $this->code, $match, null, $this->cursor)) {
|
||||
$this->pushToken(Twig_Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)));
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// opening double quoted string
|
||||
elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
|
||||
$this->brackets[] = array('"', $this->lineno);
|
||||
$this->pushState(self::STATE_STRING);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// unlexable
|
||||
else {
|
||||
throw new Twig_Error_Syntax(sprintf('Unexpected character "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexRawData($tag)
|
||||
{
|
||||
if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s" block', $tag), $this->lineno, $this->filename);
|
||||
}
|
||||
|
||||
$text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
|
||||
$this->moveCursor($text.$match[0][0]);
|
||||
|
||||
if (false !== strpos($match[1][0], $this->options['whitespace_trim'])) {
|
||||
$text = rtrim($text);
|
||||
}
|
||||
|
||||
$this->pushToken(Twig_Token::TEXT_TYPE, $text);
|
||||
}
|
||||
|
||||
protected function lexComment()
|
||||
{
|
||||
if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
|
||||
throw new Twig_Error_Syntax('Unclosed comment', $this->lineno, $this->filename);
|
||||
}
|
||||
|
||||
$this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
|
||||
}
|
||||
|
||||
protected function lexString()
|
||||
{
|
||||
if (preg_match($this->regexes['interpolation_start'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->brackets[] = array($this->options['interpolation'][0], $this->lineno);
|
||||
$this->pushToken(Twig_Token::INTERPOLATION_START_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->pushState(self::STATE_INTERPOLATION);
|
||||
|
||||
} elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, null, $this->cursor) && strlen($match[0]) > 0) {
|
||||
$this->pushToken(Twig_Token::STRING_TYPE, stripcslashes($match[0]));
|
||||
$this->moveCursor($match[0]);
|
||||
|
||||
} elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
|
||||
|
||||
list($expect, $lineno) = array_pop($this->brackets);
|
||||
if ($this->code[$this->cursor] != '"') {
|
||||
throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
|
||||
}
|
||||
|
||||
$this->popState();
|
||||
++$this->cursor;
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexInterpolation()
|
||||
{
|
||||
$bracket = end($this->brackets);
|
||||
if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, null, $this->cursor)) {
|
||||
array_pop($this->brackets);
|
||||
$this->pushToken(Twig_Token::INTERPOLATION_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
}
|
||||
|
||||
protected function pushToken($type, $value = '')
|
||||
{
|
||||
// do not push empty text tokens
|
||||
if (Twig_Token::TEXT_TYPE === $type && '' === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->tokens[] = new Twig_Token($type, $value, $this->lineno);
|
||||
}
|
||||
|
||||
protected function moveCursor($text)
|
||||
{
|
||||
$this->cursor += strlen($text);
|
||||
$this->lineno += substr_count($text, "\n");
|
||||
}
|
||||
|
||||
protected function getOperatorRegex()
|
||||
{
|
||||
$operators = array_merge(
|
||||
array('='),
|
||||
array_keys($this->env->getUnaryOperators()),
|
||||
array_keys($this->env->getBinaryOperators())
|
||||
);
|
||||
|
||||
$operators = array_combine($operators, array_map('strlen', $operators));
|
||||
arsort($operators);
|
||||
|
||||
$regex = array();
|
||||
foreach ($operators as $operator => $length) {
|
||||
// an operator that ends with a character must be followed by
|
||||
// a whitespace or a parenthesis
|
||||
if (ctype_alpha($operator[$length - 1])) {
|
||||
$regex[] = preg_quote($operator, '/').'(?=[\s()])';
|
||||
} else {
|
||||
$regex[] = preg_quote($operator, '/');
|
||||
}
|
||||
}
|
||||
|
||||
return '/'.implode('|', $regex).'/A';
|
||||
}
|
||||
|
||||
protected function pushState($state)
|
||||
{
|
||||
$this->states[] = $this->state;
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
protected function popState()
|
||||
{
|
||||
if (0 === count($this->states)) {
|
||||
throw new Exception('Cannot pop state without a previous state');
|
||||
}
|
||||
|
||||
$this->state = array_pop($this->states);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface implemented by lexer classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
interface Twig_LexerInterface
|
||||
{
|
||||
/**
|
||||
* Tokenizes a source code.
|
||||
*
|
||||
* @param string $code The source code
|
||||
* @param string $filename A unique identifier for the source code
|
||||
*
|
||||
* @return Twig_TokenStream A token stream instance
|
||||
*/
|
||||
public function tokenize($code, $filename = null);
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Loads a template from an array.
|
||||
*
|
||||
* When using this loader with a cache mechanism, you should know that a new cache
|
||||
* key is generated each time a template content "changes" (the cache key being the
|
||||
* source code of the template). If you don't want to see your cache grows out of
|
||||
* control, you need to take care of clearing the old cache file by yourself.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
|
||||
{
|
||||
protected $templates = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $templates An array of templates (keys are the names, and values are the source code)
|
||||
*
|
||||
* @see Twig_Loader
|
||||
*/
|
||||
public function __construct(array $templates)
|
||||
{
|
||||
$this->templates = $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or overrides a template.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param string $template The template source
|
||||
*/
|
||||
public function setTemplate($name, $template)
|
||||
{
|
||||
$this->templates[(string) $name] = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSource($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return $this->templates[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exists($name)
|
||||
{
|
||||
return isset($this->templates[(string) $name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheKey($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return $this->templates[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFresh($name, $time)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2011 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Loads templates from other loaders.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
|
||||
{
|
||||
private $hasSourceCache = array();
|
||||
protected $loaders = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Twig_LoaderInterface[] $loaders An array of loader instances
|
||||
*/
|
||||
public function __construct(array $loaders = array())
|
||||
{
|
||||
foreach ($loaders as $loader) {
|
||||
$this->addLoader($loader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a loader instance.
|
||||
*
|
||||
* @param Twig_LoaderInterface $loader A Loader instance
|
||||
*/
|
||||
public function addLoader(Twig_LoaderInterface $loader)
|
||||
{
|
||||
$this->loaders[] = $loader;
|
||||
$this->hasSourceCache = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSource($name)
|
||||
{
|
||||
$exceptions = array();
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return $loader->getSource($name);
|
||||
} catch (Twig_Error_Loader $e) {
|
||||
$exceptions[] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(', ', $exceptions)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exists($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
|
||||
if (isset($this->hasSourceCache[$name])) {
|
||||
return $this->hasSourceCache[$name];
|
||||
}
|
||||
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof Twig_ExistsLoaderInterface) {
|
||||
if ($loader->exists($name)) {
|
||||
return $this->hasSourceCache[$name] = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$loader->getSource($name);
|
||||
|
||||
return $this->hasSourceCache[$name] = true;
|
||||
} catch (Twig_Error_Loader $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return $this->hasSourceCache[$name] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheKey($name)
|
||||
{
|
||||
$exceptions = array();
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return $loader->getCacheKey($name);
|
||||
} catch (Twig_Error_Loader $e) {
|
||||
$exceptions[] = get_class($loader).': '.$e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFresh($name, $time)
|
||||
{
|
||||
$exceptions = array();
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return $loader->isFresh($name, $time);
|
||||
} catch (Twig_Error_Loader $e) {
|
||||
$exceptions[] = get_class($loader).': '.$e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions)));
|
||||
}
|
||||
}
|
|
@ -1,226 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Loads template from the filesystem.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
|
||||
{
|
||||
/** Identifier of the main namespace. */
|
||||
const MAIN_NAMESPACE = '__main__';
|
||||
|
||||
protected $paths = array();
|
||||
protected $cache = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string|array $paths A path or an array of paths where to look for templates
|
||||
*/
|
||||
public function __construct($paths = array())
|
||||
{
|
||||
if ($paths) {
|
||||
$this->setPaths($paths);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths to the templates.
|
||||
*
|
||||
* @param string $namespace A path namespace
|
||||
*
|
||||
* @return array The array of paths where to look for templates
|
||||
*/
|
||||
public function getPaths($namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path namespaces.
|
||||
*
|
||||
* The main namespace is always defined.
|
||||
*
|
||||
* @return array The array of defined namespaces
|
||||
*/
|
||||
public function getNamespaces()
|
||||
{
|
||||
return array_keys($this->paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the paths where templates are stored.
|
||||
*
|
||||
* @param string|array $paths A path or an array of paths where to look for templates
|
||||
* @param string $namespace A path namespace
|
||||
*/
|
||||
public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
if (!is_array($paths)) {
|
||||
$paths = array($paths);
|
||||
}
|
||||
|
||||
$this->paths[$namespace] = array();
|
||||
foreach ($paths as $path) {
|
||||
$this->addPath($path, $namespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a path where templates are stored.
|
||||
*
|
||||
* @param string $path A path where to look for templates
|
||||
* @param string $namespace A path name
|
||||
*
|
||||
* @throws Twig_Error_Loader
|
||||
*/
|
||||
public function addPath($path, $namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
// invalidate the cache
|
||||
$this->cache = array();
|
||||
|
||||
if (!is_dir($path)) {
|
||||
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
|
||||
}
|
||||
|
||||
$this->paths[$namespace][] = rtrim($path, '/\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends a path where templates are stored.
|
||||
*
|
||||
* @param string $path A path where to look for templates
|
||||
* @param string $namespace A path name
|
||||
*
|
||||
* @throws Twig_Error_Loader
|
||||
*/
|
||||
public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
// invalidate the cache
|
||||
$this->cache = array();
|
||||
|
||||
if (!is_dir($path)) {
|
||||
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
|
||||
}
|
||||
|
||||
$path = rtrim($path, '/\\');
|
||||
|
||||
if (!isset($this->paths[$namespace])) {
|
||||
$this->paths[$namespace][] = $path;
|
||||
} else {
|
||||
array_unshift($this->paths[$namespace], $path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSource($name)
|
||||
{
|
||||
return file_get_contents($this->findTemplate($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheKey($name)
|
||||
{
|
||||
return $this->findTemplate($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exists($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (isset($this->cache[$name])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->findTemplate($name);
|
||||
|
||||
return true;
|
||||
} catch (Twig_Error_Loader $exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFresh($name, $time)
|
||||
{
|
||||
return filemtime($this->findTemplate($name)) <= $time;
|
||||
}
|
||||
|
||||
protected function findTemplate($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
|
||||
// normalize name
|
||||
$name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/'));
|
||||
|
||||
if (isset($this->cache[$name])) {
|
||||
return $this->cache[$name];
|
||||
}
|
||||
|
||||
$this->validateName($name);
|
||||
|
||||
$namespace = self::MAIN_NAMESPACE;
|
||||
$shortname = $name;
|
||||
if (isset($name[0]) && '@' == $name[0]) {
|
||||
if (false === $pos = strpos($name, '/')) {
|
||||
throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
|
||||
}
|
||||
|
||||
$namespace = substr($name, 1, $pos - 1);
|
||||
$shortname = substr($name, $pos + 1);
|
||||
}
|
||||
|
||||
if (!isset($this->paths[$namespace])) {
|
||||
throw new Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace));
|
||||
}
|
||||
|
||||
foreach ($this->paths[$namespace] as $path) {
|
||||
if (is_file($path.'/'.$shortname)) {
|
||||
return $this->cache[$name] = $path.'/'.$shortname;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace])));
|
||||
}
|
||||
|
||||
protected function validateName($name)
|
||||
{
|
||||
if (false !== strpos($name, "\0")) {
|
||||
throw new Twig_Error_Loader('A template name cannot contain NUL bytes.');
|
||||
}
|
||||
|
||||
$name = ltrim($name, '/');
|
||||
$parts = explode('/', $name);
|
||||
$level = 0;
|
||||
foreach ($parts as $part) {
|
||||
if ('..' === $part) {
|
||||
--$level;
|
||||
} elseif ('.' !== $part) {
|
||||
++$level;
|
||||
}
|
||||
|
||||
if ($level < 0) {
|
||||
throw new Twig_Error_Loader(sprintf('Looks like you try to load a template outside configured directories (%s).', $name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Loads a template from a string.
|
||||
*
|
||||
* This loader should only be used for unit testing as it has many limitations
|
||||
* (for instance, the include or extends tag does not make any sense for a string
|
||||
* loader).
|
||||
*
|
||||
* When using this loader with a cache mechanism, you should know that a new cache
|
||||
* key is generated each time a template content "changes" (the cache key being the
|
||||
* source code of the template). If you don't want to see your cache grows out of
|
||||
* control, you need to take care of clearing the old cache file by yourself.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSource($name)
|
||||
{
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exists($name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheKey($name)
|
||||
{
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFresh($name, $time)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface all loaders must implement.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface Twig_LoaderInterface
|
||||
{
|
||||
/**
|
||||
* Gets the source code of a template, given its name.
|
||||
*
|
||||
* @param string $name The name of the template to load
|
||||
*
|
||||
* @return string The template source code
|
||||
*
|
||||
* @throws Twig_Error_Loader When $name is not found
|
||||
*/
|
||||
public function getSource($name);
|
||||
|
||||
/**
|
||||
* Gets the cache key to use for the cache for a given template name.
|
||||
*
|
||||
* @param string $name The name of the template to load
|
||||
*
|
||||
* @return string The cache key
|
||||
*
|
||||
* @throws Twig_Error_Loader When $name is not found
|
||||
*/
|
||||
public function getCacheKey($name);
|
||||
|
||||
/**
|
||||
* Returns true if the template is still fresh.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param timestamp $time The last modification time of the cached template
|
||||
*
|
||||
* @return Boolean true if the template is fresh, false otherwise
|
||||
*
|
||||
* @throws Twig_Error_Loader When $name is not found
|
||||
*/
|
||||
public function isFresh($name, $time);
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marks a content as safe.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Markup implements Countable
|
||||
{
|
||||
protected $content;
|
||||
protected $charset;
|
||||
|
||||
public function __construct($content, $charset)
|
||||
{
|
||||
$this->content = (string) $content;
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : strlen($this->content);
|
||||
}
|
||||
}
|
|
@ -1,226 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a node in the AST.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node implements Twig_NodeInterface
|
||||
{
|
||||
protected $nodes;
|
||||
protected $attributes;
|
||||
protected $lineno;
|
||||
protected $tag;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* The nodes are automatically made available as properties ($this->node).
|
||||
* The attributes are automatically made available as array items ($this['name']).
|
||||
*
|
||||
* @param array $nodes An array of named nodes
|
||||
* @param array $attributes An array of attributes (should not be nodes)
|
||||
* @param integer $lineno The line number
|
||||
* @param string $tag The tag name associated with the Node
|
||||
*/
|
||||
public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null)
|
||||
{
|
||||
$this->nodes = $nodes;
|
||||
$this->attributes = $attributes;
|
||||
$this->lineno = $lineno;
|
||||
$this->tag = $tag;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$attributes = array();
|
||||
foreach ($this->attributes as $name => $value) {
|
||||
$attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true)));
|
||||
}
|
||||
|
||||
$repr = array(get_class($this).'('.implode(', ', $attributes));
|
||||
|
||||
if (count($this->nodes)) {
|
||||
foreach ($this->nodes as $name => $node) {
|
||||
$len = strlen($name) + 4;
|
||||
$noderepr = array();
|
||||
foreach (explode("\n", (string) $node) as $line) {
|
||||
$noderepr[] = str_repeat(' ', $len).$line;
|
||||
}
|
||||
|
||||
$repr[] = sprintf(' %s: %s', $name, ltrim(implode("\n", $noderepr)));
|
||||
}
|
||||
|
||||
$repr[] = ')';
|
||||
} else {
|
||||
$repr[0] .= ')';
|
||||
}
|
||||
|
||||
return implode("\n", $repr);
|
||||
}
|
||||
|
||||
public function toXml($asDom = false)
|
||||
{
|
||||
$dom = new DOMDocument('1.0', 'UTF-8');
|
||||
$dom->formatOutput = true;
|
||||
$dom->appendChild($xml = $dom->createElement('twig'));
|
||||
|
||||
$xml->appendChild($node = $dom->createElement('node'));
|
||||
$node->setAttribute('class', get_class($this));
|
||||
|
||||
foreach ($this->attributes as $name => $value) {
|
||||
$node->appendChild($attribute = $dom->createElement('attribute'));
|
||||
$attribute->setAttribute('name', $name);
|
||||
$attribute->appendChild($dom->createTextNode($value));
|
||||
}
|
||||
|
||||
foreach ($this->nodes as $name => $n) {
|
||||
if (null === $n) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$child = $n->toXml(true)->getElementsByTagName('node')->item(0);
|
||||
$child = $dom->importNode($child, true);
|
||||
$child->setAttribute('name', $name);
|
||||
|
||||
$node->appendChild($child);
|
||||
}
|
||||
|
||||
return $asDom ? $dom : $dom->saveXml();
|
||||
}
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
foreach ($this->nodes as $node) {
|
||||
$node->compile($compiler);
|
||||
}
|
||||
}
|
||||
|
||||
public function getLine()
|
||||
{
|
||||
return $this->lineno;
|
||||
}
|
||||
|
||||
public function getNodeTag()
|
||||
{
|
||||
return $this->tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute is defined.
|
||||
*
|
||||
* @param string The attribute name
|
||||
*
|
||||
* @return Boolean true if the attribute is defined, false otherwise
|
||||
*/
|
||||
public function hasAttribute($name)
|
||||
{
|
||||
return array_key_exists($name, $this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an attribute.
|
||||
*
|
||||
* @param string The attribute name
|
||||
*
|
||||
* @return mixed The attribute value
|
||||
*/
|
||||
public function getAttribute($name)
|
||||
{
|
||||
if (!array_key_exists($name, $this->attributes)) {
|
||||
throw new LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, get_class($this)));
|
||||
}
|
||||
|
||||
return $this->attributes[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute.
|
||||
*
|
||||
* @param string The attribute name
|
||||
* @param mixed The attribute value
|
||||
*/
|
||||
public function setAttribute($name, $value)
|
||||
{
|
||||
$this->attributes[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an attribute.
|
||||
*
|
||||
* @param string The attribute name
|
||||
*/
|
||||
public function removeAttribute($name)
|
||||
{
|
||||
unset($this->attributes[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the node with the given identifier exists.
|
||||
*
|
||||
* @param string The node name
|
||||
*
|
||||
* @return Boolean true if the node with the given name exists, false otherwise
|
||||
*/
|
||||
public function hasNode($name)
|
||||
{
|
||||
return array_key_exists($name, $this->nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node by name.
|
||||
*
|
||||
* @param string The node name
|
||||
*
|
||||
* @return Twig_Node A Twig_Node instance
|
||||
*/
|
||||
public function getNode($name)
|
||||
{
|
||||
if (!array_key_exists($name, $this->nodes)) {
|
||||
throw new LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, get_class($this)));
|
||||
}
|
||||
|
||||
return $this->nodes[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a node.
|
||||
*
|
||||
* @param string The node name
|
||||
* @param Twig_Node A Twig_Node instance
|
||||
*/
|
||||
public function setNode($name, $node = null)
|
||||
{
|
||||
$this->nodes[$name] = $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a node by name.
|
||||
*
|
||||
* @param string The node name
|
||||
*/
|
||||
public function removeNode($name)
|
||||
{
|
||||
unset($this->nodes[$name]);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->nodes);
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->nodes);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an autoescape node.
|
||||
*
|
||||
* The value is the escaping strategy (can be html, js, ...)
|
||||
*
|
||||
* The true value is equivalent to html.
|
||||
*
|
||||
* If autoescaping is disabled, then the value is false.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_AutoEscape extends Twig_Node
|
||||
{
|
||||
public function __construct($value, Twig_NodeInterface $body, $lineno, $tag = 'autoescape')
|
||||
{
|
||||
parent::__construct(array('body' => $body), array('value' => $value), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->subcompile($this->getNode('body'));
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a block node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Block extends Twig_Node
|
||||
{
|
||||
public function __construct($name, Twig_NodeInterface $body, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('body' => $body), array('name' => $name), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("public function block_%s(\$context, array \$blocks = array())\n", $this->getAttribute('name')), "{\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
$compiler
|
||||
->subcompile($this->getNode('body'))
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a block call node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_BlockReference extends Twig_Node implements Twig_NodeOutputInterface
|
||||
{
|
||||
public function __construct($name, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array(), array('name' => $name), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("\$this->displayBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name')))
|
||||
;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2011 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a body node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Body extends Twig_Node
|
||||
{
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2011 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a do node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Do extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(";\n")
|
||||
;
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2012 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an embed node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Embed extends Twig_Node_Include
|
||||
{
|
||||
// we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
|
||||
public function __construct($filename, $index, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(new Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);
|
||||
|
||||
$this->setAttribute('filename', $filename);
|
||||
$this->setAttribute('index', $index);
|
||||
}
|
||||
|
||||
protected function addGetTemplate(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("\$this->env->loadTemplate(")
|
||||
->string($this->getAttribute('filename'))
|
||||
->raw(', ')
|
||||
->string($this->getAttribute('index'))
|
||||
->raw(")")
|
||||
;
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract class for all nodes that represents an expression.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class Twig_Node_Expression extends Twig_Node
|
||||
{
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Array extends Twig_Node_Expression
|
||||
{
|
||||
protected $index;
|
||||
|
||||
public function __construct(array $elements, $lineno)
|
||||
{
|
||||
parent::__construct($elements, array(), $lineno);
|
||||
|
||||
$this->index = -1;
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
if ($pair['key'] instanceof Twig_Node_Expression_Constant && ctype_digit((string) $pair['key']->getAttribute('value')) && $pair['key']->getAttribute('value') > $this->index) {
|
||||
$this->index = $pair['key']->getAttribute('value');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getKeyValuePairs()
|
||||
{
|
||||
$pairs = array();
|
||||
|
||||
foreach (array_chunk($this->nodes, 2) as $pair) {
|
||||
$pairs[] = array(
|
||||
'key' => $pair[0],
|
||||
'value' => $pair[1],
|
||||
);
|
||||
}
|
||||
|
||||
return $pairs;
|
||||
}
|
||||
|
||||
public function hasElement(Twig_Node_Expression $key)
|
||||
{
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
// we compare the string representation of the keys
|
||||
// to avoid comparing the line numbers which are not relevant here.
|
||||
if ((string) $key == (string) $pair['key']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addElement(Twig_Node_Expression $value, Twig_Node_Expression $key = null)
|
||||
{
|
||||
if (null === $key) {
|
||||
$key = new Twig_Node_Expression_Constant(++$this->index, $value->getLine());
|
||||
}
|
||||
|
||||
array_push($this->nodes, $key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->raw('array(');
|
||||
$first = true;
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
$first = false;
|
||||
|
||||
$compiler
|
||||
->subcompile($pair['key'])
|
||||
->raw(' => ')
|
||||
->subcompile($pair['value'])
|
||||
;
|
||||
}
|
||||
$compiler->raw(')');
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
class Twig_Node_Expression_AssignName extends Twig_Node_Expression_Name
|
||||
{
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('$context[')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(']')
|
||||
;
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
abstract class Twig_Node_Expression_Binary extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $left, Twig_NodeInterface $right, $lineno)
|
||||
{
|
||||
parent::__construct(array('left' => $left, 'right' => $right), array(), $lineno);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(' ')
|
||||
;
|
||||
$this->operator($compiler);
|
||||
$compiler
|
||||
->raw(' ')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
||||
abstract public function operator(Twig_Compiler $compiler);
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_Add extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('+');
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_And extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('&&');
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_BitwiseAnd extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('&');
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_BitwiseOr extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('|');
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_BitwiseXor extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('^');
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user