forked from leftypol/leftypol
Compare commits
1271 Commits
tinyboard_
...
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
|
|
@ -171,7 +171,7 @@ class AntiBot {
|
|||
|
||||
public function hash() {
|
||||
global $config;
|
||||
|
||||
|
||||
// This is the tricky part: create a hash to validate it after
|
||||
// First, sort the keys in alphabetical order (A-Z)
|
||||
$inputs = $this->inputs;
|
||||
|
@ -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));
|
||||
}
|
||||
|
|
85
inc/api.php
85
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,12 +91,41 @@ 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);
|
||||
$apiPost['md5'] = base64_encode(hex2bin($post->filehash));
|
||||
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) {
|
||||
|
@ -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;
|
||||
|
||||
$extra_file = array();
|
||||
$this->translateFile($f, $post, $extra_file);
|
||||
$apiPost['files'] = [];
|
||||
foreach ($post->files as $f) {
|
||||
$file = array();
|
||||
$this->translateFile($f, $post, $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;
|
||||
}
|
||||
|
||||
|
|
194
inc/bans.php
194
inc/bans.php
|
@ -1,86 +1,84 @@
|
|||
<?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 {
|
||||
static public function range_to_string($mask) {
|
||||
list($ipstart, $ipend) = $mask;
|
||||
|
||||
|
||||
if (!isset($ipend) || $ipend === false) {
|
||||
// Not a range. Single IP address.
|
||||
$ipstr = inet_ntop($ipstart);
|
||||
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 '???';
|
||||
}
|
||||
|
||||
|
||||
private static function calc_cidr($mask) {
|
||||
$cidr = new CIDR($mask);
|
||||
$range = $cidr->getRange();
|
||||
|
||||
|
||||
return array(inet_pton($range[0]), inet_pton($range[1]));
|
||||
}
|
||||
|
||||
|
||||
public static function parse_time($str) {
|
||||
if (empty($str))
|
||||
return false;
|
||||
|
||||
|
||||
if (($time = @strtotime($str)) !== false)
|
||||
return $time;
|
||||
|
||||
|
||||
if (!preg_match('/^((\d+)\s?ye?a?r?s?)?\s?+((\d+)\s?mon?t?h?s?)?\s?+((\d+)\s?we?e?k?s?)?\s?+((\d+)\s?da?y?s?)?((\d+)\s?ho?u?r?s?)?\s?+((\d+)\s?mi?n?u?t?e?s?)?\s?+((\d+)\s?se?c?o?n?d?s?)?$/', $str, $matches))
|
||||
return false;
|
||||
|
||||
|
||||
$expire = 0;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static public function parse_range($mask) {
|
||||
$ipstart = false;
|
||||
$ipend = false;
|
||||
|
||||
|
||||
if (preg_match('@^(\d{1,3}\.){1,3}([\d*]{1,3})?$@', $mask) && substr_count($mask, '*') == 1) {
|
||||
// IPv4 wildcard mask
|
||||
$parts = explode('.', $mask);
|
||||
|
@ -101,51 +99,52 @@ class Bans {
|
|||
list($ipv4, $bits) = explode('/', $mask);
|
||||
if ($bits > 32)
|
||||
return false;
|
||||
|
||||
|
||||
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)
|
||||
return false;
|
||||
} elseif (($ipstart = @inet_pton($mask)) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return array($ipstart, $ipend);
|
||||
}
|
||||
|
||||
|
||||
static public function find($ip, $board = false, $get_mod_info = false) {
|
||||
global $config;
|
||||
|
||||
|
||||
$query = prepare('SELECT ``bans``.*' . ($get_mod_info ? ', `username`' : '') . ' FROM ``bans``
|
||||
' . ($get_mod_info ? 'LEFT JOIN ``mods`` ON ``mods``.`id` = `creator`' : '') . '
|
||||
WHERE
|
||||
(' . ($board !== false ? '(`board` IS NULL OR `board` = :board) AND' : '') . '
|
||||
(`ipstart` = :ip OR (:ip >= `ipstart` AND :ip <= `ipend`)))
|
||||
ORDER BY `expires` IS NULL, `expires` DESC');
|
||||
|
||||
|
||||
if ($board !== false)
|
||||
$query->bindValue(':board', $board, PDO::PARAM_STR);
|
||||
|
||||
|
||||
$query->bindValue(':ip', inet_pton($ip));
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
|
||||
$ban_list = array();
|
||||
|
||||
|
||||
while ($ban = $query->fetch(PDO::FETCH_ASSOC)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $ban_list;
|
||||
}
|
||||
|
||||
|
@ -153,16 +152,18 @@ class Bans {
|
|||
$query = query("SELECT ``bans``.*, `username` FROM ``bans``
|
||||
LEFT JOIN ``mods`` ON ``mods``.`id` = `creator`
|
||||
ORDER BY `created` DESC") or error(db_error());
|
||||
$bans = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
$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("[");
|
||||
|
||||
$end = end($bans);
|
||||
|
||||
foreach ($bans as &$ban) {
|
||||
$ban['mask'] = self::range_to_string(array($ban['ipstart'], $ban['ipend']));
|
||||
foreach ($bans as &$ban) {
|
||||
$ban['mask'] = self::range_to_string(array($ban['ipstart'], $ban['ipend']));
|
||||
|
||||
$hide_message = false;
|
||||
foreach ($hide_regexes as $regex) {
|
||||
|
@ -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']);
|
||||
|
||||
|
@ -186,7 +187,7 @@ class Bans {
|
|||
$ban['single_addr'] = true;
|
||||
}
|
||||
if ($filter_staff || ($board_access !== false && !in_array($ban['board'], $board_access))) {
|
||||
$ban['username'] = '?';
|
||||
$ban['username'] = '?';
|
||||
}
|
||||
if ($filter_ips || ($board_access !== false && !in_array($ban['board'], $board_access))) {
|
||||
@list($ban['mask'], $subnet) = explode("/", $ban['mask']);
|
||||
|
@ -208,24 +209,25 @@ class Bans {
|
|||
}
|
||||
}
|
||||
|
||||
$out ? fputs($out, "]") : print("]");
|
||||
$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());
|
||||
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("UPDATE ``bans`` SET `seen` = 1 WHERE `id` = " . (int)$ban_id) or error(db_error());
|
||||
rebuildThemes('bans');
|
||||
}
|
||||
|
||||
|
||||
static public function purge() {
|
||||
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,50 +236,55 @@ class Bans {
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($boards !== false && !in_array($ban['board'], $boards))
|
||||
error($config['error']['noaccess']);
|
||||
|
||||
if ($boards !== false && !in_array($ban['board'], $boards)) {
|
||||
error($config['error']['noaccess']);
|
||||
}
|
||||
|
||||
$mask = self::range_to_string(array($ban['ipstart'], $ban['ipend']));
|
||||
|
||||
|
||||
modLog("Removed ban #{$ban_id} for " .
|
||||
(filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$mask\">$mask</a>" : $mask));
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static public function new_ban($mask, $reason, $length = false, $ban_board = false, $mod_id = false, $post = false) {
|
||||
global $mod, $pdo, $board;
|
||||
|
||||
|
||||
if ($mod_id === false) {
|
||||
$mod_id = isset($mod['id']) ? $mod['id'] : -1;
|
||||
}
|
||||
|
||||
|
||||
$range = self::parse_range($mask);
|
||||
$mask = self::range_to_string($range);
|
||||
|
||||
|
||||
$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());
|
||||
|
||||
|
||||
if ($reason !== '') {
|
||||
$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)) {
|
||||
$length = time() + $length;
|
||||
|
@ -288,31 +295,30 @@ class Bans {
|
|||
} else {
|
||||
$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'));
|
||||
}
|
||||
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
$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');
|
||||
|
||||
return $pdo->lastInsertId();
|
||||
|
|
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.
|
||||
);
|
2930
inc/config.php
2930
inc/config.php
File diff suppressed because it is too large
Load Diff
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 (mysql_version() >= 50503)
|
||||
query('SET NAMES utf8mb4') or error(db_error());
|
||||
else
|
||||
query('SET NAMES utf8') or error(db_error());
|
||||
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) {
|
||||
|
|
707
inc/display.php
707
inc/display.php
|
@ -5,395 +5,418 @@
|
|||
*/
|
||||
|
||||
if (realpath($_SERVER['SCRIPT_FILENAME']) == str_replace('\\', '/', __FILE__)) {
|
||||
// You cannot request this file directly.
|
||||
exit;
|
||||
// You cannot request this file directly.
|
||||
exit;
|
||||
}
|
||||
|
||||
/*
|
||||
joaoptm78@gmail.com
|
||||
http://www.php.net/manual/en/function.filesize.php#100097
|
||||
joaoptm78@gmail.com
|
||||
http://www.php.net/manual/en/function.filesize.php#100097
|
||||
*/
|
||||
function format_bytes($size) {
|
||||
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
|
||||
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
|
||||
return round($size, 2).$units[$i];
|
||||
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
|
||||
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
|
||||
return round($size, 2).$units[$i];
|
||||
}
|
||||
|
||||
function doBoardListPart($list, $root, &$boards) {
|
||||
global $config;
|
||||
|
||||
$body = '';
|
||||
foreach ($list as $key => $board) {
|
||||
if (is_array($board))
|
||||
$body .= ' <span class="sub" data-description="' . $key . '">[' . doBoardListPart($board, $root, $boards) . ']</span> ';
|
||||
else {
|
||||
if (gettype($key) == 'string') {
|
||||
$body .= ' <a href="' . $board . '">' . $key . '</a> /';
|
||||
} else {
|
||||
$title = '';
|
||||
if (isset ($boards[$board])) {
|
||||
$title = ' title="'.$boards[$board].'"';
|
||||
}
|
||||
|
||||
$body .= ' <a href="' . $root . $board . '/' . $config['file_index'] . '"'.$title.'>' . $board . '</a> /';
|
||||
}
|
||||
}
|
||||
}
|
||||
$body = preg_replace('/\/$/', '', $body);
|
||||
|
||||
return $body;
|
||||
global $config;
|
||||
|
||||
$body = '';
|
||||
foreach ($list as $key => $board) {
|
||||
if (is_array($board))
|
||||
$body .= ' <span class="sub" data-description="' . $key . '">[' . doBoardListPart($board, $root, $boards) . ']</span> ';
|
||||
else {
|
||||
if (gettype($key) == 'string') {
|
||||
$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].'"';
|
||||
}
|
||||
|
||||
$body .= ' <a href="' . $root . $board . '/' . $config['file_index'] . '"'.$title.'>' . $board . '</a> /';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$body = preg_replace('/\/$/', '', $body);
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
function createBoardlist($mod=false) {
|
||||
global $config;
|
||||
|
||||
if (!isset($config['boards'])) return array('top'=>'','bottom'=>'');
|
||||
|
||||
$xboards = listBoards();
|
||||
$boards = array();
|
||||
foreach ($xboards as $val) {
|
||||
$boards[$val['uri']] = $val['title'];
|
||||
}
|
||||
global $config;
|
||||
|
||||
if (!isset($config['boards'])) return array('top'=>'','bottom'=>'');
|
||||
|
||||
$xboards = listBoards();
|
||||
$boards = array();
|
||||
foreach ($xboards as $val) {
|
||||
$boards[$val['uri']] = $val['title'];
|
||||
}
|
||||
|
||||
$body = doBoardListPart($config['boards'], $mod?'?/':$config['root'], $boards);
|
||||
$body = doBoardListPart($config['boards'], $mod?'?/':$config['root'], $boards);
|
||||
|
||||
if ($config['boardlist_wrap_bracket'] && !preg_match('/\] $/', $body))
|
||||
$body = '[' . $body . ']';
|
||||
|
||||
$body = trim($body);
|
||||
if ($config['boardlist_wrap_bracket'] && !preg_match('/\] $/', $body))
|
||||
$body = '[' . $body . ']';
|
||||
|
||||
$body = trim($body);
|
||||
|
||||
// Message compact-boardlist.js faster, so that page looks less ugly during loading
|
||||
$top = "<script type='text/javascript'>if (typeof do_boardlist != 'undefined') do_boardlist();</script>";
|
||||
|
||||
return array(
|
||||
'top' => '<div class="boardlist">' . $body . '</div>' . $top,
|
||||
'bottom' => '<div class="boardlist bottom">' . $body . '</div>'
|
||||
);
|
||||
// Message compact-boardlist.js faster, so that page looks less ugly during loading
|
||||
$top = "<script type='text/javascript'>if (typeof do_boardlist != 'undefined') do_boardlist();</script>";
|
||||
|
||||
return array(
|
||||
'top' => '<div class="boardlist">' . $body . '</div>' . $top,
|
||||
'bottom' => '<div class="boardlist bottom">' . $body . '</div>'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function loginForm($error=false, $username=false, $redirect=false) {
|
||||
global $config;
|
||||
|
||||
die(Element('page.html', array(
|
||||
'index' => $config['root'],
|
||||
'title' => _('Login'),
|
||||
'config' => $config,
|
||||
'body' => Element('login.html', array(
|
||||
'config'=>$config,
|
||||
'error'=>$error,
|
||||
'username'=>utf8tohtml($username),
|
||||
'redirect'=>$redirect
|
||||
)
|
||||
)
|
||||
)));
|
||||
global $config;
|
||||
|
||||
die(Element('page.html', array(
|
||||
'index' => $config['root'],
|
||||
'title' => _('Login'),
|
||||
'config' => $config,
|
||||
'body' => Element('login.html', array(
|
||||
'config'=>$config,
|
||||
'error'=>$error,
|
||||
'username'=>utf8tohtml($username),
|
||||
'redirect'=>$redirect
|
||||
)
|
||||
)
|
||||
)));
|
||||
}
|
||||
|
||||
function pm_snippet($body, $len=null) {
|
||||
global $config;
|
||||
|
||||
if (!isset($len))
|
||||
$len = &$config['mod']['snippet_length'];
|
||||
|
||||
// Replace line breaks with some whitespace
|
||||
$body = preg_replace('@<br/?>@i', ' ', $body);
|
||||
|
||||
// Strip tags
|
||||
$body = strip_tags($body);
|
||||
|
||||
// Unescape HTML characters, to avoid splitting them in half
|
||||
$body = html_entity_decode($body, ENT_COMPAT, 'UTF-8');
|
||||
|
||||
// calculate strlen() so we can add "..." after if needed
|
||||
$strlen = mb_strlen($body);
|
||||
|
||||
$body = mb_substr($body, 0, $len);
|
||||
|
||||
// Re-escape the characters.
|
||||
return '<em>' . utf8tohtml($body) . ($strlen > $len ? '…' : '') . '</em>';
|
||||
global $config;
|
||||
|
||||
if (!isset($len))
|
||||
$len = &$config['mod']['snippet_length'];
|
||||
|
||||
// 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);
|
||||
|
||||
// Unescape HTML characters, to avoid splitting them in half
|
||||
$body = html_entity_decode($body, ENT_COMPAT, 'UTF-8');
|
||||
|
||||
// calculate strlen() so we can add "..." after if needed
|
||||
$strlen = mb_strlen($body);
|
||||
|
||||
$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>';
|
||||
}
|
||||
|
||||
function capcode($cap) {
|
||||
global $config;
|
||||
|
||||
if (!$cap)
|
||||
return false;
|
||||
|
||||
$capcode = array();
|
||||
if (isset($config['custom_capcode'][$cap])) {
|
||||
if (is_array($config['custom_capcode'][$cap])) {
|
||||
$capcode['cap'] = sprintf($config['custom_capcode'][$cap][0], $cap);
|
||||
if (isset($config['custom_capcode'][$cap][1]))
|
||||
$capcode['name'] = $config['custom_capcode'][$cap][1];
|
||||
if (isset($config['custom_capcode'][$cap][2]))
|
||||
$capcode['trip'] = $config['custom_capcode'][$cap][2];
|
||||
} else {
|
||||
$capcode['cap'] = sprintf($config['custom_capcode'][$cap], $cap);
|
||||
}
|
||||
} else {
|
||||
$capcode['cap'] = sprintf($config['capcode'], $cap);
|
||||
}
|
||||
|
||||
return $capcode;
|
||||
global $config;
|
||||
|
||||
if (!$cap)
|
||||
return false;
|
||||
|
||||
$capcode = array();
|
||||
if (isset($config['custom_capcode'][$cap])) {
|
||||
if (is_array($config['custom_capcode'][$cap])) {
|
||||
$capcode['cap'] = sprintf($config['custom_capcode'][$cap][0], $cap);
|
||||
if (isset($config['custom_capcode'][$cap][1]))
|
||||
$capcode['name'] = $config['custom_capcode'][$cap][1];
|
||||
if (isset($config['custom_capcode'][$cap][2]))
|
||||
$capcode['trip'] = $config['custom_capcode'][$cap][2];
|
||||
} else {
|
||||
$capcode['cap'] = sprintf($config['custom_capcode'][$cap], $cap);
|
||||
}
|
||||
} else {
|
||||
$capcode['cap'] = sprintf($config['capcode'], $cap);
|
||||
}
|
||||
|
||||
return $capcode;
|
||||
}
|
||||
|
||||
function truncate($body, $url, $max_lines = false, $max_chars = false) {
|
||||
global $config;
|
||||
|
||||
if ($max_lines === false)
|
||||
$max_lines = $config['body_truncate'];
|
||||
if ($max_chars === false)
|
||||
$max_chars = $config['body_truncate_char'];
|
||||
|
||||
// We don't want to risk truncating in the middle of an HTML comment.
|
||||
// It's easiest just to remove them all first.
|
||||
$body = preg_replace('/<!--.*?-->/s', '', $body);
|
||||
|
||||
$original_body = $body;
|
||||
|
||||
$lines = substr_count($body, '<br/>');
|
||||
|
||||
// Limit line count
|
||||
if ($lines > $max_lines) {
|
||||
if (preg_match('/(((.*?)<br\/>){' . $max_lines . '})/', $body, $m))
|
||||
$body = $m[0];
|
||||
}
|
||||
|
||||
$body = mb_substr($body, 0, $max_chars);
|
||||
|
||||
if ($body != $original_body) {
|
||||
// Remove any corrupt tags at the end
|
||||
$body = preg_replace('/<([\w]+)?([^>]*)?$/', '', $body);
|
||||
|
||||
// Open tags
|
||||
if (preg_match_all('/<([\w]+)[^>]*>/', $body, $open_tags)) {
|
||||
|
||||
$tags = array();
|
||||
for ($x=0;$x<count($open_tags[0]);$x++) {
|
||||
if (!preg_match('/\/(\s+)?>$/', $open_tags[0][$x]))
|
||||
$tags[] = $open_tags[1][$x];
|
||||
}
|
||||
|
||||
// List successfully closed tags
|
||||
if (preg_match_all('/(<\/([\w]+))>/', $body, $closed_tags)) {
|
||||
for ($x=0;$x<count($closed_tags[0]);$x++) {
|
||||
unset($tags[array_search($closed_tags[2][$x], $tags)]);
|
||||
}
|
||||
}
|
||||
|
||||
// remove broken HTML entity at the end (if existent)
|
||||
$body = preg_replace('/&[^;]+$/', '', $body);
|
||||
|
||||
$tags_no_close_needed = array("colgroup", "dd", "dt", "li", "optgroup", "option", "p", "tbody", "td", "tfoot", "th", "thead", "tr", "br", "img");
|
||||
|
||||
// Close any open tags
|
||||
foreach ($tags as &$tag) {
|
||||
if (!in_array($tag, $tags_no_close_needed))
|
||||
$body .= "</{$tag}>";
|
||||
}
|
||||
} else {
|
||||
// remove broken HTML entity at the end (if existent)
|
||||
$body = preg_replace('/&[^;]*$/', '', $body);
|
||||
}
|
||||
|
||||
$body .= '<span class="toolong">'.sprintf(_('Post too long. Click <a href="%s">here</a> to view the full text.'), $url).'</span>';
|
||||
}
|
||||
|
||||
return $body;
|
||||
global $config;
|
||||
|
||||
if ($max_lines === false)
|
||||
$max_lines = $config['body_truncate'];
|
||||
if ($max_chars === false)
|
||||
$max_chars = $config['body_truncate_char'];
|
||||
|
||||
// We don't want to risk truncating in the middle of an HTML comment.
|
||||
// It's easiest just to remove them all first.
|
||||
$body = preg_replace('/<!--.*?-->/s', '', $body);
|
||||
|
||||
$original_body = $body;
|
||||
|
||||
$lines = substr_count($body, '<br/>');
|
||||
|
||||
// Limit line count
|
||||
if ($lines > $max_lines) {
|
||||
if (preg_match('/(((.*?)<br\/>){' . $max_lines . '})/', $body, $m))
|
||||
$body = $m[0];
|
||||
}
|
||||
|
||||
$body = mb_substr($body, 0, $max_chars);
|
||||
|
||||
if ($body != $original_body) {
|
||||
// Remove any corrupt tags at the end
|
||||
$body = preg_replace('/<([\w]+)?([^>]*)?$/', '', $body);
|
||||
|
||||
// Open tags
|
||||
if (preg_match_all('/<([\w]+)[^>]*>/', $body, $open_tags)) {
|
||||
|
||||
$tags = array();
|
||||
for ($x=0;$x<count($open_tags[0]);$x++) {
|
||||
if (!preg_match('/\/(\s+)?>$/', $open_tags[0][$x]))
|
||||
$tags[] = $open_tags[1][$x];
|
||||
}
|
||||
|
||||
// List successfully closed tags
|
||||
if (preg_match_all('/(<\/([\w]+))>/', $body, $closed_tags)) {
|
||||
for ($x=0;$x<count($closed_tags[0]);$x++) {
|
||||
unset($tags[array_search($closed_tags[2][$x], $tags)]);
|
||||
}
|
||||
}
|
||||
|
||||
// remove broken HTML entity at the end (if existent)
|
||||
$body = preg_replace('/&[^;]+$/', '', $body);
|
||||
|
||||
$tags_no_close_needed = array("colgroup", "dd", "dt", "li", "optgroup", "option", "p", "tbody", "td", "tfoot", "th", "thead", "tr", "br", "img");
|
||||
|
||||
// Close any open tags
|
||||
foreach ($tags as &$tag) {
|
||||
if (!in_array($tag, $tags_no_close_needed))
|
||||
$body .= "</{$tag}>";
|
||||
}
|
||||
} else {
|
||||
// remove broken HTML entity at the end (if existent)
|
||||
$body = preg_replace('/&[^;]*$/', '', $body);
|
||||
}
|
||||
|
||||
$body .= '<span class="toolong">'.sprintf(_('Post too long. Click <a href="%s">here</a> to view the full text.'), $url).'</span>';
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
function bidi_cleanup($data) {
|
||||
// Closes all embedded RTL and LTR unicode formatting blocks in a string so that
|
||||
// it can be used inside another without controlling its direction.
|
||||
// Closes all embedded RTL and LTR unicode formatting blocks in a string so that
|
||||
// it can be used inside another without controlling its direction.
|
||||
|
||||
$explicits = '\xE2\x80\xAA|\xE2\x80\xAB|\xE2\x80\xAD|\xE2\x80\xAE';
|
||||
$pdf = '\xE2\x80\xAC';
|
||||
|
||||
preg_match_all("!$explicits!", $data, $m1, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
|
||||
preg_match_all("!$pdf!", $data, $m2, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
|
||||
|
||||
if (count($m1) || count($m2)){
|
||||
|
||||
$p = array();
|
||||
foreach ($m1 as $m){ $p[$m[0][1]] = 'push'; }
|
||||
foreach ($m2 as $m){ $p[$m[0][1]] = 'pop'; }
|
||||
ksort($p);
|
||||
|
||||
$offset = 0;
|
||||
$stack = 0;
|
||||
foreach ($p as $pos => $type){
|
||||
|
||||
if ($type == 'push'){
|
||||
$stack++;
|
||||
}else{
|
||||
if ($stack){
|
||||
$stack--;
|
||||
}else{
|
||||
# we have a pop without a push - remove it
|
||||
$data = substr($data, 0, $pos-$offset)
|
||||
.substr($data, $pos+3-$offset);
|
||||
$offset += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# now add some pops if your stack is bigger than 0
|
||||
for ($i=0; $i<$stack; $i++){
|
||||
$data .= "\xE2\x80\xAC";
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
return $data;
|
||||
$explicits = '\xE2\x80\xAA|\xE2\x80\xAB|\xE2\x80\xAD|\xE2\x80\xAE';
|
||||
$pdf = '\xE2\x80\xAC';
|
||||
|
||||
preg_match_all("!$explicits!", $data, $m1, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
|
||||
preg_match_all("!$pdf!", $data, $m2, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
|
||||
|
||||
if (count($m1) || count($m2)){
|
||||
|
||||
$p = array();
|
||||
foreach ($m1 as $m){ $p[$m[0][1]] = 'push'; }
|
||||
foreach ($m2 as $m){ $p[$m[0][1]] = 'pop'; }
|
||||
ksort($p);
|
||||
|
||||
$offset = 0;
|
||||
$stack = 0;
|
||||
foreach ($p as $pos => $type){
|
||||
|
||||
if ($type == 'push'){
|
||||
$stack++;
|
||||
}else{
|
||||
if ($stack){
|
||||
$stack--;
|
||||
}else{
|
||||
# we have a pop without a push - remove it
|
||||
$data = substr($data, 0, $pos-$offset)
|
||||
.substr($data, $pos+3-$offset);
|
||||
$offset += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# now add some pops if your stack is bigger than 0
|
||||
for ($i=0; $i<$stack; $i++){
|
||||
$data .= "\xE2\x80\xAC";
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
function secure_link_confirm($text, $title, $confirm_message, $href) {
|
||||
global $config;
|
||||
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>';
|
||||
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 secure_link($href) {
|
||||
return $href . '/' . make_secure_link_token($href);
|
||||
return $href . '/' . make_secure_link_token($href);
|
||||
}
|
||||
|
||||
function embed_html($link) {
|
||||
global $config;
|
||||
|
||||
foreach ($config['embedding'] as $embed) {
|
||||
if ($html = preg_replace($embed[0], $embed[1], $link)) {
|
||||
if ($html == $link)
|
||||
continue; // Nope
|
||||
|
||||
$html = str_replace('%%tb_width%%', $config['embed_width'], $html);
|
||||
$html = str_replace('%%tb_height%%', $config['embed_height'], $html);
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
||||
if ($link[0] == '<') {
|
||||
// Prior to v0.9.6-dev-8, HTML code for embedding was stored in the database instead of the link.
|
||||
return $link;
|
||||
}
|
||||
|
||||
return 'Embedding error.';
|
||||
global $config;
|
||||
|
||||
foreach ($config['embedding'] as $embed) {
|
||||
if ($html = preg_replace($embed[0], $embed[1], $link)) {
|
||||
if ($html == $link)
|
||||
continue; // Nope
|
||||
|
||||
$html = str_replace('%%tb_width%%', $config['embed_width'], $html);
|
||||
$html = str_replace('%%tb_height%%', $config['embed_height'], $html);
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
||||
if ($link[0] == '<') {
|
||||
// Prior to v0.9.6-dev-8, HTML code for embedding was stored in the database instead of the link.
|
||||
return $link;
|
||||
}
|
||||
|
||||
return 'Embedding error.';
|
||||
}
|
||||
|
||||
class Post {
|
||||
public function __construct($post, $root=null, $mod=false) {
|
||||
global $config;
|
||||
if (!isset($root))
|
||||
$root = &$config['root'];
|
||||
|
||||
foreach ($post as $key => $value) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
public function __construct($post, $root=null, $mod=false) {
|
||||
global $config;
|
||||
if (!isset($root))
|
||||
$root = &$config['root'];
|
||||
|
||||
foreach ($post as $key => $value) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
|
||||
if (isset($this->files) && $this->files)
|
||||
$this->files = @json_decode($this->files);
|
||||
|
||||
$this->subject = utf8tohtml($this->subject);
|
||||
$this->name = utf8tohtml($this->name);
|
||||
$this->mod = $mod;
|
||||
$this->root = $root;
|
||||
|
||||
if ($this->embed)
|
||||
$this->embed = embed_html($this->embed);
|
||||
|
||||
$this->modifiers = extract_modifiers($this->body_nomarkup);
|
||||
|
||||
if ($config['always_regenerate_markup']) {
|
||||
$this->body = $this->body_nomarkup;
|
||||
markup($this->body);
|
||||
}
|
||||
|
||||
if ($this->mod)
|
||||
// Fix internal links
|
||||
// Very complicated regex
|
||||
$this->body = preg_replace(
|
||||
'/<a((([a-zA-Z]+="[^"]+")|[a-zA-Z]+=[a-zA-Z]+|\s)*)href="' . preg_quote($config['root'], '/') . '(' . sprintf(preg_quote($config['board_path'], '/'), $config['board_regex']) . ')/u',
|
||||
'<a $1href="?/$4',
|
||||
$this->body
|
||||
);
|
||||
}
|
||||
public function link($pre = '', $page = false) {
|
||||
global $config, $board;
|
||||
|
||||
return $this->root . $board['dir'] . $config['dir']['res'] . link_for((array)$this, $page == '50') . '#' . $pre . $this->id;
|
||||
}
|
||||
|
||||
public function build($index=false) {
|
||||
global $board, $config;
|
||||
|
||||
return Element('post_reply.html', array('config' => $config, 'board' => $board, 'post' => &$this, 'index' => $index, 'mod' => $this->mod));
|
||||
}
|
||||
if (isset($this->files) && $this->files)
|
||||
$this->files = @json_decode($this->files);
|
||||
|
||||
$this->subject = utf8tohtml($this->subject);
|
||||
$this->name = utf8tohtml($this->name);
|
||||
$this->mod = $mod;
|
||||
$this->root = $root;
|
||||
|
||||
if ($this->embed)
|
||||
$this->embed = embed_html($this->embed);
|
||||
|
||||
$this->modifiers = extract_modifiers($this->body_nomarkup);
|
||||
|
||||
if ($config['always_regenerate_markup']) {
|
||||
$this->body = $this->body_nomarkup;
|
||||
markup($this->body);
|
||||
}
|
||||
|
||||
if ($this->mod)
|
||||
// Fix internal links
|
||||
// Very complicated regex
|
||||
$this->body = preg_replace(
|
||||
'/<a((([a-zA-Z]+="[^"]+")|[a-zA-Z]+=[a-zA-Z]+|\s)*)href="' . preg_quote($config['root'], '/') . '(' . sprintf(preg_quote($config['board_path'], '/'), $config['board_regex']) . ')/u',
|
||||
'<a $1href="?/$4',
|
||||
$this->body
|
||||
);
|
||||
}
|
||||
public function link($pre = '', $page = false) {
|
||||
global $config, $board;
|
||||
|
||||
return $this->root . $board['dir'] . $config['dir']['res'] . link_for((array)$this, $page == '50') . '#' . $pre . $this->id;
|
||||
}
|
||||
|
||||
public function build($index=false) {
|
||||
global $board, $config;
|
||||
|
||||
return Element('post_reply.html', array('config' => $config, 'board' => $board, 'post' => &$this, 'index' => $index, 'mod' => $this->mod));
|
||||
}
|
||||
};
|
||||
|
||||
class Thread {
|
||||
public function __construct($post, $root = null, $mod = false, $hr = true) {
|
||||
global $config;
|
||||
if (!isset($root))
|
||||
$root = &$config['root'];
|
||||
|
||||
foreach ($post as $key => $value) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
|
||||
if (isset($this->files))
|
||||
$this->files = @json_decode($this->files);
|
||||
|
||||
$this->subject = utf8tohtml($this->subject);
|
||||
$this->name = utf8tohtml($this->name);
|
||||
$this->mod = $mod;
|
||||
$this->root = $root;
|
||||
$this->hr = $hr;
|
||||
public function __construct($post, $root = null, $mod = false, $hr = true) {
|
||||
global $config;
|
||||
if (!isset($root))
|
||||
$root = &$config['root'];
|
||||
|
||||
foreach ($post as $key => $value) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
|
||||
if (isset($this->files))
|
||||
$this->files = @json_decode($this->files);
|
||||
|
||||
$this->subject = utf8tohtml($this->subject);
|
||||
$this->name = utf8tohtml($this->name);
|
||||
$this->mod = $mod;
|
||||
$this->root = $root;
|
||||
$this->hr = $hr;
|
||||
|
||||
$this->posts = array();
|
||||
$this->omitted = 0;
|
||||
$this->omitted_images = 0;
|
||||
|
||||
if ($this->embed)
|
||||
$this->embed = embed_html($this->embed);
|
||||
|
||||
$this->modifiers = extract_modifiers($this->body_nomarkup);
|
||||
|
||||
if ($config['always_regenerate_markup']) {
|
||||
$this->body = $this->body_nomarkup;
|
||||
markup($this->body);
|
||||
}
|
||||
|
||||
if ($this->mod)
|
||||
// Fix internal links
|
||||
// Very complicated regex
|
||||
$this->body = preg_replace(
|
||||
'/<a((([a-zA-Z]+="[^"]+")|[a-zA-Z]+=[a-zA-Z]+|\s)*)href="' . preg_quote($config['root'], '/') . '(' . sprintf(preg_quote($config['board_path'], '/'), $config['board_regex']) . ')/u',
|
||||
'<a $1href="?/$4',
|
||||
$this->body
|
||||
);
|
||||
}
|
||||
public function link($pre = '', $page = false) {
|
||||
global $config, $board;
|
||||
|
||||
return $this->root . $board['dir'] . $config['dir']['res'] . link_for((array)$this, $page == '50') . '#' . $pre . $this->id;
|
||||
}
|
||||
public function add(Post $post) {
|
||||
$this->posts[] = $post;
|
||||
}
|
||||
public function postCount() {
|
||||
return count($this->posts) + $this->omitted;
|
||||
}
|
||||
public function build($index=false, $isnoko50=false) {
|
||||
global $board, $config, $debug;
|
||||
|
||||
$hasnoko50 = $this->postCount() >= $config['noko50_min'];
|
||||
|
||||
event('show-thread', $this);
|
||||
$this->posts = array();
|
||||
$this->omitted = 0;
|
||||
$this->omitted_images = 0;
|
||||
|
||||
if ($this->embed)
|
||||
$this->embed = embed_html($this->embed);
|
||||
|
||||
$this->modifiers = extract_modifiers($this->body_nomarkup);
|
||||
|
||||
if ($config['always_regenerate_markup']) {
|
||||
$this->body = $this->body_nomarkup;
|
||||
markup($this->body);
|
||||
}
|
||||
|
||||
if ($this->mod)
|
||||
// Fix internal links
|
||||
// Very complicated regex
|
||||
$this->body = preg_replace(
|
||||
'/<a((([a-zA-Z]+="[^"]+")|[a-zA-Z]+=[a-zA-Z]+|\s)*)href="' . preg_quote($config['root'], '/') . '(' . sprintf(preg_quote($config['board_path'], '/'), $config['board_regex']) . ')/u',
|
||||
'<a $1href="?/$4',
|
||||
$this->body
|
||||
);
|
||||
}
|
||||
public function link($pre = '', $page = false) {
|
||||
global $config, $board;
|
||||
|
||||
return $this->root . $board['dir'] . $config['dir']['res'] . link_for((array)$this, $page == '50') . '#' . $pre . $this->id;
|
||||
}
|
||||
public function add(Post $post) {
|
||||
$this->posts[] = $post;
|
||||
}
|
||||
public function postCount() {
|
||||
return count($this->posts) + $this->omitted;
|
||||
}
|
||||
public function build($index=false, $isnoko50=false) {
|
||||
global $board, $config, $debug;
|
||||
|
||||
$hasnoko50 = $this->postCount() >= $config['noko50_min'];
|
||||
|
||||
event('show-thread', $this);
|
||||
|
||||
$file = ($index && $config['file_board']) ? 'post_thread_fileboard.html' : 'post_thread.html';
|
||||
$built = Element($file, array('config' => $config, 'board' => $board, 'post' => &$this, 'index' => $index, 'hasnoko50' => $hasnoko50, 'isnoko50' => $isnoko50, 'mod' => $this->mod));
|
||||
|
||||
return $built;
|
||||
}
|
||||
$file = ($index && $config['file_board']) ? 'post_thread_fileboard.html' : 'post_thread.html';
|
||||
$built = Element($file, array('config' => $config, 'board' => $board, 'post' => &$this, 'index' => $index, 'hasnoko50' => $hasnoko50, 'isnoko50' => $isnoko50, 'mod' => $this->mod));
|
||||
|
||||
return $built;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
451
inc/filters.php
451
inc/filters.php
|
@ -7,243 +7,240 @@
|
|||
defined('TINYBOARD') or exit;
|
||||
|
||||
class Filter {
|
||||
public $flood_check;
|
||||
private $condition;
|
||||
private $post;
|
||||
|
||||
public function __construct(array $arr) {
|
||||
foreach ($arr as $key => $value)
|
||||
$this->$key = $value;
|
||||
}
|
||||
|
||||
public function match($condition, $match) {
|
||||
$condition = strtolower($condition);
|
||||
public $flood_check;
|
||||
private $condition;
|
||||
private $post;
|
||||
|
||||
public function __construct(array $arr) {
|
||||
foreach ($arr as $key => $value)
|
||||
$this->$key = $value;
|
||||
}
|
||||
|
||||
public function match($condition, $match) {
|
||||
$condition = strtolower($condition);
|
||||
|
||||
$post = &$this->post;
|
||||
|
||||
switch($condition) {
|
||||
case 'custom':
|
||||
if (!is_callable($match))
|
||||
error('Custom condition for filter is not callable!');
|
||||
return $match($post);
|
||||
case 'flood-match':
|
||||
if (!is_array($match))
|
||||
error('Filter condition "flood-match" must be an array.');
|
||||
|
||||
// Filter out "flood" table entries which do not match this filter.
|
||||
|
||||
$flood_check_matched = array();
|
||||
|
||||
foreach ($this->flood_check as $flood_post) {
|
||||
foreach ($match as $flood_match_arg) {
|
||||
switch ($flood_match_arg) {
|
||||
case 'ip':
|
||||
if ($flood_post['ip'] != $_SERVER['REMOTE_ADDR'])
|
||||
continue 3;
|
||||
break;
|
||||
case 'body':
|
||||
if ($flood_post['posthash'] != make_comment_hex($post['body_nomarkup']))
|
||||
continue 3;
|
||||
break;
|
||||
case 'file':
|
||||
if (!isset($post['filehash']))
|
||||
return false;
|
||||
if ($flood_post['filehash'] != $post['filehash'])
|
||||
continue 3;
|
||||
break;
|
||||
case 'board':
|
||||
if ($flood_post['board'] != $post['board'])
|
||||
continue 3;
|
||||
break;
|
||||
case 'isreply':
|
||||
if ($flood_post['isreply'] == $post['op'])
|
||||
continue 3;
|
||||
break;
|
||||
default:
|
||||
error('Invalid filter flood condition: ' . $flood_match_arg);
|
||||
}
|
||||
}
|
||||
$flood_check_matched[] = $flood_post;
|
||||
}
|
||||
|
||||
$this->flood_check = $flood_check_matched;
|
||||
|
||||
return !empty($this->flood_check);
|
||||
case 'flood-time':
|
||||
foreach ($this->flood_check as $flood_post) {
|
||||
if (time() - $flood_post['time'] <= $match) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case 'flood-count':
|
||||
$count = 0;
|
||||
foreach ($this->flood_check as $flood_post) {
|
||||
if (time() - $flood_post['time'] <= $this->condition['flood-time']) {
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
return $count >= $match;
|
||||
case 'name':
|
||||
return preg_match($match, $post['name']);
|
||||
case 'trip':
|
||||
return $match === $post['trip'];
|
||||
case 'email':
|
||||
return preg_match($match, $post['email']);
|
||||
case 'subject':
|
||||
return preg_match($match, $post['subject']);
|
||||
case 'body':
|
||||
return preg_match($match, $post['body_nomarkup']);
|
||||
case 'filehash':
|
||||
return $match === $post['filehash'];
|
||||
case 'filename':
|
||||
if (!$post['files'])
|
||||
return false;
|
||||
$post = &$this->post;
|
||||
|
||||
switch($condition) {
|
||||
case 'custom':
|
||||
if (!is_callable($match))
|
||||
error('Custom condition for filter is not callable!');
|
||||
return $match($post);
|
||||
case 'flood-match':
|
||||
if (!is_array($match))
|
||||
error('Filter condition "flood-match" must be an array.');
|
||||
|
||||
// Filter out "flood" table entries which do not match this filter.
|
||||
|
||||
$flood_check_matched = array();
|
||||
|
||||
foreach ($this->flood_check as $flood_post) {
|
||||
foreach ($match as $flood_match_arg) {
|
||||
switch ($flood_match_arg) {
|
||||
case 'ip':
|
||||
if ($flood_post['ip'] != $_SERVER['REMOTE_ADDR'])
|
||||
continue 3;
|
||||
break;
|
||||
case 'body':
|
||||
if ($flood_post['posthash'] != make_comment_hex($post['body_nomarkup']))
|
||||
continue 3;
|
||||
break;
|
||||
case 'file':
|
||||
if (!isset($post['filehash']))
|
||||
return false;
|
||||
if ($flood_post['filehash'] != $post['filehash'])
|
||||
continue 3;
|
||||
break;
|
||||
case 'board':
|
||||
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'] != '1')
|
||||
continue 3;
|
||||
break;
|
||||
default:
|
||||
error('Invalid filter flood condition: ' . $flood_match_arg);
|
||||
}
|
||||
}
|
||||
$flood_check_matched[] = $flood_post;
|
||||
}
|
||||
|
||||
// is there any reason for this assignment?
|
||||
$this->flood_check = $flood_check_matched;
|
||||
|
||||
return !empty($this->flood_check);
|
||||
case 'flood-time':
|
||||
foreach ($this->flood_check as $flood_post) {
|
||||
if (time() - $flood_post['time'] <= $match) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case 'flood-count':
|
||||
$count = 0;
|
||||
foreach ($this->flood_check as $flood_post) {
|
||||
if (time() - $flood_post['time'] <= $this->condition['flood-time']) {
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
return $count >= $match;
|
||||
case 'name':
|
||||
return preg_match($match, $post['name']);
|
||||
case 'trip':
|
||||
return $match === $post['trip'];
|
||||
case 'email':
|
||||
return preg_match($match, $post['email']);
|
||||
case 'subject':
|
||||
return preg_match($match, $post['subject']);
|
||||
case 'body':
|
||||
return preg_match($match, $post['body_nomarkup']);
|
||||
case 'filehash':
|
||||
return $match === $post['filehash'];
|
||||
case 'filename':
|
||||
if (!$post['files'])
|
||||
return false;
|
||||
|
||||
foreach ($post['files'] as $file) {
|
||||
if (preg_match($match, $file['filename'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case 'extension':
|
||||
if (!$post['files'])
|
||||
return false;
|
||||
foreach ($post['files'] as $file) {
|
||||
if (preg_match($match, $file['filename'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case 'extension':
|
||||
if (!$post['files'])
|
||||
return false;
|
||||
|
||||
foreach ($post['files'] as $file) {
|
||||
if (preg_match($match, $file['extension'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case 'ip':
|
||||
return preg_match($match, $_SERVER['REMOTE_ADDR']);
|
||||
case 'op':
|
||||
return $post['op'] == $match;
|
||||
case 'has_file':
|
||||
return $post['has_file'] == $match;
|
||||
case 'board':
|
||||
return $post['board'] == $match;
|
||||
case 'password':
|
||||
return $post['password'] == $match;
|
||||
default:
|
||||
error('Unknown filter condition: ' . $condition);
|
||||
}
|
||||
}
|
||||
|
||||
public function action() {
|
||||
global $board;
|
||||
foreach ($post['files'] as $file) {
|
||||
if (preg_match($match, $file['extension'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case 'ip':
|
||||
return preg_match($match, $_SERVER['REMOTE_ADDR']);
|
||||
case 'op':
|
||||
return $post['op'] == $match;
|
||||
case 'has_file':
|
||||
return $post['has_file'] == $match;
|
||||
case 'board':
|
||||
return $post['board'] == $match;
|
||||
case 'password':
|
||||
return $post['password'] == $match;
|
||||
default:
|
||||
error('Unknown filter condition: ' . $condition);
|
||||
}
|
||||
}
|
||||
|
||||
public function action() {
|
||||
global $board;
|
||||
|
||||
$this->add_note = isset($this->add_note) ? $this->add_note : false;
|
||||
if ($this->add_note) {
|
||||
$query = prepare('INSERT INTO ``ip_notes`` VALUES (NULL, :ip, :mod, :time, :body)');
|
||||
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
|
||||
$query->bindValue(':mod', -1);
|
||||
$query->bindValue(':time', time());
|
||||
$query->bindValue(':body', "Autoban message: ".$this->post['body']);
|
||||
$query->execute() or error(db_error($query));
|
||||
}
|
||||
if (isset ($this->action)) switch($this->action) {
|
||||
case 'reject':
|
||||
error(isset($this->message) ? $this->message : 'Posting throttled by filter.');
|
||||
case 'ban':
|
||||
if (!isset($this->reason))
|
||||
error('The ban action requires a reason.');
|
||||
|
||||
$this->expires = isset($this->expires) ? $this->expires : false;
|
||||
$this->reject = isset($this->reject) ? $this->reject : true;
|
||||
$this->all_boards = isset($this->all_boards) ? $this->all_boards : false;
|
||||
|
||||
Bans::new_ban($_SERVER['REMOTE_ADDR'], $this->reason, $this->expires, $this->all_boards ? false : $board['uri'], -1);
|
||||
$this->add_note = isset($this->add_note) ? $this->add_note : false;
|
||||
if ($this->add_note) {
|
||||
$query = prepare('INSERT INTO ``ip_notes`` VALUES (NULL, :ip, :mod, :time, :body)');
|
||||
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
|
||||
$query->bindValue(':mod', -1);
|
||||
$query->bindValue(':time', time());
|
||||
$query->bindValue(':body', "Autoban message: ".$this->post['body']);
|
||||
$query->execute() or error(db_error($query));
|
||||
}
|
||||
if (isset ($this->action)) switch($this->action) {
|
||||
case 'reject':
|
||||
error(isset($this->message) ? $this->message : 'Posting blocked by filter.');
|
||||
case 'ban':
|
||||
if (!isset($this->reason))
|
||||
error('The ban action requires a reason.');
|
||||
|
||||
$this->expires = isset($this->expires) ? $this->expires : false;
|
||||
$this->reject = isset($this->reject) ? $this->reject : true;
|
||||
$this->all_boards = isset($this->all_boards) ? $this->all_boards : false;
|
||||
|
||||
Bans::new_ban($_SERVER['REMOTE_ADDR'], $this->reason, $this->expires, $this->all_boards ? false : $board['uri'], -1);
|
||||
|
||||
if ($this->reject) {
|
||||
if (isset($this->message))
|
||||
error($message);
|
||||
|
||||
checkBan($board['uri']);
|
||||
exit;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
error('Unknown filter action: ' . $this->action);
|
||||
}
|
||||
}
|
||||
|
||||
public function check(array $post) {
|
||||
$this->post = $post;
|
||||
foreach ($this->condition as $condition => $value) {
|
||||
if ($condition[0] == '!') {
|
||||
$NOT = true;
|
||||
$condition = substr($condition, 1);
|
||||
} else $NOT = false;
|
||||
|
||||
if ($this->match($condition, $value) == $NOT)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if ($this->reject) {
|
||||
if (isset($this->message))
|
||||
error($message);
|
||||
|
||||
checkBan($board['uri']);
|
||||
exit;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
error('Unknown filter action: ' . $this->action);
|
||||
}
|
||||
}
|
||||
|
||||
public function check(array $post) {
|
||||
$this->post = $post;
|
||||
foreach ($this->condition as $condition => $value) {
|
||||
if ($condition[0] == '!') {
|
||||
$NOT = true;
|
||||
$condition = substr($condition, 1);
|
||||
} else {
|
||||
$NOT = false;
|
||||
}
|
||||
|
||||
if ($this->match($condition, $value) == $NOT)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function purge_flood_table() {
|
||||
global $config;
|
||||
|
||||
// Determine how long we need to keep a cache of posts for flood prevention. Unfortunately, it is not
|
||||
// aware of flood filters in other board configurations. You can solve this problem by settings the
|
||||
// config variable $config['flood_cache'] (seconds).
|
||||
|
||||
if (isset($config['flood_cache'])) {
|
||||
$max_time = &$config['flood_cache'];
|
||||
} else {
|
||||
$max_time = 0;
|
||||
foreach ($config['filters'] as $filter) {
|
||||
if (isset($filter['condition']['flood-time']))
|
||||
$max_time = max($max_time, $filter['condition']['flood-time']);
|
||||
}
|
||||
}
|
||||
|
||||
$time = time() - $max_time;
|
||||
|
||||
query("DELETE FROM ``flood`` WHERE `time` < $time") or error(db_error());
|
||||
global $config;
|
||||
|
||||
// Determine how long we need to keep a cache of posts for flood prevention. Unfortunately, it is not
|
||||
// aware of flood filters in other board configurations. You can solve this problem by settings the
|
||||
// config variable $config['flood_cache'] (seconds).
|
||||
|
||||
if (isset($config['flood_cache'])) {
|
||||
$max_time = &$config['flood_cache'];
|
||||
} else {
|
||||
$max_time = 0;
|
||||
foreach ($config['filters'] as $filter) {
|
||||
if (isset($filter['condition']['flood-time']))
|
||||
$max_time = max($max_time, $filter['condition']['flood-time']);
|
||||
}
|
||||
}
|
||||
|
||||
$time = time() - $max_time;
|
||||
|
||||
query("DELETE FROM ``flood`` WHERE `time` < $time") or error(db_error());
|
||||
}
|
||||
|
||||
function do_filters(array $post) {
|
||||
global $config;
|
||||
|
||||
if (!isset($config['filters']) || empty($config['filters']))
|
||||
return;
|
||||
|
||||
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->execute() or error(db_error($query));
|
||||
$flood_check = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$flood_check = false;
|
||||
}
|
||||
|
||||
foreach ($config['filters'] as $filter_array) {
|
||||
$filter = new Filter($filter_array);
|
||||
$filter->flood_check = $flood_check;
|
||||
if ($filter->check($post))
|
||||
$filter->action();
|
||||
}
|
||||
|
||||
purge_flood_table();
|
||||
global $config;
|
||||
|
||||
if (!isset($config['filters']) || empty($config['filters']))
|
||||
return;
|
||||
|
||||
foreach ($config['filters'] as $filter) {
|
||||
if (isset($filter['condition']['flood-match'])) {
|
||||
$has_flood = true;
|
||||
}
|
||||
}
|
||||
if (isset($has_flood)) {
|
||||
$query = prepare("SELECT * FROM ``flood``");
|
||||
$query->execute() or error(db_error($query));
|
||||
$flood_check = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$flood_check = false;
|
||||
}
|
||||
|
||||
foreach ($config['filters'] as $filter_array) {
|
||||
$filter = new Filter($filter_array);
|
||||
$filter->flood_check = $flood_check;
|
||||
if ($filter->check($post)) {
|
||||
$filter->action();
|
||||
}
|
||||
}
|
||||
|
||||
purge_flood_table();
|
||||
}
|
||||
|
||||
|
|
735
inc/functions.php
Executable file → Normal file
735
inc/functions.php
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
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');
|
||||
}
|
254
inc/image.php
254
inc/image.php
|
@ -5,12 +5,13 @@
|
|||
*/
|
||||
|
||||
defined('TINYBOARD') or exit;
|
||||
require_once 'inc/polyfill.php';
|
||||
|
||||
class Image {
|
||||
public $src, $format, $image, $size;
|
||||
public function __construct($src, $format = false, $size = false) {
|
||||
global $config;
|
||||
|
||||
|
||||
$this->src = $src;
|
||||
$this->format = $format;
|
||||
|
||||
|
@ -24,21 +25,21 @@ class Image {
|
|||
error(_('Unsupported file format: ') . $this->format);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->image = new $classname($this, $size);
|
||||
|
||||
if (!$this->image->valid()) {
|
||||
$this->delete();
|
||||
error($config['error']['invalidimg']);
|
||||
}
|
||||
|
||||
|
||||
$this->size = (object)array('width' => $this->image->_width(), 'height' => $this->image->_height());
|
||||
if ($this->size->width < 1 || $this->size->height < 1) {
|
||||
$this->delete();
|
||||
error($config['error']['invalidimg']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function resize($extension, $max_width, $max_height) {
|
||||
global $config;
|
||||
|
||||
|
@ -62,16 +63,16 @@ class Image {
|
|||
error(_('Unsupported file format: ') . $extension);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$thumb = new $classname(false);
|
||||
$thumb->src = $this->src;
|
||||
$thumb->format = $this->format;
|
||||
$thumb->original_width = $this->size->width;
|
||||
$thumb->original_height = $this->size->height;
|
||||
|
||||
|
||||
$x_ratio = $max_width / $this->size->width;
|
||||
$y_ratio = $max_height / $this->size->height;
|
||||
|
||||
|
||||
if (($this->size->width <= $max_width) && ($this->size->height <= $max_height)) {
|
||||
$width = $this->size->width;
|
||||
$height = $this->size->height;
|
||||
|
@ -82,16 +83,16 @@ class Image {
|
|||
$width = ceil($y_ratio * $this->size->width);
|
||||
$height = $max_height;
|
||||
}
|
||||
|
||||
|
||||
$thumb->_resize($this->image->image, $width, $height);
|
||||
|
||||
|
||||
return $thumb;
|
||||
}
|
||||
|
||||
|
||||
public function to($dst) {
|
||||
$this->image->to($dst);
|
||||
}
|
||||
|
||||
|
||||
public function delete() {
|
||||
file_unlink($this->src);
|
||||
}
|
||||
|
@ -114,26 +115,26 @@ class ImageGD {
|
|||
}
|
||||
|
||||
class ImageBase extends ImageGD {
|
||||
public $image, $src, $original, $original_width, $original_height, $width, $height;
|
||||
public $image, $src, $original, $original_width, $original_height, $width, $height;
|
||||
public function valid() {
|
||||
return (bool)$this->image;
|
||||
}
|
||||
|
||||
|
||||
public function __construct($img, $size = false) {
|
||||
if (method_exists($this, 'init'))
|
||||
$this->init();
|
||||
|
||||
|
||||
if ($size && $size[0] > 0 && $size[1] > 0) {
|
||||
$this->width = $size[0];
|
||||
$this->height = $size[1];
|
||||
}
|
||||
|
||||
|
||||
if ($img !== false) {
|
||||
$this->src = $img->src;
|
||||
$this->from();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function _width() {
|
||||
if (method_exists($this, 'width'))
|
||||
return $this->width();
|
||||
|
@ -156,7 +157,7 @@ class ImageBase extends ImageGD {
|
|||
$this->original = &$original;
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
|
||||
|
||||
if (method_exists($this, 'resize'))
|
||||
$this->resize();
|
||||
else
|
||||
|
@ -199,31 +200,31 @@ class ImageImagick extends ImageBase {
|
|||
}
|
||||
public function resize() {
|
||||
global $config;
|
||||
|
||||
|
||||
if ($this->format == 'gif' && ($config['thumb_ext'] == 'gif' || $config['thumb_ext'] == '')) {
|
||||
$this->image = new Imagick();
|
||||
$this->image->setFormat('gif');
|
||||
|
||||
|
||||
$keep_frames = array();
|
||||
for ($i = 0; $i < $this->original->getNumberImages(); $i += floor($this->original->getNumberImages() / $config['thumb_keep_animation_frames']))
|
||||
$keep_frames[] = $i;
|
||||
|
||||
|
||||
$i = 0;
|
||||
$delay = 0;
|
||||
foreach ($this->original as $frame) {
|
||||
$delay += $frame->getImageDelay();
|
||||
|
||||
|
||||
if (in_array($i, $keep_frames)) {
|
||||
// $frame->scaleImage($this->width, $this->height, false);
|
||||
$frame->sampleImage($this->width, $this->height);
|
||||
$frame->setImagePage($this->width, $this->height, 0, 0);
|
||||
$frame->setImageDelay($delay);
|
||||
$delay = 0;
|
||||
|
||||
|
||||
$this->image->addImage($frame->getImage());
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->image = clone $this->original;
|
||||
$this->image->scaleImage($this->width, $this->height, false);
|
||||
|
@ -234,15 +235,15 @@ class ImageImagick extends ImageBase {
|
|||
|
||||
class ImageConvert extends ImageBase {
|
||||
public $width, $height, $temp, $gm = false, $gifsicle = false;
|
||||
|
||||
|
||||
public function init() {
|
||||
global $config;
|
||||
|
||||
|
||||
if ($config['thumb_method'] == 'gm' || $config['thumb_method'] == 'gm+gifsicle')
|
||||
$this->gm = true;
|
||||
if ($config['thumb_method'] == 'convert+gifsicle' || $config['thumb_method'] == 'gm+gifsicle')
|
||||
$this->gifsicle = true;
|
||||
|
||||
|
||||
$this->temp = false;
|
||||
}
|
||||
public function get_size($src, $try_gd_first = true) {
|
||||
|
@ -264,7 +265,7 @@ class ImageConvert extends ImageBase {
|
|||
if ($size) {
|
||||
$this->width = $size[0];
|
||||
$this->height = $size[1];
|
||||
|
||||
|
||||
$this->image = true;
|
||||
} else {
|
||||
// mark as invalid
|
||||
|
@ -273,7 +274,7 @@ class ImageConvert extends ImageBase {
|
|||
}
|
||||
public function to($src) {
|
||||
global $config;
|
||||
|
||||
|
||||
if (!$this->temp) {
|
||||
if ($config['strip_exif']) {
|
||||
if($error = shell_exec_error(($this->gm ? 'gm ' : '') . 'convert ' .
|
||||
|
@ -305,16 +306,16 @@ class ImageConvert extends ImageBase {
|
|||
}
|
||||
public function resize() {
|
||||
global $config;
|
||||
|
||||
|
||||
if ($this->temp) {
|
||||
// remove old
|
||||
$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'];
|
||||
|
||||
|
||||
if ($this->format == 'gif' && ($config['thumb_ext'] == 'gif' || $config['thumb_ext'] == '') && $config['thumb_keep_animation_frames'] > 1) {
|
||||
if ($this->gifsicle) {
|
||||
if (($error = shell_exec("gifsicle -w --unoptimize -O2 --resize {$this->width}x{$this->height} < " .
|
||||
|
@ -379,7 +380,7 @@ class ImageConvert extends ImageBase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// For when -auto-orient doesn't exist (older versions)
|
||||
static public function jpeg_exif_orientation($src, $exif = false) {
|
||||
if (!$exif) {
|
||||
|
@ -397,16 +398,16 @@ class ImageConvert extends ImageBase {
|
|||
// 8888
|
||||
// 88
|
||||
// 88
|
||||
|
||||
|
||||
return '-flop';
|
||||
case 3:
|
||||
|
||||
|
||||
// 88
|
||||
// 88
|
||||
// 8888
|
||||
// 88
|
||||
// 888888
|
||||
|
||||
|
||||
return '-flip -flop';
|
||||
case 4:
|
||||
// 88
|
||||
|
@ -414,31 +415,31 @@ class ImageConvert extends ImageBase {
|
|||
// 8888
|
||||
// 88
|
||||
// 888888
|
||||
|
||||
|
||||
return '-flip';
|
||||
case 5:
|
||||
// 8888888888
|
||||
// 88 88
|
||||
// 88
|
||||
|
||||
|
||||
return '-rotate 90 -flop';
|
||||
case 6:
|
||||
// 88
|
||||
// 88 88
|
||||
// 8888888888
|
||||
|
||||
|
||||
return '-rotate 90';
|
||||
case 7:
|
||||
// 88
|
||||
// 88 88
|
||||
// 8888888888
|
||||
|
||||
|
||||
return '-rotate "-90" -flop';
|
||||
case 8:
|
||||
// 8888888888
|
||||
// 88 88
|
||||
// 88
|
||||
|
||||
|
||||
return '-rotate "-90"';
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
$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'];
|
||||
class ImageWEBP extends ImageBase {
|
||||
public function from() {
|
||||
$this->image = @imagecreatefromwebp($this->src);
|
||||
}
|
||||
$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