mirror of
https://framagit.org/hubzilla/core.git
synced 2026-06-21 09:01:15 -04:00
Compare commits
1761 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c1d0f7498 | ||
|
|
81ba070e1a | ||
|
|
a7812657f1 | ||
|
|
18725c47a0 | ||
|
|
75e1b70584 | ||
|
|
1dc73935d9 | ||
|
|
7d7b43c5b9 | ||
|
|
b5223a4efb | ||
|
|
d71c2c245f | ||
|
|
526729c0f1 | ||
|
|
1cd3369f6a | ||
|
|
c26ae553e6 | ||
|
|
8d78698d00 | ||
|
|
d5c189753a | ||
|
|
9861e7a0c4 | ||
|
|
6d5fa9205c | ||
|
|
0fee7804fb | ||
|
|
4002dbaa8b | ||
|
|
57e32a7912 | ||
|
|
b6a72d6e4e | ||
|
|
6e592ed200 | ||
|
|
6c033fc776 | ||
|
|
7c4362db53 | ||
|
|
f7bf9ede72 | ||
|
|
1aeb05628b | ||
|
|
b464fae3bf | ||
|
|
a34ce0732d | ||
|
|
f457b6623d | ||
|
|
01ebd51fb2 | ||
|
|
cb25fc031b | ||
|
|
6c6fc82f43 | ||
|
|
990017b588 | ||
|
|
192ab22cda | ||
|
|
5e5a0d7c91 | ||
|
|
2dc1adf091 | ||
|
|
371b8440c3 | ||
|
|
49509e7347 | ||
|
|
66f5b34c07 | ||
|
|
6a3d372050 | ||
|
|
c95359024c | ||
|
|
edf898d7b7 | ||
|
|
ee0060619d | ||
|
|
4f8ede35bd | ||
|
|
9d9c102da8 | ||
|
|
3de8f5e7de | ||
|
|
a18e873d08 | ||
|
|
b3c260a145 | ||
|
|
1097bcdaf9 | ||
|
|
cfde1be097 | ||
|
|
6df4da5313 | ||
|
|
f910de849f | ||
|
|
d6eaeba239 | ||
|
|
5c2e10c01e | ||
|
|
8754f72e63 | ||
|
|
569f243ebd | ||
|
|
651a3f8380 | ||
|
|
40714ecdd0 | ||
|
|
d4b1bcd641 | ||
|
|
acc1834b0d | ||
|
|
e237cf226a | ||
|
|
7c5a0887cc | ||
|
|
0745d0616a | ||
|
|
9cbb9a4874 | ||
|
|
14f3b72c82 | ||
|
|
b05845f495 | ||
|
|
2114779037 | ||
|
|
937b6d360e | ||
|
|
0d30eed4a7 | ||
|
|
39d4f67417 | ||
|
|
68d989c79e | ||
|
|
9600789d6b | ||
|
|
ea7559c158 | ||
|
|
baa12b7497 | ||
|
|
ac4aa6a9ea | ||
|
|
732dbfd6f5 | ||
|
|
5860abf46f | ||
|
|
2874d3e1e1 | ||
|
|
42b0205ad0 | ||
|
|
36778850ee | ||
|
|
19c0e97658 | ||
|
|
fe018d646a | ||
|
|
24132e56d9 | ||
|
|
8fc0210428 | ||
|
|
64560cbca9 | ||
|
|
6e97c97920 | ||
|
|
916edcb45e | ||
|
|
8873c10364 | ||
|
|
77e1220cf9 | ||
|
|
f402baffd3 | ||
|
|
216f3755fc | ||
|
|
d846cefade | ||
|
|
f3efdbf230 | ||
|
|
826ef11247 | ||
|
|
b68ddc4cd3 | ||
|
|
41f909c415 | ||
|
|
0b4e086376 | ||
|
|
5af3c35778 | ||
|
|
dddcddc453 | ||
|
|
754d90a676 | ||
|
|
8b0dce56ad | ||
|
|
42696606e3 | ||
|
|
b645ede168 | ||
|
|
6e12b5ec08 | ||
|
|
fbf36992be | ||
|
|
e0de813700 | ||
|
|
39448a0871 | ||
|
|
4fb37ef6f3 | ||
|
|
a9d4adaf23 | ||
|
|
70dfce356b | ||
|
|
10fbfa06e9 | ||
|
|
6315757967 | ||
|
|
9ddd840897 | ||
|
|
6c5627ac0e | ||
|
|
c2d6d376a4 | ||
|
|
55532c7cb1 | ||
|
|
a93fed5ae2 | ||
|
|
ba1b48f177 | ||
|
|
898762dd95 | ||
|
|
81ce67df94 | ||
|
|
55097c47c5 | ||
|
|
97b82fc77b | ||
|
|
b7bda0b87d | ||
|
|
6bf61dfa6b | ||
|
|
0e59cfb839 | ||
|
|
328ce0a837 | ||
|
|
34e24ea5e9 | ||
|
|
907426af5e | ||
|
|
360713c689 | ||
|
|
ee8aba3221 | ||
|
|
ae657754b0 | ||
|
|
0a730935f5 | ||
|
|
d285da09fe | ||
|
|
b291f1bad3 | ||
|
|
e1b660bfa3 | ||
|
|
0036c0cde9 | ||
|
|
49c1833a46 | ||
|
|
46fa26502b | ||
|
|
ba1e705c61 | ||
|
|
043e2ff58b | ||
|
|
deba1863f5 | ||
|
|
0e27f010f9 | ||
|
|
45b1be8962 | ||
|
|
fe9ca30c5e | ||
|
|
9e8ba5f6e2 | ||
|
|
5207e1e774 | ||
|
|
c1228d6b6d | ||
|
|
6262d351b7 | ||
|
|
27e57ff7aa | ||
|
|
74fc7508f3 | ||
|
|
3eea4f475c | ||
|
|
37b22fe542 | ||
|
|
b5b8106e7a | ||
|
|
b77100ff6d | ||
|
|
5eb6572277 | ||
|
|
69bed9c889 | ||
|
|
3c88c5e66a | ||
|
|
d40d62ac4f | ||
|
|
d23ed6b11f | ||
|
|
1e2a4a57b6 | ||
|
|
7892eeb2d2 | ||
|
|
d7ceb977da | ||
|
|
36d0594b8e | ||
|
|
3f32a5239d | ||
|
|
15a7d2d4de | ||
|
|
80ed2ff89a | ||
|
|
37a0343163 | ||
|
|
8529e2f14e | ||
|
|
b73401bd7f | ||
|
|
af839a0589 | ||
|
|
4bbeb224f6 | ||
|
|
8435d9eb13 | ||
|
|
d97df1859f | ||
|
|
46f67eaa1e | ||
|
|
54451851bb | ||
|
|
5b7387459c | ||
|
|
5d64a9c90f | ||
|
|
ffaa985339 | ||
|
|
291e12574a | ||
|
|
fc5b5ba021 | ||
|
|
056c55a963 | ||
|
|
02a0af3eef | ||
|
|
96e8316633 | ||
|
|
37878bf0a3 | ||
|
|
b4f079c4b5 | ||
|
|
18abfb11ef | ||
|
|
25dbc8a9f6 | ||
|
|
c639704f3c | ||
|
|
3dd7394247 | ||
|
|
b860b730a9 | ||
|
|
17e2877c91 | ||
|
|
b7bc28c333 | ||
|
|
fb5a52cbde | ||
|
|
6d125d02d8 | ||
|
|
0b18b35f5f | ||
|
|
9859008271 | ||
|
|
423c36f67b | ||
|
|
1e0195e439 | ||
|
|
30271bb32f | ||
|
|
e2b1670d6c | ||
|
|
2bfdfbe3cc | ||
|
|
9a85421a0e | ||
|
|
1fa59df6f3 | ||
|
|
2c93294eea | ||
|
|
316829269a | ||
|
|
55236f86e0 | ||
|
|
060210e930 | ||
|
|
9291622885 | ||
|
|
07978a061e | ||
|
|
08b2356ed1 | ||
|
|
d17934ed80 | ||
|
|
94f17f0dae | ||
|
|
7ccb2a2615 | ||
|
|
067a66b927 | ||
|
|
096fad5e8c | ||
|
|
e21e4c7127 | ||
|
|
5754ea828d | ||
|
|
5fbc203367 | ||
|
|
08884c44fb | ||
|
|
bd04ca21a4 | ||
|
|
19ae8cfdfc | ||
|
|
86e953f495 | ||
|
|
4bc4f5b2a6 | ||
|
|
5f2e808497 | ||
|
|
4f69e02768 | ||
|
|
e6ce2885c0 | ||
|
|
ec19ee9d82 | ||
|
|
983f063d33 | ||
|
|
eb6a143fff | ||
|
|
1881029040 | ||
|
|
209651705d | ||
|
|
fab3c92a7c | ||
|
|
8ad7376865 | ||
|
|
c41831aff9 | ||
|
|
8515aa6966 | ||
|
|
691de5bf2e | ||
|
|
2e155892fe | ||
|
|
6680c2faf3 | ||
|
|
decc8f2162 | ||
|
|
7d7a0c160f | ||
|
|
e89563eb4c | ||
|
|
dd204ec34f | ||
|
|
b3a5dd8aa8 | ||
|
|
26ee56f39c | ||
|
|
300b0e27bd | ||
|
|
29489f62cf | ||
|
|
09465619e5 | ||
|
|
390af7722d | ||
|
|
e18157f818 | ||
|
|
5435d2881c | ||
|
|
2e8f3d1869 | ||
|
|
3ad1cab006 | ||
|
|
b806a3ccc2 | ||
|
|
5e780ba089 | ||
|
|
c0a7dfe2f6 | ||
|
|
fe50d78a0f | ||
|
|
e513950cb5 | ||
|
|
adc6390a22 | ||
|
|
9449e8bd61 | ||
|
|
f13af0f60b | ||
|
|
acdb773f89 | ||
|
|
651a288148 | ||
|
|
b0664f7349 | ||
|
|
51586037e1 | ||
|
|
f573c1772a | ||
|
|
f08e91e19d | ||
|
|
8cd9fff26a | ||
|
|
d489a2854e | ||
|
|
9cfd1c2318 | ||
|
|
2dd0677d23 | ||
|
|
00d403e729 | ||
|
|
d83e2daf36 | ||
|
|
8fe6aede03 | ||
|
|
fa4ab45692 | ||
|
|
f57fbaa5dd | ||
|
|
aba8002170 | ||
|
|
9cc85adf47 | ||
|
|
01ed001041 | ||
|
|
403539919a | ||
|
|
52ea2fa33e | ||
|
|
ecd4754f6d | ||
|
|
f71033b30d | ||
|
|
885068834f | ||
|
|
c4c4ab2f3d | ||
|
|
aec3247952 | ||
|
|
b05b756148 | ||
|
|
03819abb22 | ||
|
|
d074e2aba0 | ||
|
|
2bbecfe8dd | ||
|
|
e078e13325 | ||
|
|
9d3b852d38 | ||
|
|
fadb0a5bf2 | ||
|
|
fa7aa6cedb | ||
|
|
6df98f2fad | ||
|
|
c597017402 | ||
|
|
9cb95f6065 | ||
|
|
477b1535a2 | ||
|
|
1d652cfcbd | ||
|
|
28b604c7c7 | ||
|
|
8c11be07cd | ||
|
|
e8dd2c28ff | ||
|
|
0bdffc4a2d | ||
|
|
94b1fe4a8b | ||
|
|
11ed445319 | ||
|
|
400dfb4e6b | ||
|
|
6b951734ce | ||
|
|
000fcfd1ac | ||
|
|
051e2ed6cd | ||
|
|
aac406a245 | ||
|
|
ef2952b5fd | ||
|
|
ffdf54b097 | ||
|
|
58593d7da6 | ||
|
|
3d13f36ce2 | ||
|
|
8c843ec6d0 | ||
|
|
232c7f5301 | ||
|
|
16cd8caef3 | ||
|
|
43dabee53d | ||
|
|
f016760851 | ||
|
|
99b5166f21 | ||
|
|
4aa29db7aa | ||
|
|
f5b8b18c8e | ||
|
|
6e5566f9c8 | ||
|
|
ca216ae819 | ||
|
|
4713241444 | ||
|
|
4917170a0d | ||
|
|
87775ae37a | ||
|
|
eaa244a2a3 | ||
|
|
db91d66d1a | ||
|
|
4d64481564 | ||
|
|
0a31fc176c | ||
|
|
659a8c967c | ||
|
|
626887a792 | ||
|
|
256b66de41 | ||
|
|
5ee4f37b8d | ||
|
|
3dc122db84 | ||
|
|
99c5a4e2f8 | ||
|
|
f922a92ffa | ||
|
|
a36de8ba1a | ||
|
|
e3d30763da | ||
|
|
c73518d8ec | ||
|
|
ed0d2fed66 | ||
|
|
960bcb6b53 | ||
|
|
6252340804 | ||
|
|
517d67b2e0 | ||
|
|
e95b7ca3a0 | ||
|
|
c771d7c31a | ||
|
|
ce0e8d7497 | ||
|
|
852678e238 | ||
|
|
16e6eec3fb | ||
|
|
afe8552be6 | ||
|
|
cd0e50da24 | ||
|
|
85001c034b | ||
|
|
a56f6576e2 | ||
|
|
d3e5d05026 | ||
|
|
322b619a71 | ||
|
|
dff906ca69 | ||
|
|
515d1d5e63 | ||
|
|
bb6ed22594 | ||
|
|
d655e1d765 | ||
|
|
db70ed006d | ||
|
|
ce1dd5c632 | ||
|
|
9e2a253dda | ||
|
|
95c645865d | ||
|
|
ceb510bbf5 | ||
|
|
2590e3c99b | ||
|
|
f2f9cfaf28 | ||
|
|
62db8c3969 | ||
|
|
ae3db366e5 | ||
|
|
57570c144a | ||
|
|
c3a235242e | ||
|
|
b629eb5657 | ||
|
|
2e674cd0b3 | ||
|
|
3330e9a19a | ||
|
|
c5f6208396 | ||
|
|
c0d93bbcf4 | ||
|
|
db941e7007 | ||
|
|
4761857157 | ||
|
|
3aefe23184 | ||
|
|
6f852814fd | ||
|
|
b15e521b0e | ||
|
|
63c401e6d6 | ||
|
|
e59750e8de | ||
|
|
9c184ddfd0 | ||
|
|
9df6e821d8 | ||
|
|
9551dc5ecd | ||
|
|
d372daff60 | ||
|
|
f742e6e394 | ||
|
|
414b2b0e4c | ||
|
|
603c5692ae | ||
|
|
b35e994d1b | ||
|
|
abe2ab229a | ||
|
|
5ad9939bcf | ||
|
|
ce451128ba | ||
|
|
70470016cc | ||
|
|
2122ea77e1 | ||
|
|
69266cd6c6 | ||
|
|
062d61567e | ||
|
|
d6120fc908 | ||
|
|
91f8e7a07b | ||
|
|
f57d89245c | ||
|
|
c307a71f53 | ||
|
|
1e4e59bb57 | ||
|
|
f62d16d274 | ||
|
|
f175712d4b | ||
|
|
5f942d78e6 | ||
|
|
538c8885ad | ||
|
|
c8158c3d62 | ||
|
|
1f4762060f | ||
|
|
81c3682781 | ||
|
|
2e6e1fdd55 | ||
|
|
1a09cd560b | ||
|
|
8c9d2bc6f6 | ||
|
|
78ad5ca713 | ||
|
|
43c2e71b25 | ||
|
|
5b9f32fade | ||
|
|
76a1a6da34 | ||
|
|
a2b0abc90d | ||
|
|
0fd8e02a88 | ||
|
|
55c4bfb670 | ||
|
|
ea1030f8bf | ||
|
|
6dd285811a | ||
|
|
ee149ed1eb | ||
|
|
c3aa96d423 | ||
|
|
6e59d95da5 | ||
|
|
a396e74a79 | ||
|
|
80bdb39ae3 | ||
|
|
5cb1a9dcc6 | ||
|
|
1cfa81450c | ||
|
|
1f18fed3a2 | ||
|
|
33cb429e15 | ||
|
|
cfdbf02e6b | ||
|
|
f8c631a3f0 | ||
|
|
74911e9f6d | ||
|
|
0f453ae4cf | ||
|
|
febebc8d3d | ||
|
|
166c45f97f | ||
|
|
af58364fef | ||
|
|
eadf83d61b | ||
|
|
3c20231aad | ||
|
|
402bf395d1 | ||
|
|
9a45938fe6 | ||
|
|
d963e4a2c4 | ||
|
|
1063a67ae2 | ||
|
|
602ef883bb | ||
|
|
aad6042d42 | ||
|
|
85d8c1a97e | ||
|
|
547e6542ba | ||
|
|
f74044f9b0 | ||
|
|
42651707f8 | ||
|
|
95c13eaf5a | ||
|
|
c74068ae4d | ||
|
|
ca92dd0299 | ||
|
|
798f791b6b | ||
|
|
331622309f | ||
|
|
4f334525c2 | ||
|
|
82e704ec5b | ||
|
|
67aaa97904 | ||
|
|
4f55248a6c | ||
|
|
61b46f1a3e | ||
|
|
286104a988 | ||
|
|
8ea6ead08a | ||
|
|
bd9cc23681 | ||
|
|
f2d7298cf4 | ||
|
|
da636ca881 | ||
|
|
39bfa30185 | ||
|
|
d7c005a2f1 | ||
|
|
763b69bf5b | ||
|
|
2f2e353ece | ||
|
|
0092b7c0a4 | ||
|
|
9f48109640 | ||
|
|
b976adb8aa | ||
|
|
fe38c81e07 | ||
|
|
fed9bc7072 | ||
|
|
044e252d19 | ||
|
|
6920fb2793 | ||
|
|
3d1171de8d | ||
|
|
6e09754d06 | ||
|
|
32f2de17d4 | ||
|
|
600e8081a8 | ||
|
|
59fd7c3c9a | ||
|
|
7e70fe1126 | ||
|
|
5265c774ae | ||
|
|
d1421d720c | ||
|
|
2bd09d3b30 | ||
|
|
56e54ac820 | ||
|
|
08d85798ed | ||
|
|
38c2b36606 | ||
|
|
01b747287a | ||
|
|
c925e13e5a | ||
|
|
1ced89a869 | ||
|
|
a5e32dc3de | ||
|
|
150174c3bb | ||
|
|
601ee0b40b | ||
|
|
9627c3e7c8 | ||
|
|
d88c67eba3 | ||
|
|
bb38a90dda | ||
|
|
c36743f7ee | ||
|
|
860275f888 | ||
|
|
670228ff63 | ||
|
|
4338e4ef86 | ||
|
|
dbc712c53c | ||
|
|
bb6011ea73 | ||
|
|
1915f34de8 | ||
|
|
675f26fe90 | ||
|
|
491e309911 | ||
|
|
283d27c5b1 | ||
|
|
24d862c1bc | ||
|
|
3708c1ac8c | ||
|
|
df60be423a | ||
|
|
a1e583129f | ||
|
|
a06b28b693 | ||
|
|
198070700c | ||
|
|
3a01aa40d8 | ||
|
|
b628af2258 | ||
|
|
0cb5d0d63e | ||
|
|
526c85e2ca | ||
|
|
8a21c8e618 | ||
|
|
8d3d5747d2 | ||
|
|
f1f1406596 | ||
|
|
9e42be9884 | ||
|
|
75e75b93cf | ||
|
|
0e8ecbc9fc | ||
|
|
a88236b36f | ||
|
|
df06c54bd2 | ||
|
|
f2d88387c9 | ||
|
|
db4c6bbd3b | ||
|
|
c8e7f79f50 | ||
|
|
1157dc7dc8 | ||
|
|
c12833521f | ||
|
|
5d9346999e | ||
|
|
52c0631833 | ||
|
|
e16aefec2c | ||
|
|
053a247cc8 | ||
|
|
1426b7a6dc | ||
|
|
ec66949b35 | ||
|
|
bc9d8d1136 | ||
|
|
542a527d0d | ||
|
|
5e0c392287 | ||
|
|
05ff94941c | ||
|
|
a27d75d610 | ||
|
|
446e842904 | ||
|
|
ba153e2c18 | ||
|
|
3342ea6891 | ||
|
|
8c3d5fd295 | ||
|
|
5fc58fb10a | ||
|
|
4e2ae9cd4e | ||
|
|
4167ca2e5c | ||
|
|
9b3e9dcf02 | ||
|
|
f203fcc92e | ||
|
|
0b20069c20 | ||
|
|
bad7b778b3 | ||
|
|
f89ce93940 | ||
|
|
1ddbc8a26d | ||
|
|
0a3094fc9a | ||
|
|
816bbad28a | ||
|
|
ce4150cd06 | ||
|
|
be19b75031 | ||
|
|
3ddab5966b | ||
|
|
2d5c99f459 | ||
|
|
a5a1bbf5d7 | ||
|
|
c4af4e3297 | ||
|
|
1acfc05e2d | ||
|
|
b40c38f58c | ||
|
|
87c41cb9ac | ||
|
|
0ec715d7c9 | ||
|
|
e6a261a789 | ||
|
|
3bd2a91992 | ||
|
|
c721bc672c | ||
|
|
931b876b44 | ||
|
|
87b91e5023 | ||
|
|
4b1384be83 | ||
|
|
8da6e6a096 | ||
|
|
5181e575b4 | ||
|
|
ca994735be | ||
|
|
718c303086 | ||
|
|
7200c71673 | ||
|
|
14c97799c6 | ||
|
|
a06e8bfaee | ||
|
|
7dad60bbd5 | ||
|
|
a66b4626fb | ||
|
|
330add963d | ||
|
|
da1ccc620b | ||
|
|
c412c01a65 | ||
|
|
74d7fa61d9 | ||
|
|
ef6ea4484e | ||
|
|
ba2d775215 | ||
|
|
f866771854 | ||
|
|
70b8c57d22 | ||
|
|
b04e0d0fd4 | ||
|
|
3f9d5e11d1 | ||
|
|
09fe271b25 | ||
|
|
d5c1a49984 | ||
|
|
e80d3653c9 | ||
|
|
30b0d0a5cc | ||
|
|
9beceb9b02 | ||
|
|
6f13cabfba | ||
|
|
7bd2de9967 | ||
|
|
b0b0973e2d | ||
|
|
3d866e8975 | ||
|
|
63fb8d0392 | ||
|
|
384de0925e | ||
|
|
5b5c569c82 | ||
|
|
6f2371ee74 | ||
|
|
d6e7d90197 | ||
|
|
aa73db0fe6 | ||
|
|
d85088fc3d | ||
|
|
7e21aeedcd | ||
|
|
342460fa17 | ||
|
|
e57211fbd1 | ||
|
|
d31eb4c89c | ||
|
|
6753d260e4 | ||
|
|
a3092204a1 | ||
|
|
10b49af776 | ||
|
|
3497ced9f9 | ||
|
|
7a0f22b0a7 | ||
|
|
4f03272a5f | ||
|
|
7755936a2e | ||
|
|
9f6844ec30 | ||
|
|
c2952aa803 | ||
|
|
8f7383f5c2 | ||
|
|
750641ef19 | ||
|
|
fb48dfc082 | ||
|
|
1de5bce1c1 | ||
|
|
676603239d | ||
|
|
4c87f36a8c | ||
|
|
12b2137a04 | ||
|
|
762e1c9c2b | ||
|
|
cd26ead043 | ||
|
|
6a560cfec4 | ||
|
|
489ba72d5c | ||
|
|
5df9779669 | ||
|
|
e49d2f6b8f | ||
|
|
9550a7a954 | ||
|
|
d9f855b97e | ||
|
|
e275dae6e3 | ||
|
|
88ccc2a3b9 | ||
|
|
84487edc05 | ||
|
|
7177649969 | ||
|
|
18b7b3f125 | ||
|
|
89c7e1a8ee | ||
|
|
774a9b118e | ||
|
|
327841280b | ||
|
|
1e0a686561 | ||
|
|
96883100b4 | ||
|
|
1fcb05ed8c | ||
|
|
8c85516c57 | ||
|
|
6989a3eaad | ||
|
|
57796a2f96 | ||
|
|
59b217f7ea | ||
|
|
fb9a193c44 | ||
|
|
2739f1f3f0 | ||
|
|
0c2cb18578 | ||
|
|
db2c5aae84 | ||
|
|
aa2cec103b | ||
|
|
a532a70ac0 | ||
|
|
ec6cec8854 | ||
|
|
3fb22d27ab | ||
|
|
1cbaab5889 | ||
|
|
fef19b47c2 | ||
|
|
5126613825 | ||
|
|
07097d2fa3 | ||
|
|
8c9fb32ca0 | ||
|
|
68d921e918 | ||
|
|
330b36159d | ||
|
|
046152e798 | ||
|
|
11ca5bb7d5 | ||
|
|
e0838ff8ab | ||
|
|
11a2419c22 | ||
|
|
f3420c0adb | ||
|
|
bd84ff4838 | ||
|
|
87689df062 | ||
|
|
1bfbd57ee4 | ||
|
|
64d7585282 | ||
|
|
0ee03a565f | ||
|
|
1b0e268416 | ||
|
|
0e8d1afcaf | ||
|
|
48a33f08e2 | ||
|
|
f9ca956510 | ||
|
|
65a472c113 | ||
|
|
4b17ea04a7 | ||
|
|
423fdc0a96 | ||
|
|
396b124e84 | ||
|
|
6a27afa6ac | ||
|
|
b9812ba06a | ||
|
|
bc6aded074 | ||
|
|
904401617a | ||
|
|
02089f15c4 | ||
|
|
43a18a2569 | ||
|
|
4c2dc2bf16 | ||
|
|
e6eb2a8476 | ||
|
|
af5ae163f3 | ||
|
|
d00860430f | ||
|
|
af0b4a0fca | ||
|
|
404189f88c | ||
|
|
1a27fad9b9 | ||
|
|
c2cfe481df | ||
|
|
3a56fb6ec6 | ||
|
|
61950decb3 | ||
|
|
1a614ea023 | ||
|
|
a83b6a5e63 | ||
|
|
65d98af24c | ||
|
|
62d35627f3 | ||
|
|
84f1ed1587 | ||
|
|
31b4fb343d | ||
|
|
3c3f5c60da | ||
|
|
18914f2081 | ||
|
|
b50da2d8b7 | ||
|
|
5bdd699c9d | ||
|
|
29e8e6d95c | ||
|
|
049fc91615 | ||
|
|
b7c96f2cbd | ||
|
|
1e6408df13 | ||
|
|
7b90548c1d | ||
|
|
5d330e18ff | ||
|
|
07662ff076 | ||
|
|
9252ae1596 | ||
|
|
0155e9131d | ||
|
|
247cf40ace | ||
|
|
4a6b450c55 | ||
|
|
5b80d57a54 | ||
|
|
8ac4547e73 | ||
|
|
3658975b69 | ||
|
|
abc3545ef9 | ||
|
|
01f8cb698d | ||
|
|
5e4d0f45fc | ||
|
|
efd2f78274 | ||
|
|
690ff955fa | ||
|
|
3fb27bdc55 | ||
|
|
d8306fca6f | ||
|
|
380775540d | ||
|
|
65e3b0dafd | ||
|
|
4ae81d753c | ||
|
|
0f01aac823 | ||
|
|
ce0a96351e | ||
|
|
93dda6f1fd | ||
|
|
5e83db959e | ||
|
|
74436eb03e | ||
|
|
993178fc49 | ||
|
|
1c217cf446 | ||
|
|
0bf2be1f9d | ||
|
|
548589be4d | ||
|
|
697a1b1ed1 | ||
|
|
d893c7eb33 | ||
|
|
8957d24752 | ||
|
|
677e730235 | ||
|
|
4227d973b9 | ||
|
|
06c0af50c3 | ||
|
|
4d3a48d1c1 | ||
|
|
f6b66f4cad | ||
|
|
a56f9ab4e6 | ||
|
|
ca913e5077 | ||
|
|
b25ebe12c5 | ||
|
|
984980b170 | ||
|
|
be45005dbb | ||
|
|
b8c9f04989 | ||
|
|
bf2d41eb14 | ||
|
|
158f9afb37 | ||
|
|
f884e31d21 | ||
|
|
d3a5ff9d68 | ||
|
|
7185780d4e | ||
|
|
f995b2822f | ||
|
|
d7490ac738 | ||
|
|
383d0e17f2 | ||
|
|
7c1a3ab36e | ||
|
|
edc8d17031 | ||
|
|
f3f861234d | ||
|
|
93278c00b7 | ||
|
|
5bbe93d49f | ||
|
|
21cd8d31d7 | ||
|
|
d649b4df28 | ||
|
|
2bb9550343 | ||
|
|
ef0feff4a5 | ||
|
|
dfa43b4856 | ||
|
|
a24a1ca6d7 | ||
|
|
2d4b35fbc9 | ||
|
|
57e2910477 | ||
|
|
ffbaa7e4dc | ||
|
|
df42036100 | ||
|
|
ca9491d343 | ||
|
|
96ae569eaf | ||
|
|
fb295d53f1 | ||
|
|
95d07974f4 | ||
|
|
39d78064da | ||
|
|
a2bcbb90ed | ||
|
|
c31b37f570 | ||
|
|
2a1341b910 | ||
|
|
354e341d69 | ||
|
|
13946b2b76 | ||
|
|
29aa52df6a | ||
|
|
c3267a27d3 | ||
|
|
3543e6dd32 | ||
|
|
5412ba617d | ||
|
|
1f81a2cb1b | ||
|
|
5da58d42f6 | ||
|
|
299b6037c9 | ||
|
|
1ed8b9dd14 | ||
|
|
cf4912cb3d | ||
|
|
e6ec87f910 | ||
|
|
f72fb974a9 | ||
|
|
bdb25315b6 | ||
|
|
60f0739c3e | ||
|
|
0faab9277f | ||
|
|
51fce12cd7 | ||
|
|
d79290df75 | ||
|
|
0bf65bcad5 | ||
|
|
44dbf19e89 | ||
|
|
469a76f1f6 | ||
|
|
f851c272fa | ||
|
|
027a9fec61 | ||
|
|
09fde2f839 | ||
|
|
8a14e4261d | ||
|
|
6c9f1de265 | ||
|
|
3ad0179b62 | ||
|
|
81f33da499 | ||
|
|
80d70e64f5 | ||
|
|
eec4845cd0 | ||
|
|
b99a4ade1a | ||
|
|
84e25e8fd2 | ||
|
|
39e14eb95c | ||
|
|
f277d08244 | ||
|
|
d7c479fa6d | ||
|
|
44e5dc7d97 | ||
|
|
fdd54057a8 | ||
|
|
d43485141f | ||
|
|
2350561903 | ||
|
|
5674badccd | ||
|
|
a793f4414d | ||
|
|
e99957f99f | ||
|
|
ad5bbc463b | ||
|
|
81993cf2a3 | ||
|
|
5d40742ee6 | ||
|
|
1ce050d911 | ||
|
|
8e7393fc26 | ||
|
|
ad45205ddf | ||
|
|
409e9208ee | ||
|
|
d8811b499d | ||
|
|
dd1f9494f1 | ||
|
|
20f4538db4 | ||
|
|
d08c8e6edc | ||
|
|
f1667dbbe6 | ||
|
|
2bab7e2693 | ||
|
|
824497b97a | ||
|
|
0b7bece8e3 | ||
|
|
6ddf1a5786 | ||
|
|
b9827dfe02 | ||
|
|
64f30831ac | ||
|
|
bf5722cd25 | ||
|
|
ae074153c8 | ||
|
|
63d46dbdfb | ||
|
|
bdf1b23198 | ||
|
|
6f1c261f6b | ||
|
|
3233d5485f | ||
|
|
aa06400a50 | ||
|
|
30724bd7c4 | ||
|
|
0d3b389538 | ||
|
|
9f7dbe16fd | ||
|
|
432024123a | ||
|
|
261762448e | ||
|
|
7ce4175876 | ||
|
|
a57739c462 | ||
|
|
03bdbfa705 | ||
|
|
92246ce3a8 | ||
|
|
cdf74c2e41 | ||
|
|
4a72ea6666 | ||
|
|
f76d9b7908 | ||
|
|
dcfdf3a5d7 | ||
|
|
4873f61d6c | ||
|
|
1538107ae4 | ||
|
|
9b93dc5137 | ||
|
|
5ec721b978 | ||
|
|
24568c0e2f | ||
|
|
92a89ca258 | ||
|
|
ba71c53bc6 | ||
|
|
b3cfeb573a | ||
|
|
9083e99d2a | ||
|
|
70ca247c88 | ||
|
|
14fc0c735b | ||
|
|
70b3c8080e | ||
|
|
2c483c460a | ||
|
|
79405cf1d3 | ||
|
|
5c755fdd1c | ||
|
|
89285f1408 | ||
|
|
17c0bb2069 | ||
|
|
192736f6d7 | ||
|
|
ae928b9aff | ||
|
|
4eee8ce770 | ||
|
|
723eb6597a | ||
|
|
3dcfdba74b | ||
|
|
5428053663 | ||
|
|
d0b41d0b07 | ||
|
|
a82d861d2c | ||
|
|
1e184b781b | ||
|
|
54b684fdb8 | ||
|
|
c1ddb89990 | ||
|
|
2f5fa4df75 | ||
|
|
20735083b3 | ||
|
|
7ac7cb129f | ||
|
|
f8b2f738bb | ||
|
|
11b9e546a9 | ||
|
|
7e4721e4c7 | ||
|
|
3a3fd38e3a | ||
|
|
63a8611579 | ||
|
|
38a1b79174 | ||
|
|
0a679e503e | ||
|
|
e7c529f2c3 | ||
|
|
95cbcf30d7 | ||
|
|
dfc70021e3 | ||
|
|
9daecca0ad | ||
|
|
c9d8a4dc1d | ||
|
|
5fada6a497 | ||
|
|
ac9c33fb3b | ||
|
|
641b1c2e1b | ||
|
|
f031707563 | ||
|
|
7f0518f693 | ||
|
|
634d2f02b5 | ||
|
|
d7aee292d3 | ||
|
|
1987517836 | ||
|
|
203d9d651f | ||
|
|
8af5788fc1 | ||
|
|
3de81877c6 | ||
|
|
2665b34962 | ||
|
|
2ffef2be8f | ||
|
|
c2e5610455 | ||
|
|
234bb64250 | ||
|
|
d43a56614c | ||
|
|
9f10e7e356 | ||
|
|
dc6075aa3f | ||
|
|
972d6917ac | ||
|
|
ec3ba87f3a | ||
|
|
da034045cc | ||
|
|
65f001b4b7 | ||
|
|
d81473487f | ||
|
|
06a8d5a4fa | ||
|
|
3653a86ad3 | ||
|
|
c8d913fba7 | ||
|
|
b70acd0079 | ||
|
|
f167648115 | ||
|
|
b457c66bf9 | ||
|
|
ca0bd3ed32 | ||
|
|
7dacc7c268 | ||
|
|
0db2e6b62c | ||
|
|
e412bdbdbe | ||
|
|
f032bcc5f2 | ||
|
|
9ab9ac0e2e | ||
|
|
fd3adf4d11 | ||
|
|
c42911bd8d | ||
|
|
828087ee8c | ||
|
|
efcda1d37d | ||
|
|
656400b418 | ||
|
|
ca7bd49964 | ||
|
|
3527137264 | ||
|
|
e045a36559 | ||
|
|
ee8fc701a6 | ||
|
|
777fdeb462 | ||
|
|
1078b774ae | ||
|
|
62a363debc | ||
|
|
7839b931f1 | ||
|
|
cbf8c4bdb2 | ||
|
|
321cd9f2e0 | ||
|
|
09a60774d7 | ||
|
|
1ca988b177 | ||
|
|
07fd8cf6cd | ||
|
|
e7fb56447a | ||
|
|
de3e83f0a2 | ||
|
|
6c78f7b769 | ||
|
|
2c459fefce | ||
|
|
a1eb39872b | ||
|
|
724b8cc6a5 | ||
|
|
2bfd18e6cd | ||
|
|
20658f3b4a | ||
|
|
c945698eb4 | ||
|
|
5f89bd75a4 | ||
|
|
09c9b47265 | ||
|
|
ea2b653b9b | ||
|
|
9c3660e2f6 | ||
|
|
4bcacf858b | ||
|
|
7faa3359f0 | ||
|
|
71f1814477 | ||
|
|
a3913c5ca4 | ||
|
|
ae1cc65b23 | ||
|
|
9a16bf65bd | ||
|
|
c3d3dc9d92 | ||
|
|
e54eb21b71 | ||
|
|
0140f9fe56 | ||
|
|
03243df16a | ||
|
|
28dc8443a8 | ||
|
|
ec6832687b | ||
|
|
9065a90ac0 | ||
|
|
94b26e1e66 | ||
|
|
a2fc2e776a | ||
|
|
79b40179b4 | ||
|
|
60c968222f | ||
|
|
e99dc2193f | ||
|
|
cf1838f76e | ||
|
|
0db5016666 | ||
|
|
c4b37ba462 | ||
|
|
c3b908dcfc | ||
|
|
23f93044c1 | ||
|
|
32dcc28414 | ||
|
|
ddae3eaf84 | ||
|
|
a9ae17036d | ||
|
|
08e925758e | ||
|
|
213c8a6eec | ||
|
|
ca4ad836a3 | ||
|
|
56361fa2fc | ||
|
|
fedce4cd74 | ||
|
|
cac48fc4bf | ||
|
|
1cad844d2d | ||
|
|
2d5e09f930 | ||
|
|
fbda34935d | ||
|
|
c5ca7f236f | ||
|
|
9dc949b62c | ||
|
|
40394b94d7 | ||
|
|
bae446973f | ||
|
|
2805520d1b | ||
|
|
fb7ca18820 | ||
|
|
1b00d5657f | ||
|
|
cd9f35e78a | ||
|
|
f9cedab964 | ||
|
|
9ec516e5a4 | ||
|
|
550b6d90a4 | ||
|
|
5cb92b6e21 | ||
|
|
8a7b221412 | ||
|
|
d092e79ebc | ||
|
|
2ddb88a34d | ||
|
|
71761c9039 | ||
|
|
16555b93bb | ||
|
|
bd5e834b42 | ||
|
|
2eb4f093be | ||
|
|
d4bf587103 | ||
|
|
ae7bd8b644 | ||
|
|
f89cede020 | ||
|
|
59facf6f00 | ||
|
|
01d93733ae | ||
|
|
4c434129a6 | ||
|
|
b40499b4c1 | ||
|
|
66333aedb7 | ||
|
|
47071c58aa | ||
|
|
79d99688b4 | ||
|
|
edcb0631aa | ||
|
|
bc361e6469 | ||
|
|
c95ef1a5c0 | ||
|
|
0c0891f64f | ||
|
|
23828f53bc | ||
|
|
f909e129ab | ||
|
|
7d4bcc2e8b | ||
|
|
2eb4d87621 | ||
|
|
435888c1a3 | ||
|
|
e69fabcd71 | ||
|
|
1a283301cf | ||
|
|
031df70cf8 | ||
|
|
3ad36db7dd | ||
|
|
e16ea1ae05 | ||
|
|
941135a0a6 | ||
|
|
29776ecea2 | ||
|
|
d2edef5c5a | ||
|
|
da9349ea62 | ||
|
|
de0d2afc15 | ||
|
|
d8c94c2003 | ||
|
|
0e71bfe222 | ||
|
|
0ae402e624 | ||
|
|
b96dc11ddd | ||
|
|
37b539eaa5 | ||
|
|
0741708086 | ||
|
|
a30cfd8fec | ||
|
|
633cd11c5b | ||
|
|
3101c6540a | ||
|
|
d2d6be73b8 | ||
|
|
a46e2c1e6d | ||
|
|
2b0404fefc | ||
|
|
7ef9462286 | ||
|
|
c6e7a7eaee | ||
|
|
999e142370 | ||
|
|
a7ad117a83 | ||
|
|
1ba44fc117 | ||
|
|
ca5610ebf1 | ||
|
|
e5c70a1304 | ||
|
|
7dd2308534 | ||
|
|
62eee07b6e | ||
|
|
f6d9406063 | ||
|
|
34125177e8 | ||
|
|
d977b5d662 | ||
|
|
e36677b757 | ||
|
|
4d54755057 | ||
|
|
91917c98e4 | ||
|
|
2f687de477 | ||
|
|
5446062b95 | ||
|
|
c9e170dfcc | ||
|
|
e3a19469eb | ||
|
|
45f8e43be4 | ||
|
|
7ef0b05ce6 | ||
|
|
c5a0ec4e93 | ||
|
|
1c17768dc5 | ||
|
|
86e1dd4673 | ||
|
|
0ee41c3341 | ||
|
|
e1c2835173 | ||
|
|
5c6d3753ef | ||
|
|
6e881bcef2 | ||
|
|
4f9a933108 | ||
|
|
9f8585914c | ||
|
|
3ba27b9752 | ||
|
|
abae5de4d8 | ||
|
|
6952c5a3f5 | ||
|
|
0184d2c292 | ||
|
|
7a40561aaa | ||
|
|
79e6c9590a | ||
|
|
12a963cc40 | ||
|
|
f89975fd0e | ||
|
|
b4dffe5946 | ||
|
|
539b69d507 | ||
|
|
9927363f39 | ||
|
|
1a4f59cd70 | ||
|
|
25727a24a5 | ||
|
|
2fd2dc964b | ||
|
|
935b032f15 | ||
|
|
2bb58843ab | ||
|
|
00512579f3 | ||
|
|
6f09796142 | ||
|
|
697a74f37e | ||
|
|
d16b6c3838 | ||
|
|
e4461f2e61 | ||
|
|
baf1b8b02d | ||
|
|
3ce1373ae0 | ||
|
|
4002531b92 | ||
|
|
004d0f074d | ||
|
|
e87887c328 | ||
|
|
2de2c8e207 | ||
|
|
c76f19f19a | ||
|
|
a050e8c8f5 | ||
|
|
ae1fad5de7 | ||
|
|
68ca8951a8 | ||
|
|
1e4718eae1 | ||
|
|
680cf25f37 | ||
|
|
7ec6df495f | ||
|
|
aeda31fda7 | ||
|
|
5248aa3065 | ||
|
|
67767c5853 | ||
|
|
6de1aec683 | ||
|
|
221d3cdf6a | ||
|
|
1ff12c5520 | ||
|
|
41376ec2cd | ||
|
|
bf335ecaf3 | ||
|
|
424bc73f58 | ||
|
|
59e6989cbc | ||
|
|
0e6b27c9f4 | ||
|
|
0e157e4e8b | ||
|
|
805074a0f9 | ||
|
|
5216c5b232 | ||
|
|
8633162f0c | ||
|
|
c392a77b46 | ||
|
|
08c2c9bc22 | ||
|
|
f64bd590a8 | ||
|
|
131ffcf582 | ||
|
|
b17a0aef17 | ||
|
|
8a21783fa5 | ||
|
|
53152e2ef5 | ||
|
|
ca6ba92d82 | ||
|
|
04516b787a | ||
|
|
6e124a4d72 | ||
|
|
6da65c7ddc | ||
|
|
9eb332f032 | ||
|
|
30ddee65a4 | ||
|
|
63dc8d7fc4 | ||
|
|
a1a287bac7 | ||
|
|
e9b786d5e8 | ||
|
|
ad9fb411f6 | ||
|
|
1ff982983e | ||
|
|
8c6c43d762 | ||
|
|
6baf197842 | ||
|
|
42e5a50e4f | ||
|
|
b713c9a491 | ||
|
|
64ade742d9 | ||
|
|
1870c3c876 | ||
|
|
23b272aafd | ||
|
|
057db41758 | ||
|
|
839c5a8e41 | ||
|
|
36b8b6bf6c | ||
|
|
40d74fa779 | ||
|
|
249bdeb642 | ||
|
|
b4ee80d1ea | ||
|
|
96bf9d0769 | ||
|
|
7fc9c83986 | ||
|
|
5502f1cc63 | ||
|
|
b55801323c | ||
|
|
818374c8cc | ||
|
|
e80191d4cd | ||
|
|
c2a796b6ea | ||
|
|
00694f0dfd | ||
|
|
134f4c5b52 | ||
|
|
0840fc42f9 | ||
|
|
2650a647e9 | ||
|
|
3311269162 | ||
|
|
b2172d39f6 | ||
|
|
f4d39bd3c8 | ||
|
|
1cf659033b | ||
|
|
3dc5527690 | ||
|
|
81b02d04b5 | ||
|
|
95f6f9e10a | ||
|
|
9554f53519 | ||
|
|
5d7081a224 | ||
|
|
9cbdf3f31f | ||
|
|
c8bc1b560b | ||
|
|
40ae6b396f | ||
|
|
9c117ffa05 | ||
|
|
9e95f189ed | ||
|
|
221b31bcc6 | ||
|
|
8879776d64 | ||
|
|
5edd13c6bb | ||
|
|
7ca289edd0 | ||
|
|
bf434818d7 | ||
|
|
03aeb88832 | ||
|
|
bb5b33a0d3 | ||
|
|
53931017b9 | ||
|
|
e9ca17cec1 | ||
|
|
f70956964b | ||
|
|
4547a9d9d3 | ||
|
|
71accb6b0a | ||
|
|
2a95500b65 | ||
|
|
2ddad66ce7 | ||
|
|
108be24aed | ||
|
|
71efb05658 | ||
|
|
a9a36894cb | ||
|
|
bc9778e02f | ||
|
|
a83d2efe84 | ||
|
|
d3856caf81 | ||
|
|
e1f9b1c47d | ||
|
|
497c953d0e | ||
|
|
bec76bd057 | ||
|
|
54f4762e1a | ||
|
|
8bf5c18425 | ||
|
|
c36bb17475 | ||
|
|
2291fcdf11 | ||
|
|
4070c46d15 | ||
|
|
a749db8d0f | ||
|
|
10ba98c4f5 | ||
|
|
108a3efe0b | ||
|
|
ccd826f63a | ||
|
|
5e5f0aa955 | ||
|
|
f9a4c53e3f | ||
|
|
60b576568e | ||
|
|
d0e1b73205 | ||
|
|
ef2448e17e | ||
|
|
99a5cf1ad4 | ||
|
|
bc3113ae16 | ||
|
|
fb475da008 | ||
|
|
d5fc3ad646 | ||
|
|
8b7da6e318 | ||
|
|
ed208c4bcc | ||
|
|
6ab65519a0 | ||
|
|
4549bc89ec | ||
|
|
a0430f91bd | ||
|
|
624bb1db8d | ||
|
|
9f522bbe6c | ||
|
|
931da3b4f7 | ||
|
|
5a25fd54a4 | ||
|
|
9f0f4c50a4 | ||
|
|
0bf3d31578 | ||
|
|
8ca293c3e4 | ||
|
|
ba0c877ffc | ||
|
|
b8e00c2dc9 | ||
|
|
2dd48898a8 | ||
|
|
66fc807655 | ||
|
|
653eb2909b | ||
|
|
72520a2dd9 | ||
|
|
a7d4666a70 | ||
|
|
13476d5003 | ||
|
|
879cdedec3 | ||
|
|
05bd2a1f9e | ||
|
|
c49d1547c4 | ||
|
|
ad35363c2e | ||
|
|
b3f70140e5 | ||
|
|
2f21dc50b4 | ||
|
|
cbd8c2483b | ||
|
|
03475bfb15 | ||
|
|
bb9411e7dc | ||
|
|
bce4e2d022 | ||
|
|
7cfc14fef7 | ||
|
|
f220cf3173 | ||
|
|
725359b1aa | ||
|
|
937d6cb1ef | ||
|
|
118a5edebc | ||
|
|
1b4333e8d9 | ||
|
|
d66a0b50e9 | ||
|
|
1d56b9a1bb | ||
|
|
96535ee4df | ||
|
|
b55f314d7e | ||
|
|
480862d714 | ||
|
|
990a3af2a7 | ||
|
|
1f5a23143a | ||
|
|
fcfb9e9758 | ||
|
|
9beee689ce | ||
|
|
2c388c3833 | ||
|
|
1a75066616 | ||
|
|
3dfb0bcae9 | ||
|
|
9ac0b77e92 | ||
|
|
6421a32520 | ||
|
|
fdcae52740 | ||
|
|
a8752844ef | ||
|
|
e04ce5028e | ||
|
|
f2c4d80ac0 | ||
|
|
d8a6226805 | ||
|
|
fedf60a32c | ||
|
|
b3548fefc1 | ||
|
|
febd64c527 | ||
|
|
d67a5c5070 | ||
|
|
aefe7ddd8f | ||
|
|
254dea7558 | ||
|
|
b1550f7922 | ||
|
|
2d07c3e45d | ||
|
|
e5f3406cb5 | ||
|
|
185ddf1eaf | ||
|
|
2734335869 | ||
|
|
0132c4e36e | ||
|
|
7dee47183d | ||
|
|
9c4988c297 | ||
|
|
3bfbc70587 | ||
|
|
34858fce1c | ||
|
|
b47dab0ee9 | ||
|
|
6eeb033b96 | ||
|
|
0679cb8e00 | ||
|
|
3abc9ee387 | ||
|
|
2e9211cf41 | ||
|
|
40377796ed | ||
|
|
fedad7f31a | ||
|
|
d2a34e0107 | ||
|
|
769c822568 | ||
|
|
c452a621fe | ||
|
|
231ab95ef6 | ||
|
|
7c01b59ffb | ||
|
|
e79668ddf4 | ||
|
|
dc6b6fc353 | ||
|
|
34ea58cf38 | ||
|
|
199168c318 | ||
|
|
063d4bbd7d | ||
|
|
29fd9b9d64 | ||
|
|
c301baafb5 | ||
|
|
4282672201 | ||
|
|
ae705dd865 | ||
|
|
f0fa2ce171 | ||
|
|
d6a9a9927c | ||
|
|
40b4636858 | ||
|
|
97fe499b63 | ||
|
|
e0915cffda | ||
|
|
7602de85c3 | ||
|
|
0787817eb8 | ||
|
|
e030648957 | ||
|
|
b815935fd7 | ||
|
|
a5000ba311 | ||
|
|
5e2bb874c8 | ||
|
|
fcb5a7f645 | ||
|
|
b8c6520abb | ||
|
|
1d99c3d3f7 | ||
|
|
5e112b395d | ||
|
|
d1a8e7813a | ||
|
|
a0582fec12 | ||
|
|
2cd3a7b3f0 | ||
|
|
47448b11cd | ||
|
|
e63f043841 | ||
|
|
33dd0c83e3 | ||
|
|
32dad136f8 | ||
|
|
073ed52538 | ||
|
|
eef828cf3e | ||
|
|
d0b5930a3a | ||
|
|
c66ad9ccc8 | ||
|
|
9959a15c8e | ||
|
|
f0e4b4dc84 | ||
|
|
f62f808368 | ||
|
|
530a521679 | ||
|
|
50782dfb83 | ||
|
|
27a142d5c5 | ||
|
|
6d00b2e63d | ||
|
|
7be1415a3a | ||
|
|
0a30b12f7d | ||
|
|
3058cbfbb2 | ||
|
|
ae780c977b | ||
|
|
e146a67f4b | ||
|
|
162d86983a | ||
|
|
8534366a31 | ||
|
|
d1c6617dc9 | ||
|
|
6f994709b9 | ||
|
|
55d833a9c8 | ||
|
|
8ba4745097 | ||
|
|
940a0c8b10 | ||
|
|
8928b24e23 | ||
|
|
ae9a9191f3 | ||
|
|
cb553bd016 | ||
|
|
a75c61d71e | ||
|
|
c1dc16a89d | ||
|
|
66436ce4a2 | ||
|
|
62b1ff4ba1 | ||
|
|
304d136437 | ||
|
|
1b4268b9b0 | ||
|
|
a7968e6525 | ||
|
|
e59cc3d404 | ||
|
|
3a2d126877 | ||
|
|
ee3d6fcfee | ||
|
|
09d929bd56 | ||
|
|
bd55ae15f2 | ||
|
|
2ca80118bf | ||
|
|
04eb20ac35 | ||
|
|
f299391aa1 | ||
|
|
c334fc9d22 | ||
|
|
8520088376 | ||
|
|
9ada8518dc | ||
|
|
f0c09b374c | ||
|
|
c5f33baf27 | ||
|
|
2efcdd92e0 | ||
|
|
38fda98b6d | ||
|
|
f9fd195c24 | ||
|
|
01e82090b2 | ||
|
|
39602ede37 | ||
|
|
98a311ae40 | ||
|
|
5c398b3e8f | ||
|
|
83e585ee9b | ||
|
|
e386499bfa | ||
|
|
4552630bf8 | ||
|
|
2d8065a780 | ||
|
|
b94da93c74 | ||
|
|
695045f197 | ||
|
|
376733bd08 | ||
|
|
4c7c5137c5 | ||
|
|
930b9820f2 | ||
|
|
b2fa63f2c8 | ||
|
|
1390e1db39 | ||
|
|
e7768ae954 | ||
|
|
38d977e546 | ||
|
|
3e38a24f0a | ||
|
|
0784cd593a | ||
|
|
9c5d2ee563 | ||
|
|
be5f7c2e67 | ||
|
|
0d0f73fb67 | ||
|
|
680be6cfec | ||
|
|
2ab3d072b0 | ||
|
|
943ecff623 | ||
|
|
03973f5d1d | ||
|
|
c42a0fa9b6 | ||
|
|
61522ed31d | ||
|
|
29a527426a | ||
|
|
62ac0ff21e | ||
|
|
a41c7caa18 | ||
|
|
b3ca31bce7 | ||
|
|
b02f6a1dae | ||
|
|
d35609f33a | ||
|
|
8c19ab8f9f | ||
|
|
30ae198b89 | ||
|
|
bddeab3ac1 | ||
|
|
591349ee74 | ||
|
|
9081a25e64 | ||
|
|
1beadfc6e7 | ||
|
|
f4af532c5a | ||
|
|
76eb1a9d78 | ||
|
|
14a2790dcb | ||
|
|
46f54db197 | ||
|
|
4ffd7587a9 | ||
|
|
c48c62c7a8 | ||
|
|
9e7fd20ade | ||
|
|
efa1d381ba | ||
|
|
740fa058aa | ||
|
|
37f56e1efd | ||
|
|
6294be371a | ||
|
|
bee7549a1e | ||
|
|
db14dbacc9 | ||
|
|
27058e6297 | ||
|
|
b41175e0e2 | ||
|
|
eeea3251ad | ||
|
|
34ffff3947 | ||
|
|
65ed3818ec | ||
|
|
c8417df6f1 | ||
|
|
dc3be7ecf7 | ||
|
|
cf3c0b593b | ||
|
|
34f64148e8 | ||
|
|
fc5dad1983 | ||
|
|
c3c40548b9 | ||
|
|
0e2e932102 | ||
|
|
6930c4e23b | ||
|
|
01b9f2dfcf | ||
|
|
0cc6f66a26 | ||
|
|
d384f55dd1 | ||
|
|
1893368aa5 | ||
|
|
a520063265 | ||
|
|
1e171a72a0 | ||
|
|
5b1a0d93b9 | ||
|
|
7e04662a9c | ||
|
|
a804549781 | ||
|
|
2a60f1cc6e | ||
|
|
2ddff785e5 | ||
|
|
38882efb5c | ||
|
|
6f7786b068 | ||
|
|
0819141f03 | ||
|
|
329ef5049f | ||
|
|
34bb8c65d6 | ||
|
|
575ccae6f9 | ||
|
|
486be87e33 | ||
|
|
c0350861ef | ||
|
|
a7ec1805e3 | ||
|
|
4b06bc552f | ||
|
|
76ee7b7eea | ||
|
|
04b1e7e34f | ||
|
|
17dbb156e1 | ||
|
|
135117c637 | ||
|
|
d3348f7855 | ||
|
|
39bbcb66c8 | ||
|
|
d45e8e4d20 | ||
|
|
d65052c1ac | ||
|
|
2fbc42753f | ||
|
|
4195865965 | ||
|
|
de3f6fbeba | ||
|
|
6a377120bd | ||
|
|
502226b0a6 | ||
|
|
78206b48f4 | ||
|
|
dc3cec06ca | ||
|
|
5a7688e099 | ||
|
|
c721f01c76 | ||
|
|
463806822c | ||
|
|
b74c2f001d | ||
|
|
9fc7a8b626 | ||
|
|
3ffd92a6c3 | ||
|
|
29b02e5329 | ||
|
|
97584e046f | ||
|
|
09d2fce85d | ||
|
|
fc3060cb29 | ||
|
|
9804a67165 | ||
|
|
21eddefa41 | ||
|
|
9b62e7eedb | ||
|
|
bf30cfd8a4 | ||
|
|
139ffae367 | ||
|
|
51a48cc264 | ||
|
|
abbca12565 | ||
|
|
9e9d96a2ec | ||
|
|
615c9f1cbe | ||
|
|
7d75d0cfbd | ||
|
|
5468de2c6a | ||
|
|
6d8aabab23 | ||
|
|
e74359fcfe | ||
|
|
53c842c614 | ||
|
|
23ececeb34 | ||
|
|
521c9eb566 | ||
|
|
35877b1382 | ||
|
|
c531287170 | ||
|
|
8e79a81b88 | ||
|
|
b95ceb301f | ||
|
|
76a94495c4 | ||
|
|
b6b2299b4e | ||
|
|
34ddea87d3 | ||
|
|
3d318542cb | ||
|
|
4a8c3cdc61 | ||
|
|
c0b6f2d95f | ||
|
|
9ca7fccab8 | ||
|
|
d91fcfc866 | ||
|
|
29b53f3b9d | ||
|
|
30987095c7 | ||
|
|
43b93de570 | ||
|
|
5b310cf315 | ||
|
|
a1c2a57ea6 | ||
|
|
34bf8f1133 | ||
|
|
c708ec577e | ||
|
|
c185685f2d | ||
|
|
daee5b3477 | ||
|
|
5d0346ee30 | ||
|
|
85ad5355cf | ||
|
|
4c8b84633a | ||
|
|
c0dd4d748d | ||
|
|
c94f25570b | ||
|
|
ffa5e08832 | ||
|
|
63243c8e04 | ||
|
|
21c4ec2de0 | ||
|
|
a8d87af418 | ||
|
|
7b084a065e | ||
|
|
47f6b202e5 | ||
|
|
f588d8379b | ||
|
|
58827e130b | ||
|
|
d702334604 | ||
|
|
b04aa799e3 | ||
|
|
e113f6cb9d | ||
|
|
bc13b7eb72 | ||
|
|
f50b395da6 | ||
|
|
a0e8e40f1c | ||
|
|
cb6055c1b8 | ||
|
|
25424c16e4 | ||
|
|
99dcdee67a | ||
|
|
99928f1aea | ||
|
|
1740ae2104 | ||
|
|
d8372f8433 | ||
|
|
2a15d2c421 | ||
|
|
bacf19688f | ||
|
|
31fbdcf6c5 | ||
|
|
c8818cb7b3 | ||
|
|
eb20789821 | ||
|
|
c90862217e | ||
|
|
df87d6feeb | ||
|
|
6c808abcfc | ||
|
|
f1822bdfab | ||
|
|
c3428acd80 | ||
|
|
d619192b22 | ||
|
|
5bdc713afe | ||
|
|
46eff1c937 | ||
|
|
76e1ea1c02 | ||
|
|
755076a8e5 | ||
|
|
b49f7b8b34 | ||
|
|
c4dd8885e4 | ||
|
|
4c82952b58 | ||
|
|
0da69cb9c7 | ||
|
|
36e244060c | ||
|
|
b13a9f57af | ||
|
|
0aa67ad7f9 | ||
|
|
195a3a6827 | ||
|
|
38ecff1220 | ||
|
|
67e64287af | ||
|
|
b022703b0b | ||
|
|
e8069c0d93 | ||
|
|
7a1c6b64c2 | ||
|
|
8250cb1e8d | ||
|
|
ffe2c4d42b | ||
|
|
f06c970628 | ||
|
|
99bce46b32 | ||
|
|
f711913778 | ||
|
|
a8ac231667 | ||
|
|
f7c8791a6d | ||
|
|
7acc775c91 | ||
|
|
c2e21e837f | ||
|
|
755d0f54f7 | ||
|
|
f62d66ff25 | ||
|
|
406d19f930 | ||
|
|
42b13614eb | ||
|
|
c942bd67fe | ||
|
|
b8dc3d74b6 | ||
|
|
38fb263737 | ||
|
|
b55beed2f9 | ||
|
|
e9278c03c1 | ||
|
|
ae1fe83784 | ||
|
|
717a547c40 | ||
|
|
ec491e87ab | ||
|
|
42e30d0835 | ||
|
|
5b19418e48 | ||
|
|
1bc9a7373f | ||
|
|
23e59b5dcc | ||
|
|
c6b459cf96 | ||
|
|
33254b4cac | ||
|
|
44da40d18d | ||
|
|
c742f25801 | ||
|
|
b153687bf1 | ||
|
|
3318f093da | ||
|
|
d98d56c3b5 | ||
|
|
c3f5f6c7ad | ||
|
|
5f21edcc53 | ||
|
|
cd0731cbb0 | ||
|
|
f392ddec2f | ||
|
|
df71168ab7 | ||
|
|
e93b26bf54 | ||
|
|
a73d4a8cbd | ||
|
|
20ee57801c | ||
|
|
6a270d7f02 | ||
|
|
68639637c9 | ||
|
|
0d1eabbc33 | ||
|
|
dce249f7a9 | ||
|
|
1723d4fbd8 | ||
|
|
c4b09f1a4f | ||
|
|
788c973c13 | ||
|
|
465c5c8cfb | ||
|
|
ee28ba5be1 | ||
|
|
9a22e9cf39 | ||
|
|
2513f605b6 | ||
|
|
3b1ffb2028 | ||
|
|
47c6624e12 | ||
|
|
b6f1b064d3 | ||
|
|
17d89467df | ||
|
|
1282214d48 | ||
|
|
f4bc6ee615 | ||
|
|
f8b8d8c540 | ||
|
|
57ff667438 | ||
|
|
abe3039926 | ||
|
|
82a1117e91 | ||
|
|
b6ff3a4d99 | ||
|
|
f4046efcb2 | ||
|
|
fc1d3831cf | ||
|
|
867deda247 | ||
|
|
f8149face5 | ||
|
|
7e2aecd8bb | ||
|
|
105d121199 | ||
|
|
37d662f2f5 | ||
|
|
5b50454b4d | ||
|
|
b5e4c08fc5 | ||
|
|
db39cd8b7c | ||
|
|
a35f741a35 | ||
|
|
fc02e018cb | ||
|
|
b14a530efb | ||
|
|
f70bc571bd | ||
|
|
8cc64176b4 | ||
|
|
7450ac1a31 | ||
|
|
c72e5e3b66 | ||
|
|
5e811819e2 | ||
|
|
f1c0034a18 | ||
|
|
7342cb81a3 | ||
|
|
b40e858556 | ||
|
|
95a4ed7d6a | ||
|
|
2c2d4b6b95 | ||
|
|
4490eae4fe | ||
|
|
9d59cb0135 | ||
|
|
7d348fe69f | ||
|
|
fa8fb9e73f | ||
|
|
f6093872ec | ||
|
|
e8030e29d9 | ||
|
|
df8bb0596a | ||
|
|
0003e0b8a5 | ||
|
|
e42703d557 | ||
|
|
4636e56395 | ||
|
|
27ebeffad4 | ||
|
|
07110cee17 | ||
|
|
afa1f1416b | ||
|
|
f8dfcab0ca | ||
|
|
e14fd920d6 | ||
|
|
8c10fdae5b | ||
|
|
eee027d9ff | ||
|
|
1b1fb5d26a | ||
|
|
c36e0805d8 | ||
|
|
5c56041185 | ||
|
|
5aefe0b74f | ||
|
|
20e0359efd | ||
|
|
9c790e5a90 | ||
|
|
9c79b5be77 | ||
|
|
2d9a4f4e42 | ||
|
|
565602538c | ||
|
|
78972725ae | ||
|
|
5ab90f7791 | ||
|
|
b90d98fc2b | ||
|
|
eca3ae393b | ||
|
|
2bd69495d2 | ||
|
|
bfd3da43ac | ||
|
|
32a9eaf3b6 | ||
|
|
91cea1f28a | ||
|
|
220ed35f58 | ||
|
|
b1cf5d4e44 | ||
|
|
fe330ec1bb | ||
|
|
2968bf8241 | ||
|
|
a40babbf0d | ||
|
|
7822257e1c | ||
|
|
b37165c62b | ||
|
|
5e9d267959 | ||
|
|
1a1e6b6810 | ||
|
|
88140002e7 | ||
|
|
efc203d958 | ||
|
|
42d4cdcc39 | ||
|
|
fd433b3eb6 | ||
|
|
7483adb8ad | ||
|
|
45fd462f80 | ||
|
|
807003adf7 | ||
|
|
1b0a17c7db | ||
|
|
60b145833c | ||
|
|
39458b2ba8 | ||
|
|
5de38b3632 | ||
|
|
6ced3426cf | ||
|
|
43460c9d19 | ||
|
|
ca17fb01bc | ||
|
|
a6f65aa9c5 | ||
|
|
ae9a7727d6 | ||
|
|
3ac5d5257b | ||
|
|
668d7c73ed | ||
|
|
c95f708c91 | ||
|
|
06e214e567 | ||
|
|
18f8cafee0 | ||
|
|
6bd6afac05 | ||
|
|
481ecee9e8 |
158
.debianinstall/README.md
Normal file
158
.debianinstall/README.md
Normal file
@@ -0,0 +1,158 @@
|
||||
|
||||
# How to use
|
||||
|
||||
## Disclaimers
|
||||
|
||||
- **This script does work with a fresh install of Debian 12 only**.
|
||||
- Do not use if you have already installed and configured a webserver or sql server that was not installed by this script.
|
||||
|
||||
### Keep it Simple and Stupid
|
||||
|
||||
The script keeps everything as simple as possible (KISS):
|
||||
|
||||
- Apache as webserver (there is no choice to use another webserver like nginx)
|
||||
- default PHP version of Debian
|
||||
- one single Hubzilla intance only
|
||||
- re-running the script does no harm
|
||||
|
||||
### When to use other Scripts
|
||||
|
||||
Use the scripts under [homeinstall](https://framagit.org/hubzilla/core/-/tree/master/.homeinstall)
|
||||
if you look for more choices. The main differences are:
|
||||
|
||||
- Apache or nginx as webserver
|
||||
- install multiple instances (domains) that run side by side on the server
|
||||
- adds apache vhosts (instead of using the standard doc root /var/www/html)
|
||||
- install PHP from https://packages.sury.org/php/ (instead of using the Debian repository)
|
||||
- graphical installer whiptail
|
||||
- The script stops (fails) if it finds results of a previous installation. (The [debian-setup.sh](https://framagit.org/ojrandom/core/-/tree/dev/.debianinstall) will just jump over it.)
|
||||
- If something fails the script tries to clean up everything that was installed up to the point of failure. (That might cause trouble if certbot registered a certificate already.)
|
||||
- The script under [homeinstall](https://framagit.org/hubzilla/core/-/tree/master/.homeinstall) seems to be an older version of the scripts used for Streams
|
||||
+ [autoinstall](https://codeberg.org/streams/streams/src/branch/dev/contrib/autoinstall)
|
||||
+ [easyinstall](https://codeberg.org/streams/streams/src/branch/dev/contrib/easyinstall)
|
||||
|
||||
## Preconditions
|
||||
|
||||
Hardware
|
||||
|
||||
+ internet connection and router at home
|
||||
+ computer connected to your router (a Raspberry 3 will do for very small Hubs)
|
||||
|
||||
Software
|
||||
|
||||
+ fresh installation of Debian 12 (bookworm)
|
||||
+ router with open ports 80 and 443 for your web server
|
||||
|
||||
You can of course run the script on a VPS or any distant server as long as the above sotfware requirements are satisfied.
|
||||
|
||||
## How to run the script
|
||||
|
||||
+ Register your own domain (for example at selfHOST) or a free subdomain (for example at freeDNS)
|
||||
+ Log on to your fresh Debian
|
||||
- apt-get install git
|
||||
- mkdir -p /var/www
|
||||
- cd /var/www
|
||||
- git clone https://framagit.org/hubzilla/core.git html
|
||||
- cd html/.debianinstall
|
||||
- cp config.txt.template config.txt
|
||||
- nano config.txt
|
||||
- read the comments carefully
|
||||
- enter your values: db pass, domain
|
||||
- (optionally) Enter your values for dyn DNS
|
||||
- ./debian-setup.sh as root
|
||||
- ... wait, wait, wait until the script is finished
|
||||
+ Open your domain with a browser and step throught the initial configuration of your hubzilla instance.
|
||||
- default database name = hubzilla
|
||||
- default dababase user = hubzilla
|
||||
|
||||
## Optional - Switch verification of email on/off
|
||||
|
||||
Do this just before you register the first user without email verification.
|
||||
|
||||
In a terminal
|
||||
|
||||
su -
|
||||
cd /var/www/html
|
||||
|
||||
Check the current setting
|
||||
|
||||
util/config system verify_email
|
||||
|
||||
Switch the verification off
|
||||
|
||||
util/config system verify_email 0
|
||||
|
||||
## What the script will do for you...
|
||||
|
||||
+ install everything required by your hubzilla instance, basically a web server (Apache), PHP, a database (MySQL), certbot,...
|
||||
+ create a database
|
||||
+ run certbot to have everything for a secure connection (httpS)
|
||||
+ create a script for daily maintenance
|
||||
- renew certfificate (letsencrypt)
|
||||
- update of your hubzilla instance for core and addons (git)
|
||||
- update of Debian
|
||||
- restart
|
||||
+ create cron jobs for
|
||||
- DynDNS (selfHOST.de or freedns.afraid.org) every 5 minutes
|
||||
- Master.php for your hubzilla instance every 10 minutes
|
||||
- daily maintenance script every day at 05:30
|
||||
|
||||
The script is known to work without adjustments with
|
||||
|
||||
+ Hardware
|
||||
- standard PC with Debian 12 (bookworm)
|
||||
- Raspberry 4 with Raspbian, Debian 12 (TODO: needs confirmation after swich to Debian12)
|
||||
- for tesing purposes: under localhost inside a virtual machine, [KVM](https://wiki.debian.org/KVM)
|
||||
+ DynDNS
|
||||
- selfHOST.de
|
||||
- freedns.afraid.org
|
||||
|
||||
# Step-by-Step - some Details
|
||||
|
||||
## Preparations
|
||||
|
||||
## Configure your Router
|
||||
|
||||
Your webserver has to be visible in the internet.
|
||||
|
||||
Open the ports 80 and 443 on your router for your Debian. Make sure your web server is marked as "exposed host".
|
||||
|
||||
## Preparations Dynamic IP Address
|
||||
|
||||
Follow the instructions in .debianinstall/config.txt.
|
||||
|
||||
In short...
|
||||
|
||||
Your Hubzilla server must be reachable by a domain that you can type in your browser
|
||||
|
||||
cooldomain.org
|
||||
|
||||
You can use subdomains as well
|
||||
|
||||
my.cooldomain.org
|
||||
|
||||
There are two ways to get a domain...
|
||||
|
||||
### Method 1: Buy a Domain
|
||||
|
||||
...for example buy at selfHOST.de
|
||||
|
||||
The cost is 1,50 € per month (2019).
|
||||
|
||||
### Method 2: Register a free subdomain
|
||||
|
||||
...for example register at freedns.afraid.org
|
||||
|
||||
## Note on Rasperry
|
||||
|
||||
It is recommended to run the Raspi without graphical frontend (X-Server). Use...
|
||||
|
||||
sudo raspi-config
|
||||
|
||||
to boot the Rapsi to the client console.
|
||||
|
||||
DO NOT FORGET TO CHANGE THE DEFAULT PASSWORD FOR USER PI!
|
||||
|
||||
## Reminder for Different Web Wervers
|
||||
|
||||
For those of you who feel adventurous enough to use a different web server (i.e. Lighttpd...), don't forget that this script will install Apache or Nginx and that you can only have one web server listening to ports 80 & 443. Also, don't forget to tweak your daily shell script in /var/www/ accordingly.
|
||||
111
.debianinstall/config.txt.template
Normal file
111
.debianinstall/config.txt.template
Normal file
@@ -0,0 +1,111 @@
|
||||
|
||||
###############################################
|
||||
### MANDATORY - database password #############
|
||||
#
|
||||
# Please give your database password
|
||||
# It is better to not use blanks inside the password.
|
||||
# Example: db_pass=pass_word_with_no_blanks_in_it
|
||||
db_pass=
|
||||
|
||||
###############################################
|
||||
### MANDATORY - let's encrypt #################
|
||||
#
|
||||
# Zot requires encrypted communication via secure HTTP (HTTPS).
|
||||
# This script automates installation of an SSL certificate from
|
||||
# Let's Encrypt (https://letsencrypt.org)
|
||||
#
|
||||
# Please give the domain name of your hub/instance
|
||||
#
|
||||
# Example: my.cooldomain.org
|
||||
# Example: cooldomain.org
|
||||
#
|
||||
# You might use "localhost" for a LOCAL TEST installation.
|
||||
# This is usefull if you want to debug the server inside a VM.
|
||||
#
|
||||
# Example: localhost
|
||||
#
|
||||
# Email is optional if you use "localhost".
|
||||
#
|
||||
#
|
||||
le_domain=
|
||||
le_email=
|
||||
|
||||
|
||||
###############################################
|
||||
### OPTIONAL - selfHOST - dynamic IP address ##
|
||||
#
|
||||
# 1. Register a domain at selfhost.de
|
||||
# - choose offer "DOMAIN dynamisch" 1,50€/mon at 04/2019
|
||||
# 2. Get your configuration for dynamic IP update
|
||||
# - Log in at selfhost.de
|
||||
# - go to "DynDNS Accounte"
|
||||
# - klick "Details" of your (freshly) registered domain
|
||||
# - You will find the configuration there
|
||||
# - Benutzername (user name) > use this for "selfhost_user="
|
||||
# - Passwort (pass word) > use this for "selfhost_pass="
|
||||
#
|
||||
#
|
||||
selfhost_user=
|
||||
selfhost_pass=
|
||||
|
||||
###############################################
|
||||
### OPTIONAL - FreeDNS - dynamic IP address ###
|
||||
#
|
||||
# Please give the alpha-numeric-key of freedns
|
||||
#
|
||||
# Get a free subdomain from freedns and use it for your dynamic ip address
|
||||
# Documentation under http://www.techjawab.com/2013/06/setup-dynamic-dns-dyndns-for-free-on.html
|
||||
#
|
||||
# - Register for a Free domain at http://freedns.afraid.org/signup/
|
||||
# - WATCH THIS: Make sure you choose a domain with as less subdomains as
|
||||
# possible. Why? Let's encrpyt issues a limited count of certificates each
|
||||
# day. Possible other users of this domain will try to issue a certificate
|
||||
# at the same day.
|
||||
# - Logon to FreeDNS (where you just registered)
|
||||
# - Goto http://freedns.afraid.org/dynamic/
|
||||
# - Right click on "Direct Link" and copy the URL and paste it somewhere.
|
||||
# - You should notice a large and unique alpha-numeric key in the URL
|
||||
#
|
||||
# http://freedns.afraid.org/dynamic/update.php?alpha-numeric-key
|
||||
#
|
||||
# Provided your url from freedns is
|
||||
#
|
||||
# http://freedns.afraid.org/dynamic/update.php?U1Z6aGt2R0NzMFNPNWRjbWxxZGpsd093OjE1Mzg5NDE5
|
||||
#
|
||||
# Then you have to provide
|
||||
#
|
||||
# freedns_key=U1Z6aGt2R0NzMFNPNWRjbWxxZGpsd093OjE1Mzg5NDE5
|
||||
#
|
||||
#
|
||||
freedns_key=
|
||||
|
||||
|
||||
###############################################
|
||||
### OPTIONAL - do not mess with things below ##
|
||||
# (...if you are not certain)
|
||||
#
|
||||
# Usually you are done here
|
||||
# Everything below is OPTIONAL
|
||||
#
|
||||
###############################################
|
||||
#
|
||||
# Database for your hub/instance
|
||||
# If left empty, both your database and user will be named after your zot instance (hubzilla, zap or misty)
|
||||
# Use custom name, at least fo the database, if you plan to run more than one hub/instance on the same server
|
||||
#
|
||||
zotserver_db_name=
|
||||
zotserver_db_user=
|
||||
zotserver_db_pass=$db_pass
|
||||
#
|
||||
#
|
||||
# Password for package mysql-server
|
||||
# Example: mysqlpass=aberhallo
|
||||
# Example: mysqlpass="aber hallo has blanks in it"
|
||||
#
|
||||
mysqlpass=$db_pass
|
||||
|
||||
# Password for package phpmyadmin
|
||||
# Example: phpmyadminpass=aberhallo
|
||||
# Example: phpmyadminpass="aber hallo has blanks in it"
|
||||
phpmyadminpass=$db_pass
|
||||
|
||||
527
.debianinstall/debian-setup.sh
Normal file
527
.debianinstall/debian-setup.sh
Normal file
@@ -0,0 +1,527 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# How to use
|
||||
# ----------
|
||||
#
|
||||
# This file automates the installation of hubzilla: https://framagit.org/hubzilla/core
|
||||
# under Debian Linux "bookworm"
|
||||
#
|
||||
# 1) Copy the file "config.txt.template" to "config.txt"
|
||||
# Follow the instuctions there
|
||||
#
|
||||
# 2) Switch to user "root" by typing "su -"
|
||||
#
|
||||
# 3) Run with "./debian-setup.sh"
|
||||
# If this fails check if you can execute the script.
|
||||
# - To make it executable type "chmod +x debian-setup.sh"
|
||||
# - or run "bash debian-setup.sh"
|
||||
#
|
||||
#
|
||||
# What does this script do basically?
|
||||
# -----------------------------------
|
||||
#
|
||||
# This file automates the installation of a Hubzilla instance under Debian Linux
|
||||
# - install
|
||||
# * apache webserver,
|
||||
# * php,
|
||||
# * mariadb,
|
||||
# * adminer,
|
||||
# * addons
|
||||
# - configure cron
|
||||
# * "Master.php" for regular background processes of your hubzilla instance
|
||||
# * "apt-get update" and "apt-get dist-upgrade" and "apt-get autoremove" to keep linux up-to-date
|
||||
# * run command to keep the IP up-to-date > DynDNS provided by selfHOST.de or freedns.afraid.org
|
||||
# - run letsencrypt to create, register and use a certifacte for https
|
||||
#
|
||||
#
|
||||
# Credits
|
||||
# -------
|
||||
#
|
||||
# The script is derived from the easyinstall script of the Streams repository, which is based on
|
||||
# - Tom Wiedenhöfts (OJ Random) script homeinstall (for Hubzilla, ZAP,...) that was based on
|
||||
# - Thomas Willinghams script "debian-setup.sh" which he used to install the red#matrix.
|
||||
|
||||
function check_sanity {
|
||||
# Do some sanity checking.
|
||||
print_info "Sanity check..."
|
||||
if [ $(/usr/bin/id -u) != "0" ]
|
||||
then
|
||||
die 'Must be run by root user'
|
||||
fi
|
||||
|
||||
if [ -f /etc/lsb-release ]
|
||||
then
|
||||
die "Distribution is not supported"
|
||||
fi
|
||||
if [ ! -f /etc/debian_version ]
|
||||
then
|
||||
die "Debian is supported only"
|
||||
fi
|
||||
if ! grep -q 'Linux 12' /etc/issue
|
||||
then
|
||||
die "Linux 12 (bookworm) is supported only"x
|
||||
fi
|
||||
}
|
||||
|
||||
function check_config {
|
||||
print_info "config check..."
|
||||
# Check for required parameters
|
||||
if [ -z "$db_pass" ]
|
||||
then
|
||||
die "db_pass not set in $configfile"
|
||||
fi
|
||||
if [ -z "$le_domain" ]
|
||||
then
|
||||
die "le_domain not set in $configfile"
|
||||
fi
|
||||
}
|
||||
|
||||
function die {
|
||||
echo "ERROR: $1" > /dev/null 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
function update_upgrade {
|
||||
print_info "updated and upgrade..."
|
||||
# Run through the apt-get update/upgrade first. This should be done before
|
||||
# we try to install any package
|
||||
apt-get -q -y update && apt-get -q -y dist-upgrade && apt-get -q -y autoremove
|
||||
print_info "updated and upgraded linux"
|
||||
}
|
||||
|
||||
function nocheck_install {
|
||||
# export DEBIAN_FRONTEND=noninteractive ... answers from the package configuration database
|
||||
# - q ... without progress information
|
||||
# - y ... answer interactive questions with "yes"
|
||||
# DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -q -y install $2
|
||||
# DEBIAN_FRONTEND=noninteractive apt-get --install-suggests -q -y install $1
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -q -y install $1
|
||||
print_info "installed $1"
|
||||
}
|
||||
|
||||
|
||||
function print_info {
|
||||
echo -n -e '\e[1;34m'
|
||||
echo -n $1
|
||||
echo -e '\e[0m'
|
||||
}
|
||||
|
||||
function print_warn {
|
||||
echo -n -e '\e[1;31m'
|
||||
echo -n $1
|
||||
echo -e '\e[0m'
|
||||
}
|
||||
|
||||
function stop_zotserver {
|
||||
print_info "stopping apache..."
|
||||
systemctl stop apache2
|
||||
print_info "stopping mysql db..."
|
||||
systemctl stop mariadb
|
||||
}
|
||||
|
||||
function install_apache {
|
||||
print_info "installing apache..."
|
||||
nocheck_install "apache2 apache2-utils"
|
||||
a2enmod rewrite
|
||||
systemctl restart apache2
|
||||
}
|
||||
|
||||
function install_imagemagick {
|
||||
print_info "installing imagemagick..."
|
||||
nocheck_install "imagemagick"
|
||||
}
|
||||
|
||||
function install_curl {
|
||||
print_info "installing curl..."
|
||||
nocheck_install "curl"
|
||||
}
|
||||
|
||||
function install_wget {
|
||||
print_info "installing wget..."
|
||||
nocheck_install "wget"
|
||||
}
|
||||
|
||||
function install_sendmail {
|
||||
print_info "installing sendmail..."
|
||||
nocheck_install "sendmail sendmail-bin"
|
||||
}
|
||||
|
||||
function install_php {
|
||||
# openssl and mbstring are included in libapache2-mod-php
|
||||
print_info "installing php..."
|
||||
nocheck_install "libapache2-mod-php php php-pear php-curl php-gd php-mbstring php-xml php-zip"
|
||||
phpversion=$(php -v|grep --only-matching --perl-regexp "(PHP )\d+\.\\d+\.\\d+"|cut -c 5-7)
|
||||
sed -i "s/^upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php/$phpversion/apache2/php.ini
|
||||
sed -i "s/^post_max_size =.*/post_max_size = 100M/g" /etc/php/$phpversion/apache2/php.ini
|
||||
}
|
||||
|
||||
function install_composer {
|
||||
print_info "We check if Composer is already downloaded"
|
||||
if [ ! -f /usr/local/bin/composer ]
|
||||
then
|
||||
EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')"
|
||||
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
|
||||
ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"
|
||||
if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]
|
||||
then
|
||||
>&2 echo 'ERROR: Invalid installer checksum'
|
||||
rm composer-setup.php
|
||||
die 'ERROR: Invalid installer checksum'
|
||||
fi
|
||||
php composer-setup.php --quiet
|
||||
RESULT=$?
|
||||
rm composer-setup.php
|
||||
# exit $RESULT
|
||||
# We install Composer globally
|
||||
mv composer.phar /usr/local/bin/composer
|
||||
print_info "Composer was successfully downloaded."
|
||||
else
|
||||
print_info "Composer is already downloaded on this system."
|
||||
fi
|
||||
cd $install_path
|
||||
export COMPOSER_ALLOW_SUPERUSER=1;
|
||||
/usr/local/bin/composer install --no-dev
|
||||
/usr/local/bin/composer show
|
||||
export COMPOSER_ALLOW_SUPERUSER=0;
|
||||
}
|
||||
|
||||
|
||||
function install_mysql {
|
||||
print_info "installing mysql..."
|
||||
if [ -z "$mysqlpass" ]
|
||||
then
|
||||
die "mysqlpass not set in $configfile"
|
||||
fi
|
||||
if type mysql ; then
|
||||
echo "Yes, mysql is installed"
|
||||
else
|
||||
echo "mariadb-server"
|
||||
nocheck_install "mariadb-server"
|
||||
systemctl status mariadb
|
||||
systemctl start mariadb
|
||||
mysql --user=root <<_EOF_
|
||||
UPDATE mysql.user SET Password=PASSWORD('${mysqlpass}') WHERE User='root';
|
||||
DELETE FROM mysql.user WHERE User='';
|
||||
DROP DATABASE IF EXISTS test;
|
||||
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
|
||||
FLUSH PRIVILEGES;
|
||||
_EOF_
|
||||
fi
|
||||
}
|
||||
|
||||
function install_adminer {
|
||||
print_info "installing adminer..."
|
||||
nocheck_install "adminer"
|
||||
if [ ! -f /etc/adminer/adminer.conf ]
|
||||
then
|
||||
echo "Alias /adminer /usr/share/adminer/adminer" > /etc/adminer/adminer.conf
|
||||
ln -s /etc/adminer/adminer.conf /etc/apache2/conf-available/adminer.conf
|
||||
else
|
||||
print_info "file /etc/adminer/adminer.conf exists already"
|
||||
fi
|
||||
|
||||
a2enmod rewrite
|
||||
|
||||
if [ ! -f /etc/apache2/apache2.conf ]
|
||||
then
|
||||
die "could not find file /etc/apache2/apache2.conf"
|
||||
fi
|
||||
sed -i \
|
||||
"s/AllowOverride None/AllowOverride all/" \
|
||||
/etc/apache2/apache2.conf
|
||||
|
||||
a2enconf adminer
|
||||
systemctl restart mariadb
|
||||
systemctl reload apache2
|
||||
}
|
||||
|
||||
function create_zotserver_db {
|
||||
print_info "creating zotserver database..."
|
||||
if [ -z "$db_name" ]
|
||||
then
|
||||
die "db_name not set in $configfile"
|
||||
fi
|
||||
if [ -z "$db_user" ]
|
||||
then
|
||||
die "db_user not set in $configfile"
|
||||
fi
|
||||
if [ -z "$db_pass" ]
|
||||
then
|
||||
die "db_pass not set in $configfile"
|
||||
fi
|
||||
systemctl restart mariadb
|
||||
# Make sure we don't write over an already existing database if we install more than one Zot hub/instance
|
||||
if [ -z $(mysql -h localhost -u root -p$mysqlpass -e "SHOW DATABASES;" | grep $db_name) ]
|
||||
then
|
||||
Q1="CREATE DATABASE IF NOT EXISTS $db_name;"
|
||||
Q2="GRANT USAGE ON *.* TO $db_user@localhost IDENTIFIED BY '$db_pass';"
|
||||
Q3="GRANT ALL PRIVILEGES ON $name.* to $db_user@localhost identified by '$db_pass';"
|
||||
Q4="FLUSH PRIVILEGES;"
|
||||
SQL="${Q1}${Q2}${Q3}${Q4}"
|
||||
mysql -uroot -p$mysqlpass -e "$SQL"
|
||||
else
|
||||
echo "database $db_name does exist already"
|
||||
fi
|
||||
}
|
||||
|
||||
function run_freedns {
|
||||
print_info "run freedns (dynamic IP)..."
|
||||
if [ -z "$freedns_key" ]
|
||||
then
|
||||
print_info "freedns was not started because 'freedns_key' is empty in $configfile"
|
||||
else
|
||||
if [ -n "$selfhost_user" ]
|
||||
then
|
||||
die "You can not use freeDNS AND selfHOST for dynamic IP updates ('freedns_key' AND 'selfhost_user' set in $configfile)"
|
||||
fi
|
||||
wget --no-check-certificate -O - http://freedns.afraid.org/dynamic/update.php?$freedns_key
|
||||
fi
|
||||
}
|
||||
|
||||
function install_run_selfhost {
|
||||
print_info "install and start selfhost (dynamic IP)..."
|
||||
if [ -z "$selfhost_user" ]
|
||||
then
|
||||
print_info "selfHOST was not started because 'selfhost_user' is empty in $configfile"
|
||||
else
|
||||
if [ -n "$freedns_key" ]
|
||||
then
|
||||
die "You can not use freeDNS AND selfHOST for dynamic IP updates ('freedns_key' AND 'selfhost_user' set in $configfile)"
|
||||
fi
|
||||
if [ -z "$selfhost_pass" ]
|
||||
then
|
||||
die "selfHOST was not started because 'selfhost_pass' is empty in $configfile"
|
||||
fi
|
||||
if [ ! -d $selfhostdir ]
|
||||
then
|
||||
mkdir $selfhostdir
|
||||
fi
|
||||
# the old way
|
||||
# https://carol.selfhost.de/update?username=123456&password=supersafe
|
||||
#
|
||||
# the prefered way
|
||||
wget --output-document=$selfhostdir/$selfhostscript http://jonaspasche.de/selfhost-updater
|
||||
echo "router" > $selfhostdir/device
|
||||
echo "$selfhost_user" > $selfhostdir/user
|
||||
echo "$selfhost_pass" > $selfhostdir/pass
|
||||
bash $selfhostdir/$selfhostscript update
|
||||
fi
|
||||
}
|
||||
|
||||
function ping_domain {
|
||||
print_info "ping domain $domain..."
|
||||
# Is the domain resolved? Try to ping 6 times à 10 seconds
|
||||
COUNTER=0
|
||||
for i in {1..6}
|
||||
do
|
||||
print_info "loop $i for ping -c 1 $domain ..."
|
||||
if ping -c 4 -W 1 $le_domain
|
||||
then
|
||||
print_info "$le_domain resolved"
|
||||
break
|
||||
else
|
||||
if [ $i -gt 5 ]
|
||||
then
|
||||
die "Failed to: ping -c 1 $domain not resolved"
|
||||
fi
|
||||
fi
|
||||
sleep 10
|
||||
done
|
||||
sleep 5
|
||||
}
|
||||
|
||||
function configure_cron_freedns {
|
||||
print_info "configure cron for freedns..."
|
||||
if [ -z "$freedns_key" ]
|
||||
then
|
||||
print_info "freedns is not configured because freedns_key is empty in $configfile"
|
||||
else
|
||||
# Use cron for dynamich ip update
|
||||
# - at reboot
|
||||
# - every 30 minutes
|
||||
if [ -z "`grep 'freedns.afraid.org' /etc/crontab`" ]
|
||||
then
|
||||
echo "@reboot root http://freedns.afraid.org/dynamic/update.php?$freedns_key > /dev/null 2>&1" >> /etc/crontab
|
||||
echo "*/30 * * * * root wget --no-check-certificate -O - http://freedns.afraid.org/dynamic/update.php?$freedns_key > /dev/null 2>&1" >> /etc/crontab
|
||||
else
|
||||
print_info "cron for freedns was configured already"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function configure_cron_selfhost {
|
||||
print_info "configure cron for selfhost..."
|
||||
if [ -z "$selfhost_user" ]
|
||||
then
|
||||
print_info "selfhost is not configured because selfhost_key is empty in $configfile"
|
||||
else
|
||||
# Use cron for dynamich ip update
|
||||
# - at reboot
|
||||
# - every 5 minutes
|
||||
if [ -z "`grep 'selfhost-updater.sh' /etc/crontab`" ]
|
||||
then
|
||||
echo "@reboot root bash /etc/selfhost/selfhost-updater.sh update > /dev/null 2>&1" >> /etc/crontab
|
||||
echo "*/5 * * * * root /bin/bash /etc/selfhost/selfhost-updater.sh update > /dev/null 2>&1" >> /etc/crontab
|
||||
else
|
||||
print_info "cron for selfhost was configured already"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function install_letsencrypt {
|
||||
print_info "installing let's encrypt ..."
|
||||
# check if user gave domain
|
||||
if [ -z "$le_domain" ]
|
||||
then
|
||||
die "Failed to install let's encrypt: 'le_domain' is empty in $configfile"
|
||||
fi
|
||||
if [ -z "$le_email" ]
|
||||
then
|
||||
die "Failed to install let's encrypt: 'le_email' is empty in $configfile"
|
||||
fi
|
||||
nocheck_install "certbot python-certbot-apache"
|
||||
print_info "run certbot ..."
|
||||
certbot --apache -w $install_path -d $le_domain -m $le_email --agree-tos --non-interactive --redirect --hsts --uir
|
||||
service apache2 restart
|
||||
}
|
||||
|
||||
function check_https {
|
||||
print_info "checking httpS > testing ..."
|
||||
url_https=https://$le_domain
|
||||
wget_output=$(wget -nv --spider --max-redirect 0 $url_https)
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
print_warn "check not ok"
|
||||
else
|
||||
print_info "check ok"
|
||||
fi
|
||||
}
|
||||
|
||||
function install_zotserver {
|
||||
print_info "installing addons..."
|
||||
cd $install_path
|
||||
util/add_addon_repo https://framagit.org/hubzilla/addons hzaddons
|
||||
mkdir -p "store/[data]/smarty3"
|
||||
# chmod -R 777 store
|
||||
touch .htconfig.php
|
||||
# The next run of $cron_job (daily-update script) will correct the permissions of the next line
|
||||
chmod ou+w .htconfig.php
|
||||
cd /var/www/
|
||||
chown -R www-data:www-data $install_path
|
||||
chown root:www-data $install_path/
|
||||
chown root:www-data $install_path/.htaccess
|
||||
chmod 0644 $install_path/.htaccess
|
||||
print_info "installed addons"
|
||||
}
|
||||
|
||||
function configure_cron_daily {
|
||||
print_info "configuring cron..."
|
||||
# every 10 min for poller.php
|
||||
if [ -z "`grep 'php Zotlabs/Daemon/Master.php' /etc/crontab`" ]
|
||||
then
|
||||
echo "*/10 * * * * www-data cd $install_path; php Zotlabs/Daemon/Master.php Cron >> /dev/null 2>&1" >> /etc/crontab
|
||||
fi
|
||||
# Run external script daily at 05:30
|
||||
# - stop apache/nginx and mysql-server
|
||||
# - renew the certificate of letsencrypt
|
||||
# - update repository core and addon
|
||||
# - update and upgrade linux
|
||||
# - reboot is done by "shutdown -h now" because "reboot" hangs sometimes depending on the system
|
||||
echo "#!/bin/sh" > /var/www/$cron_job
|
||||
echo "#" >> /var/www/$cron_job
|
||||
echo "echo \" \"" >> /var/www/$cron_job
|
||||
echo "echo \"+++ \$(date) +++\"" >> /var/www/$cron_job
|
||||
echo "echo \" \"" >> /var/www/$cron_job
|
||||
echo "echo \"\$(date) - stopping apache and mysql...\"" >> /var/www/$cron_job
|
||||
echo "service apache2 stop" >> /var/www/$cron_job
|
||||
echo "/etc/init.d/mysql stop # to avoid inconsistencies" >> /var/www/$cron_job
|
||||
echo "#" >> /var/www/$cron_job
|
||||
echo "echo \"\$(date) - renew certificate...\"" >> /var/www/$cron_job
|
||||
echo "certbot renew --noninteractive" >> /var/www/$cron_job
|
||||
echo "#" >> /var/www/$cron_job
|
||||
echo "echo \"\$(date) - db size...\"" >> /var/www/$cron_job
|
||||
echo "du -h /var/lib/mysql/ | grep mysql/" >> /var/www/$cron_job
|
||||
echo "#" >> /var/www/$cron_job
|
||||
echo "# update of $le_domain Zot hub/instance" >> /var/www/$cron_job
|
||||
echo "echo \"\$(date) - updating core and addons...\"" >> /var/www/$cron_job
|
||||
echo "echo \"reaching git repository for $le_domain $zotserver hub/instance...\"" >> /var/www/$cron_job
|
||||
echo "(cd $install_path ; util/udall)" >> /var/www/$cron_job
|
||||
echo "chown -R www-data:www-data $install_path # make all accessible for the webserver" >> /var/www/$cron_job
|
||||
echo "chown root:www-data $install_path/.htaccess" >> /var/www/$cron_job
|
||||
echo "chmod 0644 $install_path/.htaccess # www-data can read but not write it" >> /var/www/$cron_job
|
||||
echo "echo \"\$(date) - updating linux...\"" >> /var/www/$cron_job
|
||||
echo "apt-get -q -y update && apt-get -q -y dist-upgrade && apt-get -q -y autoremove # update linux and upgrade" >> /var/www/$cron_job
|
||||
echo "echo \"\$(date) - Update finished. Rebooting...\"" >> /var/www/$cron_job
|
||||
echo "#" >> /var/www/$cron_job
|
||||
echo "shutdown -r now" >> /var/www/$cron_job
|
||||
|
||||
chmod a+x /var/www/$cron_job
|
||||
|
||||
# If global cron job does not exist we add it to /etc/crontab
|
||||
if grep -q $cron_job /etc/crontab
|
||||
then
|
||||
echo "cron job already in /etc/crontab"
|
||||
else
|
||||
echo "30 05 * * * root /bin/bash /var/www/$cron_job >> /var/www/daily-updates.log 2>&1" >> /etc/crontab
|
||||
echo "0 0 1 * * root rm /var/www/daily-updates.log" >> /etc/crontab
|
||||
fi
|
||||
|
||||
# This is active after either "reboot" or cron reload"
|
||||
systemctl restart cron
|
||||
print_info "configured cron for updates/upgrades"
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# START OF PROGRAM
|
||||
########################################################################
|
||||
export PATH=/bin:/usr/bin:/sbin:/usr/sbin
|
||||
check_sanity
|
||||
|
||||
print_info "We're installing a $zotserver instance"
|
||||
install_path="$(dirname "$(pwd)")"
|
||||
|
||||
# Read config file edited by user
|
||||
configfile=config.txt
|
||||
source $configfile
|
||||
|
||||
selfhostdir=/etc/selfhost
|
||||
selfhostscript=selfhost-updater.sh
|
||||
cron_job="cron_job.sh"
|
||||
|
||||
#set -x # activate debugging from here
|
||||
|
||||
zotserver=hubzilla
|
||||
check_config
|
||||
stop_zotserver
|
||||
update_upgrade
|
||||
install_curl
|
||||
install_wget
|
||||
install_sendmail
|
||||
install_apache
|
||||
install_imagemagick
|
||||
install_php
|
||||
install_composer
|
||||
install_mysql
|
||||
install_adminer
|
||||
create_zotserver_db
|
||||
run_freedns
|
||||
install_run_selfhost
|
||||
ping_domain
|
||||
configure_cron_freedns
|
||||
configure_cron_selfhost
|
||||
|
||||
if [ "$le_domain" != "localhost" ]
|
||||
then
|
||||
install_letsencrypt
|
||||
check_https
|
||||
else
|
||||
print_info "is localhost - skipped installation of letsencrypt and configuration of apache for https"
|
||||
fi
|
||||
|
||||
install_zotserver
|
||||
|
||||
configure_cron_daily
|
||||
|
||||
|
||||
#set +x # stop debugging from here
|
||||
33
.gitignore
vendored
33
.gitignore
vendored
@@ -49,6 +49,8 @@ doc/html/
|
||||
# external repositories for themes/addons
|
||||
extend/
|
||||
# files generated by phpunit
|
||||
tests/.cache
|
||||
tests/.phpunit.result.cache
|
||||
tests/results/
|
||||
|
||||
## exclude IDE files
|
||||
@@ -78,5 +80,36 @@ composer.phar
|
||||
vendor/**/tests/
|
||||
vendor/**/Test/
|
||||
vendor/sabre/*/examples/
|
||||
|
||||
# Exclude dev dependencies
|
||||
vendor/bin/pdepend
|
||||
vendor/bin/php-parse
|
||||
vendor/bin/phpcbf
|
||||
vendor/bin/phpcs
|
||||
vendor/bin/phpmd
|
||||
vendor/bin/phpunit
|
||||
vendor/composer/pcre/
|
||||
vendor/composer/xdebug-handler/
|
||||
vendor/dms/
|
||||
vendor/doctrine/
|
||||
vendor/myclabs/
|
||||
vendor/nikic/
|
||||
vendor/pdepend/
|
||||
vendor/phar-io/
|
||||
vendor/php-mock/
|
||||
vendor/phpmd/
|
||||
vendor/phpunit/
|
||||
vendor/psr/container/
|
||||
vendor/sebastian/
|
||||
vendor/squizlabs/
|
||||
vendor/symfony/config/
|
||||
vendor/symfony/dependency-injection/
|
||||
vendor/symfony/deprecation-contracts/
|
||||
vendor/symfony/filesystem/
|
||||
vendor/symfony/polyfill-ctype/
|
||||
vendor/symfony/polyfill-mbstring/
|
||||
vendor/symfony/polyfill-php80/
|
||||
vendor/symfony/service-contracts/
|
||||
vendor/theseer/
|
||||
# /info is a directory containing site-specific HTML documents
|
||||
/info/
|
||||
|
||||
145
.gitlab-ci.yml
145
.gitlab-ci.yml
@@ -1,8 +1,3 @@
|
||||
# Select image from https://hub.docker.com/_/php/
|
||||
#image: php:7.3
|
||||
# Use a prepared Hubzilla image to optimise pipeline duration
|
||||
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
|
||||
|
||||
stages:
|
||||
- test
|
||||
- deploy
|
||||
@@ -22,6 +17,7 @@ variables:
|
||||
# Ignore a Composer warning
|
||||
COMPOSER_ALLOW_SUPERUSER: 1
|
||||
# Configure MySQL/MariaDB service (https://hub.docker.com/_/mysql/, https://hub.docker.com/_/mariadb/)
|
||||
DB_HOST: mysql
|
||||
MYSQL_DATABASE: hello_world_test
|
||||
MYSQL_ROOT_PASSWORD: mysql
|
||||
# Configure PostgreSQL service (https://hub.docker.com/_/postgres/)
|
||||
@@ -31,47 +27,69 @@ variables:
|
||||
|
||||
|
||||
before_script:
|
||||
# pecl and composer do not work with PHP production restrictions (from Hubzilla Docker image)
|
||||
- if [ -f /usr/local/etc/php/conf.d/z_prod.ini ]; then mv /usr/local/etc/php/conf.d/z_prod.ini /usr/local/etc/php/conf.d/z_prod.ini.off; fi
|
||||
# Install & enable Xdebug for code coverage reports
|
||||
- pecl install xdebug
|
||||
- docker-php-ext-enable xdebug
|
||||
# Install composer
|
||||
- curl -sS https://getcomposer.org/installer | php
|
||||
# Install dev libraries from composer
|
||||
- php ./composer.phar install --no-progress
|
||||
# php.ini settings
|
||||
- echo 'xdebug.mode=coverage' >> /usr/local/etc/php/php.ini
|
||||
# Install & enable Xdebug for code coverage reports
|
||||
- apt-get update
|
||||
- apt-get install -yqq libicu-dev libjpeg-dev libpng-dev libpq-dev libyaml-dev libzip-dev mariadb-client postgresql-client unzip zip
|
||||
- pecl install xdebug yaml
|
||||
- docker-php-ext-enable xdebug yaml
|
||||
- docker-php-ext-install gd bcmath intl pdo_mysql pdo_pgsql zip
|
||||
|
||||
# Install composer
|
||||
- curl -sS https://getcomposer.org/installer | php
|
||||
# Install dev libraries from composer
|
||||
- php ./composer.phar install --no-progress
|
||||
# php.ini settings
|
||||
- echo 'xdebug.mode=coverage' >> /usr/local/etc/php/php.ini
|
||||
|
||||
# hidden job definition with template for MySQL/MariaDB
|
||||
.job_template_mysql: &job_definition_mysql
|
||||
stage: test
|
||||
variables:
|
||||
HZ_TEST_DB_HOST: $DB_HOST
|
||||
HZ_TEST_DB_TYPE: mysql
|
||||
HZ_TEST_DB_USER: root
|
||||
HZ_TEST_DB_PASS: $MYSQL_ROOT_PASSWORD
|
||||
HZ_TEST_DB_DATABASE: $MYSQL_DATABASE
|
||||
script:
|
||||
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
|
||||
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
|
||||
# Import hubzilla's DB schema
|
||||
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
|
||||
# Show databases and relations/tables of hubzilla's database
|
||||
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
|
||||
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
|
||||
# Run the actual tests
|
||||
- touch dbfail.out
|
||||
- vendor/bin/phpunit --configuration tests/phpunit.xml --verbose --stop-on-error --coverage-text --colors=never --log-junit tests/results/junit.xml || exit_code=$?
|
||||
- if [ $exit_code -ne 0 ]; then echo "Test barfed!"; cat dbfail.out; exit $exit_code; fi
|
||||
coverage: '/^\s*Lines:\s*\d+.\d+\%/'
|
||||
|
||||
|
||||
# hidden job definition with template for PostgreSQL
|
||||
.job_template_postgres: &job_definition_postgres
|
||||
stage: test
|
||||
services:
|
||||
- postgres:latest
|
||||
variables:
|
||||
HZ_TEST_DB_HOST: postgres
|
||||
HZ_TEST_DB_TYPE: postgres
|
||||
HZ_TEST_DB_USER: $POSTGRES_USER
|
||||
HZ_TEST_DB_PASS: $POSTGRES_PASSWORD
|
||||
HZ_TEST_DB_DATABASE: $POSTGRES_DB
|
||||
script:
|
||||
- export PGPASSWORD=$POSTGRES_PASSWORD
|
||||
- psql --version
|
||||
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT VERSION();"
|
||||
# Import hubzilla's DB schema
|
||||
- psql -h "postgres" -U "$POSTGRES_USER" -v ON_ERROR_STOP=1 --quiet "$POSTGRES_DB" < ./install/schema_postgres.sql
|
||||
# Show databases and relations/tables of hubzilla's database
|
||||
#- psql -h "postgres" -U "$POSTGRES_USER" -l
|
||||
#- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt;"
|
||||
# Run the actual tests
|
||||
- vendor/bin/phpunit --configuration tests/phpunit-pgsql.xml --testdox
|
||||
- export PGPASSWORD=$POSTGRES_PASSWORD
|
||||
- psql --version
|
||||
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT VERSION();"
|
||||
# Import hubzilla's DB schema
|
||||
- psql -h "postgres" -U "$POSTGRES_USER" -v ON_ERROR_STOP=1 --quiet "$POSTGRES_DB" < ./install/schema_postgres.sql
|
||||
# Show databases and relations/tables of hubzilla's database
|
||||
- psql -h "postgres" -U "$POSTGRES_USER" -l
|
||||
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt;"
|
||||
# Run the actual tests
|
||||
- touch dbfail.out
|
||||
- vendor/bin/phpunit --configuration tests/phpunit.xml --verbose --stop-on-error --coverage-text --colors=never --log-junit tests/results/junit.xml || exit_code=$?
|
||||
- if [ $exit_code -ne 0 ]; then echo "Test barfed!"; cat dbfail.out; exit $exit_code; fi
|
||||
coverage: '/^\s*Lines:\s*\d+.\d+\%/'
|
||||
|
||||
# hidden job definition with artifacts config template
|
||||
.artifacts_template:
|
||||
artifacts: &artifacts_template
|
||||
.artifacts_template: &artifacts_template
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
# Gitlab should show the results, but has problems parsing PHPUnit's junit file.
|
||||
reports:
|
||||
@@ -82,56 +100,37 @@ before_script:
|
||||
- tests/results/
|
||||
|
||||
|
||||
# PHP7.3 with MySQL 5.7
|
||||
php7.3_mysql5.7:
|
||||
<<: *job_definition_mysql
|
||||
# PHP8.1 with MySQL 8.0
|
||||
php8.1_mysql8.0.22:
|
||||
image: php:8.1
|
||||
services:
|
||||
- mysql:5.7
|
||||
|
||||
|
||||
# PHP7.3 with MySQL 8 (latest)
|
||||
php7.3_mysql8:
|
||||
- mysql:8.0
|
||||
<<: *job_definition_mysql
|
||||
<<: *artifacts_template
|
||||
|
||||
# PHP8.1 with MariaDB 10.6
|
||||
php8.1_mariadb10.6:
|
||||
image: php:8.1
|
||||
services:
|
||||
- name: mysql:8
|
||||
command: ["--default-authentication-plugin=mysql_native_password"]
|
||||
|
||||
|
||||
# PHP7.3 with MariaDB 10.2
|
||||
php7.3_mariadb10.2:
|
||||
- name: mariadb:10.6
|
||||
alias: mysql
|
||||
<<: *job_definition_mysql
|
||||
<<: *artifacts_template
|
||||
|
||||
# PHP8.1 with PostgreSQL 12
|
||||
php8.1_postgres12:
|
||||
image: php:8.1
|
||||
services:
|
||||
- name: mariadb:10.2
|
||||
alias: mysql
|
||||
|
||||
|
||||
# PHP7.3 with MariaDB 10.3 (latest)
|
||||
php7.3_mariadb10.3:
|
||||
<<: *job_definition_mysql
|
||||
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
|
||||
services:
|
||||
- name: mariadb:10.3
|
||||
alias: mysql
|
||||
|
||||
|
||||
# PHP7.3 with PostgreSQL latest (11)
|
||||
#php7.3_postgres11:
|
||||
# <<: *job_definition_postgres
|
||||
# artifacts: *artifacts_template
|
||||
|
||||
|
||||
# PHP7.3 with PostgreSQL latest (11)
|
||||
#php7.3_postgres11:
|
||||
# <<: *job_definition_postgres
|
||||
# image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
|
||||
# artifacts: *artifacts_template
|
||||
- postgres:12-alpine
|
||||
<<: *job_definition_postgres
|
||||
<<: *artifacts_template
|
||||
|
||||
|
||||
# Generate Doxygen API Documentation and deploy it as GitLab pages
|
||||
pages:
|
||||
stage: deploy
|
||||
cache: {}
|
||||
image: php:7-cli-alpine
|
||||
image: php:8-cli-alpine
|
||||
before_script:
|
||||
- apk update
|
||||
- apk add doxygen ttf-freefont graphviz
|
||||
|
||||
@@ -25,10 +25,10 @@ AddType audio/ogg .oga
|
||||
# in CGI mode.
|
||||
|
||||
RewriteCond %{REQUEST_URI} ^/\.well\-known/.*
|
||||
RewriteRule ^(.*)$ index.php?q=$1 [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
|
||||
RewriteRule ^(.*)$ index.php?q=$1 "[E=REMOTE_USER:%{HTTP:Authorization},L,QSA,B= ?]"
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?q=$1 [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
|
||||
RewriteRule ^(.*)$ index.php?q=$1 "[E=REMOTE_USER:%{HTTP:Authorization},L,QSA,B= ?]"
|
||||
</IfModule>
|
||||
|
||||
|
||||
769
CHANGELOG
769
CHANGELOG
@@ -1,3 +1,772 @@
|
||||
Hubzilla 9.0.2 (2024-06-07)
|
||||
- Fix buttons in event viewer
|
||||
- Fix some PHP warnings and errors
|
||||
- Fix issue when inReplyTo field is an array
|
||||
- Fix possible queueworker crash
|
||||
- Fix missing pdl file for mod home
|
||||
- Reduced default directory result set
|
||||
- Fix fatal error in likebanner addon
|
||||
- Fix fatal error in hilite addon
|
||||
|
||||
|
||||
Hubzilla 9.0.1 (2024-03-26)
|
||||
- Fix an issue where after an update initiated from a modal the modal backdrop would remain
|
||||
- Fix bootstrap namespace in conv list templates
|
||||
- Fix link to delivery report in conv list templates
|
||||
- Slightly improved handling of linefeeds in some bbcode block elements and added tests
|
||||
- Fix categories_widget() cache not being observer aware
|
||||
- Allow to run additional site specific commands at the end of util/udall in util/udall_extra
|
||||
- Fix linefeeds in table and list content removed
|
||||
- Pubcrawl: do not attrmpt to sign wall to wall messages - they will appear misattributed in mastodons
|
||||
- Pubcrawl: default to Note activity type for now
|
||||
|
||||
|
||||
Hubzilla 9.0 (2024-03-22)
|
||||
- Refactor browser to browser encryption based on sodium plus library
|
||||
- Added developer docs for the refactored test system
|
||||
- Move escape_tags() to Lib/Text::escape_tags() and add test
|
||||
- Messages are now sent as articles instead of notes - this can be configured for activitypub
|
||||
- Implement support for custom emojis
|
||||
- Add test for Lib/Activity::get_textfield()
|
||||
- Refactor mod things to be AS2 compliant
|
||||
- Implement basic bbcode tests and minor refactor
|
||||
- Refactor profile activities to be AS2 complient
|
||||
- Removed poke and moods app
|
||||
- Cleanup deprecated/unused activity types
|
||||
- Update doxygen config for generating online API docs
|
||||
- Make DBA driver transaction aware
|
||||
- Deprecate internal usage of ActivityStreams1 in favor of ActivityStreams2
|
||||
- Introduce Lib/Activity::get_actor() force flag to omit cache
|
||||
- Refactor mod contactedit refresh
|
||||
- Require intl PHP extension
|
||||
- Improved checks in Web/HTTPSig::find_headers()
|
||||
- Implement custom sass bootstrap builds for channels and site
|
||||
- Mark items verified in zot delivery if either JSalmon, LDSignature or EddsaSignature verified
|
||||
- Added support for code blocks with language in markdown and html
|
||||
- Improved conversation item design
|
||||
- Start using uuid for internal reference instead of base64 encoded mid
|
||||
- Store seen mids in session instead of cache
|
||||
- Increase sess_data DB column to medium text
|
||||
- Introduce Lib/Activity::init_background_fetch()
|
||||
- Refactor zotconvo daemon
|
||||
- Implement short time object cache to reduce network calls (performance)
|
||||
- Refactor Lib/Activity::fetch_and_store_parents()
|
||||
- Introduce the fetchparents daemon
|
||||
- Refactor Libzot::process_delivery()
|
||||
- Start processing source xchan in xchan_query()
|
||||
- Added CI job for MariaDB 10.6
|
||||
- Store the original announce actor (the one that pushed the item into our stream first) in source_xchan instead of owner_xchan to preserve the original owner
|
||||
- Added optional circle person avatar
|
||||
- Added min supported DB backends to administrator docs: MySql v >= 8.0.22, MariaDB v >= 10.6, PostgreSql v >= 12
|
||||
- Added CI job for MySql 8.0
|
||||
- Improved validate_email()
|
||||
- Implement fep-8b32 - object integrity proofs
|
||||
- Implement native repeats
|
||||
- Updated spanish strings
|
||||
- Add tests for check_account_email()
|
||||
- Vastly improved unit tests including the database
|
||||
- Require sodium PHP extension
|
||||
- Require bcmath or gmp PHP extension
|
||||
- Deprecate simplepie idna_convert()
|
||||
- Update apache rewite rule to fix issue with recent apache versions - issue #1822
|
||||
- Display selected mid in an open state - issue #1425
|
||||
- Add bookmark and category to AP schema
|
||||
|
||||
Bugfixes
|
||||
- Fix cover photos not uploaded into folder due to missing source option
|
||||
- Fix regression where config returned default value in some cases
|
||||
- Fix attachments listed in reverse order
|
||||
- Fix unterminated entity reference error when dealing with domxpath and add a test
|
||||
- Fix obsolete system language selector in admin/site
|
||||
- Fix imagick readImageBlob() exception not handled
|
||||
- Fix content not moved to new location if folder was renamed via webdav
|
||||
- Fix bootstrap namespaces not up-to-date in htmlpurifier
|
||||
- Fix inReplyTo field in Lib/Activity not dealing with arrays
|
||||
- Fix round buttons not being round
|
||||
- Fix import from ical if timezone was not set in the source data
|
||||
- Fix hard linebreaks from markdown and html not preserved in bbcode conversion
|
||||
- Fix indentation from markdown and html not preserved in bbcode conversion
|
||||
- Fix images with alt text from markdown and not preserved in bbcode conversion
|
||||
- Fix custom emoji reactions arriving from pleroma
|
||||
- Fix issue where if an item is created and deleted again before the notifier has completed the queueworker will dismiss the delete because it looks like a duplicate entry
|
||||
- Fix handling HTML entities via mbstring is deprecated
|
||||
- Fix various PHP deprecation warnings
|
||||
- Fix apache rewite rule to fix issue with recent apache versions - issue #1822
|
||||
- Fix display selected mid in an open state - issue #1425
|
||||
|
||||
Addons
|
||||
- Removed smileybutton addon
|
||||
- Removed smiley_pack addon
|
||||
- Pubcrawl: refactor presentation of encrypted messages
|
||||
- Removed deprecated cryptojs addon
|
||||
- Removed emojione addon
|
||||
- New addon emoji which can provide different emoji sets via config.system.emoji_set variable - emojitwo (default), openmoji, mutant are currently supported
|
||||
- Removed addon moremoods
|
||||
- Removed addon morepokes
|
||||
- Pubcrawl: implement actor_refetch hook
|
||||
- Diaspora: implement actor_refetch hook
|
||||
- Navbanner_options: fix PHP warnings
|
||||
- Pubcrawl: add assertionMethod to encode_person()
|
||||
- Socialauth: cleanup unused files
|
||||
- Openstreetmap: adjust URLs
|
||||
|
||||
|
||||
Hubzilla 8.8.8 (2024-02-29)
|
||||
- Streams compatibility fixes
|
||||
|
||||
|
||||
Hubzilla 8.8.7 (2024-01-19)
|
||||
- Fix regression in Activity::actor_store()
|
||||
|
||||
|
||||
Hubzilla 8.8.6 (2024-01-11)
|
||||
- Provide more builtin jsonld files
|
||||
- Development branch compatibility in Libsync
|
||||
|
||||
|
||||
Hubzilla 8.8.5 (2024-01-01)
|
||||
- Fix possible loop if DB is not reachable (introduced in 8.8.3)
|
||||
- Fix some errors and deprecation warnings with PHP 8.2
|
||||
- Deprecate simplepie idna_convert in favor of PHP native function
|
||||
- Fix double processed quoted strings in get_tags()
|
||||
|
||||
|
||||
Hubzilla 8.8.4 (2023-12-20)
|
||||
- Fix regression introduced in version 8.8.3
|
||||
- Add test for Lib/Config
|
||||
- Add active addons and blocked sites to siteinfo
|
||||
|
||||
|
||||
Hubzilla 8.8.3 (2023-12-17)
|
||||
- Check return from Config::Load() and retry on failure
|
||||
- Libzot::import() do not prozess items where we could not fetch the author
|
||||
- Translation updates for Norwegian Bokmål (nb_NO)
|
||||
- Add the app terms before syncing, otherwise the terms will be reset at the other end
|
||||
- Addon statistics: deprecate nodeinfo 1.0 and implement nodeinfo 2.1
|
||||
- Addon cards: fix PHP error
|
||||
|
||||
|
||||
Hubzilla 8.8.2 (2023-12-06)
|
||||
- Fix missing includes - issue #1820
|
||||
- Addon logger_stats: improved performance reading big log files
|
||||
|
||||
|
||||
Hubzilla 8.8.1 (2023-11-27)
|
||||
- Fix error in cards addon
|
||||
- Fix error in articles addon
|
||||
- Fix double left and right template css
|
||||
|
||||
|
||||
Hubzilla 8.8 (2023-11-25)
|
||||
- Add additional observer and channel info for nav templates
|
||||
- Do not provide confidential channel info for templates
|
||||
- Add link to profile to vcard
|
||||
- Improved switch colors for better visibility
|
||||
- Raise cache.v column from text to mediumtext for MYSQL
|
||||
- Implement low level support for native repeats
|
||||
- Color mode related code moved to theme_init.php in redbasic
|
||||
- Do not overrule default list style when parsing bbcode
|
||||
- Introduce Activity::get_actor() which will check for the cache record in xconfig before fetching
|
||||
- Refactor Activity::actor_store()
|
||||
- Use new language detect library which supports many more languages
|
||||
- Use Activity::encode_person() instead of plain xchan_url to set attributedTo
|
||||
- Update composer libraries
|
||||
- Move right aside into bottom of left aside if screen width is < 1200px in redbasic
|
||||
- When parsing events, use event object in first place and use the body bbcode as backup
|
||||
- Make OWA compatible with mastodon style keyId
|
||||
- Add sourced item events to the channel calendar
|
||||
- Make activity filter widget portable to other modules
|
||||
- Deprecate remains of the unused $a variable which has been replaced by the App class ages ago
|
||||
- Return object instead of json string in the custom jsonld document loader
|
||||
- Only show theme switch icon if switching is supported by the theme
|
||||
- Store the actor cache date with the actor record so we can easily invalidate it after a period of time
|
||||
- Allow themes to manipulate app icons (photos) via hook
|
||||
- Also look for widgets in view/theme/themename/widget
|
||||
- Cache seen pubstream item mids so that they can be hidden from notifications
|
||||
- Theme fixes to make barebones bootstrap themes work slightly better
|
||||
- Update Norwegian Bokmål strings
|
||||
- Update Spanish strings
|
||||
- Transparent background for colorbox controls icons
|
||||
- Use body background color for colorboxes in redbasic
|
||||
|
||||
Bugfixes
|
||||
- Remove fragment from actor urls
|
||||
- HTTPsig case insensitive digest algorithm
|
||||
- Fix possible privacy mismatch when processing zot requests
|
||||
- Fix @someone in URL turned into mention in some situations - issue #1816
|
||||
- Fix fatal error in simplepie with PHP8.2
|
||||
- Fix tools visible allthough permission has been revoked for observer in files app
|
||||
- Fix updates entry not removed if a channel was removed
|
||||
- Fix form reset button not visible in mod rpost
|
||||
- Fix missing columns for updates table in install script
|
||||
- Fix for item widget not respecting ACL if added by title - issue #1799
|
||||
- Fix sabre/dav caldav php warnings
|
||||
- Fix public stream comments/reactions not allowed if item_fetched is set
|
||||
|
||||
Addons
|
||||
- Superblock: fix php warnings
|
||||
- Pubcrawl: restrict mod ap_probe to admin and add checkbox for signed requests
|
||||
- Wiki: fix wiki_list widget not registered
|
||||
- Deprecate remains of the unused $a variable which has been replaced by the App class ages ago
|
||||
- Pubcrawl: reflect core delivery changes for repeated items
|
||||
- Cart fix regression
|
||||
- Logger_stats: new addon for admins to track hub activities
|
||||
- Pubcrawl: check if we have the record in the short term cache before actually fetching it
|
||||
- Pubcrawl: reflect core changes to Activity::actor_store()
|
||||
- Gallery: update to photoswipe 5
|
||||
- Pubcrawl: some platforms are sending activities without an object - return error 400
|
||||
- HSSE: update sce-editor to latest version
|
||||
- SSE: sse: do not process items that are older than last login date or in case we are not logged in older than 10 minutes
|
||||
- Faces: new addon for face recognision in uploaded photos
|
||||
|
||||
|
||||
Hubzilla 8.6.3 (2023-09-16)
|
||||
- Fix regression in jsonld_document_loader()
|
||||
- Improve type checking for announce activities
|
||||
- Improve query in drop_item() to prevent possible memory exhaustion
|
||||
- Addon gallery: only add gallery code if the module is supported
|
||||
- Addon hsse: port to bootstrap 5 namespaces - core issue #1793
|
||||
|
||||
|
||||
Hubzilla 8.6.2 (2023-08-27)
|
||||
- Fix public stream comments/reactions fetching
|
||||
- Fix notification text for likes in cases where obj.actor is not set
|
||||
- Fix missing pdl file for mod cover_photo
|
||||
- Fix unable to create folders with name 0
|
||||
- Fix index name mixup in mysql schema file
|
||||
- Fix missing semicolon in mysql schema file
|
||||
- Removed unused variable
|
||||
- Fix typo in manifest
|
||||
- Fix cached jsonld files fetched via network
|
||||
- Page rendering performance improvements
|
||||
- Fix internal follow activity possibly creating notification items
|
||||
- Fix admin table highlight color for dark mode
|
||||
- Pubcrawl: do not handle not implemented listen activity
|
||||
- Diaspora: fix unshare not implemented
|
||||
|
||||
|
||||
Hubzilla 8.6.1 (2023-07-18)
|
||||
- Fix diaspora discovery
|
||||
|
||||
|
||||
Hubzilla 8.6 (2023-07-11)
|
||||
- Update fullcalendar library
|
||||
- Improve and unify selection of deliverable abook xchans
|
||||
- Remove unused pseudo_abook()
|
||||
- Implement optional moderation for unsolicited items
|
||||
- Hardened comment permission handling for unsolicited items
|
||||
- Remove unused templates
|
||||
- Deprecate ActivityStreams::fetch() and provide the possibility to fetch local items directly
|
||||
- Add simplified version of automated install script
|
||||
- Shuffle queue deliveries for more randomness
|
||||
- Update composer libraries
|
||||
- Add new 2-column templates
|
||||
- Implement optional OCAP for items to allow access to restricted media without OWA
|
||||
|
||||
Bugfixes
|
||||
- Fix content in nobb and noparse text linkified - issue #1776
|
||||
- Fix editing an event changes the set time - issue #1771
|
||||
- Fix person object with mixed up hubloc info - issue #1770
|
||||
- Fix $escape variable not passed on to stringify_array_elms()
|
||||
- Fix relaying and syncing in Activity::drop()
|
||||
- Fix allow code not sticking after channel sync - issue #1769
|
||||
- Fix channel oembed regressions
|
||||
|
||||
Addons
|
||||
- Diaspora: fix signature check for likes
|
||||
- Diaspora: fix relaying retractions
|
||||
- Diaspora: port to core unsolicited comments option
|
||||
- Gallery: add an exception for streams reshares
|
||||
- Pubcrawl: dismiss comments that are expected to arrive via owner relay
|
||||
- Pubcrawl: improved inbox handling
|
||||
- Pubcrawl: catch gup.pe updates to followers collection
|
||||
- Pubcrawl: fix follow allow hook
|
||||
- Diaspora: fix follow allow hook
|
||||
- Content_import: fix crash in post handler
|
||||
- Pubcrawl: store follow url when fetching webfinger
|
||||
- Diaspora: store follow url when fetching webfinger
|
||||
- Pubcrawl: move addressing to separate function
|
||||
|
||||
|
||||
Hubzilla 8.4.2 (2023-06-02)
|
||||
- Update bootstrap to stable version 5.3.0
|
||||
- Fix hubloc confusion in mod rmagic
|
||||
- Improved unseen forums notification
|
||||
- Add workaround for friendica accept header bug for nginx
|
||||
- Fix acl not set correctly in attach_store()
|
||||
- Fix attachment name parsing
|
||||
- Fix display issue in oembed video template
|
||||
- content_import: fix crash in post handler
|
||||
- pubcrawl: move addressing to separate function
|
||||
|
||||
|
||||
Hubzilla 8.4.1 (2023-05-20)
|
||||
- Fix issue where accepting AP contacts would reset send stream permission
|
||||
- Fix link to release page in upgrade info addon
|
||||
|
||||
|
||||
Hubzilla 8.4 (2023-05-17)
|
||||
- Slightly rewrite Activity::store() to save a query
|
||||
- Use act->objprop() in decode_note() to get activity values
|
||||
- Make sure to re-discover a channel upon connecting if we have just an xchan but no hubloc
|
||||
- Updated ES translations
|
||||
- Updated RU translations
|
||||
- Redesigned profile vcard to implement cover images
|
||||
- Slightly improved discovery of AP quoted messages
|
||||
- Updated bootstrap library
|
||||
- Changed hashtag encoding to match mastodons expectations (issue #1750)
|
||||
- Implement fedearted directory flags from trusted directory servers
|
||||
- Use Activity::get_actor_hublocs() in Libzot::fetch_conversation() instead of custom queries
|
||||
- Add the conversation endpoint
|
||||
- Implement paginated fetch for zot requests in mod item
|
||||
- Implement Zotconvo daemon to fetch conversations in the background
|
||||
- Expose deleted channels to webfinger (otherwise we can not mark them deleted locally)
|
||||
- Expire items in batches to prevent memory exhaustion
|
||||
- Remove legacy zot compatibility tweaks
|
||||
- Rewrite and simplify directory sync
|
||||
- Improve potentially long running sql query finding the thread parent
|
||||
- Implement option to override permissions for posts with mentions
|
||||
|
||||
Bugfixes
|
||||
- Fix parent_mid and thr_parent not set correctly for response activities
|
||||
- Fix query for postgres in attach_list_files()
|
||||
- Fix item_url_replace() failing on arrays
|
||||
- Fix OWA compatibility with friendica
|
||||
- Fix regression in decoding the object type
|
||||
- Fix xss vulnerability in justified gallery library
|
||||
- Fix permissions not initialized after accepted follow request from AP
|
||||
- Fix empty path passed to fopen()
|
||||
- Fix % value in format_poll() not rounded
|
||||
- Fix remove_all_xchan_resources() executed for local channels
|
||||
- Fix likes not synced via libsync between clones
|
||||
|
||||
Addons
|
||||
- Pubcrawl: fix xchan_url used as follow object instead of xchan_hash
|
||||
- Channelreputation: moved to unmaintained repo due to duplication issues when storing data in pconfig
|
||||
- Pubcrawl: add lemmy accept/reject follow quirks
|
||||
- Diaspora: handle 4xx return codes in queue management
|
||||
- Diaspora: fix fatal error in the case where parent_guid is set but empty
|
||||
- Pubcrawl: if shared inbox delivery fails deliver to contacts instead of everybody we know from that site
|
||||
- Articles: fix PHP error
|
||||
- Diaspora: rewrite synced (from a clone) item author to primary
|
||||
- Diaspora: improve reshare detection
|
||||
- Diaspora: fix contact role not sets
|
||||
- Pubcrawl: rewrite synced (from a clone) item author to primary
|
||||
- Pubcrawl: if we receive a public message to the shared inbox deliver to following *and* directly addressed
|
||||
|
||||
|
||||
Hubzilla 8.2 (2023-03-19)
|
||||
- Remove redundant untranslated htconfig templates
|
||||
- Implement workaround for friendica image/attachment construct
|
||||
- Implement notification exception for forum posts repeated by group actors
|
||||
- Updated es translations
|
||||
- Rewrite totp multifactor auth and move it from addon to core (ported from streams)
|
||||
- Implement page load progress indicator for pwa standalone mode
|
||||
- Improved mod HQ logic to save some DB lookups
|
||||
- Default owner_xchan to sender in Activity::store() except for fetched items
|
||||
- Improved contact search
|
||||
- Do not force new thread if announce comes from group actor
|
||||
- Disable oembed in notes app
|
||||
- Improved queue handling
|
||||
- Slightly restructure conv item head
|
||||
- Implemet author images in HQ widget
|
||||
- Implement author filter in HQ widget
|
||||
- Add config option db_skip_locked_supported which should be set if the DB backend supports skip locked
|
||||
- Updated simple pie library
|
||||
- Updated smarty library
|
||||
- Updated forkawesome library
|
||||
- Updated fullcalendar library
|
||||
- Implement native dark/light mode in favour of dark/light schema
|
||||
- Updated bootstrap library
|
||||
|
||||
Bugfixes
|
||||
- Fix channel calendar location html not interpreted - issue #1728
|
||||
- Fix max oembed size check if content length header is an array
|
||||
- Fix group actor wall posts turned into direct messages when posting article or card
|
||||
- Fix non zot profile mentions using zrl tag
|
||||
- Fix message filter > and < not decoded
|
||||
- Fix deleted hublocs included in author lookup
|
||||
- Fix some more PHP warnings
|
||||
- Fix syntax for get_compund_property()
|
||||
- Fix xchan_pubforum not set on actor updates
|
||||
- Fix duplicate directory entries for xchans with bogus second primary entry
|
||||
- Fix missing created timestamp in like activity - issue #1729
|
||||
- Fix messages not signed when posting from non primary locations
|
||||
- Fix pinned post regression
|
||||
- Fix autocomplete items not selectable on mobile
|
||||
- Fix hubloc confusion in Libzot::fetch_conversation()
|
||||
- Fix valid duplicate notification entries deduplicated
|
||||
- Fix ics file import
|
||||
- Fix recipients deduplication in some places
|
||||
- Fix hashtag encoded with href instead of id
|
||||
|
||||
Addons
|
||||
- Pubcrawl: re-implement encrypted content which got lost in transition
|
||||
- Pubcrawl: flag fetched items
|
||||
- Totp: removed from addons in favor of implementation in core
|
||||
- Pubcrawl: fix delivery issues for relayed activities
|
||||
- Cart: fix missing bootstrap namespace
|
||||
- Gallery: do not inject gallery code in webpages
|
||||
- Pubcrawl: fix sql error if mentions string is empty
|
||||
- Cart: fix paypal order hash size
|
||||
|
||||
|
||||
Hubzilla 8.0 (2023-01-13)
|
||||
- Updated ru strings
|
||||
- Implement ability to edit addon PDL with pdledit_gui and pdledit
|
||||
- Implement queueworker auto sleep time
|
||||
- Always add sys channel to receivers for public items
|
||||
- Improved server sent events loop
|
||||
- Exclude not tagable xchan networks handle_tag()
|
||||
- Add mark html tag to the html2bbcode parser
|
||||
- Embed or attach uploaded files depending on file type
|
||||
- Changed logic to enable site only public stream - please revisit admin setting
|
||||
- Updated nb-no strings
|
||||
- Moved queueworker to core
|
||||
- Provide a PDL file for mod invite and set the profile
|
||||
- Disabled outdated context help
|
||||
- Mark unseen items seen after a certain amount of time (default 90 days)
|
||||
- Slightly restructure lib webfinger
|
||||
- Improved redbasic dark schema
|
||||
- Implement unseen thread and unseen items count in HQ widget
|
||||
- In unseen items notifications just count the first 99 and display 99+ if there are more
|
||||
|
||||
Bugfixes
|
||||
- Fix race conditions when processing multiple choice polls
|
||||
- Fix affinity slider updates - issue #1714
|
||||
- Fix issue where accounts created with a did2 could change their password
|
||||
- Fix no message displayed if file not found in mod cloud
|
||||
- Fix pubstream tagcloud unthreaded view
|
||||
- Fix french invite template
|
||||
- Fix comment permission issue in site only public stream
|
||||
- Fix privacy tag not referring to latest hubloc entry
|
||||
- Fix selected item not highlighted
|
||||
- Fix poll issue when special chars and spaces are involved
|
||||
- Fix prune_hub_reinstalls() to only care about zot6 hublocs
|
||||
- Fix redis session PHP warnings
|
||||
- Fix remote visitor photo uploads
|
||||
- Fix mod shredwithme not showing any items
|
||||
- Fix api_auth not referring to latest hubloc entry
|
||||
- Fix category widget URLs
|
||||
- Fix poller always polling broken feeds
|
||||
- Fix sse event always triggered if public stream notification are off or the app is not installed
|
||||
- Fix more PHP8 warnings
|
||||
- Fix wrong variable in tag_deliver()
|
||||
- Fix mod follow use trim() after punify() - issue #1698
|
||||
|
||||
Addons
|
||||
- Cards: update widget info
|
||||
- Articles: update widget info
|
||||
- Wiki: update widget info
|
||||
- Sse: filter apporvals
|
||||
- Cards: use html_entity_decode before purify and html2plain for summary
|
||||
- Articles: use html_entity_decode before purify and html2plain for summary
|
||||
- Wiki: use html_entity_decode before purify and html2plain for summary
|
||||
- Cards: fix comments not displayed if filter by category
|
||||
- Articles: fix comments not displayed if filter by category
|
||||
- Sse: respect the site public stream setting
|
||||
- Queueworker: moved to core
|
||||
- Pubcrawl: move db query out of foreach loop
|
||||
- Pubcrawl: add mentions and thread owner to recipients for public items
|
||||
|
||||
|
||||
Hubzilla 7.8.7 (2022-12-03)
|
||||
- Fix regression when adding feed contacts
|
||||
- Fix regression new channel calendar event not created
|
||||
|
||||
|
||||
Hubzilla 7.8.6 (2022-11-14)
|
||||
- Fix typo in boot.php
|
||||
|
||||
|
||||
Hubzilla 7.8.5 (2022-11-13)
|
||||
- Fix outbound edit activity not of type update
|
||||
- Fix mod display not falling through to fetch public item
|
||||
- Fix more PHP warnings
|
||||
- Fix regression in items_fetch() which resulted in empty atom feed
|
||||
- Pubcrawl: cleanup and slightly restructre mod inbox
|
||||
|
||||
|
||||
Hubzilla 7.8.4 (2022-11-09)
|
||||
- Fix new uuid created when editing a post
|
||||
|
||||
|
||||
Hubzilla 7.8.3 (2022-11-07)
|
||||
- Fix regression where auto created directories were not created with public permissions
|
||||
- Fix regression where pinned/featured state of apps was not displayed correctly
|
||||
|
||||
|
||||
Hubzilla 7.8.2 (2022-11-05)
|
||||
- Pubcrawl: fix regression in inbox
|
||||
- Fix display issue of shares coming from streams
|
||||
- Throw a 404 if we could not determine which channel to load
|
||||
- If we have a cached xchan/hubloc entry, make sure we fetch the latest
|
||||
- Gallery: paint the background grid with css
|
||||
|
||||
|
||||
Hubzilla 7.8.1 (2022-10-26)
|
||||
- Silence tons of PHP warnings in core
|
||||
- Catch decryption failure edgecase in receiver
|
||||
- Deal with conversation privacy mismatches in Activity::store()
|
||||
- Composer libs updates
|
||||
- Fix timeago strings not always translated
|
||||
- Fix edit link regression in cards addon
|
||||
- Fix edit link regression in article addon
|
||||
|
||||
|
||||
Hubzilla 7.8 (2022-10-10)
|
||||
- Updated spanish translations
|
||||
- Always update hubloc_updated timestamp if a hubloc is updated
|
||||
- Do better with re-installs in various situations
|
||||
- Update htconfig templates
|
||||
- Emit an info if an item is deleted manually (issue #1691)
|
||||
- Updated german translations
|
||||
- Make Activity::actor_store() fetch the actor object if none is provided
|
||||
- Check for various forms of as:Public for compatibility
|
||||
- Cleanup dark schema
|
||||
- Make use of CSS variables in redbasic
|
||||
- Implement link hover color in redbasic
|
||||
- Update to bootstrap version 5.2 which implements CSS variables
|
||||
|
||||
Bugfixes
|
||||
- Fix stream filter for polls and events does not work anymore (issue #1694)
|
||||
- Fix item menu display issue
|
||||
- Fix HQ widget displaying superblocked items
|
||||
- Fix mod poke
|
||||
- Fix link to article or card not processed correctly if umlauts are involved (issue #1687)
|
||||
- Fix mod siteinfo containing a dead link (issue #1690)
|
||||
- Fix permission issue when displaying things (issue #1686).
|
||||
- Fix check for existing profiles (issue #1688)
|
||||
- Fix modal missing for conversation settings
|
||||
- Fix missing closing div tag if there are no recent channel activities
|
||||
- Fix issue where an unkown diaspora author was not imported if the comment arrived via a relayed activity
|
||||
- Fix mangled xchan_url due to escape_tags()
|
||||
- Fix the AP url not decode bin mod search
|
||||
- Fix some php warnings
|
||||
|
||||
Addons
|
||||
- Wiki: move create_missing_page() from widget to module
|
||||
- Superblock: implement blocking for messages widget_item
|
||||
- Diaspora: fix verification of relayed comments and likes
|
||||
- Pubcrawl: make sure to store the attributedTo actor in announce activities
|
||||
- Diaspora: fix issue where an unkown author was not imported if the comment arrived via a relayed activity
|
||||
|
||||
|
||||
Hubzilla 7.6.1 (2022-08-07)
|
||||
Bugfixes
|
||||
- Fix attachments displayed in visible response activities
|
||||
- Fix wrong attribution in unseen like notifications
|
||||
|
||||
Addons
|
||||
- Cards: fix widget not implemented via pdl file (requires re-install)
|
||||
- Articles: fix widget not implemented via pdl file (requires re-install)
|
||||
- Wiki: fix widget not implemented via pdl file (requires re-install)
|
||||
|
||||
|
||||
Hubzilla 7.6 (2022-07-26)
|
||||
- Add filter rule to check for false condition
|
||||
- Implement HQ dashboard to display recently created content for various modules
|
||||
- Updated spanish translations
|
||||
- Implement a max oembed size which defaults to 1MB
|
||||
- Update composer libs
|
||||
|
||||
Addons
|
||||
- Cart: do not attempt oembed
|
||||
- Cards: implement channel_activities_widget for HQ dashboard
|
||||
- Articles: implement channel_activities_widget for HQ dashboard
|
||||
- Wiki: implement channel_activities_widget for HQ dashboard
|
||||
|
||||
Bugfixes
|
||||
- Fix language filter filtering ambigous results
|
||||
- Fix vcard-card background for dark schema
|
||||
- Fix contact edit dialog not displayed in chrome browser
|
||||
- Fix readability of category pills in dark schema
|
||||
- Fix opengraph images inside zmg opening tag
|
||||
- Fix oembed attempted for text previews
|
||||
|
||||
|
||||
Hubzilla 7.4 (2022-06-01)
|
||||
- Updated russian translations
|
||||
- Raise min PHP version to 8.0
|
||||
- Rewrite inbound attachment handling
|
||||
- Move photo flag feature from addon to core
|
||||
- Adjust default production php logging
|
||||
- Update composer libs
|
||||
- Add a hidden config to enable the hs2019 http signature algorithm
|
||||
- Allow starring of pubstream items
|
||||
- Update spanish translations
|
||||
- Add the title in forum post reshares if applicable
|
||||
- Implement inbound pleroma reactions
|
||||
- Add real name info to the navbar template data
|
||||
- Add the updated date to the icon url so that other platforms will pick it up on change
|
||||
- Move wiki from core to addon
|
||||
- Move articles from core to addon
|
||||
- Move cards from core to addon
|
||||
|
||||
Addons
|
||||
- Rendezvous: add missing default value to sql schema
|
||||
- Wiki: moved from core to addons
|
||||
- Articles: moved from core to addons
|
||||
- Cards: moved from core to addons
|
||||
- Photoflag: moved to core
|
||||
- Sse: fix issue where notifications were emited for created tasks
|
||||
|
||||
Bugfixes
|
||||
- Fix issue where inbound activitypub items lost image descriptions (issue 1679)
|
||||
- Fix issue where observer tags were not rendered correctly in event items (issue 1674)
|
||||
- Fix forum posts leaked into network stream
|
||||
- Fix tags not found in search if not logged in (issue 1677)
|
||||
- Fix dislikes causing stuck notifications (issue 1676)
|
||||
|
||||
|
||||
Hubzilla 7.2.2 (2022-04-26)
|
||||
- Fix item_verified not set due to data structure changes
|
||||
|
||||
|
||||
Hubzilla 7.2.1 (2022-04-25)
|
||||
- Fix changing profile image from new member widget - issue #1671
|
||||
- Fix regression with incoming poll answers from activitypub introduced in 7.2
|
||||
- Fix addons not removed from the DB when removed from the filesystem
|
||||
- Fix regression in attaching images for activitypub introduced in 7.2
|
||||
- Move activitypub addressing from core to the pubcrawl addon
|
||||
- Fix hub re-install issues
|
||||
- Fediwordle: slightly improved algorithm
|
||||
|
||||
|
||||
Hubzilla 7.2 (2022-03-29)
|
||||
- Streamline comment policy with downstream project
|
||||
- Add new function is_local_url()
|
||||
- Add helper function to escape URLs
|
||||
- Add signing algorithm to zotinfo()
|
||||
- Store signing algorithm in import_xchan()
|
||||
- Use bootstrap-nightfall for redbasic:dark schema
|
||||
- Add support for hs2019
|
||||
- Remove unused function script_path()
|
||||
- Move login and register button into the hamburger menu for small screens
|
||||
- Collect accept headers in an array instead of a concatenated string
|
||||
- Update composer libs
|
||||
- Enhanced content filters
|
||||
- Improve mod gprobe to also deal with URLs
|
||||
- Adapt unseen forum posts query in mod network to new forum post style
|
||||
- Remove deprecated widgets
|
||||
- Add inbound support for quoteUrl to Lib/Activity
|
||||
- Add widget descriptions
|
||||
- Add a GUI for the PDL editor
|
||||
|
||||
Addons
|
||||
- Pubcrawl: deprecate as.php in favor of core libs
|
||||
- Pubcrawl: rewrite/modernize mod inbox
|
||||
- Pubcrawl: reflect core enhanced content filter changes
|
||||
- Diaspora: reflect core enhanced content filter changes
|
||||
- Fediwordle: new addon - a distributed word game inspired by wordle
|
||||
- Pubcrawl: streamline post_local hook with diaspora
|
||||
|
||||
Bugfixes
|
||||
- Fix comments_closed date on posts where comments are disabled
|
||||
- Fix open redirect via rpath query param (CVE-2022-27256)
|
||||
- Fix cross-site scripting via rpath query param (CVE-2022-27258)
|
||||
- Fix local file inclusion in redbasic theme (CVE-2022-27257)
|
||||
- Fix baseurl for css and js
|
||||
- Fix duplicate IDs in login form
|
||||
- Fix unknown author not fetched if w2w comment arrives
|
||||
- Fix thr_parent lost across edits
|
||||
|
||||
|
||||
Hubzilla 7.0.3 (2022-02-10)
|
||||
- Allow to override the charset for the PDO connection string via $db_charset in .htconfig.php
|
||||
|
||||
|
||||
Hubzilla 7.0.2 (2022-02-09)
|
||||
- Update french templates
|
||||
- Add charset to the PDO connection strings
|
||||
- Introduce delete keytype for get_activitystreams_key()
|
||||
- Fix PHP error in Daemon/Externals
|
||||
- Improved actor cache handling
|
||||
- Implement manual fetch of packed local links
|
||||
- Add JSalmon data to the meta field instead of data in Lib/ActivityStreams
|
||||
- Fix some PHP8.1 deprecation warnings
|
||||
- Fix delivery report for likes not found in some cases
|
||||
- Allow zotfinger to recurse through all known hublocs if the one we got does not exist (404) or got removed (410)
|
||||
- Diaspora: improve relaying of comments
|
||||
- Fix regression in mod hcard
|
||||
- Add the LD signature in Daemon/Notifier in case where there is no signed data available
|
||||
- Prevent zot6 packet being saved as AP raw message
|
||||
- Attach iconfig to the activity instead of the activity object
|
||||
|
||||
Addons
|
||||
- Pubcrawl: make sure the sys channel falls through the app installed check
|
||||
- Pubcrawl: improve local delivery of shared inbox items
|
||||
|
||||
|
||||
Hubzilla 7.0.1 (2022-01-28)
|
||||
- Fix removing contacts from privacy groups in the contact edit modal
|
||||
- Fix escape_tags() messing with URLs in actor_store()
|
||||
- Fix pagination in the cards module if a category is selected
|
||||
- Remove unused entries in webfinger
|
||||
- Remove deprecated mail app from apps
|
||||
- Set item_hidden for forum comment announces
|
||||
- Fix relaying of signed messages for activitypub
|
||||
- Fix contact role permissions not re-assigned if the role permission has changed
|
||||
- Fix default channel role not set in rare cases
|
||||
|
||||
Addons
|
||||
- Pubcrawl: fix webfinger not returning the fetched URL
|
||||
- Pubcrawl: improved queue handling for rejected deliveries
|
||||
|
||||
|
||||
Hubzilla 7.0 (2022-01-21)
|
||||
- Provide theme_color and background_color in App::$theme_info for usage in page meta and manifest
|
||||
- PWA improvements according to lighthouse
|
||||
- Refactor mod profile_photo
|
||||
- Remove core legacy mail code
|
||||
- Set session samesite cookie flag
|
||||
- Improve toc bbcode for more flexible usecases
|
||||
- Deprecate include/group in favor of Lib/AccessList
|
||||
- Deprecate AccessList::widget()
|
||||
- Mark forum channel profile images with a small icon in the timelines
|
||||
- Improve privacy groups UI/UX
|
||||
- Do not show connections widget if there are no connections
|
||||
- Remove suggestions widget from various modules
|
||||
- Provide guest access links for private resources in lockview
|
||||
- Improve pconfig syncing
|
||||
- deprecate include/group in favor of Lib/AccessList
|
||||
- Implement background deleting of items in contact_remove()
|
||||
- Refactor guest access tokens for better usability and provide quick access
|
||||
- Refactor permissions handling
|
||||
- Improved poll rendering
|
||||
|
||||
Bugfixes
|
||||
- Fix items not deleted on remote channel purge
|
||||
- Fix plink in post_activity_item()
|
||||
- Fix multiple update_poll() calls dismissed in queueworker
|
||||
- Fix blocked or ignored contacts displayed in connections
|
||||
- Fix polls for forum channels
|
||||
|
||||
Addons:
|
||||
- Legacy mail: remove
|
||||
- Deprecate include/group in favor of Lib/AccessList
|
||||
- Pubcrawl: support pleroma end time for polls
|
||||
- Pubcrawl: slightly adjust the way we check mastodon direct messages
|
||||
- Socialauth: scope support and improvements
|
||||
|
||||
|
||||
Hubzilla 6.4.2 (2021-12-14)
|
||||
- Fix issue in mod sse_bs where returning message id's were assumed to be base64 encoded
|
||||
- Fix announce activity type not registered as response activity
|
||||
|
||||
|
||||
Hubzilla 6.4.1 (2021-12-03)
|
||||
- Fix hubloc_site_id in fix_system_urls() on detected site rename events
|
||||
- Fix duplicate deliveries if duplicate hublocs available
|
||||
- Redesign profile vcard for improved responsive handling
|
||||
- Fix profile photos not stored in profile photo folder
|
||||
- Maximum width of content region is now calculated in rem for improved responsive handling
|
||||
- Fix likes notices emited allthough they are disabled
|
||||
- Fix page not reloaded after comment/like in mod photos - issue #1651
|
||||
- Port improved Lib/HttpMeta from zap
|
||||
- Improved responsive aside
|
||||
|
||||
|
||||
Hubzilla 6.4 (2021-11-09)
|
||||
- Automatically connect the invitee with the inviting channel
|
||||
- Use the composer version of urlify
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Access;
|
||||
|
||||
|
||||
/**
|
||||
* @brief AccessList class which represents individual content ACLs.
|
||||
*
|
||||
@@ -17,29 +18,48 @@ class AccessList {
|
||||
* @brief Allow contacts
|
||||
* @var string
|
||||
*/
|
||||
private $allow_cid;
|
||||
private ?string $allow_cid;
|
||||
/**
|
||||
* @brief Allow groups
|
||||
* @var string
|
||||
*/
|
||||
private $allow_gid;
|
||||
private ?string $allow_gid;
|
||||
/**
|
||||
* @brief Deny contacts
|
||||
* @var string
|
||||
*/
|
||||
private $deny_cid;
|
||||
private ?string $deny_cid;
|
||||
/**
|
||||
* @brief Deny groups
|
||||
* @var string
|
||||
*/
|
||||
private $deny_gid;
|
||||
private ?string $deny_gid;
|
||||
/**
|
||||
* @brief Indicates if we are using the default constructor values or
|
||||
* values that have been set explicitly.
|
||||
* @var boolean
|
||||
*/
|
||||
private $explicit;
|
||||
private bool $explicit;
|
||||
|
||||
/**
|
||||
* @brief Keys required by the constructor if the channel array is given.
|
||||
*/
|
||||
private const REQUIRED_KEYS_CONSTRUCTOR = [
|
||||
'channel_allow_cid',
|
||||
'channel_allow_gid',
|
||||
'channel_deny_cid',
|
||||
'channel_deny_gid'
|
||||
];
|
||||
|
||||
/**
|
||||
* @brief Keys required by the set method.
|
||||
*/
|
||||
private const REQUIRED_KEYS_SET = [
|
||||
'allow_cid',
|
||||
'allow_gid',
|
||||
'deny_cid',
|
||||
'deny_gid'
|
||||
];
|
||||
|
||||
/**
|
||||
* @brief Constructor for AccessList class.
|
||||
@@ -53,8 +73,9 @@ class AccessList {
|
||||
* * \e string \b channel_deny_cid => string of denied cids
|
||||
* * \e string \b channel_deny_gid => string of denied gids
|
||||
*/
|
||||
function __construct($channel) {
|
||||
function __construct(array $channel) {
|
||||
if ($channel) {
|
||||
$this->validate_input_array($channel, self::REQUIRED_KEYS_CONSTRUCTOR);
|
||||
$this->allow_cid = $channel['channel_allow_cid'];
|
||||
$this->allow_gid = $channel['channel_allow_gid'];
|
||||
$this->deny_cid = $channel['channel_deny_cid'];
|
||||
@@ -70,13 +91,24 @@ class AccessList {
|
||||
$this->explicit = false;
|
||||
}
|
||||
|
||||
private function validate_input_array(array $arr, array $required_keys) : void {
|
||||
$missing_keys = array_diff($required_keys, array_keys($arr));
|
||||
|
||||
if (!empty($missing_keys)) {
|
||||
throw new \Exception(
|
||||
'Invalid AccessList object: Expected array with keys: '
|
||||
. implode(', ', $missing_keys)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get if we are using the default constructor values
|
||||
* or values that have been set explicitly.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
function get_explicit() {
|
||||
function get_explicit() : bool {
|
||||
return $this->explicit;
|
||||
}
|
||||
|
||||
@@ -94,7 +126,9 @@ class AccessList {
|
||||
* * \e string \b deny_gid => string of denied gids
|
||||
* @param boolean $explicit (optional) default true
|
||||
*/
|
||||
function set($arr, $explicit = true) {
|
||||
function set(array $arr, bool $explicit = true) : void {
|
||||
$this->validate_input_array($arr, self::REQUIRED_KEYS_SET);
|
||||
|
||||
$this->allow_cid = $arr['allow_cid'];
|
||||
$this->allow_gid = $arr['allow_gid'];
|
||||
$this->deny_cid = $arr['deny_cid'];
|
||||
@@ -112,7 +146,7 @@ class AccessList {
|
||||
* * \e string \b deny_cid => string of denied cids
|
||||
* * \e string \b deny_gid => string of denied gids
|
||||
*/
|
||||
function get() {
|
||||
function get() : array {
|
||||
return [
|
||||
'allow_cid' => $this->allow_cid,
|
||||
'allow_gid' => $this->allow_gid,
|
||||
@@ -138,7 +172,12 @@ class AccessList {
|
||||
* * \e array|string \b group_deny => array with gids or comma-seperated string
|
||||
* @param boolean $explicit (optional) default true
|
||||
*/
|
||||
function set_from_array($arr, $explicit = true) {
|
||||
function set_from_array(array $arr, bool $explicit = true) : void {
|
||||
$arr['contact_allow'] = $arr['contact_allow'] ?? [];
|
||||
$arr['group_allow'] = $arr['group_allow'] ?? [];
|
||||
$arr['contact_deny'] = $arr['contact_deny'] ?? [];
|
||||
$arr['group_deny'] = $arr['group_deny'] ?? [];
|
||||
|
||||
$this->allow_cid = perms2str((is_array($arr['contact_allow']))
|
||||
? $arr['contact_allow'] : explode(',', $arr['contact_allow']));
|
||||
$this->allow_gid = perms2str((is_array($arr['group_allow']))
|
||||
@@ -156,7 +195,7 @@ class AccessList {
|
||||
*
|
||||
* @return boolean Return true if any of allow_* deny_* values is set.
|
||||
*/
|
||||
function is_private() {
|
||||
function is_private() : bool {
|
||||
return (($this->allow_cid || $this->allow_gid || $this->deny_cid || $this->deny_gid) ? true : false);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,4 +89,4 @@ class PermissionLimits {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class PermissionRoles {
|
||||
* @return number
|
||||
*/
|
||||
static public function version() {
|
||||
return 2;
|
||||
return 3;
|
||||
}
|
||||
|
||||
static function role_perms($role) {
|
||||
@@ -27,6 +27,54 @@ class PermissionRoles {
|
||||
$ret['role'] = $role;
|
||||
|
||||
switch($role) {
|
||||
|
||||
case 'public':
|
||||
$ret['default_collection'] = false;
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki',
|
||||
'send_stream', 'post_comments', 'post_mail', 'post_wall', 'chat', 'post_like', 'republish'
|
||||
];
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
$ret['limits']['post_comments'] = PERMS_AUTHED;
|
||||
$ret['limits']['post_mail'] = PERMS_AUTHED;
|
||||
$ret['limits']['post_like'] = PERMS_AUTHED;
|
||||
$ret['limits']['chat'] = PERMS_AUTHED;
|
||||
break;
|
||||
|
||||
// Hubzilla default role
|
||||
case 'personal':
|
||||
$ret['default_collection'] = true;
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki',
|
||||
'send_stream', 'post_comments', 'post_mail', 'chat', 'post_like'
|
||||
];
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
$ret['limits']['view_contacts'] = PERMS_SPECIFIC;
|
||||
break;
|
||||
|
||||
case 'group':
|
||||
$ret['default_collection'] = false;
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
|
||||
'view_pages', 'view_wiki', 'post_wall', 'post_comments',
|
||||
'post_mail', 'post_like', 'chat'
|
||||
];
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
$ret['channel_type'] = 'group';
|
||||
break;
|
||||
|
||||
// Provide some defaults for the custom role so that we do not start
|
||||
// with no permissions at all if we create a new channel with this role
|
||||
case 'custom':
|
||||
$ret['default_collection'] = true;
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki',
|
||||
'send_stream', 'post_comments', 'post_mail', 'chat', 'post_like'
|
||||
];
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
break;
|
||||
|
||||
/*
|
||||
case 'social':
|
||||
$ret['perms_auto'] = false;
|
||||
$ret['default_collection'] = false;
|
||||
@@ -193,13 +241,14 @@ class PermissionRoles {
|
||||
$ret['channel_type'] = 'group';
|
||||
|
||||
break;
|
||||
*/
|
||||
|
||||
case 'custom':
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$x = get_config('system','role_perms');
|
||||
|
||||
// let system settings over-ride any or all
|
||||
if($x && is_array($x) && array_key_exists($role,$x))
|
||||
$ret = array_merge($ret,$x[$role]);
|
||||
@@ -284,6 +333,7 @@ class PermissionRoles {
|
||||
*/
|
||||
static public function roles() {
|
||||
$roles = [
|
||||
|
||||
t('Social Networking') => [
|
||||
'social_federation' => t('Social - Federation'),
|
||||
'social' => t('Social - Mostly Public'),
|
||||
@@ -317,4 +367,29 @@ class PermissionRoles {
|
||||
return $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Array with translated role names and grouping.
|
||||
*
|
||||
* Return an associative array with role names that can be used
|
||||
* to create select groups like in \e field_select_grouped.tpl.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
static public function channel_roles() {
|
||||
$channel_roles = [
|
||||
//'public' => [t('Public'), t('A very permissive role suited for participation in the fediverse')],
|
||||
//'personal' => [t('Personal'), t('The $Projectname default role suited for a personal channel')],
|
||||
//'forum' => [t('Community forum'), t('This role configures your channel to act as an community forum')],
|
||||
//'custom' => [t('Custom'), t('This role comes with the presets of the personal role but allows you to configure it to your needs')]
|
||||
'public' => t('Public'),
|
||||
'personal' => t('Personal'),
|
||||
'group' => t('Community forum'),
|
||||
'custom' => t('Custom')
|
||||
];
|
||||
|
||||
call_hooks('list_channel_roles', $channel_roles);
|
||||
|
||||
return $channel_roles;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class Permissions {
|
||||
* @return number
|
||||
*/
|
||||
static public function version() {
|
||||
return 2;
|
||||
return 3;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,9 +67,9 @@ class Permissions {
|
||||
'post_comments' => t('Can comment on or like my posts'),
|
||||
'post_mail' => t('Can send me direct messages'),
|
||||
'post_like' => t('Can like/dislike profiles and profile things'),
|
||||
'tag_deliver' => t('Can forward direct messages to all my channel connections (forum)'),
|
||||
'chat' => t('Can chat with me'),
|
||||
'republish' => t('Can source my public posts in derived channels'),
|
||||
'republish' => t('Can source/mirror my public posts in derived channels'),
|
||||
//'tag_deliver' => t('Can forward to my contacts via direct messages (forum)'),
|
||||
'delegate' => t('Can administer my channel')
|
||||
];
|
||||
|
||||
@@ -217,25 +217,23 @@ class Permissions {
|
||||
|
||||
$my_perms = [];
|
||||
$permcat = null;
|
||||
$automatic = 0;
|
||||
$automatic = get_pconfig($channel_id, 'system', 'autoperms');
|
||||
|
||||
// If a default permcat exists, use that
|
||||
|
||||
$pc = ((feature_enabled($channel_id, 'permcats')) ? get_pconfig($channel_id, 'system', 'default_permcat') : 'default');
|
||||
if (!in_array($pc, ['', 'default'])) {
|
||||
$pcp = new Zlib\Permcat($channel_id);
|
||||
$permcat = $pcp->fetch($pc);
|
||||
if ($permcat && $permcat['perms']) {
|
||||
foreach ($permcat['perms'] as $p) {
|
||||
$my_perms[$p['name']] = $p['value'];
|
||||
}
|
||||
$pc = get_pconfig($channel_id, 'system', 'default_permcat', 'default');
|
||||
$pcp = new Zlib\Permcat($channel_id);
|
||||
$permcat = $pcp->fetch($pc);
|
||||
if ($permcat && $permcat['perms']) {
|
||||
foreach ($permcat['perms'] as $p) {
|
||||
$my_perms[$p['name']] = $p['value'];
|
||||
}
|
||||
}
|
||||
|
||||
// look up the permission role to see if it specified auto-connect
|
||||
// and if there was no permcat or a default permcat, set the perms
|
||||
// from the role
|
||||
|
||||
/*
|
||||
$role = get_pconfig($channel_id, 'system', 'permissions_role');
|
||||
if ($role) {
|
||||
$xx = PermissionRoles::role_perms($role);
|
||||
@@ -247,11 +245,12 @@ class Permissions {
|
||||
$my_perms = Permissions::FilledPerms($default_perms);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// If we reached this point without having any permission information,
|
||||
// it is likely a custom permissions role. First see if there are any
|
||||
// automatic permissions.
|
||||
|
||||
/*
|
||||
if (!$my_perms) {
|
||||
$m = Permissions::FilledAutoperms($channel_id);
|
||||
if ($m) {
|
||||
@@ -259,11 +258,12 @@ class Permissions {
|
||||
$my_perms = $m;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
// If we reached this point with no permissions, the channel is using
|
||||
// custom perms but they are not automatic. They will be stored in abconfig with
|
||||
// the channel's channel_hash (the 'self' connection).
|
||||
|
||||
/*
|
||||
if (!$my_perms) {
|
||||
$r = q("select channel_hash from channel where channel_id = %d",
|
||||
intval($channel_id)
|
||||
@@ -280,10 +280,10 @@ class Permissions {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (['perms' => $my_perms, 'automatic' => $automatic]);
|
||||
*/
|
||||
return (['perms' => $my_perms, 'automatic' => $automatic, 'role' => $pc]);
|
||||
}
|
||||
|
||||
/*
|
||||
static public function serialise($p) {
|
||||
$n = [];
|
||||
if ($p) {
|
||||
@@ -295,4 +295,5 @@ class Permissions {
|
||||
}
|
||||
return implode(',', $n);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ class Addon {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
call_hooks('daemon_addon', $argv);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,10 @@ class Cache_embeds {
|
||||
$item = $c[0];
|
||||
|
||||
// bbcode conversion by default processes embeds that aren't already cached.
|
||||
// Ignore the returned html output.
|
||||
// Ignore the returned html output.
|
||||
|
||||
bbcode($item['body']);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ class Cache_query {
|
||||
if(! $argc == 3)
|
||||
return;
|
||||
|
||||
$r = null;
|
||||
$key = $argv[1];
|
||||
|
||||
$pid = get_config('procid', $key, false);
|
||||
@@ -24,13 +25,17 @@ class Cache_query {
|
||||
|
||||
array_shift($argv);
|
||||
array_shift($argv);
|
||||
|
||||
|
||||
$arr = json_decode(base64_decode($argv[0]), true);
|
||||
|
||||
$r = call_user_func_array('q', $arr);
|
||||
if($r)
|
||||
|
||||
if(is_array($r)) {
|
||||
Cache::set($key, serialize($r));
|
||||
}
|
||||
|
||||
del_config('procid', $key);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ class Checksites {
|
||||
|
||||
logger('checksites: start');
|
||||
|
||||
$site_id = '';
|
||||
$sql_options = '';
|
||||
|
||||
if (($argc > 1) && ($argv[1]))
|
||||
$site_id = $argv[1];
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ class Cli_suggest {
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
update_suggestions();
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ class Convo {
|
||||
intval($channel_id),
|
||||
dbesc($contact_hash)
|
||||
);
|
||||
|
||||
if (!$r) {
|
||||
return;
|
||||
}
|
||||
@@ -40,19 +41,26 @@ class Convo {
|
||||
|
||||
$messages = $obj->get();
|
||||
|
||||
if ($messages) {
|
||||
foreach ($messages as $message) {
|
||||
if (is_string($message)) {
|
||||
$message = Activity::fetch($message, $channel);
|
||||
}
|
||||
// set client flag because comments will probably just be objects and not full blown activities
|
||||
// and that lets us use implied_create
|
||||
$AS = new ActivityStreams($message);
|
||||
if ($AS->is_valid() && is_array($AS->obj)) {
|
||||
$item = Activity::decode_note($AS);
|
||||
Activity::store($channel, $contact['abook_xchan'], $AS, $item);
|
||||
}
|
||||
if (!$messages) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($messages as $message) {
|
||||
if (is_string($message)) {
|
||||
$message = Activity::fetch($message, $channel);
|
||||
}
|
||||
|
||||
// set client flag because comments will probably just be objects and not full blown activities
|
||||
// and that lets us use implied_create
|
||||
$AS = new ActivityStreams($message);
|
||||
if ($AS->is_valid() && is_array($AS->obj)) {
|
||||
$item = Activity::decode_note($AS);
|
||||
$item['item_fetched'] = true;
|
||||
Activity::store($channel, $contact['abook_xchan'], $AS, $item);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\Libsync;
|
||||
use Zotlabs\Lib\Libzotdir;
|
||||
|
||||
class Cron {
|
||||
|
||||
@@ -19,6 +20,7 @@ class Cron {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it.
|
||||
$lockfile = 'store/[data]/cron';
|
||||
if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600))
|
||||
@@ -30,9 +32,21 @@ class Cron {
|
||||
// Create a lockfile. Needs two vars, but $x doesn't need to contain anything.
|
||||
$x = '';
|
||||
file_put_contents($lockfile, $x);
|
||||
*/
|
||||
|
||||
logger('cron: start');
|
||||
|
||||
// If this is a directory server, request a sync with an upstream
|
||||
// directory at least once a day, up to once every poll interval.
|
||||
// Pull remote changes and push local changes.
|
||||
// potential issue: how do we keep from creating an endless update loop?
|
||||
|
||||
$dirmode = get_config('system', 'directory_mode');
|
||||
|
||||
if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
|
||||
Libzotdir::sync_directories($dirmode);
|
||||
}
|
||||
|
||||
// run queue delivery process in the background
|
||||
|
||||
Master::Summon(array('Queue'));
|
||||
@@ -50,7 +64,7 @@ class Cron {
|
||||
require_once('include/account.php');
|
||||
remove_expired_registrations();
|
||||
|
||||
$interval = get_config('system', 'delivery_interval', 3);
|
||||
$interval = get_config('queueworker', 'queue_interval', 500000);
|
||||
|
||||
// expire any expired items
|
||||
|
||||
@@ -65,8 +79,10 @@ class Cron {
|
||||
if ($rr['item_wall']) {
|
||||
// The notifier isn't normally invoked unless item_drop is interactive.
|
||||
Master::Summon(['Notifier', 'drop', $rr['id']]);
|
||||
if ($interval)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,8 +112,10 @@ class Cron {
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
Master::Summon(array('Directory', $rr['channel_id'], 'force'));
|
||||
if ($interval)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,13 +124,13 @@ class Cron {
|
||||
$r = q("SELECT DISTINCT xchan, content FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
|
||||
intval(PHOTO_CACHE),
|
||||
db_utcnow(),
|
||||
db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY')
|
||||
db_quoteinterval(get_config('system', 'cache_expire_days', 7) . ' DAY')
|
||||
);
|
||||
if ($r) {
|
||||
q("DELETE FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
|
||||
intval(PHOTO_CACHE),
|
||||
db_utcnow(),
|
||||
db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY')
|
||||
db_quoteinterval(get_config('system', 'cache_expire_days', 7) . ' DAY')
|
||||
);
|
||||
foreach ($r as $rr) {
|
||||
$file = dbunescbin($rr['content']);
|
||||
@@ -151,8 +169,10 @@ class Cron {
|
||||
);
|
||||
}
|
||||
Master::Summon(array('Notifier', 'wall-new', $rr['id']));
|
||||
if ($interval)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,10 +223,10 @@ class Cron {
|
||||
}
|
||||
|
||||
|
||||
// pull in some public posts
|
||||
// pull in some public posts if allowed
|
||||
|
||||
$disable_discover_tab = get_config('system', 'disable_discover_tab') || get_config('system', 'disable_discover_tab') === false;
|
||||
if (!$disable_discover_tab)
|
||||
$disable_externals = get_config('system', 'disable_discover_tab') || get_config('system', 'disable_discover_tab') === false || get_config('system', 'site_firehose');
|
||||
if (!$disable_externals)
|
||||
Master::Summon(['Externals']);
|
||||
|
||||
$restart = false;
|
||||
@@ -228,7 +248,7 @@ class Cron {
|
||||
set_config('system', 'lastcron', datetime_convert());
|
||||
|
||||
//All done - clear the lockfile
|
||||
@unlink($lockfile);
|
||||
//@unlink($lockfile);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,10 +49,26 @@ class Cron_daily {
|
||||
dbesc('sse_id.%')
|
||||
);
|
||||
|
||||
// Clean up emdedded content cache
|
||||
// Mark items seen after X days (default 90)
|
||||
|
||||
$r = dbq("select channel_id from channel where channel_removed = 0");
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
$mark_seen_days = get_pconfig($rr['channel_id'], 'system', 'mark_seen_days', 90);
|
||||
q("UPDATE item SET item_unseen = 0 WHERE
|
||||
uid = %d AND item_unseen = 1
|
||||
AND created < %s - INTERVAL %s",
|
||||
intval($rr['channel_id']),
|
||||
db_utcnow(),
|
||||
db_quoteinterval($mark_seen_days . ' DAY')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up cache
|
||||
q("DELETE FROM cache WHERE updated < %s - INTERVAL %s",
|
||||
db_utcnow(),
|
||||
db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY')
|
||||
db_quoteinterval(get_config('system', 'cache_expire_days', 7) . ' DAY')
|
||||
);
|
||||
|
||||
//update statistics in config
|
||||
@@ -78,17 +94,6 @@ class Cron_daily {
|
||||
// expire any expired accounts
|
||||
downgrade_accounts();
|
||||
|
||||
// If this is a directory server, request a sync with an upstream
|
||||
// directory at least once a day, up to once every poll interval.
|
||||
// Pull remote changes and push local changes.
|
||||
// potential issue: how do we keep from creating an endless update loop?
|
||||
|
||||
$dirmode = get_config('system', 'directory_mode');
|
||||
|
||||
if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
|
||||
Libzotdir::sync_directories($dirmode);
|
||||
}
|
||||
|
||||
Master::Summon(array('Expire'));
|
||||
Master::Summon(array('Cli_suggest'));
|
||||
|
||||
@@ -103,5 +108,7 @@ class Cron_daily {
|
||||
/**
|
||||
* End Cron Daily
|
||||
*/
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,12 @@ class Cron_weekly {
|
||||
|
||||
mark_orphan_hubsxchans();
|
||||
|
||||
// Find channels that were removed in the last three weeks, but
|
||||
// Find channels that were removed in the last three weeks, but
|
||||
// haven't been finally cleaned up. These should be older than 10
|
||||
// days to ensure that "purgeall" messages have gone out or bounced
|
||||
// or timed out.
|
||||
// days to ensure that "purgeall" messages have gone out or bounced
|
||||
// or timed out.
|
||||
|
||||
$r = q("select channel_id from channel where channel_removed = 1 and
|
||||
$r = q("select channel_id from channel where channel_removed = 1 and
|
||||
channel_deleted > %s - INTERVAL %s and channel_deleted < %s - INTERVAL %s",
|
||||
db_utcnow(), db_quoteinterval('21 DAY'),
|
||||
db_utcnow(), db_quoteinterval('10 DAY')
|
||||
@@ -59,5 +59,6 @@ class Cron_weekly {
|
||||
* End Cron Weekly
|
||||
*/
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ class Deliver {
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,8 +12,12 @@ class Deliver_hooks {
|
||||
$r = q("select * from item where id = '%d'",
|
||||
intval($argv[1])
|
||||
);
|
||||
if ($r)
|
||||
|
||||
if ($r) {
|
||||
call_hooks('notifier_normal', $r[0]);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
25
Zotlabs/Daemon/Delxitems.php
Normal file
25
Zotlabs/Daemon/Delxitems.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/connections.php');
|
||||
|
||||
/*
|
||||
* Daemon to remove 'item' resources in the background from a removed connection
|
||||
*/
|
||||
|
||||
class Delxitems {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
cli_startup();
|
||||
|
||||
if($argc != 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
remove_abook_items($argv[1], $argv[2]);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -97,5 +97,7 @@ class Directory {
|
||||
if ($pushall) {
|
||||
Master::Summon(array('Notifier', 'refresh_all', $channel['channel_id']));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ class Expire {
|
||||
}
|
||||
|
||||
// physically remove anything that has been deleted for more than two months
|
||||
/** @FIXME - this is a wretchedly inefficient query */
|
||||
|
||||
q("delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s",
|
||||
db_utcnow(),
|
||||
@@ -44,8 +43,8 @@ class Expire {
|
||||
|
||||
logger('expire: start with pid ' . $pid, LOGGER_DEBUG);
|
||||
|
||||
$site_expire = intval(get_config('system', 'default_expire_days'));
|
||||
$commented_days = intval(get_config('system', 'active_expire_days'));
|
||||
$site_expire = intval(get_config('system', 'default_expire_days', 30));
|
||||
$commented_days = intval(get_config('system', 'active_expire_days', 7));
|
||||
|
||||
logger('site_expire: ' . $site_expire);
|
||||
|
||||
@@ -59,8 +58,8 @@ class Expire {
|
||||
continue;
|
||||
|
||||
// service class default (if non-zero) over-rides the site default
|
||||
|
||||
$service_class_expire = service_class_fetch($rr['channel_id'], 'expire_days');
|
||||
|
||||
if (intval($service_class_expire))
|
||||
$channel_expire = $service_class_expire;
|
||||
else
|
||||
@@ -85,7 +84,6 @@ class Expire {
|
||||
|
||||
// this should probably just fetch the channel_expire_days from the sys channel,
|
||||
// but there's no convenient way to set it.
|
||||
|
||||
$expire_days = get_config('system', 'sys_expire_days');
|
||||
if ($expire_days === false)
|
||||
$expire_days = 30;
|
||||
@@ -96,12 +94,15 @@ class Expire {
|
||||
|
||||
logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG);
|
||||
|
||||
if ($expire_days)
|
||||
if ($expire_days) {
|
||||
item_expire($x['channel_id'], $expire_days, $commented_days);
|
||||
}
|
||||
|
||||
logger('Expire: sys: done', LOGGER_DEBUG);
|
||||
}
|
||||
|
||||
del_config('procid', 'expire');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ class Externals {
|
||||
$importer = get_sys_channel();
|
||||
$total = 0;
|
||||
$attempts = 0;
|
||||
$url = '';
|
||||
|
||||
logger('externals: startup', LOGGER_DEBUG);
|
||||
|
||||
@@ -67,9 +68,8 @@ class Externals {
|
||||
datetime_convert('UTC', 'UTC', 'now - 30 days')
|
||||
);
|
||||
|
||||
$contact = $r[0];
|
||||
|
||||
if ($contact) {
|
||||
if ($r) {
|
||||
$contact = $r[0];
|
||||
$url = $contact['hubloc_id_url'];
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,9 @@ class Externals {
|
||||
continue;
|
||||
}
|
||||
|
||||
Libzot::fetch_conversation($importer, $message['object']['id']);
|
||||
$obj_id = $message['object']['id'] ?? $message['object'];
|
||||
|
||||
Libzot::fetch_conversation($importer, $obj_id);
|
||||
$total++;
|
||||
continue;
|
||||
}
|
||||
@@ -141,7 +143,8 @@ class Externals {
|
||||
$AS = new ActivityStreams($message);
|
||||
if ($AS->is_valid() && is_array($AS->obj)) {
|
||||
$item = Activity::decode_note($AS);
|
||||
Activity::store($importer, $contact['abook_xchan'], $AS, $item);
|
||||
$item['item_fetched'] = true;
|
||||
Activity::store($importer, $contact['hubloc_hash'], $AS, $item);
|
||||
$total++;
|
||||
}
|
||||
}
|
||||
|
||||
42
Zotlabs/Daemon/Fetchparents.php
Normal file
42
Zotlabs/Daemon/Fetchparents.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\Activity;
|
||||
|
||||
class Fetchparents {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
logger('Fetchparents invoked: ' . print_r($argv, true));
|
||||
|
||||
if ($argc < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
$channels = explode(',', $argv[1]);
|
||||
if (!$channels) {
|
||||
return;
|
||||
}
|
||||
|
||||
$observer_hash = $argv[2];
|
||||
if (!$observer_hash) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mid = $argv[3];
|
||||
if (!$mid) {
|
||||
return;
|
||||
}
|
||||
|
||||
$force = $argv[4] ?? false;
|
||||
|
||||
foreach ($channels as $channel_id) {
|
||||
$channel = channelx_by_n($channel_id);
|
||||
Activity::fetch_and_store_parents($channel, $observer_hash, $mid, null, $force);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -15,21 +15,32 @@ class Gprobe {
|
||||
return;
|
||||
|
||||
$url = hex2bin($argv[1]);
|
||||
$is_webbie = false;
|
||||
$r = null;
|
||||
|
||||
if (!strpos($url, '@'))
|
||||
return;
|
||||
if (filter_var($url, FILTER_VALIDATE_EMAIL)) {
|
||||
$is_webbie = true;
|
||||
|
||||
$r = q("select * from hubloc where hubloc_addr = '%s' and hubloc_network = 'zot6' limit 1",
|
||||
dbesc($url)
|
||||
);
|
||||
$r = q("select * from hubloc where hubloc_addr = '%s' and hubloc_network = 'zot6' limit 1",
|
||||
dbesc($url)
|
||||
);
|
||||
}
|
||||
elseif (filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
$r = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
|
||||
dbesc($url)
|
||||
);
|
||||
}
|
||||
|
||||
if (!$r) {
|
||||
$href = Webfinger::zot_url(punify($url));
|
||||
if ($href) {
|
||||
$zf = Zotfinger::exec($href, null);
|
||||
if ($is_webbie) {
|
||||
$url = Webfinger::zot_url(punify($url));
|
||||
}
|
||||
if (is_array($zf) && array_path_exists('signature/signer', $zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) {
|
||||
Libzot::import_xchan($zf['data']);
|
||||
|
||||
if ($url) {
|
||||
$zf = Zotfinger::exec($url, null);
|
||||
if (is_array($zf) && array_path_exists('signature/signer', $zf) && $zf['signature']['signer'] === $url && intval($zf['signature']['header_valid'])) {
|
||||
Libzot::import_xchan($zf['data']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ class Importdoc {
|
||||
|
||||
self::update_docs_dir('doc/*');
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
static public function update_docs_dir($s) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\QueueWorker;
|
||||
|
||||
if (array_search(__file__, get_included_files()) === 0) {
|
||||
require_once('include/cli_startup.php');
|
||||
array_shift($argv);
|
||||
@@ -9,6 +11,7 @@ if (array_search(__file__, get_included_files()) === 0) {
|
||||
|
||||
if ($argc)
|
||||
Master::Release($argc, $argv);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,6 +19,10 @@ if (array_search(__file__, get_included_files()) === 0) {
|
||||
class Master {
|
||||
|
||||
static public function Summon($arr) {
|
||||
|
||||
QueueWorker::Summon($arr);
|
||||
return;
|
||||
/*
|
||||
$hookinfo = [
|
||||
'argv' => $arr
|
||||
];
|
||||
@@ -32,11 +39,15 @@ class Master {
|
||||
|
||||
$phpbin = get_config('system', 'phpbin', 'php');
|
||||
proc_run($phpbin, 'Zotlabs/Daemon/Master.php', $arr);
|
||||
*/
|
||||
}
|
||||
|
||||
static public function Release($argc, $argv) {
|
||||
cli_startup();
|
||||
|
||||
QueueWorker::Release($argv);
|
||||
return;
|
||||
/*
|
||||
$hookinfo = [
|
||||
'argv' => $argv
|
||||
];
|
||||
@@ -54,5 +65,6 @@ class Master {
|
||||
logger('Master: release: ' . json_encode($argv), LOGGER_ALL, LOG_DEBUG);
|
||||
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
|
||||
$cls::run($argc, $argv);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ class Notifier {
|
||||
static public $channel = null;
|
||||
static public $private = false;
|
||||
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if ($argc < 3) {
|
||||
@@ -88,7 +89,6 @@ class Notifier {
|
||||
logger('notifier: invoked: ' . print_r($argv, true), LOGGER_DEBUG);
|
||||
|
||||
$cmd = $argv[1];
|
||||
|
||||
$item_id = $argv[2];
|
||||
|
||||
if (!$item_id) {
|
||||
@@ -106,6 +106,12 @@ class Notifier {
|
||||
|
||||
$sys = get_sys_channel();
|
||||
$normal_mode = true;
|
||||
$upstream = false;
|
||||
$uplink = false;
|
||||
$target_item = null;
|
||||
$parent_item = null;
|
||||
$top_level_post = false;
|
||||
$relay_to_owner = false;
|
||||
|
||||
if ($cmd === 'keychange') {
|
||||
self::$channel = channelx_by_n($item_id);
|
||||
@@ -234,7 +240,7 @@ class Notifier {
|
||||
|
||||
$target_item = $r[0];
|
||||
|
||||
if (in_array($target_item['author']['xchan_network'], ['rss', 'anon'])) {
|
||||
if (in_array($target_item['author']['xchan_network'], ['rss', 'anon', 'token'])) {
|
||||
logger('notifier: target item author is not a fetchable actor', LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
@@ -264,14 +270,13 @@ class Notifier {
|
||||
// Check for non published items, but allow an exclusion for transmitting hidden file activities
|
||||
|
||||
if (intval($target_item['item_unpublished']) || intval($target_item['item_delayed']) ||
|
||||
intval($target_item['item_blocked']) ||
|
||||
(intval($target_item['item_hidden']) && ($target_item['obj_type'] !== ACTIVITY_OBJ_FILE))) {
|
||||
intval($target_item['item_blocked']) || intval($target_item['item_hidden'])) {
|
||||
logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
// follow/unfollow is for internal use only
|
||||
if (in_array($target_item['verb'], [ACTIVITY_FOLLOW, ACTIVITY_UNFOLLOW])) {
|
||||
if (in_array($target_item['verb'], ['Follow', 'Ignore', ACTIVITY_FOLLOW, ACTIVITY_UNFOLLOW])) {
|
||||
logger('not fowarding follow/unfollow note activity');
|
||||
return;
|
||||
}
|
||||
@@ -293,6 +298,11 @@ class Notifier {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($target_item['verb'] === ACTIVITY_SHARE) {
|
||||
// Provide correct representation across the wire. Internally this is treated as a comment.
|
||||
$target_item['parent_mid'] = $target_item['thr_parent'] = $target_item['mid'];
|
||||
}
|
||||
|
||||
if ($target_item['mid'] === $target_item['parent_mid']) {
|
||||
$parent_item = $target_item;
|
||||
$top_level_post = true;
|
||||
@@ -330,12 +340,7 @@ class Notifier {
|
||||
self::$encoded_item = json_decode($m, true);
|
||||
}
|
||||
else {
|
||||
self::$encoded_item = array_merge(['@context' => [
|
||||
ACTIVITYSTREAMS_JSONLD_REV,
|
||||
'https://w3id.org/security/v1',
|
||||
z_root() . ZOT_APSCHEMA_REV
|
||||
]], Activity::encode_activity($target_item)
|
||||
);
|
||||
self::$encoded_item = Activity::build_packet(Activity::encode_activity($target_item), self::$channel, false);
|
||||
}
|
||||
|
||||
logger('target_item: ' . print_r($target_item, true), LOGGER_DEBUG);
|
||||
@@ -351,8 +356,6 @@ class Notifier {
|
||||
// the hostname in the message_id and provides a second (fallback) opinion.
|
||||
|
||||
$relay_to_owner = (!$top_level_post && intval($target_item['item_origin']) && comment_local_origin($target_item));
|
||||
$uplink = false;
|
||||
$upstream = false;
|
||||
|
||||
// $cmd === 'relay' indicates the owner is sending it to the original recipients
|
||||
// don't allow the item in the relay command to relay to owner under any circumstances, it will loop
|
||||
@@ -370,6 +373,7 @@ class Notifier {
|
||||
|
||||
if (($relay_to_owner || $uplink) && ($cmd !== 'relay')) {
|
||||
logger('notifier: followup relay', LOGGER_DEBUG);
|
||||
// If the Parent item is an Announce the real owner is the parent author
|
||||
$sendto = (($uplink) ? $parent_item['source_xchan'] : $parent_item['owner_xchan']);
|
||||
self::$recipients = [$sendto];
|
||||
self::$private = true;
|
||||
@@ -402,9 +406,7 @@ class Notifier {
|
||||
self::$private = false;
|
||||
self::$recipients = collect_recipients($parent_item, self::$private);
|
||||
|
||||
// FIXME add any additional recipients such as mentions, etc.
|
||||
|
||||
if ($top_level_post) {
|
||||
if ($top_level_post && intval($target_item['item_wall'])) {
|
||||
// remove clones who will receive the post via sync
|
||||
self::$recipients = array_values(array_diff(self::$recipients, [$target_item['owner_xchan']]));
|
||||
}
|
||||
@@ -488,7 +490,7 @@ class Notifier {
|
||||
// Now we have collected recipients (except for external mentions, FIXME)
|
||||
// Let's reduce this to a set of hubs; checking that the site is not dead.
|
||||
|
||||
$hubs = q("select hubloc.*, site.site_crypto, site.site_flags, site.site_dead from hubloc left join site on site_url = hubloc_url
|
||||
$hubs = dbq("select hubloc.*, site.site_crypto, site.site_flags, site.site_dead from hubloc left join site on site_url = hubloc_url
|
||||
where hubloc_hash in (" . protect_sprintf(implode(',', self::$recipients)) . ")
|
||||
and hubloc_error = 0 and hubloc_deleted = 0"
|
||||
);
|
||||
@@ -496,6 +498,7 @@ class Notifier {
|
||||
// public posts won't make it to the local public stream unless there's a recipient on this site.
|
||||
// This code block sees if it's a public post and localhost is missing, and if so adds an entry for the local sys channel to the $hubs list
|
||||
|
||||
/* sys channel is now added in collect recipients
|
||||
if (!self::$private) {
|
||||
$found_localhost = false;
|
||||
if ($hubs) {
|
||||
@@ -516,6 +519,7 @@ class Notifier {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (!$hubs) {
|
||||
logger('notifier: no hubs', LOGGER_NORMAL, LOG_NOTICE);
|
||||
@@ -550,7 +554,9 @@ class Notifier {
|
||||
if (!array_key_exists($hub['hubloc_site_id'], $hub_env)) {
|
||||
$hub_env[$hub['hubloc_site_id']] = [];
|
||||
}
|
||||
$hub_env[$hub['hubloc_site_id']][] = $er;
|
||||
if (!in_array($er, $hub_env[$hub['hubloc_site_id']])) {
|
||||
$hub_env[$hub['hubloc_site_id']][] = $er;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -628,9 +634,22 @@ class Notifier {
|
||||
|
||||
// default: zot protocol
|
||||
|
||||
// Prevent zot6 delivery of group comment boosts, which are not required for conversational platforms.
|
||||
// ActivityPub conversational platforms may wish to filter these if they don't want or require them.
|
||||
// We will assume here that if $target_item exists and has a verb that it is an actual item structure
|
||||
// so we won't need to check the existence of the other item fields prior to evaluation.
|
||||
|
||||
// This shouldn't produce false positives on comment boosts that were generated on other platforms
|
||||
// because we won't be delivering them.
|
||||
|
||||
if (isset($target_item['verb']) && $target_item['verb'] === 'Announce' && $target_item['author_xchan'] === $target_item['owner_xchan'] && ! intval($target_item['item_thread_top'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$hash = new_uuid();
|
||||
|
||||
$env = (($hub_env && $hub_env[$hub['hubloc_site_id']]) ? $hub_env[$hub['hubloc_site_id']] : '');
|
||||
$env = $hub_env[$hub['hubloc_site_id']] ?? '';
|
||||
|
||||
if ((self::$private) && (!$env)) {
|
||||
continue;
|
||||
}
|
||||
@@ -667,7 +686,7 @@ class Notifier {
|
||||
|
||||
}
|
||||
|
||||
if ($normal_mode) {
|
||||
if ($normal_mode && is_array($target_item)) {
|
||||
// This wastes a process if there are no delivery hooks configured, so check this before launching the new process
|
||||
$x = q("select * from hook where hook = 'notifier_normal'");
|
||||
if ($x) {
|
||||
|
||||
@@ -15,61 +15,45 @@ class Onedirsync {
|
||||
$update_id = intval($argv[1]);
|
||||
|
||||
if (!$update_id) {
|
||||
logger('onedirsync: no update');
|
||||
logger('onedirsync: no update id');
|
||||
return;
|
||||
}
|
||||
|
||||
$r = q("select * from updates where ud_id = %d limit 1",
|
||||
$r = q("select * from updates where ud_id = %d",
|
||||
intval($update_id)
|
||||
);
|
||||
|
||||
if (!$r)
|
||||
return;
|
||||
|
||||
if (($r[0]['ud_flags'] & UPDATE_FLAGS_UPDATED) || (!$r[0]['ud_addr']))
|
||||
return;
|
||||
|
||||
// Have we probed this channel more recently than the other directory server
|
||||
// (where we received this update from) ?
|
||||
// If we have, we don't need to do anything except mark any older entries updated
|
||||
|
||||
$x = q("select * from updates where ud_addr = '%s' and ud_date > '%s' and ( ud_flags & %d )>0 order by ud_date desc limit 1",
|
||||
dbesc($r[0]['ud_addr']),
|
||||
dbesc($r[0]['ud_date']),
|
||||
intval(UPDATE_FLAGS_UPDATED)
|
||||
);
|
||||
if ($x) {
|
||||
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 and ud_date != '%s'",
|
||||
intval(UPDATE_FLAGS_UPDATED),
|
||||
dbesc($r[0]['ud_addr']),
|
||||
intval(UPDATE_FLAGS_UPDATED),
|
||||
dbesc($x[0]['ud_date'])
|
||||
);
|
||||
if (!$r) {
|
||||
logger('onedirsync: update id not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore doing an update if this ud_addr refers to a known dead hubloc
|
||||
|
||||
$h = q("select * from hubloc where hubloc_addr = '%s'",
|
||||
$h = q("select * from hubloc where hubloc_id_url = '%s' order by hubloc_id desc",
|
||||
dbesc($r[0]['ud_addr']),
|
||||
);
|
||||
|
||||
$h = Libzot::zot_record_preferred($h);
|
||||
|
||||
if (($h) && (($h['hubloc_status'] & HUBLOC_OFFLINE) || $h['hubloc_deleted'] || $h['hubloc_error'])) {
|
||||
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 ",
|
||||
intval(UPDATE_FLAGS_DELETED),
|
||||
dbesc($r[0]['ud_addr']),
|
||||
intval(UPDATE_FLAGS_UPDATED)
|
||||
);
|
||||
return;
|
||||
|
||||
// 2023-04-12: Try to update anyway since the info is not always correct
|
||||
// This might change after all directory servers run the new code.
|
||||
|
||||
// q("update updates set ud_flags = 9 where ud_hash = '%s' and ud_flags != 9",
|
||||
// dbesc($r[0]['ud_hash'])
|
||||
//);
|
||||
|
||||
// return;
|
||||
}
|
||||
|
||||
// we might have to pull this out some day, but for now update_directory_entry()
|
||||
// runs zot_finger() and is kind of zot specific
|
||||
|
||||
if ($h && $h['hubloc_network'] !== 'zot6')
|
||||
if ($h && $h['hubloc_network'] !== 'zot6') {
|
||||
return;
|
||||
}
|
||||
|
||||
Libzotdir::update_directory_entry($r[0]);
|
||||
|
||||
|
||||
@@ -30,14 +30,10 @@ class Onepoll {
|
||||
$sql_extra = ' and abook_feed = 0 ';
|
||||
}
|
||||
|
||||
$contacts = q("SELECT abook.*, xchan.*, account.*
|
||||
FROM abook LEFT JOIN account on abook_account = account_id left join xchan on xchan_hash = abook_xchan
|
||||
where abook_id = %d $sql_extra
|
||||
and abook_pending = 0 and abook_archived = 0 and abook_blocked = 0 and abook_ignored = 0
|
||||
AND (( account_flags = %d ) OR ( account_flags = %d )) limit 1",
|
||||
intval($contact_id),
|
||||
intval(ACCOUNT_OK),
|
||||
intval(ACCOUNT_UNVERIFIED)
|
||||
$contacts = q("SELECT abook.*, xchan.* FROM abook
|
||||
LEFT JOIN xchan ON xchan_hash = abook_xchan
|
||||
WHERE abook_id = %d",
|
||||
intval($contact_id)
|
||||
);
|
||||
|
||||
if (!$contacts) {
|
||||
@@ -46,6 +42,7 @@ class Onepoll {
|
||||
}
|
||||
|
||||
$contact = $contacts[0];
|
||||
|
||||
$importer_uid = $contact['abook_channel'];
|
||||
|
||||
$importer = channelx_by_n($importer_uid);
|
||||
@@ -53,7 +50,7 @@ class Onepoll {
|
||||
if (!$importer)
|
||||
return;
|
||||
|
||||
logger("onepoll: poll: ({$contact['id']}) IMPORTER: {$importer['xchan_name']}, CONTACT: {$contact['xchan_name']}");
|
||||
logger("onepoll: poll: ($contact_id) IMPORTER: {$importer['xchan_name']}, CONTACT: {$contact['xchan_name']}");
|
||||
|
||||
$last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= NULL_DATE))
|
||||
? datetime_convert('UTC', 'UTC', 'now - 7 days')
|
||||
@@ -63,12 +60,21 @@ class Onepoll {
|
||||
if ($contact['xchan_network'] === 'rss') {
|
||||
logger('onepoll: processing feed ' . $contact['xchan_name'], LOGGER_DEBUG);
|
||||
$alive = handle_feed($importer['channel_id'], $contact_id, $contact['xchan_hash']);
|
||||
if ($alive) {
|
||||
q("update abook set abook_connected = '%s' where abook_id = %d",
|
||||
|
||||
if (!$alive) {
|
||||
q("update abook set abook_updated = '%s' where abook_id = %d",
|
||||
dbesc(datetime_convert()),
|
||||
intval($contact['abook_id'])
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
q("update abook set abook_updated = '%s', abook_connected = '%s' where abook_id = %d",
|
||||
dbesc(datetime_convert()),
|
||||
dbesc(datetime_convert()),
|
||||
intval($contact['abook_id'])
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -162,6 +168,7 @@ class Onepoll {
|
||||
$AS = new ActivityStreams($message);
|
||||
if ($AS->is_valid() && is_array($AS->obj)) {
|
||||
$item = Activity::decode_note($AS);
|
||||
$item['item_fetched'] = true;
|
||||
Activity::store($importer, $contact['abook_xchan'], $AS, $item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,21 +17,7 @@ class Poller {
|
||||
}
|
||||
}
|
||||
|
||||
$interval = intval(get_config('system', 'poll_interval'));
|
||||
if (!$interval)
|
||||
$interval = ((get_config('system', 'delivery_interval') === false) ? 3 : intval(get_config('system', 'delivery_interval')));
|
||||
|
||||
// Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it.
|
||||
$lockfile = 'store/[data]/poller';
|
||||
if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600))
|
||||
&& (!get_config('system', 'override_poll_lockfile'))) {
|
||||
logger("poller: Already running");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a lockfile. Needs two vars, but $x doesn't need to contain anything.
|
||||
$x = '';
|
||||
file_put_contents($lockfile, $x);
|
||||
$interval = get_config('queueworker', 'queue_interval', 500000);
|
||||
|
||||
logger('poller: start');
|
||||
|
||||
@@ -63,6 +49,11 @@ class Poller {
|
||||
: ''
|
||||
);
|
||||
|
||||
$allow_feeds = get_config('system', 'feed_contacts');
|
||||
if(!$allow_feeds) {
|
||||
$sql_extra .= ' and abook_feed = 0 ';
|
||||
}
|
||||
|
||||
$randfunc = db_getfunc('RAND');
|
||||
|
||||
$contacts = q("SELECT abook.abook_updated, abook.abook_connected, abook.abook_feed,
|
||||
@@ -72,7 +63,7 @@ class Poller {
|
||||
account.account_lastlog, account.account_flags
|
||||
FROM abook LEFT JOIN xchan on abook_xchan = xchan_hash
|
||||
LEFT JOIN account on abook_account = account_id
|
||||
where abook_self = 0
|
||||
where abook_self = 0 and abook_pending = 0 and abook_archived = 0 and abook_blocked = 0 and abook_ignored = 0
|
||||
$sql_extra
|
||||
AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY $randfunc",
|
||||
intval(ACCOUNT_OK),
|
||||
@@ -93,13 +84,24 @@ class Poller {
|
||||
$min = intval(get_config('system', 'minimum_feedcheck_minutes'));
|
||||
if (!$min)
|
||||
$min = 60;
|
||||
$x = datetime_convert('UTC', 'UTC', "now - $min minutes");
|
||||
if ($c < $x) {
|
||||
Master::Summon(['Onepoll', $contact['abook_id']]);
|
||||
if ($interval)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
|
||||
if ($t !== $c) {
|
||||
// if the last fetch failed only attempt fetch once a day
|
||||
$min = 60 * 24;
|
||||
}
|
||||
|
||||
$x = datetime_convert('UTC', 'UTC', "now - $min minutes");
|
||||
|
||||
if ($t < $x) {
|
||||
Master::Summon(['Onepoll', $contact['abook_id']]);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if ($contact['xchan_network'] !== 'zot6')
|
||||
@@ -158,20 +160,22 @@ class Poller {
|
||||
continue;
|
||||
|
||||
Master::Summon(['Onepoll', $contact['abook_id']]);
|
||||
if ($interval)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$dirmode = intval(get_config('system', 'directory_mode'));
|
||||
|
||||
if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
|
||||
$r = q("SELECT u.ud_addr, u.ud_id, u.ud_last FROM updates AS u INNER JOIN (SELECT ud_addr, max(ud_id) AS ud_id FROM updates WHERE ( ud_flags & %d ) = 0 AND ud_addr != '' AND ( ud_last <= '%s' OR ud_last > %s - INTERVAL %s ) GROUP BY ud_addr) AS s ON s.ud_id = u.ud_id ",
|
||||
intval(UPDATE_FLAGS_UPDATED),
|
||||
$r = q("SELECT * FROM updates WHERE ud_update = 1 AND (ud_last = '%s' OR ud_last > %s - INTERVAL %s)",
|
||||
dbesc(NULL_DATE),
|
||||
db_utcnow(), db_quoteinterval('7 DAY')
|
||||
db_utcnow(),
|
||||
db_quoteinterval('7 DAY')
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
|
||||
@@ -181,19 +185,18 @@ class Poller {
|
||||
if ($rr['ud_last'] > NULL_DATE)
|
||||
if ($rr['ud_last'] > datetime_convert('UTC', 'UTC', 'now - 1 day'))
|
||||
continue;
|
||||
|
||||
Master::Summon(['Onedirsync', $rr['ud_id']]);
|
||||
if ($interval)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_config('system', 'lastpoll', datetime_convert());
|
||||
|
||||
//All done - clear the lockfile
|
||||
|
||||
@unlink($lockfile);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,77 +7,59 @@ use Zotlabs\Lib\Queue as LibQueue;
|
||||
class Queue {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
require_once('include/items.php');
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
if ($argc > 1)
|
||||
$queue_id = $argv[1];
|
||||
else
|
||||
$queue_id = EMPTY_STR;
|
||||
$queue_id = ($argc > 1) ? $argv[1] : '';
|
||||
|
||||
logger('queue: start');
|
||||
|
||||
// delete all queue items more than 3 days old
|
||||
// but first mark these sites dead if we haven't heard from them in a month
|
||||
|
||||
$r = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s",
|
||||
db_utcnow(), db_quoteinterval('3 DAY')
|
||||
$oldqItems = q("select outq_posturl, outq_hash from outq where outq_created < %s - INTERVAL %s",
|
||||
db_utcnow(),
|
||||
db_quoteinterval('3 DAY')
|
||||
);
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
$h = parse_url($rr['outq_posturl']);
|
||||
$desturl = $h['scheme'] . '://' . $h['host'] . (isset($h['port']) ? ':' . $h['port'] : '');
|
||||
|
||||
if ($oldqItems) {
|
||||
foreach ($oldqItems as $qItem) {
|
||||
$h = parse_url($qItem['outq_posturl']);
|
||||
$site_url = $h['scheme'] . '://' . $h['host'] . ((!empty($h['port'])) ? ':' . $h['port'] : '');
|
||||
q("update site set site_dead = 1 where site_dead = 0 and site_url = '%s' and site_update < %s - INTERVAL %s",
|
||||
dbesc($desturl),
|
||||
db_utcnow(), db_quoteinterval('1 MONTH')
|
||||
dbesc($site_url),
|
||||
db_utcnow(),
|
||||
db_quoteinterval('1 MONTH')
|
||||
);
|
||||
}
|
||||
|
||||
$old_hashes = ids_to_querystr($oldqItems, 'outq_hash', true);
|
||||
|
||||
logger('Removing ' . count($oldqItems) . ' old queue entries');
|
||||
dbq("DELETE FROM outq WHERE outq_hash IN ($old_hashes)");
|
||||
|
||||
}
|
||||
|
||||
q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s",
|
||||
db_utcnow(), db_quoteinterval('3 DAY')
|
||||
);
|
||||
$deliveries = [];
|
||||
|
||||
if ($queue_id) {
|
||||
$r = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1",
|
||||
$qItems = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1",
|
||||
dbesc($queue_id)
|
||||
);
|
||||
logger('queue deliver: ' . $qItems[0]['outq_hash'] . ' to ' . $qItems[0]['outq_posturl'], LOGGER_DEBUG);
|
||||
LibQueue::deliver($qItems[0]);
|
||||
}
|
||||
else {
|
||||
|
||||
// For the first 12 hours we'll try to deliver every 15 minutes
|
||||
// After that, we'll only attempt delivery once per hour.
|
||||
// This currently only handles the default queue drivers ('zot' or '') which we will group by posturl
|
||||
// so that we don't start off a thousand deliveries for a couple of dead hubs.
|
||||
// The zot driver will deliver everything destined for a single hub once contact is made (*if* contact is made).
|
||||
// Other drivers will have to do something different here and may need their own query.
|
||||
|
||||
// Note: this requires some tweaking as new posts to long dead hubs once a day will keep them in the
|
||||
// "every 15 minutes" category. We probably need to prioritise them when inserted into the queue
|
||||
// or just prior to this query based on recent and long-term delivery history. If we have good reason to believe
|
||||
// the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once
|
||||
// or twice a day.
|
||||
|
||||
$sqlrandfunc = db_getfunc('rand');
|
||||
|
||||
$r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1",
|
||||
$qItems = q("SELECT outq_hash FROM outq WHERE outq_scheduled < %s ",
|
||||
db_utcnow()
|
||||
);
|
||||
while ($r) {
|
||||
foreach ($r as $rv) {
|
||||
LibQueue::deliver($rv);
|
||||
}
|
||||
$r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1",
|
||||
db_utcnow()
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!$r)
|
||||
return;
|
||||
|
||||
foreach ($r as $rv) {
|
||||
LibQueue::deliver($rv);
|
||||
}
|
||||
if ($qItems) {
|
||||
foreach ($qItems as $qItem) {
|
||||
$deliveries[] = $qItem['outq_hash'];
|
||||
}
|
||||
|
||||
shuffle($deliveries);
|
||||
do_delivery($deliveries, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -73,5 +73,7 @@ class Thumbnail {
|
||||
|| (filectime(dbunescbin($attach['content']) . 'thumb') < (time() - 60)))) {
|
||||
$default_controller->Thumb($attach, $preview_style, $preview_width, $preview_height);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
34
Zotlabs/Daemon/Xchan_photo.php
Normal file
34
Zotlabs/Daemon/Xchan_photo.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
class Xchan_photo {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if ($argc != 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = hex2bin($argv[1]);
|
||||
$xchan = hex2bin($argv[2]);
|
||||
|
||||
$photos = import_xchan_photo($url, $xchan);
|
||||
if ($photos) {
|
||||
$result = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'",
|
||||
dbescdate(datetime_convert()),
|
||||
dbesc($photos[0]),
|
||||
dbesc($photos[1]),
|
||||
dbesc($photos[2]),
|
||||
dbesc($photos[3]),
|
||||
dbesc($xchan)
|
||||
);
|
||||
|
||||
if (! $result) {
|
||||
logger("xchan update failed for $url");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Zotlabs/Daemon/Zotconvo.php
Normal file
37
Zotlabs/Daemon/Zotconvo.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\Libzot;
|
||||
|
||||
class Zotconvo {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
logger('Zotconvo invoked: ' . print_r($argv, true));
|
||||
|
||||
if ($argc < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
$channels = explode(',', $argv[1]);
|
||||
if (!$channels) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mid = $argv[2];
|
||||
if (!$mid) {
|
||||
return;
|
||||
}
|
||||
|
||||
$force = $argv[3] ?? false;
|
||||
|
||||
foreach ($channels as $channel_id) {
|
||||
$channel = channelx_by_n($channel_id);
|
||||
Libzot::fetch_conversation($channel, $mid, $force);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
33
Zotlabs/Lib/ASCache.php
Normal file
33
Zotlabs/Lib/ASCache.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/**
|
||||
* A wrapper for the cache api
|
||||
*/
|
||||
|
||||
class ASCache {
|
||||
public static function isEnabled() {
|
||||
return Config::Get('system', 'as_object_cache_enabled', true);
|
||||
}
|
||||
|
||||
public static function getAge() {
|
||||
return Config::Get('system', 'as_object_cache_time', '10 MINUTE');
|
||||
}
|
||||
|
||||
public static function Get($key) {
|
||||
if (!self::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Cache::get($key, self::getAge());
|
||||
}
|
||||
|
||||
public static function Set($key, $value) {
|
||||
if (!self::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Cache::set($key, $value);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@ class ASCollection {
|
||||
$this->direction = $direction;
|
||||
$this->limit = $limit;
|
||||
|
||||
$data = null;
|
||||
|
||||
if (is_array($obj)) {
|
||||
$data = $obj;
|
||||
}
|
||||
@@ -147,4 +149,4 @@ class ASCollection {
|
||||
}
|
||||
logger('nextpage: ' . $this->nextpage, LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,17 @@ namespace Zotlabs\Lib;
|
||||
class AbConfig {
|
||||
|
||||
static public function Load($chan,$xhash,$family = '') {
|
||||
if($family)
|
||||
$where = '';
|
||||
|
||||
if($family) {
|
||||
$where = sprintf(" and cat = '%s' ",dbesc($family));
|
||||
}
|
||||
|
||||
$r = q("select * from abconfig where chan = %d and xchan = '%s' $where",
|
||||
intval($chan),
|
||||
dbesc($xhash)
|
||||
);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
@@ -21,7 +26,7 @@ class AbConfig {
|
||||
intval($chan),
|
||||
dbesc($xhash),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
dbesc($key)
|
||||
);
|
||||
if($r) {
|
||||
return ((preg_match('|^a:[0-9]+:{.*}$|s', $r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']);
|
||||
@@ -41,19 +46,19 @@ class AbConfig {
|
||||
dbesc($xhash),
|
||||
dbesc($family),
|
||||
dbesc($key),
|
||||
dbesc($dbvalue)
|
||||
dbesc($dbvalue)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$r = q("update abconfig set v = '%s' where chan = %d and xchan = '%s' and cat = '%s' and k = '%s' ",
|
||||
dbesc($dbvalue),
|
||||
dbesc($dbvalue),
|
||||
dbesc($chan),
|
||||
dbesc($xhash),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if($r)
|
||||
return $value;
|
||||
return false;
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
<?php
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
|
||||
class AccessList {
|
||||
|
||||
static function add($uid,$name,$public = 0) {
|
||||
|
||||
$ret = false;
|
||||
static function add($uid, $name, $public = 0) {
|
||||
|
||||
$ret = false;
|
||||
$hash = '';
|
||||
if ($uid && $name) {
|
||||
$r = self::byname($uid,$name); // check for dups
|
||||
$r = self::by_name($uid, $name); // check for dups
|
||||
if ($r !== false) {
|
||||
|
||||
// This could be a problem.
|
||||
// This could be a problem.
|
||||
// Let's assume we've just created a list which we once deleted
|
||||
// all the old members are gone, but the list remains so we don't break any security
|
||||
// access lists. What we're doing here is reviving the dead list, but old content which
|
||||
// was restricted to this list may now be seen by the new list members.
|
||||
// was restricted to this list may now be seen by the new list members.
|
||||
|
||||
$z = q("SELECT * FROM pgrp WHERE id = %d LIMIT 1",
|
||||
intval($r)
|
||||
);
|
||||
if(($z) && $z[0]['deleted']) {
|
||||
if (($z) && $z[0]['deleted']) {
|
||||
q('UPDATE pgrp SET deleted = 0 WHERE id = %d', intval($z[0]['id']));
|
||||
notice( t('A deleted list with this name was revived. Existing item permissions <strong>may</strong> apply to this list and any future members. If this is not what you intended, please create another list with a different name.') . EOL);
|
||||
notice(t('A deleted privacy group with this name was revived. Existing item permissions <strong>may</strong> apply to this privacy group and any future members. If this is not what you intended, please create another privacy group with a different name.') . EOL);
|
||||
}
|
||||
return true;
|
||||
$hash = self::by_id($uid, $r);
|
||||
return $hash;
|
||||
}
|
||||
|
||||
$hash = new_uuid();
|
||||
|
||||
$r = q("INSERT INTO pgrp ( hash, uid, visible, gname )
|
||||
$r = q("INSERT INTO pgrp ( hash, uid, visible, gname )
|
||||
VALUES( '%s', %d, %d, '%s' ) ",
|
||||
dbesc($hash),
|
||||
intval($uid),
|
||||
@@ -42,12 +41,12 @@ class AccessList {
|
||||
$ret = $r;
|
||||
}
|
||||
|
||||
Libsync::build_sync_packet($uid,null,true);
|
||||
return $ret;
|
||||
Libsync::build_sync_packet($uid, null, true);
|
||||
|
||||
return (($ret) ? $hash : $ret);
|
||||
}
|
||||
|
||||
|
||||
static function remove($uid,$name) {
|
||||
static function remove($uid, $name) {
|
||||
$ret = false;
|
||||
if ($uid && $name) {
|
||||
$r = q("SELECT id, hash FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1",
|
||||
@@ -55,36 +54,36 @@ class AccessList {
|
||||
dbesc($name)
|
||||
);
|
||||
if ($r) {
|
||||
$group_id = $r[0]['id'];
|
||||
$group_id = $r[0]['id'];
|
||||
$group_hash = $r[0]['hash'];
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// remove group from default posting lists
|
||||
$r = q("SELECT channel_default_group, channel_allow_gid, channel_deny_gid FROM channel WHERE channel_id = %d LIMIT 1",
|
||||
intval($uid)
|
||||
intval($uid)
|
||||
);
|
||||
if ($r) {
|
||||
$user_info = array_shift($r);
|
||||
$change = false;
|
||||
$change = false;
|
||||
|
||||
if ($user_info['channel_default_group'] == $group_hash) {
|
||||
$user_info['channel_default_group'] = '';
|
||||
$change = true;
|
||||
$change = true;
|
||||
}
|
||||
if (strpos($user_info['channel_allow_gid'], '<' . $group_hash . '>') !== false) {
|
||||
$user_info['channel_allow_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_allow_gid']);
|
||||
$change = true;
|
||||
$change = true;
|
||||
}
|
||||
if (strpos($user_info['channel_deny_gid'], '<' . $group_hash . '>') !== false) {
|
||||
$user_info['channel_deny_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_deny_gid']);
|
||||
$change = true;
|
||||
$change = true;
|
||||
}
|
||||
|
||||
if ($change) {
|
||||
q("UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s'
|
||||
q("UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s'
|
||||
WHERE channel_id = %d",
|
||||
intval($user_info['channel_default_group']),
|
||||
dbesc($user_info['channel_allow_gid']),
|
||||
@@ -110,16 +109,16 @@ class AccessList {
|
||||
|
||||
}
|
||||
|
||||
Libsync::build_sync_packet($uid,null,true);
|
||||
Libsync::build_sync_packet($uid, null, true);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// returns the integer id of an access group owned by $uid and named $name
|
||||
// or false.
|
||||
|
||||
static function byname($uid,$name) {
|
||||
if (! ($uid && $name)) {
|
||||
|
||||
static function by_name($uid, $name) {
|
||||
if (!($uid && $name)) {
|
||||
return false;
|
||||
}
|
||||
$r = q("SELECT id FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1",
|
||||
@@ -132,11 +131,11 @@ class AccessList {
|
||||
return false;
|
||||
}
|
||||
|
||||
static function by_id($uid,$id) {
|
||||
if (! ($uid && $id)) {
|
||||
static function by_id($uid, $id) {
|
||||
if (!($uid && $id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$r = q("SELECT * FROM pgrp WHERE uid = %d AND id = %d and deleted = 0",
|
||||
intval($uid),
|
||||
intval($id)
|
||||
@@ -147,10 +146,8 @@ class AccessList {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static function rec_byhash($uid,$hash) {
|
||||
if (! ( $uid && $hash)) {
|
||||
static function by_hash($uid, $hash) {
|
||||
if (!($uid && $hash)) {
|
||||
return false;
|
||||
}
|
||||
$r = q("SELECT * FROM pgrp WHERE uid = %d AND hash = '%s' LIMIT 1",
|
||||
@@ -163,46 +160,46 @@ class AccessList {
|
||||
return false;
|
||||
}
|
||||
|
||||
static function member_remove($uid, $name, $member, $gid = 0) {
|
||||
if (!$gid) {
|
||||
$gid = self::by_name($uid, $name);
|
||||
}
|
||||
|
||||
static function member_remove($uid,$name,$member) {
|
||||
$gid = self::byname($uid,$name);
|
||||
if (! $gid) {
|
||||
return false;
|
||||
}
|
||||
if (! ($uid && $gid && $member)) {
|
||||
if (!($uid && $gid && $member)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$r = q("DELETE FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' ",
|
||||
intval($uid),
|
||||
intval($gid),
|
||||
dbesc($member)
|
||||
);
|
||||
|
||||
Libsync::build_sync_packet($uid,null,true);
|
||||
Libsync::build_sync_packet($uid, null, true);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
||||
static function member_add($uid,$name,$member,$gid = 0) {
|
||||
if (! $gid) {
|
||||
$gid = self::byname($uid,$name);
|
||||
static function member_add($uid, $name, $member, $gid = 0) {
|
||||
if (!$gid) {
|
||||
$gid = self::by_name($uid, $name);
|
||||
}
|
||||
if (! ($gid && $uid && $member)) {
|
||||
if (!($gid && $uid && $member)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$r = q("SELECT * FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1",
|
||||
$r = q("SELECT * FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1",
|
||||
intval($uid),
|
||||
intval($gid),
|
||||
dbesc($member)
|
||||
);
|
||||
if ($r) {
|
||||
return true; // You might question this, but
|
||||
// we indicate success because the group member was in fact created
|
||||
// -- It was just created at another time
|
||||
return true;
|
||||
// You might question this, but
|
||||
// we indicate success because the group member was in fact created
|
||||
// -- It was just created at another time
|
||||
}
|
||||
else {
|
||||
else {
|
||||
$r = q("INSERT INTO pgrp_member (uid, gid, xchan)
|
||||
VALUES( %d, %d, '%s' ) ",
|
||||
intval($uid),
|
||||
@@ -210,15 +207,14 @@ class AccessList {
|
||||
dbesc($member)
|
||||
);
|
||||
}
|
||||
Libsync::build_sync_packet($uid,null,true);
|
||||
Libsync::build_sync_packet($uid, null, true);
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
||||
static function members($uid, $gid) {
|
||||
$ret = [];
|
||||
if (intval($gid)) {
|
||||
$r = q("SELECT * FROM pgrp_member
|
||||
$r = q("SELECT * FROM pgrp_member
|
||||
LEFT JOIN abook ON abook_xchan = pgrp_member.xchan left join xchan on xchan_hash = abook_xchan
|
||||
WHERE gid = %d AND abook_channel = %d and pgrp_member.uid = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC ",
|
||||
intval($gid),
|
||||
@@ -232,7 +228,7 @@ class AccessList {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static function members_xchan($uid,$gid) {
|
||||
static function members_xchan($uid, $gid) {
|
||||
$ret = [];
|
||||
if (intval($gid)) {
|
||||
$r = q("SELECT xchan FROM pgrp_member WHERE gid = %d AND uid = %d",
|
||||
@@ -248,99 +244,75 @@ class AccessList {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static function members_profile_xchan($uid,$gid) {
|
||||
static function profile_members_xchan($uid,$gid) {
|
||||
$ret = [];
|
||||
if (intval($gid)) {
|
||||
|
||||
if(intval($gid)) {
|
||||
$r = q("SELECT abook_xchan as xchan from abook left join profile on abook_profile = profile_guid where profile.id = %d and profile.uid = %d",
|
||||
intval($gid),
|
||||
intval($uid)
|
||||
);
|
||||
if ($r) {
|
||||
foreach($r as $rv) {
|
||||
$ret[] = $rv['xchan'];
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$ret[] = $rr['xchan'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static function select($uid, $options) {
|
||||
|
||||
$selected = $options['selected'] ?? '';
|
||||
$form_id = $options['form_id'] ?? 'accesslist_select';
|
||||
$label = $options['label'] ?? t('Select a privacy group');
|
||||
$before = $options['before'] ?? [];
|
||||
$after = $options['after'] ?? [];
|
||||
|
||||
|
||||
static function select($uid,$group = '') {
|
||||
|
||||
$grps = [];
|
||||
$o = '';
|
||||
|
||||
$grps[] = [
|
||||
'name' => '',
|
||||
'id' => '0',
|
||||
'selected' => false
|
||||
];
|
||||
|
||||
if ($before) {
|
||||
$grps[] = $before;
|
||||
}
|
||||
|
||||
$r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
|
||||
intval($uid)
|
||||
);
|
||||
$grps[] = [ 'name' => '', 'hash' => '0', 'selected' => '' ];
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
$grps[] = [ 'name' => $rr['gname'], 'id' => $rr['hash'], 'selected' => (($group == $rr['hash']) ? 'true' : '') ];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return replace_macros(get_markup_template('group_selection.tpl'), [
|
||||
'$label' => t('Add new connections to this access list'),
|
||||
'$groups' => $grps
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
static function widget($every="connections",$each="lists",$edit = false, $group_id = 0, $cid = '',$mode = 1) {
|
||||
|
||||
$o = '';
|
||||
|
||||
$groups = [];
|
||||
|
||||
$r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
|
||||
intval($_SESSION['uid'])
|
||||
);
|
||||
$member_of = [];
|
||||
if ($cid) {
|
||||
$member_of = self::containing(local_channel(),$cid);
|
||||
}
|
||||
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
$selected = (($group_id == $rr['id']) ? ' group-selected' : '');
|
||||
|
||||
if ($edit) {
|
||||
$groupedit = [ 'href' => "lists/".$rr['id'], 'title' => t('edit') ];
|
||||
}
|
||||
else {
|
||||
$groupedit = null;
|
||||
}
|
||||
|
||||
$groups[] = [
|
||||
'id' => $rr['id'],
|
||||
'enc_cid' => base64url_encode($cid),
|
||||
'cid' => $cid,
|
||||
'text' => $rr['gname'],
|
||||
'selected' => $selected,
|
||||
'href' => (($mode == 0) ? $each.'?f=&gid='.$rr['id'] : $each."/".$rr['id']) . ((x($_GET,'new')) ? '&new=' . $_GET['new'] : '') . ((x($_GET,'order')) ? '&order=' . $_GET['order'] : ''),
|
||||
'edit' => $groupedit,
|
||||
'ismember' => in_array($rr['id'],$member_of),
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$grps[] = [
|
||||
'name' => $rr['gname'],
|
||||
'id' => $rr['hash'],
|
||||
'selected' => ($selected == $rr['hash'])
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return replace_macros(get_markup_template('group_side.tpl'), [
|
||||
'$title' => t('Lists'),
|
||||
'$edittext' => t('Edit list'),
|
||||
'$createtext' => t('Create new list'),
|
||||
'$ungrouped' => (($every === 'contacts') ? t('Channels not in any access list') : ''),
|
||||
'$groups' => $groups,
|
||||
'$add' => t('add'),
|
||||
]);
|
||||
|
||||
if ($after) {
|
||||
$grps[] = $after;
|
||||
}
|
||||
|
||||
logger('select: ' . print_r($grps,true), LOGGER_DATA);
|
||||
|
||||
$o = replace_macros(get_markup_template('group_selection.tpl'), array(
|
||||
'$label' => $label,
|
||||
'$form_id' => $form_id,
|
||||
'$groups' => $grps
|
||||
));
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
static function expand($g) {
|
||||
if (! (is_array($g) && count($g))) {
|
||||
if (!(is_array($g) && count($g))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -350,8 +322,8 @@ class AccessList {
|
||||
// private profile linked virtual groups
|
||||
|
||||
foreach ($g as $gv) {
|
||||
if (substr($gv,0,3) === 'vp.') {
|
||||
$profile_hash = substr($gv,3);
|
||||
if (substr($gv, 0, 3) === 'vp.') {
|
||||
$profile_hash = substr($gv, 3);
|
||||
if ($profile_hash) {
|
||||
$r = q("select abook_xchan from abook where abook_profile = '%s'",
|
||||
dbesc($profile_hash)
|
||||
@@ -366,10 +338,10 @@ class AccessList {
|
||||
else {
|
||||
$x[] = $gv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($x) {
|
||||
stringify_array_elms($x,true);
|
||||
stringify_array_elms($x, true);
|
||||
$groups = implode(',', $x);
|
||||
if ($groups) {
|
||||
$r = q("SELECT xchan FROM pgrp_member WHERE gid IN ( select id from pgrp where hash in ( $groups ))");
|
||||
@@ -383,9 +355,8 @@ class AccessList {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
static function member_of($c) {
|
||||
$r = q("SELECT pgrp.gname, pgrp.id FROM pgrp LEFT JOIN pgrp_member ON pgrp_member.gid = pgrp.id
|
||||
$r = q("SELECT pgrp.gname, pgrp.id FROM pgrp LEFT JOIN pgrp_member ON pgrp_member.gid = pgrp.id
|
||||
WHERE pgrp_member.xchan = '%s' AND pgrp.deleted = 0 ORDER BY pgrp.gname ASC ",
|
||||
dbesc($c)
|
||||
);
|
||||
@@ -393,7 +364,7 @@ class AccessList {
|
||||
return $r;
|
||||
}
|
||||
|
||||
static function containing($uid,$c) {
|
||||
static function containing($uid, $c) {
|
||||
|
||||
$r = q("SELECT gid FROM pgrp_member WHERE uid = %d AND pgrp_member.xchan = '%s' ",
|
||||
intval($uid),
|
||||
@@ -405,7 +376,8 @@ class AccessList {
|
||||
foreach ($r as $rv)
|
||||
$ret[] = $rv['gid'];
|
||||
}
|
||||
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,8 +11,10 @@ class ActivityStreams {
|
||||
|
||||
public $raw = null;
|
||||
public $data = null;
|
||||
public $meta = null;
|
||||
public $valid = false;
|
||||
public $deleted = false;
|
||||
public $portable_id = null;
|
||||
public $id = '';
|
||||
public $parent_id = '';
|
||||
public $type = '';
|
||||
@@ -22,10 +24,11 @@ class ActivityStreams {
|
||||
public $origin = null;
|
||||
public $owner = null;
|
||||
public $signer = null;
|
||||
public $ldsig = null;
|
||||
public $sig = null;
|
||||
public $sigok = false;
|
||||
public $recips = null;
|
||||
public $raw_recips = null;
|
||||
public $saved_recips = null;
|
||||
|
||||
/**
|
||||
* @brief Constructor for ActivityStreams.
|
||||
@@ -34,12 +37,17 @@ class ActivityStreams {
|
||||
*
|
||||
* @param string $string
|
||||
*/
|
||||
function __construct($string) {
|
||||
function __construct($string, $portable_id = null) {
|
||||
|
||||
if(!$string)
|
||||
return;
|
||||
|
||||
$this->raw = $string;
|
||||
$this->portable_id = $portable_id;
|
||||
|
||||
if (is_array($string)) {
|
||||
$this->data = $string;
|
||||
$this->raw = json_encode($string, JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
else {
|
||||
$this->data = json_decode($string, true);
|
||||
@@ -56,18 +64,21 @@ class ActivityStreams {
|
||||
if ($ret['signer']) {
|
||||
$saved = json_encode($this->data, JSON_UNESCAPED_SLASHES);
|
||||
$this->data = $tmp;
|
||||
$this->data['signer'] = $ret['signer'];
|
||||
$this->data['signed_data'] = $saved;
|
||||
$this->meta['signer'] = $ret['signer'];
|
||||
$this->meta['signed_data'] = $saved;
|
||||
if ($ret['hubloc']) {
|
||||
$this->data['hubloc'] = $ret['hubloc'];
|
||||
$this->meta['hubloc'] = $ret['hubloc'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This indicates only that we have sucessfully decoded JSON.
|
||||
$this->valid = true;
|
||||
|
||||
if (array_key_exists('type', $this->data) && array_key_exists('actor', $this->data) && array_key_exists('object', $this->data)) {
|
||||
// Special handling for Mastodon "delete actor" activities which will often fail to verify
|
||||
// because the key cannot be fetched. We will catch this condition elsewhere.
|
||||
if (is_array($this->data) && array_key_exists('type', $this->data) && array_key_exists('actor', $this->data) && array_key_exists('object', $this->data)) {
|
||||
if ($this->data['type'] === 'Delete' && $this->data['actor'] === $this->data['object']) {
|
||||
$this->deleted = $this->data['actor'];
|
||||
$this->valid = false;
|
||||
@@ -76,8 +87,18 @@ class ActivityStreams {
|
||||
|
||||
}
|
||||
|
||||
// Attempt to assemble an Activity from what we were given.
|
||||
if ($this->is_valid()) {
|
||||
$this->id = $this->get_property_obj('id');
|
||||
$this->id = $this->get_property_obj('id');
|
||||
|
||||
if (!$this->id) {
|
||||
logger('Data with mmissing id: ' . print_r($this->data, true));
|
||||
return;
|
||||
}
|
||||
|
||||
// cache for future use
|
||||
ASCache::Set($this->id, 'json:' . $this->raw);
|
||||
|
||||
$this->type = $this->get_primary_type();
|
||||
$this->actor = $this->get_actor('actor', '', '');
|
||||
$this->obj = $this->get_compound_property('object');
|
||||
@@ -85,11 +106,19 @@ class ActivityStreams {
|
||||
$this->origin = $this->get_compound_property('origin');
|
||||
$this->recips = $this->collect_recips();
|
||||
|
||||
$this->ldsig = $this->get_compound_property('signature');
|
||||
if ($this->ldsig) {
|
||||
$this->signer = $this->get_compound_property('creator', $this->ldsig);
|
||||
if ($this->signer && is_array($this->signer) && array_key_exists('publicKey', $this->signer) && is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) {
|
||||
$this->sigok = LDSignatures::verify($this->data, $this->signer['publicKey']['publicKeyPem']);
|
||||
$this->sig = $this->get_compound_property('proof');
|
||||
if ($this->sig) {
|
||||
$this->checkEddsaSignature(); // will set signer and sigok if everything works out
|
||||
}
|
||||
|
||||
// Try LDSignatures if edsig failed
|
||||
if (!$this->sigok) {
|
||||
$this->sig = $this->get_compound_property('signature');
|
||||
if ($this->sig) {
|
||||
$this->signer = $this->get_actor('creator', $this->sig);
|
||||
if ($this->signer && is_array($this->signer) && array_key_exists('publicKey', $this->signer) && is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) {
|
||||
$this->sigok = LDSignatures::verify($this->data, $this->signer['publicKey']['publicKeyPem']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,25 +130,34 @@ class ActivityStreams {
|
||||
}
|
||||
}
|
||||
|
||||
// fetch recursive or embedded activities
|
||||
// Fetch recursive or embedded activities
|
||||
|
||||
if ($this->obj && is_array($this->obj) && array_key_exists('object', $this->obj)) {
|
||||
$this->obj['object'] = $this->get_compound_property($this->obj['object']);
|
||||
$this->obj['object'] = $this->get_compound_property('object', $this->obj);
|
||||
}
|
||||
|
||||
if ($this->obj && is_array($this->obj) && $this->obj['actor'])
|
||||
// Enumerate and store actors in referenced objects
|
||||
|
||||
if ($this->obj && is_array($this->obj) && isset($this->obj['actor'])) {
|
||||
$this->obj['actor'] = $this->get_actor('actor', $this->obj);
|
||||
if ($this->tgt && is_array($this->tgt) && $this->tgt['actor'])
|
||||
$this->tgt['actor'] = $this->get_actor('actor', $this->tgt);
|
||||
|
||||
$this->parent_id = $this->get_property_obj('inReplyTo');
|
||||
|
||||
if ((!$this->parent_id) && is_array($this->obj)) {
|
||||
$this->parent_id = $this->obj['inReplyTo'];
|
||||
}
|
||||
if ((!$this->parent_id) && is_array($this->obj)) {
|
||||
|
||||
if ($this->tgt && is_array($this->tgt) && isset($this->tgt['actor'])) {
|
||||
$this->tgt['actor'] = $this->get_actor('actor', $this->tgt);
|
||||
}
|
||||
|
||||
// Determine if this is a followup or response activity
|
||||
|
||||
$this->parent_id = ((is_array($this->get_property_obj('inReplyTo'))) ? $this->get_property_obj('inReplyTo')['id'] : $this->get_property_obj('inReplyTo'));
|
||||
|
||||
if (!$this->parent_id && isset($this->obj['inReplyTo'])) {
|
||||
$this->parent_id = ((is_array($this->obj['inReplyTo'])) ? $this->obj['inReplyTo']['id'] : $this->obj['inReplyTo']);
|
||||
}
|
||||
|
||||
if (!$this->parent_id && isset($this->obj['id'])) {
|
||||
$this->parent_id = $this->obj['id'];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,36 +174,59 @@ class ActivityStreams {
|
||||
$this->saved_recips = $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get single property from Activity object
|
||||
*
|
||||
* @param string $property
|
||||
* @param mixed $default return value if property or object not set
|
||||
* or object is a string id which could not be fetched.
|
||||
* @return mixed
|
||||
*/
|
||||
public function objprop(string $property, mixed $default = false): mixed {
|
||||
$x = $this->get_property_obj($property, $this->obj);
|
||||
return (isset($x)) ? $x : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Collects all recipients.
|
||||
*
|
||||
* @param string $base
|
||||
* @param mixed $base
|
||||
* @param string $namespace (optional) default empty
|
||||
* @return array
|
||||
*/
|
||||
function collect_recips($base = '', $namespace = '') {
|
||||
$x = [];
|
||||
public function collect_recips(mixed $base = '', string $namespace = ''): array {
|
||||
$result = [];
|
||||
$tmp = [];
|
||||
|
||||
$fields = ['to', 'cc', 'bto', 'bcc', 'audience'];
|
||||
foreach ($fields as $f) {
|
||||
$y = $this->get_compound_property($f, $base, $namespace);
|
||||
if ($y) {
|
||||
if (!is_array($this->raw_recips)) {
|
||||
$this->raw_recips = [];
|
||||
}
|
||||
|
||||
if (!is_array($y)) {
|
||||
$y = [$y];
|
||||
}
|
||||
$this->raw_recips[$f] = $y;
|
||||
$x = array_merge($x, $y);
|
||||
foreach ($fields as $field) {
|
||||
// don't expand these yet
|
||||
$values = $this->get_property_obj($field, $base, $namespace);
|
||||
if ($values) {
|
||||
$values = force_array($values);
|
||||
$tmp[$field] = $values;
|
||||
$result = array_values(array_unique(array_merge($result, $values)));
|
||||
}
|
||||
// Merge the object recipients if they exist.
|
||||
$values = $this->objprop($field);
|
||||
if ($values) {
|
||||
$values = force_array($values);
|
||||
$tmp[$field] = ((isset($tmp[$field])) ? array_merge($tmp[$field], $values) : $values);
|
||||
$result = array_values(array_unique(array_merge($result, $values)));
|
||||
}
|
||||
// remove duplicates
|
||||
if (isset($tmp[$field])) {
|
||||
$tmp[$field] = array_values(array_unique($tmp[$field]));
|
||||
}
|
||||
}
|
||||
// not yet ready for prime time
|
||||
// $x = $this->expand($x,$base,$namespace);
|
||||
return $x;
|
||||
$this->raw_recips = $tmp;
|
||||
|
||||
// not yet ready for prime time
|
||||
// $result = $this->expand($result,$base,$namespace);
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
function expand($arr, $base = '', $namespace = '') {
|
||||
$ret = [];
|
||||
|
||||
@@ -269,12 +330,27 @@ class ActivityStreams {
|
||||
* @return NULL|mixed
|
||||
*/
|
||||
|
||||
function fetch_property($url) {
|
||||
return self::fetch($url);
|
||||
}
|
||||
function fetch_property($url, $channel = null) {
|
||||
$x = null;
|
||||
|
||||
static function fetch($url, $channel = null) {
|
||||
return Activity::fetch($url, $channel);
|
||||
if (str_starts_with($url, z_root() . '/item/')) {
|
||||
$x = Activity::fetch_local($url, $this->portable_id ?? '');
|
||||
logger('local: ' . print_r($x,true));
|
||||
}
|
||||
|
||||
if (!$x) {
|
||||
$x = Activity::fetch($url, $channel);
|
||||
if ($x === null && strpos($url, '/channel/')) {
|
||||
// look for other nomadic channels which might be alive
|
||||
$zf = Zotfinger::exec($url, $channel);
|
||||
if ($zf) {
|
||||
$url = $zf['signature']['signer'];
|
||||
$x = Activity::fetch($url, $channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
static function is_an_actor($s) {
|
||||
@@ -285,7 +361,7 @@ class ActivityStreams {
|
||||
if (!$s) {
|
||||
return false;
|
||||
}
|
||||
return (in_array($s, ['Like', 'Dislike', 'Flag', 'Block', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact']));
|
||||
return (in_array($s, ['Announce', 'Like', 'Dislike', 'Flag', 'Block', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact']));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,20 +375,25 @@ class ActivityStreams {
|
||||
|
||||
function get_actor($property, $base = '', $namespace = '') {
|
||||
$x = $this->get_property_obj($property, $base, $namespace);
|
||||
|
||||
if ($this->is_url($x)) {
|
||||
$y = Activity::get_cached_actor($x);
|
||||
$y = Activity::get_actor($x);
|
||||
if ($y) {
|
||||
return $y;
|
||||
}
|
||||
}
|
||||
|
||||
$actor = $this->get_compound_property($property, $base, $namespace, true);
|
||||
|
||||
if (is_array($actor) && self::is_an_actor($actor['type'])) {
|
||||
if (array_key_exists('id', $actor) && (!array_key_exists('inbox', $actor))) {
|
||||
$actor = $this->fetch_property($actor['id']);
|
||||
}
|
||||
return $actor;
|
||||
}
|
||||
return null;
|
||||
|
||||
return Activity::get_unknown_actor($this->data);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -327,13 +408,26 @@ class ActivityStreams {
|
||||
*/
|
||||
function get_compound_property($property, $base = '', $namespace = '', $first = false) {
|
||||
$x = $this->get_property_obj($property, $base, $namespace);
|
||||
|
||||
if ($this->is_url($x)) {
|
||||
$y = $this->fetch_property($x);
|
||||
$cached = ASCache::Get($x);
|
||||
if ($cached) {
|
||||
// logger('AS cached: ' . $x);
|
||||
$y = unserialise($cached);
|
||||
}
|
||||
else {
|
||||
// logger('AS fetching: ' . $x);
|
||||
$y = $this->fetch_property($x);
|
||||
if ($y) {
|
||||
ASCache::Set($x, serialise($y));
|
||||
}
|
||||
}
|
||||
if (is_array($y)) {
|
||||
$x = $y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// verify and unpack JSalmon signature if present
|
||||
|
||||
if (is_array($x) && array_key_exists('signed', $x)) {
|
||||
@@ -343,10 +437,10 @@ class ActivityStreams {
|
||||
if ($ret['signer']) {
|
||||
$saved = json_encode($x, JSON_UNESCAPED_SLASHES);
|
||||
$x = $tmp;
|
||||
$x['signer'] = $ret['signer'];
|
||||
$x['signed_data'] = $saved;
|
||||
$x['meta']['signer'] = $ret['signer'];
|
||||
$x['meta']['signed_data'] = $saved;
|
||||
if ($ret['hubloc']) {
|
||||
$x['hubloc'] = $ret['hubloc'];
|
||||
$x['meta']['hubloc'] = $ret['hubloc'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -417,16 +511,74 @@ class ActivityStreams {
|
||||
|
||||
static function get_accept_header_string($channel = null) {
|
||||
|
||||
$ret = '';
|
||||
|
||||
$hookdata = [];
|
||||
if ($channel)
|
||||
$hookdata['channel'] = $channel;
|
||||
|
||||
$hookdata['data'] = 'application/x-zot-activity+json';
|
||||
$hookdata['data'] = ['application/x-zot-activity+json'];
|
||||
|
||||
call_hooks('get_accept_header_string', $hookdata);
|
||||
|
||||
return $hookdata['data'];
|
||||
$ret = implode(', ', $hookdata['data']);
|
||||
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
public function checkEddsaSignature() {
|
||||
$signer = $this->get_property_obj('verificationMethod', $this->sig);
|
||||
|
||||
$parseUrl = parse_url($signer);
|
||||
|
||||
if (isset($parseUrl['fragment'])) {
|
||||
if (str_starts_with($parseUrl['fragment'], 'z6Mk')) {
|
||||
$publicKey = $parseUrl['fragment'];
|
||||
}
|
||||
unset($parseUrl['fragment']);
|
||||
}
|
||||
|
||||
if (isset($parseUrl['query'])) {
|
||||
unset($parseUrl['query']);
|
||||
}
|
||||
|
||||
$url = unparse_url($parseUrl);
|
||||
|
||||
$hublocs = Activity::get_actor_hublocs($url);
|
||||
|
||||
$hasStoredKey = false;
|
||||
if ($hublocs) {
|
||||
foreach ($hublocs as $hubloc) {
|
||||
if ($publicKey && $hubloc['xchan_epubkey'] === $publicKey) {
|
||||
$hasStoredKey = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$hasStoredKey) {
|
||||
$this->signer = Activity::get_actor($url);
|
||||
|
||||
if (isset($this->signer['assertionMethod'])) {
|
||||
if (!isset($this->signer['assertionMethod'][0])) {
|
||||
$this->signer['assertionMethod'] = [$this->signer['assertionMethod']];
|
||||
}
|
||||
|
||||
foreach($this->signer['assertionMethod'] as $am) {
|
||||
if ($url === $am['controller'] &&
|
||||
$am['type'] === 'Multikey' &&
|
||||
str_starts_with($am['publicKeyMultibase'], 'z6Mk')
|
||||
) {
|
||||
$publicKey = $am['publicKeyMultibase'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($publicKey) {
|
||||
$this->sigok = (new JcsEddsa2022)->verify($this->data, $publicKey);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -67,17 +67,15 @@ class Apps {
|
||||
static public function get_base_apps() {
|
||||
$x = get_config('system','base_apps',[
|
||||
'Connections',
|
||||
'Contact Roles',
|
||||
'Network',
|
||||
'Settings',
|
||||
'Files',
|
||||
'Channel Home',
|
||||
'View Profile',
|
||||
'Channel',
|
||||
'Photos',
|
||||
'Calendar',
|
||||
'Directory',
|
||||
'Search',
|
||||
'Help',
|
||||
'Profile Photo',
|
||||
'HQ',
|
||||
'Post'
|
||||
]);
|
||||
@@ -161,7 +159,7 @@ class Apps {
|
||||
foreach(self::$available_apps as $iapp) {
|
||||
if($iapp['app_id'] == hash('whirlpool',$app['name'])) {
|
||||
$notfound = false;
|
||||
if(($iapp['app_version'] !== $app['version'])
|
||||
if((isset($app['version']) && $iapp['app_version'] !== $app['version'])
|
||||
|| ($app['plugin'] && (! $iapp['app_plugin']))) {
|
||||
return intval($iapp['app_id']);
|
||||
}
|
||||
@@ -238,6 +236,7 @@ class Apps {
|
||||
$ret['photo'] = $baseurl . '/' . get_default_profile_photo(80);
|
||||
|
||||
$ret['type'] = 'system';
|
||||
$ret['plugin'] = '';
|
||||
|
||||
foreach($ret as $k => $v) {
|
||||
if(strpos($v,'http') === 0) {
|
||||
@@ -346,15 +345,13 @@ class Apps {
|
||||
'Files' => t('Files'),
|
||||
'Webpages' => t('Webpages'),
|
||||
'Wiki' => t('Wiki'),
|
||||
'Channel Home' => t('Channel Home'),
|
||||
'Channel' => t('Channel'),
|
||||
'View Profile' => t('View Profile'),
|
||||
'Photos' => t('Photos'),
|
||||
'Calendar' => t('Calendar'),
|
||||
'Directory' => t('Directory'),
|
||||
'Help' => t('Help'),
|
||||
'Mail' => t('Mail'),
|
||||
'Mood' => t('Mood'),
|
||||
'Poke' => t('Poke'),
|
||||
'Chat' => t('Chat'),
|
||||
'Search' => t('Search'),
|
||||
'Probe' => t('Probe'),
|
||||
@@ -377,10 +374,10 @@ class Apps {
|
||||
'OAuth Apps Manager' => t('OAuth Apps Manager'),
|
||||
'OAuth2 Apps Manager' => t('OAuth2 Apps Manager'),
|
||||
'PDL Editor' => t('PDL Editor'),
|
||||
'Permission Categories' => t('Permission Categories'),
|
||||
'Contact Roles' => t('Contact Roles'),
|
||||
'Public Stream' => t('Public Stream'),
|
||||
'My Chatrooms' => t('My Chatrooms'),
|
||||
'Channel Export' => t('Channel Export'),
|
||||
'Channel Export' => t('Channel Export')
|
||||
);
|
||||
|
||||
if(array_key_exists('name',$arr)) {
|
||||
@@ -420,15 +417,32 @@ class Apps {
|
||||
static public function app_render($papp, $mode = 'view') {
|
||||
$installed = false;
|
||||
|
||||
if(! $papp)
|
||||
if(!$papp) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(! $papp['photo'])
|
||||
/**
|
||||
* @hooks app_render_before
|
||||
* Hook to manipulate the papp array before rendering
|
||||
*/
|
||||
|
||||
$hookinfo = [
|
||||
'name' => $papp['name'],
|
||||
'photo' => $papp['photo']
|
||||
];
|
||||
|
||||
call_hooks('app_render_manipulate_photo', $hookinfo);
|
||||
|
||||
// We will only allow to manipulate the photo
|
||||
$papp['photo'] = $hookinfo['photo'];
|
||||
|
||||
if(!$papp['photo']) {
|
||||
$papp['photo'] = 'icon:gear';
|
||||
}
|
||||
|
||||
self::translate_system_apps($papp);
|
||||
|
||||
if(trim($papp['plugin']) && (! plugin_is_installed(trim($papp['plugin']))))
|
||||
if(isset($papp['plugin']) && trim($papp['plugin']) && (! plugin_is_installed(trim($papp['plugin']))))
|
||||
return '';
|
||||
|
||||
$papp['papp'] = self::papp_encode($papp);
|
||||
@@ -523,8 +537,13 @@ class Apps {
|
||||
$hosturl = '';
|
||||
|
||||
if(local_channel()) {
|
||||
if(self::app_installed(local_channel(),$papp) && !$papp['deleted'])
|
||||
if(self::app_installed(local_channel(),$papp)) {
|
||||
$installed = true;
|
||||
}
|
||||
|
||||
if ($installed && isset($papp['deleted']) && $papp['deleted']) {
|
||||
$installed = false;
|
||||
}
|
||||
|
||||
$hosturl = z_root() . '/';
|
||||
}
|
||||
@@ -597,12 +616,12 @@ class Apps {
|
||||
'$edit' => ((local_channel() && $installed && $mode == 'edit') ? t('Edit') : ''),
|
||||
'$delete' => ((local_channel() && $mode == 'edit') ? t('Delete') : ''),
|
||||
'$undelete' => ((local_channel() && $mode == 'edit') ? t('Undelete') : ''),
|
||||
'$settings_url' => ((local_channel() && $installed && $mode == 'list') ? $papp['settings_url'] : ''),
|
||||
'$deleted' => $papp['deleted'],
|
||||
'$settings_url' => ((local_channel() && $installed && $mode == 'list' && isset($papp['settings_url'])) ? $papp['settings_url'] : ''),
|
||||
'$deleted' => $papp['deleted'] ?? false,
|
||||
'$feature' => ((isset($papp['embed']) || $mode == 'edit') ? false : true),
|
||||
'$pin' => ((isset($papp['embed']) || $mode == 'edit') ? false : true),
|
||||
'$featured' => ((strpos($papp['categories'], 'nav_featured_app') === false) ? false : true),
|
||||
'$pinned' => ((strpos($papp['categories'], 'nav_pinned_app') === false) ? false : true),
|
||||
'$featured' => ((isset($papp['categories']) && strpos($papp['categories'], 'nav_featured_app') !== false) ? true : false),
|
||||
'$pinned' => ((isset($papp['categories']) && strpos($papp['categories'], 'nav_pinned_app') !== false) ? true : false),
|
||||
'$mode' => $mode,
|
||||
'$add' => t('Add to app-tray'),
|
||||
'$remove' => t('Remove from app-tray'),
|
||||
@@ -641,7 +660,7 @@ class Apps {
|
||||
);
|
||||
if($r) {
|
||||
if($app['uid']) {
|
||||
if($app['categories'] && (! $app['term'])) {
|
||||
if((isset($app['categories']) && $app['categories']) && !(isset($app['term']) && $app['term'])) {
|
||||
$r[0]['term'] = q("select * from term where otype = %d and oid = %d",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($r[0]['id'])
|
||||
@@ -1186,7 +1205,7 @@ class Apps {
|
||||
$ret['success'] = true;
|
||||
$ret['app_id'] = $darray['app_id'];
|
||||
}
|
||||
if($arr['categories']) {
|
||||
if(isset($arr['categories']) && $arr['categories']) {
|
||||
$x = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc($darray['app_id']),
|
||||
intval($darray['app_channel'])
|
||||
@@ -1284,7 +1303,7 @@ class Apps {
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($x[0]['id'])
|
||||
);
|
||||
if($arr['categories']) {
|
||||
if(isset($arr['categories']) && $arr['categories']) {
|
||||
$y = explode(',',$arr['categories']);
|
||||
if($y) {
|
||||
foreach($y as $t) {
|
||||
|
||||
@@ -5,50 +5,52 @@ namespace Zotlabs\Lib;
|
||||
/**
|
||||
* cache api
|
||||
*/
|
||||
|
||||
|
||||
class Cache {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns cached content
|
||||
*
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $age in SQL format, default is '30 DAY'
|
||||
* @return string
|
||||
*/
|
||||
|
||||
public static function get($key, $age = '') {
|
||||
|
||||
$hash = hash('whirlpool',$key);
|
||||
public static function get($key, $age = '') {
|
||||
// $hash = hash('whirlpool',$key);
|
||||
$hash = uuid_from_url($key);
|
||||
|
||||
$r = q("SELECT v FROM cache WHERE k = '%s' AND updated > %s - INTERVAL %s LIMIT 1",
|
||||
dbesc($hash),
|
||||
db_utcnow(),
|
||||
db_quoteinterval(($age ? $age : get_config('system','object_cache_days', '30') . ' DAY'))
|
||||
);
|
||||
|
||||
|
||||
if ($r)
|
||||
return $r[0]['v'];
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static function set($key,$value) {
|
||||
// $hash = hash('whirlpool',$key);
|
||||
$hash = uuid_from_url($key);
|
||||
|
||||
$hash = hash('whirlpool',$key);
|
||||
|
||||
$r = q("SELECT * FROM cache WHERE k = '%s' limit 1",
|
||||
$r = q("SELECT * FROM cache WHERE k = '%s' LIMIT 1",
|
||||
dbesc($hash)
|
||||
);
|
||||
if($r) {
|
||||
q("UPDATE cache SET v = '%s', updated = '%s' WHERE k = '%s'",
|
||||
dbesc($value),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($hash));
|
||||
dbesc($hash)
|
||||
);
|
||||
}
|
||||
else {
|
||||
q("INSERT INTO cache ( k, v, updated) VALUES ('%s','%s','%s')",
|
||||
q("INSERT INTO cache (k, v, updated) VALUES ('%s', '%s', '%s')",
|
||||
dbesc($hash),
|
||||
dbesc($value),
|
||||
dbesc(datetime_convert()));
|
||||
dbesc(datetime_convert())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
|
||||
class Config {
|
||||
|
||||
@@ -14,20 +15,41 @@ class Config {
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
*/
|
||||
static public function Load($family) {
|
||||
if(! array_key_exists($family, \App::$config))
|
||||
\App::$config[$family] = array();
|
||||
public static function Load($family, $recursionCounter = 0) {
|
||||
if (! array_key_exists($family, App::$config)) {
|
||||
App::$config[$family] = [];
|
||||
}
|
||||
|
||||
if(! array_key_exists('config_loaded', \App::$config[$family])) {
|
||||
// We typically continue when presented with minor DB issues,
|
||||
// but loading the site configuration is more important.
|
||||
|
||||
// Check for query returning false and give it approx 30 seconds
|
||||
// to recover if there's a problem. This is intended to fix a
|
||||
// rare issue on Galera where temporary sync issues were causing
|
||||
// the site encryption keys to be regenerated, which was causing
|
||||
// communication issues for members.
|
||||
|
||||
// This code probably belongs at the database layer, but we don't
|
||||
// necessarily want to shut the site down for problematic queries
|
||||
// caused by bad data. That could be used in a denial of service
|
||||
// attack. Those do need to be (and they are) logged.
|
||||
|
||||
if (! array_key_exists('config_loaded', App::$config[$family])) {
|
||||
$r = q("SELECT * FROM config WHERE cat = '%s'", dbesc($family));
|
||||
if($r !== false) {
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$k = $rr['k'];
|
||||
\App::$config[$family][$k] = $rr['v'];
|
||||
}
|
||||
if ($r === false && !App::$install) {
|
||||
sleep(3);
|
||||
$recursionCounter ++;
|
||||
if ($recursionCounter > 10) {
|
||||
system_unavailable();
|
||||
}
|
||||
\App::$config[$family]['config_loaded'] = true;
|
||||
self::Load($family, $recursionCounter);
|
||||
}
|
||||
elseif (is_array($r)) {
|
||||
foreach ($r as $rr) {
|
||||
$k = $rr['k'];
|
||||
App::$config[$family][$k] = $rr['v'];
|
||||
}
|
||||
App::$config[$family]['config_loaded'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,19 +68,19 @@ class Config {
|
||||
* @return mixed
|
||||
* Return the set value, or false if the database update failed
|
||||
*/
|
||||
static public function Set($family, $key, $value) {
|
||||
public static function Set($family, $key, $value) {
|
||||
// manage array value
|
||||
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
|
||||
$dbvalue = ((is_array($value)) ? 'json:' . json_encode($value) : $value);
|
||||
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
|
||||
|
||||
if(self::Get($family, $key) === false || (! self::get_from_storage($family, $key))) {
|
||||
if (self::Get($family, $key) === false || (! self::get_from_storage($family, $key))) {
|
||||
$ret = q("INSERT INTO config ( cat, k, v ) VALUES ( '%s', '%s', '%s' ) ",
|
||||
dbesc($family),
|
||||
dbesc($key),
|
||||
dbesc($dbvalue)
|
||||
);
|
||||
if($ret) {
|
||||
\App::$config[$family][$key] = $value;
|
||||
if ($ret) {
|
||||
App::$config[$family][$key] = $value;
|
||||
$ret = $value;
|
||||
}
|
||||
return $ret;
|
||||
@@ -70,8 +92,8 @@ class Config {
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
if($ret) {
|
||||
\App::$config[$family][$key] = $value;
|
||||
if ($ret) {
|
||||
App::$config[$family][$key] = $value;
|
||||
$ret = $value;
|
||||
}
|
||||
|
||||
@@ -96,18 +118,34 @@ class Config {
|
||||
* @param string $default (optional) default false
|
||||
* @return mixed Return value or false on error or if not set
|
||||
*/
|
||||
static public function Get($family, $key, $default = false) {
|
||||
if((! array_key_exists($family, \App::$config)) || (! array_key_exists('config_loaded', \App::$config[$family])))
|
||||
self::Load($family);
|
||||
public static function Get($family, $key, $default = false) {
|
||||
|
||||
if(array_key_exists('config_loaded', \App::$config[$family])) {
|
||||
if(! array_key_exists($key, \App::$config[$family])) {
|
||||
if ((! array_key_exists($family, App::$config)) || (! array_key_exists('config_loaded', App::$config[$family]))) {
|
||||
self::Load($family);
|
||||
}
|
||||
|
||||
if (array_key_exists('config_loaded', App::$config[$family])) {
|
||||
if (! array_key_exists($key, App::$config[$family])) {
|
||||
return $default;
|
||||
}
|
||||
return ((! is_array(\App::$config[$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$family][$key]))
|
||||
? unserialize(\App::$config[$family][$key])
|
||||
: \App::$config[$family][$key]
|
||||
);
|
||||
|
||||
$value = App::$config[$family][$key];
|
||||
|
||||
if (! is_array($value)) {
|
||||
if (substr($value, 0, 5) == 'json:') {
|
||||
return json_decode(substr($value, 5), true);
|
||||
} else if (preg_match('|^a:[0-9]+:{.*}$|s', $value)) {
|
||||
// Unserialize in inherently unsafe. Try to mitigate by not
|
||||
// allowing unserializing objects. Only kept for backwards
|
||||
// compatibility. JSON serialization should be prefered.
|
||||
return unserialize($value, array('allowed_classes' => false));
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
@@ -125,12 +163,13 @@ class Config {
|
||||
* The configuration key to delete
|
||||
* @return mixed
|
||||
*/
|
||||
static public function Delete($family, $key) {
|
||||
public static function Delete($family, $key) {
|
||||
|
||||
$ret = false;
|
||||
|
||||
if(array_key_exists($family, \App::$config) && array_key_exists($key, \App::$config[$family]))
|
||||
unset(\App::$config[$family][$key]);
|
||||
if (array_key_exists($family, App::$config) && array_key_exists($key, App::$config[$family])) {
|
||||
unset(App::$config[$family][$key]);
|
||||
}
|
||||
|
||||
$ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s'",
|
||||
dbesc($family),
|
||||
@@ -153,7 +192,7 @@ class Config {
|
||||
* The configuration key to query
|
||||
* @return mixed
|
||||
*/
|
||||
static private function get_from_storage($family,$key) {
|
||||
private static function get_from_storage($family, $key) {
|
||||
$ret = q("SELECT * FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1",
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
@@ -161,5 +200,4 @@ class Config {
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -69,7 +69,8 @@ class Connect {
|
||||
$xchan_hash = '';
|
||||
$sql_options = (($protocol) ? " and xchan_network = '" . dbesc($protocol) . "' " : '');
|
||||
|
||||
$r = q("select * from xchan where ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s') $sql_options ",
|
||||
// We need both, the xchan and the hubloc here hence use JOIN instead of LEFT JOIN
|
||||
$r = q("SELECT * FROM xchan JOIN hubloc ON xchan_hash = hubloc_hash WHERE ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s') $sql_options ORDER BY hubloc_id DESC",
|
||||
dbesc($url),
|
||||
dbesc($url),
|
||||
dbesc($url)
|
||||
@@ -80,12 +81,13 @@ class Connect {
|
||||
// reset results to the best record or the first if we don't have the best
|
||||
// note: this is a single record and not an array of results
|
||||
|
||||
$r = Libzot::zot_record_preferred($r,'xchan_network');
|
||||
$r = Libzot::zot_record_preferred($r, 'xchan_network');
|
||||
|
||||
}
|
||||
|
||||
$singleton = false;
|
||||
$d = false;
|
||||
$wf = false;
|
||||
|
||||
if (! $r) {
|
||||
|
||||
@@ -108,10 +110,12 @@ class Connect {
|
||||
|
||||
if ($wf || $d) {
|
||||
|
||||
$xchan_hash = (($wf) ? $wf : $url);
|
||||
|
||||
// something was discovered - find the record which was just created.
|
||||
|
||||
$r = q("select * from xchan where ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s' ) $sql_options",
|
||||
dbesc(($wf) ? $wf : $url),
|
||||
dbesc($xchan_hash),
|
||||
dbesc($url),
|
||||
dbesc($url)
|
||||
);
|
||||
@@ -119,7 +123,7 @@ class Connect {
|
||||
// convert to a single record (once again preferring a zot solution in the case of multiples)
|
||||
|
||||
if ($r) {
|
||||
$r = Libzot::zot_record_preferred($r,'xchan_network');
|
||||
$r = Libzot::zot_record_preferred($r, 'xchan_network');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +265,8 @@ class Connect {
|
||||
'abook_feed' => intval(($xchan['xchan_network'] === 'rss') ? 1 : 0),
|
||||
'abook_created' => datetime_convert(),
|
||||
'abook_updated' => datetime_convert(),
|
||||
'abook_instance' => (($singleton) ? z_root() : '')
|
||||
'abook_instance' => (($singleton) ? z_root() : ''),
|
||||
'abook_role' => get_pconfig($uid, 'system', 'default_permcat', 'default')
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -300,7 +305,7 @@ class Connect {
|
||||
/** If there is a default group for this channel, add this connection to it */
|
||||
|
||||
if ($default_group) {
|
||||
$g = AccessList::rec_byhash($uid,$default_group);
|
||||
$g = AccessList::by_hash($uid,$default_group);
|
||||
if ($g) {
|
||||
AccessList::member_add($uid,'',$xchan_hash,$g['id']);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,10 @@ class Crypto {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$alg) {
|
||||
$alg = 'sha256';
|
||||
}
|
||||
|
||||
try {
|
||||
$verify = openssl_verify($data, $sig, $key, $alg);
|
||||
} catch (Exception $e) {
|
||||
|
||||
@@ -6,23 +6,26 @@ class DReport {
|
||||
private $location;
|
||||
private $sender;
|
||||
private $recipient;
|
||||
private $name;
|
||||
private $message_id;
|
||||
private $message_uuid;
|
||||
private $status;
|
||||
private $date;
|
||||
|
||||
function __construct($location,$sender,$recipient,$message_id,$status = 'deliver') {
|
||||
$this->location = $location;
|
||||
$this->sender = $sender;
|
||||
$this->recipient = $recipient;
|
||||
$this->name = EMPTY_STR;
|
||||
$this->message_id = $message_id;
|
||||
$this->status = $status;
|
||||
$this->date = datetime_convert();
|
||||
function __construct($location, $sender, $recipient, $message_id, $message_uuid = '', $status = 'deliver') {
|
||||
$this->location = $location;
|
||||
$this->sender = $sender;
|
||||
$this->recipient = $recipient;
|
||||
$this->name = EMPTY_STR;
|
||||
$this->message_id = $message_id;
|
||||
$this->message_uuid = $message_uuid;
|
||||
$this->status = $status;
|
||||
$this->date = datetime_convert();
|
||||
}
|
||||
|
||||
function update($status) {
|
||||
$this->status = $status;
|
||||
$this->date = datetime_convert();
|
||||
$this->status = $status;
|
||||
$this->date = datetime_convert();
|
||||
}
|
||||
|
||||
function set_name($name) {
|
||||
@@ -35,24 +38,26 @@ class DReport {
|
||||
|
||||
|
||||
function set($arr) {
|
||||
$this->location = $arr['location'];
|
||||
$this->sender = $arr['sender'];
|
||||
$this->recipient = $arr['recipient'];
|
||||
$this->name = $arr['name'];
|
||||
$this->message_id = $arr['message_id'];
|
||||
$this->status = $arr['status'];
|
||||
$this->date = $arr['date'];
|
||||
$this->location = $arr['location'];
|
||||
$this->sender = $arr['sender'];
|
||||
$this->recipient = $arr['recipient'];
|
||||
$this->name = $arr['name'];
|
||||
$this->message_id = $arr['message_id'];
|
||||
$this->message_uuid = $arr['message_uuid'] ?? '';
|
||||
$this->status = $arr['status'];
|
||||
$this->date = $arr['date'];
|
||||
}
|
||||
|
||||
function get() {
|
||||
return array(
|
||||
'location' => $this->location,
|
||||
'sender' => $this->sender,
|
||||
'recipient' => $this->recipient,
|
||||
'name' => $this->name,
|
||||
'message_id' => $this->message_id,
|
||||
'status' => $this->status,
|
||||
'date' => $this->date
|
||||
'location' => $this->location,
|
||||
'sender' => $this->sender,
|
||||
'recipient' => $this->recipient,
|
||||
'name' => $this->name,
|
||||
'message_id' => $this->message_id,
|
||||
'message_uuid' => $this->message_uuid,
|
||||
'status' => $this->status,
|
||||
'date' => $this->date
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,19 +99,6 @@ class DReport {
|
||||
if(! $c)
|
||||
return false;
|
||||
|
||||
// legacy zot recipients add a space and their name to the xchan. remove it if true.
|
||||
|
||||
$legacy_recipient = strpos($dr['recipient'], ' ');
|
||||
if($legacy_recipient !== false) {
|
||||
$legacy_recipient_parts = explode(' ', $dr['recipient'], 2);
|
||||
$rxchan = $legacy_recipient_parts[0];
|
||||
}
|
||||
else {
|
||||
$rxchan = $dr['recipient'];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// is the recipient one of our connections, or do we want to store every report?
|
||||
|
||||
$pcf = get_pconfig($c[0]['channel_id'],'system','dreport_store_all');
|
||||
@@ -117,7 +109,7 @@ class DReport {
|
||||
// So if a remote site says they can't find us, that's no big surprise
|
||||
// and just creates a lot of extra report noise
|
||||
|
||||
if(($dr['location'] !== z_root()) && ($dr['sender'] === $rxchan) && ($dr['status'] === 'recipient not found'))
|
||||
if(($dr['location'] !== z_root()) && ($dr['sender'] === $dr['recipient']) && ($dr['status'] === 'recipient not found'))
|
||||
return false;
|
||||
|
||||
// If you have a private post with a recipient list, every single site is going to report
|
||||
@@ -126,14 +118,14 @@ class DReport {
|
||||
// have a channel on that site.
|
||||
|
||||
$r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_url = '%s'",
|
||||
dbesc($rxchan),
|
||||
dbesc($dr['recipient']),
|
||||
dbesc($dr['location'])
|
||||
);
|
||||
if((! $r) && ($dr['status'] === 'recipient_not_found'))
|
||||
return false;
|
||||
|
||||
$r = q("select abook_id from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
|
||||
dbesc($rxchan),
|
||||
dbesc($dr['recipient']),
|
||||
intval($c[0]['channel_id'])
|
||||
);
|
||||
if($r)
|
||||
|
||||
@@ -122,8 +122,11 @@ class Enotify {
|
||||
// e.g. "your post", "David's photo", etc.
|
||||
$possess_desc = t('%s <!item_type!>');
|
||||
|
||||
$parent_mid = '';
|
||||
$parent_item = [];
|
||||
|
||||
// @@TODO: consider using switch instead of those elseif
|
||||
if ($params['type'] == NOTIFY_MAIL) {
|
||||
if (isset($params['type']) && $params['type'] == NOTIFY_MAIL) {
|
||||
logger('notification: mail');
|
||||
$subject = sprintf( t('[$Projectname:Notify] New direct message received at %s'), $sitename);
|
||||
|
||||
@@ -135,18 +138,18 @@ class Enotify {
|
||||
$itemlink = $siteurl . '/hq/' . gen_link_id($params['item']['mid']);
|
||||
}
|
||||
|
||||
elseif ($params['type'] === NOTIFY_COMMENT) {
|
||||
elseif (isset($params['type']) && $params['type'] === NOTIFY_COMMENT) {
|
||||
//logger("notification: params = " . print_r($params, true), LOGGER_DEBUG);
|
||||
|
||||
$moderated = (($params['item']['item_blocked'] == ITEM_MODERATED) ? true : false);
|
||||
|
||||
$itemlink = $params['link'];
|
||||
|
||||
$action = t('commented on');
|
||||
$action = (($moderated) ? t('requested to comment on') : t('commented on'));
|
||||
|
||||
if(array_key_exists('item',$params)) {
|
||||
|
||||
if(in_array($params['item']['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
|
||||
if(in_array($params['item']['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_SHARE])) {
|
||||
|
||||
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) {
|
||||
logger('notification: not a visible activity. Ignoring.');
|
||||
@@ -154,11 +157,14 @@ class Enotify {
|
||||
return;
|
||||
}
|
||||
|
||||
if(activity_match($params['verb'], ACTIVITY_LIKE))
|
||||
$action = t('liked');
|
||||
if(activity_match($params['verb'], ['Like', ACTIVITY_LIKE]))
|
||||
$action = (($moderated) ? t('requested to like') : t('liked'));
|
||||
|
||||
if(activity_match($params['verb'], ACTIVITY_DISLIKE))
|
||||
$action = t('disliked');
|
||||
if(activity_match($params['verb'], ['Dislike', ACTIVITY_DISLIKE]))
|
||||
$action = (($moderated) ? t('requested to dislike') : t('disliked'));
|
||||
|
||||
if(activity_match($params['verb'], ACTIVITY_SHARE))
|
||||
$action = t('repeated');
|
||||
|
||||
}
|
||||
|
||||
@@ -167,7 +173,7 @@ class Enotify {
|
||||
|
||||
}
|
||||
|
||||
$parent_mid = $params['parent_mid'];
|
||||
$parent_mid = $params['parent_mid'] ?? '';
|
||||
|
||||
// Check to see if there was already a notify for this post.
|
||||
// If so don't create a second notification
|
||||
@@ -251,20 +257,20 @@ class Enotify {
|
||||
|
||||
}
|
||||
|
||||
elseif ($params['type'] === NOTIFY_LIKE) {
|
||||
elseif (isset($params['type']) && $params['type'] === NOTIFY_LIKE) {
|
||||
// logger("notification: params = " . print_r($params, true), LOGGER_DEBUG);
|
||||
|
||||
$itemlink = $params['link'];
|
||||
|
||||
if (array_key_exists('item',$params) && (! activity_match($params['item']['verb'],ACTIVITY_LIKE))) {
|
||||
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) {
|
||||
if (array_key_exists('item',$params) && (activity_match($params['item']['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE]))) {
|
||||
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE) || !feature_enabled($recip['channel_id'], 'dislike')) {
|
||||
logger('notification: not a visible activity. Ignoring.');
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$parent_mid = $params['parent_mid'];
|
||||
$parent_mid = $params['parent_mid'] ?? '';
|
||||
|
||||
// Check to see if there was already a notify for this post.
|
||||
// If so don't create a second notification
|
||||
@@ -291,22 +297,36 @@ class Enotify {
|
||||
);
|
||||
}
|
||||
|
||||
if (!$p) {
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
xchan_query($p);
|
||||
|
||||
//@@FIXME $p can be null (line 285)
|
||||
$item_post_type = item_post_type($p[0]);
|
||||
// $private = $p[0]['item_private'];
|
||||
$parent_id = $p[0]['id'];
|
||||
|
||||
$parent_item = $p[0];
|
||||
|
||||
//$verb = ((activity_match($params['item']['verb'], ACTIVITY_DISLIKE)) ? t('disliked') : t('liked'));
|
||||
$moderated = (($params['item']['item_blocked'] == ITEM_MODERATED) ? true : false);
|
||||
|
||||
if(activity_match($params['item']['verb'], ['Like', ACTIVITY_LIKE]))
|
||||
$verb = (($moderated) ? t('requested to like') : t('liked'));
|
||||
|
||||
if(activity_match($params['item']['verb'], ['Dislike', ACTIVITY_DISLIKE]))
|
||||
$verb = (($moderated) ? t('requested to dislike') : t('disliked'));
|
||||
|
||||
// "your post"
|
||||
if($p[0]['owner']['xchan_name'] === $p[0]['author']['xchan_name'] && intval($p[0]['item_wall']))
|
||||
$dest_str = sprintf(t('%1$s liked [zrl=%2$s]your %3$s[/zrl]'),
|
||||
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]your %4$s[/zrl]'),
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$verb,
|
||||
$itemlink,
|
||||
$item_post_type);
|
||||
$item_post_type
|
||||
);
|
||||
else {
|
||||
pop_lang();
|
||||
return;
|
||||
@@ -328,7 +348,7 @@ class Enotify {
|
||||
|
||||
|
||||
|
||||
elseif($params['type'] === NOTIFY_WALL) {
|
||||
elseif(isset($params['type']) && $params['type'] === NOTIFY_WALL) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] %s posted to your profile wall') , $sender['xchan_name']);
|
||||
|
||||
$preamble = sprintf( t('%1$s posted to your profile wall at %2$s') , $sender['xchan_name'], $sitename);
|
||||
@@ -343,7 +363,7 @@ class Enotify {
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
elseif ($params['type'] === NOTIFY_TAGSELF) {
|
||||
elseif (isset($params['type']) && $params['type'] === NOTIFY_TAGSELF) {
|
||||
|
||||
$p = q("select id from notify where link = '%s' and uid = %d limit 1",
|
||||
dbesc($params['link']),
|
||||
@@ -367,7 +387,7 @@ class Enotify {
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
elseif ($params['type'] === NOTIFY_POKE) {
|
||||
elseif (isset($params['type']) && $params['type'] === NOTIFY_POKE) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] %1$s poked you') , $sender['xchan_name']);
|
||||
$preamble = sprintf( t('%1$s poked you at %2$s') , $sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('%1$s [zrl=%2$s]poked you[/zrl].') ,
|
||||
@@ -384,7 +404,7 @@ class Enotify {
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
elseif ($params['type'] === NOTIFY_TAGSHARE) {
|
||||
elseif (isset($params['type']) && $params['type'] === NOTIFY_TAGSHARE) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] %s tagged your post') , $sender['xchan_name']);
|
||||
$preamble = sprintf( t('%1$s tagged your post at %2$s'),$sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('%1$s tagged [zrl=%2$s]your post[/zrl]') ,
|
||||
@@ -397,7 +417,7 @@ class Enotify {
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
elseif ($params['type'] === NOTIFY_INTRO) {
|
||||
elseif (isset($params['type']) && $params['type'] === NOTIFY_INTRO) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] Introduction received'));
|
||||
$preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('You\'ve received [zrl=%1$s]a new connection request[/zrl] from %2$s.'),
|
||||
@@ -411,7 +431,7 @@ class Enotify {
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
elseif ($params['type'] === NOTIFY_SUGGEST) {
|
||||
elseif (isset($params['type']) && $params['type'] === NOTIFY_SUGGEST) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] Friend suggestion received'));
|
||||
$preamble = sprintf( t('You\'ve received a friend suggestion from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('You\'ve received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s.'),
|
||||
@@ -429,11 +449,11 @@ class Enotify {
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
elseif ($params['type'] === NOTIFY_CONFIRM) {
|
||||
elseif (isset($params['type']) && $params['type'] === NOTIFY_CONFIRM) {
|
||||
// ?
|
||||
}
|
||||
|
||||
elseif ($params['type'] === NOTIFY_SYSTEM) {
|
||||
elseif (isset($params['type']) && $params['type'] === NOTIFY_SYSTEM) {
|
||||
// ?
|
||||
}
|
||||
|
||||
@@ -466,6 +486,8 @@ class Enotify {
|
||||
|
||||
require_once('include/html2bbcode.php');
|
||||
|
||||
/*
|
||||
|
||||
do {
|
||||
$dups = false;
|
||||
$hash = random_string();
|
||||
@@ -474,10 +496,12 @@ class Enotify {
|
||||
if ($r)
|
||||
$dups = true;
|
||||
} while ($dups === true);
|
||||
*/
|
||||
|
||||
|
||||
|
||||
$datarray = [];
|
||||
$datarray['hash'] = $hash;
|
||||
$datarray['hash'] = $params['item']['uuid'] ?? new_uuid();
|
||||
$datarray['sender_hash'] = $sender['xchan_hash'];
|
||||
$datarray['xname'] = $sender['xchan_name'];
|
||||
$datarray['url'] = $sender['xchan_url'];
|
||||
@@ -488,12 +512,13 @@ class Enotify {
|
||||
$datarray['link'] = $itemlink;
|
||||
$datarray['parent'] = $parent_mid;
|
||||
$datarray['parent_item'] = $parent_item;
|
||||
$datarray['ntype'] = $params['type'];
|
||||
$datarray['verb'] = $params['verb'];
|
||||
$datarray['otype'] = $params['otype'];
|
||||
$datarray['ntype'] = $params['type'] ?? 0;
|
||||
$datarray['verb'] = $params['verb'] ?? '';
|
||||
$datarray['otype'] = $params['otype'] ?? '';
|
||||
$datarray['abort'] = false;
|
||||
$datarray['seen'] = 0;
|
||||
|
||||
$datarray['item'] = $params['item'];
|
||||
$datarray['item'] = $params['item'] ?? [];
|
||||
|
||||
call_hooks('enotify_store', $datarray);
|
||||
|
||||
@@ -504,7 +529,6 @@ class Enotify {
|
||||
|
||||
|
||||
// create notification entry in DB
|
||||
$seen = 0;
|
||||
|
||||
// Mark some notifications as seen right away
|
||||
// Note! The notification have to be created, because they are used to send emails
|
||||
@@ -514,7 +538,7 @@ class Enotify {
|
||||
|
||||
if (!$always_show_in_notices) {
|
||||
if (($params['type'] === NOTIFY_WALL) || ($params['type'] === NOTIFY_MAIL) || ($params['type'] === NOTIFY_INTRO)) {
|
||||
$seen = 1;
|
||||
$datarray['seen'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,14 +554,15 @@ class Enotify {
|
||||
intval($datarray['uid']),
|
||||
dbesc($datarray['link']),
|
||||
dbesc($datarray['parent']),
|
||||
intval($seen),
|
||||
intval($datarray['seen']),
|
||||
intval($datarray['ntype']),
|
||||
dbesc($datarray['verb']),
|
||||
dbesc($datarray['otype'])
|
||||
);
|
||||
|
||||
$r = q("select id from notify where hash = '%s' and uid = %d limit 1",
|
||||
dbesc($hash),
|
||||
$r = q("select id from notify where hash = '%s' and ntype = %d and uid = %d limit 1",
|
||||
dbesc($datarray['hash']),
|
||||
intval($datarray['ntype']),
|
||||
intval($recip['channel_id'])
|
||||
);
|
||||
if ($r) {
|
||||
@@ -606,8 +631,8 @@ class Enotify {
|
||||
$datarray['preamble'] = $preamble;
|
||||
$datarray['sitename'] = $sitename;
|
||||
$datarray['siteurl'] = $siteurl;
|
||||
$datarray['type'] = $params['type'];
|
||||
$datarray['parent'] = $params['parent_mid'];
|
||||
$datarray['type'] = $params['type'] ?? '';
|
||||
$datarray['parent'] = $params['parent_mid'] ?? '';
|
||||
$datarray['source_name'] = $sender['xchan_name'];
|
||||
$datarray['source_link'] = $sender['xchan_url'];
|
||||
$datarray['source_photo'] = $sender['xchan_photo_s'];
|
||||
@@ -674,7 +699,6 @@ class Enotify {
|
||||
'$source_name' => $datarray['source_name'],
|
||||
'$source_link' => $datarray['source_link'],
|
||||
'$source_photo' => $datarray['source_photo'],
|
||||
'$username' => $datarray['to_name'],
|
||||
'$hsitelink' => $datarray['hsitelink'],
|
||||
'$hitemlink' => $datarray['hitemlink'],
|
||||
'$thanks' => $datarray['thanks'],
|
||||
@@ -696,7 +720,6 @@ class Enotify {
|
||||
'$source_name' => $datarray['source_name'],
|
||||
'$source_link' => $datarray['source_link'],
|
||||
'$source_photo' => $datarray['source_photo'],
|
||||
'$username' => $datarray['to_name'],
|
||||
'$tsitelink' => $datarray['tsitelink'],
|
||||
'$titemlink' => $datarray['titemlink'],
|
||||
'$thanks' => $datarray['thanks'],
|
||||
@@ -808,10 +831,10 @@ class Enotify {
|
||||
|
||||
localize_item($item);
|
||||
|
||||
if($item['shortlocalize']) {
|
||||
if(isset($item['shortlocalize'])) {
|
||||
$itemem_text = $item['shortlocalize'];
|
||||
}
|
||||
elseif($item['localize']) {
|
||||
elseif(isset($item['localize'])) {
|
||||
$itemem_text = $item['localize'];
|
||||
}
|
||||
else {
|
||||
@@ -820,10 +843,6 @@ class Enotify {
|
||||
: (($item['obj_type'] === 'Answer') ? sprintf( t('voted on %s\'s poll'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]') : sprintf( t('commented on %s\'s post'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]'))
|
||||
);
|
||||
|
||||
if($item['verb'] === ACTIVITY_SHARE) {
|
||||
$itemem_text = sprintf( t('repeated %s\'s post'), '[bdi]' . $item['author']['xchan_name'] . '[/bdi]');
|
||||
}
|
||||
|
||||
if(in_array($item['obj_type'], ['Document', 'Video', 'Audio', 'Image'])) {
|
||||
$itemem_text = t('shared a file with you');
|
||||
}
|
||||
@@ -844,23 +863,27 @@ class Enotify {
|
||||
|
||||
// convert this logic into a json array just like the system notifications
|
||||
|
||||
$who = (($item['verb'] === ACTIVITY_SHARE) ? 'owner' : 'author');
|
||||
$body = html2plain(bbcode($item['body'], ['drop_media' => true, 'tryoembed' => false]), 75, true);
|
||||
if ($body) {
|
||||
$body = htmlentities($body, ENT_QUOTES, 'UTF-8', false);
|
||||
}
|
||||
|
||||
$x = array(
|
||||
'notify_link' => $item['llink'],
|
||||
'name' => $item[$who]['xchan_name'],
|
||||
'addr' => (($item[$who]['xchan_addr']) ? $item[$who]['xchan_addr'] : $item[$who]['xchan_url']),
|
||||
'url' => $item[$who]['xchan_url'],
|
||||
'photo' => $item[$who]['xchan_photo_s'],
|
||||
'name' => $item['author']['xchan_name'],
|
||||
'addr' => $item['author']['xchan_addr'] ? $item['author']['xchan_addr'] : $item['author']['xchan_url'],
|
||||
'url' => $item['author']['xchan_url'],
|
||||
'photo' => $item['author']['xchan_photo_s'],
|
||||
'when' => (($edit) ? datetime_convert('UTC', date_default_timezone_get(), $item['edited']) : datetime_convert('UTC', date_default_timezone_get(), $item['created'])),
|
||||
'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'),
|
||||
'b64mid' => (($item['mid']) ? gen_link_id($item['mid']) : ''),
|
||||
// 'b64mid' => (($item['mid']) ? gen_link_id($item['mid']) : ''),
|
||||
'b64mid' => (($item['uuid']) ? $item['uuid'] : ''),
|
||||
//'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? gen_link_id($item['thr_parent']) : gen_link_id($item['mid'])),
|
||||
'thread_top' => (($item['item_thread_top']) ? true : false),
|
||||
'message' => bbcode(escape_tags($itemem_text)),
|
||||
'body' => htmlentities(html2plain(bbcode($item['body'], ['drop_media', true]), 75, true), ENT_QUOTES, 'UTF-8', false),
|
||||
'body' => $body,
|
||||
// these are for the superblock addon
|
||||
'hash' => $item[$who]['xchan_hash'],
|
||||
'hash' => $item['author']['xchan_hash'],
|
||||
'uid' => $item['uid'],
|
||||
'display' => true
|
||||
);
|
||||
@@ -880,9 +903,6 @@ class Enotify {
|
||||
if(strpos($message, $tt['xname']) === 0)
|
||||
$message = substr($message, strlen($tt['xname']) + 1);
|
||||
|
||||
$mid = basename($tt['link']);
|
||||
|
||||
$b64mid = gen_link_id($mid);
|
||||
$x = [
|
||||
'notify_link' => (($tt['ntype'] === NOTIFY_MAIL) ? $tt['link'] : z_root() . '/notify/view/' . $tt['id']),
|
||||
'name' => $tt['xname'],
|
||||
@@ -890,7 +910,7 @@ class Enotify {
|
||||
'photo' => $tt['photo'],
|
||||
'when' => datetime_convert('UTC', date_default_timezone_get(), $tt['created']),
|
||||
'hclass' => (($tt['seen']) ? 'notify-seen' : 'notify-unseen'),
|
||||
'b64mid' => (($tt['otype'] == 'item') ? $b64mid : ''),
|
||||
'b64mid' => (($tt['otype'] == 'item') ? $tt['hash'] : ''),
|
||||
'notify_id' => (($tt['otype'] == 'item') ? $tt['id'] : ''),
|
||||
'message' => $message
|
||||
];
|
||||
@@ -902,7 +922,7 @@ class Enotify {
|
||||
static public function format_intros($rr) {
|
||||
|
||||
return [
|
||||
'notify_link' => z_root() . '/connections/ifpending',
|
||||
'notify_link' => z_root() . '/connections#' . $rr['abook_id'],
|
||||
'name' => $rr['xchan_name'],
|
||||
'addr' => $rr['xchan_addr'],
|
||||
'url' => $rr['xchan_url'],
|
||||
|
||||
@@ -1,405 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
|
||||
class Group {
|
||||
|
||||
static function add($uid,$name,$public = 0) {
|
||||
|
||||
$ret = false;
|
||||
if(x($uid) && x($name)) {
|
||||
$r = self::byname($uid,$name); // check for dups
|
||||
if($r !== false) {
|
||||
|
||||
// This could be a problem.
|
||||
// Let's assume we've just created a group which we once deleted
|
||||
// all the old members are gone, but the group remains so we don't break any security
|
||||
// access lists. What we're doing here is reviving the dead group, but old content which
|
||||
// was restricted to this group may now be seen by the new group members.
|
||||
|
||||
$z = q("SELECT * FROM pgrp WHERE id = %d LIMIT 1",
|
||||
intval($r)
|
||||
);
|
||||
if(($z) && $z[0]['deleted']) {
|
||||
q('UPDATE pgrp SET deleted = 0 WHERE id = %d', intval($z[0]['id']));
|
||||
notice( t('A deleted group with this name was revived. Existing item permissions <strong>may</strong> apply to this group and any future members. If this is not what you intended, please create another group with a different name.') . EOL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
do {
|
||||
$dups = false;
|
||||
$hash = random_string(32) . str_replace(['<','>'],['.','.'], $name);
|
||||
|
||||
$r = q("SELECT id FROM pgrp WHERE hash = '%s' LIMIT 1", dbesc($hash));
|
||||
if($r)
|
||||
$dups = true;
|
||||
} while($dups == true);
|
||||
|
||||
|
||||
$r = q("INSERT INTO pgrp ( hash, uid, visible, gname )
|
||||
VALUES( '%s', %d, %d, '%s' ) ",
|
||||
dbesc($hash),
|
||||
intval($uid),
|
||||
intval($public),
|
||||
dbesc($name)
|
||||
);
|
||||
$ret = $r;
|
||||
}
|
||||
|
||||
Libsync::build_sync_packet($uid,null,true);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
static function remove($uid,$name) {
|
||||
$ret = false;
|
||||
if(x($uid) && x($name)) {
|
||||
$r = q("SELECT id, hash FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1",
|
||||
intval($uid),
|
||||
dbesc($name)
|
||||
);
|
||||
if($r) {
|
||||
$group_id = $r[0]['id'];
|
||||
$group_hash = $r[0]['hash'];
|
||||
}
|
||||
|
||||
if(! $group_id)
|
||||
return false;
|
||||
|
||||
// remove group from default posting lists
|
||||
$r = q("SELECT channel_default_group, channel_allow_gid, channel_deny_gid FROM channel WHERE channel_id = %d LIMIT 1",
|
||||
intval($uid)
|
||||
);
|
||||
if($r) {
|
||||
$user_info = $r[0];
|
||||
$change = false;
|
||||
|
||||
if($user_info['channel_default_group'] == $group_hash) {
|
||||
$user_info['channel_default_group'] = '';
|
||||
$change = true;
|
||||
}
|
||||
if(strpos($user_info['channel_allow_gid'], '<' . $group_hash . '>') !== false) {
|
||||
$user_info['channel_allow_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_allow_gid']);
|
||||
$change = true;
|
||||
}
|
||||
if(strpos($user_info['channel_deny_gid'], '<' . $group_hash . '>') !== false) {
|
||||
$user_info['channel_deny_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_deny_gid']);
|
||||
$change = true;
|
||||
}
|
||||
|
||||
if($change) {
|
||||
q("UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s'
|
||||
WHERE channel_id = %d",
|
||||
intval($user_info['channel_default_group']),
|
||||
dbesc($user_info['channel_allow_gid']),
|
||||
dbesc($user_info['channel_deny_gid']),
|
||||
intval($uid)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// remove all members
|
||||
$r = q("DELETE FROM pgrp_member WHERE uid = %d AND gid = %d ",
|
||||
intval($uid),
|
||||
intval($group_id)
|
||||
);
|
||||
|
||||
// remove group
|
||||
$r = q("UPDATE pgrp SET deleted = 1 WHERE uid = %d AND gname = '%s'",
|
||||
intval($uid),
|
||||
dbesc($name)
|
||||
);
|
||||
|
||||
$ret = $r;
|
||||
|
||||
}
|
||||
|
||||
Libsync::build_sync_packet($uid,null,true);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
static function byname($uid,$name) {
|
||||
if((! $uid) || (! strlen($name)))
|
||||
return false;
|
||||
$r = q("SELECT * FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1",
|
||||
intval($uid),
|
||||
dbesc($name)
|
||||
);
|
||||
if($r)
|
||||
return $r[0]['id'];
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static function rec_byhash($uid,$hash) {
|
||||
if((! $uid) || (! strlen($hash)))
|
||||
return false;
|
||||
$r = q("SELECT * FROM pgrp WHERE uid = %d AND hash = '%s' LIMIT 1",
|
||||
intval($uid),
|
||||
dbesc($hash)
|
||||
);
|
||||
if($r)
|
||||
return $r[0];
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static function member_remove($uid,$name,$member) {
|
||||
$gid = self::byname($uid,$name);
|
||||
if(! $gid)
|
||||
return false;
|
||||
if(! ( $uid && $gid && $member))
|
||||
return false;
|
||||
$r = q("DELETE FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' ",
|
||||
intval($uid),
|
||||
intval($gid),
|
||||
dbesc($member)
|
||||
);
|
||||
|
||||
Libsync::build_sync_packet($uid,null,true);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
||||
static function member_add($uid,$name,$member,$gid = 0) {
|
||||
if(! $gid)
|
||||
$gid = self::byname($uid,$name);
|
||||
if((! $gid) || (! $uid) || (! $member))
|
||||
return false;
|
||||
|
||||
$r = q("SELECT * FROM pgrp_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1",
|
||||
intval($uid),
|
||||
intval($gid),
|
||||
dbesc($member)
|
||||
);
|
||||
if($r)
|
||||
return true; // You might question this, but
|
||||
// we indicate success because the group member was in fact created
|
||||
// -- It was just created at another time
|
||||
if(! $r)
|
||||
$r = q("INSERT INTO pgrp_member (uid, gid, xchan)
|
||||
VALUES( %d, %d, '%s' ) ",
|
||||
intval($uid),
|
||||
intval($gid),
|
||||
dbesc($member)
|
||||
);
|
||||
|
||||
Libsync::build_sync_packet($uid,null,true);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
||||
static function members($gid) {
|
||||
$ret = array();
|
||||
if(intval($gid)) {
|
||||
$r = q("SELECT * FROM pgrp_member
|
||||
LEFT JOIN abook ON abook_xchan = pgrp_member.xchan left join xchan on xchan_hash = abook_xchan
|
||||
WHERE gid = %d AND abook_channel = %d and pgrp_member.uid = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC ",
|
||||
intval($gid),
|
||||
intval(local_channel()),
|
||||
intval(local_channel())
|
||||
);
|
||||
if($r)
|
||||
$ret = $r;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static function members_xchan($gid) {
|
||||
$ret = [];
|
||||
if(intval($gid)) {
|
||||
$r = q("SELECT xchan FROM pgrp_member WHERE gid = %d AND uid = %d",
|
||||
intval($gid),
|
||||
intval(local_channel())
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$ret[] = $rr['xchan'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static function members_profile_xchan($uid,$gid) {
|
||||
$ret = [];
|
||||
|
||||
if(intval($gid)) {
|
||||
$r = q("SELECT abook_xchan as xchan from abook left join profile on abook_profile = profile_guid where profile.id = %d and profile.uid = %d",
|
||||
intval($gid),
|
||||
intval($uid)
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$ret[] = $rr['xchan'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static function select($uid,$group = '') {
|
||||
|
||||
$grps = [];
|
||||
$o = '';
|
||||
|
||||
$r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
|
||||
intval($uid)
|
||||
);
|
||||
$grps[] = array('name' => '', 'hash' => '0', 'selected' => '');
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$grps[] = array('name' => $rr['gname'], 'id' => $rr['hash'], 'selected' => (($group == $rr['hash']) ? 'true' : ''));
|
||||
}
|
||||
|
||||
}
|
||||
logger('select: ' . print_r($grps,true), LOGGER_DATA);
|
||||
|
||||
$o = replace_macros(get_markup_template('group_selection.tpl'), array(
|
||||
'$label' => t('Add new connections to this privacy group'),
|
||||
'$groups' => $grps
|
||||
));
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static function widget($every="connections",$each="group",$edit = false, $group_id = 0, $cid = '',$mode = 1) {
|
||||
|
||||
$o = '';
|
||||
|
||||
if(! (local_channel() && feature_enabled(local_channel(),'groups'))) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$groups = array();
|
||||
|
||||
$r = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
|
||||
intval($_SESSION['uid'])
|
||||
);
|
||||
$member_of = array();
|
||||
if($cid) {
|
||||
$member_of = self::containing(local_channel(),$cid);
|
||||
}
|
||||
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$selected = (($group_id == $rr['id']) ? ' group-selected' : '');
|
||||
|
||||
if ($edit) {
|
||||
$groupedit = [ 'href' => "group/".$rr['id'], 'title' => t('edit') ];
|
||||
}
|
||||
else {
|
||||
$groupedit = null;
|
||||
}
|
||||
|
||||
$groups[] = [
|
||||
'id' => $rr['id'],
|
||||
'enc_cid' => base64url_encode($cid),
|
||||
'cid' => $cid,
|
||||
'text' => $rr['gname'],
|
||||
'selected' => $selected,
|
||||
'href' => (($mode == 0) ? $each.'?f=&gid='.$rr['id'] : $each."/".$rr['id']) . ((x($_GET,'new')) ? '&new=' . $_GET['new'] : '') . ((x($_GET,'order')) ? '&order=' . $_GET['order'] : ''),
|
||||
'edit' => $groupedit,
|
||||
'ismember' => in_array($rr['id'],$member_of),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$tpl = get_markup_template("group_side.tpl");
|
||||
$o = replace_macros($tpl, array(
|
||||
'$title' => t('Privacy Groups'),
|
||||
'$edittext' => t('Edit group'),
|
||||
'$createtext' => t('Add privacy group'),
|
||||
'$ungrouped' => (($every === 'contacts') ? t('Channels not in any privacy group') : ''),
|
||||
'$groups' => $groups,
|
||||
'$add' => t('add'),
|
||||
));
|
||||
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
static function expand($g) {
|
||||
if(! (is_array($g) && count($g)))
|
||||
return array();
|
||||
|
||||
$ret = [];
|
||||
$x = [];
|
||||
|
||||
// private profile linked virtual groups
|
||||
|
||||
foreach($g as $gv) {
|
||||
if(substr($gv,0,3) === 'vp.') {
|
||||
$profile_hash = substr($gv,3);
|
||||
if($profile_hash) {
|
||||
$r = q("select abook_xchan from abook where abook_profile = '%s'",
|
||||
dbesc($profile_hash)
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rv) {
|
||||
$ret[] = $rv['abook_xchan'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$x[] = $gv;
|
||||
}
|
||||
}
|
||||
|
||||
if($x) {
|
||||
stringify_array_elms($x,true);
|
||||
$groups = implode(',', $x);
|
||||
if($groups) {
|
||||
$r = q("SELECT xchan FROM pgrp_member WHERE gid IN ( select id from pgrp where hash in ( $groups ))");
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$ret[] = $rr['xchan'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
static function member_of($c) {
|
||||
$r = q("SELECT pgrp.gname, pgrp.id FROM pgrp LEFT JOIN pgrp_member ON pgrp_member.gid = pgrp.id WHERE pgrp_member.xchan = '%s' AND pgrp.deleted = 0 ORDER BY pgrp.gname ASC ",
|
||||
dbesc($c)
|
||||
);
|
||||
|
||||
return $r;
|
||||
|
||||
}
|
||||
|
||||
static function containing($uid,$c) {
|
||||
|
||||
$r = q("SELECT gid FROM pgrp_member WHERE uid = %d AND pgrp_member.xchan = '%s' ",
|
||||
intval($uid),
|
||||
dbesc($c)
|
||||
);
|
||||
|
||||
$ret = array();
|
||||
if($r) {
|
||||
foreach($r as $rr)
|
||||
$ret[] = $rr['gid'];
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ class IConfig {
|
||||
static public function Get(&$item, $family, $key, $default = false) {
|
||||
|
||||
$is_item = false;
|
||||
|
||||
|
||||
if(is_array($item)) {
|
||||
$is_item = true;
|
||||
if((! array_key_exists('iconfig',$item)) || (! is_array($item['iconfig'])))
|
||||
@@ -22,7 +22,7 @@ class IConfig {
|
||||
if(array_key_exists('item_id',$item))
|
||||
$iid = $item['item_id'];
|
||||
else
|
||||
$iid = $item['id'];
|
||||
$iid = $item['id'] ?? 0;
|
||||
}
|
||||
elseif(intval($item))
|
||||
$iid = $item;
|
||||
@@ -36,7 +36,7 @@ class IConfig {
|
||||
return $c['v'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$r = q("select * from iconfig where iid = %d and cat = '%s' and k = '%s' limit 1",
|
||||
intval($iid),
|
||||
dbesc($family),
|
||||
@@ -63,11 +63,11 @@ class IConfig {
|
||||
* $value - value of meta variable
|
||||
* $sharing - boolean (default false); if true the meta information is propagated with the item
|
||||
* to other sites/channels, mostly useful when $item is an array and has not yet been stored/delivered.
|
||||
* If the meta information is added after delivery and you wish it to be shared, it may be necessary to
|
||||
* alter the item edited timestamp and invoke the delivery process on the updated item. The edited
|
||||
* If the meta information is added after delivery and you wish it to be shared, it may be necessary to
|
||||
* alter the item edited timestamp and invoke the delivery process on the updated item. The edited
|
||||
* timestamp needs to be altered in order to trigger an item_store_update() at the receiving end.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
static public function Set(&$item, $family, $key, $value, $sharing = false) {
|
||||
|
||||
@@ -162,4 +162,4 @@ class IConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class JSalmon {
|
||||
. base64url_encode($x['alg'],true);
|
||||
|
||||
$key = HTTPSig::get_key(EMPTY_STR,'zot6',base64url_decode($x['sigs']['key_id']));
|
||||
logger('key: ' . print_r($key,true));
|
||||
logger('key: ' . print_r($key,true), LOGGER_DATA);
|
||||
if($key['portable_id'] && $key['public_key']) {
|
||||
if(Crypto::verify($signed_data,base64url_decode($x['sigs']['value']),$key['public_key'])) {
|
||||
logger('verified');
|
||||
|
||||
92
Zotlabs/Lib/JcsEddsa2022.php
Normal file
92
Zotlabs/Lib/JcsEddsa2022.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Mmccook\JsonCanonicalizator\JsonCanonicalizatorFactory;
|
||||
use StephenHill\Base58;
|
||||
|
||||
class JcsEddsa2022 {
|
||||
|
||||
public function __construct() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function sign($data, $channel): array {
|
||||
$base58 = new Base58();
|
||||
$pubkey = (new Multibase())->publicKey($channel['channel_epubkey']);
|
||||
$options = [
|
||||
'type' => 'DataIntegrityProof',
|
||||
'cryptosuite' => 'eddsa-jcs-2022',
|
||||
'created' => datetime_convert('UTC', 'UTC', 'now', ATOM_TIME),
|
||||
'verificationMethod' => channel_url($channel) . '#' . $pubkey,
|
||||
'proofPurpose' => 'assertionMethod',
|
||||
];
|
||||
|
||||
$optionsHash = $this->hash($this->signableOptions($options), true);
|
||||
$dataHash = $this->hash($this->signableData($data), true);
|
||||
|
||||
$options['proofValue'] = 'z' . $base58->encode(sodium_crypto_sign_detached($optionsHash . $dataHash,
|
||||
sodium_base642bin($channel['channel_eprvkey'], SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING)));
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function verify($data, $publicKey) {
|
||||
$base58 = new Base58();
|
||||
$encodedSignature = $data['proof']['proofValue'] ?? '';
|
||||
if (!str_starts_with($encodedSignature,'z')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$encodedSignature = substr($encodedSignature, 1);
|
||||
$optionsHash = $this->hash($this->signableOptions($data['proof']), true);
|
||||
$dataHash = $this->hash($this->signableData($data),true);
|
||||
|
||||
try {
|
||||
$result = sodium_crypto_sign_verify_detached($base58->decode($encodedSignature), $optionsHash . $dataHash,
|
||||
(new Multibase())->decode($publicKey, true));
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
logger('verify exception:' . $e->getMessage());
|
||||
}
|
||||
|
||||
logger('SignatureVerify (eddsa-jcs-2022) ' . (($result) ? 'true' : 'false'));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function signableData($data) {
|
||||
$signableData = [];
|
||||
if ($data) {
|
||||
foreach ($data as $k => $v) {
|
||||
if (!in_array($k, ['proof', 'signature'])) {
|
||||
$signableData[$k] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $signableData;
|
||||
}
|
||||
|
||||
public function signableOptions($options) {
|
||||
$signableOptions = [];
|
||||
|
||||
if ($options) {
|
||||
foreach ($options as $k => $v) {
|
||||
if ($k !== 'proofValue') {
|
||||
$signableOptions[$k] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $signableOptions;
|
||||
}
|
||||
|
||||
public function hash($obj, $binary = false) {
|
||||
return hash('sha256', $this->canonicalize($obj), $binary);
|
||||
}
|
||||
|
||||
public function canonicalize($data) {
|
||||
$canonicalization = JsonCanonicalizatorFactory::getInstance();
|
||||
return $canonicalization->canonicalize($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -75,22 +75,23 @@ class LDSignatures {
|
||||
}
|
||||
|
||||
static function hash($obj) {
|
||||
|
||||
return hash('sha256',self::normalise($obj));
|
||||
return hash('sha256', self::normalise($obj));
|
||||
}
|
||||
|
||||
static function normalise($data) {
|
||||
$ret = '';
|
||||
|
||||
if(is_string($data)) {
|
||||
$data = json_decode($data);
|
||||
}
|
||||
|
||||
if(! is_object($data))
|
||||
return '';
|
||||
return $ret;
|
||||
|
||||
jsonld_set_document_loader('jsonld_document_loader');
|
||||
|
||||
try {
|
||||
$d = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]);
|
||||
$ret = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// Don't log the exception - this can exhaust memory
|
||||
@@ -98,7 +99,7 @@ class LDSignatures {
|
||||
logger('normalise error: ' . print_r($data,true));
|
||||
}
|
||||
|
||||
return $d;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static function salmon_sign($data,$channel) {
|
||||
|
||||
@@ -135,14 +135,13 @@ class Libsync {
|
||||
$info['collection_members'] = $r;
|
||||
}
|
||||
|
||||
$interval = ((get_config('system', 'delivery_interval') !== false)
|
||||
? intval(get_config('system', 'delivery_interval')) : 2);
|
||||
$interval = get_config('queueworker', 'queue_interval', 500000);
|
||||
|
||||
logger('Packet: ' . print_r($info, true), LOGGER_DATA, LOG_DEBUG);
|
||||
|
||||
$total = count($synchubs);
|
||||
foreach ($synchubs as $hub) {
|
||||
$hash = random_string();
|
||||
$hash = new_uuid();
|
||||
$n = Libzot::build_packet($channel, 'sync', $env_recips, json_encode($info), 'hz', $hub['hubloc_sitekey'], $hub['site_crypto']);
|
||||
Queue::insert([
|
||||
'hash' => $hash,
|
||||
@@ -155,19 +154,26 @@ class Libsync {
|
||||
]);
|
||||
|
||||
|
||||
/*
|
||||
$x = q("select count(outq_hash) as total from outq where outq_delivered = 0");
|
||||
|
||||
if (intval($x[0]['total']) > intval(get_config('system', 'force_queue_threshold', 3000))) {
|
||||
logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO);
|
||||
Queue::update($hash);
|
||||
continue;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
Master::Summon(['Deliver', $hash]);
|
||||
$total = $total - 1;
|
||||
|
||||
if ($interval && $total)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
/*
|
||||
$total = $total - 1;
|
||||
*/
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +198,10 @@ class Libsync {
|
||||
dbesc($sender)
|
||||
);
|
||||
|
||||
$DR = new DReport(z_root(), $sender, $d, 'sync');
|
||||
$mid = 'sync';
|
||||
|
||||
|
||||
$DR = new DReport(z_root(), $sender, $d, $mid);
|
||||
|
||||
if (!$r) {
|
||||
$DR->update('recipient not found');
|
||||
@@ -202,6 +211,7 @@ class Libsync {
|
||||
|
||||
$channel = $r[0];
|
||||
|
||||
|
||||
$DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>');
|
||||
|
||||
$max_friends = service_class_fetch($channel['channel_id'], 'total_channels');
|
||||
@@ -230,8 +240,35 @@ class Libsync {
|
||||
|
||||
if (array_key_exists('config', $arr) && is_array($arr['config']) && count($arr['config'])) {
|
||||
foreach ($arr['config'] as $cat => $k) {
|
||||
foreach ($arr['config'][$cat] as $k => $v)
|
||||
set_pconfig($channel['channel_id'], $cat, $k, $v);
|
||||
$pconfig_updated = [];
|
||||
|
||||
foreach($arr['config'][$cat] as $k => $v) {
|
||||
if ($cat === 'hz_delpconfig' && strpos($k, 'b64.') === 0) {
|
||||
$delpconfig = explode(':', unpack_link_id($k));
|
||||
|
||||
// delete the provided pconfig
|
||||
del_pconfig($channel['channel_id'], $delpconfig[0], $delpconfig[1], $v);
|
||||
|
||||
// delete the messenger pconfig
|
||||
del_pconfig($channel['channel_id'], 'hz_delpconfig', $k);
|
||||
}
|
||||
|
||||
if (strpos($k,'pcfgud:') === 0) {
|
||||
$realk = substr($k,7);
|
||||
$pconfig_updated[$realk] = $v;
|
||||
unset($arr['config'][$cat][$k]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($arr['config'][$cat] as $k => $v) {
|
||||
if (!isset($pconfig_updated[$k])) {
|
||||
$pconfig_updated[$k] = NULL;
|
||||
}
|
||||
|
||||
if ($cat !== 'hz_delpconfig') {
|
||||
set_pconfig($channel['channel_id'],$cat,$k,$v,$pconfig_updated[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,8 +303,10 @@ class Libsync {
|
||||
if (array_key_exists('event_item', $arr) && $arr['event_item'])
|
||||
sync_items($channel, $arr['event_item'], ((array_key_exists('relocate', $arr)) ? $arr['relocate'] : null));
|
||||
|
||||
if (array_key_exists('item', $arr) && $arr['item'])
|
||||
if (array_key_exists('item', $arr) && $arr['item']) {
|
||||
sync_items($channel, $arr['item'], ((array_key_exists('relocate', $arr)) ? $arr['relocate'] : null));
|
||||
$mid = $arr['item'][0]['message_id'] . '#sync';
|
||||
}
|
||||
|
||||
// deprecated, maintaining for a few months for upward compatibility
|
||||
// this should sync webpages, but the logic is a bit subtle
|
||||
@@ -286,10 +325,7 @@ class Libsync {
|
||||
|
||||
if (array_key_exists('channel', $arr) && is_array($arr['channel']) && count($arr['channel'])) {
|
||||
|
||||
$remote_channel = $arr['channel'];
|
||||
$remote_channel['channel_id'] = $channel['channel_id'];
|
||||
|
||||
if (array_key_exists('channel_pageflags', $arr['channel']) && intval($arr['channel']['channel_pageflags'])) {
|
||||
if (array_key_exists('channel_pageflags', $arr['channel'])) {
|
||||
|
||||
// Several pageflags are site-specific and cannot be sync'd.
|
||||
// Only allow those bits which are shareable from the remote and then
|
||||
@@ -300,6 +336,8 @@ class Libsync {
|
||||
|
||||
}
|
||||
|
||||
$columns = db_columns('channel');
|
||||
|
||||
$disallowed = [
|
||||
'channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey',
|
||||
'channel_address', 'channel_notifyflags', 'channel_removed', 'channel_deleted',
|
||||
@@ -310,16 +348,21 @@ class Libsync {
|
||||
'channel_a_delegate'
|
||||
];
|
||||
|
||||
$clean = [];
|
||||
foreach ($arr['channel'] as $k => $v) {
|
||||
if (in_array($k, $disallowed))
|
||||
continue;
|
||||
$clean[$k] = $v;
|
||||
if (empty($channel['channel_epubkey']) && empty($channel['channel_eprvkey'])) {
|
||||
$eckey = sodium_crypto_sign_keypair();
|
||||
$channel['channel_epubkey'] = sodium_bin2base64(sodium_crypto_sign_publickey($eckey), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
|
||||
$channel['channel_eprvkey'] = sodium_bin2base64(sodium_crypto_sign_secretkey($eckey), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
|
||||
}
|
||||
if (count($clean)) {
|
||||
foreach ($clean as $k => $v) {
|
||||
dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v) . "' where channel_id = " . intval($channel['channel_id']));
|
||||
|
||||
foreach ($arr['channel'] as $k => $v) {
|
||||
if (in_array($k, $disallowed)) {
|
||||
continue;
|
||||
}
|
||||
if (!in_array($k, $columns)) {
|
||||
continue;
|
||||
}
|
||||
$r = dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v)
|
||||
. "' where channel_id = " . intval($channel['channel_id']));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,19 +427,42 @@ class Libsync {
|
||||
// This relies on the undocumented behaviour that red sites send xchan info with the abook
|
||||
// and import_author_xchan will look them up on all federated networks
|
||||
|
||||
if ($abook['abook_xchan'] && $abook['xchan_addr']) {
|
||||
$found = false;
|
||||
if ($abook['abook_xchan'] && $abook['xchan_addr'] && (! in_array($abook['xchan_network'], [ 'token', 'unknown' ]))) {
|
||||
$h = Libzot::get_hublocs($abook['abook_xchan']);
|
||||
if (!$h) {
|
||||
if ($h) {
|
||||
$found = true;
|
||||
}
|
||||
else {
|
||||
$xhash = import_author_xchan(encode_item_xchan($abook));
|
||||
if (!$xhash) {
|
||||
if ($xhash) {
|
||||
$found = true;
|
||||
}
|
||||
else {
|
||||
logger('Import of ' . $abook['xchan_addr'] . ' failed.');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found && !in_array($abook['xchan_network'], ['zot6', 'activitypub', 'diaspora'])) {
|
||||
// just import the record.
|
||||
$xc = [];
|
||||
foreach ($abook as $k => $v) {
|
||||
if (strpos($k,'xchan_') === 0) {
|
||||
$xc[$k] = $v;
|
||||
}
|
||||
}
|
||||
$r = q("select * from xchan where xchan_hash = '%s'",
|
||||
dbesc($xc['xchan_hash'])
|
||||
);
|
||||
if (! $r) {
|
||||
xchan_store_lowlevel($xc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach ($abook as $k => $v) {
|
||||
if (in_array($k, $disallowed) || (strpos($k, 'abook') !== 0)) {
|
||||
if (in_array($k, $disallowed) || (strpos($k, 'abook_') !== 0)) {
|
||||
continue;
|
||||
}
|
||||
if (!in_array($k, $fields)) {
|
||||
@@ -410,6 +476,13 @@ class Libsync {
|
||||
|
||||
if (array_key_exists('abook_instance', $clean) && $clean['abook_instance'] && strpos($clean['abook_instance'], z_root()) === false) {
|
||||
$clean['abook_not_here'] = 1;
|
||||
|
||||
// guest pass or access token - don't try to probe since it is one-way
|
||||
// we are relying on the undocumented behaviour that the abook record also contains the xchan
|
||||
if ($abook['xchan_network'] === 'token') {
|
||||
$clean['abook_instance'] .= ',';
|
||||
$clean['abook_instance'] .= z_root();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -683,13 +756,12 @@ class Libsync {
|
||||
*/
|
||||
call_hooks('process_channel_sync_delivery', $addon);
|
||||
|
||||
$DR = new DReport(z_root(), $d, $d, 'sync', 'channel sync delivered');
|
||||
$DR = new DReport(z_root(), $d, $d, $mid, 'channel sync processed');
|
||||
|
||||
$DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>');
|
||||
|
||||
$result[] = $DR->get();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -698,13 +770,19 @@ class Libsync {
|
||||
*
|
||||
* @param array $sender
|
||||
* @param array $arr
|
||||
* @param boolean $absolute (optional) default false
|
||||
* @return array
|
||||
*/
|
||||
|
||||
static function sync_locations($sender, $arr, $absolute = false) {
|
||||
static function sync_locations($sender, $arr) {
|
||||
|
||||
$ret = [];
|
||||
$ret = [
|
||||
'change_message' => '',
|
||||
'changed' => false,
|
||||
'message' => ''
|
||||
];
|
||||
|
||||
$what = '';
|
||||
$changed = false;
|
||||
|
||||
// If a sender reports that the channel has been deleted, delete its hubloc
|
||||
if (isset($arr['deleted_locally']) && intval($arr['deleted_locally'])) {
|
||||
@@ -715,12 +793,9 @@ class Libsync {
|
||||
);
|
||||
}
|
||||
|
||||
if ($arr['locations']) {
|
||||
if (isset($arr['locations']) && $arr['locations']) {
|
||||
|
||||
if ($absolute)
|
||||
Libzot::check_location_move($sender['hash'], $arr['locations']);
|
||||
|
||||
$xisting = q("select * from hubloc where hubloc_hash = '%s'",
|
||||
$xisting = q("select * from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0",
|
||||
dbesc($sender['hash'])
|
||||
);
|
||||
|
||||
@@ -768,14 +843,13 @@ class Libsync {
|
||||
|
||||
// match as many fields as possible in case anything at all changed.
|
||||
|
||||
$r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_id_url = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_site_id = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ",
|
||||
$r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_id_url = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ",
|
||||
dbesc($sender['hash']),
|
||||
dbesc($sender['id']),
|
||||
dbesc($sender['id_sig']),
|
||||
dbesc($location['id_url']),
|
||||
dbesc($location['url']),
|
||||
dbesc($location['url_sig']),
|
||||
dbesc($location['site_id']),
|
||||
dbesc($location['host']),
|
||||
dbesc($location['address']),
|
||||
dbesc($location['callback']),
|
||||
@@ -784,7 +858,18 @@ class Libsync {
|
||||
if ($r) {
|
||||
logger('Hub exists: ' . $location['url'], LOGGER_DEBUG);
|
||||
|
||||
// update connection timestamp if this is the site we're talking to
|
||||
// generate a new hubloc_site_id if it's wrong due to historical bugs 2021-11-30
|
||||
|
||||
if ($r[0]['hubloc_site_id'] !== $location['site_id']) {
|
||||
q("update hubloc set hubloc_site_id = '%s' where hubloc_id = %d",
|
||||
dbesc(Libzot::make_xchan_hash($location['url'], $location['sitekey'])),
|
||||
intval($r[0]['hubloc_id'])
|
||||
);
|
||||
}
|
||||
|
||||
// Update connection timestamp if this is the site we're talking to.
|
||||
// Also mark all entries from the current site with different sitekeys
|
||||
// deleted (the site has been re-installed)
|
||||
// This only happens when called from import_xchan
|
||||
|
||||
$current_site = false;
|
||||
@@ -798,6 +883,12 @@ class Libsync {
|
||||
intval($r[0]['hubloc_id']),
|
||||
dbesc($t)
|
||||
);
|
||||
|
||||
q("update hubloc set hubloc_error = 1, hubloc_deleted = 1 where hubloc_url = '%s' and hubloc_sitekey != '%s'",
|
||||
dbesc($r[0]['hubloc_url']),
|
||||
dbesc($r[0]['hubloc_sitekey'])
|
||||
);
|
||||
|
||||
$current_site = true;
|
||||
}
|
||||
|
||||
@@ -847,14 +938,7 @@ class Libsync {
|
||||
$what .= 'primary_hub ';
|
||||
$changed = true;
|
||||
}
|
||||
elseif ($absolute) {
|
||||
// Absolute sync - make sure the current primary is correctly reflected in the xchan
|
||||
$pr = hubloc_change_primary($r[0]);
|
||||
if ($pr) {
|
||||
$what .= 'xchan_primary ';
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (intval($r[0]['hubloc_deleted']) && (!intval($location['deleted']))) {
|
||||
q("update hubloc set hubloc_deleted = 0, hubloc_updated = '%s' where hubloc_id_url = '%s'",
|
||||
dbesc(datetime_convert()),
|
||||
@@ -923,9 +1007,9 @@ class Libsync {
|
||||
}
|
||||
}
|
||||
|
||||
// get rid of any hubs we have for this channel which weren't reported.
|
||||
// get rid of any hublocs we have for this channel which weren't reported.
|
||||
|
||||
if ($absolute && $xisting) {
|
||||
if ($xisting) {
|
||||
foreach ($xisting as $x) {
|
||||
if (!array_key_exists('updated', $x)) {
|
||||
logger('Deleting unreferenced hub location ' . $x['hubloc_addr']);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -145,8 +145,8 @@ class Libzotdir {
|
||||
if(! $directory_sort_order)
|
||||
$directory_sort_order = 'date';
|
||||
|
||||
$current_order = (($_REQUEST['order']) ? $_REQUEST['order'] : $directory_sort_order);
|
||||
$suggest = (($_REQUEST['suggest']) ? '&suggest=' . $_REQUEST['suggest'] : '');
|
||||
$current_order = $_REQUEST['order'] ?? $directory_sort_order;
|
||||
$suggest = ((isset($_REQUEST['suggest'])) ? '&suggest=' . $_REQUEST['suggest'] : '');
|
||||
|
||||
$url = 'directory?f=';
|
||||
|
||||
@@ -172,13 +172,12 @@ class Libzotdir {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks the directory mode of this hub.
|
||||
* @brief fetches updates from known directories
|
||||
*
|
||||
* Checks the directory mode of this hub to see if it is some form of directory server. If it is,
|
||||
* get the directory realm of this hub. Fetch a list of all other directory servers in this realm and request
|
||||
* a directory sync packet. This will contain both directory updates and new ratings. Store these all in the DB.
|
||||
* In the case of updates, we will query each of them asynchronously from a poller task. Ratings are stored
|
||||
* directly if the rater's signature matches.
|
||||
* a directory sync packet. Store these all in the DB.
|
||||
* In the case of updates, we will query each of them asynchronously from a poller task.
|
||||
*
|
||||
* @param int $dirmode;
|
||||
*/
|
||||
@@ -233,6 +232,8 @@ class Libzotdir {
|
||||
if (! $r)
|
||||
return;
|
||||
|
||||
$dir_trusted_hosts = array_merge(get_directory_fallback_servers(), get_config('system', 'trusted_directory_servers', []));
|
||||
|
||||
foreach ($r as $rr) {
|
||||
if (! $rr['site_directory'])
|
||||
continue;
|
||||
@@ -264,31 +265,65 @@ class Libzotdir {
|
||||
|
||||
if (is_array($j['transactions']) && count($j['transactions'])) {
|
||||
foreach ($j['transactions'] as $t) {
|
||||
|
||||
if (empty($t['hash']) || empty($t['transaction_id']) || empty($t['address'])) {
|
||||
if (empty($t['hash']) || empty($t['host']) || empty($t['address'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$r = q("select * from updates where ud_guid = '%s' limit 1",
|
||||
dbesc($t['transaction_id'])
|
||||
$r = q("select * from updates where ud_hash = '%s' limit 1",
|
||||
dbesc($t['hash'])
|
||||
);
|
||||
if($r)
|
||||
continue;
|
||||
|
||||
$ud_flags = 0;
|
||||
if (is_array($t['flags']) && in_array('deleted',$t['flags']))
|
||||
$ud_flags |= UPDATE_FLAGS_DELETED;
|
||||
if (is_array($t['flags']) && in_array('forced',$t['flags']))
|
||||
$ud_flags |= UPDATE_FLAGS_FORCED;
|
||||
if ($r) {
|
||||
$update = 0;
|
||||
|
||||
$z = q("insert into updates ( ud_hash, ud_guid, ud_date, ud_flags, ud_addr )
|
||||
values ( '%s', '%s', '%s', %d, '%s' ) ",
|
||||
dbesc($t['hash']),
|
||||
dbesc($t['transaction_id']),
|
||||
dbesc($t['timestamp']),
|
||||
intval($ud_flags),
|
||||
dbesc($t['address'])
|
||||
);
|
||||
// no need to look at updates that originated from our own site
|
||||
if ($t['host'] === z_root()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// there is more recent xchan information
|
||||
if ($r[0]['ud_date'] <= $t['timestamp']) {
|
||||
$update = 1;
|
||||
}
|
||||
|
||||
// the host is trusted and flags have changed - update flags immediately
|
||||
if (in_array($t['host'], $dir_trusted_hosts) &&
|
||||
$rr['site_url'] === $t['host'] &&
|
||||
intval($r[0]['ud_flags']) !== intval($t['flags'])) {
|
||||
|
||||
q("UPDATE updates SET ud_update = %d, ud_flags = %d WHERE ud_id = %d",
|
||||
intval($update),
|
||||
intval($t['flags']),
|
||||
dbesc($r[0]['ud_id'])
|
||||
);
|
||||
|
||||
q("UPDATE xchan SET xchan_censored = %d WHERE xchan_hash = '%s'",
|
||||
intval($t['flags']),
|
||||
dbesc($r[0]['ud_hash'])
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$update) {
|
||||
continue;
|
||||
}
|
||||
|
||||
q("UPDATE updates SET ud_update = %d WHERE ud_id = %d",
|
||||
intval($update),
|
||||
dbesc($r[0]['ud_id'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
q("insert into updates ( ud_hash, ud_host, ud_date, ud_addr, ud_update, ud_flags )
|
||||
values ( '%s', '%s', '%s', '%s', 1, %d) ",
|
||||
dbesc($t['hash']),
|
||||
dbesc($t['host']),
|
||||
dbesc($t['timestamp']),
|
||||
dbesc($t['address']),
|
||||
dbesc(in_array($t['host'], $dir_trusted_hosts) ? $t['flags'] : 0)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,8 +338,9 @@ class Libzotdir {
|
||||
*
|
||||
* Ignore updating records marked as deleted.
|
||||
*
|
||||
* If successful, sets ud_last in the DB to the current datetime for this
|
||||
* If successful, sets ud_updated in the DB to the current datetime for this
|
||||
* reddress/webbie.
|
||||
* Else update ud_last so we can stop trying after 7 days (Daemon/Poller.php)
|
||||
*
|
||||
* @param array $ud Entry from update table
|
||||
*/
|
||||
@@ -313,32 +349,44 @@ class Libzotdir {
|
||||
|
||||
logger('update_directory_entry: ' . print_r($ud,true), LOGGER_DATA);
|
||||
|
||||
if ($ud['ud_addr'] && (! ($ud['ud_flags'] & UPDATE_FLAGS_DELETED))) {
|
||||
$success = false;
|
||||
$zf = [];
|
||||
// TODO: remove this check after all directory servers have version > 8.4
|
||||
// ud_addr will always be the channel url at that time
|
||||
$href = ((strpos($ud['ud_addr'], '://') === false) ? Webfinger::zot_url(punify($ud['ud_addr'])) : punify($ud['ud_addr']));
|
||||
if($href) {
|
||||
$zf = Zotfinger::exec($href);
|
||||
if($zf && array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) {
|
||||
$xc = Libzot::import_xchan($zf['data']);
|
||||
|
||||
// xchan_hash mismatch - this can happen after a site re-install at the same url
|
||||
if ($xc['success'] && $xc['hash'] !== $ud['ud_hash']) {
|
||||
self::delete_by_hash($ud['ud_hash']);
|
||||
}
|
||||
|
||||
// if the channel was deleted - delete the entry in updates
|
||||
if (!empty($zf['data']['deleted_locally'])) {
|
||||
self::delete_by_hash($ud['ud_hash']);
|
||||
}
|
||||
|
||||
$href = Webfinger::zot_url(punify($ud['ud_addr']));
|
||||
if($href) {
|
||||
$zf = Zotfinger::exec($href);
|
||||
}
|
||||
if(array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) {
|
||||
$xc = Libzot::import_xchan($zf['data'], 0, $ud);
|
||||
// This is a workaround for a missing xchan_updated column
|
||||
// TODO: implement xchan_updated in the xchan table and update this column instead
|
||||
if($zf['data']['primary_location']['address'] && $zf['data']['primary_location']['url']) {
|
||||
if(!empty($zf['data']['primary_location']['url'])) {
|
||||
q("UPDATE hubloc SET hubloc_updated = '%s' WHERE hubloc_id_url = '%s' AND hubloc_primary = 1",
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($zf['data']['primary_location']['url'])
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
q("update updates set ud_last = '%s' where ud_addr = '%s'",
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($ud['ud_addr'])
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
q("UPDATE updates SET ud_addr = '%s', ud_last = '%s' WHERE ud_hash = '%s'",
|
||||
dbesc($href ? $href : $ud['ud_addr']),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($ud['ud_hash'])
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -353,85 +401,78 @@ class Libzotdir {
|
||||
*/
|
||||
|
||||
static function local_dir_update($uid, $force) {
|
||||
logger('local_dir_update uid: ' . $uid, LOGGER_DEBUG);
|
||||
|
||||
|
||||
logger('local_dir_update: uid: ' . $uid, LOGGER_DEBUG);
|
||||
|
||||
$p = q("select channel.channel_hash, channel_address, channel_timezone, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1",
|
||||
$p = q("select channel.channel_hash, channel_address, channel_timezone, profile.*, xchan.xchan_hidden, xchan.xchan_url from profile left join channel on channel_id = uid left join xchan on channel_hash = xchan_hash where profile.uid = %d and profile.is_default = 1",
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
$profile = array();
|
||||
$profile['encoding'] = 'zot';
|
||||
|
||||
if ($p) {
|
||||
$hash = $p[0]['channel_hash'];
|
||||
|
||||
$profile['description'] = $p[0]['pdesc'];
|
||||
$profile['birthday'] = $p[0]['dob'];
|
||||
if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],''))
|
||||
$profile['age'] = $age;
|
||||
|
||||
$profile['gender'] = $p[0]['gender'];
|
||||
$profile['marital'] = $p[0]['marital'];
|
||||
$profile['sexual'] = $p[0]['sexual'];
|
||||
$profile['locale'] = $p[0]['locality'];
|
||||
$profile['region'] = $p[0]['region'];
|
||||
$profile['postcode'] = $p[0]['postal_code'];
|
||||
$profile['country'] = $p[0]['country_name'];
|
||||
$profile['about'] = $p[0]['about'];
|
||||
$profile['homepage'] = $p[0]['homepage'];
|
||||
$profile['hometown'] = $p[0]['hometown'];
|
||||
|
||||
if ($p[0]['keywords']) {
|
||||
$tags = array();
|
||||
$k = explode(' ', $p[0]['keywords']);
|
||||
if ($k)
|
||||
foreach ($k as $kk)
|
||||
if (trim($kk))
|
||||
$tags[] = trim($kk);
|
||||
|
||||
if ($tags)
|
||||
$profile['keywords'] = $tags;
|
||||
}
|
||||
|
||||
$hidden = (1 - intval($p[0]['publish']));
|
||||
|
||||
logger('hidden: ' . $hidden);
|
||||
|
||||
$r = q("select xchan_hidden from xchan where xchan_hash = '%s'",
|
||||
dbesc($p[0]['channel_hash'])
|
||||
);
|
||||
|
||||
if(intval($r[0]['xchan_hidden']) != $hidden) {
|
||||
$r = q("update xchan set xchan_hidden = %d where xchan_hash = '%s'",
|
||||
intval($hidden),
|
||||
dbesc($hash)
|
||||
);
|
||||
}
|
||||
|
||||
$arr = [ 'channel_id' => $uid, 'hash' => $hash, 'profile' => $profile ];
|
||||
call_hooks('local_dir_update', $arr);
|
||||
|
||||
$address = channel_reddress($p[0]);
|
||||
|
||||
if (perm_is_allowed($uid, '', 'view_profile')) {
|
||||
self::import_directory_profile($hash, $arr['profile'], $address, 0);
|
||||
}
|
||||
else {
|
||||
// they may have made it private
|
||||
q("delete from xprof where xprof_hash = '%s'",
|
||||
dbesc($hash)
|
||||
);
|
||||
q("delete from xtag where xtag_hash = '%s'",
|
||||
dbesc($hash)
|
||||
);
|
||||
}
|
||||
|
||||
if (!$p) {
|
||||
logger('profile not found');
|
||||
return;
|
||||
}
|
||||
|
||||
$ud_hash = random_string() . '@' . \App::get_hostname();
|
||||
self::update_modtime($hash, $ud_hash, channel_reddress($p[0]),(($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED));
|
||||
$profile = [];
|
||||
$profile['encoding'] = 'zot';
|
||||
|
||||
$hash = $p[0]['channel_hash'];
|
||||
|
||||
$profile['description'] = $p[0]['pdesc'];
|
||||
$profile['birthday'] = $p[0]['dob'];
|
||||
if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],''))
|
||||
$profile['age'] = $age;
|
||||
|
||||
$profile['gender'] = $p[0]['gender'];
|
||||
$profile['marital'] = $p[0]['marital'];
|
||||
$profile['sexual'] = $p[0]['sexual'];
|
||||
$profile['locale'] = $p[0]['locality'];
|
||||
$profile['region'] = $p[0]['region'];
|
||||
$profile['postcode'] = $p[0]['postal_code'];
|
||||
$profile['country'] = $p[0]['country_name'];
|
||||
$profile['about'] = $p[0]['about'];
|
||||
$profile['homepage'] = $p[0]['homepage'];
|
||||
$profile['hometown'] = $p[0]['hometown'];
|
||||
|
||||
if ($p[0]['keywords']) {
|
||||
$tags = array();
|
||||
$k = explode(' ', $p[0]['keywords']);
|
||||
if ($k)
|
||||
foreach ($k as $kk)
|
||||
if (trim($kk))
|
||||
$tags[] = trim($kk);
|
||||
|
||||
if ($tags)
|
||||
$profile['keywords'] = $tags;
|
||||
}
|
||||
|
||||
$hidden = (1 - intval($p[0]['publish']));
|
||||
|
||||
logger('hidden: ' . $hidden);
|
||||
|
||||
if(intval($p[0]['xchan_hidden']) !== $hidden) {
|
||||
q("update xchan set xchan_hidden = %d where xchan_hash = '%s'",
|
||||
intval($hidden),
|
||||
dbesc($hash)
|
||||
);
|
||||
}
|
||||
|
||||
$arr = [ 'channel_id' => $uid, 'hash' => $hash, 'profile' => $profile ];
|
||||
call_hooks('local_dir_update', $arr);
|
||||
|
||||
if (perm_is_allowed($uid, '', 'view_profile')) {
|
||||
self::import_directory_profile($hash, $arr['profile']);
|
||||
}
|
||||
else {
|
||||
// they may have made it private
|
||||
q("delete from xprof where xprof_hash = '%s'",
|
||||
dbesc($hash)
|
||||
);
|
||||
q("delete from xtag where xtag_hash = '%s'",
|
||||
dbesc($hash)
|
||||
);
|
||||
}
|
||||
|
||||
self::update($hash, $p[0]['xchan_url']);
|
||||
}
|
||||
|
||||
|
||||
@@ -441,39 +482,48 @@ class Libzotdir {
|
||||
*
|
||||
* @param string $hash
|
||||
* @param array $profile
|
||||
* @param string $addr
|
||||
* @param number $ud_flags (optional) UPDATE_FLAGS_UPDATED
|
||||
* @param number $suppress_update (optional) default 0
|
||||
* @return boolean $updated if something changed
|
||||
*/
|
||||
|
||||
static function import_directory_profile($hash, $profile, $addr, $ud_flags = UPDATE_FLAGS_UPDATED, $suppress_update = 0) {
|
||||
static function import_directory_profile($hash, $profile) {
|
||||
|
||||
logger('import_directory_profile', LOGGER_DEBUG);
|
||||
if (! $hash)
|
||||
return false;
|
||||
|
||||
$arr = array();
|
||||
$arr = [];
|
||||
|
||||
$arr['xprof_hash'] = $hash;
|
||||
$arr['xprof_dob'] = (($profile['birthday'] === '0000-00-00') ? $profile['birthday'] : datetime_convert('','',$profile['birthday'],'Y-m-d')); // !!!! check this for 0000 year
|
||||
$arr['xprof_age'] = (($profile['age']) ? intval($profile['age']) : 0);
|
||||
$arr['xprof_desc'] = (($profile['description']) ? htmlspecialchars($profile['description'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_gender'] = (($profile['gender']) ? htmlspecialchars($profile['gender'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_marital'] = (($profile['marital']) ? htmlspecialchars($profile['marital'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_sexual'] = (($profile['sexual']) ? htmlspecialchars($profile['sexual'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_locale'] = (($profile['locale']) ? htmlspecialchars($profile['locale'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_region'] = (($profile['region']) ? htmlspecialchars($profile['region'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_postcode'] = (($profile['postcode']) ? htmlspecialchars($profile['postcode'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_country'] = (($profile['country']) ? htmlspecialchars($profile['country'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_about'] = (($profile['about']) ? htmlspecialchars($profile['about'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_homepage'] = (($profile['homepage']) ? htmlspecialchars($profile['homepage'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_hometown'] = (($profile['hometown']) ? htmlspecialchars($profile['hometown'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_hash'] = $hash;
|
||||
$arr['xprof_dob'] = '0000-00-00';
|
||||
|
||||
if (isset($profile['birthday'])) {
|
||||
$arr['xprof_dob'] = (($profile['birthday'] === '0000-00-00')
|
||||
? $profile['birthday']
|
||||
: datetime_convert('', '', $profile['birthday'], 'Y-m-d')); // !!!! check this for 0000 year
|
||||
}
|
||||
|
||||
$arr['xprof_age'] = ((isset($profile['age']) && $profile['age']) ? intval($profile['age']) : 0);
|
||||
$arr['xprof_desc'] = ((isset($profile['description']) && $profile['description']) ? htmlspecialchars($profile['description'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_gender'] = ((isset($profile['gender']) && $profile['gender']) ? htmlspecialchars($profile['gender'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_marital'] = ((isset($profile['marital']) && $profile['marital']) ? htmlspecialchars($profile['marital'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_sexual'] = ((isset($profile['sexual']) && $profile['sexual']) ? htmlspecialchars($profile['sexual'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_locale'] = ((isset($profile['locale']) && $profile['locale']) ? htmlspecialchars($profile['locale'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_region'] = ((isset($profile['region']) && $profile['region']) ? htmlspecialchars($profile['region'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_postcode'] = ((isset($profile['postcode']) && $profile['postcode']) ? htmlspecialchars($profile['postcode'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_country'] = ((isset($profile['country']) && $profile['country']) ? htmlspecialchars($profile['country'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_about'] = ((isset($profile['about']) && $profile['about']) ? htmlspecialchars($profile['about'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_homepage'] = ((isset($profile['homepage']) && $profile['homepage']) ? htmlspecialchars($profile['homepage'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
$arr['xprof_hometown'] = ((isset($profile['hometown']) && $profile['hometown']) ? htmlspecialchars($profile['hometown'], ENT_COMPAT,'UTF-8',false) : '');
|
||||
|
||||
$clean = array();
|
||||
if (array_key_exists('keywords', $profile) and is_array($profile['keywords'])) {
|
||||
self::import_directory_keywords($hash,$profile['keywords']);
|
||||
|
||||
foreach ($profile['keywords'] as $kw) {
|
||||
if (in_array($kw, $clean)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$kw = trim(htmlspecialchars($kw,ENT_COMPAT, 'UTF-8', false));
|
||||
$kw = trim($kw, ',');
|
||||
$clean[] = $kw;
|
||||
@@ -580,9 +630,6 @@ class Libzotdir {
|
||||
*/
|
||||
call_hooks('import_directory_profile', $d);
|
||||
|
||||
if (($d['update']) && (! $suppress_update))
|
||||
self::update_modtime($arr['xprof_hash'],random_string() . '@' . \App::get_hostname(), $addr, $ud_flags);
|
||||
|
||||
return $d['update'];
|
||||
}
|
||||
|
||||
@@ -600,6 +647,10 @@ class Libzotdir {
|
||||
dbesc($hash)
|
||||
);
|
||||
|
||||
$xchan = q("select xchan_censored from xchan where xchan_hash = '%s'",
|
||||
dbesc($hash)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
foreach($r as $rr)
|
||||
$existing[] = $rr['xtag_term'];
|
||||
@@ -607,6 +658,10 @@ class Libzotdir {
|
||||
|
||||
$clean = array();
|
||||
foreach($keywords as $kw) {
|
||||
if (in_array($kw, $clean)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$kw = trim(htmlspecialchars($kw,ENT_COMPAT, 'UTF-8', false));
|
||||
$kw = trim($kw, ',');
|
||||
$clean[] = $kw;
|
||||
@@ -621,9 +676,10 @@ class Libzotdir {
|
||||
}
|
||||
foreach($clean as $x) {
|
||||
if(! in_array($x, $existing)) {
|
||||
$r = q("insert into xtag ( xtag_hash, xtag_term, xtag_flags) values ( '%s' ,'%s', 0 )",
|
||||
$r = q("insert into xtag ( xtag_hash, xtag_term, xtag_flags) values ( '%s' ,'%s', %d )",
|
||||
dbesc($hash),
|
||||
dbesc($x)
|
||||
dbesc($x),
|
||||
intval($xchan[0]['xchan_censored'])
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -633,13 +689,12 @@ class Libzotdir {
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param string $hash
|
||||
* @param string $guid
|
||||
* @param string $addr
|
||||
* @param int $flags (optional) default 0
|
||||
* @param string $hash the channel hash
|
||||
* @param string $addr the channel url
|
||||
* @param bool $bump_date (optional) default true
|
||||
*/
|
||||
|
||||
static function update_modtime($hash, $guid, $addr, $flags = 0) {
|
||||
static function update($hash, $addr, $bump_date = true, $flag = null) {
|
||||
|
||||
$dirmode = intval(get_config('system', 'directory_mode'));
|
||||
|
||||
@@ -647,26 +702,70 @@ class Libzotdir {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($hash) || empty($guid) || empty($addr)) {
|
||||
if (empty($hash) || empty($addr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if($flags) {
|
||||
q("insert into updates (ud_hash, ud_guid, ud_date, ud_flags, ud_addr ) values ( '%s', '%s', '%s', %d, '%s' )",
|
||||
dbesc($hash),
|
||||
dbesc($guid),
|
||||
dbesc(datetime_convert()),
|
||||
intval($flags),
|
||||
dbesc($addr)
|
||||
);
|
||||
$u = q("SELECT * FROM updates WHERE ud_hash = '%s' LIMIT 1",
|
||||
dbesc($hash)
|
||||
);
|
||||
|
||||
$date_sql = '';
|
||||
if ($bump_date) {
|
||||
$date_sql = "ud_date = '" . dbesc(datetime_convert()) . "',";
|
||||
}
|
||||
else {
|
||||
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ",
|
||||
intval(UPDATE_FLAGS_UPDATED),
|
||||
|
||||
$flag_sql = '';
|
||||
if ($flag !== null) {
|
||||
$flag_sql = "ud_flags = '" . intval($flag) . "',";
|
||||
}
|
||||
|
||||
|
||||
if ($u) {
|
||||
$x = q("UPDATE updates SET $date_sql $flag_sql ud_last = '%s', ud_host = '%s', ud_addr = '%s', ud_update = 0 WHERE ud_id = %d",
|
||||
dbesc(NULL_DATE),
|
||||
dbesc(z_root()),
|
||||
dbesc($addr),
|
||||
intval(UPDATE_FLAGS_UPDATED)
|
||||
intval($u[0]['ud_id'])
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
q("INSERT INTO updates (ud_hash, ud_host, ud_date, ud_addr, ud_flags) VALUES ( '%s', '%s', '%s', '%s', %d )",
|
||||
dbesc($hash),
|
||||
dbesc(z_root()),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($addr),
|
||||
intval($flag)
|
||||
);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief deletes a entry in updates by hash
|
||||
*
|
||||
* @param string $hash the channel hash
|
||||
* @return boolean
|
||||
*/
|
||||
|
||||
static function delete_by_hash($hash) {
|
||||
if (!$hash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$x = q("DELETE FROM updates WHERE ud_hash = '%s'",
|
||||
dbesc($hash)
|
||||
);
|
||||
|
||||
if ($x) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,85 +2,120 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
|
||||
require_once('include/html2plain.php');
|
||||
|
||||
class MessageFilter {
|
||||
|
||||
public static function evaluate($item, $incl, $excl) {
|
||||
|
||||
static public function evaluate($item,$incl,$excl) {
|
||||
|
||||
require_once('include/html2plain.php');
|
||||
|
||||
$text = prepare_text($item['body'],$item['mimetype']);
|
||||
$text = prepare_text($item['body'],((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
|
||||
$text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text);
|
||||
|
||||
|
||||
$lang = null;
|
||||
|
||||
if((strpos($incl,'lang=') !== false) || (strpos($excl,'lang=') !== false) || (strpos($incl,'lang!=') !== false) || (strpos($excl,'lang!=') !== false)) {
|
||||
if ((strpos($incl, 'lang=') !== false) || (strpos($excl, 'lang=') !== false) || (strpos($incl, 'lang!=') !== false) || (strpos($excl, 'lang!=') !== false)) {
|
||||
$lang = detect_language($text);
|
||||
}
|
||||
|
||||
$tags = ((is_array($item['term']) && count($item['term'])) ? $item['term'] : false);
|
||||
$tags = ((isset($item['term']) && is_array($item['term']) && count($item['term'])) ? $item['term'] : false);
|
||||
|
||||
// exclude always has priority
|
||||
|
||||
$exclude = (($excl) ? explode("\n",$excl) : null);
|
||||
$exclude = (($excl) ? explode("\n", $excl) : null);
|
||||
|
||||
if($exclude) {
|
||||
foreach($exclude as $word) {
|
||||
$word = trim($word);
|
||||
if(! $word)
|
||||
if ($exclude) {
|
||||
foreach ($exclude as $word) {
|
||||
$word = html_entity_decode(trim($word));
|
||||
if (! $word) {
|
||||
continue;
|
||||
if(substr($word,0,1) === '#' && $tags) {
|
||||
foreach($tags as $t)
|
||||
if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*')))
|
||||
return false;
|
||||
}
|
||||
elseif(substr($word,0,1) === '$' && $tags) {
|
||||
foreach($tags as $t)
|
||||
if(($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*')))
|
||||
return false;
|
||||
if (isset($lang) && ((strpos($word, 'lang=') === 0) || (strpos($word, 'lang!=') === 0))) {
|
||||
if (!strlen($lang)) {
|
||||
// Result is ambiguous. As we are matching deny rules only at this time, continue tests.
|
||||
// Any matching deny rule concludes testing.
|
||||
continue;
|
||||
}
|
||||
if (strpos($word, 'lang=') === 0 && strcasecmp($lang, trim(substr($word, 5))) == 0) {
|
||||
return false;
|
||||
} elseif (strpos($word, 'lang!=') === 0 && strcasecmp($lang, trim(substr($word, 6))) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
elseif((strpos($word,'/') === 0) && preg_match($word,$text))
|
||||
elseif (substr($word, 0, 1) === '#' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '$' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 2) === '?+') {
|
||||
if (self::test_condition(substr($word, 2), $item['obj'])) {
|
||||
return false;
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '?') {
|
||||
if (self::test_condition(substr($word, 1), $item)) {
|
||||
return false;
|
||||
}
|
||||
} elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) {
|
||||
return false;
|
||||
elseif((strpos($word,'lang=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,5))) == 0))
|
||||
return false;
|
||||
elseif((strpos($word,'lang!=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,6))) != 0))
|
||||
return false;
|
||||
elseif(stristr($text,$word) !== false)
|
||||
} elseif (stristr($text, $word) !== false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$include = (($incl) ? explode("\n",$incl) : null);
|
||||
$include = (($incl) ? explode("\n", $incl) : null);
|
||||
|
||||
if($include) {
|
||||
foreach($include as $word) {
|
||||
$word = trim($word);
|
||||
if(! $word)
|
||||
if ($include) {
|
||||
foreach ($include as $word) {
|
||||
$word = html_entity_decode(trim($word));
|
||||
if (! $word) {
|
||||
continue;
|
||||
if(substr($word,0,1) === '#' && $tags) {
|
||||
foreach($tags as $t)
|
||||
if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*')))
|
||||
return true;
|
||||
}
|
||||
elseif(substr($word,0,1) === '$' && $tags) {
|
||||
foreach($tags as $t)
|
||||
if(($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*')))
|
||||
return true;
|
||||
if (isset($lang) && ((strpos($word, 'lang=') === 0) || (strpos($word, 'lang!=') === 0))) {
|
||||
if (!strlen($lang)) {
|
||||
// Result is ambiguous. However we are checking allow rules
|
||||
// and an ambiguous language is always permitted.
|
||||
return true;
|
||||
}
|
||||
if (strpos($word, 'lang=') === 0 && strcasecmp($lang, trim(substr($word, 5))) == 0) {
|
||||
return true;
|
||||
} elseif (strpos($word, 'lang!=') === 0 && strcasecmp($lang, trim(substr($word, 6))) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
elseif((strpos($word,'/') === 0) && preg_match($word,$text))
|
||||
elseif (substr($word, 0, 1) === '#' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '$' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 2) === '?+') {
|
||||
if (self::test_condition(substr($word, 2), $item['obj'])) {
|
||||
return true;
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '?') {
|
||||
if (self::test_condition(substr($word, 1), $item)) {
|
||||
return true;
|
||||
}
|
||||
} elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) {
|
||||
return true;
|
||||
elseif((strpos($word,'lang=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,5))) == 0))
|
||||
return true;
|
||||
elseif((strpos($word,'lang!=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,6))) != 0))
|
||||
return true;
|
||||
elseif(stristr($text,$word) !== false)
|
||||
} elseif (stristr($text, $word) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -88,4 +123,123 @@ class MessageFilter {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Test for Conditional Execution conditions. Shamelessly ripped off from Code/Render/Comanche
|
||||
*
|
||||
* This is extensible. The first version of variable testing supports tests of the forms:
|
||||
*
|
||||
* - ?foo ~= baz which will check if item.foo contains the string 'baz';
|
||||
* - ?foo == baz which will check if item.foo is the string 'baz';
|
||||
* - ?foo != baz which will check if item.foo is not the string 'baz';
|
||||
* - ?foo >= 3 which will check if item.foo is greater than or equal to 3;
|
||||
* - ?foo > 3 which will check if item.foo is greater than 3;
|
||||
* - ?foo <= 3 which will check if item.foo is less than or equal to 3;
|
||||
* - ?foo < 3 which will check if item.foo is less than 3;
|
||||
*
|
||||
* - ?foo {} baz which will check if 'baz' is an array element in item.foo
|
||||
* - ?foo {*} baz which will check if 'baz' is an array key in item.foo
|
||||
* - ?foo which will check for a return of a true condition for item.foo;
|
||||
* - ?!foo which will check for a return of a false condition for item.foo;
|
||||
*
|
||||
* The values 0, '', an empty array, and an unset value will all evaluate to false.
|
||||
*
|
||||
* @param string $s
|
||||
* @param array $item
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public static function test_condition($s,$item) {
|
||||
|
||||
if (preg_match('/(.*?)\s\~\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (stripos($x, trim($matches[2])) !== false) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/(.*?)\s\=\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x == trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/(.*?)\s\!\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x != trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/(.*?)\s\>\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x >= trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/(.*?)\s\<\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x <= trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/(.*?)\s\>\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x > trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/(.*?)\s\>\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x < trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/[\$](.*?)\s\{\}\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (is_array($x) && in_array(trim($matches[2]), $x)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/(.*?)\s\{\*\}\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (is_array($x) && array_key_exists(trim($matches[2]), $x)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ordering of this check (for falsiness) with relation to the following one (check for truthiness) is important.
|
||||
if (preg_match('/\!(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (!$x) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
34
Zotlabs/Lib/Multibase.php
Normal file
34
Zotlabs/Lib/Multibase.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use StephenHill\Base58;
|
||||
|
||||
class Multibase {
|
||||
|
||||
protected $key = null;
|
||||
|
||||
public function __construct() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function publicKey($key) {
|
||||
$base58 = new Base58();
|
||||
$raw = hex2bin('ed01') . sodium_base642bin($key, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
|
||||
return 'z' . $base58->encode($raw);
|
||||
}
|
||||
|
||||
public function secretKey($key) {
|
||||
$base58 = new Base58();
|
||||
$raw = hex2bin('8026') . sodium_base642bin($key, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
|
||||
return 'z' . $base58->encode($raw);
|
||||
}
|
||||
|
||||
public function decode($key, $binary = false) {
|
||||
$base58 = new Base58();
|
||||
$key = substr($key,1);
|
||||
$raw = $base58->decode($key);
|
||||
$binaryKey = substr($raw, 2);
|
||||
return $binary ? $binaryKey : sodium_bin2base64($binaryKey, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,322 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
define ( 'NWIKI_ITEM_RESOURCE_TYPE', 'nwiki' );
|
||||
|
||||
class NativeWiki {
|
||||
|
||||
|
||||
public static function listwikis($channel, $observer_hash) {
|
||||
|
||||
$sql_extra = item_permissions_sql($channel['channel_id'], $observer_hash);
|
||||
$wikis = q("SELECT * FROM item
|
||||
WHERE resource_type = '%s' AND mid = parent_mid AND uid = %d AND item_deleted = 0 $sql_extra",
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
|
||||
if($wikis) {
|
||||
foreach($wikis as &$w) {
|
||||
|
||||
$w['json_allow_cid'] = acl2json($w['allow_cid']);
|
||||
$w['json_allow_gid'] = acl2json($w['allow_gid']);
|
||||
$w['json_deny_cid'] = acl2json($w['deny_cid']);
|
||||
$w['json_deny_gid'] = acl2json($w['deny_gid']);
|
||||
|
||||
$w['rawName'] = get_iconfig($w, 'wiki', 'rawName');
|
||||
$w['htmlName'] = escape_tags($w['rawName']);
|
||||
//$w['urlName'] = urlencode(urlencode($w['rawName']));
|
||||
$w['urlName'] = self::name_encode($w['rawName']);
|
||||
$w['mimeType'] = get_iconfig($w, 'wiki', 'mimeType');
|
||||
$w['typelock'] = get_iconfig($w, 'wiki', 'typelock');
|
||||
$w['lockstate'] = (($w['allow_cid'] || $w['allow_gid'] || $w['deny_cid'] || $w['deny_gid']) ? 'lock' : 'unlock');
|
||||
}
|
||||
}
|
||||
// TODO: query db for wikis the observer can access. Return with two lists, for read and write access
|
||||
return array('wikis' => $wikis);
|
||||
}
|
||||
|
||||
|
||||
public static function create_wiki($channel, $observer_hash, $wiki, $acl) {
|
||||
|
||||
$resource_id = new_uuid();
|
||||
$uuid = new_uuid();
|
||||
|
||||
$ac = $acl->get();
|
||||
$mid = z_root() . '/item/' . $uuid;
|
||||
|
||||
$arr = array(); // Initialize the array of parameters for the post
|
||||
$item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0);
|
||||
$wiki_url = z_root() . '/wiki/' . $channel['channel_address'] . '/' . $wiki['urlName'];
|
||||
$arr['aid'] = $channel['channel_account_id'];
|
||||
$arr['uuid'] = $uuid;
|
||||
$arr['uid'] = $channel['channel_id'];
|
||||
$arr['mid'] = $mid;
|
||||
$arr['parent_mid'] = $mid;
|
||||
$arr['item_hidden'] = $item_hidden;
|
||||
$arr['resource_type'] = NWIKI_ITEM_RESOURCE_TYPE;
|
||||
$arr['resource_id'] = $resource_id;
|
||||
$arr['owner_xchan'] = $channel['channel_hash'];
|
||||
$arr['author_xchan'] = $observer_hash;
|
||||
$arr['plink'] = $mid;
|
||||
$arr['llink'] = z_root() . '/display/' . gen_link_id($mid);
|
||||
$arr['title'] = $wiki['htmlName']; // name of new wiki;
|
||||
$arr['allow_cid'] = $ac['allow_cid'];
|
||||
$arr['allow_gid'] = $ac['allow_gid'];
|
||||
$arr['deny_cid'] = $ac['deny_cid'];
|
||||
$arr['deny_gid'] = $ac['deny_gid'];
|
||||
$arr['item_wall'] = 1;
|
||||
$arr['item_origin'] = 1;
|
||||
$arr['item_thread_top'] = 1;
|
||||
$arr['item_private'] = intval($acl->is_private());
|
||||
$arr['verb'] = ACTIVITY_CREATE;
|
||||
$arr['obj_type'] = 'Document';
|
||||
$arr['body'] = '[table][tr][td][h1]New Wiki[/h1][/td][/tr][tr][td][zrl=' . $wiki_url . ']' . $wiki['htmlName'] . '[/zrl][/td][/tr][/table]';
|
||||
|
||||
$arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_wiki'),true);
|
||||
|
||||
// Save the wiki name information using iconfig. This is shareable.
|
||||
if(! set_iconfig($arr, 'wiki', 'rawName', $wiki['rawName'], true)) {
|
||||
return array('item' => null, 'success' => false);
|
||||
}
|
||||
if(! set_iconfig($arr, 'wiki', 'mimeType', $wiki['mimeType'], true)) {
|
||||
return array('item' => null, 'success' => false);
|
||||
}
|
||||
|
||||
set_iconfig($arr,'wiki','typelock',$wiki['typelock'],true);
|
||||
|
||||
$post = item_store($arr);
|
||||
|
||||
$item_id = $post['item_id'];
|
||||
|
||||
if($item_id) {
|
||||
\Zotlabs\Daemon\Master::Summon(array('Notifier', 'activity', $item_id));
|
||||
return array('item' => $post['item'], 'item_id' => $item_id, 'success' => true);
|
||||
}
|
||||
else {
|
||||
return array('item' => null, 'success' => false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function update_wiki($channel_id, $observer_hash, $arr, $acl) {
|
||||
|
||||
$w = self::get_wiki($channel_id, $observer_hash, $arr['resource_id']);
|
||||
$item = $w['wiki'];
|
||||
|
||||
if(! $item) {
|
||||
return array('item' => null, 'success' => false);
|
||||
}
|
||||
|
||||
$x = $acl->get();
|
||||
|
||||
$item['allow_cid'] = $x['allow_cid'];
|
||||
$item['allow_gid'] = $x['allow_gid'];
|
||||
$item['deny_cid'] = $x['deny_cid'];
|
||||
$item['deny_gid'] = $x['deny_gid'];
|
||||
$item['item_private'] = intval($acl->is_private());
|
||||
|
||||
$update_title = false;
|
||||
|
||||
if($item['title'] !== $arr['updateRawName']) {
|
||||
$update_title = true;
|
||||
$item['title'] = $arr['updateRawName'];
|
||||
}
|
||||
|
||||
$update = item_store_update($item);
|
||||
|
||||
$item_id = $update['item_id'];
|
||||
|
||||
// update acl for any existing wiki pages
|
||||
|
||||
q("update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d where resource_type = 'nwikipage' and resource_id = '%s'",
|
||||
dbesc($item['allow_cid']),
|
||||
dbesc($item['allow_gid']),
|
||||
dbesc($item['deny_cid']),
|
||||
dbesc($item['deny_gid']),
|
||||
dbesc($item['item_private']),
|
||||
dbesc($arr['resource_id'])
|
||||
);
|
||||
|
||||
|
||||
if($update['item_id']) {
|
||||
info( t('Wiki updated successfully'));
|
||||
if($update_title) {
|
||||
// Update the wiki name information using iconfig.
|
||||
if(! set_iconfig($update['item_id'], 'wiki', 'rawName', $arr['updateRawName'], true)) {
|
||||
return array('item' => null, 'success' => false);
|
||||
}
|
||||
}
|
||||
return array('item' => $update['item'], 'item_id' => $update['item_id'], 'success' => $update['success']);
|
||||
}
|
||||
else {
|
||||
return array('item' => null, 'success' => false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function sync_a_wiki_item($uid,$id,$resource_id) {
|
||||
|
||||
$r = q("SELECT * from item WHERE uid = %d AND ( id = %d OR ( resource_type = '%s' and resource_id = '%s' )) ",
|
||||
intval($uid),
|
||||
intval($id),
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
dbesc($resource_id)
|
||||
);
|
||||
if($r) {
|
||||
|
||||
$q = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s'",
|
||||
dbesc($r[0]['resource_id'])
|
||||
);
|
||||
if($q) {
|
||||
$r = array_merge($r,$q);
|
||||
}
|
||||
xchan_query($r);
|
||||
$sync_item = fetch_post_tags($r);
|
||||
if($sync_item) {
|
||||
$pkt = [];
|
||||
foreach($sync_item as $w) {
|
||||
$pkt[] = encode_item($w,true);
|
||||
}
|
||||
Libsync::build_sync_packet($uid,array('wiki' => $pkt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function delete_wiki($channel_id,$observer_hash,$resource_id) {
|
||||
|
||||
$w = self::get_wiki($channel_id,$observer_hash,$resource_id);
|
||||
if(! $w['wiki']) {
|
||||
return [ 'success' => false ];
|
||||
}
|
||||
else {
|
||||
|
||||
$r = q("SELECT id FROM item WHERE uid = %s AND resource_id = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($resource_id)
|
||||
);
|
||||
|
||||
$ids = array_column($r, 'id');
|
||||
drop_items($ids, true, DROPITEM_PHASE1);
|
||||
|
||||
info(t('Wiki files deleted successfully'));
|
||||
|
||||
return [ 'success' => true ];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function get_wiki($channel_id, $observer_hash, $resource_id) {
|
||||
|
||||
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
|
||||
|
||||
$item = q("SELECT * FROM item WHERE uid = %d AND resource_type = '%s' AND resource_id = '%s' AND item_deleted = 0
|
||||
$sql_extra ORDER BY id LIMIT 1",
|
||||
intval($channel_id),
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
dbesc($resource_id)
|
||||
);
|
||||
if(! $item) {
|
||||
return [ 'wiki' => null ];
|
||||
}
|
||||
else {
|
||||
|
||||
$w = $item[0]; // wiki item table record
|
||||
// Get wiki metadata
|
||||
$rawName = get_iconfig($w, 'wiki', 'rawName');
|
||||
$mimeType = get_iconfig($w, 'wiki', 'mimeType');
|
||||
$typelock = get_iconfig($w, 'wiki', 'typelock');
|
||||
|
||||
return array(
|
||||
'wiki' => $w,
|
||||
'rawName' => $rawName,
|
||||
'htmlName' => escape_tags($rawName),
|
||||
//'urlName' => urlencode(urlencode($rawName)),
|
||||
'urlName' => self::name_encode($rawName),
|
||||
'mimeType' => $mimeType,
|
||||
'typelock' => $typelock
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function exists_by_name($uid, $urlName) {
|
||||
|
||||
$sql_extra = item_permissions_sql($uid);
|
||||
|
||||
$item = q("SELECT item.id, resource_id FROM item left join iconfig on iconfig.iid = item.id
|
||||
WHERE resource_type = '%s' AND iconfig.v = '%s' AND uid = %d
|
||||
AND item_deleted = 0 $sql_extra limit 1",
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
//dbesc(urldecode($urlName)),
|
||||
dbesc(self::name_decode($urlName)),
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
if($item) {
|
||||
return array('id' => $item[0]['id'], 'resource_id' => $item[0]['resource_id']);
|
||||
}
|
||||
else {
|
||||
return array('id' => null, 'resource_id' => null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function get_permissions($resource_id, $owner_id, $observer_hash) {
|
||||
|
||||
// TODO: For now, only the owner can edit
|
||||
$sql_extra = item_permissions_sql($owner_id, $observer_hash);
|
||||
|
||||
if(local_channel() && local_channel() == $owner_id) {
|
||||
return [ 'read' => true, 'write' => true, 'success' => true ];
|
||||
}
|
||||
|
||||
$r = q("SELECT * FROM item WHERE uid = %d and resource_type = '%s' AND resource_id = '%s' $sql_extra LIMIT 1",
|
||||
intval($owner_id),
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
dbesc($resource_id)
|
||||
);
|
||||
|
||||
if(! $r) {
|
||||
return array('read' => false, 'write' => false, 'success' => true);
|
||||
}
|
||||
else {
|
||||
$write = perm_is_allowed($owner_id, $observer_hash,'write_wiki');
|
||||
return array('read' => true, 'write' => $write, 'success' => true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function name_encode ($string) {
|
||||
|
||||
$string = html_entity_decode($string);
|
||||
$encoding = mb_internal_encoding();
|
||||
mb_internal_encoding("UTF-8");
|
||||
$ret = mb_ereg_replace_callback ('[^A-Za-z0-9\-\_\.\~]',function ($char) {
|
||||
$charhex = unpack('H*',$char[0]);
|
||||
$ret = '('.$charhex[1].')';
|
||||
return $ret;
|
||||
}
|
||||
,$string);
|
||||
mb_internal_encoding($encoding);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
public static function name_decode ($string) {
|
||||
|
||||
$encoding = mb_internal_encoding();
|
||||
mb_internal_encoding("UTF-8");
|
||||
$ret = mb_ereg_replace_callback ('(\(([0-9a-f]+)\))',function ($chars) {
|
||||
return pack('H*',$chars[2]);
|
||||
}
|
||||
,$string);
|
||||
mb_internal_encoding($encoding);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,725 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Access\PermissionLimits;
|
||||
|
||||
class NativeWikiPage {
|
||||
|
||||
static public function page_list($channel_id, $observer_hash, $resource_id) {
|
||||
|
||||
// TODO: Create item table records for pages so that metadata like title can be applied
|
||||
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
|
||||
$pages[] = [
|
||||
'resource_id' => '',
|
||||
'title' => 'Home',
|
||||
'url' => 'Home',
|
||||
'link_id' => 'id_wiki_home_0'
|
||||
];
|
||||
|
||||
$sql_extra = item_permissions_sql($channel_id, $observer_hash);
|
||||
|
||||
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and item_deleted = 0
|
||||
$sql_extra order by title asc",
|
||||
dbesc($resource_id),
|
||||
intval($channel_id)
|
||||
);
|
||||
if ($r) {
|
||||
$x = [];
|
||||
$y = [];
|
||||
|
||||
foreach ($r as $rv) {
|
||||
if (!in_array($rv['mid'], $x)) {
|
||||
$y[] = $rv;
|
||||
$x[] = $rv['mid'];
|
||||
}
|
||||
}
|
||||
|
||||
$items = fetch_post_tags($y, true);
|
||||
|
||||
foreach ($items as $page_item) {
|
||||
$title = get_iconfig($page_item['id'], 'nwikipage', 'pagetitle', t('(No Title)'));
|
||||
if (urldecode($title) !== 'Home') {
|
||||
$pages[] = [
|
||||
'resource_id' => $resource_id,
|
||||
'title' => escape_tags($title),
|
||||
//'url' => str_replace('%2F','/',urlencode(str_replace('%2F','/',urlencode($title)))),
|
||||
'url' => NativeWiki::name_encode($title),
|
||||
'link_id' => 'id_' . substr($resource_id, 0, 10) . '_' . $page_item['id']
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ['pages' => $pages, 'wiki' => $w];
|
||||
}
|
||||
|
||||
|
||||
static public function create_page($channel, $observer_hash, $name, $resource_id, $mimetype = 'text/bbcode') {
|
||||
|
||||
logger('mimetype: ' . $mimetype);
|
||||
|
||||
if (!in_array($mimetype, ['text/markdown', 'text/bbcode', 'text/plain', 'text/html']))
|
||||
$mimetype = 'text/markdown';
|
||||
|
||||
$w = NativeWiki::get_wiki($channel['channel_id'], $observer_hash, $resource_id);
|
||||
|
||||
if (!$w['wiki']) {
|
||||
return ['content' => null, 'message' => 'Error reading wiki', 'success' => false];
|
||||
}
|
||||
|
||||
// backslashes won't work well in the javascript functions
|
||||
$name = str_replace('\\', '', $name);
|
||||
|
||||
$uuid = new_uuid();
|
||||
$mid = z_root() . '/item/' . $uuid;
|
||||
|
||||
// create an empty activity
|
||||
$arr = [];
|
||||
$arr['aid'] = $channel['channel_account_id'];
|
||||
$arr['uid'] = $channel['channel_id'];
|
||||
$arr['mid'] = $mid;
|
||||
$arr['parent_mid'] = $w['wiki']['mid'];
|
||||
$arr['parent'] = $w['wiki']['parent'];
|
||||
$arr['uuid'] = $uuid;
|
||||
$arr['item_hidden'] = $w['wiki']['item_hidden'];
|
||||
$arr['plink'] = $mid;
|
||||
$arr['llink'] = z_root() . '/display/' . gen_link_id($mid);
|
||||
$arr['author_xchan'] = $observer_hash;
|
||||
$arr['mimetype'] = $mimetype;
|
||||
$arr['title'] = $name;
|
||||
$arr['resource_type'] = 'nwikipage';
|
||||
$arr['resource_id'] = $resource_id;
|
||||
$arr['allow_cid'] = $w['wiki']['allow_cid'];
|
||||
$arr['allow_gid'] = $w['wiki']['allow_gid'];
|
||||
$arr['deny_cid'] = $w['wiki']['deny_cid'];
|
||||
$arr['deny_gid'] = $w['wiki']['deny_gid'];
|
||||
$arr['item_private'] = $w['wiki']['item_private'];
|
||||
$arr['item_wall'] = 1;
|
||||
$arr['item_origin'] = 1;
|
||||
$arr['item_thread_top'] = 1;
|
||||
$arr['verb'] = ACTIVITY_CREATE;
|
||||
$arr['obj_type'] = 'Document';
|
||||
// TODO: add an object?
|
||||
$arr['public_policy'] = map_scope(PermissionLimits::Get($channel['channel_id'], 'view_wiki'), true);
|
||||
|
||||
// We may wish to change this some day.
|
||||
$arr['item_unpublished'] = 1;
|
||||
|
||||
set_iconfig($arr, 'nwikipage', 'pagetitle', (($name) ? $name : t('(No Title)')), true);
|
||||
$p = item_store($arr, false, false);
|
||||
|
||||
if ($p['item_id']) {
|
||||
$page = [
|
||||
'rawName' => $name,
|
||||
'htmlName' => escape_tags($name),
|
||||
//'urlName' => urlencode($name),
|
||||
'urlName' => NativeWiki::name_encode($name)
|
||||
|
||||
];
|
||||
|
||||
return ['page' => $page, 'item_id' => $p['item_id'], 'item' => $p['activity'], 'wiki' => $w, 'message' => '', 'success' => true];
|
||||
}
|
||||
return ['success' => false, 'message' => t('Wiki page create failed.')];
|
||||
}
|
||||
|
||||
|
||||
static public function rename_page($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : '');
|
||||
$pageNewName = ((array_key_exists('pageNewName', $arr)) ? $arr['pageNewName'] : '');
|
||||
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
|
||||
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
|
||||
|
||||
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if (!$w['wiki']) {
|
||||
return ['message' => t('Wiki not found.'), 'success' => false];
|
||||
}
|
||||
|
||||
|
||||
$ic = q("select * from iconfig left join item on iconfig.iid = item.id
|
||||
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($pageNewName)
|
||||
);
|
||||
|
||||
if ($ic) {
|
||||
return ['success' => false, 'message' => t('Destination name already exists')];
|
||||
}
|
||||
|
||||
|
||||
$ids = [];
|
||||
|
||||
$ic = q("select *, item.id as item_id from iconfig left join item on iconfig.iid = item.id
|
||||
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($pageUrlName)
|
||||
);
|
||||
|
||||
if ($ic) {
|
||||
foreach ($ic as $c) {
|
||||
set_iconfig($c['item_id'], 'nwikipage', 'pagetitle', $pageNewName);
|
||||
$ids[] = $c['item_id'];
|
||||
}
|
||||
|
||||
$str_ids = implode(',', $ids);
|
||||
q("update item set title = '%s' where id in ($str_ids)",
|
||||
dbesc($pageNewName)
|
||||
);
|
||||
|
||||
$page = [
|
||||
'rawName' => $pageNewName,
|
||||
'htmlName' => escape_tags($pageNewName),
|
||||
//'urlName' => urlencode(escape_tags($pageNewName))
|
||||
'urlName' => NativeWiki::name_encode($pageNewName)
|
||||
];
|
||||
|
||||
return ['success' => true, 'page' => $page];
|
||||
}
|
||||
|
||||
return ['success' => false, 'message' => t('Page not found')];
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function get_page_content($arr) {
|
||||
|
||||
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
|
||||
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id', $arr)) ? intval($arr['channel_id']) : 0);
|
||||
|
||||
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if (!$w['wiki']) {
|
||||
return ['content' => null, 'message' => 'Error reading wiki', 'success' => false];
|
||||
}
|
||||
|
||||
$item = self::load_page($arr);
|
||||
|
||||
if ($item) {
|
||||
$content = $item['body'];
|
||||
|
||||
return [
|
||||
'content' => $content,
|
||||
'mimeType' => $w['mimeType'],
|
||||
'pageMimeType' => $item['mimetype'],
|
||||
'message' => '',
|
||||
'success' => true
|
||||
];
|
||||
}
|
||||
|
||||
return ['content' => null, 'message' => t('Error reading page content'), 'success' => false];
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function page_history($arr) {
|
||||
|
||||
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
|
||||
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
|
||||
|
||||
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if (!$w['wiki']) {
|
||||
return ['history' => null, 'message' => 'Error reading wiki', 'success' => false];
|
||||
}
|
||||
|
||||
$items = self::load_page_history($arr);
|
||||
|
||||
$history = [];
|
||||
|
||||
if ($items) {
|
||||
$processed = 0;
|
||||
foreach ($items as $item) {
|
||||
if ($processed > 1000)
|
||||
break;
|
||||
$processed++;
|
||||
$history[] = [
|
||||
'revision' => $item['revision'],
|
||||
'date' => datetime_convert('UTC', date_default_timezone_get(), $item['edited']),
|
||||
'name' => $item['author']['xchan_name'],
|
||||
'title' => get_iconfig($item, 'nwikipage', 'commit_msg')
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return ['success' => true, 'history' => $history];
|
||||
}
|
||||
|
||||
return ['success' => false];
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function load_page($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : '');
|
||||
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
|
||||
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
|
||||
$revision = ((array_key_exists('revision', $arr)) ? $arr['revision'] : (-1));
|
||||
|
||||
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
|
||||
if (!$w['wiki']) {
|
||||
return ['content' => null, 'message' => 'Error reading wiki', 'success' => false];
|
||||
}
|
||||
|
||||
$ids = '';
|
||||
|
||||
$ic = q("select * from iconfig left join item on iconfig.iid = item.id where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($pageUrlName)
|
||||
);
|
||||
|
||||
if ($ic) {
|
||||
foreach ($ic as $c) {
|
||||
if ($ids)
|
||||
$ids .= ',';
|
||||
$ids .= intval($c['iid']);
|
||||
}
|
||||
}
|
||||
|
||||
$sql_extra = item_permissions_sql($channel_id, $observer_hash);
|
||||
|
||||
if ($revision == (-1))
|
||||
$sql_extra .= " order by revision desc ";
|
||||
elseif ($revision)
|
||||
$sql_extra .= " and revision = " . intval($revision) . " ";
|
||||
|
||||
$r = null;
|
||||
|
||||
|
||||
if ($ids) {
|
||||
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) $sql_extra limit 1",
|
||||
dbesc($resource_id),
|
||||
intval($channel_id)
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
$items = fetch_post_tags($r, true);
|
||||
return $items[0];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
static public function load_page_history($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : '');
|
||||
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
|
||||
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
|
||||
|
||||
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if (!$w['wiki']) {
|
||||
return ['content' => null, 'message' => 'Error reading wiki', 'success' => false];
|
||||
}
|
||||
|
||||
$ids = '';
|
||||
|
||||
$ic = q("select * from iconfig left join item on iconfig.iid = item.id where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($pageUrlName)
|
||||
);
|
||||
|
||||
if ($ic) {
|
||||
foreach ($ic as $c) {
|
||||
if ($ids)
|
||||
$ids .= ',';
|
||||
$ids .= intval($c['iid']);
|
||||
}
|
||||
}
|
||||
|
||||
$sql_extra = item_permissions_sql($channel_id, $observer_hash);
|
||||
|
||||
$sql_extra .= " order by revision desc ";
|
||||
|
||||
$r = null;
|
||||
if ($ids) {
|
||||
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) and item_deleted = 0 $sql_extra",
|
||||
dbesc($resource_id),
|
||||
intval($channel_id)
|
||||
);
|
||||
if ($r) {
|
||||
xchan_query($r);
|
||||
$items = fetch_post_tags($r, true);
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
static public function save_page($arr) {
|
||||
$pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : '');
|
||||
$content = ((array_key_exists('content', $arr)) ? $arr['content'] : '');
|
||||
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
|
||||
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
|
||||
|
||||
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
|
||||
if (!$w['wiki']) {
|
||||
return ['message' => t('Error reading wiki'), 'success' => false];
|
||||
}
|
||||
|
||||
|
||||
// fetch the most recently saved revision.
|
||||
|
||||
$item = self::load_page($arr);
|
||||
|
||||
if (!$item) {
|
||||
return ['message' => t('Page not found'), 'success' => false];
|
||||
}
|
||||
|
||||
$mimetype = $item['mimetype'];
|
||||
|
||||
// change just the fields we need to change to create a revision;
|
||||
|
||||
unset($item['id']);
|
||||
unset($item['author']);
|
||||
$item['parent'] = 0;
|
||||
$item['body'] = $content;
|
||||
$item['author_xchan'] = $observer_hash;
|
||||
$item['revision'] = (($arr['revision']) ? intval($arr['revision']) + 1 : intval($item['revision']) + 1);
|
||||
$item['edited'] = datetime_convert();
|
||||
$item['mimetype'] = $mimetype;
|
||||
|
||||
if ($item['iconfig'] && is_array($item['iconfig']) && count($item['iconfig'])) {
|
||||
for ($x = 0; $x < count($item['iconfig']); $x++) {
|
||||
unset($item['iconfig'][$x]['id']);
|
||||
unset($item['iconfig'][$x]['iid']);
|
||||
}
|
||||
}
|
||||
|
||||
$ret = item_store($item, false, false);
|
||||
|
||||
if ($ret['item_id'])
|
||||
return ['message' => '', 'item_id' => $ret['item_id'], 'filename' => $pageUrlName, 'success' => true];
|
||||
else
|
||||
return ['message' => t('Page update failed.'), 'success' => false];
|
||||
}
|
||||
|
||||
|
||||
static public function delete_page($arr) {
|
||||
|
||||
$pageUrlName = (array_key_exists('pageUrlName', $arr) ? $arr['pageUrlName'] : '');
|
||||
$resource_id = (array_key_exists('resource_id', $arr) ? $arr['resource_id'] : '');
|
||||
$observer_hash = (array_key_exists('observer_hash', $arr) ? $arr['observer_hash'] : '');
|
||||
$channel_id = (array_key_exists('channel_id', $arr) ? $arr['channel_id'] : 0);
|
||||
|
||||
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if (!$w['wiki']) {
|
||||
return ['success' => false, 'message' => t('Error reading wiki')];
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
$ic = q("select * from iconfig left join item on iconfig.iid = item.id
|
||||
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($pageUrlName)
|
||||
);
|
||||
|
||||
if ($ic) {
|
||||
foreach ($ic as $c) {
|
||||
$ids[] = intval($c['iid']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($ids) {
|
||||
drop_items($ids, true, DROPITEM_PHASE1);
|
||||
return ['success' => true];
|
||||
}
|
||||
|
||||
return ['success' => false, 'message' => t('Nothing deleted')];
|
||||
}
|
||||
|
||||
|
||||
static public function revert_page($arr) {
|
||||
|
||||
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
|
||||
$commitHash = ((array_key_exists('commitHash', $arr)) ? $arr['commitHash'] : null);
|
||||
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
|
||||
|
||||
if (!$commitHash) {
|
||||
return ['message' => 'No commit was provided', 'success' => false];
|
||||
}
|
||||
|
||||
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if (!$w['wiki']) {
|
||||
return ['message' => 'Error reading wiki', 'success' => false];
|
||||
}
|
||||
|
||||
$x = $arr;
|
||||
|
||||
if (intval($commitHash) > 0) {
|
||||
unset($x['commitHash']);
|
||||
$x['revision'] = intval($commitHash) - 1;
|
||||
$loaded = self::load_page($x);
|
||||
|
||||
if ($loaded) {
|
||||
$content = $loaded['body'];
|
||||
return ['content' => $content, 'success' => true];
|
||||
}
|
||||
return ['success' => false];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static public function compare_page($arr) {
|
||||
|
||||
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
|
||||
$compareCommit = ((array_key_exists('compareCommit', $arr)) ? $arr['compareCommit'] : 0);
|
||||
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
|
||||
|
||||
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
|
||||
if (!$w['wiki']) {
|
||||
return ['message' => t('Error reading wiki'), 'success' => false];
|
||||
}
|
||||
|
||||
$x = $arr;
|
||||
$x['revision'] = (-1);
|
||||
|
||||
$currpage = self::load_page($x);
|
||||
if ($currpage)
|
||||
$currentContent = $currpage['body'];
|
||||
|
||||
$x['revision'] = $compareCommit;
|
||||
$comppage = self::load_page($x);
|
||||
if ($comppage)
|
||||
$compareContent = $comppage['body'];
|
||||
|
||||
if ($currpage && $comppage) {
|
||||
require_once('library/class.Diff.php');
|
||||
$diff = \Diff::toTable(\Diff::compare($currentContent, $compareContent));
|
||||
|
||||
return ['success' => true, 'diff' => $diff];
|
||||
}
|
||||
return ['success' => false, 'message' => t('Compare: object not found.')];
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function commit($arr) {
|
||||
|
||||
$commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : t('Page updated'));
|
||||
$observer_hash = ((array_key_exists('observer_hash', $arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id', $arr)) ? $arr['channel_id'] : 0);
|
||||
|
||||
if (array_key_exists('resource_id', $arr)) {
|
||||
$resource_id = $arr['resource_id'];
|
||||
}
|
||||
else {
|
||||
return ['message' => t('Wiki resource_id required for git commit'), 'success' => false];
|
||||
}
|
||||
|
||||
$w = NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if (!$w['wiki']) {
|
||||
return ['message' => t('Error reading wiki'), 'success' => false];
|
||||
}
|
||||
|
||||
|
||||
$page = self::load_page($arr);
|
||||
|
||||
if ($page) {
|
||||
set_iconfig($page['id'], 'nwikipage', 'commit_msg', escape_tags($commit_msg), true);
|
||||
return ['success' => true, 'item_id' => $page['id'], 'page' => $page];
|
||||
}
|
||||
|
||||
return ['success' => false, 'message' => t('Page not found.')];
|
||||
|
||||
}
|
||||
|
||||
static public function convert_links($s, $wikiURL) {
|
||||
|
||||
if (strpos($s, '[[') !== false) {
|
||||
preg_match_all("/\[\[(.*?)\]\]/", $s, $match);
|
||||
$pages = $pageURLs = [];
|
||||
foreach ($match[1] as $m) {
|
||||
// TODO: Why do we need to double urlencode for this to work?
|
||||
//$pageURLs[] = urlencode(urlencode(escape_tags($m)));
|
||||
$titleUri = explode('|', $m);
|
||||
$page = $titleUri[0] ?? '';
|
||||
$title = $titleUri[1] ?? $page;
|
||||
$pageURLs[] = NativeWiki::name_encode(escape_tags($page));
|
||||
$pages[] = $title;
|
||||
}
|
||||
$idx = 0;
|
||||
while (strpos($s, '[[') !== false) {
|
||||
$replace = '<a href="' . $wikiURL . '/' . $pageURLs[$idx] . '">' . $pages[$idx] . '</a>';
|
||||
$s = preg_replace("/\[\[(.*?)\]\]/", $replace, $s, 1);
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
static public function render_page_history($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : '');
|
||||
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
|
||||
|
||||
$pageHistory = self::page_history([
|
||||
'channel_id' => App::$profile_uid,
|
||||
'observer_hash' => get_observer_hash(),
|
||||
'resource_id' => $resource_id,
|
||||
'pageUrlName' => $pageUrlName
|
||||
]);
|
||||
|
||||
return replace_macros(get_markup_template('nwiki_page_history.tpl'), [
|
||||
'$pageHistory' => $pageHistory['history'],
|
||||
'$permsWrite' => $arr['permsWrite'],
|
||||
'$name_lbl' => t('Name'),
|
||||
'$msg_label' => t('Message', 'wiki_history'),
|
||||
'$date_lbl' => t('Date'),
|
||||
'$revert_btn' => t('Revert'),
|
||||
'$compare_btn' => t('Compare')
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace the instances of the string [toc] with a list element that will be populated by
|
||||
* a table of contents by the JavaScript library
|
||||
* @param string $s
|
||||
* @return string
|
||||
*/
|
||||
static public function generate_toc($s) {
|
||||
if (strpos($s, '[toc]') !== false) {
|
||||
//$toc_md = wiki_toc($s); // Generate Markdown-formatted list prior to HTML render
|
||||
$toc_md = '<ul id="wiki-toc"></ul>'; // use the available jQuery plugin http://ndabas.github.io/toc/
|
||||
$s = preg_replace("/\[toc\]/", $toc_md, $s, -1);
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a select set of bbcode tags. Much of the code is copied from include/bbcode.php
|
||||
* @param string $s
|
||||
* @return string
|
||||
*/
|
||||
static public function bbcode($s) {
|
||||
|
||||
$s = str_replace(['[baseurl]', '[sitename]'], [z_root(), get_config('system', 'sitename')], $s);
|
||||
|
||||
$s = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism", 'oblanguage_callback', $s);
|
||||
|
||||
$s = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism", 'oblanguage_necallback', $s);
|
||||
|
||||
|
||||
$observer = App::get_observer();
|
||||
if ($observer) {
|
||||
$s1 = '<span class="bb_observer" title="' . t('Different viewers will see this text differently') . '">';
|
||||
$s2 = '</span>';
|
||||
$obsBaseURL = $observer['xchan_connurl'];
|
||||
$obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL);
|
||||
$s = str_replace('[observer.baseurl]', $obsBaseURL, $s);
|
||||
$s = str_replace('[observer.url]', $observer['xchan_url'], $s);
|
||||
$s = str_replace('[observer.name]', $s1 . $observer['xchan_name'] . $s2, $s);
|
||||
$s = str_replace('[observer.address]', $s1 . $observer['xchan_addr'] . $s2, $s);
|
||||
$s = str_replace('[observer.webname]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $s);
|
||||
$s = str_replace('[observer.photo]', '', $s);
|
||||
}
|
||||
else {
|
||||
$s = str_replace('[observer.baseurl]', '', $s);
|
||||
$s = str_replace('[observer.url]', '', $s);
|
||||
$s = str_replace('[observer.name]', '', $s);
|
||||
$s = str_replace('[observer.address]', '', $s);
|
||||
$s = str_replace('[observer.webname]', '', $s);
|
||||
$s = str_replace('[observer.photo]', '', $s);
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
static public function get_file_ext($arr) {
|
||||
|
||||
if ($arr['mimetype'] === 'text/bbcode')
|
||||
return '.bb';
|
||||
elseif ($arr['mimetype'] === 'text/markdown')
|
||||
return '.md';
|
||||
elseif ($arr['mimetype'] === 'text/plain')
|
||||
return '.txt';
|
||||
|
||||
}
|
||||
|
||||
// This function is derived from
|
||||
// http://stackoverflow.com/questions/32068537/generate-table-of-contents-from-markdown-in-php
|
||||
static public function toc($content) {
|
||||
// ensure using only "\n" as line-break
|
||||
$source = str_replace(["\r\n", "\r"], "\n", $content);
|
||||
|
||||
// look for markdown TOC items
|
||||
preg_match_all(
|
||||
'/^(?:=|-|#).*$/m',
|
||||
$source,
|
||||
$matches,
|
||||
PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE
|
||||
);
|
||||
|
||||
// preprocess: iterate matched lines to create an array of items
|
||||
// where each item is an array(level, text)
|
||||
$file_size = strlen($source);
|
||||
foreach ($matches[0] as $item) {
|
||||
$found_mark = substr($item[0], 0, 1);
|
||||
if ($found_mark == '#') {
|
||||
// text is the found item
|
||||
$item_text = $item[0];
|
||||
$item_level = strrpos($item_text, '#') + 1;
|
||||
$item_text = substr($item_text, $item_level);
|
||||
}
|
||||
else {
|
||||
// text is the previous line (empty if <hr>)
|
||||
$item_offset = $item[1];
|
||||
$prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2));
|
||||
$item_text =
|
||||
substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1);
|
||||
$item_text = trim($item_text);
|
||||
$item_level = $found_mark == '=' ? 1 : 2;
|
||||
}
|
||||
if (!trim($item_text) or strpos($item_text, '|') !== FALSE) {
|
||||
// item is an horizontal separator or a table header, don't mind
|
||||
continue;
|
||||
}
|
||||
$raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)];
|
||||
}
|
||||
$o = '';
|
||||
foreach ($raw_toc as $t) {
|
||||
$level = intval($t['level']);
|
||||
$text = $t['text'];
|
||||
switch ($level) {
|
||||
case 1:
|
||||
$li = '* ';
|
||||
break;
|
||||
case 2:
|
||||
$li = ' * ';
|
||||
break;
|
||||
case 3:
|
||||
$li = ' * ';
|
||||
break;
|
||||
case 4:
|
||||
$li = ' * ';
|
||||
break;
|
||||
default:
|
||||
$li = '* ';
|
||||
break;
|
||||
}
|
||||
$o .= $li . $text . "\n";
|
||||
}
|
||||
return $o;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
|
||||
/**
|
||||
* @brief Class for handling channel specific configurations.
|
||||
*
|
||||
@@ -32,15 +34,15 @@ class PConfig {
|
||||
if(is_null($uid) || $uid === false)
|
||||
return false;
|
||||
|
||||
if(! is_array(\App::$config)) {
|
||||
if(! is_array(App::$config)) {
|
||||
btlogger('App::$config not an array');
|
||||
}
|
||||
|
||||
if(! array_key_exists($uid, \App::$config)) {
|
||||
\App::$config[$uid] = array();
|
||||
if(! array_key_exists($uid, App::$config)) {
|
||||
App::$config[$uid] = array();
|
||||
}
|
||||
|
||||
if(! is_array(\App::$config[$uid])) {
|
||||
if(! is_array(App::$config[$uid])) {
|
||||
btlogger('App::$config[$uid] not an array: ' . $uid);
|
||||
}
|
||||
|
||||
@@ -52,12 +54,12 @@ class PConfig {
|
||||
foreach($r as $rr) {
|
||||
$k = $rr['k'];
|
||||
$c = $rr['cat'];
|
||||
if(! array_key_exists($c, \App::$config[$uid])) {
|
||||
\App::$config[$uid][$c] = array();
|
||||
\App::$config[$uid][$c]['config_loaded'] = true;
|
||||
if(! array_key_exists($c, App::$config[$uid])) {
|
||||
App::$config[$uid][$c] = array();
|
||||
App::$config[$uid][$c]['config_loaded'] = true;
|
||||
}
|
||||
\App::$config[$uid][$c][$k] = $rr['v'];
|
||||
\App::$config[$uid][$c]['pcfgud:'.$k] = $rr['updated'];
|
||||
App::$config[$uid][$c][$k] = $rr['v'];
|
||||
App::$config[$uid][$c]['pcfgud:'.$k] = $rr['updated'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,15 +88,15 @@ class PConfig {
|
||||
if(is_null($uid) || $uid === false)
|
||||
return $default;
|
||||
|
||||
if(! array_key_exists($uid, \App::$config))
|
||||
if(! array_key_exists($uid, App::$config))
|
||||
self::Load($uid);
|
||||
|
||||
if((! array_key_exists($family, \App::$config[$uid])) || (! array_key_exists($key, \App::$config[$uid][$family])))
|
||||
if((! array_key_exists($family, App::$config[$uid])) || (! array_key_exists($key, App::$config[$uid][$family])))
|
||||
return $default;
|
||||
|
||||
return ((! is_array(\App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$uid][$family][$key]))
|
||||
? unserialize(\App::$config[$uid][$family][$key])
|
||||
: \App::$config[$uid][$family][$key]
|
||||
return ((! is_array(App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', App::$config[$uid][$family][$key]))
|
||||
? unserialize(App::$config[$uid][$family][$key])
|
||||
: App::$config[$uid][$family][$key]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -133,6 +135,7 @@ class PConfig {
|
||||
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
|
||||
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
|
||||
$new = false;
|
||||
$update = false;
|
||||
|
||||
$now = datetime_convert();
|
||||
if (! $updated) {
|
||||
@@ -143,23 +146,22 @@ class PConfig {
|
||||
$updated = datetime_convert('UTC','UTC','-2 seconds');
|
||||
}
|
||||
|
||||
$hash = hash('sha256',$family.':'.$key);
|
||||
$hash = gen_link_id($family.':'.$key);
|
||||
|
||||
if (self::Get($uid, 'hz_delpconfig', $hash) !== false) {
|
||||
if (self::Get($uid, 'hz_delpconfig', $hash) > $now) {
|
||||
logger('Refusing to update pconfig with outdated info (Item deleted more recently).', LOGGER_NORMAL, LOG_ERR);
|
||||
return self::Get($uid,$family,$key);
|
||||
} else {
|
||||
self::Delete($uid,'hz_delpconfig',$hash);
|
||||
self::Delete($uid, 'hz_delpconfig', $hash);
|
||||
}
|
||||
}
|
||||
|
||||
if(self::Get($uid, $family, $key) === false) {
|
||||
if(! array_key_exists($uid, \App::$config))
|
||||
\App::$config[$uid] = array();
|
||||
if(! array_key_exists($family, \App::$config[$uid]))
|
||||
\App::$config[$uid][$family] = array();
|
||||
|
||||
if(! array_key_exists($uid, App::$config))
|
||||
App::$config[$uid] = array();
|
||||
if(! array_key_exists($family, App::$config[$uid]))
|
||||
App::$config[$uid][$family] = array();
|
||||
|
||||
$ret = q("INSERT INTO pconfig ( uid, cat, k, v, updated ) VALUES ( %d, '%s', '%s', '%s', '%s' ) ",
|
||||
intval($uid),
|
||||
@@ -177,13 +179,14 @@ class PConfig {
|
||||
logger("Error: Insert to pconfig failed.",LOGGER_NORMAL, LOG_ERR);
|
||||
}
|
||||
|
||||
\App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
|
||||
$new = true;
|
||||
App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
|
||||
|
||||
}
|
||||
else {
|
||||
$new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now);
|
||||
$update = (App::$config[$uid][$family]['pcfgud:'.$key] < $now);
|
||||
|
||||
if ($new) {
|
||||
if ($update) {
|
||||
|
||||
// @NOTE There is still a possible race condition under limited circumstances
|
||||
// where a value will be updated by another thread with more current data than
|
||||
@@ -198,7 +201,7 @@ class PConfig {
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
\App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
|
||||
App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
|
||||
|
||||
} else {
|
||||
logger('Refusing to update pconfig with outdated info.', LOGGER_NORMAL, LOG_ERR);
|
||||
@@ -211,16 +214,16 @@ class PConfig {
|
||||
// set in the life of this page. We need this to
|
||||
// synchronise channel clones.
|
||||
|
||||
if(! array_key_exists('transient', \App::$config[$uid]))
|
||||
\App::$config[$uid]['transient'] = array();
|
||||
if(! array_key_exists($family, \App::$config[$uid]['transient']))
|
||||
\App::$config[$uid]['transient'][$family] = array();
|
||||
if(! array_key_exists('transient', App::$config[$uid]))
|
||||
App::$config[$uid]['transient'] = array();
|
||||
if(! array_key_exists($family, App::$config[$uid]['transient']))
|
||||
App::$config[$uid]['transient'][$family] = array();
|
||||
|
||||
\App::$config[$uid][$family][$key] = $value;
|
||||
App::$config[$uid][$family][$key] = $value;
|
||||
|
||||
if ($new) {
|
||||
\App::$config[$uid]['transient'][$family][$key] = $value;
|
||||
\App::$config[$uid]['transient'][$family]['pcfgud:'.$key] = $updated;
|
||||
if ($new || $update) {
|
||||
App::$config[$uid]['transient'][$family][$key] = $value;
|
||||
App::$config[$uid]['transient'][$family]['pcfgud:'.$key] = $updated;
|
||||
}
|
||||
|
||||
if($ret)
|
||||
@@ -253,7 +256,7 @@ class PConfig {
|
||||
|
||||
$updated = ($updated) ? $updated : datetime_convert('UTC','UTC','-2 seconds');
|
||||
$now = datetime_convert();
|
||||
$newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now);
|
||||
$newer = (App::$config[$uid][$family]['pcfgud:'.$key] < $now);
|
||||
|
||||
if (! $newer) {
|
||||
logger('Refusing to delete pconfig with outdated delete request.', LOGGER_NORMAL, LOG_ERR);
|
||||
@@ -262,12 +265,12 @@ class PConfig {
|
||||
|
||||
$ret = false;
|
||||
|
||||
if (isset(\App::$config[$uid][$family][$key])) {
|
||||
unset(\App::$config[$uid][$family][$key]);
|
||||
if (isset(App::$config[$uid][$family][$key])) {
|
||||
unset(App::$config[$uid][$family][$key]);
|
||||
}
|
||||
|
||||
if (isset(\App::$config[$uid][$family]['pcfgud:'.$key])) {
|
||||
unset(\App::$config[$uid][$family]['pcfgud:'.$key]);
|
||||
if (isset(App::$config[$uid][$family]['pcfgud:'.$key])) {
|
||||
unset(App::$config[$uid][$family]['pcfgud:'.$key]);
|
||||
}
|
||||
|
||||
$ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'",
|
||||
@@ -278,9 +281,9 @@ class PConfig {
|
||||
|
||||
// Synchronize delete with clones.
|
||||
|
||||
if ($family != 'hz_delpconfig') {
|
||||
$hash = hash('sha256',$family.':'.$key);
|
||||
set_pconfig($uid,'hz_delpconfig',$hash,$updated);
|
||||
if ($family !== 'hz_delpconfig') {
|
||||
$hash = gen_link_id($family.':'.$key);
|
||||
set_pconfig($uid, 'hz_delpconfig', $hash, $updated);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Access\PermissionRoles;
|
||||
use Zotlabs\Access\Permissions;
|
||||
use Zotlabs\Daemon\Master;
|
||||
|
||||
/**
|
||||
* @brief Permission Categories. Permission rules for various classes of connections.
|
||||
@@ -38,33 +39,33 @@ class Permcat {
|
||||
|
||||
// first check role perms for a perms_connect setting
|
||||
|
||||
$role = get_pconfig($channel_id,'system','permissions_role');
|
||||
if($role) {
|
||||
$role = get_pconfig($channel_id, 'system', 'permissions_role');
|
||||
if ($role) {
|
||||
$x = PermissionRoles::role_perms($role);
|
||||
if($x['perms_connect']) {
|
||||
if ($x['perms_connect']) {
|
||||
$perms = Permissions::FilledPerms($x['perms_connect']);
|
||||
}
|
||||
}
|
||||
|
||||
// if no role perms it may be a custom role, see if there any autoperms
|
||||
|
||||
if(! $perms) {
|
||||
if (!$perms) {
|
||||
$perms = Permissions::FilledAutoPerms($channel_id);
|
||||
}
|
||||
|
||||
// if no autoperms it may be a custom role with manual perms
|
||||
|
||||
if(! $perms) {
|
||||
if (!$perms) {
|
||||
$r = q("select channel_hash from channel where channel_id = %d",
|
||||
intval($channel_id)
|
||||
);
|
||||
if($r) {
|
||||
if ($r) {
|
||||
$x = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'my_perms'",
|
||||
intval($channel_id),
|
||||
dbesc($r[0]['channel_hash'])
|
||||
);
|
||||
if($x) {
|
||||
foreach($x as $xv) {
|
||||
if ($x) {
|
||||
foreach ($x as $xv) {
|
||||
$perms[$xv['k']] = intval($xv['v']);
|
||||
}
|
||||
}
|
||||
@@ -73,25 +74,27 @@ class Permcat {
|
||||
|
||||
// nothing was found - create a filled permission array where all permissions are 0
|
||||
|
||||
if(! $perms) {
|
||||
if (!$perms) {
|
||||
$perms = Permissions::FilledPerms([]);
|
||||
}
|
||||
|
||||
$this->permcats[] = [
|
||||
'name' => 'default',
|
||||
'localname' => t('default','permcat'),
|
||||
'localname' => t('Default', 'permcat'),
|
||||
'perms' => Permissions::Operms($perms),
|
||||
'raw_perms' => $perms,
|
||||
'system' => 1
|
||||
];
|
||||
|
||||
|
||||
$p = $this->load_permcats($channel_id);
|
||||
if($p) {
|
||||
for($x = 0; $x < count($p); $x++) {
|
||||
if ($p) {
|
||||
for ($x = 0; $x < count($p); $x++) {
|
||||
$this->permcats[] = [
|
||||
'name' => $p[$x][0],
|
||||
'localname' => $p[$x][1],
|
||||
'perms' => Permissions::Operms(Permissions::FilledPerms($p[$x][2])),
|
||||
'raw_perms' => Permissions::FilledPerms($p[$x][2]),
|
||||
'system' => intval($p[$x][3])
|
||||
];
|
||||
}
|
||||
@@ -116,9 +119,9 @@ class Permcat {
|
||||
* * \e bool \b error if $name not found in permcats true
|
||||
*/
|
||||
public function fetch($name) {
|
||||
if($name && $this->permcats) {
|
||||
foreach($this->permcats as $permcat) {
|
||||
if(strcasecmp($permcat['name'], $name) === 0) {
|
||||
if ($name && $this->permcats) {
|
||||
foreach ($this->permcats as $permcat) {
|
||||
if (strcasecmp($permcat['name'], $name) === 0) {
|
||||
return $permcat;
|
||||
}
|
||||
}
|
||||
@@ -128,31 +131,28 @@ class Permcat {
|
||||
}
|
||||
|
||||
public function load_permcats($uid) {
|
||||
|
||||
/*
|
||||
$permcats = [
|
||||
[ 'follower', t('follower','permcat'),
|
||||
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki',
|
||||
'post_like' ], 1
|
||||
],
|
||||
[ 'contributor', t('contributor','permcat'),
|
||||
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki',
|
||||
'post_wall','post_comments','write_wiki','post_like','tag_deliver','chat' ], 1
|
||||
],
|
||||
[ 'publisher', t('publisher','permcat'),
|
||||
[ 'contributor', t('Contributor','permcat'),
|
||||
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages',
|
||||
'write_storage','post_wall','write_pages','write_wiki','post_comments','post_like','tag_deliver',
|
||||
'chat', 'republish' ], 1
|
||||
]
|
||||
'write_storage','post_wall','write_pages','write_wiki','post_comments', 'post_mail', 'post_like',
|
||||
'chat' ], 1
|
||||
],
|
||||
[ 'muted', t('Muted','permcat'),
|
||||
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki',
|
||||
'post_comments','write_wiki','post_like' ], 1
|
||||
],
|
||||
];
|
||||
|
||||
if($uid) {
|
||||
*/
|
||||
if ($uid) {
|
||||
$x = q("select * from pconfig where uid = %d and cat = 'permcat'",
|
||||
intval($uid)
|
||||
);
|
||||
if($x) {
|
||||
foreach($x as $xv) {
|
||||
$value = ((preg_match('|^a:[0-9]+:{.*}$|s', $xv['v'])) ? unserialize($xv['v']) : $xv['v']);
|
||||
$permcats[] = [ $xv['k'], $xv['k'], $value, 0 ];
|
||||
|
||||
if ($x) {
|
||||
foreach ($x as $xv) {
|
||||
$value = ((preg_match('|^a:[0-9]+:{.*}$|s', $xv['v'])) ? unserialize($xv['v']) : $xv['v']);
|
||||
$permcats[] = [$xv['k'], $xv['k'], $value, 0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,11 +167,11 @@ class Permcat {
|
||||
}
|
||||
|
||||
static public function find_permcat($arr, $name) {
|
||||
if((! $arr) || (! $name))
|
||||
if ((!$arr) || (!$name))
|
||||
return false;
|
||||
|
||||
foreach($arr as $p)
|
||||
if($p['name'] == $name)
|
||||
foreach ($arr as $p)
|
||||
if ($p['name'] == $name)
|
||||
return $p['value'];
|
||||
}
|
||||
|
||||
@@ -183,4 +183,105 @@ class Permcat {
|
||||
PConfig::Delete($channel_id, 'permcat', $name);
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* @brief assign a contact role to contacts
|
||||
*
|
||||
* @param array $channel
|
||||
* @param string $role the name of the role
|
||||
* @param array $contacts an array of contact hashes
|
||||
*/
|
||||
public static function assign($channel, $role, $contacts) {
|
||||
|
||||
if (!isset($channel['channel_id'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_array($contacts) || empty($contacts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$role) {
|
||||
// lookup the default
|
||||
$role = get_pconfig($channel['channel_id'], 'system', 'default_permcat', 'default');
|
||||
}
|
||||
|
||||
|
||||
// Doublecheck that we do not assign a role to ourself.
|
||||
// It does not make a difference but could be confusing.
|
||||
if (in_array($channel['channel_hash'], $contacts)) {
|
||||
$contacts = array_diff($contacts, [$channel['channel_hash']]);
|
||||
}
|
||||
|
||||
$all_perms = Permissions::Perms();
|
||||
$permcats = new Permcat($channel['channel_id']);
|
||||
$role_perms = $permcats->fetch($role);
|
||||
|
||||
if (isset($role_perms['error'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$perms = $role_perms['raw_perms'];
|
||||
|
||||
$values_sql = '';
|
||||
stringify_array_elms($contacts, true);
|
||||
|
||||
if ($all_perms && $perms) {
|
||||
|
||||
foreach ($contacts as $contact) {
|
||||
foreach ($all_perms as $perm => $desc) {
|
||||
if (array_key_exists($perm, $perms)) {
|
||||
$values_sql .= " (" . intval($channel['channel_id']) . ", " . protect_sprintf($contact) . ", 'my_perms', '" . dbesc($perm) . "', " . intval($perms[$perm]) . "),";
|
||||
}
|
||||
else {
|
||||
$values_sql .= " (" . intval($channel['channel_id']) . ", " . protect_sprintf($contact) . ", 'my_perms', '" . dbesc($perm) . "', 0), ";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$values_sql = rtrim($values_sql, ',');
|
||||
|
||||
dbq("DELETE FROM abconfig WHERE chan = " . intval($channel['channel_id']) . " AND cat = 'my_perms' AND xchan IN (" . protect_sprintf(implode(',', $contacts)) . ")");
|
||||
|
||||
dbq("INSERT INTO abconfig ( chan, xchan, cat, k, v ) VALUES $values_sql");
|
||||
|
||||
q("UPDATE abook SET abook_role = '%s'
|
||||
WHERE abook_xchan IN (" . protect_sprintf(implode(',', $contacts)) . ") AND abook_channel = %d",
|
||||
dbesc($role),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
|
||||
$r = q("SELECT abook.*, xchan.* FROM abook LEFT JOIN xchan ON abook.abook_xchan = xchan.xchan_hash WHERE abook.abook_xchan IN (" . protect_sprintf(implode(',', $contacts)) . ") AND abook.abook_channel = %d AND abook_self = 0",
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
|
||||
foreach ($r as $rr) {
|
||||
|
||||
if (intval($rr['abook_self'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Master::Summon([
|
||||
'Notifier',
|
||||
'permission_update',
|
||||
$rr['abook_id']
|
||||
]);
|
||||
|
||||
$clone = $rr;
|
||||
|
||||
unset($clone['abook_id']);
|
||||
unset($clone['abook_account']);
|
||||
unset($clone['abook_channel']);
|
||||
|
||||
$abconfig = load_abconfig($channel['channel_id'], $clone['abook_xchan']);
|
||||
if ($abconfig)
|
||||
$clone['abconfig'] = $abconfig;
|
||||
|
||||
Libsync::build_sync_packet(0 /* use the current local_channel */, ['abook' => [$clone]]);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,7 +57,6 @@ class Queue {
|
||||
outq_priority = outq_priority + %d,
|
||||
outq_scheduled = '%s'
|
||||
WHERE outq_hash = '%s'",
|
||||
|
||||
dbesc(datetime_convert()),
|
||||
intval($add_priority),
|
||||
dbesc($next),
|
||||
@@ -65,16 +64,38 @@ class Queue {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
static function remove($id,$channel_id = 0) {
|
||||
logger('queue: remove queue item ' . $id,LOGGER_DEBUG);
|
||||
public static function remove($id, $channel_id = 0) {
|
||||
logger('queue: remove queue item ' . $id, LOGGER_DEBUG);
|
||||
$sql_extra = (($channel_id) ? " and outq_channel = " . intval($channel_id) . " " : '');
|
||||
|
||||
q("DELETE FROM outq WHERE outq_hash = '%s' $sql_extra",
|
||||
// figure out what endpoint it is going to.
|
||||
$record = q("select outq_posturl from outq where outq_hash = '%s' $sql_extra",
|
||||
dbesc($id)
|
||||
);
|
||||
}
|
||||
|
||||
if ($record) {
|
||||
q("DELETE FROM outq WHERE outq_hash = '%s' $sql_extra",
|
||||
dbesc($id)
|
||||
);
|
||||
|
||||
// If there's anything remaining in the queue for this site, move one of them to the next active
|
||||
// queue run by setting outq_scheduled back to the present. We may be attempting to deliver it
|
||||
// as a 'piled_up' delivery, but this ensures the site has an active queue entry as long as queued
|
||||
// entries still exist for it. This fixes an issue where one immediate delivery left everything
|
||||
// else for that site undeliverable since all the other entries had been pushed far into the future.
|
||||
|
||||
$r = q("SELECT outq_hash, outq_posturl FROM outq WHERE outq_posturl = '%s' LIMIT 1",
|
||||
dbesc($record[0]['outq_posturl'])
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
$hashes = ids_to_querystr($r, 'outq_hash', true);
|
||||
$x = q("UPDATE outq SET outq_scheduled = '%s' WHERE outq_hash IN ($hashes)",
|
||||
dbesc(datetime_convert())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static function remove_by_posturl($posturl) {
|
||||
logger('queue: remove queue posturl ' . $posturl,LOGGER_DEBUG);
|
||||
@@ -84,8 +105,6 @@ class Queue {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static function set_delivered($id,$channel = 0) {
|
||||
logger('queue: set delivered ' . $id,LOGGER_DEBUG);
|
||||
$sql_extra = (($channel['channel_id']) ? " and outq_channel = " . intval($channel['channel_id']) . " " : '');
|
||||
@@ -110,21 +129,30 @@ class Queue {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hash = $arr['hash'] ?? '';
|
||||
$account_id = $arr['account_id'] ?? 0;
|
||||
$channel_id = $arr['channel_id'] ?? 0;
|
||||
$driver = $arr['driver'] ?? 'zot6';
|
||||
$posturl = $arr['posturl'] ?? '';
|
||||
$priority = $arr['priority'] ?? 0;
|
||||
$notify = $arr['notify'] ?? '';
|
||||
$msg = $arr['msg'] ?? '';
|
||||
|
||||
$x = q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_priority,
|
||||
outq_created, outq_updated, outq_scheduled, outq_notify, outq_msg )
|
||||
values ( '%s', %d, %d, '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s' )",
|
||||
dbesc($arr['hash']),
|
||||
intval($arr['account_id']),
|
||||
intval($arr['channel_id']),
|
||||
dbesc(($arr['driver']) ? $arr['driver'] : 'zot6'),
|
||||
dbesc($arr['posturl']),
|
||||
dbesc($hash),
|
||||
intval($account_id),
|
||||
intval($channel_id),
|
||||
dbesc($driver),
|
||||
dbesc($posturl),
|
||||
intval(1),
|
||||
intval(isset($arr['priority']) ? $arr['priority'] : 0),
|
||||
intval($priority),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($arr['notify']),
|
||||
dbesc(($arr['msg']) ? $arr['msg'] : '')
|
||||
dbesc($notify),
|
||||
dbesc($msg)
|
||||
);
|
||||
return $x;
|
||||
|
||||
@@ -143,17 +171,19 @@ class Queue {
|
||||
$y = q("select site_update, site_dead from site where site_url = '%s' ",
|
||||
dbesc($base)
|
||||
);
|
||||
if($y) {
|
||||
if(intval($y[0]['site_dead'])) {
|
||||
|
||||
if ($y) {
|
||||
// Don't bother delivering if the site is dead.
|
||||
// And if we haven't heard from the site in over a month - let them through but 3 strikes you're out.
|
||||
if (intval($y[0]['site_dead']) || ($y[0]['site_update'] < datetime_convert('UTC', 'UTC', 'now - 1 month') && $outq['outq_priority'] > 20)) {
|
||||
q("update dreport set dreport_result = '%s' where dreport_queue = '%s'",
|
||||
dbesc('site dead'),
|
||||
dbesc($outq['outq_hash'])
|
||||
);
|
||||
self::remove_by_posturl($outq['outq_posturl']);
|
||||
logger('dead site ignored ' . $base);
|
||||
return;
|
||||
}
|
||||
if($y[0]['site_update'] < datetime_convert('UTC','UTC','now - 1 month')) {
|
||||
self::update($outq['outq_hash'], 10);
|
||||
logger('immediate delivery deferred for site ' . $base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -202,7 +232,7 @@ class Queue {
|
||||
|
||||
if($channel && $base) {
|
||||
$h = q("SELECT hubloc_sitekey, site_crypto FROM hubloc LEFT JOIN site ON hubloc_url = site_url
|
||||
WHERE site_url = '%s' AND hubloc_network = 'zot6' ORDER BY hubloc_id DESC LIMIT 1",
|
||||
WHERE site_url = '%s' AND hubloc_network = 'zot6' AND hubloc_deleted = 0 ORDER BY hubloc_primary DESC, hubloc_id DESC LIMIT 1",
|
||||
dbesc($base)
|
||||
);
|
||||
if($h) {
|
||||
@@ -216,7 +246,7 @@ class Queue {
|
||||
|
||||
if($result['success']) {
|
||||
logger('deliver: remote zot delivery succeeded to ' . $outq['outq_posturl']);
|
||||
Libzot::process_response($outq['outq_posturl'],$result, $outq);
|
||||
Libzot::process_response($outq['outq_posturl'], $result, $outq);
|
||||
}
|
||||
else {
|
||||
logger('deliver: remote zot delivery failed to ' . $outq['outq_posturl']);
|
||||
|
||||
384
Zotlabs/Lib/QueueWorker.php
Normal file
384
Zotlabs/Lib/QueueWorker.php
Normal file
@@ -0,0 +1,384 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Ramsey\Uuid\Exception\UnableToBuildUuidException;
|
||||
|
||||
class QueueWorker {
|
||||
|
||||
public static $queueworker = null;
|
||||
public static $maxworkers = 0;
|
||||
public static $workermaxage = 0;
|
||||
public static $workersleep = 100;
|
||||
public static $default_priorities = [
|
||||
'Notifier' => 10,
|
||||
'Deliver' => 10,
|
||||
'Cache_query' => 10,
|
||||
'Content_importer' => 1,
|
||||
'File_importer' => 1,
|
||||
'Channel_purge' => 1,
|
||||
'Directory' => 1
|
||||
];
|
||||
|
||||
// Exceptions for processtimeout ($workermaxage) value.
|
||||
// Currently the value is overriden with 3600 seconds (1h).
|
||||
public static $long_running_cmd = [
|
||||
'Queue',
|
||||
'Expire'
|
||||
];
|
||||
|
||||
private static function qstart() {
|
||||
q('START TRANSACTION');
|
||||
}
|
||||
|
||||
private static function qcommit() {
|
||||
q("COMMIT");
|
||||
}
|
||||
|
||||
private static function qrollback() {
|
||||
q("ROLLBACK");
|
||||
}
|
||||
|
||||
public static function Summon($argv) {
|
||||
|
||||
if ($argv[0] !== 'Queueworker') {
|
||||
|
||||
$priority = 0; // @TODO allow reprioritization
|
||||
|
||||
if (isset(self::$default_priorities[$argv[0]])) {
|
||||
$priority = self::$default_priorities[$argv[0]];
|
||||
}
|
||||
|
||||
$workinfo = ['argc' => count($argv), 'argv' => $argv];
|
||||
$workinfo_json = json_encode($workinfo);
|
||||
$uuid = self::getUuid($workinfo_json);
|
||||
|
||||
$r = q("SELECT * FROM workerq WHERE workerq_uuid = '%s'",
|
||||
dbesc($uuid)
|
||||
);
|
||||
if ($r) {
|
||||
logger("Summon: Ignoring duplicate workerq task", LOGGER_DEBUG);
|
||||
logger(print_r($workinfo, true));
|
||||
return;
|
||||
}
|
||||
|
||||
logger('queueworker_stats_summon: cmd:' . $argv[0] . ' ' . 'timestamp:' . time());
|
||||
|
||||
self::qstart();
|
||||
$r = q("INSERT INTO workerq (workerq_priority, workerq_data, workerq_uuid, workerq_cmd) VALUES (%d, '%s', '%s', '%s')",
|
||||
intval($priority),
|
||||
$workinfo_json,
|
||||
dbesc($uuid),
|
||||
dbesc($argv[0])
|
||||
);
|
||||
if (!$r) {
|
||||
self::qrollback();
|
||||
logger("INSERT FAILED", LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
self::qcommit();
|
||||
logger('INSERTED: ' . $workinfo_json, LOGGER_DEBUG);
|
||||
}
|
||||
|
||||
$workers = self::GetWorkerCount();
|
||||
if ($workers < self::$maxworkers) {
|
||||
logger($workers . '/' . self::$maxworkers . ' workers active', LOGGER_DEBUG);
|
||||
$phpbin = get_config('system', 'phpbin', 'php');
|
||||
proc_run($phpbin, 'Zotlabs/Daemon/Master.php', ['Queueworker']);
|
||||
}
|
||||
}
|
||||
|
||||
public static function Release($argv) {
|
||||
|
||||
if ($argv[0] !== 'Queueworker') {
|
||||
|
||||
$priority = 0; // @TODO allow reprioritization
|
||||
if (isset(self::$default_priorities[$argv[0]])) {
|
||||
$priority = self::$default_priorities[$argv[0]];
|
||||
}
|
||||
|
||||
$workinfo = ['argc' => count($argv), 'argv' => $argv];
|
||||
$workinfo_json = json_encode($workinfo);
|
||||
$uuid = self::getUuid($workinfo_json);
|
||||
|
||||
$r = q("SELECT * FROM workerq WHERE workerq_uuid = '%s'",
|
||||
dbesc($uuid)
|
||||
);
|
||||
if ($r) {
|
||||
logger("Release: Duplicate task - do not insert.", LOGGER_DEBUG);
|
||||
logger(print_r($workinfo, true));
|
||||
return;
|
||||
}
|
||||
|
||||
self::qstart();
|
||||
$r = q("INSERT INTO workerq (workerq_priority, workerq_data, workerq_uuid, workerq_cmd) VALUES (%d, '%s', '%s', '%s')",
|
||||
intval($priority),
|
||||
$workinfo_json,
|
||||
dbesc($uuid),
|
||||
dbesc($argv[0])
|
||||
);
|
||||
if (!$r) {
|
||||
self::qrollback();
|
||||
logger("Insert failed: " . $workinfo_json, LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
self::qcommit();
|
||||
logger('INSERTED: ' . $workinfo_json, LOGGER_DEBUG);
|
||||
}
|
||||
|
||||
self::Process();
|
||||
}
|
||||
|
||||
public static function GetWorkerCount() {
|
||||
if (self::$maxworkers == 0) {
|
||||
self::$maxworkers = get_config('queueworker', 'max_queueworkers', 4);
|
||||
self::$maxworkers = self::$maxworkers > 3 ? self::$maxworkers : 4;
|
||||
}
|
||||
if (self::$workermaxage == 0) {
|
||||
self::$workermaxage = get_config('queueworker', 'max_queueworker_age');
|
||||
self::$workermaxage = self::$workermaxage > 120 ? self::$workermaxage : 300;
|
||||
}
|
||||
|
||||
self::qstart();
|
||||
|
||||
// skip locked is preferred but is not supported by mariadb < 10.6 which is still used a lot - hence make it optional
|
||||
$sql_quirks = ((get_config('system', 'db_skip_locked_supported')) ? 'SKIP LOCKED' : 'NOWAIT');
|
||||
|
||||
$r = q("SELECT workerq_id FROM workerq WHERE workerq_reservationid IS NOT NULL AND workerq_processtimeout < %s FOR UPDATE $sql_quirks",
|
||||
db_utcnow()
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
// TODO: some long running services store their pid in config.procid.daemon
|
||||
// we could possibly check if a pid exist and check if the process is still alive
|
||||
// prior to reseting workerq_reservationid
|
||||
|
||||
$ids = ids_to_querystr($r, 'workerq_id');
|
||||
$u = dbq("update workerq set workerq_reservationid = null where workerq_id in ($ids)");
|
||||
}
|
||||
|
||||
self::qcommit();
|
||||
|
||||
//q("update workerq set workerq_reservationid = null where workerq_reservationid is not null and workerq_processtimeout < %s",
|
||||
//db_utcnow()
|
||||
//);
|
||||
|
||||
//usleep(self::$workersleep);
|
||||
|
||||
$workers = dbq("select count(*) as total from workerq where workerq_reservationid is not null");
|
||||
logger("WORKERCOUNT: " . $workers[0]['total'], LOGGER_DEBUG);
|
||||
|
||||
return intval($workers[0]['total']);
|
||||
}
|
||||
|
||||
public static function GetWorkerID() {
|
||||
if (self::$queueworker) {
|
||||
return self::$queueworker;
|
||||
}
|
||||
|
||||
$wid = uniqid('', true);
|
||||
|
||||
//usleep(mt_rand(300000, 1000000)); //Sleep .3 - 1 seconds before creating a new worker.
|
||||
|
||||
$workers = self::GetWorkerCount();
|
||||
|
||||
if ($workers >= self::$maxworkers) {
|
||||
logger("Too many active workers ($workers) max = " . self::$maxworkers, LOGGER_DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
self::$queueworker = $wid;
|
||||
|
||||
return $wid;
|
||||
}
|
||||
|
||||
private static function getWorkId() {
|
||||
self::GetWorkerCount();
|
||||
|
||||
self::qstart();
|
||||
|
||||
// skip locked is preferred but is not supported by mariadb < 10.6 which is still used a lot - hence make it optional
|
||||
$sql_quirks = ((get_config('system', 'db_skip_locked_supported')) ? 'SKIP LOCKED' : 'NOWAIT');
|
||||
|
||||
$work = dbq("SELECT workerq_id, workerq_cmd FROM workerq WHERE workerq_reservationid IS NULL ORDER BY workerq_priority DESC, workerq_id ASC LIMIT 1 FOR UPDATE $sql_quirks");
|
||||
|
||||
if (!$work) {
|
||||
self::qrollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
$id = $work[0]['workerq_id'];
|
||||
$cmd = $work[0]['workerq_cmd'];
|
||||
$age = self::$workermaxage;
|
||||
|
||||
if (in_array($cmd, self::$long_running_cmd)) {
|
||||
$age = 3600; // 1h TODO: make this configurable
|
||||
}
|
||||
|
||||
$work = q("UPDATE workerq SET workerq_reservationid = '%s', workerq_processtimeout = %s + INTERVAL %s WHERE workerq_id = %d",
|
||||
self::$queueworker,
|
||||
db_utcnow(),
|
||||
db_quoteinterval($age . " SECOND"),
|
||||
intval($id)
|
||||
);
|
||||
|
||||
if (!$work) {
|
||||
self::qrollback();
|
||||
logger("Could not update workerq.", LOGGER_DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
logger("GOTWORK: " . json_encode($work), LOGGER_DEBUG);
|
||||
self::qcommit();
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
public static function Process() {
|
||||
$sleep = intval(get_config('queueworker', 'queue_worker_sleep', 100));
|
||||
$auto_queue_worker_sleep = get_config('queueworker', 'auto_queue_worker_sleep', 0);
|
||||
|
||||
if (!self::GetWorkerID()) {
|
||||
if ($auto_queue_worker_sleep) {
|
||||
set_config('queueworker', 'queue_worker_sleep', $sleep + 100);
|
||||
}
|
||||
|
||||
logger('Unable to get worker ID. Exiting.', LOGGER_DEBUG);
|
||||
killme();
|
||||
}
|
||||
|
||||
if ($auto_queue_worker_sleep && $sleep > 100) {
|
||||
$next_sleep = $sleep - 100;
|
||||
set_config('queueworker', 'queue_worker_sleep', (($next_sleep < 100) ? 100 : $next_sleep));
|
||||
}
|
||||
|
||||
$jobs = 0;
|
||||
$workid = self::getWorkId();
|
||||
$load_average_sleep = false;
|
||||
self::$workersleep = $sleep;
|
||||
self::$workersleep = ((intval(self::$workersleep) > 100) ? intval(self::$workersleep) : 100);
|
||||
|
||||
if (function_exists('sys_getloadavg') && get_config('queueworker', 'load_average_sleep')) {
|
||||
// very experimental!
|
||||
$load_average_sleep = true;
|
||||
}
|
||||
|
||||
while ($workid) {
|
||||
|
||||
if ($load_average_sleep) {
|
||||
$load_average = sys_getloadavg();
|
||||
self::$workersleep = intval($load_average[0]) * 10000;
|
||||
|
||||
if (!self::$workersleep) {
|
||||
self::$workersleep = 100;
|
||||
}
|
||||
}
|
||||
|
||||
logger('queue_worker_sleep: ' . self::$workersleep, LOGGER_DEBUG);
|
||||
|
||||
usleep(self::$workersleep);
|
||||
|
||||
$workitem = dbq("SELECT * FROM workerq WHERE workerq_id = $workid");
|
||||
|
||||
if ($workitem) {
|
||||
// At least SOME work to do.... in case there's more, let's ramp up workers.
|
||||
$workers = self::GetWorkerCount();
|
||||
|
||||
if ($workers < self::$maxworkers) {
|
||||
logger($workers . '/' . self::$maxworkers . ' workers active', LOGGER_DEBUG);
|
||||
$phpbin = get_config('system', 'phpbin', 'php');
|
||||
proc_run($phpbin, 'Zotlabs/Daemon/Master.php', ['Queueworker']);
|
||||
}
|
||||
|
||||
$jobs++;
|
||||
|
||||
logger("Workinfo: " . $workitem[0]['workerq_data'], LOGGER_DEBUG);
|
||||
|
||||
$workinfo = json_decode($workitem[0]['workerq_data'], true);
|
||||
$argv = $workinfo['argv'];
|
||||
|
||||
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
|
||||
$argv = flatten_array_recursive($argv);
|
||||
$argc = count($argv);
|
||||
$rnd = random_string(16);
|
||||
|
||||
logger('PROCESSING: ' . $rnd . ' ' . print_r($argv[0], true));
|
||||
|
||||
$start_timestamp = microtime(true);
|
||||
|
||||
$cls::run($argc, $argv);
|
||||
|
||||
logger('logger_stats_data cmd:' . $argv[0] . ' start:' . $start_timestamp . ' ' . 'end:' . microtime(true) . ' meta:' . $rnd);
|
||||
|
||||
logger('COMPLETED: ' . $rnd);
|
||||
|
||||
// @FIXME: Right now we assume that if we get a return, everything is OK.
|
||||
// At some point we may want to test whether the run returns true/false
|
||||
// and requeue the work to be tried again if needed. But we probably want
|
||||
// to implement some sort of "retry interval" first.
|
||||
|
||||
dbq("delete from workerq where workerq_id = $workid");
|
||||
}
|
||||
else {
|
||||
logger("NO WORKITEM!", LOGGER_DEBUG);
|
||||
}
|
||||
$workid = self::getWorkId();
|
||||
}
|
||||
logger('Master: Worker Thread: queue items processed:' . $jobs, LOGGER_DEBUG);
|
||||
}
|
||||
|
||||
public static function ClearQueue() {
|
||||
$work = q("select * from workerq");
|
||||
while ($work) {
|
||||
foreach ($work as $workitem) {
|
||||
$workinfo = json_decode($workitem['v'], true);
|
||||
$argc = $workinfo['argc'];
|
||||
$argv = $workinfo['argv'];
|
||||
|
||||
logger('Master: process: ' . print_r($argv, true), LOGGER_ALL, LOG_DEBUG);
|
||||
|
||||
if (!isset($argv[0])) {
|
||||
q("delete from workerq where workerq_id = %d",
|
||||
$work[0]['workerq_id']
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
|
||||
$cls::run($argc, $argv);
|
||||
|
||||
q("delete from workerq where workerq_id = %d",
|
||||
$work[0]['workerq_id']
|
||||
);
|
||||
|
||||
//Give the server .3 seconds to catch its breath between tasks.
|
||||
//This will hopefully keep it from crashing to it's knees entirely
|
||||
//if the last task ended up initiating other parallel processes
|
||||
//(eg. polling remotes)
|
||||
usleep(300000);
|
||||
}
|
||||
|
||||
//Make sure nothing new came in
|
||||
$work = q("select * from workerq");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a name-based v5 UUID with custom namespace
|
||||
*
|
||||
* @param string $data
|
||||
* @return string $uuid
|
||||
*/
|
||||
private static function getUuid(string $data) {
|
||||
$namespace = '3a112e42-f147-4ccf-a78b-f6841339ea2a';
|
||||
try {
|
||||
$uuid = Uuid::uuid5($namespace, $data)->toString();
|
||||
} catch (UnableToBuildUuidException $e) {
|
||||
logger('UUID generation failed');
|
||||
return '';
|
||||
}
|
||||
return $uuid;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -112,7 +112,7 @@ class Share {
|
||||
if(! $this->item)
|
||||
return $bb;
|
||||
|
||||
$is_photo = (($this->item['obj_type'] === ACTIVITY_OBJ_PHOTO) ? true : false);
|
||||
$is_photo = ((in_array($this->item['obj_type'], ['Image', ACTIVITY_OBJ_PHOTO])) ? true : false);
|
||||
if($is_photo) {
|
||||
$object = json_decode($this->item['obj'],true);
|
||||
$photo_bb = $object['body'];
|
||||
|
||||
@@ -16,13 +16,13 @@ class System {
|
||||
}
|
||||
|
||||
static public function get_site_name() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['sitename'])
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && isset(\App::$config['system']['sitename']))
|
||||
return \App::$config['system']['sitename'];
|
||||
return '';
|
||||
}
|
||||
|
||||
static public function get_project_version() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['hide_version'])
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && isset(\App::$config['system']['hide_version']))
|
||||
return '';
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && array_key_exists('std_version',\App::$config['system']))
|
||||
return \App::$config['system']['std_version'];
|
||||
@@ -31,33 +31,33 @@ class System {
|
||||
}
|
||||
|
||||
static public function get_update_version() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['hide_version'])
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && isset(\App::$config['system']['hide_version']))
|
||||
return '';
|
||||
return DB_UPDATE_VERSION;
|
||||
}
|
||||
|
||||
|
||||
static public function get_notify_icon() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['email_notify_icon_url'])
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && isset(\App::$config['system']['email_notify_icon_url']))
|
||||
return \App::$config['system']['email_notify_icon_url'];
|
||||
return z_root() . DEFAULT_NOTIFY_ICON;
|
||||
}
|
||||
|
||||
static public function get_site_icon() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['site_icon_url'])
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && isset(\App::$config['system']['site_icon_url']))
|
||||
return \App::$config['system']['site_icon_url'];
|
||||
return z_root() . DEFAULT_PLATFORM_ICON ;
|
||||
}
|
||||
|
||||
|
||||
static public function get_project_link() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['project_link'])
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && isset(\App::$config['system']['project_link']))
|
||||
return \App::$config['system']['project_link'];
|
||||
return 'https://hubzilla.org';
|
||||
}
|
||||
|
||||
static public function get_project_srclink() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['project_srclink'])
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && isset(\App::$config['system']['project_srclink']))
|
||||
return \App::$config['system']['project_srclink'];
|
||||
return 'https://framagit.org/hubzilla/core.git';
|
||||
}
|
||||
@@ -68,7 +68,7 @@ class System {
|
||||
|
||||
|
||||
static public function get_zot_revision() {
|
||||
$x = [ 'revision' => ZOT_REVISION ];
|
||||
$x = [ 'revision' => ZOT_REVISION ];
|
||||
call_hooks('zot_revision',$x);
|
||||
return $x['revision'];
|
||||
}
|
||||
|
||||
24
Zotlabs/Lib/Text.php
Normal file
24
Zotlabs/Lib/Text.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
class Text {
|
||||
|
||||
/**
|
||||
* use this on "body" or "content" input where angle chars shouldn't be removed,
|
||||
* and allow them to be safely displayed.
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
public static function escape_tags(string $string): string {
|
||||
if (!$string) {
|
||||
return EMPTY_STR;
|
||||
}
|
||||
|
||||
return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use Zotlabs\Access\AccessList;
|
||||
|
||||
require_once('include/text.php');
|
||||
|
||||
@@ -17,7 +19,7 @@ class ThreadItem {
|
||||
private $comment_box_template = 'comment_item.tpl';
|
||||
private $commentable = false;
|
||||
// list of supported reaction emojis - a site can over-ride this via config system.reactions
|
||||
private $reactions = ['1f60a','1f44f','1f37e','1f48b','1f61e','2665','1f606','1f62e','1f634','1f61c','1f607','1f608'];
|
||||
private $reactions = ['slightly_smiling_face','clapping_hands','bottle_with_popping_cork','kiss_mark','disappointed_face','red_heart','grinning_face','astonished_face','sleeping_face','winking_face_with_tongue','smiling_face_with_halo','smiling_face_with_horns'];
|
||||
private $toplevel = false;
|
||||
private $children = array();
|
||||
private $parent = null;
|
||||
@@ -32,6 +34,7 @@ class ThreadItem {
|
||||
private $channel = null;
|
||||
private $display_mode = 'normal';
|
||||
private $reload = '';
|
||||
private $mid_uuid_map = [];
|
||||
|
||||
|
||||
public function __construct($data) {
|
||||
@@ -44,6 +47,7 @@ class ThreadItem {
|
||||
|
||||
// Prepare the children
|
||||
if(isset($data['children'])) {
|
||||
|
||||
foreach($data['children'] as $item) {
|
||||
|
||||
/*
|
||||
@@ -54,12 +58,16 @@ class ThreadItem {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$child = new ThreadItem($item);
|
||||
$this->add_child($child);
|
||||
}
|
||||
|
||||
// performance: we have already added the children
|
||||
unset($this->data['children']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// allow a site to configure the order and content of the reaction emoji list
|
||||
if($this->toplevel) {
|
||||
$x = get_config('system','reactions');
|
||||
@@ -77,12 +85,10 @@ class ThreadItem {
|
||||
* _ false on failure
|
||||
*/
|
||||
|
||||
public function get_template_data($conv_responses, $thread_level=1, $conv_flags = []) {
|
||||
|
||||
$result = array();
|
||||
|
||||
$item = $this->get_data();
|
||||
public function get_template_data($conv_responses, $mid_uuid_map, $thread_level=1, $conv_flags = []) {
|
||||
|
||||
$result = [];
|
||||
$item = $this->get_data();
|
||||
$commentww = '';
|
||||
$sparkle = '';
|
||||
$buttons = '';
|
||||
@@ -93,33 +99,53 @@ class ThreadItem {
|
||||
$is_item = false;
|
||||
$osparkle = '';
|
||||
$total_children = $this->count_descendants();
|
||||
$unseen_comments = (($item['real_uid']) ? 0 : $this->count_unseen_descendants());
|
||||
$unseen_comments = ((isset($item['real_uid']) && $item['real_uid']) ? 0 : $this->count_unseen_descendants());
|
||||
|
||||
$conv = $this->get_conversation();
|
||||
$observer = $conv->get_observer();
|
||||
|
||||
$lock = (((intval($item['item_private'])) || (($item['uid'] == local_channel()) && (strlen($item['allow_cid']) || strlen($item['allow_gid'])
|
||||
|| strlen($item['deny_cid']) || strlen($item['deny_gid']))))
|
||||
? t('Private Message')
|
||||
: false);
|
||||
$locktype = $item['item_private'];
|
||||
$acl = new AccessList([]);
|
||||
$acl->set($item);
|
||||
|
||||
$shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false);
|
||||
$lock = ((intval($item['item_private']) || ($item['uid'] == local_channel() && $acl->is_private()))
|
||||
? t('Restricted message')
|
||||
: false);
|
||||
|
||||
// 1 = restricted message, 2 = direct message
|
||||
$locktype = intval($item['item_private']);
|
||||
|
||||
if ($locktype === 2) {
|
||||
$lock = t('Direct message');
|
||||
}
|
||||
|
||||
// 0 = limited based on public policy
|
||||
if ($item['uid'] == local_channel() && intval($item['item_private']) && !$acl->is_private() && strlen($item['public_policy'])) {
|
||||
$lock = t('Public Policy');
|
||||
$locktype = 0;
|
||||
}
|
||||
|
||||
$shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && (intval($item['item_private']) === 0)) ? true : false);
|
||||
|
||||
// allow an exemption for sharing stuff from your private feeds
|
||||
if($item['author']['xchan_network'] === 'rss')
|
||||
$shareable = true;
|
||||
|
||||
$privacy_warning = false;
|
||||
if(intval($item['item_private']) && ($item['owner']['xchan_network'] === 'activitypub')) {
|
||||
$recips = get_iconfig($item['parent'], 'activitypub', 'recips');
|
||||
$repeatable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && (intval($item['item_private']) === 0) && (in_array($item['author']['xchan_network'], ['zot6', 'activitypub']))) ? true : false);
|
||||
|
||||
if(! is_array($recips['to']) || ! in_array($observer['xchan_url'], $recips['to']))
|
||||
$privacy_warning = true;
|
||||
// @fixme
|
||||
// Have recently added code to properly handle polls in group reshares by redirecting all of the poll responses to the group.
|
||||
// Sharing a poll using a regular embedded share is harder because the poll will need to fork. This is due to comment permissions.
|
||||
// The original poll author may not accept responses from strangers. Forking the poll will receive responses from the sharer's
|
||||
// followers, but there's no elegant way to merge these two sets of results together. For now, we'll disable sharing polls.
|
||||
|
||||
if ($item['obj_type'] === 'Question') {
|
||||
$shareable = false;
|
||||
}
|
||||
|
||||
$privacy_warning = ($item['owner']['xchan_network'] === 'activitypub' && intval($item['item_private']) === 1);
|
||||
|
||||
if ($lock) {
|
||||
if (($item['mid'] == $item['parent_mid']) && count(get_terms_oftype($item['term'],TERM_FORUM))) {
|
||||
if (($item['mid'] == $item['parent_mid']) && isset($item['term']) && count(get_terms_oftype($item['term'], TERM_FORUM))) {
|
||||
$privacy_warning = true;
|
||||
$conv_flags['parent_privacy_warning'] = true;
|
||||
}
|
||||
@@ -133,24 +159,11 @@ class ThreadItem {
|
||||
|
||||
$mode = $conv->get_mode();
|
||||
|
||||
switch($item['item_type']) {
|
||||
case ITEM_TYPE_CARD:
|
||||
$edlink = 'card_edit';
|
||||
break;
|
||||
case ITEM_TYPE_ARTICLE:
|
||||
$edlink = 'article_edit';
|
||||
break;
|
||||
default:
|
||||
$edlink = 'editpost';
|
||||
break;
|
||||
}
|
||||
|
||||
if(local_channel() && $observer['xchan_hash'] === $item['author_xchan'])
|
||||
$edpost = array(z_root() . '/' . $edlink . '/' . $item['id'], t('Edit'));
|
||||
$edpost = array(z_root() . '/editpost/' . $item['id'], t('Edit'));
|
||||
else
|
||||
$edpost = false;
|
||||
|
||||
|
||||
if($observer && $observer['xchan_hash']
|
||||
&& ($observer['xchan_hash'] == $this->get_data_value('author_xchan')
|
||||
|| $observer['xchan_hash'] == $this->get_data_value('owner_xchan')
|
||||
@@ -164,7 +177,7 @@ class ThreadItem {
|
||||
$dropping = false;
|
||||
}
|
||||
|
||||
|
||||
$drop = [];
|
||||
if($dropping) {
|
||||
$drop = array(
|
||||
'dropping' => $dropping,
|
||||
@@ -175,29 +188,26 @@ class ThreadItem {
|
||||
$drop = [ 'dropping' => true, 'delete' => t('Admin Delete') ];
|
||||
}
|
||||
|
||||
// FIXME
|
||||
if($observer_is_pageowner) {
|
||||
$multidrop = array(
|
||||
'select' => t('Select'),
|
||||
);
|
||||
}
|
||||
|
||||
$filer = ((($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) ? t("Save to Folder") : false);
|
||||
|
||||
$profile_avatar = $item['author']['xchan_photo_m'];
|
||||
$profile_avatar = $item['author']['xchan_photo_s'];
|
||||
$profile_link = chanlink_hash($item['author_xchan']);
|
||||
$profile_name = $item['author']['xchan_name'];
|
||||
|
||||
$location = format_location($item);
|
||||
$isevent = false;
|
||||
$attend = null;
|
||||
$canvote = false;
|
||||
|
||||
// process action responses - e.g. like/dislike/attend/agree/whatever
|
||||
$response_verbs = array('like');
|
||||
if(feature_enabled($conv->get_profile_owner(),'dislike'))
|
||||
$response_verbs[] = 'like';
|
||||
|
||||
if(feature_enabled($conv->get_profile_owner(),'dislike')) {
|
||||
$response_verbs[] = 'dislike';
|
||||
if($item['obj_type'] === ACTIVITY_OBJ_EVENT) {
|
||||
}
|
||||
|
||||
$response_verbs[] = 'announce';
|
||||
|
||||
if(in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) {
|
||||
$response_verbs[] = 'attendyes';
|
||||
$response_verbs[] = 'attendno';
|
||||
$response_verbs[] = 'attendmaybe';
|
||||
@@ -211,17 +221,6 @@ class ThreadItem {
|
||||
$response_verbs[] = 'answer';
|
||||
}
|
||||
|
||||
$consensus = (intval($item['item_consensus']) ? true : false);
|
||||
if($consensus) {
|
||||
$response_verbs[] = 'agree';
|
||||
$response_verbs[] = 'disagree';
|
||||
$response_verbs[] = 'abstain';
|
||||
if($this->is_commentable() && $observer) {
|
||||
$conlabels = array( t('I agree'), t('I disagree'), t('I abstain'));
|
||||
$canvote = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(! feature_enabled($conv->get_profile_owner(),'dislike'))
|
||||
unset($conv_responses['dislike']);
|
||||
|
||||
@@ -229,9 +228,12 @@ class ThreadItem {
|
||||
|
||||
$my_responses = [];
|
||||
foreach($response_verbs as $v) {
|
||||
$my_responses[$v] = (($conv_responses[$v][$item['mid'] . '-m']) ? 1 : 0);
|
||||
|
||||
$my_responses[$v] = ((isset($conv_responses[$v][$item['mid'] . '-m'])) ? 1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
$like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : '');
|
||||
$like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : '');
|
||||
if (($like_list) && (count($like_list) > MAX_LIKERS)) {
|
||||
@@ -242,6 +244,17 @@ class ThreadItem {
|
||||
}
|
||||
$like_button_label = tt('Like','Likes',$like_count,'noun');
|
||||
|
||||
$repeat_count = ((x($conv_responses['announce'],$item['mid'])) ? $conv_responses['announce'][$item['mid']] : '');
|
||||
$repeat_list = ((x($conv_responses['announce'],$item['mid'])) ? $conv_responses['announce'][$item['mid'] . '-l'] : '');
|
||||
if (($repeat_list) && (count($repeat_list) > MAX_LIKERS)) {
|
||||
$repeat_list_part = array_slice($repeat_list, 0, MAX_LIKERS);
|
||||
array_push($repeat_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#repeatModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
|
||||
} else {
|
||||
$repeat_list_part = '';
|
||||
}
|
||||
$repeat_button_label = tt('Repeat','Repeats',$repeat_count,'noun');
|
||||
|
||||
$showdislike = '';
|
||||
if (feature_enabled($conv->get_profile_owner(),'dislike')) {
|
||||
$dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : '');
|
||||
$dislike_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : '');
|
||||
@@ -252,11 +265,12 @@ class ThreadItem {
|
||||
} else {
|
||||
$dislike_list_part = '';
|
||||
}
|
||||
|
||||
$showdislike = ((x($conv_responses['dislike'],$item['mid'])) ? format_like($conv_responses['dislike'][$item['mid']],$conv_responses['dislike'][$item['mid'] . '-l'],'dislike',$item['mid']) : '');
|
||||
}
|
||||
|
||||
$showlike = ((x($conv_responses['like'],$item['mid'])) ? format_like($conv_responses['like'][$item['mid']],$conv_responses['like'][$item['mid'] . '-l'],'like',$item['mid']) : '');
|
||||
$showdislike = ((x($conv_responses['dislike'],$item['mid']) && feature_enabled($conv->get_profile_owner(),'dislike'))
|
||||
? format_like($conv_responses['dislike'][$item['mid']],$conv_responses['dislike'][$item['mid'] . '-l'],'dislike',$item['mid']) : '');
|
||||
*/
|
||||
|
||||
/*
|
||||
* We should avoid doing this all the time, but it depends on the conversation mode
|
||||
@@ -267,14 +281,11 @@ class ThreadItem {
|
||||
$this->check_wall_to_wall();
|
||||
|
||||
if($this->is_toplevel()) {
|
||||
// FIXME check this permission
|
||||
if(($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) {
|
||||
|
||||
$star = array(
|
||||
if((local_channel() && $conv->get_profile_owner() === local_channel()) || (local_channel() && App::$module === 'pubstream')) {
|
||||
$star = [
|
||||
'toggle' => t("Toggle Star Status"),
|
||||
'isstarred' => ((intval($item['item_starred'])) ? true : false),
|
||||
);
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -291,7 +302,7 @@ class ThreadItem {
|
||||
$tagger = [];
|
||||
|
||||
// FIXME - check this permission
|
||||
if($conv->get_profile_owner() == local_channel()) {
|
||||
if(local_channel() && $conv->get_profile_owner() == local_channel()) {
|
||||
/* disable until we agree on how to implemnt this in zot6/activitypub
|
||||
$tagger = array(
|
||||
'tagit' => t("Add Tag"),
|
||||
@@ -303,7 +314,7 @@ class ThreadItem {
|
||||
}
|
||||
|
||||
$has_bookmarks = false;
|
||||
if(Apps::system_app_installed(local_channel(), 'Bookmarks') && is_array($item['term'])) {
|
||||
if(Apps::system_app_installed(local_channel(), 'Bookmarks') && isset($item['term']) && is_array($item['term'])) {
|
||||
foreach($item['term'] as $t) {
|
||||
if(($t['ttype'] == TERM_BOOKMARK))
|
||||
$has_bookmarks = true;
|
||||
@@ -311,23 +322,27 @@ class ThreadItem {
|
||||
}
|
||||
|
||||
$has_event = false;
|
||||
if(($item['obj_type'] === ACTIVITY_OBJ_EVENT) && $conv->get_profile_owner() == local_channel())
|
||||
if((in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) && $conv->get_profile_owner() == local_channel())
|
||||
$has_event = true;
|
||||
|
||||
$like = [];
|
||||
$dislike = [];
|
||||
$reply_to = [];
|
||||
|
||||
if($this->is_commentable() && $observer) {
|
||||
$like = array( t("I like this \x28toggle\x29"), t("like"));
|
||||
$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
|
||||
$reply_to = array( t("Reply on this comment"), t("reply"), t("Reply to"));
|
||||
}
|
||||
|
||||
$share = [];
|
||||
$embed = [];
|
||||
if ($shareable) {
|
||||
// This actually turns out not to be possible in some protocol stacks without opening up hundreds of new issues.
|
||||
// Will allow it only for uri resolvable sources.
|
||||
if(strpos($item['mid'],'http') === 0) {
|
||||
$share = []; //Not yet ready for primetime
|
||||
//$share = array( t('Repeat This'), t('repeat'));
|
||||
}
|
||||
$embed = array( t('Share This'), t('share'));
|
||||
$embed = [t('Share'), t('share')];
|
||||
}
|
||||
|
||||
if ($repeatable) {
|
||||
$share = [t('Repeat'), t('repeat')];
|
||||
}
|
||||
|
||||
$dreport = '';
|
||||
@@ -336,11 +351,13 @@ class ThreadItem {
|
||||
if($keep_reports === 0)
|
||||
$keep_reports = 10;
|
||||
|
||||
if((! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) {
|
||||
$dreport_link = '';
|
||||
if((intval($item['item_type']) == ITEM_TYPE_POST) && (! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) {
|
||||
$dreport = t('Delivery Report');
|
||||
$dreport_link = gen_link_id($item['mid']);
|
||||
$dreport_link = '?mid=' . $item['mid'];
|
||||
}
|
||||
|
||||
$is_new = false;
|
||||
if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0)
|
||||
$is_new = true;
|
||||
|
||||
@@ -356,8 +373,8 @@ class ThreadItem {
|
||||
if($conv->get_mode() === 'channel')
|
||||
$viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . urlencode(gen_link_id($item['mid']));
|
||||
|
||||
$comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children );
|
||||
$list_unseen_txt = (($unseen_comments) ? sprintf( t('%d unseen'),$unseen_comments) : '');
|
||||
$comment_count_txt = ['label' => sprintf(tt('%d comment', '%d comments', $total_children), $total_children), 'count' => $total_children];
|
||||
$list_unseen_txt = $unseen_comments ? ['label' => sprintf(t('%d unseen'), $unseen_comments), 'count' => $unseen_comments] : [];
|
||||
|
||||
$children = $this->get_children();
|
||||
|
||||
@@ -367,8 +384,8 @@ class ThreadItem {
|
||||
call_hooks('dropdown_extras',$dropdown_extras_arr);
|
||||
$dropdown_extras = $dropdown_extras_arr['dropdown_extras'];
|
||||
|
||||
$midb64 = gen_link_id($item['mid']);
|
||||
$mids = [ $midb64 ];
|
||||
$midb64 = $item['uuid'];
|
||||
$mids = [ $item['uuid'] ];
|
||||
$response_mids = [];
|
||||
foreach($response_verbs as $v) {
|
||||
if(isset($conv_responses[$v]['mids'][$item['mid']])) {
|
||||
@@ -384,6 +401,12 @@ class ThreadItem {
|
||||
$pinned_items = ($allowed_type ? get_pconfig($item['uid'], 'pinned', $item['item_type'], []) : []);
|
||||
$pinned = ((!empty($pinned_items) && in_array($midb64, $pinned_items)) ? true : false);
|
||||
|
||||
$contact = [];
|
||||
|
||||
if(App::$contacts && array_key_exists($item['author_xchan'], App::$contacts)) {
|
||||
$contact = App::$contacts[$item['author_xchan']];
|
||||
}
|
||||
|
||||
$tmp_item = array(
|
||||
'template' => $this->get_template(),
|
||||
'mode' => $mode,
|
||||
@@ -401,11 +424,9 @@ class ThreadItem {
|
||||
'mids' => $json_mids,
|
||||
'parent' => $item['parent'],
|
||||
'author_id' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']),
|
||||
'author_is_group_actor' => (($item['author']['xchan_pubforum']) ? t('Forum') : ''),
|
||||
'isevent' => $isevent,
|
||||
'attend' => $attend,
|
||||
'consensus' => $consensus,
|
||||
'conlabels' => $conlabels,
|
||||
'canvote' => $canvote,
|
||||
'linktitle' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']),
|
||||
'olinktitle' => (($item['owner']['xchan_addr']) ? $item['owner']['xchan_addr'] : $item['owner']['xchan_url']),
|
||||
'llink' => $item['llink'],
|
||||
@@ -474,36 +495,50 @@ class ThreadItem {
|
||||
'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''),
|
||||
'addtocal' => (($has_event) ? t('Add to Calendar') : ''),
|
||||
'drop' => $drop,
|
||||
'multidrop' => ((feature_enabled($conv->get_profile_owner(),'multi_delete')) ? $multidrop : ''),
|
||||
'dropdown_extras' => $dropdown_extras,
|
||||
// end toolbar buttons
|
||||
'unseen_comments' => $unseen_comments,
|
||||
'comment_count' => $total_children,
|
||||
'comment_count_txt' => $comment_count_txt,
|
||||
'list_unseen_txt' => $list_unseen_txt,
|
||||
'markseen' => t('Mark all seen'),
|
||||
'markseen' => t('Mark all comments seen'),
|
||||
'responses' => $responses,
|
||||
'my_responses' => $my_responses,
|
||||
/*
|
||||
'like_count' => $like_count,
|
||||
'like_list' => $like_list,
|
||||
'like_list_part' => $like_list_part,
|
||||
'like_button_label' => $like_button_label,
|
||||
'like_modal_title' => t('Likes','noun'),
|
||||
|
||||
'repeat_count' => $repeat_count,
|
||||
'repeat_list' => $repeat_list,
|
||||
'repeat_list_part' => $repeat_list_part,
|
||||
'repeat_button_label' => $repeat_button_label,
|
||||
'repeat_modal_title' => t('Repeats','noun'),
|
||||
|
||||
|
||||
'dislike_modal_title' => t('Dislikes','noun'),
|
||||
'dislike_count' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_count : ''),
|
||||
'dislike_list' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list : ''),
|
||||
'dislike_list_part' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list_part : ''),
|
||||
'dislike_button_label' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_button_label : ''),
|
||||
*/
|
||||
'modal_dismiss' => t('Close'),
|
||||
'showlike' => $showlike,
|
||||
'showdislike' => $showdislike,
|
||||
'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box($indent)),
|
||||
// 'showlike' => $showlike,
|
||||
// 'showdislike' => $showdislike,
|
||||
'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box()),
|
||||
'previewing' => ($conv->is_preview() ? true : false ),
|
||||
'preview_lbl' => t('This is an unsaved preview'),
|
||||
'wait' => t('Please wait'),
|
||||
'thread_level' => $thread_level,
|
||||
'settings' => $settings,
|
||||
'thr_parent' => (($item['parent_mid'] != $item['thr_parent']) ? gen_link_id($item['thr_parent']) : '')
|
||||
'thr_parent_uuid' => (($item['parent_mid'] != $item['thr_parent']) ? $mid_uuid_map[$item['thr_parent']] : ''),
|
||||
'contact_id' => (($contact) ? $contact['abook_id'] : ''),
|
||||
'moderate' => ($item['item_blocked'] == ITEM_MODERATED),
|
||||
'moderate_approve' => t('Approve'),
|
||||
'moderate_delete' => t('Delete'),
|
||||
'rtl' => in_array($item['lang'], rtl_languages()),
|
||||
);
|
||||
|
||||
$arr = array('item' => $item, 'output' => $tmp_item);
|
||||
@@ -526,12 +561,12 @@ class ThreadItem {
|
||||
|
||||
if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
|
||||
foreach($children as $child) {
|
||||
$result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1,$conv_flags);
|
||||
$result['children'][] = $child->get_template_data($conv_responses, $mid_uuid_map, $thread_level + 1,$conv_flags);
|
||||
}
|
||||
// Collapse
|
||||
if(($nb_children > $visible_comments) || ($thread_level > 1)) {
|
||||
$result['children'][0]['comment_firstcollapsed'] = true;
|
||||
$result['children'][0]['num_comments'] = $comment_count_txt;
|
||||
$result['children'][0]['num_comments'] = $comment_count_txt['label'];
|
||||
$result['children'][0]['hide_text'] = sprintf( t('%s show all'), '<i class="fa fa-chevron-down"></i>');
|
||||
if($thread_level > 1) {
|
||||
$result['children'][$nb_children - 1]['comment_lastcollapsed'] = true;
|
||||
@@ -608,7 +643,7 @@ class ThreadItem {
|
||||
* Only add what will be displayed
|
||||
*/
|
||||
|
||||
if(activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE)) {
|
||||
if(activity_match($item->get_data_value('verb'), ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -789,7 +824,7 @@ class ThreadItem {
|
||||
* _ The comment box string (empty if no comment box)
|
||||
* _ false on failure
|
||||
*/
|
||||
private function get_comment_box($indent) {
|
||||
private function get_comment_box() {
|
||||
|
||||
if(!$this->is_toplevel() && !get_config('system','thread_allow')) {
|
||||
return '';
|
||||
@@ -835,7 +870,6 @@ class ThreadItem {
|
||||
'$edurl' => t('Insert Link'),
|
||||
'$edvideo' => t('Video'),
|
||||
'$preview' => t('Preview'), // ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''),
|
||||
'$indent' => $indent,
|
||||
'$can_upload' => (perm_is_allowed($conv->get_profile_owner(),get_observer_hash(),'write_storage') && $conv->is_uploadable()),
|
||||
'$feature_encrypt' => ((feature_enabled($conv->get_profile_owner(),'content_encrypt')) ? true : false),
|
||||
'$encrypt' => t('Encrypt text'),
|
||||
@@ -870,10 +904,16 @@ class ThreadItem {
|
||||
|
||||
if($this->is_toplevel() && ($this->get_data_value('author_xchan') != $this->get_data_value('owner_xchan'))) {
|
||||
$this->owner_url = chanlink_hash($this->data['owner']['xchan_hash']);
|
||||
$this->owner_photo = $this->data['owner']['xchan_photo_m'];
|
||||
$this->owner_photo = $this->data['owner']['xchan_photo_s'];
|
||||
$this->owner_name = $this->data['owner']['xchan_name'];
|
||||
$this->wall_to_wall = true;
|
||||
}
|
||||
elseif($this->is_toplevel() && $this->get_data_value('verb') === 'Announce' && isset($this->data['source'])) {
|
||||
$this->owner_url = chanlink_hash($this->data['source']['xchan_hash']);
|
||||
$this->owner_photo = $this->data['source']['xchan_photo_s'];
|
||||
$this->owner_name = $this->data['source']['xchan_name'];
|
||||
$this->wall_to_wall = true;
|
||||
}
|
||||
}
|
||||
|
||||
private function is_wall_to_wall() {
|
||||
|
||||
@@ -4,18 +4,30 @@ namespace Zotlabs\Lib;
|
||||
|
||||
class ThreadListener {
|
||||
|
||||
public static function isEnabled() {
|
||||
return Config::Get('system','enable_thread_listener');
|
||||
}
|
||||
|
||||
static public function store($target_id,$portable_id,$ltype = 0) {
|
||||
if (!self::isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$x = self::fetch($target_id,$portable_id,$ltype = 0);
|
||||
if(! $x) {
|
||||
if(! $x) {
|
||||
$r = q("insert into listeners ( target_id, portable_id, ltype ) values ( '%s', '%s' , %d ) ",
|
||||
dbesc($target_id),
|
||||
dbesc($portable_id),
|
||||
intval($ltype)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public function fetch($target_id,$portable_id,$ltype = 0) {
|
||||
if (!self::isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$x = q("select * from listeners where target_id = '%s' and portable_id = '%s' and ltype = %d limit 1",
|
||||
dbesc($target_id),
|
||||
dbesc($portable_id),
|
||||
@@ -28,6 +40,10 @@ class ThreadListener {
|
||||
}
|
||||
|
||||
static public function fetch_by_target($target_id,$ltype = 0) {
|
||||
if (!self::isEnabled()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$x = q("select * from listeners where target_id = '%s' and ltype = %d",
|
||||
dbesc($target_id),
|
||||
intval($ltype)
|
||||
|
||||
@@ -69,12 +69,12 @@ class ThreadStream {
|
||||
case 'cards':
|
||||
$this->profile_owner = \App::$profile['profile_uid'];
|
||||
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
|
||||
$this->reload = $_SESSION['return_url'];
|
||||
//$this->reload = $_SESSION['return_url'];
|
||||
break;
|
||||
case 'articles':
|
||||
$this->profile_owner = \App::$profile['profile_uid'];
|
||||
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
|
||||
$this->reload = $_SESSION['return_url'];
|
||||
//$this->reload = $_SESSION['return_url'];
|
||||
break;
|
||||
case 'display':
|
||||
// in this mode we set profile_owner after initialisation (from conversation()) and then
|
||||
@@ -171,7 +171,7 @@ class ThreadStream {
|
||||
*/
|
||||
|
||||
|
||||
if(($item->get_data_value('id') != $item->get_data_value('parent')) && (activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE))) {
|
||||
if($item->get_data_value('id') != $item->get_data_value('parent') && activity_match($item->get_data_value('verb'), ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ class ThreadStream {
|
||||
* _ The data requested on success
|
||||
* _ false on failure
|
||||
*/
|
||||
public function get_template_data($conv_responses) {
|
||||
public function get_template_data($conv_responses, $mid_uuid_map) {
|
||||
$result = array();
|
||||
|
||||
foreach($this->threads as $item) {
|
||||
@@ -220,7 +220,7 @@ class ThreadStream {
|
||||
$item_data = $this->prepared_item;
|
||||
}
|
||||
else {
|
||||
$item_data = $item->get_template_data($conv_responses);
|
||||
$item_data = $item->get_template_data($conv_responses, $mid_uuid_map);
|
||||
}
|
||||
if(!$item_data) {
|
||||
logger('Failed to get item template data ('. $item->get_id() .').', LOGGER_DEBUG, LOG_ERR);
|
||||
|
||||
@@ -52,15 +52,21 @@ class Webfinger {
|
||||
|
||||
if(strpos($resource,'http') === 0) {
|
||||
$m = parse_url($resource);
|
||||
if($m) {
|
||||
if($m['scheme'] !== 'https') {
|
||||
return false;
|
||||
}
|
||||
self::$server = $m['host'] . (($m['port']) ? ':' . $m['port'] : '');
|
||||
}
|
||||
else {
|
||||
|
||||
if (!$m) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(isset($m['scheme']) && $m['scheme'] !== 'https') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isset($m['host'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self::$server = $m['host'] . ((isset($m['port'])) ? ':' . $m['port'] : '');
|
||||
|
||||
}
|
||||
elseif(strpos($resource,'tag:') === 0) {
|
||||
$arr = explode(':',$resource); // split the tag
|
||||
@@ -86,7 +92,7 @@ class Webfinger {
|
||||
/**
|
||||
* @brief fetch a webfinger resource and return a zot6 discovery url if present
|
||||
*
|
||||
*/
|
||||
*/
|
||||
|
||||
static function zot_url($resource) {
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ class XConfig {
|
||||
*/
|
||||
static public function Delete($xchan, $family, $key) {
|
||||
|
||||
if(x(\App::$config[$xchan][$family], $key))
|
||||
if(isset(\App::$config[$xchan][$family][$key]))
|
||||
unset(\App::$config[$xchan][$family][$key]);
|
||||
|
||||
$ret = q("DELETE FROM xconfig WHERE xchan = '%s' AND cat = '%s' AND k = '%s'",
|
||||
|
||||
@@ -87,4 +87,4 @@ class ZotURL {
|
||||
return ids_to_array($r,'hubloc_url');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use Zotlabs\Web\HTTPSig;
|
||||
|
||||
class Zotfinger {
|
||||
|
||||
static function exec($resource,$channel = null, $verify = true) {
|
||||
static function exec($resource, $channel = null, $verify = true, $recurse = true) {
|
||||
|
||||
if(! $resource) {
|
||||
return false;
|
||||
@@ -35,9 +35,37 @@ class Zotfinger {
|
||||
$result = [];
|
||||
|
||||
$redirects = 0;
|
||||
$x = z_post_url($resource,$data,$redirects, [ 'headers' => $h ] );
|
||||
|
||||
logger('fetch: ' . print_r($x,true));
|
||||
$start_timestamp = microtime(true);
|
||||
$x = z_post_url($resource,$data,$redirects, [ 'headers' => $h ] );
|
||||
logger('logger_stats_data cmd:Zotfinger' . ' start:' . $start_timestamp . ' ' . 'end:' . microtime(true) . ' meta:' . $resource . '#' . random_string(16));
|
||||
btlogger('Zotfinger');
|
||||
|
||||
logger('fetch: ' . print_r($x,true), LOGGER_DATA);
|
||||
|
||||
if (in_array(intval($x['return_code']), [ 404, 410 ]) && $recurse) {
|
||||
|
||||
// The resource has been deleted or doesn't exist at this location.
|
||||
// Try to find another nomadic resource for this channel and return that.
|
||||
|
||||
// First, see if there's a hubloc for this site. Fetch that record to
|
||||
// obtain the nomadic identity hash. Then use that to find any additional
|
||||
// nomadic locations.
|
||||
|
||||
$h = Activity::get_actor_hublocs($resource, 'zot6');
|
||||
if ($h) {
|
||||
// mark this location deleted
|
||||
hubloc_delete($h[0]);
|
||||
$hubs = Activity::get_actor_hublocs($h[0]['hubloc_hash']);
|
||||
if ($hubs) {
|
||||
foreach ($hubs as $hub) {
|
||||
if ($hub['hubloc_id_url'] !== $resource && !$hub['hubloc_deleted']) {
|
||||
return self::exec($hub['hubloc_id_url'], $channel, $verify);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($x['success']) {
|
||||
if ($verify) {
|
||||
@@ -50,7 +78,7 @@ class Zotfinger {
|
||||
$result['data'] = json_decode(Crypto::unencapsulate($result['data'],get_config('system','prvkey')),true);
|
||||
}
|
||||
|
||||
logger('decrypted: ' . print_r($result,true));
|
||||
logger('decrypted: ' . print_r($result,true), LOGGER_DATA);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use Zotlabs\Lib\Libzotdir;
|
||||
use Zotlabs\Lib\AccessList;
|
||||
|
||||
require_once 'include/acl_selectors.php';
|
||||
require_once 'include/group.php';
|
||||
|
||||
/**
|
||||
* @brief ACL selector json backend.
|
||||
@@ -66,6 +66,11 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
killme();
|
||||
|
||||
$permitted = [];
|
||||
$sql_extra = '';
|
||||
$sql_extra2 = '';
|
||||
$sql_extra3 = '';
|
||||
$sql_extra2_xchan = '';
|
||||
$order_extra2 = '';
|
||||
|
||||
if(in_array($type, [ 'm', 'a', 'c', 'f' ])) {
|
||||
|
||||
@@ -81,7 +86,6 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
|
||||
}
|
||||
|
||||
|
||||
if($search) {
|
||||
$sql_extra = " AND pgrp.gname LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ";
|
||||
$sql_extra2 = "AND ( xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'%" . dbesc(punify($search)) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") ";
|
||||
@@ -100,10 +104,6 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
$sql_extra3 = "AND ( xchan_addr like " . protect_sprintf( "'%" . dbesc(punify($search)) . "%'" ) . " OR xchan_name like " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ) ";
|
||||
|
||||
}
|
||||
else {
|
||||
$sql_extra = $sql_extra2 = $sql_extra3 = "";
|
||||
}
|
||||
|
||||
|
||||
$groups = array();
|
||||
$contacts = array();
|
||||
@@ -123,7 +123,7 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
"name" => t('Profile','acl') . ' ' . $rv['profile_name'],
|
||||
"id" => 'vp' . $rv['id'],
|
||||
"xid" => 'vp.' . $rv['profile_guid'],
|
||||
"uids" => group_get_profile_members_xchan(local_channel(), $rv['id']),
|
||||
"uids" => AccessList::profile_members_xchan(local_channel(), $rv['id']),
|
||||
"link" => ''
|
||||
);
|
||||
}
|
||||
@@ -146,14 +146,14 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
|
||||
if($r) {
|
||||
foreach($r as $g){
|
||||
// logger('acl: group: ' . $g['gname'] . ' members: ' . group_get_members_xchan($g['id']));
|
||||
// logger('acl: group: ' . $g['gname'] . ' members: ' . AccessList::members_xchan(local_channel(), $g['id']));
|
||||
$groups[] = array(
|
||||
"type" => "g",
|
||||
"photo" => "images/twopeople.png",
|
||||
"name" => $g['gname'],
|
||||
"id" => $g['id'],
|
||||
"xid" => $g['hash'],
|
||||
"uids" => group_get_members_xchan($g['id']),
|
||||
"uids" => AccessList::members_xchan(local_channel(), $g['id']),
|
||||
"link" => ''
|
||||
);
|
||||
}
|
||||
@@ -284,7 +284,7 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
elseif($type == 'm') {
|
||||
$r = array();
|
||||
$z = q("SELECT xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url
|
||||
$z = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url, abook_self
|
||||
FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
WHERE abook_channel = %d
|
||||
and xchan_deleted = 0
|
||||
@@ -304,7 +304,7 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
elseif($type == 'a') {
|
||||
|
||||
$r = q("SELECT abook_id as id, xchan_name as name, xchan_network as net, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url, xchan_addr as attag , abook_their_perms FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
$r = q("SELECT abook_id as id, xchan_name as name, xchan_network as net, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url, xchan_addr as attag, abook_their_perms, abook_self FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
WHERE abook_channel = %d
|
||||
and xchan_deleted = 0
|
||||
$sql_extra3
|
||||
@@ -342,7 +342,7 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
$x = [];
|
||||
foreach($r as $g) {
|
||||
|
||||
if(in_array($g['net'],['rss','anon','unknown']) && ($type != 'a'))
|
||||
if(isset($g['net']) && in_array($g['net'], ['rss','anon','unknown']) && ($type != 'a'))
|
||||
continue;
|
||||
|
||||
$g['hash'] = urlencode($g['hash']);
|
||||
@@ -378,12 +378,13 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
"name" => $g['name'],
|
||||
"id" => urlencode($g['id']),
|
||||
"xid" => $g['hash'],
|
||||
"url" => $g['url'],
|
||||
"link" => $clink,
|
||||
"nick" => ((strpos($g['nick'],'@')) ? substr($g['nick'],0,strpos($g['nick'],'@')) : $g['nick']),
|
||||
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
|
||||
"taggable" => '',
|
||||
"label" => '',
|
||||
"net" => $g['net']
|
||||
"net" => $g['net'] ?? ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Daemon\Master;
|
||||
use Zotlabs\Lib\Activity as ZlibActivity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\LDSignatures;
|
||||
use Zotlabs\Web\HTTPSig;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\ThreadListener;
|
||||
@@ -26,7 +25,7 @@ class Activity extends Controller {
|
||||
|
||||
$portable_id = EMPTY_STR;
|
||||
|
||||
$item_normal_extra = sprintf(" and not verb in ('%s', '%s') ",
|
||||
$item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ",
|
||||
dbesc(ACTIVITY_FOLLOW),
|
||||
dbesc(ACTIVITY_UNFOLLOW)
|
||||
);
|
||||
@@ -155,22 +154,7 @@ class Activity extends Controller {
|
||||
if(! $i)
|
||||
http_status_exit(404, 'Not found');
|
||||
|
||||
$x = array_merge(['@context' => [
|
||||
ACTIVITYSTREAMS_JSONLD_REV,
|
||||
'https://w3id.org/security/v1',
|
||||
z_root() . ZOT_APSCHEMA_REV
|
||||
]], $i);
|
||||
|
||||
$headers = [];
|
||||
$headers['Content-Type'] = 'application/x-zot+json' ;
|
||||
$x['signature'] = LDSignatures::sign($x,$chan);
|
||||
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
|
||||
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
|
||||
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
|
||||
$h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],channel_url($chan));
|
||||
HTTPSig::set_headers($h);
|
||||
echo $ret;
|
||||
killme();
|
||||
as_return_and_die($i, $chan);
|
||||
|
||||
}
|
||||
|
||||
@@ -182,7 +166,7 @@ class Activity extends Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
$ob_authorise = false;
|
||||
$ob_authorize = false;
|
||||
$item_uid = 0;
|
||||
|
||||
$bear = ZlibActivity::token_from_request();
|
||||
@@ -202,7 +186,7 @@ class Activity extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
$item_normal_extra = sprintf(" and not verb in ('%s', '%s') ",
|
||||
$item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ",
|
||||
dbesc(ACTIVITY_FOLLOW),
|
||||
dbesc(ACTIVITY_UNFOLLOW)
|
||||
);
|
||||
@@ -260,25 +244,7 @@ class Activity extends Controller {
|
||||
|
||||
$channel = channelx_by_n($items[0]['uid']);
|
||||
|
||||
$x = array_merge( ['@context' => [
|
||||
ACTIVITYSTREAMS_JSONLD_REV,
|
||||
'https://w3id.org/security/v1',
|
||||
z_root() . ZOT_APSCHEMA_REV
|
||||
]], ZlibActivity::encode_activity($items[0],true));
|
||||
|
||||
$headers = [];
|
||||
$headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ;
|
||||
$x['signature'] = LDSignatures::sign($x,$channel);
|
||||
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
|
||||
$headers['Date'] = datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T');
|
||||
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
|
||||
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
|
||||
|
||||
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel));
|
||||
HTTPSig::set_headers($h);
|
||||
echo $ret;
|
||||
killme();
|
||||
|
||||
as_return_and_die(ZlibActivity::encode_activity($items[0]), $channel);
|
||||
}
|
||||
|
||||
goaway(z_root() . '/item/' . argv(1));
|
||||
|
||||
@@ -346,7 +346,7 @@ class Accounts {
|
||||
'$users' => $users,
|
||||
'$msg' => t('Message')
|
||||
));
|
||||
$o .= paginate($a);
|
||||
$o .= paginate();
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ class Addons {
|
||||
} catch (\PHPGit\Exception\GitException $e) {
|
||||
json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false));
|
||||
}
|
||||
break;
|
||||
case 'removerepo':
|
||||
if (array_key_exists('repoName', $_REQUEST)) {
|
||||
$repoName = $_REQUEST['repoName'];
|
||||
@@ -111,6 +112,7 @@ class Addons {
|
||||
} else {
|
||||
json_return_and_die(array('message' => 'Error deleting addon repo.', 'success' => false));
|
||||
}
|
||||
break;
|
||||
case 'installrepo':
|
||||
if (array_key_exists('repoURL', $_REQUEST)) {
|
||||
require_once('library/PHPGit.autoload.php'); // Load PHPGit dependencies
|
||||
@@ -172,6 +174,7 @@ class Addons {
|
||||
$repo = $git->probeRepo();
|
||||
json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true));
|
||||
}
|
||||
break;
|
||||
case 'addrepo':
|
||||
if (array_key_exists('repoURL', $_REQUEST)) {
|
||||
require_once('library/PHPGit.autoload.php'); // Load PHPGit dependencies
|
||||
|
||||
@@ -168,7 +168,7 @@ class Channels {
|
||||
'$baseurl' => z_root(),
|
||||
'$channels' => $channels,
|
||||
));
|
||||
$o .= paginate($a);
|
||||
$o .= paginate();
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
class Dbsync {
|
||||
|
||||
|
||||
|
||||
function get() {
|
||||
$o = '';
|
||||
|
||||
|
||||
if(argc() > 3 && intval(argv(3)) && argv(2) === 'mark') {
|
||||
// remove the old style config if it exists
|
||||
del_config('database', 'update_r' . intval(argv(3)));
|
||||
@@ -29,7 +29,7 @@ class Dbsync {
|
||||
if(method_exists($c,'verify')) {
|
||||
$retval = $c->verify();
|
||||
if($retval === UPDATE_FAILED) {
|
||||
$o .= sprintf( t('Verification of update %s failed. Check system logs.'), $s);
|
||||
$o .= sprintf( t('Verification of update %s failed. Check system logs.'), $s);
|
||||
}
|
||||
elseif($retval === UPDATE_SUCCESS) {
|
||||
$o .= sprintf( t('Update %s was successfully applied.'), $s);
|
||||
@@ -44,20 +44,8 @@ class Dbsync {
|
||||
}
|
||||
else
|
||||
$o .= sprintf( t('Update function %s could not be found.'), $s);
|
||||
|
||||
|
||||
return $o;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// remove the old style config if it exists
|
||||
del_config('database', 'update_r' . intval(argv(3)));
|
||||
set_config('database', '_' . intval(argv(3)), 'success');
|
||||
if(intval(get_config('system','db_version')) < intval(argv(3)))
|
||||
set_config('system','db_version',intval(argv(3)));
|
||||
info( t('Update has been marked successful') . EOL);
|
||||
goaway(z_root() . '/admin/dbsync');
|
||||
}
|
||||
|
||||
if(argc() > 2 && intval(argv(2))) {
|
||||
@@ -68,7 +56,7 @@ class Dbsync {
|
||||
$c = new $cls();
|
||||
$retval = $c->run();
|
||||
if($retval === UPDATE_FAILED) {
|
||||
$o .= sprintf( t('Executing update procedure %s failed. Check system logs.'), $s);
|
||||
$o .= sprintf( t('Executing update procedure %s failed. Check system logs.'), $s);
|
||||
}
|
||||
elseif($retval === UPDATE_SUCCESS) {
|
||||
$o .= sprintf( t('Update %s was successfully applied.'), $s);
|
||||
@@ -79,10 +67,10 @@ class Dbsync {
|
||||
}
|
||||
else
|
||||
$o .= sprintf( t('Update function %s could not be found.'), $s);
|
||||
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
$failed = array();
|
||||
$r = q("select * from config where cat = 'database' ");
|
||||
if(count($r)) {
|
||||
@@ -107,7 +95,7 @@ class Dbsync {
|
||||
else {
|
||||
return '<div class="generic-content-wrapper-styled"><h3>' . t('No failed updates.') . '</h3></div>';
|
||||
}
|
||||
|
||||
|
||||
return $o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,23 +12,33 @@ class Queue {
|
||||
|
||||
$o = '';
|
||||
|
||||
$expert = ((array_key_exists('expert',$_REQUEST)) ? intval($_REQUEST['expert']) : 0);
|
||||
$expert = $_REQUEST['expert'] ?? false;
|
||||
|
||||
if($_REQUEST['drophub']) {
|
||||
if(isset($_REQUEST['drophub'])) {
|
||||
hubloc_mark_as_down($_REQUEST['drophub']);
|
||||
LibQueue::remove_by_posturl($_REQUEST['drophub']);
|
||||
}
|
||||
|
||||
if($_REQUEST['emptyhub']) {
|
||||
if(isset($_REQUEST['emptyhub'])) {
|
||||
LibQueue::remove_by_posturl($_REQUEST['emptyhub']);
|
||||
}
|
||||
|
||||
$r = q("select count(outq_posturl) as total, max(outq_priority) as priority, outq_posturl from outq
|
||||
if(isset($_REQUEST['deliverhub'])) {
|
||||
|
||||
$hubq = q("SELECT * FROM outq WHERE outq_posturl = '%s'",
|
||||
dbesc($_REQUEST['deliverhub'])
|
||||
);
|
||||
|
||||
foreach ($hubq as $q) {
|
||||
LibQueue::deliver($q, true);
|
||||
}
|
||||
}
|
||||
|
||||
$r = dbq("select count(outq_posturl) as total, max(outq_priority) as priority, outq_posturl from outq
|
||||
where outq_delivered = 0 group by outq_posturl order by total desc");
|
||||
|
||||
for($x = 0; $x < count($r); $x ++) {
|
||||
$r[$x]['eurl'] = urlencode($r[$x]['outq_posturl']);
|
||||
$r[$x]['connected'] = datetime_convert('UTC',date_default_timezone_get(),$r[$x]['connected'],'Y-m-d');
|
||||
}
|
||||
|
||||
$o = replace_macros(get_markup_template('admin_queue.tpl'), array(
|
||||
@@ -37,6 +47,7 @@ class Queue {
|
||||
'$priority' => t('Priority'),
|
||||
'$desturl' => t('Destination URL'),
|
||||
'$nukehub' => t('Mark hub permanently offline'),
|
||||
'$deliverhub' => t('Retry delivery to this hub'),
|
||||
'$empty' => t('Empty queue for this hub'),
|
||||
'$lastconn' => t('Last known contact'),
|
||||
'$hasentries' => ((count($r)) ? true : false),
|
||||
|
||||
121
Zotlabs/Module/Admin/Queueworker.php
Normal file
121
Zotlabs/Module/Admin/Queueworker.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
class Queueworker extends Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
}
|
||||
|
||||
function post() {
|
||||
|
||||
check_form_security_token('form_security_token', 'queueworker');
|
||||
|
||||
$maxqueueworkers = intval($_POST['queueworker_maxworkers']);
|
||||
$maxqueueworkers = ($maxqueueworkers > 3) ? $maxqueueworkers : 4;
|
||||
set_config('queueworker', 'max_queueworkers', $maxqueueworkers);
|
||||
|
||||
$maxworkerage = intval($_POST['queueworker_max_age']);
|
||||
$maxworkerage = ($maxworkerage >= 120) ? $maxworkerage : 300;
|
||||
set_config('queueworker', 'queueworker_max_age', $maxworkerage);
|
||||
|
||||
$queueworkersleep = intval($_POST['queue_worker_sleep']);
|
||||
$queueworkersleep = ($queueworkersleep > 100) ? $queueworkersleep : 100;
|
||||
set_config('queueworker', 'queue_worker_sleep', $queueworkersleep);
|
||||
|
||||
$auto_queue_worker_sleep = intval($_POST['auto_queue_worker_sleep']);
|
||||
set_config('queueworker', 'auto_queue_worker_sleep', $auto_queue_worker_sleep);
|
||||
|
||||
goaway(z_root() . '/admin/queueworker');
|
||||
}
|
||||
|
||||
function get() {
|
||||
|
||||
$content = "<H1>Queue Status</H1>\n";
|
||||
|
||||
$r = q('select count(*) as total from workerq');
|
||||
|
||||
$content .= "<H4>There are " . $r[0]['total'] . " queue items to be processed.</H4>";
|
||||
|
||||
$r = dbq("select count(*) as qworkers from workerq where workerq_reservationid is not null");
|
||||
|
||||
$content .= "<H4>Active workers: " . $r[0]['qworkers'] . "</H4>";
|
||||
|
||||
$r = dbq("select workerq_cmd, count(*) as total from workerq where true group by workerq_cmd");
|
||||
|
||||
if ($r) {
|
||||
$content .= "<H4>Work items</H4>";
|
||||
foreach($r as $rr) {
|
||||
$content .= $rr['workerq_cmd'] . ': ' . $rr['total'] . '<br>';
|
||||
}
|
||||
}
|
||||
|
||||
$maxqueueworkers = get_config('queueworker', 'max_queueworkers', 4);
|
||||
$maxqueueworkers = ($maxqueueworkers > 3) ? $maxqueueworkers : 4;
|
||||
|
||||
$sc = '';
|
||||
|
||||
$sc .= replace_macros(get_markup_template('field_input.tpl'), [
|
||||
'$field' => [
|
||||
'queueworker_maxworkers',
|
||||
t('Max queueworker threads'),
|
||||
$maxqueueworkers,
|
||||
t('Minimum 4, default 4')
|
||||
]
|
||||
]);
|
||||
|
||||
$workermaxage = get_config('queueworker', 'queueworker_max_age');
|
||||
$workermaxage = ($workermaxage >= 120) ? $workermaxage : 300;
|
||||
|
||||
$sc .= replace_macros(get_markup_template('field_input.tpl'), [
|
||||
'$field' => [
|
||||
'queueworker_max_age',
|
||||
t('Assume workers dead after'),
|
||||
$workermaxage,
|
||||
t('Minimum 120, default 300 seconds')
|
||||
]
|
||||
]);
|
||||
|
||||
$queueworkersleep = get_config('queueworker', 'queue_worker_sleep');
|
||||
$queueworkersleep = ($queueworkersleep > 100) ? $queueworkersleep : 100;
|
||||
|
||||
$auto_queue_worker_sleep = get_config('queueworker', 'auto_queue_worker_sleep', 0);
|
||||
|
||||
$sc .= replace_macros(get_markup_template('field_input.tpl'), [
|
||||
'$field' => [
|
||||
'queue_worker_sleep',
|
||||
t('Pause before starting next task'),
|
||||
$queueworkersleep,
|
||||
t('Minimum 100, default 100 microseconds'),
|
||||
'',
|
||||
(($auto_queue_worker_sleep) ? 'disabled' : '')
|
||||
]
|
||||
]);
|
||||
|
||||
$sc .= replace_macros(get_markup_template('field_checkbox.tpl'), [
|
||||
'$field' => [
|
||||
'auto_queue_worker_sleep',
|
||||
t('Automatically adjust pause before starting next task'),
|
||||
$auto_queue_worker_sleep,
|
||||
]
|
||||
]);
|
||||
|
||||
$tpl = get_markup_template('settings_addon.tpl');
|
||||
$content .= replace_macros($tpl, [
|
||||
'$action_url' => 'admin/queueworker',
|
||||
'$form_security_token' => get_form_security_token('queueworker'),
|
||||
'$title' => t('Queueworker Settings'),
|
||||
'$content' => $sc,
|
||||
'$baseurl' => z_root(),
|
||||
'$submit' => t('Save')
|
||||
]
|
||||
);
|
||||
|
||||
return $content;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,12 @@ class Security {
|
||||
|
||||
function post() {
|
||||
check_form_security_token_redirectOnErr('/admin/security', 'admin_security');
|
||||
|
||||
|
||||
$allowed_email = ((x($_POST,'allowed_email')) ? notags(trim($_POST['allowed_email'])) : '');
|
||||
$not_allowed_email = ((x($_POST,'not_allowed_email')) ? notags(trim($_POST['not_allowed_email'])) : '');
|
||||
|
||||
set_config('system','allowed_email', $allowed_email);
|
||||
set_config('system','not_allowed_email', $not_allowed_email);
|
||||
set_config('system','not_allowed_email', $not_allowed_email);
|
||||
|
||||
$block_public = ((x($_POST,'block_public')) ? True : False);
|
||||
set_config('system','block_public',$block_public);
|
||||
@@ -25,22 +25,22 @@ class Security {
|
||||
|
||||
$ws = $this->trim_array_elems(explode("\n",$_POST['whitelisted_sites']));
|
||||
set_config('system','whitelisted_sites',$ws);
|
||||
|
||||
|
||||
$bs = $this->trim_array_elems(explode("\n",$_POST['blacklisted_sites']));
|
||||
set_config('system','blacklisted_sites',$bs);
|
||||
|
||||
|
||||
$wc = $this->trim_array_elems(explode("\n",$_POST['whitelisted_channels']));
|
||||
set_config('system','whitelisted_channels',$wc);
|
||||
|
||||
|
||||
$bc = $this->trim_array_elems(explode("\n",$_POST['blacklisted_channels']));
|
||||
set_config('system','blacklisted_channels',$bc);
|
||||
|
||||
|
||||
$embed_sslonly = ((x($_POST,'embed_sslonly')) ? True : False);
|
||||
set_config('system','embed_sslonly',$embed_sslonly);
|
||||
|
||||
|
||||
$we = $this->trim_array_elems(explode("\n",$_POST['embed_allow']));
|
||||
set_config('system','embed_allow',$we);
|
||||
|
||||
|
||||
$be = $this->trim_array_elems(explode("\n",$_POST['embed_deny']));
|
||||
set_config('system','embed_deny',$be);
|
||||
|
||||
@@ -49,47 +49,54 @@ class Security {
|
||||
|
||||
$inline_pdf = ((x($_POST,'inline_pdf')) ? intval($_POST['inline_pdf']) : 0);
|
||||
set_config('system', 'inline_pdf' , $inline_pdf);
|
||||
|
||||
|
||||
$ts = ((x($_POST,'transport_security')) ? True : False);
|
||||
set_config('system','transport_security_header',$ts);
|
||||
|
||||
$cs = ((x($_POST,'content_security')) ? True : False);
|
||||
set_config('system','content_security_policy',$cs);
|
||||
|
||||
$trusted_directory_servers = $this->trim_array_elems(explode("\n", $_POST['trusted_directory_servers']));
|
||||
set_config('system', 'trusted_directory_servers', $trusted_directory_servers);
|
||||
|
||||
goaway(z_root() . '/admin/security');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
|
||||
$whitesites = get_config('system','whitelisted_sites');
|
||||
$whitesites_str = ((is_array($whitesites)) ? implode("\n",$whitesites) : '');
|
||||
|
||||
|
||||
$blacksites = get_config('system','blacklisted_sites');
|
||||
$blacksites_str = ((is_array($blacksites)) ? implode("\n",$blacksites) : '');
|
||||
|
||||
|
||||
|
||||
|
||||
$whitechannels = get_config('system','whitelisted_channels');
|
||||
$whitechannels_str = ((is_array($whitechannels)) ? implode("\n",$whitechannels) : '');
|
||||
|
||||
|
||||
$blackchannels = get_config('system','blacklisted_channels');
|
||||
$blackchannels_str = ((is_array($blackchannels)) ? implode("\n",$blackchannels) : '');
|
||||
|
||||
|
||||
|
||||
|
||||
$whiteembeds = get_config('system','embed_allow');
|
||||
$whiteembeds_str = ((is_array($whiteembeds)) ? implode("\n",$whiteembeds) : '');
|
||||
|
||||
|
||||
$blackembeds = get_config('system','embed_deny');
|
||||
$blackembeds_str = ((is_array($blackembeds)) ? implode("\n",$blackembeds) : '');
|
||||
|
||||
|
||||
$trusted_directory_servers = get_config('system', 'trusted_directory_servers');
|
||||
$trusted_directory_servers_str = ((is_array($trusted_directory_servers)) ? implode("\n", $trusted_directory_servers) : '');
|
||||
$is_dir = (intval(get_config('system', 'directory_mode', DIRECTORY_MODE_NORMAL)) !== DIRECTORY_MODE_NORMAL);
|
||||
|
||||
$embed_coop = intval(get_config('system','embed_coop'));
|
||||
|
||||
|
||||
if((! $whiteembeds) && (! $blackembeds)) {
|
||||
$embedhelp1 = t("By default, unfiltered HTML is allowed in embedded media. This is inherently insecure.");
|
||||
}
|
||||
|
||||
$embedhelp2 = t("The recommended setting is to only allow unfiltered HTML from the following sites:");
|
||||
$embedhelp2 = t("The recommended setting is to only allow unfiltered HTML from the following sites:");
|
||||
$embedhelp3 = t("https://youtube.com/<br />https://www.youtube.com/<br />https://youtu.be/<br />https://vimeo.com/<br />https://soundcloud.com/<br />");
|
||||
$embedhelp4 = t("All other embedded content will be filtered, <strong>unless</strong> embedded content from that site is explicitly blocked.");
|
||||
|
||||
@@ -99,7 +106,7 @@ class Security {
|
||||
'$page' => t('Security'),
|
||||
'$form_security_token' => get_form_security_token('admin_security'),
|
||||
'$block_public' => array('block_public', t("Block public"), get_config('system','block_public'), t("Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated.")),
|
||||
'$cloud_noroot' => [ 'cloud_noroot', t('Provide a cloud root directory'), 1 - intval(get_config('system','cloud_disable_siteroot')), t('The cloud root directory lists all channel names which provide public files') ],
|
||||
'$cloud_noroot' => [ 'cloud_noroot', t('Provide a cloud root directory'), 1 - intval(get_config('system','cloud_disable_siteroot')), t('The cloud root directory lists all channel names which provide public files') ],
|
||||
'$cloud_disksize' => [ 'cloud_disksize', t('Show total disk space available to cloud uploads'), intval(get_config('system','cloud_report_disksize')), '' ],
|
||||
'$transport_security' => array('transport_security', t('Set "Transport Security" HTTP header'),intval(get_config('system','transport_security_header')),''),
|
||||
'$content_security' => array('content_security', t('Set "Content Security Policy" HTTP header'),intval(get_config('system','content_security_policy')),''),
|
||||
@@ -115,6 +122,8 @@ class Security {
|
||||
'$thumbnail_security' => [ 'thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system','thumbnail_security',0), t("WARNING: SVG images may contain malicious code.") ],
|
||||
'$inline_pdf' => [ 'inline_pdf', t("Allow embedded (inline) PDF files"), get_config('system','inline_pdf',0), '' ],
|
||||
|
||||
'$trusted_directory_servers' => (($is_dir) ? ['trusted_directory_servers', t('Additional trusted directory server URLs'), $trusted_directory_servers_str, t('Accept directory flags (spam, nsfw) from those servers. One per line like https://example.tld')] : ''),
|
||||
|
||||
// '$embed_coop' => array('embed_coop', t('Cooperative embed security'), $embed_coop, t('Enable to share embed security with other compatible sites/hubs')),
|
||||
|
||||
'$submit' => t('Submit')
|
||||
@@ -124,7 +133,7 @@ class Security {
|
||||
|
||||
function trim_array_elems($arr) {
|
||||
$narr = array();
|
||||
|
||||
|
||||
if($arr && is_array($arr)) {
|
||||
for($x = 0; $x < count($arr); $x ++) {
|
||||
$y = trim($arr[$x]);
|
||||
@@ -134,6 +143,6 @@ class Security {
|
||||
}
|
||||
return $narr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -67,9 +67,8 @@ class Site {
|
||||
$open_pubstream = ((x($_POST,'open_pubstream')) ? True : False);
|
||||
$login_on_homepage = ((x($_POST,'login_on_homepage')) ? True : False);
|
||||
$enable_context_help = ((x($_POST,'enable_context_help')) ? True : False);
|
||||
$global_directory = ((x($_POST,'directory_submit_url')) ? notags(trim($_POST['directory_submit_url'])) : '');
|
||||
$no_community_page = !((x($_POST,'no_community_page')) ? True : False);
|
||||
$default_expire_days = ((array_key_exists('default_expire_days',$_POST)) ? intval($_POST['default_expire_days']) : 0);
|
||||
$default_expire_days = ((array_key_exists('default_expire_days',$_POST)) ? intval($_POST['default_expire_days']) : 30);
|
||||
$active_expire_days = ((array_key_exists('active_expire_days',$_POST)) ? intval($_POST['active_expire_days']) : 7);
|
||||
|
||||
$reply_address = ((array_key_exists('reply_address',$_POST) && trim($_POST['reply_address'])) ? trim($_POST['reply_address']) : 'noreply@' . \App::get_hostname());
|
||||
@@ -100,7 +99,7 @@ class Site {
|
||||
$reg_expire = (preg_match('/^[a-z]{1,1}$/', $regexpireu) ? $regexpiren . $regexpireu : '');
|
||||
|
||||
$imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : '');
|
||||
$force_queue = ((intval($_POST['force_queue']) > 0) ? intval($_POST['force_queue']) : 3000);
|
||||
//$force_queue = ((intval($_POST['force_queue']) > 0) ? intval($_POST['force_queue']) : 3000);
|
||||
$pub_incl = escape_tags(trim($_POST['pub_incl']));
|
||||
$pub_excl = escape_tags(trim($_POST['pub_excl']));
|
||||
|
||||
@@ -129,7 +128,7 @@ class Site {
|
||||
set_config('system', 'register_duty', $this->register_duty);
|
||||
set_config('system', 'register_duty_jso', $this->joo);
|
||||
} else {
|
||||
notice('ZAR0130E,'.t('Errors') . ': ' . $this->error) . EOL . $this->msgfg;
|
||||
notice('ZAR0130E,' . t('Errors') . ': ' . $this->error . EOL . $this->msgfg . EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,7 +180,7 @@ class Site {
|
||||
set_config('system', 'admininfo', $admininfo);
|
||||
}
|
||||
set_config('system','siteinfo',$siteinfo);
|
||||
set_config('system', 'language', $language);
|
||||
//set_config('system', 'language', $language);
|
||||
set_config('system', 'theme', $theme);
|
||||
// if ( $theme_mobile === '---' ) {
|
||||
// del_config('system', 'mobile_theme');
|
||||
@@ -205,12 +204,7 @@ class Site {
|
||||
set_config('system','disable_discover_tab', $disable_discover_tab);
|
||||
set_config('system','site_firehose', $site_firehose);
|
||||
set_config('system','open_pubstream', $open_pubstream);
|
||||
set_config('system','force_queue_threshold', $force_queue);
|
||||
if ($global_directory == '') {
|
||||
del_config('system', 'directory_submit_url');
|
||||
} else {
|
||||
set_config('system', 'directory_submit_url', $global_directory);
|
||||
}
|
||||
//set_config('system','force_queue_threshold', $force_queue);
|
||||
|
||||
set_config('system','no_community_page', $no_community_page);
|
||||
set_config('system','no_utf', $no_utf);
|
||||
@@ -233,20 +227,6 @@ class Site {
|
||||
*/
|
||||
function get() {
|
||||
|
||||
/* Installed langs */
|
||||
$lang_choices = array();
|
||||
$langs = glob('view/*/hstrings.php');
|
||||
|
||||
if(is_array($langs) && count($langs)) {
|
||||
if(! in_array('view/en/hstrings.php',$langs))
|
||||
$langs[] = 'view/en/';
|
||||
asort($langs);
|
||||
foreach($langs as $l) {
|
||||
$t = explode("/",$l);
|
||||
$lang_choices[$t[1]] = $t[1];
|
||||
}
|
||||
}
|
||||
|
||||
/* Installed themes */
|
||||
$theme_choices_mobile["---"] = t("Default");
|
||||
$theme_choices = array();
|
||||
@@ -283,7 +263,7 @@ class Site {
|
||||
}
|
||||
|
||||
$dir_choices = null;
|
||||
$dirmode = get_config('system','directory_mode');
|
||||
$dirmode = get_config('system', 'directory_mode', DIRECTORY_MODE_NORMAL);
|
||||
$realm = get_directory_realm();
|
||||
|
||||
// directory server should not be set or settable unless we are a directory client
|
||||
@@ -301,6 +281,12 @@ class Site {
|
||||
$dir_choices[$xx['site_url']] = $xx['site_url'];
|
||||
}
|
||||
}
|
||||
if ($realm === DIRECTORY_REALM) {
|
||||
$fallback_servers = get_directory_fallback_servers();
|
||||
foreach ($fallback_servers as $fallback_server) {
|
||||
$dir_choices[$fallback_server] = $fallback_server;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Banner */
|
||||
@@ -339,12 +325,15 @@ class Site {
|
||||
// now invert the logic for the setting.
|
||||
$discover_tab = (1 - $discover_tab);
|
||||
|
||||
$perm_roles = \Zotlabs\Access\PermissionRoles::roles();
|
||||
$default_role = get_config('system','default_permissions_role','social');
|
||||
$perm_roles = \Zotlabs\Access\PermissionRoles::channel_roles();
|
||||
$default_role = get_config('system', 'default_permissions_role', 'personal');
|
||||
|
||||
if (!in_array($default_role, array_keys($perm_roles))) {
|
||||
$default_role = 'personal';
|
||||
}
|
||||
|
||||
$role = array('permissions_role' , t('Default permission role for new accounts'), $default_role, t('This role will be used for the first channel created after registration.'),$perm_roles);
|
||||
|
||||
|
||||
$homelogin = get_config('system','login_on_homepage');
|
||||
$enable_context_help = get_config('system','enable_context_help');
|
||||
|
||||
@@ -422,7 +411,6 @@ class Site {
|
||||
'$banner' => array('banner', t("Banner/Logo"), $banner, t('Unfiltered HTML/CSS/JS is allowed')),
|
||||
'$admininfo' => array('admininfo', t("Administrator Information"), $admininfo, t("Contact information for site administrators. Displayed on siteinfo page. BBCode can be used here")),
|
||||
'$siteinfo' => array('siteinfo', t('Site Information'), get_config('system','siteinfo'), t("Publicly visible description of this site. Displayed on siteinfo page. BBCode can be used here")),
|
||||
'$language' => array('language', t("System language"), get_config('system','language'), "", $lang_choices),
|
||||
'$theme' => array('theme', t("System theme"), get_config('system','theme'), t("Default system theme - may be over-ridden by user profiles - <a href='#' id='cnftheme'>change theme settings</a>"), $theme_choices),
|
||||
// '$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile),
|
||||
// '$site_channel' => array('site_channel', t("Channel to use for this website's static pages"), get_config('system','site_channel'), t("Site Channel")),
|
||||
@@ -505,10 +493,10 @@ class Site {
|
||||
'$frontpage' => array('frontpage', t("Site homepage to show visitors (default: login box)"), get_config('system','frontpage'), t("example: 'pubstream' to show public stream, 'page/sys/home' to show a system webpage called 'home' or 'include:home.html' to include a file.")),
|
||||
'$mirror_frontpage' => array('mirror_frontpage', t("Preserve site homepage URL"), get_config('system','mirror_frontpage'), t('Present the site homepage in a frame at the original location instead of redirecting')),
|
||||
'$allowed_sites' => array('allowed_sites', t("Allowed friend domains"), get_config('system','allowed_sites'), t("Comma separated list of domains which are allowed to establish friendships with this site. Wildcards are accepted. Empty to allow any domains")),
|
||||
'$force_publish' => array('publish_all', t("Force publish"), get_config('system','publish_all'), t("Check to force all profiles on this site to be listed in the site directory.")),
|
||||
'$disable_discover_tab' => array('disable_discover_tab', t('Import Public Streams'), $discover_tab, t('Import and allow access to public content pulled from other sites. Warning: this content is unmoderated.')),
|
||||
'$site_firehose' => array('site_firehose', t('Site only Public Streams'), get_config('system','site_firehose'), t('Allow access to public content originating only from this site if Imported Public Streams are disabled.')),
|
||||
'$open_pubstream' => array('open_pubstream', t('Allow anybody on the internet to access the Public streams'), get_config('system','open_pubstream',1), t('Disable to require authentication before viewing. Warning: this content is unmoderated.')),
|
||||
'$force_publish' => array('publish_all', t("Force publish"), get_config('system','publish_all'), t("Check to force all profiles on this site to be listed in the site directory")),
|
||||
'$disable_discover_tab' => array('disable_discover_tab', t('Enable public stream'), $discover_tab, t('Enable the public stream. Warning: this content is unmoderated')),
|
||||
'$site_firehose' => array('site_firehose', t('Site only public stream'), get_config('system','site_firehose'), t('Restrict the public stream to content originating at this site')),
|
||||
'$open_pubstream' => array('open_pubstream', t('Allow anybody on the internet to access the public streams'), get_config('system','open_pubstream',1), t('Disable to require authentication before viewing')),
|
||||
'$incl' => array('pub_incl',t('Only import Public stream posts with this text'), get_config('system','pubstream_incl'),t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')),
|
||||
'$excl' => array('pub_excl',t('Do not import Public stream posts with this text'), get_config('system','pubstream_excl'),t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')),
|
||||
|
||||
@@ -529,11 +517,11 @@ class Site {
|
||||
'$timeout' => array('timeout', t("Network timeout"), (x(get_config('system','curl_timeout'))?get_config('system','curl_timeout'):60), t("Value is in seconds. Set to 0 for unlimited (not recommended).")),
|
||||
'$delivery_interval' => array('delivery_interval', t("Delivery interval"), (x(get_config('system','delivery_interval'))?get_config('system','delivery_interval'):2), t("Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.")),
|
||||
'$delivery_batch_count' => array('delivery_batch_count', t('Deliveries per process'),(x(get_config('system','delivery_batch_count'))?get_config('system','delivery_batch_count'):1), t("Number of deliveries to attempt in a single operating system process. Adjust if necessary to tune system performance. Recommend: 1-5.")),
|
||||
'$force_queue' => array('force_queue', t("Queue Threshold"), get_config('system','force_queue_threshold',3000), t("Always defer immediate delivery if queue contains more than this number of entries.")),
|
||||
//'$force_queue' => array('force_queue', t("Queue Threshold"), get_config('system','force_queue_threshold',3000), t("Always defer immediate delivery if queue contains more than this number of entries.")),
|
||||
'$poll_interval' => array('poll_interval', t("Poll interval"), (x(get_config('system','poll_interval'))?get_config('system','poll_interval'):2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")),
|
||||
'$imagick_path' => array('imagick_path', t("Path to ImageMagick convert program"), get_config('system','imagick_convert_path'), t("If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert")),
|
||||
'$maxloadavg' => array('maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.")),
|
||||
'$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system','default_expire_days')), t('0 for no expiration of imported content')),
|
||||
'$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system','default_expire_days', 30)), t('0 for no expiration of imported content')),
|
||||
'$active_expire_days' => array('active_expire_days', t('Do not expire any posts which have comments less than this many days ago'), intval(get_config('system','active_expire_days',7)), ''),
|
||||
'$sellpage' => array('site_sellpage', t('Public servers: Optional landing (marketing) webpage for new registrants'), get_config('system','sellpage',''), sprintf( t('Create this page first. Default is %s/register'),z_root())),
|
||||
'$first_page' => array('first_page', t('Page to display after creating a new channel'), get_config('system','workflow_channel_next','profiles'), t('Default: profiles')),
|
||||
|
||||
@@ -80,10 +80,25 @@ class Themes {
|
||||
|
||||
$this->toggle_theme($themes, $theme, $result);
|
||||
$s = $this->rebuild_theme_table($themes);
|
||||
if($result)
|
||||
info( sprintf('Theme %s enabled.', $theme));
|
||||
else
|
||||
info( sprintf('Theme %s disabled.', $theme));
|
||||
|
||||
if($result) {
|
||||
if (is_file("view/theme/$theme/php/config.php")){
|
||||
require_once("view/theme/$theme/php/config.php");
|
||||
if (function_exists($theme . '_theme_admin_enable')){
|
||||
call_user_func($theme . '_theme_admin_enable');
|
||||
}
|
||||
}
|
||||
info(sprintf('Theme %s enabled.', $theme));
|
||||
}
|
||||
else {
|
||||
if (is_file("view/theme/$theme/php/config.php")){
|
||||
require_once("view/theme/$theme/php/config.php");
|
||||
if (function_exists($theme . '_theme_admin_disable')){
|
||||
call_user_func($theme . '_theme_admin_disable');
|
||||
}
|
||||
}
|
||||
info(sprintf('Theme %s disabled.', $theme));
|
||||
}
|
||||
|
||||
set_config('system', 'allowed_themes', $s);
|
||||
goaway(z_root() . '/admin/themes' );
|
||||
|
||||
@@ -24,42 +24,42 @@ class Api extends \Zotlabs\Web\Controller {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
if(\App::$cmd === 'api/oauth/authorize'){
|
||||
|
||||
/*
|
||||
|
||||
/*
|
||||
* api/oauth/authorize interact with the user. return a standard page
|
||||
*/
|
||||
|
||||
|
||||
\App::$page['template'] = 'minimal';
|
||||
|
||||
|
||||
// get consumer/client from request token
|
||||
try {
|
||||
$request = \OAuth1Request::from_request();
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
logger('OAuth exception: ' . print_r($e,true));
|
||||
// echo "<pre>"; var_dump($e);
|
||||
// echo "<pre>"; var_dump($e);
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if(x($_POST,'oauth_yes')){
|
||||
|
||||
|
||||
$app = $this->oauth_get_client($request);
|
||||
if (is_null($app))
|
||||
if (is_null($app))
|
||||
return "Invalid request. Unknown token.";
|
||||
|
||||
$consumer = new \OAuth1Consumer($app['client_id'], $app['pw'], $app['redirect_uri']);
|
||||
|
||||
|
||||
$verifier = md5($app['secret'] . local_channel());
|
||||
set_config('oauth', $verifier, local_channel());
|
||||
|
||||
|
||||
|
||||
|
||||
if($consumer->callback_url != null) {
|
||||
$params = $request->get_parameters();
|
||||
$glue = '?';
|
||||
@@ -68,28 +68,28 @@ class Api extends \Zotlabs\Web\Controller {
|
||||
goaway($consumer->callback_url . $glue . "oauth_token=" . \OAuth1Util::urlencode_rfc3986($params['oauth_token']) . "&oauth_verifier=" . \OAuth1Util::urlencode_rfc3986($verifier));
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
$tpl = get_markup_template("oauth_authorize_done.tpl");
|
||||
$o = replace_macros($tpl, array(
|
||||
'$title' => t('Authorize application connection'),
|
||||
'$info' => t('Return to your app and insert this Security Code:'),
|
||||
'$code' => $verifier,
|
||||
));
|
||||
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if(! local_channel()) {
|
||||
//TODO: we need login form to redirect to this page
|
||||
notice( t('Please login to continue.') . EOL );
|
||||
return login(false,'api-login',$request->get_parameters());
|
||||
}
|
||||
|
||||
|
||||
$app = $this->oauth_get_client($request);
|
||||
if (is_null($app))
|
||||
return "Invalid request. Unknown token.";
|
||||
|
||||
|
||||
$tpl = get_markup_template('oauth_authorize.tpl');
|
||||
$o = replace_macros($tpl, array(
|
||||
'$title' => t('Authorize application connection'),
|
||||
@@ -98,12 +98,12 @@ class Api extends \Zotlabs\Web\Controller {
|
||||
'$yes' => t('Yes'),
|
||||
'$no' => t('No'),
|
||||
));
|
||||
|
||||
|
||||
//echo "<pre>"; var_dump($app); killme();
|
||||
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
echo api_call();
|
||||
killme();
|
||||
}
|
||||
@@ -112,8 +112,8 @@ class Api extends \Zotlabs\Web\Controller {
|
||||
|
||||
$params = $request->get_parameters();
|
||||
$token = $params['oauth_token'];
|
||||
|
||||
$r = q("SELECT clients.* FROM clients, tokens WHERE clients.client_id = tokens.client_id
|
||||
|
||||
$r = q("SELECT clients.* FROM clients, tokens WHERE clients.client_id = tokens.client_id
|
||||
AND tokens.id = '%s' AND tokens.auth_scope = 'request' ",
|
||||
dbesc($token)
|
||||
);
|
||||
@@ -121,7 +121,7 @@ class Api extends \Zotlabs\Web\Controller {
|
||||
return $r[0];
|
||||
|
||||
return null;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class Appman extends \Zotlabs\Web\Controller {
|
||||
if(! local_channel())
|
||||
return;
|
||||
|
||||
if($_POST['url']) {
|
||||
if(isset($_POST['url']) && $_POST['url']) {
|
||||
$arr = array(
|
||||
'uid' => intval($_REQUEST['uid']),
|
||||
'url' => escape_tags($_REQUEST['url']),
|
||||
@@ -50,7 +50,7 @@ class Appman extends \Zotlabs\Web\Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
if($_POST['install']) {
|
||||
if(isset($_POST['install']) && $_POST['install']) {
|
||||
Apps::app_install(local_channel(),$papp);
|
||||
if(Apps::app_installed(local_channel(),$papp))
|
||||
info( t('App installed.') . EOL);
|
||||
@@ -65,15 +65,15 @@ class Appman extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
|
||||
if (intval($sync[0]['app_system'])) {
|
||||
Libsync::build_sync_packet($uid, ['sysapp' => $sync]);
|
||||
Libsync::build_sync_packet(local_channel(), ['sysapp' => $sync]);
|
||||
}
|
||||
else {
|
||||
Libsync::build_sync_packet($uid, ['app' => $sync]);
|
||||
Libsync::build_sync_packet(local_channel(), ['app' => $sync]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if($_POST['delete']) {
|
||||
if(isset($_POST['delete']) && $_POST['delete']) {
|
||||
|
||||
// Fetch the app for sync before it is deleted (if it is deletable))
|
||||
$sync = q("SELECT * FROM app WHERE app_channel = %d AND app_id = '%s' LIMIT 1",
|
||||
@@ -91,18 +91,18 @@ class Appman extends \Zotlabs\Web\Controller {
|
||||
$sync[0]['app_deleted'] = 1;
|
||||
|
||||
if (intval($sync[0]['app_system'])) {
|
||||
Libsync::build_sync_packet($uid, ['sysapp' => $sync]);
|
||||
Libsync::build_sync_packet(local_channel(), ['sysapp' => $sync]);
|
||||
}
|
||||
else {
|
||||
Libsync::build_sync_packet($uid, ['app' => $sync]);
|
||||
Libsync::build_sync_packet(local_channel(), ['app' => $sync]);
|
||||
}
|
||||
}
|
||||
|
||||
if($_POST['edit']) {
|
||||
if(isset($_POST['edit']) && $_POST['edit']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if($_POST['feature']) {
|
||||
if(isset($_POST['feature']) && $_POST['feature']) {
|
||||
Apps::app_feature(local_channel(), $papp, $_POST['feature']);
|
||||
|
||||
$sync = q("SELECT * FROM app WHERE app_channel = %d AND app_id = '%s' LIMIT 1",
|
||||
@@ -110,15 +110,20 @@ class Appman extends \Zotlabs\Web\Controller {
|
||||
dbesc($papp['guid'])
|
||||
);
|
||||
|
||||
$sync[0]['term'] = q("select * from term where otype = %d and oid = %d",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($sync[0]['id'])
|
||||
);
|
||||
|
||||
if (intval($sync[0]['app_system'])) {
|
||||
Libsync::build_sync_packet($uid, ['sysapp' => $sync]);
|
||||
Libsync::build_sync_packet(local_channel(), ['sysapp' => $sync]);
|
||||
}
|
||||
else {
|
||||
Libsync::build_sync_packet($uid, ['app' => $sync]);
|
||||
Libsync::build_sync_packet(local_channel(), ['app' => $sync]);
|
||||
}
|
||||
}
|
||||
|
||||
if($_POST['pin']) {
|
||||
if(isset($_POST['pin']) && $_POST['pin']) {
|
||||
Apps::app_feature(local_channel(), $papp, $_POST['pin']);
|
||||
|
||||
$sync = q("SELECT * FROM app WHERE app_channel = %d AND app_id = '%s' LIMIT 1",
|
||||
@@ -126,15 +131,20 @@ class Appman extends \Zotlabs\Web\Controller {
|
||||
dbesc($papp['guid'])
|
||||
);
|
||||
|
||||
$sync[0]['term'] = q("select * from term where otype = %d and oid = %d",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($sync[0]['id'])
|
||||
);
|
||||
|
||||
if (intval($sync[0]['app_system'])) {
|
||||
Libsync::build_sync_packet($uid, ['sysapp' => $sync]);
|
||||
Libsync::build_sync_packet(local_channel(), ['sysapp' => $sync]);
|
||||
}
|
||||
else {
|
||||
Libsync::build_sync_packet($uid, ['app' => $sync]);
|
||||
Libsync::build_sync_packet(local_channel(), ['app' => $sync]);
|
||||
}
|
||||
}
|
||||
|
||||
if($_POST['aj']) {
|
||||
if(isset($_POST['aj']) && $_POST['aj']) {
|
||||
killme();
|
||||
}
|
||||
|
||||
@@ -171,7 +181,7 @@ class Appman extends \Zotlabs\Web\Controller {
|
||||
|
||||
$app = null;
|
||||
$embed = null;
|
||||
if($_REQUEST['appid']) {
|
||||
if(isset($_REQUEST['appid']) && $_REQUEST['appid']) {
|
||||
$r = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc($_REQUEST['appid']),
|
||||
dbesc(local_channel())
|
||||
@@ -200,27 +210,25 @@ class Appman extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
|
||||
return replace_macros(get_markup_template('app_create.tpl'), array(
|
||||
|
||||
'$banner' => (($app) ? t('Edit App') : t('Create App')),
|
||||
'$app' => $app,
|
||||
'$guid' => (($app) ? $app['app_id'] : ''),
|
||||
'$author' => (($app) ? $app['app_author'] : $channel['channel_hash']),
|
||||
'$addr' => (($app) ? $app['app_addr'] : $channel['xchan_addr']),
|
||||
'$name' => array('name', t('Name of app'),(($app) ? $app['app_name'] : ''), t('Required')),
|
||||
'$url' => array('url', t('Location (URL) of app'),(($app) ? $app['app_url'] : ''), t('Required')),
|
||||
'$desc' => array('desc', t('Description'),(($app) ? $app['app_desc'] : ''), ''),
|
||||
'$photo' => array('photo', t('Photo icon URL'),(($app) ? $app['app_photo'] : ''), t('80 x 80 pixels - optional')),
|
||||
'$categories' => array('categories',t('Categories (optional, comma separated list)'),(($app) ? $app['categories'] : ''),''),
|
||||
'$version' => array('version', t('Version ID'),(($app) ? $app['app_version'] : ''), ''),
|
||||
'$price' => array('price', t('Price of app'),(($app) ? $app['app_price'] : ''), ''),
|
||||
'$page' => array('page', t('Location (URL) to purchase app'),(($app) ? $app['app_page'] : ''), ''),
|
||||
'$system' => (($app) ? intval($app['app_system']) : 0),
|
||||
'$plugin' => (($app) ? $app['app_plugin'] : ''),
|
||||
'$requires' => (($app) ? $app['app_requires'] : ''),
|
||||
'$guid' => $app['app_id'] ?? '',
|
||||
'$author' => $app['app_author'] ?? $channel['channel_hash'],
|
||||
'$addr' => $app['app_addr'] ?? $channel['xchan_addr'],
|
||||
'$name' => array('name', t('Name of app'), $app['app_name'] ?? '', t('Required')),
|
||||
'$url' => array('url', t('Location (URL) of app'), $app['app_url'] ?? '', t('Required')),
|
||||
'$desc' => array('desc', t('Description'), $app['app_desc'] ?? '', ''),
|
||||
'$photo' => array('photo', t('Photo icon URL'),$app['app_photo'] ?? '', t('80 x 80 pixels - optional')),
|
||||
'$categories' => array('categories',t('Categories (optional, comma separated list)'), $app['categories'] ?? '',''),
|
||||
'$version' => array('version', t('Version ID'), $app['app_version'] ?? '', ''),
|
||||
'$price' => array('price', t('Price of app'), $app['app_price'] ?? '', ''),
|
||||
'$page' => array('page', t('Location (URL) to purchase app'), $app['app_page'] ?? '', ''),
|
||||
'$system' => $app['app_system'] ?? 0,
|
||||
'$plugin' => $app['app_plugin'] ?? '',
|
||||
'$requires' => $app['app_requires'] ?? '',
|
||||
'$embed' => $embed,
|
||||
'$submit' => t('Submit')
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ class Apps extends \Zotlabs\Web\Controller {
|
||||
|
||||
$_SESSION['return_url'] = \App::$query_string;
|
||||
|
||||
$apps = array();
|
||||
$apps = [];
|
||||
$cat = [];
|
||||
|
||||
if(local_channel()) {
|
||||
Zlib\Apps::import_system_apps();
|
||||
|
||||
@@ -2,65 +2,13 @@
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\Activity;
|
||||
|
||||
class Apschema extends \Zotlabs\Web\Controller {
|
||||
|
||||
class Apschema extends Controller {
|
||||
function init() {
|
||||
|
||||
$base = z_root();
|
||||
|
||||
$arr = [
|
||||
'@context' => [
|
||||
'zot' => z_root() . '/apschema#',
|
||||
'id' => '@id',
|
||||
'type' => '@type',
|
||||
'commentPolicy' => 'as:commentPolicy',
|
||||
'meData' => 'zot:meData',
|
||||
'meDataType' => 'zot:meDataType',
|
||||
'meEncoding' => 'zot:meEncoding',
|
||||
'meAlgorithm' => 'zot:meAlgorithm',
|
||||
'meCreator' => 'zot:meCreator',
|
||||
'meSignatureValue' => 'zot:meSignatureValue',
|
||||
'locationAddress' => 'zot:locationAddress',
|
||||
'locationPrimary' => 'zot:locationPrimary',
|
||||
'locationDeleted' => 'zot:locationDeleted',
|
||||
'nomadicLocation' => 'zot:nomadicLocation',
|
||||
'nomadicHubs' => 'zot:nomadicHubs',
|
||||
'emojiReaction' => 'zot:emojiReaction',
|
||||
'expires' => 'zot:expires',
|
||||
'directMessage' => 'zot:directMessage',
|
||||
'schema' => 'http://schema.org#',
|
||||
'PropertyValue' => 'schema:PropertyValue',
|
||||
'value' => 'schema:value',
|
||||
|
||||
'magicEnv' => [
|
||||
'@id' => 'zot:magicEnv',
|
||||
'@type' => '@id'
|
||||
],
|
||||
|
||||
'nomadicLocations' => [
|
||||
'@id' => 'zot:nomadicLocations',
|
||||
'@type' => '@id'
|
||||
],
|
||||
|
||||
'ostatus' => 'http://ostatus.org#',
|
||||
'conversation' => 'ostatus:conversation',
|
||||
|
||||
'diaspora' => 'https://diasporafoundation.org/ns/',
|
||||
'guid' => 'diaspora:guid',
|
||||
|
||||
'Hashtag' => 'as:Hashtag'
|
||||
|
||||
]
|
||||
];
|
||||
|
||||
header('Content-Type: application/ld+json');
|
||||
echo json_encode($arr,JSON_UNESCAPED_SLASHES);
|
||||
echo json_encode(Activity::ap_context(), JSON_UNESCAPED_SLASHES);
|
||||
killme();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/channel.php');
|
||||
require_once('include/acl_selectors.php');
|
||||
require_once('include/conversation.php');
|
||||
|
||||
class Article_edit extends \Zotlabs\Web\Controller {
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
// Figure out which post we're editing
|
||||
$post_id = ((argc() > 1) ? intval(argv(1)) : 0);
|
||||
|
||||
if(! $post_id) {
|
||||
notice( t('Item not found') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$itm = q("SELECT * FROM item WHERE id = %d and item_type = %d LIMIT 1",
|
||||
intval($post_id),
|
||||
intval(ITEM_TYPE_ARTICLE)
|
||||
);
|
||||
if($itm) {
|
||||
$item_id = q("select * from iconfig where cat = 'system' and k = 'ARTICLE' and iid = %d limit 1",
|
||||
intval($itm[0]['id'])
|
||||
);
|
||||
if($item_id)
|
||||
$card_title = $item_id[0]['v'];
|
||||
}
|
||||
else {
|
||||
notice( t('Item not found') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$owner = $itm[0]['uid'];
|
||||
$uid = local_channel();
|
||||
|
||||
$observer = \App::get_observer();
|
||||
|
||||
$channel = channelx_by_n($owner);
|
||||
if(! $channel) {
|
||||
notice( t('Channel not found.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
|
||||
if(! perm_is_allowed($owner,$ob_hash,'write_pages')) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$is_owner = (($uid && $uid == $owner) ? true : false);
|
||||
|
||||
$o = '';
|
||||
|
||||
|
||||
|
||||
$category = '';
|
||||
$catsenabled = ((feature_enabled($owner,'categories')) ? 'categories' : '');
|
||||
|
||||
if ($catsenabled){
|
||||
$itm = fetch_post_tags($itm);
|
||||
|
||||
$cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
|
||||
|
||||
foreach ($cats as $cat) {
|
||||
if (strlen($category))
|
||||
$category .= ', ';
|
||||
$category .= $cat['term'];
|
||||
}
|
||||
}
|
||||
|
||||
if($itm[0]['attach']) {
|
||||
$j = json_decode($itm[0]['attach'],true);
|
||||
if($j) {
|
||||
foreach($j as $jj) {
|
||||
$itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$mimetype = $itm[0]['mimetype'];
|
||||
|
||||
$content = $itm[0]['body'];
|
||||
|
||||
$rp = 'articles/' . $channel['channel_address'];
|
||||
|
||||
$x = array(
|
||||
'nickname' => $channel['channel_address'],
|
||||
'bbco_autocomplete'=> 'bbcode',
|
||||
'return_path' => $rp,
|
||||
'webpage' => ITEM_TYPE_ARTICLE,
|
||||
'button' => t('Edit'),
|
||||
'writefiles' => perm_is_allowed($owner, get_observer_hash(), 'write_pages'),
|
||||
'weblink' => t('Insert web link'),
|
||||
'hide_voting' => false,
|
||||
'hide_future' => false,
|
||||
'hide_location' => false,
|
||||
'hide_expire' => false,
|
||||
'showacl' => true,
|
||||
'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')),
|
||||
'permissions' => $itm[0],
|
||||
'lockstate' => (($itm[0]['allow_cid'] || $itm[0]['allow_gid'] || $itm[0]['deny_cid'] || $itm[0]['deny_gid']) ? 'lock' : 'unlock'),
|
||||
'ptyp' => $itm[0]['type'],
|
||||
'mimeselect' => false,
|
||||
'mimetype' => $itm[0]['mimetype'],
|
||||
'body' => undo_post_tagging($content),
|
||||
'post_id' => $post_id,
|
||||
'visitor' => true,
|
||||
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
|
||||
'summary' => htmlspecialchars($itm[0]['summary'],ENT_COMPAT,'UTF-8'),
|
||||
'placeholdertitle' => t('Title (optional)'),
|
||||
'pagetitle' => $card_title,
|
||||
'profile_uid' => (intval($channel['channel_id'])),
|
||||
'catsenabled' => $catsenabled,
|
||||
'category' => $category,
|
||||
'bbcode' => (($mimetype == 'text/bbcode') ? true : false)
|
||||
);
|
||||
|
||||
$editor = status_editor($a, $x, false, 'Article_edit');
|
||||
|
||||
$o .= replace_macros(get_markup_template('edpost_head.tpl'), array(
|
||||
'$title' => t('Edit Article'),
|
||||
'$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false),
|
||||
'$id' => $itm[0]['id'],
|
||||
'$cancel' => t('Cancel'),
|
||||
'$editor' => $editor
|
||||
));
|
||||
|
||||
return $o;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\PermissionDescription;
|
||||
|
||||
require_once('include/channel.php');
|
||||
require_once('include/conversation.php');
|
||||
require_once('include/acl_selectors.php');
|
||||
require_once('include/opengraph.php');
|
||||
|
||||
|
||||
class Articles extends Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
if(argc() > 1)
|
||||
$which = argv(1);
|
||||
|
||||
if(! $which) {
|
||||
if(local_channel()) {
|
||||
$channel = App::get_channel();
|
||||
if($channel && $channel['channel_address'])
|
||||
$which = $channel['channel_address'];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
profile_load($which);
|
||||
|
||||
}
|
||||
|
||||
function get($update = 0, $load = false) {
|
||||
|
||||
if(observer_prohibited(true)) {
|
||||
return login();
|
||||
}
|
||||
|
||||
if(! App::$profile) {
|
||||
notice( t('Requested profile is not available.') . EOL );
|
||||
App::$error = 404;
|
||||
return;
|
||||
}
|
||||
|
||||
if(! Apps::system_app_installed(App::$profile_uid, 'Articles')) {
|
||||
//Do not display any associated widgets at this point
|
||||
App::$pdl = '';
|
||||
$papp = Apps::get_papp('Articles');
|
||||
return Apps::app_render($papp, 'module');
|
||||
}
|
||||
|
||||
nav_set_selected('Articles');
|
||||
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/json+oembed',
|
||||
'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string),
|
||||
'title' => 'oembed'
|
||||
]);
|
||||
|
||||
|
||||
$category = (($_REQUEST['cat']) ? escape_tags(trim($_REQUEST['cat'])) : '');
|
||||
|
||||
if($category) {
|
||||
$sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'],'item', $category, TERM_CATEGORY));
|
||||
}
|
||||
|
||||
$datequery = ((x($_GET,'dend') && is_a_date_arg($_GET['dend'])) ? notags($_GET['dend']) : '');
|
||||
$datequery2 = ((x($_GET,'dbegin') && is_a_date_arg($_GET['dbegin'])) ? notags($_GET['dbegin']) : '');
|
||||
|
||||
$which = argv(1);
|
||||
|
||||
$selected_card = ((argc() > 2) ? argv(2) : '');
|
||||
|
||||
$_SESSION['return_url'] = App::$query_string;
|
||||
|
||||
$uid = local_channel();
|
||||
$owner = App::$profile_uid;
|
||||
$observer = App::get_observer();
|
||||
|
||||
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
|
||||
if(! perm_is_allowed($owner,$ob_hash,'view_pages')) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$is_owner = ($uid && $uid == $owner);
|
||||
|
||||
$channel = channelx_by_n($owner);
|
||||
|
||||
if($channel) {
|
||||
$channel_acl = array(
|
||||
'allow_cid' => $channel['channel_allow_cid'],
|
||||
'allow_gid' => $channel['channel_allow_gid'],
|
||||
'deny_cid' => $channel['channel_deny_cid'],
|
||||
'deny_gid' => $channel['channel_deny_gid']
|
||||
);
|
||||
}
|
||||
else {
|
||||
$channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ];
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(perm_is_allowed($owner,$ob_hash,'write_pages')) {
|
||||
|
||||
$x = [
|
||||
'webpage' => ITEM_TYPE_ARTICLE,
|
||||
'is_owner' => true,
|
||||
'content_label' => t('Add Article'),
|
||||
'button' => t('Save'),
|
||||
'nickname' => $channel['channel_address'],
|
||||
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid']
|
||||
|| $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
|
||||
'acl' => (($is_owner) ? populate_acl($channel_acl, false,
|
||||
PermissionDescription::fromGlobalPermission('view_pages')) : ''),
|
||||
'permissions' => $channel_acl,
|
||||
'showacl' => (($is_owner) ? true : false),
|
||||
'visitor' => true,
|
||||
'hide_location' => false,
|
||||
'hide_voting' => false,
|
||||
'profile_uid' => intval($owner),
|
||||
'mimetype' => 'text/bbcode',
|
||||
'mimeselect' => false,
|
||||
'layoutselect' => false,
|
||||
'expanded' => false,
|
||||
'novoting' => false,
|
||||
'catsenabled' => feature_enabled($owner,'categories'),
|
||||
'bbco_autocomplete' => 'bbcode',
|
||||
'bbcode' => true
|
||||
];
|
||||
|
||||
if($_REQUEST['title'])
|
||||
$x['title'] = $_REQUEST['title'];
|
||||
if($_REQUEST['body'])
|
||||
$x['body'] = $_REQUEST['body'];
|
||||
$editor = status_editor($a,$x,false,'Articles');
|
||||
|
||||
}
|
||||
else {
|
||||
$editor = '';
|
||||
}
|
||||
|
||||
$itemspage = get_pconfig(local_channel(),'system','itemspage');
|
||||
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10));
|
||||
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start']));
|
||||
|
||||
|
||||
$sql_extra = item_permissions_sql($owner);
|
||||
$sql_item = '';
|
||||
|
||||
if($selected_card) {
|
||||
$r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.v = '%s' limit 1",
|
||||
dbesc($selected_card)
|
||||
);
|
||||
if($r) {
|
||||
$sql_item = "and item.id = " . intval($r[0]['iid']) . " ";
|
||||
}
|
||||
}
|
||||
if($datequery) {
|
||||
$sql_extra2 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery))));
|
||||
$order = 'post';
|
||||
}
|
||||
if($datequery2) {
|
||||
$sql_extra2 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery2))));
|
||||
}
|
||||
|
||||
if($datequery || $datequery2) {
|
||||
$sql_extra2 .= " and item.item_thread_top != 0 ";
|
||||
}
|
||||
|
||||
$r = q("select * from item
|
||||
where item.uid = %d and item_type = %d
|
||||
$sql_extra $sql_extra2 $sql_item order by item.created desc $pager_sql",
|
||||
intval($owner),
|
||||
intval(ITEM_TYPE_ARTICLE)
|
||||
);
|
||||
|
||||
$item_normal = " and item.item_hidden = 0 and item.item_type in (0,7) and item.item_deleted = 0
|
||||
and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
|
||||
and item.item_blocked = 0 ";
|
||||
|
||||
if($r) {
|
||||
|
||||
$pager_total = count($r);
|
||||
|
||||
$parents_str = ids_to_querystr($r,'id');
|
||||
|
||||
$r = q("SELECT item.*, item.id AS item_id
|
||||
FROM item
|
||||
WHERE item.uid = %d $item_normal
|
||||
AND item.parent IN ( %s )
|
||||
$sql_extra $sql_extra2 ",
|
||||
intval(App::$profile['profile_uid']),
|
||||
dbesc($parents_str)
|
||||
);
|
||||
if($r) {
|
||||
xchan_query($r);
|
||||
$items = fetch_post_tags($r, true);
|
||||
$items = conv_sort($items,'updated');
|
||||
}
|
||||
else
|
||||
$items = [];
|
||||
}
|
||||
|
||||
// Add Opengraph markup
|
||||
opengraph_add_meta((! empty($items) ? $r[0] : []), $channel);
|
||||
|
||||
$mode = 'articles';
|
||||
|
||||
if(get_pconfig(local_channel(),'system','articles_list_mode') && (! $selected_card))
|
||||
$page_mode = 'pager_list';
|
||||
else
|
||||
$page_mode = 'traditional';
|
||||
|
||||
$content = conversation($items,$mode,false,$page_mode);
|
||||
|
||||
$o = replace_macros(get_markup_template('cards.tpl'), [
|
||||
'$title' => t('Articles'),
|
||||
'$editor' => $editor,
|
||||
'$content' => $content,
|
||||
'$pager' => alt_pager($pager_total)
|
||||
]);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -95,7 +95,7 @@ class Attach extends Controller {
|
||||
killme();
|
||||
}
|
||||
|
||||
$r = attach_by_hash(argv(1), get_observer_hash(), ((argc() > 2) ? intval(argv(2)) : 0));
|
||||
$r = attach_by_hash(argv(1), get_observer_hash(), ((argc() > 2) ? intval(argv(2)) : 0), $token);
|
||||
|
||||
if (!$r['success']) {
|
||||
notice($r['message'] . EOL);
|
||||
|
||||
@@ -9,43 +9,43 @@ require_once('include/acl_selectors.php');
|
||||
class Blocks extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
|
||||
if(argc() > 1 && argv(1) === 'sys' && is_site_admin()) {
|
||||
$sys = get_sys_channel();
|
||||
if($sys && intval($sys['channel_id'])) {
|
||||
\App::$is_sys = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(argc() > 1)
|
||||
$which = argv(1);
|
||||
else
|
||||
return;
|
||||
|
||||
|
||||
profile_load($which);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
|
||||
if(! \App::$profile) {
|
||||
notice( t('Requested profile is not available.') . EOL );
|
||||
\App::$error = 404;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$which = argv(1);
|
||||
|
||||
|
||||
$_SESSION['return_url'] = \App::$query_string;
|
||||
|
||||
|
||||
$uid = local_channel();
|
||||
$owner = 0;
|
||||
$channel = null;
|
||||
$observer = \App::get_observer();
|
||||
|
||||
|
||||
$channel = \App::get_channel();
|
||||
|
||||
|
||||
if(\App::$is_sys && is_site_admin()) {
|
||||
$sys = get_sys_channel();
|
||||
if($sys && intval($sys['channel_id'])) {
|
||||
@@ -54,7 +54,7 @@ class Blocks extends \Zotlabs\Web\Controller {
|
||||
$observer = $sys;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(! $owner) {
|
||||
// Figure out who the page owner is.
|
||||
$r = q("select channel_id from channel where channel_address = '%s'",
|
||||
@@ -64,24 +64,24 @@ class Blocks extends \Zotlabs\Web\Controller {
|
||||
$owner = intval($r[0]['channel_id']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
|
||||
|
||||
$perms = get_all_perms($owner,$ob_hash);
|
||||
|
||||
|
||||
if(! $perms['write_pages']) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Block design features from visitors
|
||||
|
||||
|
||||
// Block design features from visitors
|
||||
|
||||
if((! $uid) || ($uid != $owner)) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$mimetype = (($_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner,'system','page_mimetype'));
|
||||
|
||||
$mimetype = ((isset($_REQUEST['mimetype']) && $_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner,'system','page_mimetype'));
|
||||
|
||||
$x = array(
|
||||
'webpage' => ITEM_TYPE_BLOCK,
|
||||
@@ -101,27 +101,25 @@ class Blocks extends \Zotlabs\Web\Controller {
|
||||
'bbco_autocomplete' => 'bbcode',
|
||||
'bbcode' => true
|
||||
);
|
||||
|
||||
if($_REQUEST['title'])
|
||||
$x['title'] = $_REQUEST['title'];
|
||||
if($_REQUEST['body'])
|
||||
$x['body'] = $_REQUEST['body'];
|
||||
if($_REQUEST['pagetitle'])
|
||||
$x['pagetitle'] = $_REQUEST['pagetitle'];
|
||||
|
||||
$editor = status_editor($a,$x,false,'Blocks');
|
||||
|
||||
|
||||
$r = q("select iconfig.iid, iconfig.k, iconfig.v, mid, title, body, mimetype, created, edited from iconfig
|
||||
$x['title'] = $_REQUEST['title'] ?? '';
|
||||
$x['body'] = $_REQUEST['body'] ?? '';
|
||||
$x['pagetitle'] = $_REQUEST['pagetitle'] ?? '';
|
||||
|
||||
$a = '';
|
||||
$editor = status_editor($x, false, 'Blocks');
|
||||
|
||||
|
||||
$r = q("select iconfig.iid, iconfig.k, iconfig.v, mid, title, body, mimetype, created, edited from iconfig
|
||||
left join item on iconfig.iid = item.id
|
||||
where uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK'
|
||||
where uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK'
|
||||
and item_type = %d order by item.created desc",
|
||||
intval($owner),
|
||||
intval(ITEM_TYPE_BLOCK)
|
||||
);
|
||||
|
||||
|
||||
$pages = null;
|
||||
|
||||
|
||||
if($r) {
|
||||
$pages = array();
|
||||
foreach($r as $rr) {
|
||||
@@ -143,13 +141,13 @@ class Blocks extends \Zotlabs\Web\Controller {
|
||||
'edited' => $rr['edited'],
|
||||
'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Build the base URL for edit links
|
||||
$url = z_root() . '/editblock/' . $which;
|
||||
|
||||
$o .= replace_macros(get_markup_template('blocklist.tpl'), array(
|
||||
$url = z_root() . '/editblock/' . $which;
|
||||
|
||||
$o = replace_macros(get_markup_template('blocklist.tpl'), array(
|
||||
'$baseurl' => $url,
|
||||
'$title' => t('Blocks'),
|
||||
'$name' => t('Block Name'),
|
||||
@@ -166,8 +164,8 @@ class Blocks extends \Zotlabs\Web\Controller {
|
||||
'$view' => t('View'),
|
||||
'$preview' => '1',
|
||||
));
|
||||
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class Cal extends Controller {
|
||||
|
||||
head_set_icon(App::$data['channel']['xchan_photo_s']);
|
||||
|
||||
App::$page['htmlhead'] .= "<script> var profile_uid = " . ((App::$data['channel']) ? App::$data['channel']['channel_id'] : 0) . "; </script>" ;
|
||||
App::$page['htmlhead'] = "<script> var profile_uid = " . ((App::$data['channel']) ? App::$data['channel']['channel_id'] : 0) . "; </script>" ;
|
||||
|
||||
}
|
||||
|
||||
@@ -65,13 +65,9 @@ class Cal extends Controller {
|
||||
|
||||
nav_set_selected('Calendar');
|
||||
|
||||
head_add_css('/library/fullcalendar/packages/core/main.min.css');
|
||||
head_add_css('/library/fullcalendar/packages/daygrid/main.min.css');
|
||||
head_add_js('/library/fullcalendar/dist/index.global.js');
|
||||
head_add_css('cdav_calendar.css');
|
||||
|
||||
head_add_js('/library/fullcalendar/packages/core/main.min.js');
|
||||
head_add_js('/library/fullcalendar/packages/daygrid/main.min.js');
|
||||
|
||||
$sql_extra = permissions_sql($channel['channel_id'], get_observer_hash(), 'event');
|
||||
|
||||
if(! perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_contacts') || App::$profile['hide_friends'])
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/channel.php');
|
||||
require_once('include/acl_selectors.php');
|
||||
require_once('include/conversation.php');
|
||||
|
||||
class Card_edit extends \Zotlabs\Web\Controller {
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
// Figure out which post we're editing
|
||||
$post_id = ((argc() > 1) ? intval(argv(1)) : 0);
|
||||
|
||||
if(! $post_id) {
|
||||
notice( t('Item not found') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$itm = q("SELECT * FROM item WHERE id = %d and item_type = %d LIMIT 1",
|
||||
intval($post_id),
|
||||
intval(ITEM_TYPE_CARD)
|
||||
);
|
||||
if($itm) {
|
||||
$item_id = q("select * from iconfig where cat = 'system' and k = 'CARD' and iid = %d limit 1",
|
||||
intval($itm[0]['id'])
|
||||
);
|
||||
if($item_id)
|
||||
$card_title = $item_id[0]['v'];
|
||||
}
|
||||
else {
|
||||
notice( t('Item not found') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$owner = $itm[0]['uid'];
|
||||
$uid = local_channel();
|
||||
|
||||
$observer = \App::get_observer();
|
||||
|
||||
$channel = channelx_by_n($owner);
|
||||
if(! $channel) {
|
||||
notice( t('Channel not found.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
|
||||
if(! perm_is_allowed($owner,$ob_hash,'write_pages')) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$is_owner = (($uid && $uid == $owner) ? true : false);
|
||||
|
||||
$o = '';
|
||||
|
||||
|
||||
|
||||
$category = '';
|
||||
$catsenabled = ((feature_enabled($owner,'categories')) ? 'categories' : '');
|
||||
|
||||
if ($catsenabled){
|
||||
$itm = fetch_post_tags($itm);
|
||||
|
||||
$cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
|
||||
|
||||
foreach ($cats as $cat) {
|
||||
if (strlen($category))
|
||||
$category .= ', ';
|
||||
$category .= $cat['term'];
|
||||
}
|
||||
}
|
||||
|
||||
if($itm[0]['attach']) {
|
||||
$j = json_decode($itm[0]['attach'],true);
|
||||
if($j) {
|
||||
foreach($j as $jj) {
|
||||
$itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$mimetype = $itm[0]['mimetype'];
|
||||
|
||||
$content = $itm[0]['body'];
|
||||
|
||||
|
||||
|
||||
$rp = 'cards/' . $channel['channel_address'];
|
||||
|
||||
$x = array(
|
||||
'nickname' => $channel['channel_address'],
|
||||
'bbco_autocomplete'=> 'bbcode',
|
||||
'return_path' => $rp,
|
||||
'webpage' => ITEM_TYPE_CARD,
|
||||
'button' => t('Edit'),
|
||||
'writefiles' => perm_is_allowed($owner, get_observer_hash(), 'write_pages'),
|
||||
'weblink' => t('Insert web link'),
|
||||
'hide_voting' => false,
|
||||
'hide_future' => false,
|
||||
'hide_location' => false,
|
||||
'hide_expire' => false,
|
||||
'showacl' => true,
|
||||
'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')),
|
||||
'permissions' => $itm[0],
|
||||
'lockstate' => (($itm[0]['allow_cid'] || $itm[0]['allow_gid'] || $itm[0]['deny_cid'] || $itm[0]['deny_gid']) ? 'lock' : 'unlock'),
|
||||
'ptyp' => $itm[0]['type'],
|
||||
'mimeselect' => false,
|
||||
'mimetype' => $itm[0]['mimetype'],
|
||||
'body' => undo_post_tagging($content),
|
||||
'post_id' => $post_id,
|
||||
'visitor' => true,
|
||||
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
|
||||
'summary' => htmlspecialchars($itm[0]['summary'],ENT_COMPAT,'UTF-8'),
|
||||
'placeholdertitle' => t('Title (optional)'),
|
||||
'pagetitle' => $card_title,
|
||||
'profile_uid' => (intval($channel['channel_id'])),
|
||||
'catsenabled' => $catsenabled,
|
||||
'category' => $category,
|
||||
'bbcode' => (($mimetype == 'text/bbcode') ? true : false)
|
||||
);
|
||||
|
||||
$editor = status_editor($a, $x, false, 'Card_edit');
|
||||
|
||||
$o .= replace_macros(get_markup_template('edpost_head.tpl'), array(
|
||||
'$title' => t('Edit Card'),
|
||||
'$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false),
|
||||
'$id' => $itm[0]['id'],
|
||||
'$cancel' => t('Cancel'),
|
||||
'$editor' => $editor
|
||||
));
|
||||
|
||||
return $o;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\PermissionDescription;
|
||||
|
||||
require_once('include/channel.php');
|
||||
require_once('include/conversation.php');
|
||||
require_once('include/acl_selectors.php');
|
||||
|
||||
/**
|
||||
* @brief Provides the Cards module.
|
||||
*
|
||||
*/
|
||||
class Cards extends Controller {
|
||||
|
||||
public function init() {
|
||||
|
||||
if(argc() > 1)
|
||||
$which = argv(1);
|
||||
else
|
||||
return;
|
||||
|
||||
profile_load($which);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \\Zotlabs\\Web\\Controller::get()
|
||||
*
|
||||
* @return string Parsed HTML from template 'cards.tpl'
|
||||
*/
|
||||
public function get($update = 0, $load = false) {
|
||||
|
||||
if(observer_prohibited(true)) {
|
||||
return login();
|
||||
}
|
||||
|
||||
if(! App::$profile) {
|
||||
notice( t('Requested profile is not available.') . EOL );
|
||||
App::$error = 404;
|
||||
return;
|
||||
}
|
||||
|
||||
if(! Apps::system_app_installed(App::$profile_uid, 'Cards')) {
|
||||
//Do not display any associated widgets at this point
|
||||
App::$pdl = '';
|
||||
$papp = Apps::get_papp('Cards');
|
||||
return Apps::app_render($papp, 'module');
|
||||
}
|
||||
|
||||
nav_set_selected('Cards');
|
||||
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/json+oembed',
|
||||
'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string),
|
||||
'title' => 'oembed'
|
||||
]);
|
||||
|
||||
|
||||
$category = (($_REQUEST['cat']) ? escape_tags(trim($_REQUEST['cat'])) : '');
|
||||
|
||||
if($category) {
|
||||
$sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'], 'item', $category, TERM_CATEGORY));
|
||||
}
|
||||
|
||||
|
||||
$which = argv(1);
|
||||
|
||||
$selected_card = ((argc() > 2) ? argv(2) : '');
|
||||
|
||||
$_SESSION['return_url'] = App::$query_string;
|
||||
|
||||
$uid = local_channel();
|
||||
$owner = App::$profile_uid;
|
||||
$observer = App::get_observer();
|
||||
|
||||
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
|
||||
if(! perm_is_allowed($owner, $ob_hash, 'view_pages')) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$is_owner = ($uid && $uid == $owner);
|
||||
|
||||
$channel = channelx_by_n($owner);
|
||||
|
||||
if($channel) {
|
||||
$channel_acl = [
|
||||
'allow_cid' => $channel['channel_allow_cid'],
|
||||
'allow_gid' => $channel['channel_allow_gid'],
|
||||
'deny_cid' => $channel['channel_deny_cid'],
|
||||
'deny_gid' => $channel['channel_deny_gid']
|
||||
];
|
||||
}
|
||||
else {
|
||||
$channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ];
|
||||
}
|
||||
|
||||
|
||||
if(perm_is_allowed($owner, $ob_hash, 'write_pages')) {
|
||||
|
||||
$x = [
|
||||
'webpage' => ITEM_TYPE_CARD,
|
||||
'is_owner' => true,
|
||||
'content_label' => t('Add Card'),
|
||||
'button' => t('Save'),
|
||||
'nickname' => $channel['channel_address'],
|
||||
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid']
|
||||
|| $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
|
||||
'acl' => (($is_owner) ? populate_acl($channel_acl, false,
|
||||
PermissionDescription::fromGlobalPermission('view_pages')) : ''),
|
||||
'permissions' => $channel_acl,
|
||||
'showacl' => (($is_owner) ? true : false),
|
||||
'visitor' => true,
|
||||
'hide_location' => false,
|
||||
'hide_voting' => false,
|
||||
'profile_uid' => intval($owner),
|
||||
'mimetype' => 'text/bbcode',
|
||||
'mimeselect' => false,
|
||||
'layoutselect' => false,
|
||||
'expanded' => false,
|
||||
'novoting' => false,
|
||||
'catsenabled' => feature_enabled($owner, 'categories'),
|
||||
'bbco_autocomplete' => 'bbcode',
|
||||
'bbcode' => true
|
||||
];
|
||||
|
||||
if($_REQUEST['title'])
|
||||
$x['title'] = $_REQUEST['title'];
|
||||
if($_REQUEST['body'])
|
||||
$x['body'] = $_REQUEST['body'];
|
||||
|
||||
$editor = status_editor($a, $x, false, 'Cards');
|
||||
}
|
||||
else {
|
||||
$editor = '';
|
||||
}
|
||||
|
||||
|
||||
$itemspage = get_pconfig(local_channel(),'system','itemspage');
|
||||
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10));
|
||||
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start']));
|
||||
|
||||
|
||||
$sql_extra = item_permissions_sql($owner);
|
||||
$sql_item = '';
|
||||
|
||||
if($selected_card) {
|
||||
$r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.v = '%s' limit 1",
|
||||
dbesc($selected_card)
|
||||
);
|
||||
if($r) {
|
||||
$sql_item = "and item.id = " . intval($r[0]['iid']) . " ";
|
||||
}
|
||||
}
|
||||
|
||||
$r = q("select * from item
|
||||
where uid = %d and item_type = %d
|
||||
$sql_extra $sql_item order by item.created desc $pager_sql",
|
||||
intval($owner),
|
||||
intval(ITEM_TYPE_CARD)
|
||||
);
|
||||
|
||||
$item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0
|
||||
and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
|
||||
and item.item_blocked = 0 ";
|
||||
|
||||
$items_result = [];
|
||||
if($r) {
|
||||
|
||||
$pager_total = count($r);
|
||||
|
||||
$parents_str = ids_to_querystr($r, 'id');
|
||||
|
||||
$items = q("SELECT item.*, item.id AS item_id
|
||||
FROM item
|
||||
WHERE item.uid = %d $item_normal
|
||||
AND item.parent IN ( %s )
|
||||
$sql_extra $sql_extra2 ",
|
||||
intval(App::$profile['profile_uid']),
|
||||
dbesc($parents_str)
|
||||
);
|
||||
if($items) {
|
||||
xchan_query($items);
|
||||
$items = fetch_post_tags($items, true);
|
||||
$items_result = conv_sort($items, 'updated');
|
||||
}
|
||||
}
|
||||
|
||||
$mode = 'cards';
|
||||
|
||||
if(get_pconfig(local_channel(),'system','articles_list_mode') && (! $selected_card))
|
||||
$page_mode = 'pager_list';
|
||||
else
|
||||
$page_mode = 'traditional';
|
||||
|
||||
$content = conversation($items_result, $mode, false, $page_mode);
|
||||
|
||||
$o = replace_macros(get_markup_template('cards.tpl'), [
|
||||
'$title' => t('Cards'),
|
||||
'$editor' => $editor,
|
||||
'$content' => $content,
|
||||
'$pager' => alt_pager($pager_total)
|
||||
]);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -200,7 +200,7 @@ class Cdav extends Controller {
|
||||
$etag = (isset($_SERVER['HTTP_IF_MATCH']) ? $_SERVER['HTTP_IF_MATCH'] : false);
|
||||
|
||||
// delete
|
||||
if($httpmethod === 'DELETE' && $cdavdata['etag'] == $etag) {
|
||||
if($httpmethod === 'DELETE' && $etag && isset($cdavdata['etag']) && $cdavdata['etag'] == $etag) {
|
||||
Libsync::build_sync_packet($channel['channel_id'], [
|
||||
$sync => [
|
||||
'action' => 'delete_card',
|
||||
@@ -210,7 +210,7 @@ class Cdav extends Controller {
|
||||
]);
|
||||
}
|
||||
else {
|
||||
if($etag && $cdavdata['etag'] !== $etag) {
|
||||
if($etag && isset($cdavdata['etag']) && $cdavdata['etag'] !== $etag) {
|
||||
// update
|
||||
Libsync::build_sync_packet($channel['channel_id'], [
|
||||
$sync => [
|
||||
@@ -317,7 +317,7 @@ class Cdav extends Controller {
|
||||
$calendars = $caldavBackend->getCalendarsForUser($principalUri);
|
||||
|
||||
//create new calendar
|
||||
if($_REQUEST['{DAV:}displayname'] && $_REQUEST['create']) {
|
||||
if((isset($_REQUEST['{DAV:}displayname']) && $_REQUEST['{DAV:}displayname']) && (isset($_REQUEST['create']) && $_REQUEST['create'])) {
|
||||
do {
|
||||
$duplicate = false;
|
||||
$calendarUri = random_string(40);
|
||||
@@ -332,9 +332,9 @@ class Cdav extends Controller {
|
||||
} while ($duplicate == true);
|
||||
|
||||
$properties = [
|
||||
'{DAV:}displayname' => $_REQUEST['{DAV:}displayname'],
|
||||
'{http://apple.com/ns/ical/}calendar-color' => $_REQUEST['color'],
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-description' => $channel['channel_name']
|
||||
'{DAV:}displayname' => escape_tags($_REQUEST['{DAV:}displayname']),
|
||||
'{http://apple.com/ns/ical/}calendar-color' => escape_tags($_REQUEST['color']),
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-description' => escape_tags($channel['channel_name'])
|
||||
];
|
||||
|
||||
$id = $caldavBackend->createCalendar($principalUri, $calendarUri, $properties);
|
||||
@@ -352,7 +352,7 @@ class Cdav extends Controller {
|
||||
}
|
||||
|
||||
//create new calendar object via ajax request
|
||||
if($_REQUEST['submit'] === 'create_event' && $_REQUEST['title'] && $_REQUEST['target'] && $_REQUEST['dtstart']) {
|
||||
if((isset($_REQUEST['submit']) && $_REQUEST['submit'] === 'create_event') && $_REQUEST['title'] && $_REQUEST['target'] && $_REQUEST['dtstart']) {
|
||||
|
||||
$id = explode(':', $_REQUEST['target']);
|
||||
|
||||
@@ -366,7 +366,7 @@ class Cdav extends Controller {
|
||||
|
||||
$allday = $_REQUEST['allday'];
|
||||
|
||||
$title = $_REQUEST['title'];
|
||||
$title = escape_tags($_REQUEST['title']);
|
||||
$start = datetime_convert('UTC', 'UTC', $_REQUEST['dtstart']);
|
||||
$dtstart = new \DateTime($start);
|
||||
|
||||
@@ -374,8 +374,8 @@ class Cdav extends Controller {
|
||||
$end = datetime_convert('UTC', 'UTC', $_REQUEST['dtend']);
|
||||
$dtend = new \DateTime($end);
|
||||
}
|
||||
$description = $_REQUEST['description'];
|
||||
$location = $_REQUEST['location'];
|
||||
$description = escape_tags($_REQUEST['description']);
|
||||
$location = escape_tags($_REQUEST['location']);
|
||||
|
||||
do {
|
||||
$duplicate = false;
|
||||
@@ -431,7 +431,7 @@ class Cdav extends Controller {
|
||||
}
|
||||
|
||||
//edit calendar name and color
|
||||
if($_REQUEST['{DAV:}displayname'] && $_REQUEST['edit'] && $_REQUEST['id']) {
|
||||
if((isset($_REQUEST['{DAV:}displayname']) && $_REQUEST['{DAV:}displayname']) && $_REQUEST['edit'] && $_REQUEST['id']) {
|
||||
|
||||
$id = explode(':', $_REQUEST['id']);
|
||||
|
||||
@@ -441,8 +441,8 @@ class Cdav extends Controller {
|
||||
$cdavdata = $this->get_cdav_data($id[0], 'calendarinstances');
|
||||
|
||||
$mutations = [
|
||||
'{DAV:}displayname' => $_REQUEST['{DAV:}displayname'],
|
||||
'{http://apple.com/ns/ical/}calendar-color' => $_REQUEST['color']
|
||||
'{DAV:}displayname' => escape_tags($_REQUEST['{DAV:}displayname']),
|
||||
'{http://apple.com/ns/ical/}calendar-color' => escape_tags($_REQUEST['color'])
|
||||
];
|
||||
|
||||
$patch = new \Sabre\DAV\PropPatch($mutations);
|
||||
@@ -459,7 +459,7 @@ class Cdav extends Controller {
|
||||
}
|
||||
|
||||
//edit calendar object via ajax request
|
||||
if($_REQUEST['submit'] === 'update_event' && $_REQUEST['uri'] && $_REQUEST['title'] && $_REQUEST['target'] && $_REQUEST['dtstart']) {
|
||||
if((isset($_REQUEST['submit']) && $_REQUEST['submit'] === 'update_event') && $_REQUEST['uri'] && $_REQUEST['title'] && $_REQUEST['target'] && $_REQUEST['dtstart']) {
|
||||
|
||||
$id = explode(':', $_REQUEST['target']);
|
||||
|
||||
@@ -471,18 +471,18 @@ class Cdav extends Controller {
|
||||
$timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : '');
|
||||
$tz = (($timezone) ? $timezone : date_default_timezone_get());
|
||||
|
||||
$allday = $_REQUEST['allday'];
|
||||
$allday = intval($_REQUEST['allday']);
|
||||
|
||||
$uri = $_REQUEST['uri'];
|
||||
$title = $_REQUEST['title'];
|
||||
$uri = escape_tags($_REQUEST['uri']);
|
||||
$title = escape_tags($_REQUEST['title']);
|
||||
$start = datetime_convert('UTC', 'UTC', $_REQUEST['dtstart']);
|
||||
$dtstart = new \DateTime($start);
|
||||
if($_REQUEST['dtend']) {
|
||||
$end = datetime_convert('UTC', 'UTC', $_REQUEST['dtend']);
|
||||
$dtend = new \DateTime($end);
|
||||
}
|
||||
$description = $_REQUEST['description'];
|
||||
$location = $_REQUEST['location'];
|
||||
$description = escape_tags($_REQUEST['description']);
|
||||
$location = escape_tags($_REQUEST['location']);
|
||||
|
||||
$object = $caldavBackend->getCalendarObject($id, $uri);
|
||||
|
||||
@@ -654,7 +654,7 @@ class Cdav extends Controller {
|
||||
$duplicate = true;
|
||||
} while ($duplicate == true);
|
||||
|
||||
$properties = ['{DAV:}displayname' => $_REQUEST['{DAV:}displayname']];
|
||||
$properties = ['{DAV:}displayname' => escape_tags($_REQUEST['{DAV:}displayname'])];
|
||||
|
||||
$carddavBackend->createAddressBook($principalUri, $addressbookUri, $properties);
|
||||
|
||||
@@ -668,9 +668,9 @@ class Cdav extends Controller {
|
||||
}
|
||||
|
||||
//edit addressbook
|
||||
if($_REQUEST['{DAV:}displayname'] && $_REQUEST['edit'] && intval($_REQUEST['id'])) {
|
||||
if($_REQUEST['{DAV:}displayname'] && $_REQUEST['edit'] && $_REQUEST['id']) {
|
||||
|
||||
$id = $_REQUEST['id'];
|
||||
$id = intval($_REQUEST['id']);
|
||||
|
||||
if(! cdav_perms($id,$addressbooks))
|
||||
return;
|
||||
@@ -678,7 +678,7 @@ class Cdav extends Controller {
|
||||
$cdavdata = $this->get_cdav_data($id, 'addressbooks');
|
||||
|
||||
$mutations = [
|
||||
'{DAV:}displayname' => $_REQUEST['{DAV:}displayname']
|
||||
'{DAV:}displayname' => escape_tags($_REQUEST['{DAV:}displayname'])
|
||||
];
|
||||
|
||||
$patch = new \Sabre\DAV\PropPatch($mutations);
|
||||
@@ -901,19 +901,10 @@ class Cdav extends Controller {
|
||||
|
||||
//Display calendar(s) here
|
||||
if(argc() <= 3 && argv(1) === 'calendar') {
|
||||
|
||||
head_add_css('/library/fullcalendar/packages/core/main.min.css');
|
||||
head_add_css('/library/fullcalendar/packages/daygrid/main.min.css');
|
||||
head_add_css('/library/fullcalendar/packages/timegrid/main.min.css');
|
||||
head_add_css('/library/fullcalendar/packages/list/main.min.css');
|
||||
head_add_js('/library/fullcalendar/dist/index.global.js');
|
||||
head_add_css('cdav_calendar.css');
|
||||
|
||||
head_add_js('/library/fullcalendar/packages/core/main.min.js');
|
||||
head_add_js('/library/fullcalendar/packages/interaction/main.min.js');
|
||||
head_add_js('/library/fullcalendar/packages/daygrid/main.min.js');
|
||||
head_add_js('/library/fullcalendar/packages/timegrid/main.min.js');
|
||||
head_add_js('/library/fullcalendar/packages/list/main.min.js');
|
||||
|
||||
$o = '';
|
||||
$sources = '';
|
||||
$resource_id = '';
|
||||
$resource = null;
|
||||
@@ -921,6 +912,8 @@ class Cdav extends Controller {
|
||||
if(argc() == 3)
|
||||
$resource_id = argv(2);
|
||||
|
||||
$categories = '';
|
||||
|
||||
if($resource_id) {
|
||||
$r = q("SELECT event.*, item.author_xchan, item.owner_xchan, item.plink, item.id as item_id FROM event LEFT JOIN item ON event.event_hash = item.resource_id
|
||||
WHERE event.uid = %d AND event.event_hash = '%s' LIMIT 1",
|
||||
@@ -944,7 +937,6 @@ class Cdav extends Controller {
|
||||
$resource = $r[0];
|
||||
|
||||
$catsenabled = feature_enabled(local_channel(),'categories');
|
||||
$categories = '';
|
||||
if($catsenabled){
|
||||
if($r[0]['term']) {
|
||||
$cats = get_terms_oftype($r[0]['term'], TERM_CATEGORY);
|
||||
@@ -1095,7 +1087,11 @@ class Cdav extends Controller {
|
||||
|
||||
$filters['name'] = 'VCALENDAR';
|
||||
$filters['prop-filters'][0]['name'] = 'VEVENT';
|
||||
$filters['prop-filters'][0]['is-not-defined'] = null;
|
||||
$filters['prop-filters'][0]['param-filters'] = null;
|
||||
$filters['prop-filters'][0]['text-match'] = null;
|
||||
$filters['comp-filters'][0]['name'] = 'VEVENT';
|
||||
$filters['comp-filters'][0]['is-not-defined'] = null;
|
||||
$filters['comp-filters'][0]['time-range']['start'] = $start;
|
||||
$filters['comp-filters'][0]['time-range']['end'] = $end;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user