mirror of
https://github.com/Sarjuuk/aowow.git
synced 2025-11-29 15:58:16 +08:00
Compare commits
835 Commits
v1.1
...
10ef33f709
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10ef33f709 | ||
|
|
ae1b6c59b1 | ||
|
|
b764200c2a | ||
|
|
c0454917ac | ||
|
|
b3e215cc40 | ||
|
|
d4694cd2db | ||
|
|
a5051c9bf5 | ||
|
|
be3701df91 | ||
|
|
9b905883df | ||
|
|
9db3e766da | ||
|
|
7f29c1d4b7 | ||
|
|
57665aaa9e | ||
|
|
4cb544182d | ||
|
|
a2b87da285 | ||
|
|
03bab92cb8 | ||
|
|
103287f91b | ||
|
|
82f36fd342 | ||
|
|
f5654ae21f | ||
|
|
1fe3690244 | ||
|
|
45417122c2 | ||
|
|
7cf5dded98 | ||
|
|
31f51276b2 | ||
|
|
643c3c2a83 | ||
|
|
a135dfce90 | ||
|
|
0d42d2a2c4 | ||
|
|
fa89a5ad1e | ||
|
|
6eb5a67add | ||
|
|
8a169eb400 | ||
|
|
cf4e8a527c | ||
|
|
48564ab8b5 | ||
|
|
edc297f97a | ||
|
|
5d02a20719 | ||
|
|
f44de66de7 | ||
|
|
16c5b73cd3 | ||
|
|
9020e36db6 | ||
|
|
597898450d | ||
|
|
6a94888686 | ||
|
|
e3d6f7b3a7 | ||
|
|
37380ff515 | ||
|
|
8212811970 | ||
|
|
1e9e406ff0 | ||
|
|
3984bd0ae2 | ||
|
|
441ad38543 | ||
|
|
88cc76feae | ||
|
|
96c777191d | ||
|
|
40b2830cad | ||
|
|
9741774683 | ||
|
|
e8bc37f82f | ||
|
|
7cbe1f6007 | ||
|
|
3a25c2390f | ||
|
|
cf2ace805b | ||
|
|
2f8e035783 | ||
|
|
1a55b30766 | ||
|
|
6d7f9c0f00 | ||
|
|
862b3dff73 | ||
|
|
b1f22f7e68 | ||
|
|
1d922c1147 | ||
|
|
f9ace6a671 | ||
|
|
6ea1457c4f | ||
|
|
f522c960d9 | ||
|
|
1365cdb261 | ||
|
|
9b591e7a3a | ||
|
|
6da71afc68 | ||
|
|
033a9181ae | ||
|
|
1dcc9363da | ||
|
|
51b6e29316 | ||
|
|
14c159c164 | ||
|
|
33cd290dc3 | ||
|
|
2e029f3d96 | ||
|
|
6a32c770cd | ||
|
|
4d421d2bbb | ||
|
|
176cf137fb | ||
|
|
830edb8265 | ||
|
|
7d8b524478 | ||
|
|
95918c0410 | ||
|
|
a275955ee3 | ||
|
|
37beaa2db5 | ||
|
|
c0097f3987 | ||
|
|
92c58cc5d1 | ||
|
|
04f3aa7a82 | ||
|
|
65d490a8ae | ||
|
|
816eacaf73 | ||
|
|
034eca1f58 | ||
|
|
a33abb84fe | ||
|
|
fb7b22db36 | ||
|
|
dd838fa994 | ||
|
|
d32074fdcd | ||
|
|
494061de82 | ||
|
|
9b0aa5c885 | ||
|
|
40e98081c9 | ||
|
|
77f2a0c21d | ||
|
|
465e019eaa | ||
|
|
63053757c9 | ||
|
|
a96f6c4cdf | ||
|
|
b832fc172c | ||
|
|
196f60f176 | ||
|
|
204d4b8ae2 | ||
|
|
5d2fd00358 | ||
|
|
1dcdf9623b | ||
|
|
215ad39cc6 | ||
|
|
a9ed897ea6 | ||
|
|
3edac3c77a | ||
|
|
95ee9d2c25 | ||
|
|
d79742d599 | ||
|
|
e300086cc8 | ||
|
|
a7e9ac2cf2 | ||
|
|
05f5b0ed34 | ||
|
|
e37620c01b | ||
|
|
c40bd3851b | ||
|
|
452615a92d | ||
|
|
704894c1e3 | ||
|
|
045c16c241 | ||
|
|
7b429811a9 | ||
|
|
aa7c0186fc | ||
|
|
7b752143a0 | ||
|
|
baf4ba5b98 | ||
|
|
eb95b03e31 | ||
|
|
4fe35d9e3c | ||
|
|
5355989015 | ||
|
|
9fc84cdf9e | ||
|
|
a6108be400 | ||
|
|
ff690770b5 | ||
|
|
6263ccd92a | ||
|
|
60eb816002 | ||
|
|
bc112b2b16 | ||
|
|
6d86f880f4 | ||
|
|
bd1f139c2e | ||
|
|
36aa33ac26 | ||
|
|
647be4a946 | ||
|
|
92c1c59d3a | ||
|
|
6557e70d5c | ||
|
|
a48e94cd8b | ||
|
|
1d5539b362 | ||
|
|
258ac19f0a | ||
|
|
8fadce88ad | ||
|
|
f16479b50c | ||
|
|
155bf1e4a3 | ||
|
|
ab27976132 | ||
|
|
3f8a1838c0 | ||
|
|
f1b613cfa0 | ||
|
|
398ff16b65 | ||
|
|
69df20af63 | ||
|
|
fef27c58e6 | ||
|
|
cb523353fd | ||
|
|
a369244908 | ||
|
|
3d3e2211e5 | ||
|
|
3f8d5d90e1 | ||
|
|
0cf9069eb1 | ||
|
|
1672883186 | ||
|
|
3f7f522d50 | ||
|
|
d66a863f55 | ||
|
|
e876463f3b | ||
|
|
253cbcb4d9 | ||
|
|
f17b4f58bf | ||
|
|
a824bb106c | ||
|
|
79c937e0a3 | ||
|
|
f76869ecbe | ||
|
|
e6980ce220 | ||
|
|
3ba0cc4ade | ||
|
|
64ef350e0d | ||
|
|
cdb7e1e7ec | ||
|
|
2dd9265700 | ||
|
|
e33bc9117c | ||
|
|
98a54cd871 | ||
|
|
26226e2bad | ||
|
|
11bb5a521b | ||
|
|
70e4bca10f | ||
|
|
503b9458e0 | ||
|
|
12ef04c634 | ||
|
|
d71ab58855 | ||
|
|
2899cc881b | ||
|
|
b3b790d424 | ||
|
|
b3ea80c6cc | ||
|
|
d03f482864 | ||
|
|
e17cbfe51f | ||
|
|
2c1b1196a7 | ||
|
|
d5275b1bf8 | ||
|
|
24cb218060 | ||
|
|
1f5152c871 | ||
|
|
81d9248541 | ||
|
|
5713834f90 | ||
|
|
e943e27b5b | ||
|
|
226f521439 | ||
|
|
aeb84327d6 | ||
|
|
8cf0b6243d | ||
|
|
8243be8d8e | ||
|
|
a4a3876cdc | ||
|
|
4c89c9061e | ||
|
|
7d8ffdd7da | ||
|
|
eb3b4ca5ec | ||
|
|
0753bfbcf6 | ||
|
|
8d7c95378c | ||
|
|
16eabb90b6 | ||
|
|
08f0ae711e | ||
|
|
569c9efca4 | ||
|
|
6d3b3e1fcb | ||
|
|
112acb2216 | ||
|
|
ceec228718 | ||
|
|
40b5c992e2 | ||
|
|
b35ab67360 | ||
|
|
a99fff46aa | ||
|
|
086760b9b1 | ||
|
|
bffdb9672e | ||
|
|
58412e0491 | ||
|
|
5de9759b90 | ||
|
|
967841fcb9 | ||
|
|
3f0d6c2de6 | ||
|
|
0928b1b430 | ||
|
|
0562989196 | ||
|
|
06bd7aa665 | ||
|
|
a7cf96307c | ||
|
|
8403f9ffd9 | ||
|
|
fbfb81cd25 | ||
|
|
7a803a8783 | ||
|
|
dfefa914af | ||
|
|
707dc32495 | ||
|
|
e7baa27e27 | ||
|
|
f826e4d68a | ||
|
|
e173de9a97 | ||
|
|
7c527e6144 | ||
|
|
74c0727bdb | ||
|
|
cd94a2fa4e | ||
|
|
91bb53aa1d | ||
|
|
069ca27b35 | ||
|
|
c3048fe1f8 | ||
|
|
ee02e70571 | ||
|
|
af69aa8e94 | ||
|
|
1de0535629 | ||
|
|
9b6ed672c4 | ||
|
|
9d704c5ff9 | ||
|
|
6475a9d181 | ||
|
|
a8e1e3cf19 | ||
|
|
44e0b6c62d | ||
|
|
b837e55edc | ||
|
|
d75aa56b38 | ||
|
|
9c73f3cf95 | ||
|
|
9a893e03d7 | ||
|
|
43970189a6 | ||
|
|
682b315e17 | ||
|
|
3078763ec3 | ||
|
|
44ff43c113 | ||
|
|
790264ba08 | ||
|
|
e29d1e69fe | ||
|
|
2689fba992 | ||
|
|
db1d3ccace | ||
|
|
3a6c86092b | ||
|
|
4ccf917707 | ||
|
|
b347794ce5 | ||
|
|
ed25f1f5f5 | ||
|
|
390801f53c | ||
|
|
73f4a69a41 | ||
|
|
197b097ee5 | ||
|
|
f2bbb87eef | ||
|
|
676c8617a5 | ||
|
|
6f8b72c980 | ||
|
|
b2d3bc1076 | ||
|
|
9345309c07 | ||
|
|
50a5ccbaf6 | ||
|
|
460f4857ad | ||
|
|
3b0d288418 | ||
|
|
c712d1234b | ||
|
|
31ad2e4944 | ||
|
|
6249f2957e | ||
|
|
efc8b51c8f | ||
|
|
44bd9f521b | ||
|
|
8b59551905 | ||
|
|
4fe930cdca | ||
|
|
337eddcc0b | ||
|
|
013f1845b5 | ||
|
|
4c6d93881c | ||
|
|
fa33467610 | ||
|
|
1f59e6fe2d | ||
|
|
a5bd6ddc8a | ||
|
|
6660125154 | ||
|
|
9abe9b0d56 | ||
|
|
83b99c47d2 | ||
|
|
cd4c023c61 | ||
|
|
870cbea2ca | ||
|
|
748a78c3c7 | ||
|
|
398b93e9a7 | ||
|
|
40c2c63d1b | ||
|
|
9da1e1575f | ||
|
|
88c066a8f5 | ||
|
|
88da3588e5 | ||
|
|
5309843d77 | ||
|
|
452e056499 | ||
|
|
d86936f6f5 | ||
|
|
c1eecb4c22 | ||
|
|
a62f24b97c | ||
|
|
e3fc4ebd62 | ||
|
|
79aa8fda7e | ||
|
|
fdf8d783b1 | ||
|
|
48ce7267e7 | ||
|
|
5270e70cd1 | ||
|
|
50f5af07f3 | ||
|
|
64fb86f3a9 | ||
|
|
778c21e817 | ||
|
|
af303f447a | ||
|
|
03cf3a5918 | ||
|
|
3aede18926 | ||
|
|
075e15ba0c | ||
|
|
7d2e306f0c | ||
|
|
b11c1125f6 | ||
|
|
57ad861da7 | ||
|
|
d2e109d818 | ||
|
|
dab110475c | ||
|
|
b330f88699 | ||
|
|
481a3dc63f | ||
|
|
1b2b773663 | ||
|
|
b5c2f7a296 | ||
|
|
07e001ee9c | ||
|
|
c30e68d86f | ||
|
|
7d545167df | ||
|
|
31928d56a7 | ||
|
|
99a95f3995 | ||
|
|
a9f1832b6d | ||
|
|
b0a51f4746 | ||
|
|
f55945780b | ||
|
|
81078bcf3d | ||
|
|
c14d53067b | ||
|
|
460615c112 | ||
|
|
98b1771850 | ||
|
|
37def70f6a | ||
|
|
f4364099c6 | ||
|
|
92a6e0122f | ||
|
|
828fa40d4b | ||
|
|
815f13e530 | ||
|
|
9435c0fc2e | ||
|
|
b8898797ed | ||
|
|
be7a84a651 | ||
|
|
0f6b8015a1 | ||
|
|
bd5200de85 | ||
|
|
f21e8045b2 | ||
|
|
8d885a5a67 | ||
|
|
a4bcb33ba4 | ||
|
|
8b46607c29 | ||
|
|
c3bae7fe5e | ||
|
|
2e9b503c59 | ||
|
|
02e33b4038 | ||
|
|
c3347b8e9c | ||
|
|
cd4e049680 | ||
|
|
2bd588045a | ||
|
|
05c036bd9f | ||
|
|
d93b5df5bc | ||
|
|
f12d16ea5b | ||
|
|
d2277d1034 | ||
|
|
4d306e64fb | ||
|
|
8016802ec6 | ||
|
|
2386e35207 | ||
|
|
5d4051928a | ||
|
|
33d2192431 | ||
|
|
ae54e5e213 | ||
|
|
cdf06deb90 | ||
|
|
69de457108 | ||
|
|
040cac41a1 | ||
|
|
abbedf9ae4 | ||
|
|
22b0f8c1c1 | ||
|
|
a4c734435e | ||
|
|
88c5127ab5 | ||
|
|
a08a713dcc | ||
|
|
79764ced60 | ||
|
|
e614f415a9 | ||
|
|
10de320616 | ||
|
|
9bb5afd9a7 | ||
|
|
615c203c7a | ||
|
|
931a90f5c8 | ||
|
|
bf184e7555 | ||
|
|
ab4cf67e80 | ||
|
|
7412a518a5 | ||
|
|
3d84870db8 | ||
|
|
b5b62a5a62 | ||
|
|
7e0be11323 | ||
|
|
e164023b8a | ||
|
|
12ddc6fe82 | ||
|
|
06ffba0239 | ||
|
|
d03448b053 | ||
|
|
0117916da9 | ||
|
|
c2bbfe17a6 | ||
|
|
7b924a197e | ||
|
|
8fe18ed41c | ||
|
|
d16b08bb29 | ||
|
|
cb3c7d4ef0 | ||
|
|
a4d05dc036 | ||
|
|
ce0e57e390 | ||
|
|
33ee358e0c | ||
|
|
82c04c9ed5 | ||
|
|
fc86825b15 | ||
|
|
e873d8cbd0 | ||
|
|
69fe0b5c7d | ||
|
|
e734b41632 | ||
|
|
5c1e9747c6 | ||
|
|
f861886fdf | ||
|
|
efab0bad32 | ||
|
|
bc7d561da2 | ||
|
|
84555afae3 | ||
|
|
f77d676a19 | ||
|
|
454e09cc78 | ||
|
|
2d5caba814 | ||
|
|
f422c4ecfb | ||
|
|
a87b869896 | ||
|
|
2c451b8deb | ||
|
|
f6565ea924 | ||
|
|
7d5930865c | ||
|
|
90b04865f5 | ||
|
|
a03d44ae67 | ||
|
|
a97cede1e0 | ||
|
|
b2690aea08 | ||
|
|
c87178c791 | ||
|
|
b5829cc954 | ||
|
|
9a1cb5f2f9 | ||
|
|
d7fa4a900e | ||
|
|
bf06c418d4 | ||
|
|
937bec3d84 | ||
|
|
99eca2661f | ||
|
|
6b25288e2b | ||
|
|
92c51237c6 | ||
|
|
5bb277bf2f | ||
|
|
5f4c62644d | ||
|
|
41c0af16b3 | ||
|
|
a4c15653d8 | ||
|
|
42d284dce0 | ||
|
|
d084e6072b | ||
|
|
67d4f12cfe | ||
|
|
97d59dbb98 | ||
|
|
f35adfeb3a | ||
|
|
e5e4446366 | ||
|
|
e01c3ac205 | ||
|
|
e09e3a7260 | ||
|
|
7b43739dbc | ||
|
|
8e5bdebea0 | ||
|
|
555a6211bd | ||
|
|
6249ac6b2a | ||
|
|
494328cf53 | ||
|
|
e2e0a0295b | ||
|
|
676a7ef24e | ||
|
|
c01c9ce901 | ||
|
|
d37eb9a59b | ||
|
|
d4a0abf704 | ||
|
|
ec1a2afc5f | ||
|
|
3e6e43fd68 | ||
|
|
c37d2fd594 | ||
|
|
25b5928a22 | ||
|
|
88b62730e1 | ||
|
|
f00ccedbd3 | ||
|
|
de2fa3770b | ||
|
|
be06b1e0cf | ||
|
|
29f80f9a76 | ||
|
|
e85a9e9d6a | ||
|
|
54b224d929 | ||
|
|
d0e5bec845 | ||
|
|
b125e55a4a | ||
|
|
611d2c40bd | ||
|
|
8b1fd3ac79 | ||
|
|
9831038729 | ||
|
|
8bbffae837 | ||
|
|
b2ca072120 | ||
|
|
9aeb2177cf | ||
|
|
3dfdc300c5 | ||
|
|
4e65f0a955 | ||
|
|
dd9eaf49ff | ||
|
|
6b0f617d1b | ||
|
|
ba53a5c760 | ||
|
|
6958efcd0f | ||
|
|
85e8175338 | ||
|
|
a14b5e2be1 | ||
|
|
d8a6f67688 | ||
|
|
979a21afae | ||
|
|
8269d4946f | ||
|
|
a39881e73f | ||
|
|
cfa5030f85 | ||
|
|
c84d1181bb | ||
|
|
d92b17a386 | ||
|
|
9e857035bd | ||
|
|
79383fc83a | ||
|
|
0e0116b274 | ||
|
|
deba5325a7 | ||
|
|
a73d71b966 | ||
|
|
06ecfd93d5 | ||
|
|
ac34b47c26 | ||
|
|
9b16f2d84a | ||
|
|
cc594e3415 | ||
|
|
4d6fb4975e | ||
|
|
0f186576d3 | ||
|
|
837fdf78a0 | ||
|
|
6382302a30 | ||
|
|
da8943095b | ||
|
|
26da03f029 | ||
|
|
73dd745fe8 | ||
|
|
eca3e09482 | ||
|
|
d8d2676596 | ||
|
|
a6f6e0b05d | ||
|
|
e9622e6991 | ||
|
|
fcf24b3a45 | ||
|
|
77f81c1bde | ||
|
|
fc7a526a67 | ||
|
|
e71da620c6 | ||
|
|
0d6a6e163c | ||
|
|
1c5e43d378 | ||
|
|
d16d685b70 | ||
|
|
856a98d875 | ||
|
|
bc3ba23162 | ||
|
|
0e9ca3ff90 | ||
|
|
24d683332d | ||
|
|
ebc20be508 | ||
|
|
8bf7b3ee06 | ||
|
|
78f7f6b9cf | ||
|
|
bfb7abb843 | ||
|
|
70a93d9905 | ||
|
|
153d489400 | ||
|
|
40014755e2 | ||
|
|
acb9c60c9a | ||
|
|
934066fed8 | ||
|
|
e916deaafc | ||
|
|
1130581152 | ||
|
|
5c227c01e4 | ||
|
|
a6897ad2f8 | ||
|
|
5be5c2b59e | ||
|
|
9c8656f4b5 | ||
|
|
14658a5016 | ||
|
|
5f708470fc | ||
|
|
f2a0e75bb1 | ||
|
|
4f13c492f3 | ||
|
|
6123b6bafc | ||
|
|
2c142c506c | ||
|
|
9f1cbc0233 | ||
|
|
b06d1a5c2c | ||
|
|
beb32da3b4 | ||
|
|
2e82bf84d2 | ||
|
|
25ddb0ca99 | ||
|
|
02239b4f74 | ||
|
|
e0a3c44776 | ||
|
|
e513e01b29 | ||
|
|
138dbbc8a5 | ||
|
|
6f87870e09 | ||
|
|
2210c0c4c5 | ||
|
|
8ab8eee1f4 | ||
|
|
d77e459da3 | ||
|
|
6ee0d63766 | ||
|
|
0c47f262ea | ||
|
|
ffa4cf5b29 | ||
|
|
ca26955ac2 | ||
|
|
f05fe60030 | ||
|
|
2b15c13e88 | ||
|
|
df1ba841c5 | ||
|
|
47da18b717 | ||
|
|
117ab617b6 | ||
|
|
7e5659f49f | ||
|
|
e493acca0d | ||
|
|
6de6853cfe | ||
|
|
8ec6cc548d | ||
|
|
c0e9159c1e | ||
|
|
1bd752a60f | ||
|
|
df3694b539 | ||
|
|
6594d6fa42 | ||
|
|
8425eeb685 | ||
|
|
2daa724720 | ||
|
|
6db77ed4f2 | ||
|
|
b08d30d043 | ||
|
|
b3e8f5e50f | ||
|
|
1f5e2645f0 | ||
|
|
04e55b5498 | ||
|
|
32b4c451e4 | ||
|
|
05f6d68070 | ||
|
|
e572967c08 | ||
|
|
1dc8d50289 | ||
|
|
7caabc0fa8 | ||
|
|
d819bf60f5 | ||
|
|
b890d6504e | ||
|
|
33a870ef78 | ||
|
|
e109a6deed | ||
|
|
feb6ee25dd | ||
|
|
a8edf6c912 | ||
|
|
3cb02f2204 | ||
|
|
4b32811424 | ||
|
|
4972cc0faf | ||
|
|
65bfd93761 | ||
|
|
e092a69175 | ||
|
|
1c7acf6a08 | ||
|
|
3a98201837 | ||
|
|
012ebe578b | ||
|
|
e125cab690 | ||
|
|
a1e7bfaa02 | ||
|
|
73c1118601 | ||
|
|
33ec14f232 | ||
|
|
366f68f24f | ||
|
|
91f8fe8925 | ||
|
|
8431329645 | ||
|
|
19584feab0 | ||
|
|
bc71ae762c | ||
|
|
10805a1f70 | ||
|
|
a24e8594fb | ||
|
|
01a9744ba7 | ||
|
|
53e2af2116 | ||
|
|
4255933328 | ||
|
|
1856f0ef8f | ||
|
|
731f648cd2 | ||
|
|
6a2511866e | ||
|
|
b777262670 | ||
|
|
c950cdc757 | ||
|
|
541224f87b | ||
|
|
dcb8995b1a | ||
|
|
2ec4809c7f | ||
|
|
061449686d | ||
|
|
f3f8dacef9 | ||
|
|
a1f0f3d30d | ||
|
|
e4b8a8e908 | ||
|
|
41f415095a | ||
|
|
b824bc54c2 | ||
|
|
35aca18346 | ||
|
|
4b37a4fd70 | ||
|
|
c665062faa | ||
|
|
fca627c408 | ||
|
|
ea25776225 | ||
|
|
5781dfb69e | ||
|
|
60e3f62504 | ||
|
|
9cda84ef88 | ||
|
|
3495a7a40d | ||
|
|
8acc93c325 | ||
|
|
4755058fd0 | ||
|
|
306f8eb9b6 | ||
|
|
5fe9c23be2 | ||
|
|
68e262d96c | ||
|
|
8727d4f66f | ||
|
|
07b9ed4275 | ||
|
|
cb2274bbab | ||
|
|
7c293c405f | ||
|
|
6819c1b1f2 | ||
|
|
57864d2544 | ||
|
|
f9ed75d5af | ||
|
|
453690c575 | ||
|
|
557638b3fe | ||
|
|
f9584f09b5 | ||
|
|
6b49aa6069 | ||
|
|
56e70e22bb | ||
|
|
6f69144498 | ||
|
|
aa82655845 | ||
|
|
c456ade870 | ||
|
|
aa70ea0323 | ||
|
|
fbc5d43aab | ||
|
|
43778b01e7 | ||
|
|
f5701e7979 | ||
|
|
33c3132346 | ||
|
|
d6ca3d70cf | ||
|
|
392b5bbdda | ||
|
|
e810554695 | ||
|
|
8313bb4194 | ||
|
|
0c1b73d6ac | ||
|
|
7a74c36448 | ||
|
|
23abce304c | ||
|
|
e5e62e2936 | ||
|
|
c0a1e393b0 | ||
|
|
898a4fd8a4 | ||
|
|
3978169bf6 | ||
|
|
01e89db491 | ||
|
|
59f58f8506 | ||
|
|
c65bd88867 | ||
|
|
2eaed47ba4 | ||
|
|
888ff28121 | ||
|
|
baf47433dd | ||
|
|
89275a0e71 | ||
|
|
15fb7b8711 | ||
|
|
467a31fa3b | ||
|
|
5b414500a7 | ||
|
|
cac57f5cd8 | ||
|
|
3188a7b55f | ||
|
|
4ed2d0836f | ||
|
|
be77474637 | ||
|
|
82d3a8508d | ||
|
|
d070b303b4 | ||
|
|
7062ff74a4 | ||
|
|
df1f29c986 | ||
|
|
178c13ec72 | ||
|
|
e2fe765980 | ||
|
|
003ac1c931 | ||
|
|
0430d75989 | ||
|
|
59fb6c1bc9 | ||
|
|
12c5f6f468 | ||
|
|
c92ab37de1 | ||
|
|
88417cd5a6 | ||
|
|
583f8658d7 | ||
|
|
04a14393e5 | ||
|
|
f34b4827ee | ||
|
|
aac9aab3c9 | ||
|
|
349c32d0a6 | ||
|
|
5179e6e09b | ||
|
|
366a0f2040 | ||
|
|
93948a2ec5 | ||
|
|
3e24814518 | ||
|
|
726fe90bb0 | ||
|
|
fb2ab8f613 | ||
|
|
72e950713a | ||
|
|
ce90229a8b | ||
|
|
4bd32207db | ||
|
|
844a4c0e52 | ||
|
|
d858c85465 | ||
|
|
82d3441b22 | ||
|
|
c08773eb6f | ||
|
|
240024fb10 | ||
|
|
321f28d35c | ||
|
|
8e14e4a9f3 | ||
|
|
184e5e5c8d | ||
|
|
7b9b542e65 | ||
|
|
a3d35a7ad5 | ||
|
|
366bdf54a6 | ||
|
|
d22e90ca85 | ||
|
|
c290f845d6 | ||
|
|
8741c7479f | ||
|
|
62acd541b2 | ||
|
|
7db841b823 | ||
|
|
6cabfd3864 | ||
|
|
b044488308 | ||
|
|
a1d3be86f7 | ||
|
|
8378354f8b | ||
|
|
08f8297629 | ||
|
|
9b4c0bb09e | ||
|
|
92a1016a0f | ||
|
|
08c98bc8ee | ||
|
|
b7bf58a664 | ||
|
|
64ce4826c6 | ||
|
|
96bbe326a8 | ||
|
|
0f9a3e8cb6 | ||
|
|
9f8c643cf4 | ||
|
|
c2a1556e8a | ||
|
|
15246ea964 | ||
|
|
3b749025de | ||
|
|
bf2e1bd612 | ||
|
|
301c29e944 | ||
|
|
54b20c3131 | ||
|
|
55d19589b8 | ||
|
|
59ef124a26 | ||
|
|
47458e3ec2 | ||
|
|
4d08fb974d | ||
|
|
3f1e44d3e2 | ||
|
|
aa66a7644b | ||
|
|
3ff855afe8 | ||
|
|
3c088cd4e8 | ||
|
|
0694367ee9 | ||
|
|
4bda806b9b | ||
|
|
b37efc480a | ||
|
|
717e9c43be | ||
|
|
1f84cae1dd | ||
|
|
5bb15d47d7 | ||
|
|
16bd59e0cc | ||
|
|
d4c0c0535a | ||
|
|
eaa982e5b3 | ||
|
|
48552a97ab | ||
|
|
243429bf68 | ||
|
|
bc18ca174c | ||
|
|
1c08e5d9cf | ||
|
|
d3c5011694 | ||
|
|
c77cd92609 | ||
|
|
1ff81ab07c | ||
|
|
1c7660316d | ||
|
|
eeb39bb83f | ||
|
|
f99ff8c4b8 | ||
|
|
cf129ca3ce | ||
|
|
fd04e9f977 | ||
|
|
ccef11323b | ||
|
|
163e3d82b0 | ||
|
|
fc0902d476 | ||
|
|
0fd2944d8b | ||
|
|
7f36dc87cf | ||
|
|
34fe4c2654 | ||
|
|
392610b899 | ||
|
|
ead6e72668 | ||
|
|
db012cfa8c | ||
|
|
09176d1ae9 | ||
|
|
972a7f241e | ||
|
|
da1946df0f | ||
|
|
cc5be5261c | ||
|
|
af7b9f57b0 | ||
|
|
d9cd24026c | ||
|
|
f8a34aa98e | ||
|
|
13db19c64f | ||
|
|
04209cfc6d | ||
|
|
470498f63c | ||
|
|
484944bfc0 | ||
|
|
8620cbcf20 | ||
|
|
0286cb20f1 | ||
|
|
31ec17d279 | ||
|
|
6f578b31e0 | ||
|
|
0fd0a66137 | ||
|
|
ceedf3fbdd | ||
|
|
ed614d3938 | ||
|
|
d32f21f9ca | ||
|
|
e0150feda6 | ||
|
|
56ccc592a6 | ||
|
|
04e183f5e3 | ||
|
|
adc1273b08 | ||
|
|
93a72013b8 | ||
|
|
c5d28a9bbc | ||
|
|
7dc283f649 | ||
|
|
9645ad0877 | ||
|
|
45f325a0fa | ||
|
|
37652ce011 | ||
|
|
594b2269bd | ||
|
|
55a554ca06 | ||
|
|
a0198ae5f4 | ||
|
|
3bbd7f97da | ||
|
|
761da59ee9 | ||
|
|
09d2013012 | ||
|
|
4f0d045ff4 | ||
|
|
e2e23c430a | ||
|
|
9609c93f8c | ||
|
|
35dc835c62 | ||
|
|
808bbcc2fd | ||
|
|
c5e9762830 | ||
|
|
b13f3af03e | ||
|
|
000a3208c0 | ||
|
|
efd68bdb5f | ||
|
|
d0b7fa9ef5 | ||
|
|
9dcb0b3a15 | ||
|
|
2518e4730b | ||
|
|
74308da407 | ||
|
|
bb9e6a36dd | ||
|
|
c448207724 | ||
|
|
81335ad6dc | ||
|
|
d46a78b3cb | ||
|
|
492b7cd3c4 | ||
|
|
c19691033a | ||
|
|
164cdc234c | ||
|
|
394a2699d8 | ||
|
|
e50333a518 | ||
|
|
d848d316fe | ||
|
|
fa46aefa27 | ||
|
|
0912248fd5 | ||
|
|
fab71f9325 | ||
|
|
51eda12099 | ||
|
|
bf42973c00 | ||
|
|
431e984f03 | ||
|
|
e973e5e33b | ||
|
|
22d02378ef | ||
|
|
c17cf9c043 | ||
|
|
72c1dacd3f | ||
|
|
bc834245d7 |
10
.gitattributes
vendored
10
.gitattributes
vendored
@@ -1,4 +1,14 @@
|
||||
* text=input
|
||||
|
||||
*.php text eol=lf
|
||||
*.js text eol=lf
|
||||
*.css text eol=lf
|
||||
*.sql text eol=lf
|
||||
aowow text eol=lf
|
||||
prQueue text eol=lf
|
||||
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.gif binary
|
||||
*.ttf binary
|
||||
*.swf binary
|
||||
|
||||
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: issue template
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug and how to reproduce it**
|
||||
additionally paste relevant lines from db table `aowow_errors`
|
||||
or your browsers console here.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**System:**
|
||||
- OS: [e.g. Win10]
|
||||
- PHP version:
|
||||
- revision used:
|
||||
- Browser (in case of JavaScript / display errors):
|
||||
- AzerothCore: yes/no
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -3,15 +3,15 @@
|
||||
|
||||
# cache
|
||||
/cache/template/*
|
||||
/setup/generated/alphaMaps/*.png
|
||||
/cache/firstrun
|
||||
/cache/alphaMaps/*
|
||||
/cache/setup/*
|
||||
|
||||
# extract from MPQ
|
||||
/setup/mpqdata/*
|
||||
|
||||
# generated files
|
||||
/static/js/profile_all.js
|
||||
/static/js/locale.js
|
||||
/static/js/global.js
|
||||
/static/widgets/power.js
|
||||
/static/widgets/power/demo.html
|
||||
/static/widgets/searchbox.js
|
||||
@@ -49,4 +49,4 @@
|
||||
/static/uploads/screenshots/*
|
||||
/static/uploads/signatures/*
|
||||
/static/uploads/temp/*
|
||||
|
||||
/static/uploads/guide/images/*
|
||||
|
||||
60
README.md
60
README.md
@@ -13,30 +13,34 @@ While the first releases can be found as early as 2008, today it is impossible t
|
||||
This is a complete rewrite of the serverside php code and update to the clientside javascripts from 2008 to something 2013ish.
|
||||
|
||||
I myself take no credit for the clientside scripting, design and layout that these php-scripts cater to.
|
||||
Also, this project is not meant to be used for commercial puposes of any kind!
|
||||
Also, this project is not meant to be used for commercial purposes of any kind!
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
+ Webserver running PHP ≥ 5.5.0 including extensions:
|
||||
+ SimpleXML
|
||||
+ GD
|
||||
+ Mysqli
|
||||
+ mbString
|
||||
+ MySQL ≥ 5.5.30
|
||||
+ [TDB 335.63](https://github.com/TrinityCore/TrinityCore/releases/tag/TDB335.63) - including world updates up to 04.05.2017
|
||||
+ Webserver running PHP ≥ 8.2 including extensions:
|
||||
+ [SimpleXML](https://www.php.net/manual/en/book.simplexml.php)
|
||||
+ [GD](https://www.php.net/manual/en/book.image)
|
||||
+ [MySQL Improved](https://www.php.net/manual/en/book.mysqli.php)
|
||||
+ [Multibyte String](https://www.php.net/manual/en/book.mbstring.php)
|
||||
+ [File Information](https://www.php.net/manual/en/book.fileinfo.php)
|
||||
+ [Internationalization](https://www.php.net/manual/en/book.intl.php)
|
||||
+ [GNU Multiple Precision](https://www.php.net/manual/en/book.gmp.php) (When using TrinityCore as auth source)
|
||||
+ MySQL ≥ 5.7.0 OR MariaDB ≥ 10.6.4 OR similar
|
||||
+ [TDB 335.21101](https://github.com/TrinityCore/TrinityCore/releases/tag/TDB335.21101) (no other other providers are supported at this time)
|
||||
+ WIN: php.exe needs to be added to the `PATH` system variable, if it isn't already.
|
||||
+ Tools require cmake: Please refer to the individual repositories for detailed information
|
||||
+ [MPQExtractor](https://github.com/Sarjuuk/MPQExtractor) / [FFmpeg](https://ffmpeg.org/download.html) / [BLPConverter](https://github.com/Sarjuuk/BLPConverter) (optional)
|
||||
+ [MPQExtractor](https://github.com/Sarjuuk/MPQExtractor) / [FFmpeg](https://ffmpeg.org/download.html) / (optional: [BLPConverter](https://github.com/Sarjuuk/BLPConverter))
|
||||
+ WIN users may find it easier to use these alternatives
|
||||
+ [MPQEditor](http://www.zezula.net/en/mpq/download.html) / [FFmpeg](http://ffmpeg.zeranoe.com/builds/) / [BLPConverter](https://github.com/PatrickCyr/BLPConverter) (optional)
|
||||
+ [MPQEditor](http://www.zezula.net/en/mpq/download.html) / [FFmpeg](http://ffmpeg.zeranoe.com/builds/) / (optional: [BLPConverter](https://github.com/PatrickCyr/BLPConverter))
|
||||
|
||||
audio processing may require [lame](https://sourceforge.net/projects/lame/files/lame/3.99/) or [vorbis-tools](https://www.xiph.org/downloads/) (which may require libvorbis (which may require libogg))
|
||||
|
||||
|
||||
#### Highly Recommended
|
||||
+ setting the following configuration values on your TrintyCore server will greatly increase the accuracy of spawn points
|
||||
+ setting the following configuration values on your TrinityCore server will greatly increase the accuracy of spawn points
|
||||
> Calculate.Creature.Zone.Area.Data = 1
|
||||
> Calculate.Gameoject.Zone.Area.Data = 1
|
||||
> Calculate.Gameobject.Zone.Area.Data = 1
|
||||
|
||||
|
||||
## Install
|
||||
@@ -47,7 +51,7 @@ audio processing may require [lame](https://sourceforge.net/projects/lame/files/
|
||||
|
||||
#### 2. Prepare the database
|
||||
Ensure that the account you are going to use has **full** access on the database AoWoW is going to occupy and ideally only **read** access on the world database you are going to reference.
|
||||
Import `setup/db_structure.sql` into the AoWoW database `mysql -p {your-db-here} < setup/db_structure.sql`
|
||||
Import files 01 - 03 from `setup/sql/` in order into the AoWoW database `mysql -p {your-db-here} < setup/sql/01-db_structure.sql`, etc.
|
||||
|
||||
#### 3. Server created files
|
||||
See to it, that the web server is able to write the following directories and their children. If they are missing, the setup will create them with appropriate permissions
|
||||
@@ -69,19 +73,19 @@ Extract the following directories from the client archives into `setup/mpqdata/`
|
||||
|
||||
.. once is enough (still apply the localeCode though):
|
||||
> \<localeCode>/Interface/TalentFrame/
|
||||
> \<localeCode>/Interface/Glues/Credits/
|
||||
> \<localeCode>/Interface/Icons/
|
||||
> \<localeCode>/Interface/Spellbook/
|
||||
> \<localeCode>/Interface/PaperDoll/
|
||||
> \<localeCode>/Interface/GLUES/CHARACTERCREATE/
|
||||
> \<localeCode>/Interface/Glues/CharacterCreate/
|
||||
> \<localeCode>/Interface/Pictures
|
||||
> \<localeCode>/Interface/PvPRankBadges
|
||||
> \<localeCode>/Interface/FlavorImages
|
||||
> \<localeCode>/Interface/Calendar/Holidays/
|
||||
> \<localeCode>/Sound/
|
||||
|
||||
.. optionaly (not used in AoWoW):
|
||||
> \<localeCode>/Interface/GLUES/LOADINGSCREENS/
|
||||
.. optionally (not used in AoWoW):
|
||||
> \<localeCode>/Interface/Glues/Loadingscreens/
|
||||
> \<localeCode>/Interface/Glues/Credits/
|
||||
|
||||
#### 5. Reencode the audio files
|
||||
WAV-files need to be reencoded as `ogg/vorbis` and some MP3s may identify themselves as `application/octet-stream` instead of `audio/mpeg`.
|
||||
@@ -89,7 +93,7 @@ WAV-files need to be reencoded as `ogg/vorbis` and some MP3s may identify themse
|
||||
* [example for \*nix](https://gist.github.com/Sarjuuk/1f05ef2affe49a7e7ca0fad7b01c081d)
|
||||
|
||||
#### 6. Run the initial setup from the CLI
|
||||
`php aowow --firstrun`.
|
||||
`php aowow --setup`.
|
||||
This should guide you through with minimal input required from your end, but will take some time though, especially compiling the zone-images. Use it to familiarize yourself with the other functions this setup has. Yes, I'm dead serious: *Go read the code!* It will help you understand how to configure AoWoW and keep it in sync with your world database.
|
||||
When you've created your admin account you are done.
|
||||
|
||||
@@ -97,35 +101,41 @@ When you've created your admin account you are done.
|
||||
## Troubleshooting
|
||||
|
||||
Q: The Page appears white, without any styles.
|
||||
A: The static content is not being displayed. You are either using SSL and AoWoW is unable to detect it or STATIC_HOST is not defined poperly. Either way this can be fixed via config `php aowow --siteconfig`
|
||||
A: The static content is not being displayed. You are either using SSL and AoWoW is unable to detect it or STATIC_HOST is not defined properly. Either way this can be fixed via config `php aowow --siteconfig`
|
||||
|
||||
Q: Fatal error: Can't inherit abstract function \<functionName> (previously declared abstract in \<className>) in \<path>
|
||||
A: You are using cache optimization modules for php, that are in confict with each other. (Zend OPcache, XCache, ..) Disable all but one.
|
||||
A: You are using cache optimization modules for php, that are in conflict with each other. (Zend OPcache, XCache, ..) Disable all but one.
|
||||
|
||||
Q: Some generated images appear distorted or have alpha-channel issues.
|
||||
A: Image compression is beyond my understanding, so i am unable to fix these issues within the blpReader.
|
||||
BUT you can convert the affected blp file into a png file in the same directory, using the provided BLPConverter.
|
||||
AoWoW will priorize png files over blp files.
|
||||
AoWoW will prioritize png files over blp files.
|
||||
|
||||
Q: How can i get the modelviewer to work?
|
||||
A: You can't anymore. Wowhead switched from Flash to WebGL (as they should) and moved or deleted the old files in the process.
|
||||
|
||||
Q: I'm getting random javascript errors!
|
||||
A: Some server configurations or external services (like Cloudflare) come with modules, that automaticly minify js and css files. Sometimes they break in the process. Disable the module in this case.
|
||||
A: Some server configurations or external services (like Cloudflare) come with modules, that automatically minify js and css files. Sometimes they break in the process. Disable the module in this case.
|
||||
|
||||
Q: Some search results within the profiler act rather strange. How does it work?
|
||||
A: Whenever you try to view a new character, AoWoW needs to fetch it first. Since the data is structured for the needs of TrinityCore and not for easy viewing, AoWoW needs to save and restructure it locally. To this end, every char request is placed in a queue. While the queue is not empty, a single instance of `prQueue` is run in the background as not to overwhelm the characters database with requests. This also means, some more exotic search queries can't be run agains the characters database and have to use the incomplete/outdated cached profiles of AoWoW.
|
||||
A: Whenever you try to view a new character, AoWoW needs to fetch it first. Since the data is structured for the needs of TrinityCore and not for easy viewing, AoWoW needs to save and restructure it locally. To this end, every char request is placed in a queue. While the queue is not empty, a single instance of `prQueue` is run in the background as not to overwhelm the characters database with requests. This also means, some more exotic search queries can't be run against the characters database and have to use the incomplete/outdated cached profiles of AoWoW.
|
||||
|
||||
Q: Screenshot upload fails, because the file size is too large and/or the subdirectories are visible from the web!
|
||||
A: That's a web server configuration issue. If you are using Apache you may need to [enable the use of .htaccess](http://httpd.apache.org/docs/2.4/de/mod/core.html#allowoverride). Other servers require individual configuration.
|
||||
|
||||
Q: An Item, Quest or NPC i added or edited can't be searched. Why?
|
||||
A: A search is only conducted against the currently used locale. You may have only edited the name field in the base table instead of adding multiple strings into the appropriate \*_locale tables. In this case searches in a non-english locale are run against an empty name field.
|
||||
|
||||
## Thanks
|
||||
|
||||
@mix: for providing the php-script to parse .blp and .dbc into usable images and tables
|
||||
@LordJZ: the wrapper-class for DBSimple; the basic idea for the user-class
|
||||
@kliver: basic implementation of screenshot uploads
|
||||
@Sarjuuk: maintainer of the project
|
||||
|
||||
|
||||
## Special Thanks
|
||||
Said website with the red smiling rocket, for providing this beautifull website!
|
||||
Please do not reagard this project as blatant rip-off, rather as "We do really liked your presentation, but since time and content progresses, you are sadly no longer supplying the data we need".
|
||||
Said website with the red smiling rocket, for providing this beautiful website!
|
||||
Please do not regard this project as blatant rip-off, rather as "We do really liked your presentation, but since time and content progresses, you are sadly no longer supplying the data we need".
|
||||
|
||||

|
||||
|
||||
17
aowow
17
aowow
@@ -1,12 +1,15 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
require 'includes/shared.php';
|
||||
|
||||
if (!CLI)
|
||||
if (PHP_SAPI !== 'cli')
|
||||
die("this script must be run from CLI\n");
|
||||
if (CLI && getcwd().DIRECTORY_SEPARATOR.'aowow' != __FILE__)
|
||||
die("this script must be run from root directory\n");
|
||||
else
|
||||
require 'setup/setup.php';
|
||||
if (PHP_SAPI === 'cli' && getcwd().DIRECTORY_SEPARATOR.'aowow' != __FILE__)
|
||||
die("this script must be run from the aowow root directory\n");
|
||||
|
||||
require_once 'includes/kernel.php';
|
||||
require_once 'includes/setup/cli.class.php';
|
||||
require_once 'includes/setup/timer.class.php';
|
||||
|
||||
require_once 'setup/setup.php';
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// use as example. File is generated from setup.
|
||||
|
||||
// -- Aowow Database --
|
||||
// contains world-data, user-data and logs
|
||||
$AoWoWconf['aowow'] = array(
|
||||
'host' => '127.0.0.1',
|
||||
'user' => '<user>',
|
||||
'pass' => '<pass>',
|
||||
'db' => 'world',
|
||||
'prefix' => 'aowow_'
|
||||
);
|
||||
|
||||
// -- World Database --
|
||||
// used to generate data-tables
|
||||
$AoWoWconf['world'] = array(
|
||||
'host' => '127.0.0.1',
|
||||
'user' => '<user>',
|
||||
'pass' => '<pass>',
|
||||
'db' => 'world',
|
||||
'prefix' => ''
|
||||
);
|
||||
|
||||
// -- Auth Database --
|
||||
// used to generate user-tables
|
||||
$AoWoWconf['auth'] = array(
|
||||
'host' => '127.0.0.1',
|
||||
'user' => '<user>',
|
||||
'pass' => '<pass>',
|
||||
'db' => 'auth',
|
||||
'prefix' => ''
|
||||
);
|
||||
|
||||
// -- Characters Database --
|
||||
// used to display profiles
|
||||
$AoWoWconf['characters'][<realmId>] = array(
|
||||
'host' => '127.0.0.1',
|
||||
'user' => '<user>',
|
||||
'pass' => '<pass>',
|
||||
'db' => 'characters',
|
||||
'prefix' => ''
|
||||
);
|
||||
|
||||
?>
|
||||
@@ -4,13 +4,16 @@ if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
function extAuth($user, $pass, &$userId = 0, &$userGroup = -1)
|
||||
function extAuth(string &$usernameOrEmail, #[\SensitiveParameter] string $password, int &$userId = 0, int &$userGroup = -1) : int
|
||||
{
|
||||
/*
|
||||
insert some auth mechanism here
|
||||
|
||||
see defines for usable return values
|
||||
set userId for identification
|
||||
set usernameOrEmail to a valid username, do not pass back the email if used for login
|
||||
set userId to uid from external auth provider for identification
|
||||
(optional) set userGroup to a valid userGroup (see U_GROUP_* defines)
|
||||
|
||||
return an AUTH_* result (see defines)
|
||||
*/
|
||||
|
||||
return AUTH_INTERNAL_ERR;
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
Mapper.multiLevelZones = {
|
||||
206: ['206-1', '206-2', '206-3'],
|
||||
209: ['209-1', '209-2', '209-3', '209-4', '209-5', '209-6', '209-7'],
|
||||
616: ['616-1', '616_1', '616_2'],
|
||||
719: ['719-1', '719-2', '719-3'],
|
||||
721: ['721-1', '721-2', '721-3', '721-4'],
|
||||
796: ['796-1', '796-2', '796-3', '796-4'],
|
||||
1196: ['1196-1', '1196-2'],
|
||||
1337: ['1337-1', '1337-2'],
|
||||
1581: ['1581-1', '1581-2'],
|
||||
1583: ['1583-1', '1583-2', '1583-3', '1583-4', '1583-5', '1583-6', '1583-7'],
|
||||
1584: ['1584-1', '1584-2'],
|
||||
2017: ['2017-1', '2017-2'],
|
||||
2057: ['2057-1', '2057-2', '2057-3', '2057-4'],
|
||||
2100: ['2100-1', '2100-2'],
|
||||
2557: ['2557-1', '2557-2', '2557-3', '2557-4', '2557-5', '2557-6'],
|
||||
2677: ['2677-1', '2677-2', '2677-3', '2677-4'],
|
||||
3959: ['3959', '3959-1', '3959-2', '3959-3', '3959-4', '3959-5', '3959-6', '3959-7'],
|
||||
3428: ['3428-1', '3428-2', '3428-3'],
|
||||
3456: ['3456-1', '3456-2', '3456-3', '3456-4', '3456-5', '3456-6'],
|
||||
3457: ['3457-1', '3457-2', '3457-3', '3457-4', '3457-5', '3457-6', '3457-7', '3457-8', '3457-9', '3457-10', '3457-11', '3457-12', '3457-13', '3457-14', '3457-15', '3457-16', '3457-17'],
|
||||
3477: ['3477-1', '3477-2', '3477-3'],
|
||||
3715: ['3715-1', '3715-2'],
|
||||
3790: ['3790-1', '3790-2'],
|
||||
3791: ['3791-1', '3791-2'],
|
||||
3848: ['3848-1', '3848-2', '3848-3'],
|
||||
3849: ['3849-1', '3849-2'],
|
||||
4075: ['4075', '4075-1'],
|
||||
4100: ['4100-1', '4100-2'],
|
||||
4131: ['4131-1', '4131-2'],
|
||||
4196: ['4196-1', '4196-2'],
|
||||
4228: ['4228-1', '4228-2', '4228-3', '4228-4'],
|
||||
4272: ['4272-1', '4272-2'],
|
||||
4273: ['4273-0', '4273-1', '4273-2', '4273-3', '4273-4', '4273-5'],
|
||||
4277: ['4277-1', '4277-2', '4277-3'],
|
||||
4395: ['4395-1', '4395-2'],
|
||||
4494: ['4494-1', '4494-2'],
|
||||
4714: ['4714-1', '4714_1', '4714_2', '4714_3'],
|
||||
4722: ['4722-1', '4722-2'],
|
||||
4812: ['4812-1', '4812-2', '4812-3', '4812-4', '4812-5', '4812-6', '4812-7', '4812-8'],
|
||||
};
|
||||
|
||||
/*
|
||||
var g_zone_areas = {};
|
||||
in locale files
|
||||
*/
|
||||
34
endpoints/aboutus/aboutus.php
Normal file
34
endpoints/aboutus/aboutus.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AboutusBaseResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'aboutus';
|
||||
protected ?int $activeTab = parent::TAB_MORE;
|
||||
protected array $breadcrumb = [2, 0];
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
parent::__construct($pageParam);
|
||||
|
||||
if ($pageParam)
|
||||
$this->generateError();
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = Lang::main('moreTitles', $this->pageName);
|
||||
|
||||
array_unshift($this->title, $this->h1);
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
177
endpoints/account/account.php
Normal file
177
endpoints/account/account.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AccountBaseResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'account';
|
||||
protected string $pageName = 'account';
|
||||
|
||||
protected array $scripts = [[SC_JS_FILE, 'js/account.js']];
|
||||
|
||||
// display status of executed step (forwarding back to this page)
|
||||
public ?array $generalMessage = null;
|
||||
public ?array $emailMessage = null;
|
||||
public ?array $usernameMessage = null;
|
||||
public ?array $passwordMessage = null;
|
||||
public ?array $communityMessage = null;
|
||||
public ?array $avatarMessage = null;
|
||||
public ?array $premiumborderMessage = null;
|
||||
|
||||
// form fields
|
||||
public int $modelrace = 0;
|
||||
public int $modelgender = 0;
|
||||
public int $idsInLists = 0;
|
||||
public string $curEmail = '';
|
||||
public string $curName = '';
|
||||
public string $renameCD = '';
|
||||
public string $activeCD = '';
|
||||
public array $description = [];
|
||||
public array $signature = [];
|
||||
public int $avMode = 0;
|
||||
public string $wowicon = '';
|
||||
public int $customicon = 0;
|
||||
public array $customicons = [];
|
||||
public bool $premium = false;
|
||||
public int $reputation = 0;
|
||||
public ?Listview $avatarManager = null;
|
||||
|
||||
public ?array $bans;
|
||||
|
||||
public function __construct($pageParam)
|
||||
{
|
||||
if (!User::isLoggedIn())
|
||||
$this->forwardToSignIn('account');
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
array_unshift($this->title, Lang::account('settings'));
|
||||
|
||||
$user = DB::Aowow()->selectRow('SELECT `debug`, `email`, `description`, `avatar`, `wowicon`, `renameCooldown` FROM ?_account WHERE `id` = ?d', User::$id);
|
||||
|
||||
Lang::sort('game', 'ra');
|
||||
|
||||
parent::generate();
|
||||
|
||||
|
||||
/*************/
|
||||
/* Ban Popup */
|
||||
/*************/
|
||||
|
||||
$b = DB::Aowow()->select(
|
||||
'SELECT ab.`end` AS "0", ab.`reason` AS "1", a.`username` AS "2"
|
||||
FROM ?_account_banned ab
|
||||
LEFT JOIN ?_account a ON a.`id` = ab.`staffId`
|
||||
WHERE ab.`userId` = ?d AND ab.`typeMask` & ?d AND (ab.`end` = 0 OR ab.`end` > UNIX_TIMESTAMP())',
|
||||
User::$id, ACC_BAN_TEMP | ACC_BAN_PERM
|
||||
);
|
||||
|
||||
$this->bans = $b ?: null;
|
||||
|
||||
|
||||
/*******************/
|
||||
/* Status Messages */
|
||||
/*******************/
|
||||
|
||||
if (isset($_SESSION['msg']))
|
||||
{
|
||||
[$var, $status, $msg] = $_SESSION['msg'];
|
||||
if (property_exists($this, $var.'Message'))
|
||||
$this->{$var.'Message'} = [$status, $msg];
|
||||
else
|
||||
trigger_error('AccountBaseResponse::generate - unknown var in $_SESSION msg: '.$var, E_USER_WARNING);
|
||||
|
||||
unset($_SESSION['msg']);
|
||||
}
|
||||
|
||||
|
||||
/*************/
|
||||
/* Form Data */
|
||||
/*************/
|
||||
|
||||
/* GENERAL */
|
||||
|
||||
// Modelviewer
|
||||
if ($_ = DB::Aowow()->selectCell('SELECT `data` FROM ?_account_cookies WHERE `name` = ? AND `userId` = ?d', 'default_3dmodel', User::$id))
|
||||
[$this->modelrace, $this->modelgender] = explode(',', $_);
|
||||
|
||||
// Lists
|
||||
$this->idsInLists = $user['debug'] ? 1 : 0;
|
||||
|
||||
/* PERSONAL */
|
||||
|
||||
// Email address
|
||||
$this->curEmail = $user['email'] ?? '';
|
||||
|
||||
// Username
|
||||
$this->curName = User::$username;
|
||||
$this->renameCD = DateTime::formatTimeElapsedFloat(Cfg::get('ACC_RENAME_DECAY') * 1000);
|
||||
if ($user['renameCooldown'] > time())
|
||||
{
|
||||
$locCode = implode('_', str_split(Lang::getLocale()->json(), 2)); // ._.
|
||||
$this->activeCD = (new \IntlDateFormatter($locCode, pattern: Lang::main('dateFmtIntl')))->format($user['renameCooldown']);
|
||||
}
|
||||
|
||||
/* COMMUNITY */
|
||||
|
||||
// Public Description
|
||||
$this->description = ['body' => $user['description']];
|
||||
|
||||
// Forum Signature
|
||||
// $this->signature = ['body' => $user['signature']];
|
||||
|
||||
// Avatar
|
||||
$this->wowicon = $user['wowicon'];
|
||||
$this->avMode = $user['avatar'];
|
||||
|
||||
// status [reviewing, ok, rejected]? (only 2: rejected processed in js)
|
||||
if (User::isPremium() && ($cuAvatars = DB::Aowow()->select('SELECT `id`, `name`, `current`, `size`, `status`, `when` FROM ?_account_avatars WHERE `userId` = ?d', User::$id)))
|
||||
{
|
||||
array_walk($cuAvatars, function (&$x) {
|
||||
$x['when'] *= 1000; // uploaded timestamp expected as msec for some reason
|
||||
$x['caption'] = $x['name']; // only used for getVisibleText, duplicates name?
|
||||
$x['type'] = 1; // always 1 ?, Dialog-popup doesn't work without it
|
||||
});
|
||||
|
||||
foreach ($cuAvatars as $a)
|
||||
if ($a['status'] != AvatarMgr::STATUS_REJECTED)
|
||||
$this->customicons[$a['id']] = $a['name'];
|
||||
|
||||
// TODO - replace with array_find in PHP 8.4
|
||||
if ($x = array_filter($cuAvatars, fn($x) => $x['current'] > 0 ))
|
||||
$this->customicon = array_pop($x)['id'];
|
||||
}
|
||||
|
||||
/* PREMIUM */
|
||||
|
||||
$this->premium = User::isPremium();
|
||||
|
||||
if (!$this->premium)
|
||||
return;
|
||||
|
||||
$this->reputation = User::getReputation();
|
||||
|
||||
// Avatar Manager
|
||||
$this->avatarManager = new Listview([
|
||||
'template' => 'avatar',
|
||||
'id' => 'avatar',
|
||||
'name' => '$LANG.tab_avatars',
|
||||
'parent' => 'avatar-manage',
|
||||
'hideNav' => 1 | 2, // top | bottom
|
||||
'data' => $cuAvatars ?? [],
|
||||
'note' => Lang::account('avatarSlots', [count($this->customicons), Cfg::get('acc_max_avatar_uploads')])
|
||||
]);
|
||||
|
||||
// Premium Border Selector
|
||||
// solved by js
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
73
endpoints/account/activate.php
Normal file
73
endpoints/account/activate.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via activation email link
|
||||
* empty page with status box
|
||||
*/
|
||||
|
||||
class AccountActivateResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'activate';
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if (!Cfg::get('ACC_ALLOW_REGISTER') || Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
$this->generateError();
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->title[] = Lang::account('title');
|
||||
|
||||
$msg = $this->activate();
|
||||
|
||||
if ($this->success)
|
||||
$this->inputbox = ['inputbox-status', ['head' => Lang::account('inputbox', 'head', 'register', [2]), 'message' => $msg]];
|
||||
else
|
||||
{
|
||||
$_SESSION['error']['activate'] = $msg;
|
||||
$this->forward('?account=resend');
|
||||
}
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
|
||||
private function activate() : string
|
||||
{
|
||||
if (!$this->assertGET('key'))
|
||||
return Lang::main('intError');
|
||||
|
||||
if (DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE `status` IN (?a) AND `token` = ?', [ACC_STATUS_NONE, ACC_STATUS_NEW], $this->_get['key']))
|
||||
{
|
||||
// don't remove the token yet. It's needed on signin page.
|
||||
DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = 0, `userGroups` = ?d WHERE `token` = ?', ACC_STATUS_NONE, U_GROUP_NONE, $this->_get['key']);
|
||||
|
||||
// fully apply block for further registration attempts from this ip
|
||||
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d + 1, UNIX_TIMESTAMP() + ?d)',
|
||||
User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'accActivated', [$this->_get['key']]);
|
||||
}
|
||||
|
||||
// grace period expired and other user claimed name
|
||||
return Lang::main('intError');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
128
endpoints/account/confirm-delete.php
Normal file
128
endpoints/account/confirm-delete.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// custom handler
|
||||
class AccountConfirmdeleteResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'delete';
|
||||
protected string $pageName = 'confirm-delete';
|
||||
|
||||
protected array $scripts = array(
|
||||
[SC_CSS_FILE, 'css/delete.css'],
|
||||
[SC_CSS_STRING, '[type="submit"] { margin: 0px 10px; }']
|
||||
);
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => [FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
|
||||
);
|
||||
protected array $expectedPOST = array(
|
||||
'submit' => [FILTER_UNSAFE_RAW ],
|
||||
'cancel' => [FILTER_UNSAFE_RAW ],
|
||||
'confirm' => [FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ],
|
||||
'key' => [FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
|
||||
);
|
||||
|
||||
public bool $confirm = true; // just to select the correct localized brick
|
||||
public string $username = '';
|
||||
public string $deleteFormTarget = '?account=confirm-delete';
|
||||
public ?array $inputbox = null;
|
||||
public string $key = '';
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
$this->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
array_unshift($this->title, Lang::account('accDelete'));
|
||||
|
||||
$this->username = User::$username;
|
||||
|
||||
parent::generate();
|
||||
|
||||
$msg = Lang::account('inputbox', 'error', 'purgeTokenUsed');
|
||||
|
||||
// display default confirm template
|
||||
if ($this->assertGET('key') && DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP() AND `token` = ?', ACC_STATUS_PURGING, $this->_get['key']))
|
||||
{
|
||||
$this->key = $this->_get['key'];
|
||||
return;
|
||||
}
|
||||
|
||||
// perform action and display status
|
||||
if ($this->assertPOST('key') && ($userId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP() AND `token` = ?', ACC_STATUS_PURGING, $this->_post['key'])))
|
||||
{
|
||||
if ($this->_post['cancel'])
|
||||
$msg = $this->cancel($userId);
|
||||
else if ($this->_post['submit'] && $this->_post['confirm'])
|
||||
$msg = $this->purge($userId);
|
||||
}
|
||||
|
||||
// throw error and display in status
|
||||
$this->inputbox = ['inputbox-status', array(
|
||||
'head' => Lang::account('inputbox', 'head', $this->success ? 'success' : 'error'),
|
||||
'message' => $this->success ? $msg : '',
|
||||
'error' => $this->success ? '' : $msg
|
||||
)];
|
||||
}
|
||||
|
||||
private function cancel(int $userId) : string
|
||||
{
|
||||
if (DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = 0, `token` = "" WHERE `id` = ?d', ACC_STATUS_NONE, $userId))
|
||||
{
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'deleteCancel');
|
||||
}
|
||||
|
||||
return Lang::main('intError');
|
||||
}
|
||||
|
||||
private function purge(int $userId) : string
|
||||
{
|
||||
// empty all user settings and cookies
|
||||
DB::Aowow()->query('DELETE FROM ?_account_cookies WHERE `userId` = ?d', $userId);
|
||||
DB::Aowow()->query('DELETE FROM ?_account_avatars WHERE `userId` = ?d', $userId);
|
||||
DB::Aowow()->query('DELETE FROM ?_account_excludes WHERE `userId` = ?d', $userId);
|
||||
DB::Aowow()->query('DELETE FROM ?_account_favorites WHERE `userId` = ?d', $userId);
|
||||
DB::Aowow()->query('DELETE FROM ?_account_reputation WHERE `userId` = ?d', $userId);
|
||||
DB::Aowow()->query('DELETE FROM ?_account_weightscales WHERE `userId` = ?d', $userId); // cascades to aowow_account_weightscale_data
|
||||
|
||||
// delete profiles, unlink chars
|
||||
DB::Aowow()->query('DELETE pp FROM ?_profiler_profiles pp JOIN ?_account_profiles ap ON ap.`profileId` = pp.`id` WHERE ap.`accountId` = ?d', $userId);
|
||||
// DB::Aowow()->query('DELETE FROM ?_account_profiles WHERE `accountId` = ?d', $userId); // already deleted via FK?
|
||||
|
||||
// delete all sessions and bans
|
||||
DB::Aowow()->query('DELETE FROM ?_account_banned WHERE `userId` = ?d', $userId);
|
||||
DB::Aowow()->query('DELETE FROM ?_account_sessions WHERE `userId` = ?d', $userId);
|
||||
|
||||
// delete forum posts (msg: This post was from a user who has deleted their account. (no translations at src); comments/replies are unaffected)
|
||||
// ...
|
||||
|
||||
// replace username with userId and empty fields
|
||||
DB::Aowow()->query(
|
||||
'UPDATE ?_account SET
|
||||
`login` = "", `passHash` = "", `username` = `id`, `email` = NULL, `userGroups` = 0, `userPerms` = 0,
|
||||
`curIp` = "", `prevIp` = "", `curLogin` = 0, `prevLogin` = 0,
|
||||
`locale` = 0, `debug` = 0, `avatar` = 0, `wowicon` = "", `title` = "", `description` = "", `excludeGroups` = 0,
|
||||
`status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "", `renameCooldown` = 0
|
||||
WHERE `id` = ?d',
|
||||
ACC_STATUS_DELETED, $userId
|
||||
);
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'deleteOk');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
62
endpoints/account/confirm-email-address.php
Normal file
62
endpoints/account/confirm-email-address.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via confirmation email link
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
// ?auth=email-change
|
||||
class AccountConfirmemailaddressResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'confirm-email-address';
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
parent::generate();
|
||||
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
$msg = $this->change();
|
||||
|
||||
$this->inputbox = ['inputbox-status', array(
|
||||
'head' => Lang::account('inputbox', 'head', $this->success ? 'success' : 'error'),
|
||||
'message' => $this->success ? $msg : '',
|
||||
'error' => $this->success ? '' : $msg,
|
||||
)];
|
||||
}
|
||||
|
||||
// this should probably leave change info intact for revert
|
||||
// todo - move personal settings changes to separate table
|
||||
private function change() : string
|
||||
{
|
||||
if (!$this->assertGET('key'))
|
||||
return Lang::main('intError');
|
||||
|
||||
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']);
|
||||
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_EMAIL || $acc['statusTimer'] < time())
|
||||
return Lang::account('inputbox', 'error', 'mailTokenUsed');
|
||||
|
||||
// 0 changes == error
|
||||
if (!DB::Aowow()->query('UPDATE ?_account SET `email` = `updateValue`, `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key']))
|
||||
return Lang::main('intError');
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'mailChangeOk');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
60
endpoints/account/confirm-password.php
Normal file
60
endpoints/account/confirm-password.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via confirmation email link
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
// 2025 - no longer in use?
|
||||
class AccountConfirmpasswordResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'confirm-password';
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
parent::generate();
|
||||
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
$msg = $this->confirm();
|
||||
|
||||
$this->inputbox = ['inputbox-status', array(
|
||||
'head' => Lang::account('inputbox', 'head', $this->success ? 'success' : 'error'),
|
||||
'message' => $this->success ? $msg : '',
|
||||
'error' => $this->success ? '' : $msg,
|
||||
)];
|
||||
}
|
||||
|
||||
private function confirm() : string
|
||||
{
|
||||
if (!$this->assertGET('key'))
|
||||
return Lang::main('intError');
|
||||
|
||||
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']);
|
||||
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_PASS || $acc['statusTimer'] < time())
|
||||
return Lang::account('inputbox', 'error', 'passTokenUsed');
|
||||
|
||||
// 0 changes == error
|
||||
if (!DB::Aowow()->query('UPDATE ?_account SET `passHash` = `updateValue`, `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key']))
|
||||
return Lang::main('intError');
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'passChangeOk');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
47
endpoints/account/delete-icon.php
Normal file
47
endpoints/account/delete-icon.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via form button on user settings page
|
||||
*/
|
||||
|
||||
class AccountDeleteiconResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
protected int $requiredUserGroup = U_GROUP_PREMIUM_PERMISSIONS;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
|
||||
/*
|
||||
* response not evaluated
|
||||
*/
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned() || !$this->assertPOST('id'))
|
||||
return;
|
||||
|
||||
// non-int > error
|
||||
$selected = DB::Aowow()->selectCell('SELECT `current` FROM ?_account_avatars WHERE `id` = ?d AND `userId` = ?d', $this->_post['id'], User::$id);
|
||||
if ($selected === null || $selected === false)
|
||||
return;
|
||||
|
||||
DB::Aowow()->query('DELETE FROM ?_account_avatars WHERE `id` = ?d AND `userId` = ?d', $this->_post['id'], User::$id);
|
||||
|
||||
// if deleted avatar is also currently selected, unset
|
||||
if ($selected)
|
||||
DB::Aowow()->query('UPDATE ?_account SET `avatar` = 0 WHERE `id` = ?d', User::$id);
|
||||
|
||||
$path = sprintf('static/uploads/avatars/%d.jpg', $this->_post['id']);
|
||||
if (!unlink($path))
|
||||
trigger_error('AccountDeleteiconResponse - failed to delete file: '.$path, E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
71
endpoints/account/delete.php
Normal file
71
endpoints/account/delete.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via account settings link
|
||||
* empty page with status box
|
||||
*/
|
||||
|
||||
class AccountDeleteResponse extends TemplateResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected string $template = 'delete';
|
||||
protected string $pageName = 'delete';
|
||||
|
||||
protected array $scripts = [[SC_CSS_FILE, 'css/delete.css']];
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'proceed' => ['filter' => FILTER_UNSAFE_RAW]
|
||||
);
|
||||
|
||||
public string $username = '';
|
||||
public string $deleteFormTarget = '?account=delete';
|
||||
public ?array $inputbox = null;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
$this->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
array_unshift($this->title, Lang::account('accDelete'));
|
||||
|
||||
parent::generate();
|
||||
|
||||
$this->username = User::$username;
|
||||
|
||||
if ($this->_post['proceed'])
|
||||
{
|
||||
$error = false;
|
||||
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `status` NOT IN (?a) AND `statusTimer` > UNIX_TIMESTAMP() AND `id` = ?d', [ACC_STATUS_NEW, ACC_STATUS_NONE, ACC_STATUS_PURGING], User::$id))
|
||||
{
|
||||
$token = Util::createHash(40);
|
||||
|
||||
DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d, `token` = ? WHERE `id` = ?d',
|
||||
ACC_STATUS_PURGING, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id);
|
||||
|
||||
Util::sendMail(User::$email, 'delete-account', [$token, User::$email, User::$username]);
|
||||
}
|
||||
else
|
||||
$error = true;
|
||||
|
||||
$this->inputbox = ['inputbox-status', array(
|
||||
'head' => Lang::account('inputbox', 'head', $error ? 'error' : 'success'),
|
||||
'message' => $error ? '' : Lang::account('inputbox', 'message', 'deleteAccSent', [User::$email]),
|
||||
'error' => $error ? Lang::account('inputbox', 'error', 'isRecovering') : ''
|
||||
)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
76
endpoints/account/exclude.php
Normal file
76
endpoints/account/exclude.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed from character profiles, when setting exclusions on collections
|
||||
* always returns emptry string
|
||||
*/
|
||||
|
||||
class AccountExcludeResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'mode' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]],
|
||||
'reset' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]],
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdList'] ],
|
||||
'type' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'groups' => ['filter' => FILTER_VALIDATE_INT ]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if ($this->_post['mode'] == 1) // directly set exludes
|
||||
$this->excludeById();
|
||||
|
||||
else if ($this->_post['reset'] == 1) // defaults to unavailable
|
||||
$this->resetExcludes();
|
||||
|
||||
else if ($this->_post['groups']) // exclude by group mask
|
||||
$this->updateGroups();
|
||||
}
|
||||
|
||||
private function excludeById() : void
|
||||
{
|
||||
if (!$this->assertPOST('type', 'id'))
|
||||
return;
|
||||
|
||||
if ($validIds = Type::validateIds($this->_post['type'], $this->_post['id']))
|
||||
{
|
||||
// ready for some bullshit? here it comes!
|
||||
// we don't get signaled whether an id should be added to or removed from either includes or excludes
|
||||
// so we throw everything into one table and toggle the mode if its already in here
|
||||
|
||||
$includes = DB::Aowow()->selectCol('SELECT `typeId` FROM ?_profiler_excludes WHERE `type` = ?d AND `typeId` IN (?a)', $this->_post['type'], $validIds);
|
||||
|
||||
foreach ($validIds as $typeId)
|
||||
DB::Aowow()->query('INSERT INTO ?_account_excludes (`userId`, `type`, `typeId`, `mode`) VALUES (?a) ON DUPLICATE KEY UPDATE `mode` = (`mode` ^ 0x3)',
|
||||
[User::$id, $this->_post['type'], $typeId, in_array($typeId, $includes) ? 2 : 1]
|
||||
);
|
||||
}
|
||||
else
|
||||
trigger_error('AccountExcludeResponse::excludeById - validation failed [type: '.$this->_post['type'].', typeId: '.implode(',', $this->_post['id']).']', E_USER_NOTICE);
|
||||
}
|
||||
|
||||
private function resetExcludes() : void
|
||||
{
|
||||
DB::Aowow()->query('DELETE FROM ?_account_excludes WHERE `userId` = ?d', User::$id);
|
||||
DB::Aowow()->query('UPDATE ?_account SET `excludeGroups` = ?d WHERE `id` = ?d', PR_EXCLUDE_GROUP_UNAVAILABLE, User::$id);
|
||||
}
|
||||
|
||||
private function updateGroups() : void
|
||||
{
|
||||
if ($this->assertPOST('groups')) // clamp to real groups
|
||||
DB::Aowow()->query('UPDATE ?_account SET `excludeGroups` = ?d WHERE `id` = ?d', $this->_post['groups'] & PR_EXCLUDE_GROUP_ANY, User::$id);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
52
endpoints/account/favorites.php
Normal file
52
endpoints/account/favorites.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed from db detail pages, when clicking on the fav star near the h1 element
|
||||
* always returns emptry string
|
||||
*/
|
||||
|
||||
class AccountFavoritesResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'add' => ['filter' => FILTER_VALIDATE_INT],
|
||||
'remove' => ['filter' => FILTER_VALIDATE_INT],
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT],
|
||||
// 'sessionKey' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']] // usage of sessionKey omitted
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if ($this->_post['remove'])
|
||||
$this->removeFavorite();
|
||||
|
||||
else if ($this->_post['add'])
|
||||
$this->addFavorite();
|
||||
}
|
||||
|
||||
private function removeFavorite() : void
|
||||
{
|
||||
if ($this->assertPOST('id', 'remove'))
|
||||
DB::Aowow()->query('DELETE FROM ?_account_favorites WHERE `userId` = ?d AND `type` = ?d AND `typeId` = ?d', User::$id, $this->_post['remove'], $this->_post['id']);
|
||||
}
|
||||
|
||||
private function addFavorite() : void
|
||||
{
|
||||
if ($this->assertPOST('id', 'add') && Type::validateIds($this->_post['add'], $this->_post['id']))
|
||||
DB::Aowow()->query('INSERT INTO ?_account_favorites (`userId`, `type`, `typeId`) VALUES (?d, ?d, ?d)', User::$id, $this->_post['add'], $this->_post['id']);
|
||||
else
|
||||
trigger_error('AccountFavoritesResponse::addFavorite() - failed to add [userId: '.User::$id.', type: '.$this->_post['add'].', typeId: '.$this->_post['id'], E_USER_NOTICE);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
101
endpoints/account/forgot-password.php
Normal file
101
endpoints/account/forgot-password.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via links on signin form and from recovery email
|
||||
*
|
||||
* A) redirect to external page
|
||||
* B) 1. click password reset link > display email form
|
||||
* 2. submit email form > send mail with recovery link
|
||||
* 3. click recovery link from mail > display password reset form
|
||||
* 4. submit password reset form > update password
|
||||
*/
|
||||
|
||||
class AccountforgotpasswordResponse extends TemplateResponse
|
||||
{
|
||||
use TrRecoveryHelper, TrGetNext;
|
||||
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'forgot-password';
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
// don't redirect logged in users
|
||||
// you can be forgetful AND logged in
|
||||
|
||||
if (Cfg::get('ACC_EXT_RECOVER_URL'))
|
||||
$this->forward(Cfg::get('ACC_EXT_RECOVER_URL'));
|
||||
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
$this->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->title[] = Lang::account('title');
|
||||
|
||||
parent::generate();
|
||||
|
||||
$msg = $this->processMailForm();
|
||||
|
||||
if ($this->success)
|
||||
$this->inputbox = ['inputbox-status', ['head' => Lang::account('inputbox', 'head', 'recoverPass', [1.5]), 'message' => $msg]];
|
||||
else
|
||||
$this->inputbox = ['inputbox-form-email', array(
|
||||
'head' => Lang::account('inputbox', 'head', 'recoverPass', [1]),
|
||||
'error' => $msg,
|
||||
'action' => '?account=forgot-password&next='.$this->getNext(),
|
||||
'email' => $this->_post['email'] ?? ''
|
||||
)];
|
||||
}
|
||||
|
||||
private function processMailForm() : string
|
||||
{
|
||||
// no input yet. show clean email form
|
||||
if (is_null($this->_post['email']))
|
||||
return '';
|
||||
|
||||
// truncated due to validation fail
|
||||
if (!$this->_post['email'])
|
||||
return Lang::account('emailInvalid');
|
||||
|
||||
$timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ?_account_bannedips WHERE `ip` = ? AND `type` = ?d AND `count` > ?d AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_PASSWORD_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT'));
|
||||
|
||||
// on cooldown pretend we dont know the email address
|
||||
if ($timeout && $timeout > time())
|
||||
return Cfg::get('DEBUG') ? 'resend on cooldown: '.DateTime::formatTimeElapsed($timeout * 1000).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
|
||||
|
||||
// pretend recovery started
|
||||
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `email` = ?', $this->_post['email']))
|
||||
{
|
||||
// do not confirm or deny existence of email
|
||||
$this->success = !Cfg::get('DEBUG');
|
||||
return Cfg::get('DEBUG') ? Lang::account('inputbox', 'error', 'emailNotFound') : Lang::account('inputbox', 'message', 'recovPassSent', [$this->_post['email']]);
|
||||
}
|
||||
|
||||
// recovery actually started
|
||||
if ($err = $this->startRecovery(ACC_STATUS_RECOVER_PASS, 'reset-password', $this->_post['email']))
|
||||
return $err;
|
||||
|
||||
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + ?d, `unbanDate` = UNIX_TIMESTAMP() + ?d',
|
||||
User::$ip, IP_BAN_TYPE_PASSWORD_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'recovPassSent', [$this->_post['email']]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
100
endpoints/account/forgot-username.php
Normal file
100
endpoints/account/forgot-username.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via link on signin form
|
||||
*
|
||||
* A) redirect to external page
|
||||
* B) 1. click password reset link > display email form
|
||||
* 2. submit email form > send mail with recovery link
|
||||
* ( 3. click recovery link from mail to go to signin page (so not on this page) )
|
||||
*/
|
||||
|
||||
class AccountforgotusernameResponse extends TemplateResponse
|
||||
{
|
||||
use TrRecoveryHelper;
|
||||
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'forgot-username';
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
// if the user is looged in goto account dashboard
|
||||
if (User::isLoggedIn())
|
||||
$this->forward('?account');
|
||||
|
||||
if (Cfg::get('ACC_EXT_RECOVER_URL'))
|
||||
$this->forward(Cfg::get('ACC_EXT_RECOVER_URL'));
|
||||
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
$this->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->title[] = Lang::account('title');
|
||||
|
||||
parent::generate();
|
||||
|
||||
$msg = $this->processMailForm();
|
||||
|
||||
if ($this->success)
|
||||
$this->inputbox = ['inputbox-status', ['head' => Lang::account('inputbox', 'head', 'recoverUser'), 'message' => $msg]];
|
||||
else
|
||||
$this->inputbox = ['inputbox-form-email', array(
|
||||
'head' => Lang::account('inputbox', 'head', 'recoverUser'),
|
||||
'error' => $msg,
|
||||
'action' => '?account=forgot-username'
|
||||
)];
|
||||
}
|
||||
|
||||
private function processMailForm() : string
|
||||
{
|
||||
// no input yet. show empty form
|
||||
if (is_null($this->_post['email']))
|
||||
return '';
|
||||
|
||||
// truncated due to validation fail
|
||||
if (!$this->_post['email'])
|
||||
return Lang::account('emailInvalid');
|
||||
|
||||
$timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ?_account_bannedips WHERE `ip` = ? AND `type` = ?d AND `count` > ?d AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_USERNAME_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT'));
|
||||
|
||||
// on cooldown pretend we dont know the email address
|
||||
if ($timeout && $timeout > time())
|
||||
return Cfg::get('DEBUG') ? 'resend on cooldown: '.DateTime::formatTimeElapsed($timeout * 1000).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
|
||||
|
||||
// pretend recovery started
|
||||
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `email` = ?', $this->_post['email']))
|
||||
{
|
||||
// do not confirm or deny existence of email
|
||||
$this->success = !Cfg::get('DEBUG');
|
||||
return Cfg::get('DEBUG') ? Lang::account('inputbox', 'error', 'emailNotFound') : Lang::account('inputbox', 'message', 'recovUserSent', [$this->_post['email']]);
|
||||
}
|
||||
|
||||
// recovery actually started
|
||||
if ($err = $this->startRecovery(ACC_STATUS_RECOVER_USER, 'recover-user', $this->_post['email']))
|
||||
return $err;
|
||||
|
||||
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + ?d, `unbanDate` = UNIX_TIMESTAMP() + ?d',
|
||||
User::$ip, IP_BAN_TYPE_USERNAME_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'recovUserSent', [$this->_post['email']]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
108
endpoints/account/forum-avatar.php
Normal file
108
endpoints/account/forum-avatar.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via form submit on user settings page
|
||||
*/
|
||||
|
||||
class AccountForumavatarResponse extends TextResponse
|
||||
{
|
||||
protected ?string $redirectTo = '?account#community';
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
// called via form submit
|
||||
protected array $expectedPOST = array(
|
||||
'avatar' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0, 'max_range' => 2 ]],
|
||||
'wowicon' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/' ]], // file name can have \W chars: inv_misc_fork&knife, achievement_dungeon_drak'tharon_heroic
|
||||
'customicon' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1 ]]
|
||||
);
|
||||
// called via ajax
|
||||
protected array $expectedGET = array(
|
||||
'avatar' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 2, 'max_range' => 2]],
|
||||
'customicon' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1 ]]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
$msg = match ($this->_post['avatar'] ?? $this->_get['avatar'])
|
||||
{
|
||||
0 => $this->unset(), // none
|
||||
1 => $this->fromIcon(), // wow icon
|
||||
2 => $this->fromUpload(!$this->_get['avatar']), // custom icon (premium feature)
|
||||
default => Lang::main('genericError')
|
||||
};
|
||||
|
||||
if ($msg)
|
||||
$_SESSION['msg'] = ['avatar', $this->success, $msg];
|
||||
}
|
||||
|
||||
private function unset() : string
|
||||
{
|
||||
$x = DB::Aowow()->query('UPDATE ?_account SET `avatar` = 0 WHERE `id` = ?d', User::$id);
|
||||
if ($x === null || $x === false)
|
||||
return Lang::main('genericError');
|
||||
|
||||
$this->success = true;
|
||||
|
||||
return Lang::account('updateMessage', $x === 0 ? 'avNoChange' : 'avSuccess');
|
||||
}
|
||||
|
||||
private function fromIcon() : string
|
||||
{
|
||||
if (!$this->assertPOST('wowicon'))
|
||||
return Lang::main('intError');
|
||||
|
||||
$icon = strtolower(trim($this->_post['wowicon']));
|
||||
|
||||
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_icons WHERE `name` = ?', $icon))
|
||||
return Lang::account('updateMessage', 'avNotFound');
|
||||
|
||||
$x = DB::Aowow()->query('UPDATE ?_account SET `avatar` = 1, `wowicon` = ? WHERE `id` = ?d', strtolower($icon), User::$id);
|
||||
if ($x === null || $x === false)
|
||||
return Lang::main('genericError');
|
||||
|
||||
$this->success = true;
|
||||
|
||||
$msg = Lang::account('updateMessage', $x === 0 ? 'avNoChange' : 'avSuccess');
|
||||
if (($qty = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_account WHERE `wowicon` = ?', $icon)) > 1)
|
||||
$msg .= ' '.Lang::account('updateMessage', 'avNthUser', [$qty]);
|
||||
else
|
||||
$msg .= ' '.Lang::account('updateMessage', 'av1stUser');
|
||||
|
||||
return $msg;
|
||||
}
|
||||
|
||||
protected function fromUpload(bool $viaPOST) : string
|
||||
{
|
||||
if (!User::isPremium())
|
||||
return Lang::main('genericError');
|
||||
|
||||
if (($viaPOST && !$this->assertPOST('customicon')) || (!$viaPOST && !$this->assertGET('customicon')))
|
||||
return Lang::main('intError');
|
||||
|
||||
$customIcon = $this->_post['customicon'] ?? $this->_get['customicon'];
|
||||
|
||||
$x = DB::Aowow()->query('UPDATE ?_account_avatars SET `current` = IF(`id` = ?d, 1, 0) WHERE `userId` = ?d AND `status` <> ?d', $customIcon, User::$id, AvatarMgr::STATUS_REJECTED);
|
||||
if (!is_int($x))
|
||||
return Lang::main('genericError');
|
||||
|
||||
if (!is_int(DB::Aowow()->query('UPDATE ?_account SET `avatar` = 2 WHERE `id` = ?d', User::$id)))
|
||||
return Lang::main('intError');
|
||||
|
||||
$this->success = true;
|
||||
|
||||
return Lang::account('updateMessage', $x === 0 ? 'avNoChange' : 'avSuccess');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
41
endpoints/account/premium-border.php
Normal file
41
endpoints/account/premium-border.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via form submit on user settings page
|
||||
*/
|
||||
|
||||
class AccountPremiumborderResponse extends TextResponse
|
||||
{
|
||||
protected ?string $redirectTo = '?account#premium';
|
||||
protected bool $requiresLogin = true;
|
||||
protected int $requiredUserGroup = U_GROUP_PREMIUM_PERMISSIONS;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'avatarborder' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0, 'max_range' => 4]],
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if (!$this->assertPOST('avatarborder'))
|
||||
return;
|
||||
|
||||
$x = DB::Aowow()->query('UPDATE ?_account SET `avatarborder` = ?d WHERE `id` = ?d', $this->_post['avatarborder'], User::$id);
|
||||
if (!is_int($x))
|
||||
$_SESSION['msg'] = ['premiumborder', false, Lang::main('genericError')];
|
||||
else if (!$x)
|
||||
$_SESSION['msg'] = ['premiumborder', true, Lang::account('updateMessage', 'avNoChange')];
|
||||
else
|
||||
$_SESSION['msg'] = ['premiumborder', true, Lang::account('updateMessage', 'avSuccess')];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
36
endpoints/account/rename-icon.php
Normal file
36
endpoints/account/rename-icon.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via form button on user settings page
|
||||
*/
|
||||
|
||||
class AccountRenameiconResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
protected int $requiredUserGroup = U_GROUP_PREMIUM_PERMISSIONS;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'name' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' =>'/^[a-zA-Z][a-zA-Z0-9 ]{0,19}$/']]
|
||||
);
|
||||
|
||||
/*
|
||||
* response not evaluated
|
||||
*/
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned() || !$this->assertPOST('id', 'name'))
|
||||
return;
|
||||
|
||||
// regexp same as in account.js
|
||||
DB::Aowow()->query('UPDATE ?_account_avatars SET `name` = ? WHERE `id` = ?d AND `userId` = ?d', trim($this->_post['name']), $this->_post['id'], User::$id);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
52
endpoints/account/resend-submit.php
Normal file
52
endpoints/account/resend-submit.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed after successful resend request
|
||||
* empty page with status box
|
||||
*/
|
||||
|
||||
class AccountResendsubmitResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'resend';
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
|
||||
);
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
if (!Cfg::get('ACC_ALLOW_REGISTER') || Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
$this->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->title[] = Lang::account('title');
|
||||
|
||||
$error = $message = '';
|
||||
|
||||
if ($this->assertPOST('email'))
|
||||
$message = Lang::account('inputbox', 'message', 'createAccSent', [$this->_post['email']]);
|
||||
else
|
||||
$error = Lang::main('intError');
|
||||
|
||||
parent::generate();
|
||||
|
||||
$this->inputbox = ['inputbox-status', array(
|
||||
'head' => Lang::account('inputbox', 'head', 'register', [1.5]),
|
||||
'message' => $message,
|
||||
'error' => $error
|
||||
)];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
98
endpoints/account/resend.php
Normal file
98
endpoints/account/resend.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via link on login page
|
||||
* empty page with status box
|
||||
*/
|
||||
|
||||
class AccountResendResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'resend';
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
if (Cfg::get('ACC_EXT_RECOVER_URL'))
|
||||
$this->forward(Cfg::get('ACC_EXT_RECOVER_URL'));
|
||||
|
||||
if (!Cfg::get('ACC_ALLOW_REGISTER') || Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
$this->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->title[] = Lang::account('title');
|
||||
|
||||
parent::generate();
|
||||
|
||||
// error from account=activate
|
||||
if (isset($_SESSION['error']['activate']))
|
||||
{
|
||||
$msg = $_SESSION['error']['activate'];
|
||||
unset($_SESSION['error']['activate']);
|
||||
}
|
||||
else
|
||||
$msg = $this->resend();
|
||||
|
||||
if ($this->success)
|
||||
$this->inputbox = ['inputbox-status', ['head' => Lang::account('inputbox', 'head', 'resendMail'), 'message' => $msg]];
|
||||
else
|
||||
$this->inputbox = ['inputbox-form-email', array(
|
||||
'head' => Lang::account('inputbox', 'head', 'resendMail'),
|
||||
'message' => Lang::account('inputbox', 'message', 'resendMail'),
|
||||
'error' => $msg,
|
||||
'action' => '?account=resend',
|
||||
)];
|
||||
}
|
||||
|
||||
private function resend() : string
|
||||
{
|
||||
// no input yet. show clean form
|
||||
if (is_null($this->_post['email']))
|
||||
return '';
|
||||
|
||||
// truncated due to validation fail
|
||||
if (!$this->_post['email'])
|
||||
return Lang::account('emailInvalid');
|
||||
|
||||
$timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ?_account_bannedips WHERE `ip` = ? AND `type` = ?d AND `count` > ?d AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT'));
|
||||
|
||||
// on cooldown pretend we dont know the email address
|
||||
if ($timeout && $timeout > time())
|
||||
return Cfg::get('DEBUG') ? 'resend on cooldown: '.DateTime::formatTimeElapsed($timeout * 1000).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
|
||||
|
||||
// check email and account status
|
||||
if ($token = DB::Aowow()->selectCell('SELECT `token` FROM ?_account WHERE `email` = ? AND `status` = ?d', $this->_post['email'], ACC_STATUS_NEW))
|
||||
{
|
||||
if (!Util::sendMail($this->_post['email'], 'activate-account', [$token]))
|
||||
return Lang::main('intError');
|
||||
|
||||
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + ?d, `unbanDate` = UNIX_TIMESTAMP() + ?d',
|
||||
User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'createAccSent', [$this->_post['email']]);
|
||||
}
|
||||
|
||||
// pretend recovery started
|
||||
// do not confirm or deny existence of email
|
||||
$this->success = !Cfg::get('DEBUG');
|
||||
return Cfg::get('DEBUG') ? Lang::account('inputbox', 'error', 'emailNotFound') : Lang::account('inputbox', 'message', 'createAccSent', [$this->_post['email']]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
121
endpoints/account/reset-password.php
Normal file
121
endpoints/account/reset-password.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via links on signin form and from recovery email
|
||||
*
|
||||
* A) redirect to external page
|
||||
* B) 1. click password reset link > display email form
|
||||
* 2. submit email form > send mail with recovery link
|
||||
* 3. click recovery link from mail > display password reset form
|
||||
* 4. submit password reset form > update password
|
||||
*/
|
||||
|
||||
class AccountresetpasswordResponse extends TemplateResponse
|
||||
{
|
||||
use TrRecoveryHelper, TrGetNext;
|
||||
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'reset-password';
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']],
|
||||
'next' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/' ]]
|
||||
);
|
||||
protected array $expectedPOST = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']],
|
||||
'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW ],
|
||||
'password' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
|
||||
'c_password' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->title[] = Lang::account('title');
|
||||
|
||||
parent::__construct();
|
||||
|
||||
// don't redirect logged in users
|
||||
// you can be forgetful AND logged in
|
||||
|
||||
if (Cfg::get('ACC_EXT_RECOVER_URL'))
|
||||
$this->forward(Cfg::get('ACC_EXT_RECOVER_URL'));
|
||||
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
$this->generateError();
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
parent::generate();
|
||||
|
||||
$errMsg = '';
|
||||
if (!$this->assertGET('key') && !$this->assertPOST('key'))
|
||||
$errMsg = Lang::account('inputbox', 'error', 'passTokenLost');
|
||||
else if ($this->_get['key'] && !DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `token` = ? AND `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP()', $this->_get['key'], ACC_STATUS_RECOVER_PASS))
|
||||
$errMsg = Lang::account('inputbox', 'error', 'passTokenUsed');
|
||||
|
||||
if ($errMsg)
|
||||
{
|
||||
$this->inputbox = ['inputbox-status', array(
|
||||
'head' => Lang::account('inputbox', 'head', 'error'),
|
||||
'error' => $errMsg
|
||||
)];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// step "2.5"
|
||||
$errMsg = $this->doResetPass();
|
||||
if ($this->success)
|
||||
$this->forward('?account=signin');
|
||||
|
||||
// step 2
|
||||
$this->inputbox = ['inputbox-form-password', array(
|
||||
'head' => Lang::account('inputbox', 'head', 'recoverPass', [2]),
|
||||
'token' => $this->_post['key'] ?? $this->_get['key'],
|
||||
'action' => '?account=reset-password&next=account=signin',
|
||||
'error' => $errMsg,
|
||||
)];
|
||||
}
|
||||
|
||||
private function doResetPass() : string
|
||||
{
|
||||
// no input yet. show clean form
|
||||
if (!$this->assertPOST('key', 'password', 'c_password') && is_null($this->_post['email']))
|
||||
return '';
|
||||
|
||||
// truncated due to validation fail
|
||||
if (!$this->_post['email'])
|
||||
return Lang::account('emailInvalid');
|
||||
|
||||
if ($this->_post['password'] != $this->_post['c_password'])
|
||||
return Lang::account('passCheckFail');
|
||||
|
||||
$userData = DB::Aowow()->selectRow('SELECT `id`, `passHash` FROM ?_account WHERE `token` = ? AND `email` = ? AND `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP()',
|
||||
$this->_post['key'],
|
||||
$this->_post['email'],
|
||||
ACC_STATUS_RECOVER_PASS
|
||||
);
|
||||
if (!$userData)
|
||||
return Lang::account('inputbox', 'error', 'emailNotFound');
|
||||
|
||||
if (!User::verifyCrypt($this->_post['c_password'], $userData['passHash']))
|
||||
return Lang::account('newPassDiff');
|
||||
|
||||
if (!DB::Aowow()->query('UPDATE ?_account SET `passHash` = ?, `status` = ?d WHERE `id` = ?d', User::hashCrypt($this->_post['c_password']), ACC_STATUS_NONE, $userData['id']))
|
||||
return Lang::main('intError');
|
||||
|
||||
$this->success = true;
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
62
endpoints/account/revert-email-address.php
Normal file
62
endpoints/account/revert-email-address.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via revert email link
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
// ?auth=email-revert
|
||||
class AccountRevertemailaddressResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'revert-email-address';
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
parent::generate();
|
||||
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
$msg = $this->revert();
|
||||
|
||||
$this->inputbox = ['inputbox-status', array(
|
||||
'head' => Lang::account('inputbox', 'head', $this->success ? 'success' : 'error'),
|
||||
'message' => $this->success ? $msg : '',
|
||||
'error' => $this->success ? '' : $msg,
|
||||
)];
|
||||
}
|
||||
|
||||
// this should probably take precedence over email-change
|
||||
// todo - move personal settings changes to separate table
|
||||
private function revert() : string
|
||||
{
|
||||
if (!$this->assertGET('key'))
|
||||
return Lang::main('intError');
|
||||
|
||||
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']);
|
||||
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_EMAIL || $acc['statusTimer'] < time())
|
||||
return Lang::account('inputbox', 'error', 'mailTokenUsed');
|
||||
|
||||
// 0 changes == error
|
||||
if (!DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key']))
|
||||
return Lang::main('intError');
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('inputbox', 'message', 'mailRevertOk');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
148
endpoints/account/signin.php
Normal file
148
endpoints/account/signin.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
2 modes
|
||||
A) show form
|
||||
B) execute login and forward to
|
||||
* self on failure
|
||||
* next on success
|
||||
*/
|
||||
|
||||
class AccountSigninResponse extends TemplateResponse
|
||||
{
|
||||
use TrGetNext;
|
||||
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'signin';
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'username' => ['filter' => FILTER_CALLBACK, 'options' => [Util::class, 'validateLogin'] ],
|
||||
'password' => ['filter' => FILTER_CALLBACK, 'options' => [Util::class, 'validatePassword']],
|
||||
'remember_me' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkRememberMe'] ]
|
||||
);
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']],
|
||||
'next' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/'] ]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// if the user is logged in, goto user dashboard
|
||||
if (User::isLoggedIn())
|
||||
$this->forward('?user='.User::$username);
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$username =
|
||||
$error = '';
|
||||
$rememberMe = !!$this->_post['remember_me'];
|
||||
|
||||
$this->title = [Lang::account('title')];
|
||||
|
||||
// coming from user recovery or creation, prefill username
|
||||
if ($this->_get['key'])
|
||||
{
|
||||
if ($userData = DB::Aowow()->selectRow('SELECT a.`login` AS "0", IF(s.`expires`, 0, 1) AS "1" FROM ?_account a LEFT JOIN ?_account_sessions s ON a.`id` = s.`userId` AND a.`token` = s.`sessionId` WHERE a.`status` IN (?a) AND a.`token` = ?',
|
||||
[ACC_STATUS_RECOVER_USER, ACC_STATUS_NONE], $this->_get['key']))
|
||||
[$username, $rememberMe] = $userData;
|
||||
}
|
||||
|
||||
if ($this->doSignIn($error))
|
||||
$this->forward($this->getNext(true));
|
||||
|
||||
if ($error)
|
||||
User::destroy();
|
||||
|
||||
$this->inputbox = ['inputbox-form-signin', array(
|
||||
'head' => Lang::account('inputbox', 'head', 'signin'),
|
||||
'action' => '?account=signin&next='.$this->getNext(),
|
||||
'error' => $error,
|
||||
'username' => $username,
|
||||
'rememberMe' => $rememberMe,
|
||||
'hasRecovery' => Cfg::get('ACC_EXT_RECOVER_URL') || Cfg::get('ACC_AUTH_MODE') == AUTH_MODE_SELF,
|
||||
)];
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
|
||||
private function doSignIn(string &$error) : bool
|
||||
{
|
||||
if (is_null($this->_post['username']) && is_null($this->_post['password']))
|
||||
return false;
|
||||
|
||||
if (!$this->assertPOST('username'))
|
||||
{
|
||||
$error = Lang::account('userNotFound');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->assertPOST('password'))
|
||||
{
|
||||
$error = Lang::account('wrongPass');
|
||||
return false;
|
||||
}
|
||||
|
||||
$error = match (User::authenticate($this->_post['username'], $this->_post['password']))
|
||||
{
|
||||
AUTH_OK, AUTH_BANNED => $this->onAuthSuccess(),
|
||||
// AUTH_BANNED => Lang::account('accBanned'); // ToDo: should this return an error? the actual account functionality should be blocked elsewhere
|
||||
AUTH_WRONGUSER => Lang::account('userNotFound'),
|
||||
AUTH_WRONGPASS => Lang::account('wrongPass'),
|
||||
AUTH_IPBANNED => Lang::account('inputbox', 'error', 'loginExceeded', [DateTime::formatTimeElapsedFloat(Cfg::get('ACC_FAILED_AUTH_BLOCK') * 1000)]),
|
||||
AUTH_INTERNAL_ERR => Lang::main('intError'),
|
||||
default => Lang::main('intError')
|
||||
};
|
||||
|
||||
return !$error;
|
||||
}
|
||||
|
||||
private function onAuthSuccess() : string
|
||||
{
|
||||
if (!User::$ip)
|
||||
{
|
||||
trigger_error('AccountSigninResponse::onAuthSuccess() - tried to login user without ip set', E_USER_ERROR);
|
||||
return Lang::main('intError');
|
||||
}
|
||||
|
||||
// reset account status, update expiration
|
||||
$ok = DB::Aowow()->query('UPDATE ?_account SET `prevIP` = IF(`curIp` = ?, `prevIP`, `curIP`), `curIP` = IF(`curIp` = ?, `curIP`, ?), `status` = IF(`status` = ?d, `status`, 0), `statusTimer` = IF(`status` = ?d, `statusTimer`, 0), `token` = IF(`status` = ?d, `token`, "") WHERE `id` = ?d',
|
||||
User::$ip, User::$ip, User::$ip,
|
||||
ACC_STATUS_NEW, ACC_STATUS_NEW, ACC_STATUS_NEW,
|
||||
User::$id // available after successful User:authenticate
|
||||
);
|
||||
|
||||
if (!is_int($ok)) // num updated fields or null on fail
|
||||
{
|
||||
trigger_error('AccountSigninResponse::onAuthSuccess() - failed to update account status', E_USER_ERROR);
|
||||
return Lang::main('intError');
|
||||
}
|
||||
|
||||
// DELETE temp session
|
||||
if ($this->_get['key'])
|
||||
DB::Aowow()->query('DELETE FROM ?_account_sessions WHERE `sessionId` = ?', $this->_get['key']);
|
||||
|
||||
session_regenerate_id(true); // user status changed => regenerate id
|
||||
|
||||
// create new session entry
|
||||
DB::Aowow()->query('INSERT INTO ?_account_sessions (`userId`, `sessionId`, `created`, `expires`, `touched`, `deviceInfo`, `ip`, `status`) VALUES (?d, ?, ?d, ?d, ?d, ?, ?, ?d)',
|
||||
User::$id, session_id(), time(), $this->_post['remember_me'] ? 0 : time() + Cfg::get('SESSION_TIMEOUT_DELAY'), time(), User::$agent, User::$ip, SESSION_ACTIVE);
|
||||
|
||||
if (User::init()) // reinitialize the user
|
||||
User::save();
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
40
endpoints/account/signout.php
Normal file
40
endpoints/account/signout.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AccountSignoutResponse extends TextResponse
|
||||
{
|
||||
use TrGetNext;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'next' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']],
|
||||
'global' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ]
|
||||
);
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
// if the user not is logged in goto login page
|
||||
if (!User::isLoggedIn())
|
||||
$this->forwardToSignIn();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if ($this->_get['global'])
|
||||
DB::Aowow()->query('UPDATE ?_account_sessions SET `touched` = ?d, `status` = ?d WHERE `userId` = ?d', time(), SESSION_FORCED_LOGOUT, User::$id);
|
||||
else
|
||||
DB::Aowow()->query('UPDATE ?_account_sessions SET `touched` = ?d, `status` = ?d WHERE `sessionId` = ?', time(), SESSION_LOGOUT, session_id());
|
||||
|
||||
User::destroy();
|
||||
|
||||
$this->redirectTo = $this->getNext(true);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
163
endpoints/account/signup.php
Normal file
163
endpoints/account/signup.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via signup link
|
||||
* self referencing
|
||||
*/
|
||||
class AccountSignupResponse extends TemplateResponse
|
||||
{
|
||||
use TrGetNext;
|
||||
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'signup';
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'username' => ['filter' => FILTER_SANITIZE_SPECIAL_CHARS, 'flags' => FILTER_FLAG_STRIP_AOWOW ],
|
||||
'email' => ['filter' => FILTER_SANITIZE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW ],
|
||||
'password' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
|
||||
'c_password' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
|
||||
'remember_me' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkRememberMe']]
|
||||
);
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'next' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// if the user is logged in goto account dashboard
|
||||
if (User::isLoggedIn())
|
||||
$this->forward('?account');
|
||||
|
||||
// redirect to external registration page, if set
|
||||
if (Cfg::get('ACC_EXT_CREATE_URL'))
|
||||
$this->forward(Cfg::get('ACC_EXT_CREATE_URL'));
|
||||
|
||||
parent::__construct();
|
||||
|
||||
// registration not enabled on self
|
||||
if (!Cfg::get('ACC_ALLOW_REGISTER'))
|
||||
$this->generateError();
|
||||
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
$this->generateError();
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->title[] = Lang::account('title');
|
||||
|
||||
// step 1 - no params > signup form
|
||||
// step 2 - any param > status box
|
||||
// step 3 - on ?account=activate
|
||||
|
||||
$message = $this->doSignUp();
|
||||
|
||||
if ($this->success)
|
||||
{
|
||||
$this->inputbox = ['inputbox-status', array(
|
||||
'head' => Lang::account('inputbox', 'head', 'register', [1.5]),
|
||||
'message' => Lang::account('inputbox', 'message', 'createAccSent', [$this->_post['email']])
|
||||
)];
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->inputbox = ['inputbox-form-signup', array(
|
||||
'head' => Lang::account('inputbox', 'head', 'register', [1]),
|
||||
'error' => $message,
|
||||
'action' => '?account=signup&next='.$this->getNext(),
|
||||
'username' => $this->_post['username'] ?? '',
|
||||
'email' => $this->_post['email'] ?? '',
|
||||
'rememberMe' => !!$this->_post['remember_me'],
|
||||
)];
|
||||
}
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
|
||||
private function doSignUp() : string
|
||||
{
|
||||
// no input yet. show clean form
|
||||
if (!$this->assertPOST('username', 'password', 'c_password') && is_null($this->_post['email']))
|
||||
return '';
|
||||
|
||||
// truncated due to validation fail
|
||||
if (!$this->_post['email'])
|
||||
return Lang::account('emailInvalid');
|
||||
|
||||
// check username
|
||||
if (!Util::validateUsername($this->_post['username'], $e))
|
||||
return Lang::account($e == 1 ? 'errNameLength' : 'errNameChars');
|
||||
|
||||
// check password
|
||||
if (!Util::validatePassword($this->_post['password'], $e))
|
||||
return Lang::account($e == 1 ? 'errPassLength' : 'errPassChars');
|
||||
|
||||
if ($this->_post['password'] !== $this->_post['c_password'])
|
||||
return Lang::account('passMismatch');
|
||||
|
||||
// check ip
|
||||
if (!User::$ip)
|
||||
return Lang::main('intError');
|
||||
|
||||
// limit account creation
|
||||
if (DB::Aowow()->selectRow('SELECT 1 FROM ?_account_bannedips WHERE `type` = ?d AND `ip` = ? AND `count` >= ?d AND `unbanDate` >= UNIX_TIMESTAMP()', IP_BAN_TYPE_REGISTRATION_ATTEMPT, User::$ip, Cfg::get('ACC_FAILED_AUTH_COUNT')))
|
||||
{
|
||||
DB::Aowow()->query('UPDATE ?_account_bannedips SET `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + ?d WHERE `ip` = ? AND `type` = ?d', Cfg::get('ACC_FAILED_AUTH_BLOCK'), User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT);
|
||||
return Lang::account('inputbox', 'error', 'signupExceeded', [DateTime::formatTimeElapsedFloat(Cfg::get('ACC_FAILED_AUTH_BLOCK') * 1000)]);
|
||||
}
|
||||
|
||||
// username / email taken
|
||||
if ($inUseData = DB::Aowow()->SelectRow('SELECT `id`, `username`, `status` = ?d AND `statusTimer` < UNIX_TIMESTAMP() AS "expired" FROM ?_account WHERE (LOWER(`username`) = LOWER(?) OR LOWER(`email`) = LOWER(?))', ACC_STATUS_NEW, $this->_post['username'], $this->_post['email']))
|
||||
{
|
||||
if ($inUseData['expired'])
|
||||
DB::Aowow()->query('DELETE FROM ?_account WHERE `id` = ?d', $inUseData['id']);
|
||||
else
|
||||
return Util::lower($inUseData['username']) == Util::lower($this->_post['username']) ? Lang::account('nameInUse') : Lang::account('mailInUse');
|
||||
}
|
||||
|
||||
// create..
|
||||
$token = Util::createHash();
|
||||
$userId = DB::Aowow()->query('INSERT INTO ?_account (`login`, `passHash`, `username`, `email`, `joindate`, `curIP`, `locale`, `userGroups`, `status`, `statusTimer`, `token`) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(), ?, ?d, ?d, ?d, UNIX_TIMESTAMP() + ?d, ?)',
|
||||
$this->_post['username'],
|
||||
User::hashCrypt($this->_post['password']),
|
||||
$this->_post['username'],
|
||||
$this->_post['email'],
|
||||
User::$ip,
|
||||
Lang::getLocale()->value,
|
||||
U_GROUP_PENDING,
|
||||
ACC_STATUS_NEW,
|
||||
Cfg::get('ACC_CREATE_SAVE_DECAY'),
|
||||
$token
|
||||
);
|
||||
|
||||
if (!$userId)
|
||||
return Lang::main('intError');
|
||||
|
||||
// create session tied to the token to store remember_me status
|
||||
DB::Aowow()->query('INSERT INTO ?_account_sessions (`userId`, `sessionId`, `created`, `expires`, `touched`, `deviceInfo`, `ip`, `status`) VALUES (?d, ?, ?d, ?d, ?d, ?, ?, ?d)',
|
||||
$userId, $token, time(), $this->_post['remember_me'] ? 0 : time() + Cfg::get('SESSION_TIMEOUT_DELAY'), time(), User::$agent, User::$ip, SESSION_ACTIVE);
|
||||
|
||||
if (!Util::sendMail($this->_post['email'], 'activate-account', [$token], Cfg::get('ACC_CREATE_SAVE_DECAY')))
|
||||
return Lang::main('intError2', ['send mail']);
|
||||
|
||||
// success: update ip-bans
|
||||
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, 1, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + ?d',
|
||||
User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
|
||||
|
||||
Util::gainSiteReputation($userId, SITEREP_ACTION_REGISTER);
|
||||
|
||||
$this->success = true;
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
48
endpoints/account/update-community-settings.php
Normal file
48
endpoints/account/update-community-settings.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via account settings form submit
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
class AccountUpdatecommunitysettingsResponse extends TextResponse
|
||||
{
|
||||
protected ?string $redirectTo = '?account#community';
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'desc' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if ($message = $this->updateSettings())
|
||||
$_SESSION['msg'] = ['community', $this->success, $message];
|
||||
}
|
||||
|
||||
protected function updateSettings()
|
||||
{
|
||||
if (is_null($this->_post['desc'])) // assertPOST tests for empty string which is valid here
|
||||
return Lang::main('genericError');
|
||||
|
||||
// description - 0 modified rows is still success
|
||||
if (!is_int(DB::Aowow()->query('UPDATE ?_account SET `description` = ? WHERE `id` = ?d', $this->_post['desc'], User::$id)))
|
||||
return Lang::main('genericError');
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('updateMessage', 'community');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
80
endpoints/account/update-email.php
Normal file
80
endpoints/account/update-email.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via account settings form submit
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
class AccountUpdateemailResponse extends TextResponse
|
||||
{
|
||||
protected ?string $redirectTo = '?account#personal';
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'newemail' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
(new TemplateResponse())->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if ($msg = $this->updateMail())
|
||||
$_SESSION['msg'] = ['email', $this->success, $msg];
|
||||
}
|
||||
|
||||
private function updateMail() : string
|
||||
{
|
||||
// no input yet
|
||||
if (is_null($this->_post['newemail']))
|
||||
return Lang::main('intError');
|
||||
// truncated due to validation fail
|
||||
if (!$this->_post['newemail'])
|
||||
return Lang::account('emailInvalid');
|
||||
|
||||
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `email` = ? AND `id` <> ?d', $this->_post['newemail'], User::$id))
|
||||
return Lang::account('mailInUse');
|
||||
|
||||
$status = DB::Aowow()->selectCell('SELECT `status` FROM ?_account WHERE `statusTimer` > UNIX_TIMESTAMP() AND `id` = ?d', User::$id);
|
||||
if ($status != ACC_STATUS_NONE && $status != ACC_STATUS_CHANGE_EMAIL)
|
||||
return Lang::account('inputbox', 'error', 'isRecovering', [DateTime::formatTimeElapsedFloat(Cfg::get('ACC_RECOVERY_DECAY') * 1000)]);
|
||||
|
||||
$oldEmail = DB::Aowow()->selectCell('SELECT `email` FROM ?_account WHERE `id` = ?d', User::$id);
|
||||
if ($this->_post['newemail'] == $oldEmail)
|
||||
return Lang::account('newMailDiff');
|
||||
|
||||
$token = Util::createHash();
|
||||
|
||||
// store new mail in updateValue field, exchange when confirmation mail gets confirmed
|
||||
if (!DB::Aowow()->query('UPDATE ?_account SET `updateValue` = ?, `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d, `token` = ? WHERE `id` = ?d',
|
||||
$this->_post['newemail'], ACC_STATUS_CHANGE_EMAIL, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id))
|
||||
return Lang::main('intError');
|
||||
|
||||
if (!Util::sendMail($this->_post['newemail'], 'change-email', [$token, $this->_post['newemail']], Cfg::get('ACC_RECOVERY_DECAY')))
|
||||
return Lang::main('intError2', ['send mail']);
|
||||
|
||||
if (!Util::sendMail($oldEmail, 'revert-email', [$token, $oldEmail], Cfg::get('ACC_RECOVERY_DECAY')))
|
||||
return Lang::main('intError2', ['send mail']);
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('updateMessage', 'personal', [$this->_post['newemail']]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
60
endpoints/account/update-general-settings.php
Normal file
60
endpoints/account/update-general-settings.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via account settings form submit
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
class AccountUpdategeneralsettingsResponse extends TextResponse
|
||||
{
|
||||
protected ?string $redirectTo = '?account#general';
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'modelrace' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['default' => 0, 'min_range' => 1, 'max_range' => 11]],
|
||||
'modelgender' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['default' => 0, 'min_range' => 1, 'max_range' => 2] ],
|
||||
'idsInLists' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCheckbox'] ]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if ($message = $this->updateGeneral())
|
||||
$_SESSION['msg'] = ['general', $this->success, $message];
|
||||
}
|
||||
|
||||
private function updateGeneral() : string
|
||||
{
|
||||
if (!$this->assertPOST('modelrace', 'modelgender'))
|
||||
return Lang::main('genericError');
|
||||
|
||||
if ($this->_post['modelrace'] && !ChrRace::tryFrom($this->_post['modelrace']))
|
||||
return Lang::main('genericError');
|
||||
|
||||
// js handles this as cookie, so saved as cookie; Q - also save in ?_account table?
|
||||
if (!DB::Aowow()->query('REPLACE INTO ?_account_cookies (`userId`, `name`, `data`) VALUES (?d, ?, ?)', User::$id, 'default_3dmodel', $this->_post['modelrace']. ',' . $this->_post['modelgender']))
|
||||
return Lang::main('genericError');
|
||||
|
||||
if (!setcookie('default_3dmodel', $this->_post['modelrace']. ',' . $this->_post['modelgender'], 0, '/'))
|
||||
return Lang::main('intError');
|
||||
|
||||
// int > number of edited rows > no changes is still success
|
||||
if (!is_int(DB::Aowow()->query('UPDATE ?_account SET `debug` = ?d WHERE `id` = ?d', $this->_post['idsInLists'] ? 1 : 0, User::$id)))
|
||||
return Lang::main('intError');
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('updateMessage', 'general');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
86
endpoints/account/update-password.php
Normal file
86
endpoints/account/update-password.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via account settings form submit
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
class AccountUpdatepasswordResponse extends TextResponse
|
||||
{
|
||||
protected ?string $redirectTo = '?account#personal';
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'currentPassword' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
|
||||
'newPassword' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
|
||||
'confirmPassword' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
|
||||
'globalLogout' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCheckbox']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
(new TemplateResponse())->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if ($msg = $this->updatePassword())
|
||||
$_SESSION['msg'] = ['password', $this->success, $msg];
|
||||
}
|
||||
|
||||
private function updatePassword() : string
|
||||
{
|
||||
if (!$this->assertPOST('currentPassword', 'newPassword', 'confirmPassword'))
|
||||
return Lang::main('intError');
|
||||
|
||||
if (!Util::validatePassword($this->_post['newPassword'], $e))
|
||||
return Lang::account($e == 1 ? 'errPassLength' : 'errPassChars');
|
||||
|
||||
if ($this->_post['newPassword'] !== $this->_post['confirmPassword'])
|
||||
return Lang::account('passMismatch');
|
||||
|
||||
$userData = DB::Aowow()->selectRow('SELECT `status`, `passHash`, `statusTimer` FROM ?_account WHERE `id` = ?d', User::$id);
|
||||
if ($userData['status'] != ACC_STATUS_NONE && $userData['status'] != ACC_STATUS_CHANGE_PASS && $userData['statusTimer'] > time())
|
||||
return Lang::account('inputbox', 'error', 'isRecovering', [DateTime::formatTimeElapsedFloat(Cfg::get('ACC_RECOVERY_DECAY') * 1000)]);
|
||||
|
||||
if (!User::verifyCrypt($this->_post['currentPassword'], $userData['passHash']))
|
||||
return Lang::account('wrongPass');
|
||||
|
||||
if (User::verifyCrypt($this->_post['newPassword'], $userData['passHash']))
|
||||
return Lang::account('newPassDiff');
|
||||
|
||||
$token = Util::createHash();
|
||||
|
||||
// store new hash in updateValue field, exchange when confirmation mail gets confirmed
|
||||
if (!DB::Aowow()->query('UPDATE ?_account SET `updateValue` = ?, `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d, `token` = ? WHERE `id` = ?d',
|
||||
User::hashCrypt($this->_post['newPassword']), ACC_STATUS_CHANGE_PASS, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id))
|
||||
return Lang::main('intError');
|
||||
|
||||
$email = DB::Aowow()->selectCell('SELECT `email` FROM ?_account WHERE `id` = ?d', User::$id);
|
||||
if (!Util::sendMail($email, 'update-password', [$token, $email], Cfg::get('ACC_RECOVERY_DECAY')))
|
||||
return Lang::main('intError2', ['send mail']);
|
||||
|
||||
// logout all other active sessions
|
||||
if ($this->_post['globalLogout'])
|
||||
DB::Aowow()->query('UPDATE ?_account_sessions SET `status` = ?d, `touched` = ?d WHERE `userId` = ?d AND `sessionId` <> ? AND `status` = ?d', SESSION_FORCED_LOGOUT, time(), User::$id, session_id(), SESSION_ACTIVE);
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('updateMessage', 'personal', [User::$email]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
61
endpoints/account/update-username.php
Normal file
61
endpoints/account/update-username.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via account settings form submit
|
||||
* write status to session and redirect to account settings
|
||||
*/
|
||||
|
||||
class AccountUpdateusernameResponse extends TextResponse
|
||||
{
|
||||
protected ?string $redirectTo = '?account#personal';
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'newUsername' => ['filter' => FILTER_CALLBACK, 'options' => [Util::class, 'validateUsername']]
|
||||
);
|
||||
|
||||
private bool $success = false;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
|
||||
(new TemplateResponse())->generateError();
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if ($msg = $this->updateUsername())
|
||||
$_SESSION['msg'] = ['username', $this->success, $msg];
|
||||
}
|
||||
|
||||
private function updateUsername() : string
|
||||
{
|
||||
if (!$this->assertPOST('newUsername'))
|
||||
return Lang::main('intError');
|
||||
|
||||
if (DB::Aowow()->selectCell('SELECT `renameCooldown` FROM ?_account WHERE `id` = ?d', User::$id) > time())
|
||||
return Lang::main('intError'); // should have grabbed the error response..
|
||||
|
||||
// yes, including your current name. you don't want to change into your current name, right?
|
||||
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_post['newUsername']))
|
||||
return Lang::account('nameInUse');
|
||||
|
||||
DB::Aowow()->query('UPDATE ?_account SET `username` = ?, `renameCooldown` = ?d WHERE `id` = ?d', $this->_post['newUsername'], time() + Cfg::get('acc_rename_decay'), User::$id);
|
||||
|
||||
$this->success = true;
|
||||
return Lang::account('updateMessage', 'username', [User::$username, $this->_post['newUsername']]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
117
endpoints/account/weightscales.php
Normal file
117
endpoints/account/weightscales.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/*
|
||||
* accessed via ajax
|
||||
* returns scaleId if successful, 0 if not
|
||||
*/
|
||||
|
||||
class AccountWeightscalesResponse extends TextResponse
|
||||
{
|
||||
private const /* int */ MAX_SCALES = 5; // more or less hard-defined in LANG.message_weightscalesaveerror
|
||||
|
||||
protected bool $requiresLogin = true;
|
||||
protected mixed $result = 0; // default to error
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'save' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]],
|
||||
'delete' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]],
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'name' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkName'] ],
|
||||
'scale' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkScale'] ]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (User::isBanned())
|
||||
return;
|
||||
|
||||
if ($this->_post['save'] && $this->_post['id'])
|
||||
$this->updateWeights();
|
||||
|
||||
else if ($this->_post['save'])
|
||||
$this->createWeights();
|
||||
|
||||
else if ($this->_post['delete'])
|
||||
$this->deleteWeights();
|
||||
}
|
||||
|
||||
private function createWeights() : void
|
||||
{
|
||||
if (!$this->assertPOST('name', 'scale'))
|
||||
return;
|
||||
|
||||
$nScales = DB::Aowow()->selectCell('SELECT COUNT(`id`) FROM ?_account_weightscales WHERE `userId` = ?d', User::$id);
|
||||
if ($nScales >= self::MAX_SCALES)
|
||||
return;
|
||||
|
||||
if ($id = DB::Aowow()->query('INSERT INTO ?_account_weightscales (`userId`, `name`) VALUES (?d, ?)', User::$id, $this->_post['name']))
|
||||
if ($this->storeScaleData($id))
|
||||
$this->result = $id;
|
||||
}
|
||||
|
||||
private function updateWeights() : void
|
||||
{
|
||||
if (!$this->assertPOST('name', 'scale', 'id'))
|
||||
return;
|
||||
|
||||
// not in DB or not owned by user
|
||||
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account_weightscales WHERE `userId` = ?d AND `id` = ?d', User::$id, $this->_post['id']))
|
||||
{
|
||||
trigger_error('AccountWeightscalesResponse::updateWeights - scale #'.$this->_post['id'].' not in db or not owned by user #'.User::$id, E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
DB::Aowow()->query('UPDATE ?_account_weightscales SET `name` = ? WHERE `id` = ?d', $this->_post['name'], $this->_post['id']);
|
||||
$this->storeScaleData($this->_post['id']);
|
||||
|
||||
// return edited id on success
|
||||
$this->result = $this->_post['id'];
|
||||
}
|
||||
|
||||
private function deleteWeights() : void
|
||||
{
|
||||
if ($this->assertPOST('id'))
|
||||
DB::Aowow()->query('DELETE FROM ?_account_weightscales WHERE `id` = ?d AND `userId` = ?d', $this->_post['id'], User::$id);
|
||||
|
||||
$this->result = '';
|
||||
}
|
||||
|
||||
private function storeScaleData(int $scaleId) : bool
|
||||
{
|
||||
if (!is_int(DB::Aowow()->query('DELETE FROM ?_account_weightscale_data WHERE `id` = ?d', $scaleId)))
|
||||
return false;
|
||||
|
||||
foreach ($this->_post['scale'] as [$k, $v])
|
||||
if (in_array($k, Util::$weightScales)) // $v is known to be a positive int due to regex check
|
||||
if (!is_int(DB::Aowow()->query('INSERT INTO ?_account_weightscale_data VALUES (?d, ?, ?d)', $scaleId, $k, $v)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*************************************/
|
||||
/* additional request data callbacks */
|
||||
/*************************************/
|
||||
|
||||
protected static function checkScale(string $val) : array
|
||||
{
|
||||
if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val))
|
||||
return array_map(fn($x) => explode(':', $x), explode(',', $val));
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
protected static function checkName(string $val) : string
|
||||
{
|
||||
return mb_substr(preg_replace('/[^[:print:]]/', '', trim(urldecode($val))), 0, 32);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
511
endpoints/achievement/achievement.php
Normal file
511
endpoints/achievement/achievement.php
Normal file
@@ -0,0 +1,511 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
/* Notes:
|
||||
* can create achievement progress bars with
|
||||
* g_createProgressBar(c)
|
||||
* var c = {
|
||||
* text: "",
|
||||
* hoverText: "",
|
||||
* color: "", // cssClassName rep[0-7] | ach[0|1]
|
||||
* width: 0, // 0 <=> 100
|
||||
* }
|
||||
*/
|
||||
|
||||
class AchievementBaseResponse extends TemplateResponse implements ICache
|
||||
{
|
||||
use TrDetailPage, TrCache;
|
||||
|
||||
protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
|
||||
|
||||
protected string $template = 'achievement';
|
||||
protected string $pageName = 'achievement';
|
||||
protected ?int $activeTab = parent::TAB_DATABASE;
|
||||
protected array $breadcrumb = [0, 9];
|
||||
|
||||
public int $type = Type::ACHIEVEMENT;
|
||||
public int $typeId = 0;
|
||||
public int $reqCrtQty = 0;
|
||||
public ?array $mail = null;
|
||||
public string $description = '';
|
||||
public array $criteria = [];
|
||||
public ?array $rewards = null;
|
||||
|
||||
private AchievementList $subject;
|
||||
|
||||
public function __construct(string $id)
|
||||
{
|
||||
parent::__construct($id);
|
||||
|
||||
$this->typeId = intVal($id);
|
||||
$this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE;
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->subject = new AchievementList(array(['id', $this->typeId]));
|
||||
if ($this->subject->error)
|
||||
$this->generateNotFound(Lang::game('achievement'), Lang::achievement('notFound'));
|
||||
|
||||
$this->extendGlobalData($this->subject->getJSGlobals(GLOBALINFO_REWARDS));
|
||||
|
||||
$this->h1 = $this->subject->getField('name', true);
|
||||
|
||||
$this->gPageInfo += array(
|
||||
'type' => $this->type,
|
||||
'typeId' => $this->typeId,
|
||||
'name' => $this->h1
|
||||
);
|
||||
|
||||
|
||||
/*************/
|
||||
/* Menu Path */
|
||||
/*************/
|
||||
|
||||
// create page title and path
|
||||
$curCat = $this->subject->getField('category');
|
||||
$catPath = [];
|
||||
while ($curCat > 0)
|
||||
{
|
||||
$catPath[] = $curCat;
|
||||
$curCat = DB::Aowow()->SelectCell('SELECT `parentCat` FROM ?_achievementcategory WHERE `id` = ?d', $curCat);
|
||||
}
|
||||
|
||||
$this->breadcrumb = array_merge($this->breadcrumb, array_reverse($catPath));
|
||||
|
||||
|
||||
/**************/
|
||||
/* Page Title */
|
||||
/**************/
|
||||
|
||||
array_unshift($this->title, $this->subject->getField('name', true), Util::ucFirst(Lang::game('achievement')));
|
||||
|
||||
|
||||
/***********/
|
||||
/* Infobox */
|
||||
/***********/
|
||||
|
||||
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
|
||||
|
||||
// points
|
||||
if ($_ = $this->subject->getField('points'))
|
||||
$infobox[] = Lang::achievement('points').Lang::main('colon').'[achievementpoints='.$_.']';
|
||||
|
||||
// location
|
||||
// todo (low)
|
||||
|
||||
// faction
|
||||
$infobox[] = Lang::main('side') . match ($this->subject->getField('faction'))
|
||||
{
|
||||
SIDE_ALLIANCE => '[span class=icon-alliance]'.Lang::game('si', SIDE_ALLIANCE).'[/span]',
|
||||
SIDE_HORDE => '[span class=icon-horde]'.Lang::game('si', SIDE_HORDE).'[/span]',
|
||||
default => Lang::game('si', SIDE_BOTH) // 0, 3
|
||||
};
|
||||
|
||||
// id
|
||||
$infobox[] = Lang::achievement('id') . $this->typeId;
|
||||
|
||||
// icon
|
||||
if ($_ = $this->subject->getField('iconId'))
|
||||
{
|
||||
$infobox[] = Util::ucFirst(Lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
|
||||
$this->extendGlobalIds(Type::ICON, $_);
|
||||
}
|
||||
|
||||
// profiler relateed (note that this is part of the cache. I don't think this is important enough to calc for every view)
|
||||
if (Cfg::get('PROFILER_ENABLE') && !($this->subject->getField('flags') & ACHIEVEMENT_FLAG_COUNTER))
|
||||
{
|
||||
$x = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_completion_achievements WHERE `achievementId` = ?d', $this->typeId);
|
||||
$y = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_profiles WHERE `custom` = 0 AND `stub` = 0');
|
||||
$infobox[] = Lang::profiler('attainedBy', [round(($x ?: 0) * 100 / ($y ?: 1))]);
|
||||
|
||||
// completion row added by InfoboxMarkup
|
||||
}
|
||||
|
||||
// original name
|
||||
if (Lang::getLocale() != Locale::EN)
|
||||
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
|
||||
|
||||
if ($infobox)
|
||||
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0', !($this->subject->getField('flags') & ACHIEVEMENT_FLAG_COUNTER));
|
||||
|
||||
|
||||
/**********/
|
||||
/* Series */
|
||||
/**********/
|
||||
|
||||
$series = [];
|
||||
if ($c = $this->subject->getField('chainId'))
|
||||
{
|
||||
$chainAcv = new AchievementList(array(['chainId', $c]));
|
||||
|
||||
foreach ($chainAcv->iterate() as $aId => $__)
|
||||
{
|
||||
$pos = $chainAcv->getField('chainPos');
|
||||
if (!isset($series[$pos]))
|
||||
$series[$pos] = [];
|
||||
|
||||
$series[$pos][] = array(
|
||||
'side' => (int)$chainAcv->getField('faction'),
|
||||
'typeStr' => Type::getFileString(Type::ACHIEVEMENT),
|
||||
'typeId' => $aId,
|
||||
'name' => $chainAcv->getField('name', true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($series)
|
||||
$this->series = [[array_values($series), null]];
|
||||
|
||||
|
||||
/****************/
|
||||
/* Main Content */
|
||||
/****************/
|
||||
|
||||
$this->headIcons = [$this->subject->getField('iconString')];
|
||||
$this->description = $this->subject->getField('description', true);
|
||||
$this->redButtons = array(
|
||||
BUTTON_WOWHEAD => !($this->subject->getField('cuFlags') & CUSTOM_SERVERSIDE),
|
||||
BUTTON_LINKS => array(
|
||||
'linkColor' => 'ffffff00',
|
||||
'linkId' => Type::getFileString(Type::ACHIEVEMENT).':'.$this->typeId.':"..UnitGUID("player")..":0:0:0:0:0:0:0:0',
|
||||
'linkName' => $this->h1,
|
||||
'type' => $this->type,
|
||||
'typeId' => $this->typeId
|
||||
)
|
||||
);
|
||||
$this->reqCrtQty = $this->subject->getField('reqCriteriaCount');
|
||||
|
||||
if ($this->createMail())
|
||||
$this->addScript([SC_CSS_FILE, 'css/Book.css']);
|
||||
|
||||
// create rewards
|
||||
$rewItems = $rewTitles = [];
|
||||
if ($foo = $this->subject->getField('rewards'))
|
||||
{
|
||||
if ($itemRewards = array_filter($foo, fn($x) => $x[0] == Type::ITEM))
|
||||
{
|
||||
$bar = new ItemList(array(['i.id', array_column($itemRewards, 1)]));
|
||||
foreach ($bar->iterate() as $id => $__)
|
||||
$rewItems[] = new IconElement(Type::ITEM, $id, $bar->getField('name', true), quality: $bar->getField('quality'));
|
||||
}
|
||||
|
||||
if ($titleRewards = array_filter($foo, fn($x) => $x[0] == Type::TITLE))
|
||||
{
|
||||
$bar = new TitleList(array(['id', array_column($titleRewards, 1)]));
|
||||
foreach ($bar->iterate() as $id => $__)
|
||||
$rewTitles[] = Lang::achievement('titleReward', [$id, trim(str_replace('%s', '', $bar->getField('male', true)))]);
|
||||
}
|
||||
}
|
||||
|
||||
if (($text = $this->subject->getField('reward', true)) || $rewItems || $rewTitles)
|
||||
$this->rewards = [$rewItems, $rewTitles, $text];
|
||||
|
||||
// factionchange-equivalent
|
||||
if ($pendant = DB::World()->selectCell('SELECT IF(`horde_id` = ?d, `alliance_id`, -`horde_id`) FROM player_factionchange_achievement WHERE `alliance_id` = ?d OR `horde_id` = ?d', $this->typeId, $this->typeId, $this->typeId))
|
||||
{
|
||||
$altAcv = new AchievementList(array(['id', abs($pendant)]));
|
||||
if (!$altAcv->error)
|
||||
{
|
||||
$this->transfer = Lang::achievement('_transfer', array(
|
||||
$altAcv->id,
|
||||
ITEM_QUALITY_NORMAL,
|
||||
$altAcv->getField('iconString'),
|
||||
$altAcv->getField('name', true),
|
||||
$pendant > 0 ? 'alliance' : 'horde',
|
||||
$pendant > 0 ? Lang::game('si', SIDE_ALLIANCE) : Lang::game('si', SIDE_HORDE)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************/
|
||||
/* Criteria List */
|
||||
/*****************/
|
||||
|
||||
// serverside extra-Data (not sure why ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE is set, let a lone a couple hundred times)
|
||||
if ($crtIds = array_column($this->subject->getCriteria(), 'id'))
|
||||
$crtExtraData = DB::World()->select('SELECT `criteria_id` AS ARRAY_KEY, `type` AS ARRAY_KEY2, `value1`, `value2`, `ScriptName` FROM achievement_criteria_data WHERE `type` <> ?d AND `criteria_id` IN (?a)', ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE, $crtIds);
|
||||
else
|
||||
$crtExtraData = [];
|
||||
|
||||
foreach ($this->subject->getCriteria() as $crt)
|
||||
{
|
||||
// hide hidden criteria for regular users (really do..?)
|
||||
// if (($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_HIDDEN) && !User::isInGroup(U_GROUP_STAFF))
|
||||
// continue;
|
||||
|
||||
// alternative display option
|
||||
$crtName = Util::localizedString($crt, 'name');
|
||||
$killSuffix = null;
|
||||
|
||||
$obj = (int)$crt['value1'];
|
||||
$qty = (int)$crt['value2'];
|
||||
|
||||
switch ($crt['type'])
|
||||
{
|
||||
// link to npc
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
|
||||
$killSuffix = Lang::achievement('slain');
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
|
||||
$crtIcon = new IconElement(Type::NPC, $obj, $crtName ?: CreatureList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon', extraText: $crtName ? null : $killSuffix);
|
||||
break;
|
||||
// link to area (by map)
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
|
||||
$zoneId = DB::Aowow()->selectCell('SELECT `id` FROM ?_zones WHERE `mapId` = ?', $obj);
|
||||
$crtIcon = new IconElement(Type::ZONE, $zoneId ?: 0, $crtName ?: ZoneList::getName($zoneId), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
|
||||
break;
|
||||
// link to area
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
|
||||
$crtIcon = new IconElement(Type::ZONE, $obj, $crtName ?: ZoneList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
|
||||
break;
|
||||
// link to skills
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
|
||||
$crtIcon = new IconElement(Type::SKILL, $obj, $crtName ?: SkillList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
|
||||
$this->extendGlobalIds(Type::SKILL, $obj);
|
||||
break;
|
||||
// link to class
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
|
||||
$crtIcon = new IconElement(Type::CHR_CLASS, $obj, $crtName ?: CharClassList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
|
||||
break;
|
||||
// link to race
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
|
||||
$crtIcon = new IconElement(Type::CHR_RACE, $obj, $crtName ?: CharRaceList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
|
||||
break;
|
||||
// link to achivement
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
|
||||
$crtIcon = new IconElement(Type::ACHIEVEMENT, $obj, $crtName ?: AchievementList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
|
||||
$this->extendGlobalIds(Type::ACHIEVEMENT, $obj);
|
||||
break;
|
||||
// link to quest
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
|
||||
$crtIcon = new IconElement(Type::QUEST, $obj, $crtName ?: QuestList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
|
||||
break;
|
||||
// link to spell
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
|
||||
$crtIcon = new IconElement(Type::SPELL, $obj, $crtName ?: SpellList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
|
||||
$this->extendGlobalIds(Type::SPELL, $obj);
|
||||
break;
|
||||
// link to item
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
|
||||
$item = new ItemList([['id', $obj]]);
|
||||
$crtIcon = new IconElement(Type::ITEM, $obj, $crtName ?: $item->getField('name', true), quality: $item->getField('quality'), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
|
||||
$this->extendGlobalData($item->getJSGlobals());
|
||||
break;
|
||||
// link to faction (/w target reputation)
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
|
||||
$crtIcon = new IconElement(Type::FACTION, $obj, $crtName ?: FactionList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon', extraText: '('.Lang::getReputationLevelForPoints($qty).')');
|
||||
break;
|
||||
// link to GObject
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
|
||||
$crtIcon = new IconElement(Type::OBJECT, $obj, $crtName ?: GameObjectList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
|
||||
break;
|
||||
// link to emote
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
|
||||
$crtIcon = new IconElement(Type::EMOTE, $obj, $crtName ?: EmoteList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
|
||||
break;
|
||||
default:
|
||||
// Add a gold coin icon if required
|
||||
if ($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER )
|
||||
$crtIcon = new IconElement(0, 0, '', extraText: Util::formatMoney($qty));
|
||||
else
|
||||
$crtIcon = new IconElement(0, 0, $crtName);
|
||||
break;
|
||||
}
|
||||
|
||||
if (User::isInGroup(U_GROUP_STAFF))
|
||||
$crtIcon->extraText .= ' [CriteriaId: '.$crt['id'].']';
|
||||
|
||||
$extraData = [];
|
||||
foreach ($crtExtraData[$crt['id']] ?? [] as $xType => $xData)
|
||||
{
|
||||
switch ($xType)
|
||||
{
|
||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
|
||||
$extraData[] = CreatureList::makeLink($xData['value1']);
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
|
||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE:
|
||||
if ($xData['value1'])
|
||||
$extraData[] = CharClassList::makeLink($xData['value1']);
|
||||
|
||||
if ($xData['value2'])
|
||||
$extraData[] = CharRaceList::makeLink($xData['value2']);
|
||||
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
|
||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA:
|
||||
$extraData[] = SpellList::makeLink($xData['value1']);
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA:
|
||||
$extraData[] = ZoneList::makeLink($xData['value1']);
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT:
|
||||
if ($xData['ScriptName'] && User::isInGroup(U_GROUP_STAFF))
|
||||
$extraData[] = 'Script '.$xData['ScriptName'];
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY:
|
||||
if ($we = new WorldEventList(array(['holidayId', $xData['value1']])))
|
||||
$extraData[] = '<a href="?event='.$we->id.'">'.$we->getField('name', true).'</a>';
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_ID:
|
||||
if ($z = new ZoneList(array(['mapIdBak', $xData['value1']])))
|
||||
$extraData[] = '<a href="?zone='.$z->id.'">'.$z->getField('name', true).'</a>';
|
||||
break;
|
||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE:
|
||||
$extraData[] = TitleList::makeLink($xData['value1']);
|
||||
break;
|
||||
default:
|
||||
if (User::isInGroup(U_GROUP_STAFF))
|
||||
$extraData[] = 'has extra criteria data';
|
||||
}
|
||||
}
|
||||
|
||||
if ($extraData)
|
||||
$crtIcon->extraText .= ' <br /><sup style="margin-left:8px;">('.implode(', ', $extraData).')</sup>';
|
||||
|
||||
$this->criteria[] = $crtIcon;
|
||||
}
|
||||
|
||||
|
||||
/**************/
|
||||
/* Extra Tabs */
|
||||
/**************/
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
|
||||
|
||||
// tab: see also
|
||||
$conditions = array(
|
||||
['name_loc'.Lang::getLocale()->value, $this->subject->getField('name', true)],
|
||||
['id', $this->typeId, '!']
|
||||
);
|
||||
$saList = new AchievementList($conditions);
|
||||
if (!$saList->error)
|
||||
{
|
||||
$this->extendGlobalData($saList->getJSGlobals());
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview(array(
|
||||
'data' => $saList->getListviewData(),
|
||||
'id' => 'see-also',
|
||||
'name' => '$LANG.tab_seealso',
|
||||
'visibleCols' => ['category']
|
||||
), AchievementList::$brickFile));
|
||||
}
|
||||
|
||||
// tab: criteria of
|
||||
$refs = DB::Aowow()->SelectCol('SELECT `refAchievementId` FROM ?_achievementcriteria WHERE `type` = ?d AND `value1` = ?d',
|
||||
ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT,
|
||||
$this->typeId
|
||||
);
|
||||
|
||||
if (!empty($refs))
|
||||
{
|
||||
$coList = new AchievementList(array(['id', $refs]));
|
||||
if (!$coList->error)
|
||||
{
|
||||
$this->extendGlobalData($coList->getJSGlobals());
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview(array(
|
||||
'data' => $coList->getListviewData(),
|
||||
'id' => 'criteria-of',
|
||||
'name' => '$LANG.tab_criteriaof',
|
||||
'visibleCols' => ['category']
|
||||
), AchievementList::$brickFile));
|
||||
}
|
||||
}
|
||||
|
||||
// tab: condition for
|
||||
$cnd = new Conditions();
|
||||
$cnd->getByCondition(Type::ACHIEVEMENT, $this->typeId)->prepare();
|
||||
if ($tab = $cnd->toListviewTab('condition-for', '$LANG.tab_condition_for'))
|
||||
{
|
||||
$this->extendGlobalData($cnd->getJsGlobals());
|
||||
$this->lvTabs->addDataTab(...$tab);
|
||||
}
|
||||
|
||||
parent::generate();
|
||||
|
||||
if ($this->subject->getField('flags') & ACHIEVEMENT_FLAG_REALM_FIRST)
|
||||
$this->result->registerDisplayHook('infobox', [self::class, 'infoboxHook']);
|
||||
}
|
||||
|
||||
private function createMail() : bool
|
||||
{
|
||||
if ($_ = $this->subject->getField('mailTemplate'))
|
||||
{
|
||||
$letter = DB::Aowow()->selectRow('SELECT * FROM ?_mails WHERE `id` = ?d', $_);
|
||||
if (!$letter)
|
||||
return false;
|
||||
|
||||
$this->mail = array(
|
||||
'attachments' => [],
|
||||
'subject' => Util::parseHtmlText(Util::localizedString($letter, 'subject', true)),
|
||||
'text' => Util::parseHtmlText(Util::localizedString($letter, 'text', true)),
|
||||
'header' => [$_, null, null]
|
||||
);
|
||||
}
|
||||
else if ($_ = Util::parseHtmlText($this->subject->getField('text', true, true)))
|
||||
{
|
||||
$this->mail = array(
|
||||
'attachments' => [],
|
||||
'subject' => Util::parseHtmlText($this->subject->getField('subject', true, true)),
|
||||
'text' => $_,
|
||||
'header' => [-$this->typeId, null, null]
|
||||
);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
if ($senderId = $this->subject->getField('sender'))
|
||||
if ($senderName = CreatureList::getName($senderId))
|
||||
$this->mail['header'][1] = Lang::mail('mailBy', [$senderId, $senderName]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* finalize infobox */
|
||||
public static function infoboxHook(Template\PageTemplate &$pt, ?InfoboxMarkup &$markup) : void
|
||||
{
|
||||
// realm first still available?
|
||||
if (!DB::isConnectable(DB_AUTH))
|
||||
return;
|
||||
|
||||
$avlb = [];
|
||||
foreach (Profiler::getRealms() AS $rId => $rData)
|
||||
if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE `achievement` = ?d', $pt->typeId))
|
||||
$avlb[] = Util::ucWords($rData['name']);
|
||||
|
||||
if (!$avlb)
|
||||
return;
|
||||
|
||||
$addRow = Lang::achievement('rfAvailable').implode(', ', $avlb);
|
||||
|
||||
if (!$markup)
|
||||
$markup = new InfoboxMarkup([$addRow], ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
|
||||
else
|
||||
$markup->addItem($addRow);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
50
endpoints/achievement/achievement_power.php
Normal file
50
endpoints/achievement/achievement_power.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AchievementPowerResponse extends TextResponse implements ICache
|
||||
{
|
||||
use TrCache, TrTooltip;
|
||||
|
||||
private const /* string */ POWER_TEMPLATE = '$WowheadPower.registerAchievement(%d, %d, %s);';
|
||||
|
||||
protected int $type = Type::ACHIEVEMENT;
|
||||
protected int $typeId = 0;
|
||||
protected int $cacheType = CACHE_TYPE_TOOLTIP;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'domain' => ['filter' => FILTER_CALLBACK, 'options' => [Locale::class, 'tryFromDomain']]
|
||||
);
|
||||
|
||||
public function __construct($id)
|
||||
{
|
||||
parent::__construct($id);
|
||||
|
||||
// temp locale
|
||||
if ($this->_get['domain'])
|
||||
Lang::load($this->_get['domain']);
|
||||
|
||||
$this->typeId = intVal($id);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$achievement = new AchievementList(array(['id', $this->typeId]));
|
||||
if ($achievement->error)
|
||||
$this->cacheType = CACHE_TYPE_NONE;
|
||||
else
|
||||
$opts = array(
|
||||
'name' => $achievement->getField('name', true),
|
||||
'tooltip' => $achievement->renderTooltip(),
|
||||
'icon' => $achievement->getField('iconString')
|
||||
);
|
||||
|
||||
$this->result = new Tooltip(self::POWER_TEMPLATE, $this->typeId, $opts ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
168
endpoints/achievements/achievements.php
Normal file
168
endpoints/achievements/achievements.php
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AchievementsBaseResponse extends TemplateResponse implements ICache
|
||||
{
|
||||
use TrListPage, TrCache;
|
||||
|
||||
protected int $type = Type::ACHIEVEMENT;
|
||||
protected int $cacheType = CACHE_TYPE_LIST_PAGE;
|
||||
|
||||
protected string $template = 'achievements';
|
||||
protected string $pageName = 'achievements';
|
||||
protected ?int $activeTab = parent::TAB_DATABASE;
|
||||
protected array $breadcrumb = [0, 9];
|
||||
|
||||
protected array $scripts = [[SC_JS_FILE, 'js/filters.js']];
|
||||
protected array $expectedGET = array(
|
||||
'filter' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]]
|
||||
);
|
||||
protected array $validCats = array(
|
||||
92 => true,
|
||||
96 => [14861, 14862, 14863],
|
||||
97 => [14777, 14778, 14779, 14780],
|
||||
95 => [165, 14801, 14802, 14803, 14804, 14881, 14901, 15003],
|
||||
168 => [14808, 14805, 14806, 14921, 14922, 14923, 14961, 14962, 15001, 15002, 15041, 15042],
|
||||
169 => [170, 171, 172],
|
||||
201 => [14864, 14865, 14866],
|
||||
155 => [160, 187, 159, 163, 161, 162, 158, 14981, 156, 14941],
|
||||
81 => true,
|
||||
1 => array (
|
||||
130 => [140, 145, 147, 191],
|
||||
141 => true,
|
||||
128 => [135, 136, 137],
|
||||
122 => [123, 124, 125, 126, 127],
|
||||
133 => true,
|
||||
14807 => [14821, 14822, 14823, 14963, 15021, 15062],
|
||||
132 => [178, 173],
|
||||
134 => true,
|
||||
131 => true,
|
||||
21 => [152, 153, 154]
|
||||
)
|
||||
);
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
$this->getCategoryFromUrl($pageParam);
|
||||
|
||||
parent::__construct($pageParam);
|
||||
|
||||
$this->subCat = $pageParam !== '' ? '='.$pageParam : '';
|
||||
$this->filter = new AchievementListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]);
|
||||
if ($this->filter->shouldReload)
|
||||
{
|
||||
$_SESSION['error']['fi'] = $this->filter::class;
|
||||
$get = $this->filter->buildGETParam();
|
||||
$this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : ''));
|
||||
}
|
||||
$this->filterError = $this->filter->error;
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = Util::ucFirst(Lang::game('achievements'));
|
||||
|
||||
$conditions = [];
|
||||
if (!User::isInGroup(U_GROUP_EMPLOYEE))
|
||||
$conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
|
||||
|
||||
// include child categories if current category is empty
|
||||
if ($this->category)
|
||||
$conditions[] = ['category', end($this->category)];
|
||||
|
||||
if ($fiCnd = $this->filter->getConditions())
|
||||
$conditions[] = $fiCnd;
|
||||
|
||||
|
||||
/*************/
|
||||
/* Menu Path */
|
||||
/*************/
|
||||
|
||||
foreach ($this->category as $cat)
|
||||
$this->breadcrumb[] = $cat;
|
||||
|
||||
|
||||
/**************/
|
||||
/* Page Title */
|
||||
/**************/
|
||||
|
||||
array_unshift($this->title, Util::ucFirst(Lang::game('achievements')));
|
||||
if ($this->category)
|
||||
array_unshift($this->title, Lang::achievement('cat', end($this->category)));
|
||||
|
||||
|
||||
/****************/
|
||||
/* Main Content */
|
||||
/****************/
|
||||
|
||||
// fix modern client achievement category structure: top catg [1:char, 2:statistic, 3:guild]
|
||||
if ($this->category && $this->category[0] != 1)
|
||||
$link = '=1.'.implode('.', $this->category);
|
||||
else if ($this->category)
|
||||
$link = '=2'.(count($this->category) > 1 ? '.'.implode('.', array_slice($this->category, 1)) : '');
|
||||
else
|
||||
$link = '';
|
||||
|
||||
$this->redButtons[BUTTON_WOWHEAD] = true;
|
||||
$this->wowheadLink = sprintf(WOWHEAD_LINK, Lang::getLocale()->domain(), $this->pageName, $link);
|
||||
|
||||
if ($fiQuery = $this->filter->buildGETParam())
|
||||
$this->wowheadLink .= '&filter='.$fiQuery;
|
||||
|
||||
$acvList = new AchievementList($conditions, ['calcTotal' => true]);
|
||||
if (!$acvList->getMatches() && $this->category)
|
||||
{
|
||||
// ToDo - we also branch into here if the filter prohibits results. That should be skipped.
|
||||
$conditions = [];
|
||||
if ($fiCnd)
|
||||
$conditions[] = $fiCnd;
|
||||
if ($catList = DB::Aowow()->SelectCol('SELECT `id` FROM ?_achievementcategory WHERE `parentCat` IN (?a) OR `parentCat2` IN (?a) ', $this->category, $this->category))
|
||||
$conditions[] = ['category', $catList];
|
||||
|
||||
$acvList = new AchievementList($conditions, ['calcTotal' => true]);
|
||||
}
|
||||
|
||||
$tabData = [];
|
||||
if (!$acvList->error)
|
||||
{
|
||||
$tabData['data'] = $acvList->getListviewData();
|
||||
|
||||
// fill g_items, g_titles, g_achievements
|
||||
$this->extendGlobalData($acvList->getJSGlobals());
|
||||
|
||||
// if we are have different cats display field
|
||||
if ($acvList->hasDiffFields('category'))
|
||||
$tabData['visibleCols'] = ['category'];
|
||||
|
||||
if ($this->filter->fiExtraCols)
|
||||
$tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
|
||||
|
||||
// create note if search limit was exceeded
|
||||
if ($acvList->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
|
||||
{
|
||||
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_achievementsfound', $acvList->getMatches(), Cfg::get('SQL_LIMIT_DEFAULT'));
|
||||
$tabData['_truncated'] = 1;
|
||||
}
|
||||
}
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview($tabData, AchievementList::$brickFile));
|
||||
|
||||
parent::generate();
|
||||
|
||||
$this->setOnCacheLoaded([self::class, 'onBeforeDisplay']);
|
||||
}
|
||||
|
||||
public static function onBeforeDisplay()
|
||||
{
|
||||
// sort for dropdown-menus in filter
|
||||
Lang::sort('game', 'si');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
68
endpoints/admin/announcements.php
Normal file
68
endpoints/admin/announcements.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminAnnouncementsResponse extends TemplateResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU;
|
||||
|
||||
protected string $template = 'text-page-generic';
|
||||
protected string $pageName = 'announcements';
|
||||
protected ?int $activeTab = parent::TAB_STAFF;
|
||||
protected array $breadcrumb = [4, 1, 3]; // Staff > Content > Announcements
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'edit' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ],
|
||||
'status' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0, 'max_range' => 2]]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if ($this->_get['id'] && isset($this->_get['status']))
|
||||
{
|
||||
$this->updateStatus();
|
||||
$this->forward($_SERVER['HTTP_REFERER'] ?? '.');
|
||||
}
|
||||
else if ($this->_get['edit'])
|
||||
$this->displayEditor();
|
||||
else
|
||||
$this->displayListing();
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
|
||||
private function updateStatus() : void
|
||||
{
|
||||
if (!$this->assertGET('status', 'id'))
|
||||
{
|
||||
trigger_error('AdminAnnouncementsResponse::updateStatus - error in _GET id/status');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_announcements WHERE `id` = ?d', $this->_get['id']))
|
||||
{
|
||||
trigger_error('AdminAnnouncementsResponse::updateStatus - announcement does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
DB::Aowow()->query('UPDATE ?_announcements SET `status` = ?d WHERE `id` = ?d', $this->_get['status'], $this->_get['id']);
|
||||
}
|
||||
|
||||
private function displayEditor() : void
|
||||
{
|
||||
// TBD
|
||||
$this->extraHTML = 'TODO - editor';
|
||||
}
|
||||
|
||||
private function displayListing() : void
|
||||
{
|
||||
// TBD
|
||||
// some form of listview with [NEW] button somewhere near the head i guess
|
||||
$this->extraHTML = 'TODO - announcements listing';
|
||||
}
|
||||
}
|
||||
51
endpoints/admin/comment.php
Normal file
51
endpoints/admin/comment.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminCommentResponse extends TextResponse
|
||||
{
|
||||
private const /* int */ ERR_NONE = 1;
|
||||
private const /* int */ ERR_WRITE_DB = 0;
|
||||
private const /* int */ ERR_MISCELLANEOUS = 999;
|
||||
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_MOD;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'status' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0, 'max_range' => 1]]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id', 'status'))
|
||||
{
|
||||
trigger_error('AdminCommentResponse - malformed request received', E_USER_ERROR);
|
||||
$this->result = self::ERR_MISCELLANEOUS;
|
||||
return;
|
||||
}
|
||||
|
||||
// check if is marked as outdated CC_FLAG_OUTDATED?
|
||||
|
||||
$ok = false;
|
||||
if ($this->_post['status']) // outdated, mark as deleted and clear other flags (sticky + outdated)
|
||||
{
|
||||
if ($ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = ?d, `deleteUserId` = ?d, `deleteDate` = ?d WHERE `id` = ?d', CC_FLAG_DELETED, User::$id, time(), $this->_post['id']))
|
||||
if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id']))
|
||||
$rep->close(Report::STATUS_CLOSED_SOLVED);
|
||||
}
|
||||
else // up to date
|
||||
{
|
||||
if ($ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']))
|
||||
if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id']))
|
||||
$rep->close(Report::STATUS_CLOSED_WONTFIX);
|
||||
}
|
||||
|
||||
$this->result = $ok ? self::ERR_NONE : self::ERR_WRITE_DB;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
81
endpoints/admin/guide.php
Normal file
81
endpoints/admin/guide.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminGuideResponse extends TextResponse
|
||||
{
|
||||
private const /* int */ ERR_NONE = 0;
|
||||
private const /* int */ ERR_GUIDE = 1;
|
||||
private const /* int */ ERR_STATUS = 2;
|
||||
private const /* int */ ERR_WRITE_DB = 3;
|
||||
private const /* int */ ERR_MISCELLANEOUS = 999;
|
||||
|
||||
protected int $requiredUserGroup = U_GROUP_STAFF;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'status' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => GuideMgr::STATUS_APPROVED, 'max_range' => GuideMgr::STATUS_REJECTED]],
|
||||
'msg' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id', 'status'))
|
||||
{
|
||||
trigger_error('AdminGuideResponse - malformed request received', E_USER_ERROR);
|
||||
$this->result = self::ERR_MISCELLANEOUS;
|
||||
return;
|
||||
}
|
||||
|
||||
$guide = DB::Aowow()->selectRow('SELECT `userId`, `status` FROM ?_guides WHERE `id` = ?d', $this->_post['id']);
|
||||
if (!$guide)
|
||||
{
|
||||
trigger_error('AdminGuideResponse - guide #'.$this->_post['id'].' not found', E_USER_ERROR);
|
||||
$this->result = self::ERR_GUIDE;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->_post['status'] == $guide['status'])
|
||||
{
|
||||
trigger_error('AdminGuideResponse - guide #'.$this->_post['id'].' already has status #'.$this->_post['status'], E_USER_ERROR);
|
||||
$this->result = self::ERR_STATUS;
|
||||
return;
|
||||
}
|
||||
|
||||
// status can only be APPROVED or REJECTED due to input validation
|
||||
if (!$this->update($this->_post['id'], $this->_post['status'], $this->_post['msg']))
|
||||
{
|
||||
trigger_error('AdminGuideResponse - write to db failed for guide #'.$this->_post['id'], E_USER_ERROR);
|
||||
$this->result = self::ERR_WRITE_DB;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->_post['status'] == GuideMgr::STATUS_APPROVED)
|
||||
Util::gainSiteReputation($guide['userId'], SITEREP_ACTION_ARTICLE, ['id' => $this->_post['id']]);
|
||||
|
||||
$this->result = self::ERR_NONE;
|
||||
}
|
||||
|
||||
private function update(int $id, int $status, ?string $msg = null) : bool
|
||||
{
|
||||
if ($status == GuideMgr::STATUS_APPROVED) // set display rev to latest
|
||||
$ok = DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d, `rev` = (SELECT `rev` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d ORDER BY `rev` DESC LIMIT 1), `approveUserId` = ?d, `approveDate` = ?d WHERE `id` = ?d', $status, Type::GUIDE, $id, User::$id, time(), $id);
|
||||
else
|
||||
$ok = DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d WHERE `id` = ?d', $status, $id);
|
||||
|
||||
if (!$ok)
|
||||
return false;
|
||||
|
||||
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `status`) VALUES (?d, ?d, ?d, ?d)', $id, time(), User::$id, $status);
|
||||
if ($msg)
|
||||
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `msg`) VALUES (?d, ?d, ?d, ?)', $id, time(), User::$id, $msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
46
endpoints/admin/guides.php
Normal file
46
endpoints/admin/guides.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminGuidesResponse extends TemplateResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_STAFF;
|
||||
|
||||
protected string $template = 'list-page-generic';
|
||||
protected string $pageName = 'guides';
|
||||
protected ?int $activeTab = parent::TAB_STAFF;
|
||||
protected array $breadcrumb = [4, 1, 25]; // Staff > Content > Guides Awaiting Approval
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = 'Pending Guides';
|
||||
array_unshift($this->title, $this->h1);
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
|
||||
|
||||
parent::generate();
|
||||
|
||||
$pending = new GuideList([['status', GuideMgr::STATUS_REVIEW]]);
|
||||
if ($pending->error)
|
||||
$data = [];
|
||||
else
|
||||
{
|
||||
$data = $pending->getListviewData();
|
||||
$latest = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, MAX(`rev`) FROM ?_articles WHERE `type` = ?d AND `typeId` IN (?a) GROUP BY `rev`', Type::GUIDE, $pending->getFoundIDs());
|
||||
foreach ($latest as $id => $rev)
|
||||
$data[$id]['rev'] = $rev;
|
||||
}
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview(array(
|
||||
'data' => array_values($data),
|
||||
'hiddenCols' => ['patch', 'comments', 'views', 'rating'],
|
||||
'extraCols' => '$_'
|
||||
), GuideList::$brickFile, 'guideAdminCol'));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
34
endpoints/admin/out-of-date.php
Normal file
34
endpoints/admin/out-of-date.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminOutofdateResponse extends TemplateResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_MOD;
|
||||
|
||||
protected string $template = 'list-page-generic';
|
||||
protected string $pageName = 'out-of-date';
|
||||
protected ?int $activeTab = parent::TAB_STAFF;
|
||||
protected array $breadcrumb = [4, 1, 23]; // Staff > Content > Out of Date Comments
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = 'Out of Date Comments';
|
||||
array_unshift($this->title, $this->h1);
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
|
||||
|
||||
parent::generate();
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview(array(
|
||||
'data' => CommunityContent::getCommentPreviews(['flags' => CC_FLAG_OUTDATED]),
|
||||
'extraCols' => '$_'
|
||||
), 'commentpreview', 'commentAdminCol'));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
80
endpoints/admin/phpinfo.php
Normal file
80
endpoints/admin/phpinfo.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminPhpinfoResponse extends TemplateResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_DEV;
|
||||
|
||||
protected string $template = 'list-page-generic';
|
||||
protected string $pageName = 'phpinfo';
|
||||
protected ?int $activeTab = parent::TAB_STAFF;
|
||||
protected array $breadcrumb = [4, 2, 21]; // Staff > Development > PHP Information
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = 'PHP Information';
|
||||
array_unshift($this->title, $this->h1);
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
|
||||
|
||||
parent::generate();
|
||||
|
||||
$this->addScript([SC_CSS_STRING, <<<CSS
|
||||
|
||||
pre { margin: 0px; font-family: monospace; }
|
||||
.d, th { border: 1px solid #000000; vertical-align: baseline; }
|
||||
.p { text-align: left; }
|
||||
.e { background-color: #ccccff; font-weight: bold; color: #000000; }
|
||||
.h { background-color: #9999cc; font-weight: bold; color: #000000; }
|
||||
.v { background-color: #cccccc; color: #000000; }
|
||||
.vr { background-color: #cccccc; text-align: right; color: #000000; }
|
||||
|
||||
CSS]);
|
||||
|
||||
$bits = [INFO_GENERAL, INFO_CONFIGURATION, INFO_ENVIRONMENT, INFO_MODULES];
|
||||
$names = ['General', '', '', 'Module'];
|
||||
foreach ($bits as $i => $b)
|
||||
{
|
||||
ob_start();
|
||||
phpinfo($b);
|
||||
$buff = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$buff = explode('<div class="center">', $buff)[1];
|
||||
$buff = explode('</div>', $buff);
|
||||
array_pop($buff); // remove last from stack
|
||||
$buff = implode('</div>', $buff); // sew it together
|
||||
|
||||
if (strpos($buff, '<h1>'))
|
||||
$buff = explode('</h1>', $buff)[1];
|
||||
|
||||
if (strpos($buff, '<h2>'))
|
||||
{
|
||||
$parts = explode('<h2>', $buff);
|
||||
foreach ($parts as $p)
|
||||
{
|
||||
if (!preg_match('/\w/i', $p))
|
||||
continue;
|
||||
|
||||
$p = explode('</h2>', $p);
|
||||
$name = $names[$i] ? $names[$i].': ' : '';
|
||||
if (preg_match('/<a[^>]*>([\w\s\d]+)<\/a>/i', $p[0], $m))
|
||||
$name .= $m[1];
|
||||
else
|
||||
$name .= $p[0];
|
||||
|
||||
$this->lvTabs->addDataTab(strtolower(strtr($name, [' ' => ''])), $name, $p[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
$this->lvTabs->addDataTab(strtolower($names[$i]), $names[$i], $buff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
29
endpoints/admin/reports.php
Normal file
29
endpoints/admin/reports.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminReportsResponse extends TemplateResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_EDITOR | U_GROUP_MOD | U_GROUP_LOCALIZER | U_GROUP_SCREENSHOT | U_GROUP_VIDEO;
|
||||
|
||||
protected string $template = 'admin/reports';
|
||||
protected string $pageName = 'reports';
|
||||
protected ?int $activeTab = parent::TAB_STAFF;
|
||||
protected array $breadcrumb = [4, 5]; // Staff > Reports
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = 'Reports';
|
||||
array_unshift($this->title, $this->h1);
|
||||
|
||||
$this->extraHTML = 'NYI';
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
68
endpoints/admin/screenshots.php
Normal file
68
endpoints/admin/screenshots.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminScreenshotsResponse extends TemplateResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
|
||||
|
||||
protected string $template = 'admin/screenshots';
|
||||
protected string $pageName = 'screenshots';
|
||||
protected ?int $activeTab = parent::TAB_STAFF;
|
||||
protected array $breadcrumb = [4, 1, 5]; // Staff > Content > Screenshots
|
||||
|
||||
protected array $scripts = array(
|
||||
[SC_JS_FILE, 'js/screenshot.js'],
|
||||
[SC_CSS_STRING, '.layout {margin: 0px 25px; max-width: inherit; min-width: 1200px; }'],
|
||||
[SC_CSS_STRING, '#highlightedRow { background-color: #322C1C; }']
|
||||
);
|
||||
protected array $expectedGET = array(
|
||||
'action' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
|
||||
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']],
|
||||
'type' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'typeid' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'urldecode' ]
|
||||
);
|
||||
|
||||
public ?bool $getAll = null;
|
||||
public array $ssPages = [];
|
||||
public array $ssData = [];
|
||||
public int $ssNFound = 0;
|
||||
public array $pageTypes = [];
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = 'Screenshot Manager';
|
||||
|
||||
// types that can have screenshots
|
||||
foreach (Type::getClassesFor(0, 'contribute', CONTRIBUTE_SS) as $type => $obj)
|
||||
$this->pageTypes[$type] = Util::ucWords(Lang::game(Type::getFileString($type)));
|
||||
|
||||
$ssGetAll = $this->_get['all'];
|
||||
$ssPages = [];
|
||||
$ssData = [];
|
||||
$nMatches = 0;
|
||||
|
||||
if ($this->_get['type'] && $this->_get['typeid'])
|
||||
$ssData = ScreenshotMgr::getScreenshots($this->_get['type'], $this->_get['typeid'], nFound: $nMatches);
|
||||
else if ($this->_get['user'])
|
||||
{
|
||||
if (mb_strlen($this->_get['user']) >= 3)
|
||||
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user']))
|
||||
$ssData = ScreenshotMgr::getScreenshots(userId: $uId, nFound: $nMatches);
|
||||
}
|
||||
else
|
||||
$ssPages = ScreenshotMgr::getPages($ssGetAll, $nMatches);
|
||||
|
||||
$this->getAll = $ssGetAll;
|
||||
$this->ssPages = $ssPages;
|
||||
$this->ssData = $ssData;
|
||||
$this->ssNFound = $nMatches; // ssm_numPagesFound
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
}
|
||||
62
endpoints/admin/screenshots_approve.php
Normal file
62
endpoints/admin/screenshots_approve.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
use GdImage;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminScreenshotsActionApproveResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id'))
|
||||
{
|
||||
trigger_error('AdminScreenshotsActionApproveResponse - screenshotId empty', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ScreenshotMgr::init();
|
||||
|
||||
// create resized and thumb version of screenshot
|
||||
$ssEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId` FROM ?_screenshots WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_APPROVED, $this->_get['id']);
|
||||
foreach ($ssEntries as $id => $ssData)
|
||||
{
|
||||
if (!ScreenshotMgr::loadFile(ScreenshotMgr::PATH_PENDING, $id))
|
||||
continue;
|
||||
|
||||
if (!ScreenshotMgr::createResized($id))
|
||||
continue;
|
||||
|
||||
if (!ScreenshotMgr::createThumbnail($id))
|
||||
continue;
|
||||
|
||||
// move pending > normal
|
||||
if (!rename(sprintf(ScreenshotMgr::PATH_PENDING, $id), sprintf(ScreenshotMgr::PATH_NORMAL, $id)))
|
||||
continue;
|
||||
|
||||
// set as approved in DB
|
||||
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id);
|
||||
|
||||
// gain siterep
|
||||
Util::gainSiteReputation($ssData['userIdOwner'], SITEREP_ACTION_SUBMIT_SCREENSHOT, ['id' => $id, 'what' => 1, 'date' => $ssData['date']]);
|
||||
|
||||
// flag DB entry as having screenshots
|
||||
if ($tbl = Type::getClassAttrib($ssData['type'], 'dataTable'))
|
||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $ssData['typeId']);
|
||||
|
||||
unset($ssEntries[$id]);
|
||||
}
|
||||
|
||||
if (!$ssEntries)
|
||||
trigger_error('AdminScreenshotsActionApproveResponse - screenshot(s) # '.implode(', ', array_keys($ssEntries)).' not in db or already approved', E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
62
endpoints/admin/screenshots_delete.php
Normal file
62
endpoints/admin/screenshots_delete.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminScreenshotsActionDeleteResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
|
||||
);
|
||||
|
||||
// 2 steps: 1) remove from sight, 2) remove from disk
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id'))
|
||||
{
|
||||
trigger_error('AdminScreenshotsActionDeleteResponse - screenshotId empty', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->_get['id'] as $id)
|
||||
{
|
||||
// irrevocably purge files already flagged as deleted (should only exist as pending)
|
||||
if (User::isInGroup(U_GROUP_ADMIN) && DB::Aowow()->selectCell('SELECT 1 FROM ?_screenshots WHERE `status` & ?d AND `id` = ?d', CC_FLAG_DELETED, $id))
|
||||
{
|
||||
DB::Aowow()->query('DELETE FROM ?_screenshots WHERE `id` = ?d', $id);
|
||||
if (file_exists(sprintf(ScreenshotMgr::PATH_PENDING, $id)))
|
||||
unlink(sprintf(ScreenshotMgr::PATH_PENDING, $id));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// move normal to pending and remove resized and thumb
|
||||
if (file_exists(sprintf(ScreenshotMgr::PATH_NORMAL, $id)))
|
||||
rename(sprintf(ScreenshotMgr::PATH_NORMAL, $id), sprintf(ScreenshotMgr::PATH_PENDING, $id));
|
||||
|
||||
if (file_exists(sprintf(ScreenshotMgr::PATH_THUMB, $id)))
|
||||
unlink(sprintf(ScreenshotMgr::PATH_THUMB, $id));
|
||||
|
||||
if (file_exists(sprintf(ScreenshotMgr::PATH_RESIZED, $id)))
|
||||
unlink(sprintf(ScreenshotMgr::PATH_RESIZED, $id));
|
||||
}
|
||||
|
||||
// flag as deleted if not aready
|
||||
$oldEntries = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, GROUP_CONCAT(`typeId`) FROM ?_screenshots WHERE `id` IN (?a) GROUP BY `type`', $this->_get['id']);
|
||||
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = ?d, `userIdDelete` = ?d WHERE `id` IN (?a)', CC_FLAG_DELETED, User::$id, $this->_get['id']);
|
||||
|
||||
// deflag db entry as having screenshots
|
||||
foreach ($oldEntries as $type => $typeIds)
|
||||
{
|
||||
$typeIds = explode(',', $typeIds);
|
||||
$toUnflag = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, IF(BIT_OR(`status`) & ?d, 1, 0) AS "hasMore" FROM ?_screenshots WHERE `type` = ?d AND `typeId` IN (?a) GROUP BY `typeId` HAVING `hasMore` = 0', CC_FLAG_APPROVED, $type, $typeIds);
|
||||
if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable')))
|
||||
DB::Aowow()->query('UPDATE ?# SET cuFlags = cuFlags & ~?d WHERE id IN (?a)', $tbl, CUSTOM_HAS_SCREENSHOT, array_keys($toUnflag));
|
||||
}
|
||||
}
|
||||
}
|
||||
32
endpoints/admin/screenshots_editalt.php
Normal file
32
endpoints/admin/screenshots_editalt.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminScreenshotsActionEditaltResponse extends TextResponse
|
||||
{
|
||||
use TrCommunityHelper;
|
||||
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
protected array $expectedPOST = array(
|
||||
'alt' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id'))
|
||||
return;
|
||||
|
||||
DB::Aowow()->query('UPDATE ?_screenshots SET `caption` = ? WHERE `id` = ?d',
|
||||
$this->handleCaption($this->_post['alt']),
|
||||
$this->_get['id']
|
||||
);
|
||||
}
|
||||
}
|
||||
23
endpoints/admin/screenshots_list.php
Normal file
23
endpoints/admin/screenshots_list.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminScreenshotsActionListResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$pages = ScreenshotMgr::getPages($this->_get['all'], $nPages);
|
||||
$this->result = 'ssm_screenshotPages = '.Util::toJSON($pages).";\n";
|
||||
$this->result .= 'ssm_numPagesFound = '.$nPages.';';
|
||||
}
|
||||
}
|
||||
31
endpoints/admin/screenshots_manage.php
Normal file
31
endpoints/admin/screenshots_manage.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminScreenshotsActionManageResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'type' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'typeid' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'urldecode']
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$res = [];
|
||||
|
||||
if ($this->_get['type'] && $this->_get['typeid'])
|
||||
$res = ScreenshotMgr::getScreenshots($this->_get['type'], $this->_get['typeid']);
|
||||
else if ($this->_get['user'])
|
||||
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user']))
|
||||
$res = ScreenshotMgr::getScreenshots(userId: $uId);
|
||||
|
||||
$this->result = 'ssm_screenshotData = '.Util::toJSON($res);
|
||||
}
|
||||
}
|
||||
48
endpoints/admin/screenshots_relocate.php
Normal file
48
endpoints/admin/screenshots_relocate.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminScreenshotsActionRelocateResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT],
|
||||
'typeid' => ['filter' => FILTER_VALIDATE_INT]
|
||||
// (but not type..?)
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id', 'typeid'))
|
||||
{
|
||||
trigger_error('AdminScreenshotsActionRelocateResponse - screenshotId or typeId empty', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
[$type, $oldTypeId] = array_values(DB::Aowow()->selectRow('SELECT `type`, `typeId` FROM ?_screenshots WHERE `id` = ?d', $this->_get['id']));
|
||||
$typeId = $this->_get['typeid'];
|
||||
|
||||
if (Type::validateIds($type, $typeId))
|
||||
{
|
||||
$tbl = Type::getClassAttrib($type, 'dataTable');
|
||||
|
||||
// move screenshot
|
||||
DB::Aowow()->query('UPDATE ?_screenshots SET `typeId` = ?d WHERE `id` = ?d', $typeId, $this->_get['id']);
|
||||
|
||||
// flag target as having screenshot
|
||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $typeId);
|
||||
|
||||
// deflag source for having had screenshots (maybe)
|
||||
$ssInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~`status`) & ?d, 1, 0) AS "hasMore" FROM ?_screenshots WHERE `status`& ?d AND `type` = ?d AND `typeId` = ?d', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId);
|
||||
if ($ssInfo || !$ssInfo['hasMore'])
|
||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $oldTypeId);
|
||||
}
|
||||
else
|
||||
trigger_error('AdminScreenshotsActionRelocateResponse - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
72
endpoints/admin/screenshots_sticky.php
Normal file
72
endpoints/admin/screenshots_sticky.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminScreenshotsActionStickyResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id'))
|
||||
{
|
||||
trigger_error('AdminScreenshotsActionStickyResponse - screenshotId empty', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// this one is a bit strange: as far as i've seen, the only thing a 'sticky' screenshot does is show up in the infobox
|
||||
// this also means, that only one screenshot per page should be sticky
|
||||
// so, handle it one by one and the last one affecting one particular type/typId-key gets the cake
|
||||
$ssEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId`, `status` FROM ?_screenshots WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_DELETED, $this->_get['id']);
|
||||
foreach ($ssEntries as $id => $ssData)
|
||||
{
|
||||
// approve yet unapproved screenshots
|
||||
if (!($ssData['status'] & CC_FLAG_APPROVED))
|
||||
{
|
||||
ScreenshotMgr::init();
|
||||
|
||||
if (!ScreenshotMgr::loadFile(ScreenshotMgr::PATH_PENDING, $id))
|
||||
continue;
|
||||
|
||||
if (!ScreenshotMgr::createResized($id))
|
||||
continue;
|
||||
|
||||
if (!ScreenshotMgr::createThumbnail($id))
|
||||
continue;
|
||||
|
||||
// move pending > normal
|
||||
if (!rename(sprintf(ScreenshotMgr::PATH_PENDING, $id), sprintf(ScreenshotMgr::PATH_NORMAL, $id)))
|
||||
continue;
|
||||
|
||||
// set as approved in DB
|
||||
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id);
|
||||
|
||||
// gain siterep
|
||||
Util::gainSiteReputation($ssData['userIdOwner'], SITEREP_ACTION_SUBMIT_SCREENSHOT, ['id' => $id, 'what' => 1, 'date' => $ssData['date']]);
|
||||
|
||||
// flag DB entry as having screenshots
|
||||
if ($tbl = Type::getClassAttrib($ssData['type'], 'dataTable'))
|
||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $ssData['typeId']);
|
||||
}
|
||||
|
||||
// reset all others
|
||||
DB::Aowow()->query('UPDATE ?_screenshots a, ?_screenshots b SET a.`status` = a.`status` & ~?d WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND a.`id` <> b.`id` AND b.`id` = ?d', CC_FLAG_STICKY, $id);
|
||||
|
||||
// toggle sticky status
|
||||
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = IF(`status` & ?d, `status` & ~?d, `status` | ?d) WHERE `id` = ?d AND `status` & ?d', CC_FLAG_STICKY, CC_FLAG_STICKY, CC_FLAG_STICKY, $id, CC_FLAG_APPROVED);
|
||||
|
||||
unset($ssEntries[$id]);
|
||||
}
|
||||
|
||||
if ($ssEntries)
|
||||
trigger_error('AdminScreenshotsActionStickyResponse - screenshot(s) # '.implode(', ', array_keys($ssEntries)).' not in db or flagged as deleted', E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
113
endpoints/admin/siteconfig.php
Normal file
113
endpoints/admin/siteconfig.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminSiteconfigResponse extends TemplateResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_DEV;
|
||||
|
||||
protected string $template = 'admin/siteconfig';
|
||||
protected string $pageName = 'siteconfig';
|
||||
protected ?int $activeTab = parent::TAB_STAFF;
|
||||
protected array $breadcrumb = [4, 2, 18]; // Staff > Development > Site Configuration
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = 'Site Configuration';
|
||||
array_unshift($this->title, $this->h1);
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
|
||||
|
||||
parent::generate();
|
||||
|
||||
$this->addScript([SC_CSS_STRING, <<<CSS
|
||||
|
||||
.grid input[type='text'], .grid input[type='number'] { width:250px; text-align:left; }
|
||||
.grid input[type='button'] { width:65px; padding:2px; }
|
||||
.grid a.tip { margin:0px 5px; opacity:0.8; }
|
||||
.grid a.tip:hover { opacity:1; }
|
||||
.grid tr { height:30px; }
|
||||
.grid .disabled { opacity:0.4 !important; }
|
||||
.grid .status { position:absolute; right:5px; }
|
||||
|
||||
CSS]);
|
||||
|
||||
$head = '<tr><th><b>Key</b></th><th><b>Value</b></th><th style="width:150px;"><b>Options</b></th></tr>';
|
||||
foreach (Cfg::$categories as $idx => $catName)
|
||||
{
|
||||
$rows = '';
|
||||
foreach (Cfg::forCategory($idx) as $key => [$value, $flags, , $default, $comment])
|
||||
$rows .= $this->buildRow($key, $value, $flags, $default, $comment);
|
||||
|
||||
if ($idx == Cfg::CAT_MISCELLANEOUS)
|
||||
$rows .= '<tr><td colspan="3"><a class="icon-add" onclick="cfg_add(this)">new configuration</a></td></tr>';
|
||||
|
||||
if (!$rows)
|
||||
continue;
|
||||
|
||||
$this->lvTabs->addDataTab(Profiler::urlize($catName), $catName, '<table class="grid">' . $head . $rows . '</table>');
|
||||
}
|
||||
}
|
||||
|
||||
private function buildRow(string $key, string $value, int $flags, ?string $default, string $comment) : string
|
||||
{
|
||||
$buff = '<tr>';
|
||||
$info = explode(' - ', $comment);
|
||||
$key = $flags & Cfg::FLAG_PHP ? strtolower($key) : strtoupper($key);
|
||||
|
||||
// name
|
||||
if (!empty($info[0]))
|
||||
$buff .= '<td>'.sprintf(Util::$dfnString, $info[0], $key).'</td>';
|
||||
else
|
||||
$buff .= '<td>'.$key.'</td>';
|
||||
|
||||
// value
|
||||
if ($flags & Cfg::FLAG_TYPE_BOOL)
|
||||
$buff .= '<td><div id="'.$key.'"><input id="'.$key.'1" type="radio" name="'.$key.'" value="1" '.($value ? 'checked' : null).' /><label for="'.$key.'1">Enabled</label> <input id="'.$key.'0" type="radio" name="'.$key.'" value="0" '.($value ? null : 'checked').' /><label for="'.$key.'0">Disabled</label></div></td>';
|
||||
else if ($flags & Cfg::FLAG_OPT_LIST && !empty($info[1]))
|
||||
{
|
||||
$buff .= '<td><select id="'.$key.'" name="'.$key.'">';
|
||||
foreach (explode(', ', $info[1]) as $option)
|
||||
{
|
||||
[$idx, $name] = explode(':', $option);
|
||||
$buff .= '<option value="'.$idx.'"'.($value == $idx ? ' selected ' : null).'>'.$name.'</option>';
|
||||
}
|
||||
$buff .= '</select></td>';
|
||||
}
|
||||
else if ($flags & Cfg::FLAG_BITMASK && !empty($info[1]))
|
||||
{
|
||||
$buff .= '<td><div id="'.$key.'">';
|
||||
foreach (explode(', ', $info[1]) as $option)
|
||||
{
|
||||
[$idx, $name] = explode(':', $option);
|
||||
$buff .= '<input id="'.$key.$idx.'" type="checkbox" name="'.$key.'" value="'.$idx.'"'.($value & (1 << $idx) ? ' checked ' : null).'><label for="'.$key.$idx.'">'.$name.'</label>';
|
||||
}
|
||||
$buff .= '</div></td>';
|
||||
}
|
||||
else
|
||||
$buff .= '<td><input id="'.$key.'" type="'.($flags & Cfg::FLAG_TYPE_STRING ? 'text" placeholder="<empty>' : 'number'.($flags & Cfg::FLAG_TYPE_FLOAT ? '" step="any' : '')).'" name="'.$key.'" value="'.$value.'" /></td>';
|
||||
|
||||
// actions
|
||||
$buff .= '<td style="position:relative;">';
|
||||
|
||||
$buff .= '<a class="icon-save tip" onclick="cfg_submit.bind(this, \''.$key.'\')()" onmouseover="$WH.Tooltip.showAtCursor(event, \'Save Changes\', 0, 0, \'q\')" onmousemove="$WH.Tooltip.cursorUpdate(event)" onmouseout="$WH.Tooltip.hide()"></a>';
|
||||
|
||||
if ($default)
|
||||
$buff .= '|<a class="icon-refresh tip" onclick="cfg_default(\''.$key.'\', \''.$default.'\')" onmouseover="$WH.Tooltip.showAtCursor(event, \'Restore Default Value\', 0, 0, \'q\')" onmousemove="$WH.Tooltip.cursorUpdate(event)" onmouseout="$WH.Tooltip.hide()"></a>';
|
||||
else
|
||||
$buff .= '|<a class="icon-refresh tip disabled"></a>';
|
||||
|
||||
if (!($flags & Cfg::FLAG_PERSISTENT))
|
||||
$buff .= '|<a class="icon-delete tip" onclick="cfg_remove.bind(this, \''.$key.'\')()" onmouseover="$WH.Tooltip.showAtCursor(event, \'Remove Setting\', 0, 0, \'q\')" onmousemove="$WH.Tooltip.cursorUpdate(event)" onmouseout="$WH.Tooltip.hide()"></a>';
|
||||
|
||||
$buff .= '<span class="status"></span></td></tr>';
|
||||
|
||||
return $buff;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
34
endpoints/admin/siteconfig_add.php
Normal file
34
endpoints/admin/siteconfig_add.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminSiteconfigActionAddResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_DEV | U_GROUP_ADMIN;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Cfg::PATTERN_CONF_KEY_FULL]],
|
||||
'val' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('key', 'val'))
|
||||
{
|
||||
trigger_error('AdminSiteconfigActionAddResponse - malformed request received', E_USER_ERROR);
|
||||
$this->result = Lang::main('intError');
|
||||
return;
|
||||
}
|
||||
|
||||
$key = trim($this->_get['key']);
|
||||
$val = trim(urldecode($this->_get['val']));
|
||||
|
||||
$this->result = Cfg::add($key, $val);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
30
endpoints/admin/siteconfig_remove.php
Normal file
30
endpoints/admin/siteconfig_remove.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminSiteconfigActionRemoveResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_DEV | U_GROUP_ADMIN;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Cfg::PATTERN_CONF_KEY_FULL]]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('key'))
|
||||
{
|
||||
trigger_error('AdminSiteconfigActionRemoveResponse - malformed request received', E_USER_ERROR);
|
||||
$this->result = Lang::main('intError');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->result = Cfg::delete($this->_get['key']);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
34
endpoints/admin/siteconfig_update.php
Normal file
34
endpoints/admin/siteconfig_update.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminSiteconfigActionUpdateResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_DEV | U_GROUP_ADMIN;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Cfg::PATTERN_CONF_KEY_FULL]],
|
||||
'val' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('key', 'val'))
|
||||
{
|
||||
trigger_error('AdminSiteconfigActionUpdateResponse - malformed request received', E_USER_ERROR);
|
||||
$this->result = Lang::main('intError');
|
||||
return;
|
||||
}
|
||||
|
||||
$key = trim($this->_get['key']);
|
||||
$val = trim(urldecode($this->_get['val']));
|
||||
|
||||
$this->result = Cfg::set($key, $val);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
116
endpoints/admin/spawn-override.php
Normal file
116
endpoints/admin/spawn-override.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
use Error;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminSpawnoverrideResponse extends TextResponse
|
||||
{
|
||||
private const /* int */ ERR_NONE = 0;
|
||||
private const /* int */ ERR_NO_POINTS = 1;
|
||||
private const /* int */ ERR_WORLD_POS = 2;
|
||||
private const /* int */ ERR_WRONG_TYPE = 3;
|
||||
private const /* int */ ERR_WRITE_DB = 4;
|
||||
private const /* int */ ERR_MISCELLANEOUS = 999;
|
||||
|
||||
protected int $requiredUserGroup = U_GROUP_MODERATOR;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'type' => ['filter' => FILTER_VALIDATE_INT],
|
||||
'guid' => ['filter' => FILTER_VALIDATE_INT],
|
||||
'area' => ['filter' => FILTER_VALIDATE_INT],
|
||||
'floor' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('type', 'guid', 'area', 'floor'))
|
||||
{
|
||||
trigger_error('AdminSpawnoverrideResponse - malformed request received', E_USER_ERROR);
|
||||
$this->result = self::ERR_MISCELLANEOUS;
|
||||
return;
|
||||
}
|
||||
|
||||
$guid = $this->_get['guid'];
|
||||
$type = $this->_get['type'];
|
||||
$area = $this->_get['area'];
|
||||
$floor = $this->_get['floor'];
|
||||
|
||||
if (!in_array($type, [Type::NPC, Type::OBJECT, Type::SOUND, Type::AREATRIGGER, Type::ZONE]))
|
||||
{
|
||||
trigger_error('AdminSpawnoverrideResponse - can\'t move pip of type '.Type::getFileString($type), E_USER_ERROR);
|
||||
$this->result = self::ERR_WRONG_TYPE;
|
||||
return;
|
||||
}
|
||||
|
||||
DB::Aowow()->query('REPLACE INTO ?_spawns_override (`type`, `typeGuid`, `areaId`, `floor`, `revision`) VALUES (?d, ?d, ?d, ?d, ?d)', $type, $guid, $area, $floor, AOWOW_REVISION);
|
||||
|
||||
$wPos = WorldPosition::getForGUID($type, $guid);
|
||||
if (!$wPos)
|
||||
{
|
||||
$this->result = self::ERR_WORLD_POS;
|
||||
return;
|
||||
}
|
||||
|
||||
$point = WorldPosition::toZonePos($wPos[$guid]['mapId'], $wPos[$guid]['posX'], $wPos[$guid]['posY'], $area, $floor);
|
||||
if (!$point)
|
||||
{
|
||||
$this->result = self::ERR_NO_POINTS;
|
||||
return;
|
||||
}
|
||||
|
||||
$updGUIDs = [$guid];
|
||||
$newPos = array(
|
||||
'posX' => $point[0]['posX'],
|
||||
'posY' => $point[0]['posY'],
|
||||
'areaId' => $point[0]['areaId'],
|
||||
'floor' => $point[0]['floor']
|
||||
);
|
||||
|
||||
// if creature try for waypoints
|
||||
if ($type == Type::NPC)
|
||||
{
|
||||
$jobs = array(
|
||||
'SELECT -w.`id` AS "entry", w.`point` AS "pointId", w.`position_x` AS "posX", w.`position_y` AS "posY" FROM creature_addon ca JOIN waypoint_data w ON w.`id` = ca.`path_id` WHERE ca.`guid` = ?d AND ca.`path_id` <> 0',
|
||||
'SELECT `entry`, `pointId`, `location_x` AS "posX", `location_y` AS "posY" FROM `script_waypoint` WHERE `entry` = ?d',
|
||||
'SELECT `entry`, `pointId`, `position_x` AS "posX", `position_y` AS "posY" FROM `waypoints` WHERE `entry` = ?d'
|
||||
);
|
||||
|
||||
foreach ($jobs as $idx => $job)
|
||||
{
|
||||
if ($swp = DB::World()->select($job, $idx ? $wPos[$guid]['id'] : $guid))
|
||||
{
|
||||
foreach ($swp as $w)
|
||||
{
|
||||
if ($point = WorldPosition::toZonePos($wPos[$guid]['mapId'], $w['posX'], $w['posY'], $area, $floor))
|
||||
{
|
||||
$p = array(
|
||||
'posX' => $point[0]['posX'],
|
||||
'posY' => $point[0]['posY'],
|
||||
'areaId' => $point[0]['areaId'],
|
||||
'floor' => $point[0]['floor']
|
||||
);
|
||||
|
||||
DB::Aowow()->query('UPDATE ?_creature_waypoints SET ?a WHERE `creatureOrPath` = ?d AND `point` = ?d', $p, $w['entry'], $w['pointId']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// also move linked vehicle accessories (on the very same position)
|
||||
$updGUIDs = array_merge($updGUIDs, DB::Aowow()->selectCol('SELECT s2.`guid` FROM ?_spawns s1 JOIN ?_spawns s2 ON s1.`posX` = s2.`posX` AND s1.`posY` = s2.`posY` AND
|
||||
s1.`areaId` = s2.`areaId` AND s1.`floor` = s2.`floor` AND s2.`guid` < 0 WHERE s1.`guid` = ?d', $guid));
|
||||
}
|
||||
|
||||
if (DB::Aowow()->query('UPDATE ?_spawns SET ?a WHERE `type` = ?d AND `guid` IN (?a)', $newPos, $type, $updGUIDs))
|
||||
$this->result = self::ERR_NONE;
|
||||
else
|
||||
$this->result = self::ERR_WRITE_DB;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
68
endpoints/admin/videos.php
Normal file
68
endpoints/admin/videos.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminVideosResponse extends TemplateResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
|
||||
|
||||
protected string $template = 'admin/videos';
|
||||
protected string $pageName = 'videos';
|
||||
protected ?int $activeTab = parent::TAB_STAFF;
|
||||
protected array $breadcrumb = [4, 1, 17]; // Staff > Content > Videos
|
||||
|
||||
protected array $scripts = array(
|
||||
[SC_JS_FILE, 'js/video.js'],
|
||||
[SC_CSS_STRING, '.layout {margin: 0px 25px; max-width: inherit; min-width: 1200px; }'],
|
||||
[SC_CSS_STRING, '#highlightedRow { background-color: #322C1C; }']
|
||||
);
|
||||
protected array $expectedGET = array(
|
||||
'action' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
|
||||
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']],
|
||||
'type' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'typeid' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'urldecode' ]
|
||||
);
|
||||
|
||||
public ?bool $getAll = null;
|
||||
public array $viPages = [];
|
||||
public array $viData = [];
|
||||
public int $viNFound = 0;
|
||||
public array $pageTypes = [];
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = 'Video Manager';
|
||||
|
||||
// types that can have videos
|
||||
foreach (Type::getClassesFor(0, 'contribute', CONTRIBUTE_SS) as $type => $obj)
|
||||
$this->pageTypes[$type] = Util::ucWords(Lang::game(Type::getFileString($type)));
|
||||
|
||||
$viGetAll = $this->_get['all'];
|
||||
$viPages = [];
|
||||
$viData = [];
|
||||
$nMatches = 0;
|
||||
|
||||
if ($this->_get['type'] && $this->_get['typeid'])
|
||||
$viData = VideoMgr::getVideos($this->_get['type'], $this->_get['typeid'], nFound: $nMatches);
|
||||
else if ($this->_get['user'])
|
||||
{
|
||||
if (mb_strlen($this->_get['user']) >= 3)
|
||||
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user']))
|
||||
$viData = VideoMgr::getVideos(userId: $uId, nFound: $nMatches);
|
||||
}
|
||||
else
|
||||
$viPages = VideoMgr::getPages($viGetAll, $nMatches);
|
||||
|
||||
$this->getAll = $viGetAll;
|
||||
$this->viPages = $viPages;
|
||||
$this->viData = $viData;
|
||||
$this->viNFound = $nMatches; // ssm_numPagesFound
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
}
|
||||
46
endpoints/admin/videos_approve.php
Normal file
46
endpoints/admin/videos_approve.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
use GdImage;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminVideosActionApproveResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id'))
|
||||
{
|
||||
trigger_error('AdminVideosActionApproveResponse - videoId empty', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
$viEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId` FROM ?_videos WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_APPROVED, $this->_get['id']);
|
||||
foreach ($viEntries as $id => $viData)
|
||||
{
|
||||
// set as approved in DB
|
||||
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id);
|
||||
|
||||
// gain siterep
|
||||
Util::gainSiteReputation($viData['userIdOwner'], SITEREP_ACTION_SUGGEST_VIDEO, ['id' => $id, 'what' => 1, 'date' => $viData['date']]);
|
||||
|
||||
// flag DB entry as having videos
|
||||
if ($tbl = Type::getClassAttrib($viData['type'], 'dataTable'))
|
||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $viData['typeId']);
|
||||
|
||||
unset($viEntries[$id]);
|
||||
}
|
||||
|
||||
if (!$viEntries)
|
||||
trigger_error('AdminVideosActionApproveResponse - video(s) # '.implode(', ', array_keys($viEntries)).' not in db or already approved', E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
43
endpoints/admin/videos_delete.php
Normal file
43
endpoints/admin/videos_delete.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminVideosActionDeleteResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
|
||||
);
|
||||
|
||||
// 2 steps: 1) remove from sight, 2) remove from disk
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id'))
|
||||
{
|
||||
trigger_error('AdminVideosActionDeleteResponse - videoId empty', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// irrevocably purge files already flagged as deleted (should only exist as pending)
|
||||
if (User::isInGroup(U_GROUP_ADMIN))
|
||||
DB::Aowow()->selectCell('SELECT 1 FROM ?_videos WHERE `status` & ?d AND `id` IN (?a)', CC_FLAG_DELETED, $this->_get['id']);
|
||||
|
||||
// flag as deleted if not aready
|
||||
$oldEntries = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, GROUP_CONCAT(`typeId`) FROM ?_videos WHERE `id` IN (?a) GROUP BY `type`', $this->_get['id']);
|
||||
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdDelete` = ?d WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_DELETED, User::$id, CC_FLAG_DELETED, $this->_get['id']);
|
||||
|
||||
// deflag db entry as having videos
|
||||
foreach ($oldEntries as $type => $typeIds)
|
||||
{
|
||||
$typeIds = explode(',', $typeIds);
|
||||
$toUnflag = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, IF(BIT_OR(`status`) & ?d, 1, 0) AS "hasMore" FROM ?_videos WHERE `type` = ?d AND `typeId` IN (?a) GROUP BY `typeId` HAVING `hasMore` = 0', CC_FLAG_APPROVED, $type, $typeIds);
|
||||
if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable')))
|
||||
DB::Aowow()->query('UPDATE ?# SET cuFlags = cuFlags & ~?d WHERE id IN (?a)', $tbl, CUSTOM_HAS_VIDEO, array_keys($toUnflag));
|
||||
}
|
||||
}
|
||||
}
|
||||
31
endpoints/admin/videos_edittitle.php
Normal file
31
endpoints/admin/videos_edittitle.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminVideosActionEdittitleResponse extends TextResponse
|
||||
{
|
||||
use TrCommunityHelper;
|
||||
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
|
||||
);
|
||||
protected array $expectedPOST = array(
|
||||
'title' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id'))
|
||||
return;
|
||||
|
||||
$caption = $this->handleCaption($this->_post['title']);
|
||||
|
||||
DB::Aowow()->query('UPDATE ?_videos SET `caption` = ? WHERE `id` = ?d', $caption, $this->_get['id'][0]);
|
||||
}
|
||||
}
|
||||
23
endpoints/admin/videos_list.php
Normal file
23
endpoints/admin/videos_list.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminVideosActionListResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$pages = VideoMgr::getPages($this->_get['all'], $nPages);
|
||||
$this->result = 'vim_videoPages = '.Util::toJSON($pages).";\n";
|
||||
$this->result .= 'vim_numPagesFound = '.$nPages.';';
|
||||
}
|
||||
}
|
||||
31
endpoints/admin/videos_manage.php
Normal file
31
endpoints/admin/videos_manage.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminVideosActionManageResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'type' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'typeid' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'urldecode']
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$res = [];
|
||||
|
||||
if ($this->_get['type'] && $this->_get['typeid'])
|
||||
$res = VideoMgr::getVideos($this->_get['type'], $this->_get['typeid']);
|
||||
else if ($this->_get['user'])
|
||||
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user']))
|
||||
$res = VideoMgr::getVideos(userId: $uId);
|
||||
|
||||
$this->result = 'vim_videoData = '.Util::toJSON($res);
|
||||
}
|
||||
}
|
||||
57
endpoints/admin/videos_order.php
Normal file
57
endpoints/admin/videos_order.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminVideosActionOrderResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned'] ],
|
||||
'move' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => -1, 'max_range' => 1]] // -1 = up, 1 = down
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id', 'move') || $this->_get['move'] === 0)
|
||||
{
|
||||
trigger_error('AdminVideosActionOrderResponse - id or move empty', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
$id = $this->_get['id'][0];
|
||||
|
||||
$videos = DB::Aowow()->selectCol('SELECT a.`id` AS ARRAY_KEY, a.`pos` FROM ?_videos a, ?_videos b WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND (a.`status` & ?d) = 0 AND b.`id` = ?d ORDER BY a.`pos` ASC', CC_FLAG_DELETED, $id);
|
||||
if (!$videos || count($videos) == 1)
|
||||
{
|
||||
trigger_error('AdminVideosActionOrderResponse - not enough videos to sort', E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
$dir = $this->_get['move'];
|
||||
$curPos = $videos[$id];
|
||||
|
||||
if ($dir == -1 && $curPos == 0)
|
||||
{
|
||||
trigger_error('AdminVideosActionOrderResponse - video #'.$id.' already in top position', E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($dir == 1 && $curPos + 1 == count($videos))
|
||||
{
|
||||
trigger_error('AdminVideosActionOrderResponse - video #'.$id.' already in bottom position', E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
$oldKey = array_search($curPos + $dir, $videos);
|
||||
$videos[$oldKey] -= $dir;
|
||||
$videos[$id] += $dir;
|
||||
|
||||
foreach ($videos as $id => $pos)
|
||||
DB::Aowow()->query('UPDATE ?_videos SET `pos` = ?d WHERE `id` = ?d', $pos, $id);
|
||||
}
|
||||
}
|
||||
49
endpoints/admin/videos_relocate.php
Normal file
49
endpoints/admin/videos_relocate.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminVideosActionRelocateResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']],
|
||||
'typeid' => ['filter' => FILTER_VALIDATE_INT ]
|
||||
// (but not type..?)
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id', 'typeid'))
|
||||
{
|
||||
trigger_error('AdminVideosActionRelocateResponse - videoId or typeId empty', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
$id = $this->_get['id'][0];
|
||||
[$type, $oldTypeId] = array_values(DB::Aowow()->selectRow('SELECT `type`, `typeId` FROM ?_videos WHERE `id` = ?d', $id));
|
||||
$typeId = $this->_get['typeid'];
|
||||
|
||||
if (Type::validateIds($type, $typeId))
|
||||
{
|
||||
$tbl = Type::getClassAttrib($type, 'dataTable');
|
||||
|
||||
// move video
|
||||
DB::Aowow()->query('UPDATE ?_videos SET `typeId` = ?d WHERE `id` = ?d', $typeId, $id);
|
||||
|
||||
// flag target as having video
|
||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $typeId);
|
||||
|
||||
// deflag source for having had videos (maybe)
|
||||
$viInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~`status`) & ?d, 1, 0) AS "hasMore" FROM ?_videos WHERE `status`& ?d AND `type` = ?d AND `typeId` = ?d', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId);
|
||||
if ($viInfo || !$viInfo['hasMore'])
|
||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $oldTypeId);
|
||||
}
|
||||
else
|
||||
trigger_error('AdminVideosActionRelocateResponse - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
56
endpoints/admin/videos_sticky.php
Normal file
56
endpoints/admin/videos_sticky.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminVideosActionStickyResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id'))
|
||||
{
|
||||
trigger_error('AdminVideosActionStickyResponse - videoId empty', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// this one is a bit strange: as far as i've seen, the only thing a 'sticky' video does is show up in the infobox
|
||||
// this also means, that only one video per page should be sticky
|
||||
// so, handle it one by one and the last one affecting one particular type/typId-key gets the cake
|
||||
$viEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId`, `status` FROM ?_videos WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_DELETED, $this->_get['id']);
|
||||
foreach ($viEntries as $id => $viData)
|
||||
{
|
||||
// approve yet unapproved videos
|
||||
if (!($viData['status'] & CC_FLAG_APPROVED))
|
||||
{
|
||||
// set as approved in DB
|
||||
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id);
|
||||
|
||||
// gain siterep
|
||||
Util::gainSiteReputation($viData['userIdOwner'], SITEREP_ACTION_SUGGEST_VIDEO, ['id' => $id, 'what' => 1, 'date' => $viData['date']]);
|
||||
|
||||
// flag DB entry as having videos
|
||||
if ($tbl = Type::getClassAttrib($viData['type'], 'dataTable'))
|
||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $viData['typeId']);
|
||||
}
|
||||
|
||||
// reset all others
|
||||
DB::Aowow()->query('UPDATE ?_videos a, ?_videos b SET a.`status` = a.`status` & ~?d WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND a.`id` <> b.`id` AND b.`id` = ?d', CC_FLAG_STICKY, $id);
|
||||
|
||||
// toggle sticky status
|
||||
DB::Aowow()->query('UPDATE ?_videos SET `status` = IF(`status` & ?d, `status` & ~?d, `status` | ?d) WHERE `id` = ?d AND `status` & ?d', CC_FLAG_STICKY, CC_FLAG_STICKY, CC_FLAG_STICKY, $id, CC_FLAG_APPROVED);
|
||||
|
||||
unset($viEntries[$id]);
|
||||
}
|
||||
|
||||
if ($viEntries)
|
||||
trigger_error('AdminVideosActionStickyResponse - video(s) # '.implode(', ', array_keys($viEntries)).' not in db or flagged as deleted', E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
54
endpoints/admin/weight-presets.php
Normal file
54
endpoints/admin/weight-presets.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminWeightpresetsResponse extends TemplateResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_DEV | U_GROUP_BUREAU;
|
||||
|
||||
protected string $template = 'admin/weight-presets';
|
||||
protected string $pageName = 'weight-presets';
|
||||
protected ?int $activeTab = parent::TAB_STAFF;
|
||||
protected array $breadcrumb = [4, 2, 16]; // Staff > Development > Weight Presets
|
||||
|
||||
protected array $scripts = array(
|
||||
[SC_JS_FILE, 'js/filters.js'],
|
||||
[SC_CSS_STRING, '.wt-edit {display:inline-block; vertical-align:top; width:350px;}']
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = 'Weight Presets';
|
||||
array_unshift($this->title, $this->h1);
|
||||
|
||||
$head = $body = '';
|
||||
|
||||
$scales = DB::Aowow()->select('SELECT `class` AS ARRAY_KEY, `id` AS ARRAY_KEY2, `name`, `icon` FROM ?_account_weightscales WHERE `userId` = 0');
|
||||
$weights = DB::Aowow()->selectCol('SELECT awd.`id` AS ARRAY_KEY, awd.`field` AS ARRAY_KEY2, awd.`val` FROM ?_account_weightscale_data awd JOIN ?_account_weightscales ad ON awd.`id` = ad.`id` WHERE ad.`userId` = 0');
|
||||
foreach ($scales as $cl => $data)
|
||||
{
|
||||
$ul = '';
|
||||
foreach ($data as $id => $s)
|
||||
{
|
||||
$weights[$id]['__icon'] = $s['icon'];
|
||||
$ul .= '[url=# onclick="loadScale.bind(this, '.$id.')();"]'.$s['name'].'[/url][br]';
|
||||
}
|
||||
|
||||
$head .= '[td=header][class='.$cl.'][/td]';
|
||||
$body .= '[td valign=top]'.$ul.'[/td]';
|
||||
$this->extendGlobalIds(Type::CHR_CLASS, $cl);
|
||||
}
|
||||
|
||||
$this->extraText = new Markup('[table class=grid][tr]'.$head.'[/tr][tr]'.$body.'[/tr][/table]', ['allow' => Markup::CLASS_ADMIN], 'text-generic');
|
||||
|
||||
$this->extraHTML = '<script type="text/javascript">var wt_presets = '.Util::toJSON($weights).";</script>\n\n";
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
74
endpoints/admin/weight-presets_save.php
Normal file
74
endpoints/admin/weight-presets_save.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AdminWeightpresetsActionSaveResponse extends TextResponse
|
||||
{
|
||||
private const /* int */ ERR_NONE = 0;
|
||||
private const /* int */ ERR_WRITE_DB = 1;
|
||||
private const /* int */ ERR_WRITE_FILE = 2;
|
||||
private const /* int */ ERR_MISCELLANEOUS = 999;
|
||||
|
||||
protected int $requiredUserGroup = U_GROUP_DEV | U_GROUP_ADMIN | U_GROUP_BUREAU;
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'__icon' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Cfg::PATTERN_CONF_KEY_FULL]],
|
||||
'scale' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkScale'] ]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id', '__icon', 'scale'))
|
||||
{
|
||||
trigger_error('AdminWeightpresetsActionSaveResponse - malformed request received', E_USER_ERROR);
|
||||
$this->result = self::ERR_MISCELLANEOUS;
|
||||
return;
|
||||
}
|
||||
|
||||
// save to db
|
||||
DB::Aowow()->query('DELETE FROM ?_account_weightscale_data WHERE `id` = ?d', $this->_post['id']);
|
||||
DB::Aowow()->query('UPDATE ?_account_weightscales SET `icon`= ? WHERE `id` = ?d', $this->_post['__icon'], $this->_post['id']);
|
||||
|
||||
foreach (explode(',', $this->_post['scale']) as $s)
|
||||
{
|
||||
[$k, $v] = explode(':', $s);
|
||||
|
||||
if (!in_array($k, Util::$weightScales) || $v < 1)
|
||||
continue;
|
||||
|
||||
if (DB::Aowow()->query('INSERT INTO ?_account_weightscale_data VALUES (?d, ?, ?d)', $this->_post['id'], $k, $v) === null)
|
||||
{
|
||||
trigger_error('AdminWeightpresetsActionSaveResponse - failed to write to database', E_USER_ERROR);
|
||||
$this->result = self::ERR_WRITE_DB;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// write dataset
|
||||
exec('php aowow --build=weightPresets', $out);
|
||||
foreach ($out as $o)
|
||||
if (strstr($o, 'ERR'))
|
||||
{
|
||||
trigger_error('AdminWeightpresetsActionSaveResponse - failed to write dataset' . $o, E_USER_ERROR);
|
||||
$this->result = self::ERR_WRITE_FILE;
|
||||
return;
|
||||
}
|
||||
|
||||
// all done
|
||||
$this->result = self::ERR_NONE;
|
||||
}
|
||||
|
||||
protected static function checkScale(string $val) : string
|
||||
{
|
||||
if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val))
|
||||
return $val;
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
141
endpoints/areatrigger/areatrigger.php
Normal file
141
endpoints/areatrigger/areatrigger.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AreatriggerBaseResponse extends TemplateResponse implements ICache
|
||||
{
|
||||
use TrDetailPage, TrCache;
|
||||
|
||||
protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
|
||||
protected int $requiredUserGroup = U_GROUP_STAFF;
|
||||
|
||||
protected string $template = 'detail-page-generic';
|
||||
protected string $pageName = 'areatrigger';
|
||||
protected ?int $activeTab = parent::TAB_DATABASE;
|
||||
protected array $breadcrumb = [0, 102];
|
||||
|
||||
public int $type = Type::AREATRIGGER;
|
||||
public int $typeId = 0;
|
||||
|
||||
private AreaTriggerList $subject;
|
||||
|
||||
public function __construct(string $id)
|
||||
{
|
||||
parent::__construct($id);
|
||||
|
||||
$this->typeId = intVal($id);
|
||||
$this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE;
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->subject = new AreaTriggerList(array(['id', $this->typeId]));
|
||||
if ($this->subject->error)
|
||||
$this->generateNotFound(Lang::game('areatrigger'), Lang::areatrigger('notFound'));
|
||||
|
||||
$this->h1 = $this->subject->getField('name') ?: 'Areatrigger #'.$this->typeId;
|
||||
|
||||
$this->gPageInfo += array(
|
||||
'type' => $this->type,
|
||||
'typeId' => $this->typeId,
|
||||
'name' => $this->h1
|
||||
);
|
||||
|
||||
|
||||
/*************/
|
||||
/* Menu Path */
|
||||
/*************/
|
||||
|
||||
$this->breadcrumb[] = $this->subject->getField('type');
|
||||
|
||||
|
||||
/**************/
|
||||
/* Page Title */
|
||||
/**************/
|
||||
|
||||
array_unshift($this->title, $this->h1, Util::ucFirst(Lang::game('areatrigger')));
|
||||
|
||||
|
||||
/****************/
|
||||
/* Main Content */
|
||||
/****************/
|
||||
|
||||
$_type = $this->subject->getField('type');
|
||||
|
||||
// get spawns
|
||||
if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL))
|
||||
{
|
||||
$this->addDataLoader('zones');
|
||||
$this->map = array(
|
||||
['parent' => 'mapper-generic'], // Mapper
|
||||
$spawns, // mapperData
|
||||
null, // ShowOnMap
|
||||
[Lang::areatrigger('foundIn')] // foundIn
|
||||
);
|
||||
foreach ($spawns as $areaId => $_)
|
||||
$this->map[3][$areaId] = ZoneList::getName($areaId);
|
||||
}
|
||||
|
||||
// Smart AI
|
||||
if ($_type == AT_TYPE_SMART)
|
||||
{
|
||||
$sai = new SmartAI(SmartAI::SRC_TYPE_AREATRIGGER, $this->typeId, ['teleportTargetArea' => $this->subject->getField('areaId')]);
|
||||
if ($sai->prepare())
|
||||
{
|
||||
$this->extendGlobalData($sai->getJSGlobals());
|
||||
$this->smartAI = $sai->getMarkup();
|
||||
}
|
||||
}
|
||||
|
||||
$this->redButtons = array(
|
||||
BUTTON_LINKS => false,
|
||||
BUTTON_WOWHEAD => false
|
||||
);
|
||||
|
||||
|
||||
/**************/
|
||||
/* Extra Tabs */
|
||||
/**************/
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
|
||||
|
||||
// tab: conditions
|
||||
$cnd = new Conditions();
|
||||
$cnd->getBySource(Conditions::SRC_AREATRIGGER_CLIENT, entry: $this->typeId)->prepare();
|
||||
if ($tab = $cnd->toListviewTab())
|
||||
{
|
||||
$this->extendGlobalData($cnd->getJsGlobals());
|
||||
$this->lvTabs->addDataTab(...$tab);
|
||||
}
|
||||
|
||||
if ($_type == AT_TYPE_OBJECTIVE)
|
||||
{
|
||||
$relQuest = new QuestList(array(['id', $this->subject->getField('quest')]));
|
||||
if (!$relQuest->error)
|
||||
{
|
||||
$this->extendGlobalData($relQuest->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
|
||||
$this->lvTabs->addListviewTab(new Listview(['data' => $relQuest->getListviewData()], QuestList::$brickFile));
|
||||
}
|
||||
}
|
||||
else if ($_type == AT_TYPE_TELEPORT)
|
||||
{
|
||||
$relZone = new ZoneList(array(['id', $this->subject->getField('areaId')]));
|
||||
if (!$relZone->error)
|
||||
$this->lvTabs->addListviewTab(new Listview(['data' => $relZone->getListviewData()], ZoneList::$brickFile));
|
||||
}
|
||||
else if ($_type == AT_TYPE_SCRIPT)
|
||||
{
|
||||
$relTrigger = new AreaTriggerList(array(['id', $this->typeId, '!'], ['name', $this->subject->getField('name')]));
|
||||
if (!$relTrigger->error)
|
||||
$this->lvTabs->addListviewTab(new Listview(['data' => $relTrigger->getListviewData(), 'name' => Util::ucFirst(Lang::game('areatrigger'))]), AreaTriggerList::$brickFile, 'areatrigger');
|
||||
}
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
102
endpoints/areatriggers/areatriggers.php
Normal file
102
endpoints/areatriggers/areatriggers.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AreatriggersBaseResponse extends TemplateResponse implements ICache
|
||||
{
|
||||
use TrListPage, TrCache;
|
||||
|
||||
protected int $type = Type::AREATRIGGER;
|
||||
protected int $cacheType = CACHE_TYPE_LIST_PAGE;
|
||||
protected int $requiredUserGroup = U_GROUP_STAFF;
|
||||
|
||||
protected string $template = 'areatriggers';
|
||||
protected string $pageName = 'areatriggers';
|
||||
protected ?int $activeTab = parent::TAB_DATABASE;
|
||||
protected array $breadcrumb = [0, 102];
|
||||
|
||||
protected array $scripts = [[SC_JS_FILE, 'js/filters.js']];
|
||||
protected array $expectedGET = ['filter' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]]];
|
||||
protected array $validCats = [0, 1, 2, 3, 4, 5];
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
$this->getCategoryFromUrl($pageParam);
|
||||
|
||||
if (isset($this->category[0]))
|
||||
$this->forward('?areatriggers&filter=ty='.$this->category[0]);
|
||||
|
||||
parent::__construct($pageParam);
|
||||
|
||||
$this->filter = new AreaTriggerListFilter($this->_get['filter'] ?? '');
|
||||
if ($this->filter->shouldReload)
|
||||
{
|
||||
$_SESSION['error']['fi'] = $this->filter::class;
|
||||
$get = $this->filter->buildGETParam();
|
||||
$this->forward('?' . $this->pageName . ($get ? '&filter=' . $get : ''));
|
||||
}
|
||||
$this->filterError = $this->filter->error;
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = Util::ucFirst(Lang::game('areatriggers'));
|
||||
|
||||
$fiForm = $this->filter->values;
|
||||
|
||||
|
||||
/**************/
|
||||
/* Page Title */
|
||||
/**************/
|
||||
|
||||
array_unshift($this->title, $this->h1);
|
||||
|
||||
if (count($fiForm['ty']) == 1)
|
||||
array_unshift($this->title, Lang::areatrigger('types', $fiForm['ty'][0]));
|
||||
|
||||
|
||||
/*************/
|
||||
/* Menu Path */
|
||||
/*************/
|
||||
|
||||
if (count($fiForm['ty']) == 1)
|
||||
$this->breadcrumb[] = $fiForm['ty'];
|
||||
|
||||
|
||||
/****************/
|
||||
/* Main Content */
|
||||
/****************/
|
||||
|
||||
$this->redButtons[BUTTON_WOWHEAD] = false;
|
||||
|
||||
$conditions = [];
|
||||
if ($_ = $this->filter->getConditions())
|
||||
$conditions[] = $_;
|
||||
|
||||
$tabData = [];
|
||||
$trigger = new AreaTriggerList($conditions, ['calcTotal' => true]);
|
||||
if (!$trigger->error)
|
||||
{
|
||||
$tabData['data'] = $trigger->getListviewData();
|
||||
|
||||
// create note if search limit was exceeded; overwriting 'note' is intentional
|
||||
if ($trigger->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
|
||||
{
|
||||
$tabData['note'] = sprintf(Util::$tryFilteringEntityString, $trigger->getMatches(), '"'.Lang::game('areatriggers').'"', Cfg::get('SQL_LIMIT_DEFAULT'));
|
||||
$tabData['_truncated'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview($tabData, AreaTriggerList::$brickFile, 'areatrigger'));
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
149
endpoints/arena-team/arena-team.php
Normal file
149
endpoints/arena-team/arena-team.php
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class ArenateamBaseResponse extends TemplateResponse
|
||||
{
|
||||
use TrProfilerDetail;
|
||||
|
||||
protected string $template = 'roster';
|
||||
protected string $pageName = 'arena-team';
|
||||
protected ?int $activeTab = parent::TAB_TOOLS;
|
||||
protected array $breadcrumb = [1, 5, 3]; // Tools > Profiler > Arena Team
|
||||
|
||||
protected array $dataLoader = ['realms', 'weight-presets'];
|
||||
protected array $scripts = array(
|
||||
[SC_JS_FILE, 'js/profile_all.js'],
|
||||
[SC_JS_FILE, 'js/profile.js'],
|
||||
[SC_CSS_FILE, 'css/Profiler.css']
|
||||
);
|
||||
|
||||
public int $type = Type::ARENA_TEAM;
|
||||
|
||||
public function __construct(string $idOrProfile)
|
||||
{
|
||||
parent::__construct($idOrProfile);
|
||||
|
||||
if (!Cfg::get('PROFILER_ENABLE'))
|
||||
$this->generateError();
|
||||
|
||||
if (!$idOrProfile)
|
||||
$this->generateError();
|
||||
|
||||
$this->getSubjectFromUrl($idOrProfile);
|
||||
|
||||
// we have an ID > ok
|
||||
if ($this->typeId)
|
||||
return;
|
||||
|
||||
// param was incomplete profile > error
|
||||
if (!$this->subjectName)
|
||||
$this->generateError();
|
||||
|
||||
// 3 possibilities
|
||||
// 1) already synced to aowow
|
||||
if ($subject = DB::Aowow()->selectRow('SELECT `id`, `realmGUID`, `stub` FROM ?_profiler_arena_team WHERE `realm` = ?d AND `nameUrl` = ?', $this->realmId, Profiler::urlize($this->subjectName)))
|
||||
{
|
||||
$this->typeId = $subject['id'];
|
||||
|
||||
if ($subject['stub'])
|
||||
$this->handleIncompleteData(Type::ARENA_TEAM, $subject['realmGUID']);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) not yet synced but exists on realm (wont work if we get passed an urlized name, but there is nothing we can do about it)
|
||||
$subjects = DB::Characters($this->realmId)->select('SELECT at.`arenaTeamId` AS "realmGUID", at.`name`, at.`type` FROM arena_team at WHERE at.`name` = ?', $this->subjectName);
|
||||
if ($subject = array_filter($subjects, fn($x) => Util::lower($x['name']) === Util::lower($this->subjectName)))
|
||||
{
|
||||
$subject = array_pop($subject);
|
||||
$subject['realm'] = $this->realmId;
|
||||
$subject['stub'] = 1;
|
||||
$subject['nameUrl'] = Profiler::urlize($subject['name']);
|
||||
|
||||
// create entry from realm with basic info
|
||||
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team (?#) VALUES (?a)', array_keys($subject), array_values($subject));
|
||||
|
||||
$this->handleIncompleteData(Type::ARENA_TEAM, $subject['realmGUID']);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3) does not exist at all
|
||||
$this->notFound();
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if ($this->doResync)
|
||||
{
|
||||
parent::generate();
|
||||
return;
|
||||
}
|
||||
|
||||
$subject = new LocalArenaTeamList(array(['at.id', $this->typeId]));
|
||||
if ($subject->error)
|
||||
$this->notFound();
|
||||
|
||||
// arena team accessed by id
|
||||
if (!$this->subjectName)
|
||||
$this->forward($subject->getProfileUrl());
|
||||
|
||||
$this->h1 = Lang::profiler('arenaRoster', [$subject->getField('name')]);
|
||||
|
||||
|
||||
/*************/
|
||||
/* Menu Path */
|
||||
/*************/
|
||||
|
||||
$this->followBreadcrumbPath();
|
||||
|
||||
|
||||
/**************/
|
||||
/* Page Title */
|
||||
/**************/
|
||||
|
||||
array_unshift(
|
||||
$this->title,
|
||||
$subject->getField('name').' ('.$this->realm.' - '.Lang::profiler('regions', $this->region).')',
|
||||
Util::ucFirst(Lang::profiler('profiler'))
|
||||
);
|
||||
|
||||
|
||||
/****************/
|
||||
/* Main Content */
|
||||
/****************/
|
||||
|
||||
parent::generate();
|
||||
|
||||
$this->redButtons[BUTTON_RESYNC] = [$this->typeId, 'arena-team'];
|
||||
|
||||
// statistic calculations here
|
||||
|
||||
|
||||
/**************/
|
||||
/* Extra Tabs */
|
||||
/**************/
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated');
|
||||
|
||||
// tab: members
|
||||
$member = new LocalProfileList(array(['atm.arenaTeamId', $this->typeId]));
|
||||
$this->lvTabs->addListviewTab(new Listview(array(
|
||||
'data' => $member->getListviewData(PROFILEINFO_CHARACTER | PROFILEINFO_ARENA),
|
||||
'sort' => [-15],
|
||||
'visibleCols' => ['race', 'classs', 'level', 'talents', 'gearscore', 'rating', 'wins', 'losses'],
|
||||
'hiddenCols' => ['guild', 'location']
|
||||
), ProfileList::$brickFile));
|
||||
}
|
||||
|
||||
private function notFound() : never
|
||||
{
|
||||
parent::generateNotFound(Lang::game('arenateam'), Lang::profiler('notFound', 'arenateam'));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
48
endpoints/arena-team/resync.php
Normal file
48
endpoints/arena-team/resync.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class ArenaTeamResyncResponse extends TextResponse
|
||||
{
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdList'] ],
|
||||
'profile' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
|
||||
);
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
parent::__construct($pageParam);
|
||||
|
||||
if (!Cfg::get('PROFILER_ENABLE'))
|
||||
$this->generate404();
|
||||
}
|
||||
|
||||
/* params
|
||||
id: <prId1,prId2,..,prIdN>
|
||||
user: <string> [optional, not used]
|
||||
profile: <empty> [optional, also get related chars]
|
||||
return: 1
|
||||
*/
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id'))
|
||||
return;
|
||||
|
||||
if ($teams = DB::Aowow()->select('SELECT `realm`, `realmGUID` FROM ?_profiler_arena_team WHERE `id` IN (?a)', $this->_get['id']))
|
||||
foreach ($teams as $t)
|
||||
Profiler::scheduleResync(Type::ARENA_TEAM, $t['realm'], $t['realmGUID']);
|
||||
|
||||
if ($this->_get['profile'])
|
||||
if ($chars = DB::Aowow()->select('SELECT `realm`, `realmGUID` FROM ?_profiler_profiles p JOIN ?_profiler_arena_team_member atm ON atm.`profileId` = p.`id` WHERE atm.`arenaTeamId` IN (?a)', $this->_get['id']))
|
||||
foreach ($chars as $c)
|
||||
Profiler::scheduleResync(Type::PROFILE, $c['realm'], $c['realmGUID']);
|
||||
|
||||
$this->result = 1; // as string?
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
29
endpoints/arena-team/status.php
Normal file
29
endpoints/arena-team/status.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class ArenaTeamStatusResponse extends TextResponse
|
||||
{
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdList']]
|
||||
);
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
parent::__construct($pageParam);
|
||||
|
||||
if (!Cfg::get('PROFILER_ENABLE'))
|
||||
$this->generate404();
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->result = Profiler::resyncStatus(Type::ARENA_TEAM, $this->_get['id']);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
158
endpoints/arena-teams/arena-teams.php
Normal file
158
endpoints/arena-teams/arena-teams.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class ArenateamsBaseResponse extends TemplateResponse implements IProfilerList
|
||||
{
|
||||
use TrProfilerList, TrListPage;
|
||||
|
||||
protected string $template = 'arena-teams';
|
||||
protected string $pageName = 'arena-teams';
|
||||
protected ?int $activeTab = parent::TAB_TOOLS;
|
||||
protected array $breadcrumb = [1, 5, 3]; // Tools > Profiler > Arena Teams
|
||||
|
||||
protected array $dataLoader = ['realms'];
|
||||
protected array $scripts = array(
|
||||
[SC_JS_FILE, 'js/filters.js'],
|
||||
[SC_JS_FILE, 'js/profile_all.js'],
|
||||
[SC_JS_FILE, 'js/profile.js']
|
||||
);
|
||||
protected array $expectedGET = array(
|
||||
'filter' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]]
|
||||
);
|
||||
|
||||
public int $type = Type::ARENA_TEAM;
|
||||
|
||||
private int $sumSubjects = 0;
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
if (!Cfg::get('PROFILER_ENABLE'))
|
||||
$this->generateError();
|
||||
|
||||
$this->getSubjectFromUrl($pageParam);
|
||||
|
||||
parent::__construct($pageParam);
|
||||
|
||||
$realms = [];
|
||||
foreach (Profiler::getRealms() as $idx => $r)
|
||||
{
|
||||
if ($this->region && $r['region'] != $this->region)
|
||||
continue;
|
||||
|
||||
if ($this->realm && $r['name'] != $this->realm)
|
||||
continue;
|
||||
|
||||
$this->sumSubjects += DB::Characters($idx)->selectCell('SELECT count(*) FROM arena_team');
|
||||
$realms[] = $idx;
|
||||
}
|
||||
|
||||
$this->subCat = $pageParam !== '' ? '='.$pageParam : '';
|
||||
$this->filter = new ArenaTeamListFilter($this->_get['filter'] ?? '', ['realms' => $realms]);
|
||||
if ($this->filter->shouldReload)
|
||||
{
|
||||
$_SESSION['error']['fi'] = $this->filter::class;
|
||||
$get = $this->filter->buildGETParam();
|
||||
$this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : ''));
|
||||
}
|
||||
$this->filterError = $this->filter->error;
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = Lang::game('arenateams');
|
||||
|
||||
|
||||
/*************/
|
||||
/* Menu Path */
|
||||
/*************/
|
||||
|
||||
$this->followBreadcrumbPath();
|
||||
|
||||
|
||||
/**************/
|
||||
/* Page Title */
|
||||
/**************/
|
||||
|
||||
if ($this->realm)
|
||||
array_unshift($this->title, $this->realm,/* Cfg::get('BATTLEGROUP'),*/ Lang::profiler('regions', $this->region), Lang::game('arenateams'));
|
||||
else if ($this->region)
|
||||
array_unshift($this->title, Lang::profiler('regions', $this->region), Lang::game('arenateams'));
|
||||
else
|
||||
array_unshift($this->title, Lang::game('arenateams'));
|
||||
|
||||
|
||||
/****************/
|
||||
/* Main Content */
|
||||
/****************/
|
||||
|
||||
$conditions = [];
|
||||
if (!User::isInGroup(U_GROUP_EMPLOYEE))
|
||||
$conditions[] = ['at.seasonGames', 0, '>'];
|
||||
|
||||
if ($_ = $this->filter->getConditions())
|
||||
$conditions[] = $_;
|
||||
|
||||
$this->getRegions();
|
||||
|
||||
$tabData = array(
|
||||
'id' => 'arena-teams',
|
||||
'data' => [],
|
||||
'hideCount' => 1,
|
||||
'sort' => [-16],
|
||||
'extraCols' => ['$Listview.extraCols.members'],
|
||||
'visibleCols' => ['rank', 'wins', 'losses', 'rating'],
|
||||
'hiddenCols' => ['arenateam', 'guild']
|
||||
);
|
||||
|
||||
if (!$this->filter->values['sz'])
|
||||
$tabData['visibleCols'][] = 'size';
|
||||
|
||||
if ($this->filter->values['si'])
|
||||
$tabData['hiddenCols'][] = 'faction';
|
||||
|
||||
$miscParams = ['calcTotal' => true];
|
||||
if ($this->realm)
|
||||
$miscParams['sv'] = $this->realm;
|
||||
if ($this->region)
|
||||
$miscParams['rg'] = $this->region;
|
||||
|
||||
$teams = new RemoteArenaTeamList($conditions, $miscParams);
|
||||
if (!$teams->error)
|
||||
{
|
||||
$teams->initializeLocalEntries();
|
||||
|
||||
$tabData['data'] = $teams->getListviewData();
|
||||
|
||||
// create note if search limit was exceeded
|
||||
if ($this->filter->query && $teams->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
|
||||
{
|
||||
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound2', $this->sumSubjects, $teams->getMatches());
|
||||
$tabData['_truncated'] = 1;
|
||||
}
|
||||
else if ($teams->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
|
||||
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound', $this->sumSubjects, 0);
|
||||
}
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated');
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview($tabData, ArenaTeamList::$brickFile, 'membersCol'));
|
||||
|
||||
parent::generate();
|
||||
|
||||
$this->result->registerDisplayHook('filter', [self::class, 'filterFormHook']);
|
||||
}
|
||||
|
||||
public static function filterFormHook(Template\PageTemplate &$pt, ArenaTeamListFilter $filter) : void
|
||||
{
|
||||
// sort for dropdown-menus
|
||||
Lang::sort('game', 'cl');
|
||||
Lang::sort('game', 'ra');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
277
endpoints/class/class.php
Normal file
277
endpoints/class/class.php
Normal file
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class ClassBaseResponse extends TemplateResponse implements ICache
|
||||
{
|
||||
use TrDetailPage, TrCache;
|
||||
|
||||
private const TC_CLASS_IDS = [null, 8, 3, 1, 5, 4, 9, 6, 2, 7, null, 0]; // see TalentCalc.js
|
||||
|
||||
protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
|
||||
|
||||
protected string $template = 'detail-page-generic';
|
||||
protected string $pageName = 'class';
|
||||
protected ?int $activeTab = parent::TAB_DATABASE;
|
||||
protected array $breadcrumb = [0, 12];
|
||||
|
||||
public int $type = Type::CHR_CLASS;
|
||||
public int $typeId = 0;
|
||||
public ?string $expansion = null;
|
||||
|
||||
private CharClassList $subject;
|
||||
|
||||
public function __construct(string $id)
|
||||
{
|
||||
parent::__construct($id);
|
||||
|
||||
$this->typeId = intVal($id);
|
||||
$this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE;
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->subject = new CharClassList(array(['id', $this->typeId]));
|
||||
if ($this->subject->error)
|
||||
$this->generateNotFound(Lang::game('class'), Lang::chrClass('notFound'));
|
||||
|
||||
$this->h1 = $this->subject->getField('name', true);
|
||||
|
||||
$this->gPageInfo += array(
|
||||
'type' => $this->type,
|
||||
'typeId' => $this->typeId,
|
||||
'name' => $this->h1
|
||||
);
|
||||
|
||||
|
||||
/*************/
|
||||
/* Menu Path */
|
||||
/*************/
|
||||
|
||||
$this->breadcrumb[] = $this->typeId;
|
||||
|
||||
|
||||
/**************/
|
||||
/* Page Title */
|
||||
/**************/
|
||||
|
||||
array_unshift($this->title, $this->h1, Util::ucFirst(Lang::game('class')));
|
||||
|
||||
|
||||
/***********/
|
||||
/* Infobox */
|
||||
/***********/
|
||||
|
||||
$cl = ChrClass::from($this->typeId);
|
||||
|
||||
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
|
||||
|
||||
// hero class
|
||||
if ($this->subject->getField('flags') & 0x40)
|
||||
$infobox[] = '[tooltip=tooltip_heroclass]'.Lang::game('heroClass').'[/tooltip]';
|
||||
|
||||
// resource
|
||||
if ($cl == ChrClass::DRUID) // special Druid case
|
||||
$infobox[] = Lang::game('resources').
|
||||
'[tooltip name=powertype1]'.Lang::game('st', 0).', '.Lang::game('st', 31).', '.Lang::game('st', 2).'[/tooltip][span class=tip tooltip=powertype1]'.Util::ucFirst(Lang::spell('powerTypes', POWER_MANA)).'[/span], '.
|
||||
'[tooltip name=powertype2]'.Lang::game('st', 5).', '.Lang::game('st', 8).'[/tooltip][span class=tip tooltip=powertype2]'.Util::ucFirst(Lang::spell('powerTypes', POWER_RAGE)).'[/span], '.
|
||||
'[tooltip name=powertype8]'.Lang::game('st', 1).'[/tooltip][span class=tip tooltip=powertype8]'.Util::ucFirst(Lang::spell('powerTypes', POWER_ENERGY)).'[/span]';
|
||||
else if ($cl == ChrClass::DEATHKNIGHT) // special DK case
|
||||
$infobox[] = Lang::game('resources').'[span]'.Util::ucFirst(Lang::spell('powerTypes', POWER_RUNE)).', '.Util::ucFirst(Lang::spell('powerTypes', $this->subject->getField('powerType'))).'[/span]';
|
||||
else // regular case
|
||||
$infobox[] = Lang::game('resource').'[span]'.Util::ucFirst(Lang::spell('powerTypes', $this->subject->getField('powerType'))).'[/span]';
|
||||
|
||||
// roles
|
||||
$roles = [];
|
||||
for ($i = 0; $i < 4; $i++)
|
||||
if ($this->subject->getField('roles') & (1 << $i))
|
||||
$roles[] = (count($roles) == 2 ? "[br]" : '').Lang::game('_roles', $i);
|
||||
|
||||
if ($roles)
|
||||
$infobox[] = (count($roles) > 1 ? Lang::game('roles') : Lang::game('role')).implode(', ', $roles);
|
||||
|
||||
// specs
|
||||
$specList = [];
|
||||
$skills = new SkillList(array(['id', $this->subject->getField('skills')]));
|
||||
foreach ($skills->iterate() as $k => $__)
|
||||
$specList[$k] = '[icon name='.$skills->getField('iconString').'][url=?spells=7.'.$this->typeId.'.'.$k.']'.$skills->getField('name', true).'[/url][/icon]';
|
||||
|
||||
if ($specList)
|
||||
$infobox[] = Lang::game('specs').'[ul][li]'.implode('[/li][li]', $specList).'[/li][/ul]';
|
||||
|
||||
// id
|
||||
$infobox[] = Lang::chrClass('id') . $this->typeId;
|
||||
|
||||
// original name
|
||||
if (Lang::getLocale() != Locale::EN)
|
||||
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
|
||||
|
||||
if ($infobox)
|
||||
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
|
||||
|
||||
|
||||
/****************/
|
||||
/* Main Content */
|
||||
/****************/
|
||||
|
||||
$this->expansion = Util::$expansionString[$this->subject->getField('expansion')];
|
||||
$this->headIcons = ['class_'.$cl->json()];
|
||||
$this->redButtons = array(
|
||||
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
|
||||
BUTTON_WOWHEAD => true,
|
||||
BUTTON_TALENT => ['href' => '?talent#'.Util::$tcEncoding[self::TC_CLASS_IDS[$this->typeId] * 3], 'pet' => false],
|
||||
BUTTON_FORUM => false // todo (low): Cfg::get('BOARD_URL') + X
|
||||
);
|
||||
|
||||
|
||||
/**************/
|
||||
/* Extra Tabs */
|
||||
/**************/
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
|
||||
|
||||
// Tab: Spells (grouped)
|
||||
// '$LANG.tab_armorproficiencies',
|
||||
// '$LANG.tab_weaponskills',
|
||||
// '$LANG.tab_glyphs',
|
||||
// '$LANG.tab_abilities',
|
||||
// '$LANG.tab_talents',
|
||||
$conditions = array(
|
||||
['s.typeCat', [-13, -11, -2, 7]],
|
||||
[['s.cuFlags', (SPELL_CU_TRIGGERED | CUSTOM_EXCLUDE_FOR_LISTVIEW), '&'], 0],
|
||||
[
|
||||
'OR',
|
||||
// Glyphs, Proficiencies
|
||||
['s.reqClassMask', $cl->toMask(), '&'],
|
||||
// Abilities / Talents
|
||||
['s.skillLine1', $this->subject->getField('skills')],
|
||||
['AND', ['s.skillLine1', 0, '>'], ['s.skillLine2OrMask', $this->subject->getField('skills')]]
|
||||
],
|
||||
[ // last rank or unranked
|
||||
'OR',
|
||||
['s.cuFlags', SPELL_CU_LAST_RANK, '&'],
|
||||
['s.rankNo', 0]
|
||||
],
|
||||
Cfg::get('SQL_LIMIT_NONE')
|
||||
);
|
||||
|
||||
$genSpells = new SpellList($conditions);
|
||||
if (!$genSpells->error)
|
||||
{
|
||||
$this->extendGlobalData($genSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview(array(
|
||||
'data' => $genSpells->getListviewData(),
|
||||
'id' => 'spells',
|
||||
'name' => '$LANG.tab_spells',
|
||||
'visibleCols' => ['level', 'schools', 'type', 'classes'],
|
||||
'hiddenCols' => ['reagents', 'skill'],
|
||||
'sort' => ['-level', 'type', 'name'],
|
||||
'computeDataFunc' => '$Listview.funcBox.initSpellFilter',
|
||||
'onAfterCreate' => '$Listview.funcBox.addSpellIndicator'
|
||||
), SpellList::$brickFile));
|
||||
}
|
||||
|
||||
// Tab: Items (grouped)
|
||||
$conditions = array(
|
||||
['requiredClass', 0, '>'],
|
||||
['requiredClass', $cl->toMask(), '&'],
|
||||
[['requiredClass', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!'],
|
||||
['itemset', 0],
|
||||
Cfg::get('SQL_LIMIT_NONE')
|
||||
);
|
||||
|
||||
$items = new ItemList($conditions);
|
||||
if (!$items->error)
|
||||
{
|
||||
$this->extendGlobalData($items->getJSGlobals());
|
||||
|
||||
$hiddenCols = null;
|
||||
if ($items->hasDiffFields('requiredRace'))
|
||||
$hiddenCols = ['side'];
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview(array(
|
||||
'data' => $items->getListviewData(),
|
||||
'id' => 'items',
|
||||
'name' => '$LANG.tab_items',
|
||||
'visibleCols' => ['dps', 'armor', 'slot'],
|
||||
'hiddenCols' => $hiddenCols,
|
||||
'computeDataFunc' => '$Listview.funcBox.initSubclassFilter',
|
||||
'onAfterCreate' => '$Listview.funcBox.addSubclassIndicator',
|
||||
'note' => sprintf(Util::$filterResultString, '?items&filter=cr=152;crs='.$this->typeId.';crv=0'),
|
||||
'_truncated' => 1
|
||||
), ItemList::$brickFile));
|
||||
}
|
||||
|
||||
// Tab: Quests
|
||||
$conditions = array(
|
||||
['reqClassMask', $cl->toMask(), '&'],
|
||||
[['reqClassMask', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!']
|
||||
);
|
||||
|
||||
$quests = new QuestList($conditions);
|
||||
if (!$quests->error)
|
||||
{
|
||||
$this->extendGlobalData($quests->getJSGlobals());
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview(array(
|
||||
'data' => $quests->getListviewData(),
|
||||
'sort' => ['reqlevel', 'name']
|
||||
), QuestList::$brickFile));
|
||||
}
|
||||
|
||||
// Tab: Itemsets
|
||||
$sets = new ItemsetList(array(['classMask', $cl->toMask(), '&']));
|
||||
if (!$sets->error)
|
||||
{
|
||||
$this->extendGlobalData($sets->getJSGlobals(GLOBALINFO_SELF));
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview(array(
|
||||
'data' => $sets->getListviewData(),
|
||||
'note' => sprintf(Util::$filterResultString, '?itemsets&filter=cl='.$this->typeId),
|
||||
'hiddenCols' => ['classes'],
|
||||
'sort' => ['-level', 'name']
|
||||
), ItemsetList::$brickFile));
|
||||
}
|
||||
|
||||
// Tab: Trainer
|
||||
$conditions = array(
|
||||
['npcflag', NPC_FLAG_TRAINER | NPC_FLAG_CLASS_TRAINER, '&'],
|
||||
['trainerType', 0], // trains class spells
|
||||
['trainerRequirement', $this->typeId]
|
||||
);
|
||||
|
||||
$trainer = new CreatureList($conditions);
|
||||
if (!$trainer->error)
|
||||
{
|
||||
$this->addDataLoader('zones');
|
||||
$this->lvTabs->addListviewTab(new Listview(array(
|
||||
'data' => $trainer->getListviewData(),
|
||||
'id' => 'trainers',
|
||||
'name' => '$LANG.tab_trainers'
|
||||
), CreatureList::$brickFile));
|
||||
}
|
||||
|
||||
// Tab: Races
|
||||
$races = new CharRaceList(array(['classMask', $cl->toMask(), '&']));
|
||||
if (!$races->error)
|
||||
$this->lvTabs->addListviewTab(new Listview(['data' => $races->getListviewData()], CharRaceList::$brickFile));
|
||||
|
||||
// tab: condition-for
|
||||
$cnd = new Conditions();
|
||||
$cnd->getByCondition(Type::CHR_CLASS, $this->typeId)->prepare();
|
||||
if ($tab = $cnd->toListviewTab('condition-for', '$LANG.tab_condition_for'))
|
||||
{
|
||||
$this->extendGlobalData($cnd->getJsGlobals());
|
||||
$this->lvTabs->addDataTab(...$tab);
|
||||
}
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
48
endpoints/classes/classes.php
Normal file
48
endpoints/classes/classes.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class ClassesBaseResponse extends TemplateResponse implements ICache
|
||||
{
|
||||
use TrListPage, TrCache;
|
||||
|
||||
protected int $type = Type::CHR_CLASS;
|
||||
protected int $cacheType = CACHE_TYPE_LIST_PAGE;
|
||||
|
||||
protected string $template = 'list-page-generic';
|
||||
protected string $pageName = 'classes';
|
||||
protected ?int $activeTab = parent::TAB_DATABASE;
|
||||
protected array $breadcrumb = [0, 12];
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
$this->getCategoryFromUrl($pageParam);
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = Util::ucFirst(Lang::game('classes'));
|
||||
|
||||
|
||||
array_unshift($this->title, Util::ucFirst(Lang::game('classes')));
|
||||
|
||||
|
||||
$this->redButtons[BUTTON_WOWHEAD] = true;
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
|
||||
|
||||
$classes = new CharClassList();
|
||||
if (!$classes->error)
|
||||
$this->lvTabs->addListviewTab(new Listview(['data' => $classes->getListviewData()], CharClassList::$brickFile));
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
51
endpoints/comment/add-reply.php
Normal file
51
endpoints/comment/add-reply.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// returns all replies on success
|
||||
// must have non-200 header on error
|
||||
class CommentAddreplyResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'commentId' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'replyId' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'body' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']]
|
||||
);
|
||||
|
||||
protected function generate(): void
|
||||
{
|
||||
if (!$this->assertPOST('commentId', 'replyId', 'body'))
|
||||
{
|
||||
trigger_error('CommentAddreplyResponse - malformed request received', E_USER_ERROR);
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
|
||||
}
|
||||
|
||||
if (!User::canReply())
|
||||
$this->generate404(Lang::main('cannotComment'));
|
||||
|
||||
if (!$this->_post['commentId'] || !DB::Aowow()->selectCell('SELECT 1 FROM ?_comments WHERE `id` = ?d', $this->_post['commentId']))
|
||||
{
|
||||
trigger_error('CommentAddreplyResponse - parent comment #'.$this->_post['commentId'].' does not exist', E_USER_ERROR);
|
||||
$this->generate404(Lang::main('intError'));
|
||||
}
|
||||
|
||||
if (mb_strlen($this->_post['body']) < CommunityContent::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > CommunityContent::REPLY_LENGTH_MAX)
|
||||
$this->generate404(Lang::main('textLength', [mb_strlen($this->_post['body']), CommunityContent::REPLY_LENGTH_MIN, CommunityContent::REPLY_LENGTH_MAX]));
|
||||
|
||||
if (!DB::Aowow()->query('INSERT INTO ?_comments (`userId`, `roles`, `body`, `date`, `replyTo`) VALUES (?d, ?d, ?, UNIX_TIMESTAMP(), ?d)', User::$id, User::$groups, $this->_post['body'], $this->_post['commentId']))
|
||||
{
|
||||
trigger_error('CommentAddreplyResponse - write to db failed', E_USER_ERROR);
|
||||
$this->generate404(Lang::main('intError'));
|
||||
}
|
||||
|
||||
$this->result = Util::toJSON(CommunityContent::getCommentReplies($this->_post['commentId']));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
79
endpoints/comment/add.php
Normal file
79
endpoints/comment/add.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CommentAddResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'commentbody' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']]
|
||||
);
|
||||
protected array $expectedGET = array(
|
||||
'type' => ['filter' => FILTER_VALIDATE_INT],
|
||||
'typeid' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
|
||||
// i .. have problems believing, that everything uses nifty ajax while adding comments requires a brutal header(Loacation: <wherever>), yet, thats how it is
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('type', 'typeid') || !$this->assertPOST('commentbody') || !Type::validateIds($this->_get['type'], $this->_get['typeid']))
|
||||
{
|
||||
trigger_error('CommentAddResponse - malforemd request received', E_USER_ERROR);
|
||||
return; // whatever, we cant even send him back
|
||||
}
|
||||
|
||||
// we now have a valid return target
|
||||
$idOrUrl = $this->_get['typeid'];
|
||||
if ($this->_get['type'] == Type::GUIDE)
|
||||
if ($_ = DB::Aowow()->selectCell('SELECT `url` FROM ?_guides WHERE `id` = ?d', $this->_get['typeid']))
|
||||
$idOrUrl = $_;
|
||||
|
||||
$this->redirectTo = '?'.Type::getFileString($this->_get['type']).'='.$idOrUrl.'#comments';
|
||||
|
||||
// this type cannot be commented on
|
||||
if (!Type::checkClassAttrib($this->_get['type'], 'contribute', CONTRIBUTE_CO))
|
||||
{
|
||||
trigger_error('CommentAddResponse - tried to comment on unsupported type: '.Type::getFileString($this->_get['type']), E_USER_ERROR);
|
||||
$_SESSION['error']['co'] = Lang::main('intError');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!User::canComment())
|
||||
{
|
||||
$_SESSION['error']['co'] = Lang::main('cannotComment');
|
||||
return;
|
||||
}
|
||||
|
||||
$len = mb_strlen($this->_post['commentbody']);
|
||||
|
||||
if ((!User::isInGroup(U_GROUP_MODERATOR) && $len < CommunityContent::COMMENT_LENGTH_MIN) || ($len > CommunityContent::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)))
|
||||
{
|
||||
$_SESSION['error']['co'] = Lang::main('textLength', [$len, CommunityContent::COMMENT_LENGTH_MIN, CommunityContent::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)]);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($postId = DB::Aowow()->query('INSERT INTO ?_comments (`type`, `typeId`, `userId`, `roles`, `body`, `date`) VALUES (?d, ?d, ?d, ?d, ?, UNIX_TIMESTAMP())', $this->_get['type'], $this->_get['typeid'], User::$id, User::$groups, $this->_post['commentbody']))
|
||||
{
|
||||
Util::gainSiteReputation(User::$id, SITEREP_ACTION_COMMENT, ['id' => $postId]);
|
||||
|
||||
// every comment starts with a rating of +1 and i guess the simplest thing to do is create a db-entry with the system as owner
|
||||
DB::Aowow()->query('INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, 0, 1)', RATING_COMMENT, $postId);
|
||||
|
||||
// flag target with hasComment
|
||||
if ($tbl = Type::getClassAttrib($this->_get['type'], 'dataTable'))
|
||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $this->_get['typeid']);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
trigger_error('CommentAddResponse - write to db failed', E_USER_ERROR);
|
||||
$_SESSION['error']['co'] = Lang::main('intError');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
44
endpoints/comment/delete-reply.php
Normal file
44
endpoints/comment/delete-reply.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// expects non-200 header on error
|
||||
class CommentDeletereplyResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id'))
|
||||
{
|
||||
trigger_error('CommentDeletereplyResponse - malformed request received', E_USER_ERROR);
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
|
||||
}
|
||||
|
||||
// flag as deleted (unset sticky (can a reply even be sticky?)
|
||||
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d | ?d, `deleteUserId` = ?d, `deleteDate` = UNIX_TIMESTAMP() WHERE `id` = ?d { AND `userId` = ?d }',
|
||||
CC_FLAG_STICKY, CC_FLAG_DELETED,
|
||||
User::$id,
|
||||
$this->_post['id'],
|
||||
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
|
||||
);
|
||||
|
||||
if ($ok)
|
||||
DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d', RATING_COMMENT, $this->_post['id']);
|
||||
else
|
||||
{
|
||||
trigger_error('CommentDeletereplyResponse - deleting reply #'.$this->_post['id'].' by user #'.User::$id.' from db failed', E_USER_ERROR);
|
||||
$this->generate404(Lang::main('intError'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
53
endpoints/comment/delete.php
Normal file
53
endpoints/comment/delete.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CommentDeleteResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']],
|
||||
// 'username' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id'))
|
||||
{
|
||||
trigger_error('CommentDeleteResponse - malformed request received', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// in theory, there is a username passed alongside if executed from userpage... lets just use the current user (see user.js)
|
||||
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d, `deleteUserId` = ?d, `deleteDate` = UNIX_TIMESTAMP() WHERE `id` IN (?a) { AND `userId` = ?d }',
|
||||
CC_FLAG_DELETED,
|
||||
User::$id,
|
||||
$this->_post['id'],
|
||||
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
|
||||
);
|
||||
|
||||
// unflag subject: hasComment
|
||||
if ($ok)
|
||||
{
|
||||
$coInfo = DB::Aowow()->select(
|
||||
'SELECT IF(BIT_OR(~b.`flags`) & ?d, 1, 0) AS "0", b.`type` AS "1", b.`typeId` AS "2" FROM ?_comments a JOIN ?_comments b ON a.`type` = b.`type` AND a.`typeId` = b.`typeId` WHERE a.`id` IN (?a) GROUP BY b.`type`, b.`typeId`',
|
||||
CC_FLAG_DELETED, $this->_post['id']
|
||||
);
|
||||
|
||||
foreach ($coInfo as [$hasMore, $type, $typeId])
|
||||
if (!$hasMore && ($tbl = Type::getClassAttrib($type, 'dataTable')))
|
||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $typeId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
trigger_error('CommentDeleteResponse - user #'.User::$id.' could not flag comment(s) #'.implode(', ', $this->_post['id']).' as deleted', E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
30
endpoints/comment/detach-reply.php
Normal file
30
endpoints/comment/detach-reply.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// expects non-200 header on error
|
||||
class CommentDetachreplyResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_MODERATOR;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id'))
|
||||
{
|
||||
trigger_error('CommentDetachreplyResponse - malformed request received', E_USER_ERROR);
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
|
||||
}
|
||||
|
||||
DB::Aowow()->query('UPDATE ?_comments c1, ?_comments c2 SET c1.`replyTo` = 0, c1.`type` = c2.`type`, c1.`typeId` = c2.`typeId` WHERE c1.`replyTo` = c2.`id` AND c1.`id` = ?d', $this->_post['id']);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
61
endpoints/comment/downvote-reply.php
Normal file
61
endpoints/comment/downvote-reply.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// expects non-200 header on error
|
||||
class CommentDownvotereplyResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id'))
|
||||
{
|
||||
trigger_error('CommentDownvotereplyResponse - malformed request received', E_USER_ERROR);
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
|
||||
}
|
||||
|
||||
if (!User::canDownvote())
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'cannot downvote' : '');
|
||||
|
||||
$comment = DB::Aowow()->selectRow('SELECT `userId`, IF(`flags` & ?d, 1, 0) AS "deleted" FROM ?_comments WHERE `id` = ?d', CC_FLAG_DELETED, $this->_post['id']);
|
||||
if (!$comment)
|
||||
{
|
||||
trigger_error('CommentDownvotereplyResponse - comment #'.$this->_post['id'].' not found in db', E_USER_ERROR);
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'replyID not found' : '');
|
||||
}
|
||||
|
||||
if (User::$id == $comment['userId']) // not worth logging?
|
||||
$this->generate404('LANG.voteself_tip');
|
||||
|
||||
if ($comment['deleted'])
|
||||
$this->generate404('LANG.votedeleted_tip');
|
||||
|
||||
$ok = DB::Aowow()->query(
|
||||
'INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)',
|
||||
RATING_COMMENT,
|
||||
$this->_post['id'],
|
||||
User::$id,
|
||||
User::canSupervote() ? -2 : -1
|
||||
);
|
||||
|
||||
if (!is_int($ok))
|
||||
{
|
||||
trigger_error('CommentDownvotereplyResponse - write to db failed', E_USER_ERROR);
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'write to db failed' : '');
|
||||
}
|
||||
|
||||
Util::gainSiteReputation($comment['userId'], SITEREP_ACTION_DOWNVOTED, ['id' => $this->_post['id'], 'voterId' => User::$id]);
|
||||
User::decrementDailyVotes();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
62
endpoints/comment/edit-reply.php
Normal file
62
endpoints/comment/edit-reply.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// returns all replies on success
|
||||
// must have non-200 header on error
|
||||
class CommentEditreplyResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'commentId' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'replyId' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'body' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('commentId', 'replyId', 'body'))
|
||||
{
|
||||
trigger_error('CommentEditreplyResponse - malformed request received', E_USER_ERROR);
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
|
||||
}
|
||||
|
||||
$ownerId = DB::Aowow()->selectCell('SELECT `userId` FROM ?_comments WHERE `id` = ?d AND `replyTo` = ?d', $this->_post['replyId'], $this->_post['commentId']);
|
||||
|
||||
if (!User::canReply() || (User::$id != $ownerId && !User::isInGroup(U_GROUP_MODERATOR)))
|
||||
$this->generate404(Lang::main('cannotComment'));
|
||||
|
||||
if (!$ownerId)
|
||||
{
|
||||
trigger_error('CommentEditreplyResponse - comment #'.$this->_post['commentId'].' or reply #'.$this->_post['replyId'].' does not exist', E_USER_ERROR);
|
||||
$this->generate404(Lang::main('intError'));
|
||||
}
|
||||
|
||||
if (mb_strlen($this->_post['body']) < CommunityContent::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > CommunityContent::REPLY_LENGTH_MAX)
|
||||
$this->generate404(Lang::main('textLength', [mb_strlen($this->_post['body']), CommunityContent::REPLY_LENGTH_MIN, CommunityContent::REPLY_LENGTH_MAX]));
|
||||
|
||||
$update = array(
|
||||
'body' => $this->_post['body'],
|
||||
'editUserId' => User::$id,
|
||||
'editDate' => time()
|
||||
);
|
||||
if (User::$id == $ownerId)
|
||||
$update['roles'] = User::$groups;
|
||||
|
||||
if (!DB::Aowow()->query('UPDATE ?_comments SET `editCount` = `editCount` + 1, ?a WHERE `id` = ?d AND `replyTo` = ?d { AND `userId` = ?d }',
|
||||
$update, $this->_post['replyId'], $this->_post['commentId'], User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id))
|
||||
{
|
||||
trigger_error('CommentEditreplyResponse - write to db failed', E_USER_ERROR);
|
||||
$this->generate404(Lang::main('intError'));
|
||||
}
|
||||
|
||||
$this->result = Util::toJSON(CommunityContent::getCommentReplies($this->_post['commentId']));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
63
endpoints/comment/edit.php
Normal file
63
endpoints/comment/edit.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CommentEditResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'body' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']],
|
||||
'response' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']]
|
||||
);
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id') || !$this->assertPOST('body'))
|
||||
{
|
||||
trigger_error('CommentEditResponse - malforemd request received', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
$ownerId = DB::Aowow()->selectCell('SELECT `userId` FROM ?_comments WHERE `id` = ?d', $this->_get['id']);
|
||||
|
||||
if (!User::canComment() || (User::$id != $ownerId && !User::isInGroup(U_GROUP_MODERATOR)))
|
||||
{
|
||||
trigger_error('CommentEditResponse - user #'.User::$id.' not allowed to edit comment #'.$this->_get['id'], E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!User::isInGroup(U_GROUP_MODERATOR) && mb_strlen($this->_post['body']) < CommunityContent::COMMENT_LENGTH_MIN)
|
||||
return; // no point in reporting this trifle
|
||||
|
||||
// trim to max length
|
||||
if (!User::isInGroup(U_GROUP_MODERATOR))
|
||||
$this->_post['body'] = mb_substr($this->_post['body'], 0, (CommunityContent::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)));
|
||||
|
||||
$update = array(
|
||||
'body' => $this->_post['body'],
|
||||
'editUserId' => User::$id,
|
||||
'editDate' => time()
|
||||
);
|
||||
if (User::$id == $ownerId)
|
||||
$update['roles'] = User::$groups;
|
||||
|
||||
if (User::isInGroup(U_GROUP_MODERATOR))
|
||||
{
|
||||
$update['responseBody'] = $this->_post['response'] ?? '';
|
||||
$update['responseUserId'] = User::$id;
|
||||
$update['responseRoles'] = User::$groups;
|
||||
}
|
||||
|
||||
DB::Aowow()->query('UPDATE ?_comments SET `editCount` = `editCount` + 1, ?a WHERE `id` = ?d', $update, $this->_get['id']);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
45
endpoints/comment/flag-reply.php
Normal file
45
endpoints/comment/flag-reply.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// expects non-200 header on error
|
||||
class CommentFlagreplyResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id'))
|
||||
{
|
||||
trigger_error('CommentFlagreplyResponse - malformed request received', E_USER_ERROR);
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
|
||||
}
|
||||
|
||||
$replyOwner = DB::Aowow()->selectCell('SELECT `userId` FROM ?_commments WHERE `id` = ?d', $this->_post['id']);
|
||||
if (!$replyOwner)
|
||||
{
|
||||
trigger_error('CommentFlagreplyResponse - reply not found', E_USER_ERROR);
|
||||
$this->generate404(Lang::main('intError'));
|
||||
}
|
||||
|
||||
// ui element should not be present
|
||||
if ($replyOwner == User::$id)
|
||||
$this->generate404();
|
||||
|
||||
$report = new Report(Report::MODE_COMMENT, Report::CO_INAPPROPRIATE, $this->_post['id']);
|
||||
if (!$report->create('Report Reply Button Click'))
|
||||
$this->generate404('LANG.ct_resp_error'.$report->getError());
|
||||
else if (count($report->getSimilar()) >= CommunityContent::REPORT_THRESHOLD_AUTO_DELETE)
|
||||
DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_DELETED, $this->_post['id']);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
58
endpoints/comment/out-of-date.php
Normal file
58
endpoints/comment/out-of-date.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// toggle flag
|
||||
class CommentOutofdateResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'remove' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]],
|
||||
'reason' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id'))
|
||||
{
|
||||
trigger_error('CommentOutofdateResponse - malformed request received', E_USER_ERROR);
|
||||
if (User::isInGroup(U_GROUP_STAFF))
|
||||
$this->result = 'malformed request received';
|
||||
}
|
||||
|
||||
$ok = false;
|
||||
if (User::isInGroup(U_GROUP_MODERATOR)) // directly mark as outdated
|
||||
{
|
||||
if (!$this->_post['remove'])
|
||||
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']);
|
||||
else
|
||||
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']);
|
||||
}
|
||||
else // try to report as outdated
|
||||
{
|
||||
$report = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id']);
|
||||
if (!$report->create($this->_post['reason']))
|
||||
$this->result = Lang::main('intError');
|
||||
|
||||
if (count($report->getSimilar()) >= CommunityContent::REPORT_THRESHOLD_AUTO_OUT_OF_DATE)
|
||||
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']);
|
||||
}
|
||||
|
||||
if (!$ok)
|
||||
{
|
||||
trigger_error('CommentOutofdateResponse - failed to update comment in db', E_USER_ERROR);
|
||||
$this->result = Lang::main('intError');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->result = 'ok'; // the js expects the actual characters 'ok' on success, not some json string like '"ok"'
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
31
endpoints/comment/rating.php
Normal file
31
endpoints/comment/rating.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// up/down - distribution
|
||||
class CommentRatingResponse extends TextResponse
|
||||
{
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id'))
|
||||
{
|
||||
$this->result = Util::toJSON(['success' => 0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($votes = DB::Aowow()->selectRow('SELECT 1 AS "success", SUM(IF(`value` > 0, `value`, 0)) AS "up", SUM(IF(`value` < 0, -`value`, 0)) AS "down" FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND `userId` <> 0 GROUP BY `entry`', RATING_COMMENT, $this->_get['id']))
|
||||
$this->result = Util::toJSON($votes);
|
||||
else
|
||||
$this->result = Util::toJSON(['success' => 1, 'up' => 0, 'down' => 0]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
24
endpoints/comment/show-replies.php
Normal file
24
endpoints/comment/show-replies.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CommentShowrepliesResponse extends TextResponse
|
||||
{
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertGET('id'))
|
||||
$this->result = Util::toJSON([]);
|
||||
else
|
||||
$this->result = Util::toJSON(CommunityContent::getCommentReplies($this->_get['id']));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
34
endpoints/comment/sticky.php
Normal file
34
endpoints/comment/sticky.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// toggle flag
|
||||
class CommentStickyResponse extends TextResponse
|
||||
{
|
||||
protected int $requiredUserGroup = U_GROUP_MODERATOR;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'sticky' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0, 'max_range' => 1]]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id', 'sticky'))
|
||||
{
|
||||
trigger_error('CommentStickyResponse - malformed request received', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->_post['sticky'])
|
||||
DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_STICKY, $this->_post['id']);
|
||||
else
|
||||
DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` = ?d', CC_FLAG_STICKY, $this->_post['id']);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
48
endpoints/comment/undelete.php
Normal file
48
endpoints/comment/undelete.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CommentUndeleteResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']],
|
||||
// 'username' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id'))
|
||||
{
|
||||
trigger_error('CommentUndeleteResponse - malformed request received', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// in theory, there is a username passed alongside if executed from userpage... lets just use the current user (see user.js)
|
||||
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` IN (?a) { AND `userId` = `deleteUserId` AND `deleteUserId` = ?d }',
|
||||
CC_FLAG_DELETED,
|
||||
$this->_post['id'],
|
||||
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
|
||||
);
|
||||
|
||||
// unflag subject: hasComment
|
||||
if ($ok)
|
||||
{
|
||||
$coInfo = DB::Aowow()->select('SELECT `type` AS "0", `typeId` AS "1" FROM ?_comments WHERE `id` IN (?a) GROUP BY `type`, `typeId`', $this->_post['id']);
|
||||
foreach ($coInfo as [$type, $typeId])
|
||||
if ($tbl = Type::getClassAttrib($type, 'dataTable'))
|
||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $typeId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
trigger_error('CommentUndeleteResponse - user #'.User::$id.' could not unflag comment(s) #'.implode(', ', $this->_post['id']).' from deleted', E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
61
endpoints/comment/upvote-reply.php
Normal file
61
endpoints/comment/upvote-reply.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// expects non-200 header on error
|
||||
class CommentUpvotereplyResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedPOST = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT]
|
||||
);
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('id'))
|
||||
{
|
||||
trigger_error('CommentUpvotereplyResponse - malformed request received', E_USER_ERROR);
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
|
||||
}
|
||||
|
||||
if (!User::canUpvote())
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'cannot upvote' : '');
|
||||
|
||||
$comment = DB::Aowow()->selectRow('SELECT `userId`, IF(`flags` & ?d, 1, 0) AS "deleted" FROM ?_comments WHERE `id` = ?d', CC_FLAG_DELETED, $this->_post['id']);
|
||||
if (!$comment)
|
||||
{
|
||||
trigger_error('CommentUpvotereplyResponse - comment #'.$this->_post['id'].' not found in db', E_USER_ERROR);
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'replyID not found' : '');
|
||||
}
|
||||
|
||||
if (User::$id == $comment['userId']) // not worth logging?
|
||||
$this->generate404('LANG.voteself_tip');
|
||||
|
||||
if ($comment['deleted'])
|
||||
$this->generate404('LANG.votedeleted_tip');
|
||||
|
||||
$ok = DB::Aowow()->query(
|
||||
'INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)',
|
||||
RATING_COMMENT,
|
||||
$this->_post['id'],
|
||||
User::$id,
|
||||
User::canSupervote() ? 2 : 1
|
||||
);
|
||||
|
||||
if (!is_int($ok))
|
||||
{
|
||||
trigger_error('CommentUpvotereplyResponse - write to db failed', E_USER_ERROR);
|
||||
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'write to db failed' : '');
|
||||
}
|
||||
|
||||
Util::gainSiteReputation($comment['userId'], SITEREP_ACTION_UPVOTED, ['id' => $this->_post['id'], 'voterId' => User::$id]);
|
||||
User::decrementDailyVotes();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
84
endpoints/comment/vote.php
Normal file
84
endpoints/comment/vote.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// up, down and remove
|
||||
class CommentVoteResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'rating' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => -2, 'max_range' => 2]]
|
||||
);
|
||||
|
||||
protected function generate(): void
|
||||
{
|
||||
if (!$this->assertGET('id', 'rating'))
|
||||
{
|
||||
trigger_error('CommentVoteResponse - malformed request received', E_USER_ERROR);
|
||||
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (User::getCurrentDailyVotes() <= 0)
|
||||
{
|
||||
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('tooManyVotes')]);
|
||||
return;
|
||||
}
|
||||
|
||||
$target = DB::Aowow()->selectRow(
|
||||
'SELECT c.`userId` AS "owner", ur.`value`, IF(c.`flags` & ?d, 1, 0) AS "deleted" FROM ?_comments c LEFT JOIN ?_user_ratings ur ON ur.`type` = ?d AND ur.`entry` = c.id AND ur.`userId` = ?d WHERE c.id = ?d',
|
||||
CC_FLAG_DELETED, RATING_COMMENT, User::$id, $this->_get['id']
|
||||
);
|
||||
if (!$target)
|
||||
{
|
||||
trigger_error('CommentVoteResponse - target comment #'.$this->_get['id'].' not found', E_USER_ERROR);
|
||||
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
|
||||
return;
|
||||
}
|
||||
|
||||
$val = User::canSupervote() ? 2 : 1;
|
||||
if ($this->_get['rating'] < 0)
|
||||
$val *= -1;
|
||||
|
||||
if (User::$id == $target['owner'] || $val != $this->_get['rating'] || $target['deleted'])
|
||||
{
|
||||
// circumvented the checks in JS
|
||||
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (($val > 0 && !User::canUpvote()) || ($val < 0 && !User::canDownvote()))
|
||||
{
|
||||
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('bannedRating')]);
|
||||
return;
|
||||
}
|
||||
|
||||
$ok = false;
|
||||
// old and new have same sign; undo vote (user may have gained/lost access to superVote in the meantime)
|
||||
if ($target['value'] && ($target['value'] < 0) == ($val < 0))
|
||||
$ok = DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND `userId` = ?d', RATING_COMMENT, $this->_get['id'], User::$id);
|
||||
else // replace, because we may be overwriting an old, opposing vote
|
||||
if ($ok = DB::Aowow()->query('REPLACE INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)', RATING_COMMENT, $this->_get['id'], User::$id, $val))
|
||||
User::decrementDailyVotes(); // do not refund retracted votes!
|
||||
|
||||
if ($ok)
|
||||
{
|
||||
if ($val > 0) // gain rep
|
||||
Util::gainSiteReputation($target['owner'], SITEREP_ACTION_UPVOTED, ['id' => $this->_get['id'], 'voterId' => User::$id]);
|
||||
else if ($val < 0)
|
||||
Util::gainSiteReputation($target['owner'], SITEREP_ACTION_DOWNVOTED, ['id' => $this->_get['id'], 'voterId' => User::$id]);
|
||||
|
||||
$this->result = Util::toJSON(['error' => 0]);
|
||||
}
|
||||
else
|
||||
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('intError')]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
116
endpoints/compare/compare.php
Normal file
116
endpoints/compare/compare.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
// tabId 1: Tools g_initHeader()
|
||||
class CompareBaseResponse extends TemplateResponse
|
||||
{
|
||||
protected string $template = 'compare';
|
||||
protected string $pageName = 'compare';
|
||||
protected ?int $activeTab = parent::TAB_TOOLS;
|
||||
protected array $breadcrumb = [1, 3];
|
||||
|
||||
protected array $dataLoader = ['weight-presets', 'gems', 'enchants', 'itemsets'];
|
||||
protected array $scripts = array(
|
||||
[SC_JS_FILE, 'js/profile.js'],
|
||||
[SC_JS_FILE, 'js/Draggable.js'],
|
||||
[SC_JS_FILE, 'js/filters.js'],
|
||||
[SC_JS_FILE, 'js/Summary.js'],
|
||||
[SC_CSS_FILE, 'css/Summary.css']
|
||||
);
|
||||
protected array $expectedGET = array(
|
||||
'compare' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCompareString']]
|
||||
);
|
||||
protected array $expectedCOOKIE = array(
|
||||
'compare_groups' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCompareString']]
|
||||
);
|
||||
|
||||
public Summary $summary;
|
||||
public array $cmpItems = [];
|
||||
|
||||
private string $compareString = '';
|
||||
|
||||
public function __construct($pageParam)
|
||||
{
|
||||
parent::__construct($pageParam);
|
||||
|
||||
// prefer GET over COOKIE
|
||||
if ($this->_get['compare'])
|
||||
$this->compareString = $this->_get['compare'];
|
||||
else if ($this->_cookie['compare_groups'])
|
||||
$this->compareString = $this->_cookie['compare_groups'];
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = Lang::main('compareTool');
|
||||
|
||||
|
||||
array_unshift($this->title, $this->h1);
|
||||
|
||||
|
||||
$this->summary = new Summary(array(
|
||||
'template' => 'compare',
|
||||
'id' => 'compare',
|
||||
'parent' => 'compare-generic'
|
||||
));
|
||||
|
||||
if ($this->compareString)
|
||||
{
|
||||
$items = [];
|
||||
foreach (explode(';', $this->compareString) as $itemsString)
|
||||
{
|
||||
$suGroup = [];
|
||||
foreach (explode(':', $itemsString) as $itemDef)
|
||||
{
|
||||
// [itemId, subItem, permEnch, tempEnch, gem1, gem2, gem3, gem4]
|
||||
$params = array_pad(array_map('intVal', explode('.', $itemDef)), 8, 0);
|
||||
$items[] = $params[0];
|
||||
$suGroup[] = $params;
|
||||
}
|
||||
|
||||
$this->summary->addGroup($suGroup);
|
||||
}
|
||||
|
||||
$iList = new ItemList(array(['i.id', $items]));
|
||||
$data = $iList->getListviewData(ITEMINFO_SUBITEMS | ITEMINFO_JSON);
|
||||
|
||||
foreach ($iList->iterate() as $itemId => $__)
|
||||
{
|
||||
if (empty($data[$itemId]))
|
||||
continue;
|
||||
|
||||
if (!empty($data[$itemId]['subitems']))
|
||||
foreach ($data[$itemId]['subitems'] as &$si)
|
||||
{
|
||||
$si['enchantment'] = implode(', ', $si['enchantment']);
|
||||
unset($si['chance']);
|
||||
}
|
||||
|
||||
$this->cmpItems[$itemId] = [
|
||||
'name_'.Lang::getLocale()->json() => $iList->getField('name', true),
|
||||
'quality' => $iList->getField('quality'),
|
||||
'icon' => $iList->getField('iconString'),
|
||||
'jsonequip' => $data[$itemId]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
|
||||
protected static function checkCompareString(string $val) : string
|
||||
{
|
||||
$val = urldecode($val);
|
||||
if (preg_match('/[^-?\d\.:;]/', $val))
|
||||
return '';
|
||||
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
49
endpoints/contactus/contactus.php
Normal file
49
endpoints/contactus/contactus.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class ContactusBaseResponse extends TextResponse
|
||||
{
|
||||
protected array $expectedPOST = array(
|
||||
'mode' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'reason' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'ua' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
|
||||
'appname' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
|
||||
'page' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']],
|
||||
'desc' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ],
|
||||
'id' => ['filter' => FILTER_VALIDATE_INT ],
|
||||
'relatedurl' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']],
|
||||
'email' => ['filter' => FILTER_SANITIZE_EMAIL ]
|
||||
);
|
||||
|
||||
/* responses
|
||||
0: success
|
||||
1: captcha invalid
|
||||
2: description too long
|
||||
3: reason missing
|
||||
7: already reported
|
||||
$: prints response
|
||||
*/
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->assertPOST('mode', 'reason'))
|
||||
{
|
||||
$this->result = 4;
|
||||
return;
|
||||
}
|
||||
|
||||
$report = new Report($this->_post['mode'], $this->_post['reason'], $this->_post['id']);
|
||||
if ($report->create($this->_post['desc'], $this->_post['ua'], $this->_post['appname'], $this->_post['page'], $this->_post['relatedurl'], $this->_post['email']))
|
||||
$this->result = 0;
|
||||
else if (($e = $report->getError()) > 0)
|
||||
$this->result = $e;
|
||||
else
|
||||
$this->result = Lang::main('intError');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
54
endpoints/cookie/cookie.php
Normal file
54
endpoints/cookie/cookie.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CookieBaseResponse extends TextResponse
|
||||
{
|
||||
protected bool $requiresLogin = true;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'purge' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
|
||||
);
|
||||
|
||||
public function __construct(private string $param)
|
||||
{
|
||||
// note that parent::__construct has to come after this
|
||||
if ($param && preg_match('/^[\w-]+$/i', $param))
|
||||
$this->expectedGET = [$param => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']]];
|
||||
|
||||
// NOW we know, what to expect and sanitize
|
||||
parent::__construct($param);
|
||||
}
|
||||
|
||||
/* responses
|
||||
0: success
|
||||
$: silent error
|
||||
*/
|
||||
protected function generate() : void
|
||||
{
|
||||
if (!$this->param && $this->_get['purge'])
|
||||
{
|
||||
if (User::$id && DB::Aowow()->query('UPDATE ?_account_cookies SET `data` = "purged" WHERE `userId` = ?d AND `name` LIKE "announcement-%"', User::$id) !== null)
|
||||
$this->result = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->param || !$this->assertGET($this->param))
|
||||
{
|
||||
trigger_error('CookieBaseResponse - malformed request received', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DB::Aowow()->query('REPLACE INTO ?_account_cookies VALUES (?d, ?, ?)', User::$id, $this->param, $this->_get[$this->param]))
|
||||
$this->result = 0;
|
||||
else
|
||||
trigger_error('CookieBaseResponse - write to db failed', E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
76
endpoints/currencies/currencies.php
Normal file
76
endpoints/currencies/currencies.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CurrenciesBaseResponse extends TemplateResponse implements ICache
|
||||
{
|
||||
use TrListPage, TrCache;
|
||||
|
||||
protected int $type = Type::CURRENCY;
|
||||
protected int $cacheType = CACHE_TYPE_LIST_PAGE;
|
||||
|
||||
protected string $template = 'list-page-generic';
|
||||
protected string $pageName = 'currencies';
|
||||
protected ?int $activeTab = parent::TAB_DATABASE;
|
||||
protected array $breadcrumb = [0, 15];
|
||||
|
||||
protected array $validCats = [1, 2, 3, 22];
|
||||
|
||||
public function __construct(string $pageParam)
|
||||
{
|
||||
$this->getCategoryFromUrl($pageParam);
|
||||
|
||||
parent::__construct($pageParam);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->h1 = Util::ucFirst(Lang::game('currencies'));
|
||||
|
||||
|
||||
/**************/
|
||||
/* Page Title */
|
||||
/**************/
|
||||
|
||||
array_unshift($this->title, $this->h1);
|
||||
if ($this->category)
|
||||
array_unshift($this->title, Lang::currency('cat', $this->category[0]));
|
||||
|
||||
|
||||
/*************/
|
||||
/* Menu Path */
|
||||
/*************/
|
||||
|
||||
if ($this->category)
|
||||
$this->breadcrumb[] = $this->category[0];
|
||||
|
||||
|
||||
/****************/
|
||||
/* Main Content */
|
||||
/****************/
|
||||
|
||||
$this->redButtons[BUTTON_WOWHEAD] = true;
|
||||
|
||||
$conditions = [];
|
||||
|
||||
if (!User::isInGroup(U_GROUP_EMPLOYEE))
|
||||
$conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
|
||||
|
||||
if ($this->category)
|
||||
$conditions[] = ['category', $this->category[0]];
|
||||
|
||||
$money = new CurrencyList($conditions);
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview(['data' => $money->getListviewData()], CurrencyList::$brickFile));
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
267
endpoints/currency/currency.php
Normal file
267
endpoints/currency/currency.php
Normal file
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CurrencyBaseResponse extends TemplateResponse implements ICache
|
||||
{
|
||||
use TrDetailPage, TrCache;
|
||||
|
||||
protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
|
||||
|
||||
protected string $template = 'detail-page-generic';
|
||||
protected string $pageName = 'currency';
|
||||
protected ?int $activeTab = parent::TAB_DATABASE;
|
||||
protected array $breadcrumb = [0, 15];
|
||||
|
||||
public int $type = Type::CURRENCY;
|
||||
public int $typeId = 0;
|
||||
|
||||
private CurrencyList $subject;
|
||||
|
||||
public function __construct(string $id)
|
||||
{
|
||||
parent::__construct($id);
|
||||
|
||||
$this->typeId = intVal($id);
|
||||
$this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE;
|
||||
}
|
||||
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$this->subject = new CurrencyList(array(['id', $this->typeId]));
|
||||
if ($this->subject->error)
|
||||
$this->generateNotFound(Lang::game('currency'), Lang::currency('notFound'));
|
||||
|
||||
$this->h1 = $this->subject->getField('name', true);
|
||||
|
||||
$this->gPageInfo += array(
|
||||
'type' => $this->type,
|
||||
'typeId' => $this->typeId,
|
||||
'name' => $this->h1
|
||||
);
|
||||
|
||||
$_relItemId = $this->subject->getField('itemId');
|
||||
|
||||
|
||||
/**************/
|
||||
/* Page Title */
|
||||
/**************/
|
||||
|
||||
array_unshift($this->title, $this->h1, Util::ucFirst(Lang::game('currency')));
|
||||
|
||||
|
||||
/*************/
|
||||
/* Menu Path */
|
||||
/*************/
|
||||
|
||||
$this->breadcrumb[] = $this->subject->getField('category');
|
||||
|
||||
|
||||
/***********/
|
||||
/* Infobox */
|
||||
/**********/
|
||||
|
||||
$infobox = Lang::getInfoBoxForFlags(intval($this->subject->getField('cuFlags')));
|
||||
|
||||
// cap
|
||||
if ($_ = $this->subject->getField('cap'))
|
||||
$infobox[] = Lang::currency('cap').Lang::nf($_);
|
||||
|
||||
// id
|
||||
$infobox[] = Lang::currency('id') . $this->typeId;
|
||||
|
||||
// icon
|
||||
if ($_ = $this->subject->getField('iconId'))
|
||||
{
|
||||
$infobox[] = Util::ucFirst(Lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
|
||||
$this->extendGlobalIds(Type::ICON, $_);
|
||||
}
|
||||
|
||||
// original name
|
||||
if (Lang::getLocale() != Locale::EN)
|
||||
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
|
||||
|
||||
if ($infobox)
|
||||
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
|
||||
|
||||
|
||||
/****************/
|
||||
/* Main Content */
|
||||
/****************/
|
||||
|
||||
$hi = $this->subject->getJSGlobals()[Type::CURRENCY][$this->typeId]['icon'];
|
||||
if ($hi[0] == $hi[1])
|
||||
unset($hi[1]);
|
||||
|
||||
$this->headIcons = $hi;
|
||||
$this->redButtons = array(
|
||||
BUTTON_WOWHEAD => true,
|
||||
BUTTON_LINKS => true
|
||||
);
|
||||
|
||||
if ($_ = $this->subject->getField('description', true))
|
||||
$this->extraText = new Markup($_, ['dbpage' => true, 'allow' => Markup::CLASS_ADMIN], 'text-generic');
|
||||
|
||||
|
||||
/**************/
|
||||
/* Extra Tabs */
|
||||
/**************/
|
||||
|
||||
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
|
||||
|
||||
if ($this->typeId != CURRENCY_HONOR_POINTS && $this->typeId != CURRENCY_ARENA_POINTS)
|
||||
{
|
||||
// tabs: this currency is contained in..
|
||||
$lootTabs = new LootByItem($_relItemId);
|
||||
|
||||
if ($lootTabs->getByItem())
|
||||
{
|
||||
$this->extendGlobalData($lootTabs->jsGlobals);
|
||||
|
||||
foreach ($lootTabs->iterate() as [$template, $tabData])
|
||||
{
|
||||
if ($template == 'npc' || $template == 'object')
|
||||
$this->addDataLoader('zones');
|
||||
|
||||
if ($template != 'quest')
|
||||
{
|
||||
foreach ($tabData['data'] as &$row)
|
||||
if (!empty($row['stack']))
|
||||
$row['currency'] = [[$this->typeId, $row['stack'][0]]];
|
||||
|
||||
$tabData['extraCols'][] = '$Listview.extraCols.currency';
|
||||
}
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview($tabData, $template));
|
||||
}
|
||||
}
|
||||
|
||||
// tab: sold by
|
||||
$itemObj = new ItemList(array(['id', $_relItemId]));
|
||||
if (!empty($itemObj->getExtendedCost()[$_relItemId]))
|
||||
{
|
||||
$vendors = $itemObj->getExtendedCost()[$_relItemId];
|
||||
$this->extendGlobalData($itemObj->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
|
||||
|
||||
$soldBy = new CreatureList(array(['id', array_keys($vendors)]));
|
||||
if (!$soldBy->error)
|
||||
{
|
||||
$sbData = $soldBy->getListviewData();
|
||||
$extraCols = ['$Listview.extraCols.stock', "\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost', '$Listview.extraCols.condition'];
|
||||
foreach ($sbData as $k => &$row)
|
||||
{
|
||||
$items = [];
|
||||
$tokens = [];
|
||||
// note: can only display one entry per row, so only use first entry of each vendor
|
||||
foreach ($vendors[$k][0] as $id => $qty)
|
||||
{
|
||||
if (is_string($id))
|
||||
continue;
|
||||
|
||||
if ($id > 0)
|
||||
$tokens[] = [$id, $qty];
|
||||
else if ($id < 0)
|
||||
$items[] = [-$id, $qty];
|
||||
}
|
||||
|
||||
if ($e = $vendors[$k][0]['event'])
|
||||
if (Conditions::extendListviewRow($row, Conditions::SRC_NONE, $k, [Conditions::ACTIVE_EVENT, $e]))
|
||||
$this->extendGlobalIds(Type::WORLDEVENT, $e);
|
||||
|
||||
$row['stock'] = $vendors[$k][0]['stock'];
|
||||
$row['stack'] = $itemObj->getField('buyCount');
|
||||
$row['cost'] = array(
|
||||
$itemObj->getField('buyPrice'),
|
||||
$items ?: null,
|
||||
$tokens ?: null
|
||||
);
|
||||
}
|
||||
|
||||
// no conditions > remove conditions column
|
||||
if (!array_column($sbData, 'condition'))
|
||||
array_pop($extraCols);
|
||||
|
||||
$this->addDataLoader('zones');
|
||||
$this->lvTabs->addListviewTab(new Listview(array(
|
||||
'data' => $sbData,
|
||||
'name' => '$LANG.tab_soldby',
|
||||
'id' => 'sold-by-npc',
|
||||
'extraCols' => $extraCols,
|
||||
'hiddenCols' => ['level', 'type']
|
||||
), CreatureList::$brickFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tab: created by (spell) [for items its handled in LootByItem]
|
||||
if ($this->typeId == CURRENCY_HONOR_POINTS)
|
||||
{
|
||||
$createdBy = new SpellList(array(['effect1Id', SPELL_EFFECT_ADD_HONOR], ['effect2Id', SPELL_EFFECT_ADD_HONOR], ['effect3Id', SPELL_EFFECT_ADD_HONOR], 'OR'));
|
||||
if (!$createdBy->error)
|
||||
{
|
||||
$this->extendGlobalData($createdBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
|
||||
|
||||
$tabData = array(
|
||||
'data' => $createdBy->getListviewData(),
|
||||
'name' => '$LANG.tab_createdby',
|
||||
'id' => 'created-by',
|
||||
);
|
||||
|
||||
if ($createdBy->hasSetFields('reagent1', 'reagent2', 'reagent3', 'reagent4', 'reagent5', 'reagent6', 'reagent7', 'reagent8'))
|
||||
$tabData['visibleCols'] = ['reagents'];
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview($tabData, SpellList::$brickFile));
|
||||
}
|
||||
}
|
||||
|
||||
// tab: currency for
|
||||
$n = $w = null;
|
||||
if ($this->typeId == CURRENCY_ARENA_POINTS)
|
||||
{
|
||||
$n = '?items&filter=cr=145;crs=1;crv=0';
|
||||
$w = '`reqArenaPoints` > 0';
|
||||
}
|
||||
else if ($this->typeId == CURRENCY_HONOR_POINTS)
|
||||
{
|
||||
$n = '?items&filter=cr=144;crs=1;crv=0';
|
||||
$w = '`reqHonorPoints` > 0';
|
||||
}
|
||||
else
|
||||
$w = '`reqItemId1` = '.$_relItemId.' OR `reqItemId2` = '.$_relItemId.' OR `reqItemId3` = '.$_relItemId.' OR `reqItemId4` = '.$_relItemId.' OR `reqItemId5` = '.$_relItemId;
|
||||
|
||||
if (!$n && !is_null(ItemListFilter::getCriteriaIndex(158, $_relItemId)))
|
||||
$n = '?items&filter=cr=158;crs='.$_relItemId.';crv=0';
|
||||
|
||||
$xCosts = DB::Aowow()->selectCol('SELECT `id` FROM ?_itemextendedcost WHERE '.$w);
|
||||
$boughtBy = $xCosts ? DB::World()->selectCol('SELECT `item` FROM npc_vendor WHERE `extendedCost` IN (?a) UNION SELECT `item` FROM game_event_npc_vendor WHERE `extendedCost` IN (?a)', $xCosts, $xCosts) : [];
|
||||
if ($boughtBy)
|
||||
{
|
||||
$boughtBy = new ItemList(array(['id', $boughtBy]));
|
||||
if (!$boughtBy->error)
|
||||
{
|
||||
$tabData = array(
|
||||
'data' => $boughtBy->getListviewData(ITEMINFO_VENDOR, [Type::CURRENCY => $this->typeId]),
|
||||
'name' => '$LANG.tab_currencyfor',
|
||||
'id' => 'currency-for',
|
||||
'extraCols' => ["\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost']
|
||||
);
|
||||
|
||||
if ($n)
|
||||
$tabData['note'] = sprintf(Util::$filterResultString, $n);
|
||||
|
||||
$this->lvTabs->addListviewTab(new Listview($tabData, ItemList::$brickFile));
|
||||
|
||||
$this->extendGlobalData($boughtBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
|
||||
}
|
||||
}
|
||||
|
||||
parent::generate();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
50
endpoints/currency/currency_power.php
Normal file
50
endpoints/currency/currency_power.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CurrencyPowerResponse extends TextResponse implements ICache
|
||||
{
|
||||
use TrTooltip, TrCache;
|
||||
|
||||
private const /* string */ POWER_TEMPLATE = '$WowheadPower.registerCurrency(%d, %d, %s);';
|
||||
|
||||
protected int $type = Type::CURRENCY;
|
||||
protected int $typeId = 0;
|
||||
protected int $cacheType = CACHE_TYPE_TOOLTIP;
|
||||
|
||||
protected array $expectedGET = array(
|
||||
'domain' => ['filter' => FILTER_CALLBACK, 'options' => [Locale::class, 'tryFromDomain']]
|
||||
);
|
||||
|
||||
public function __construct(string $id)
|
||||
{
|
||||
parent::__construct($id);
|
||||
|
||||
// temp locale
|
||||
if ($this->_get['domain'])
|
||||
Lang::load($this->_get['domain']);
|
||||
|
||||
$this->typeId = intVal($id);
|
||||
}
|
||||
|
||||
protected function generate() : void
|
||||
{
|
||||
$currency = new CurrencyList(array(['id', $this->typeId]));
|
||||
if ($currency->error)
|
||||
$this->cacheType = CACHE_TYPE_NONE;
|
||||
else
|
||||
$opts = array(
|
||||
'name' => $currency->getField('name', true),
|
||||
'tooltip' => $currency->renderTooltip(),
|
||||
'icon' => $currency->getField('iconString')
|
||||
);
|
||||
|
||||
$this->result = new Tooltip(self::POWER_TEMPLATE, $this->typeId, $opts ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user