mirror of
https://framagit.org/hubzilla/core.git
synced 2026-06-21 00:52:33 -04:00
Compare commits
2696 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a2e5d480c | ||
|
|
26e851ff7f | ||
|
|
89ec043ce1 | ||
|
|
98f0272a84 | ||
|
|
0933925a2b | ||
|
|
e92929fce4 | ||
|
|
f388412c48 | ||
|
|
836de7f1a5 | ||
|
|
43008aa42b | ||
|
|
7618d8301a | ||
|
|
09cfc979e4 | ||
|
|
00fc5541db | ||
|
|
954d06bdb7 | ||
|
|
12828ee4d4 | ||
|
|
c2e007a839 | ||
|
|
605bf3b9d3 | ||
|
|
ebd0333256 | ||
|
|
d7dcf192c7 | ||
|
|
2ad6f6e11f | ||
|
|
36a814b03e | ||
|
|
92bc99a805 | ||
|
|
b5ce207344 | ||
|
|
d04ff264fa | ||
|
|
9ad2b017e2 | ||
|
|
1fd5f5c893 | ||
|
|
43260891b7 | ||
|
|
aef0350346 | ||
|
|
83fbb0678c | ||
|
|
df3778c64f | ||
|
|
0dc09ea238 | ||
|
|
f085c3c98f | ||
|
|
8d4b1ba7d4 | ||
|
|
b3c543265d | ||
|
|
6eee404bb1 | ||
|
|
ec4226b5de | ||
|
|
8266a1490a | ||
|
|
56554710ea | ||
|
|
6a101e6260 | ||
|
|
f12038e63e | ||
|
|
ad2bf187bb | ||
|
|
4cbd97e409 | ||
|
|
e67c780afd | ||
|
|
0cbdeb7bf1 | ||
|
|
6be464ef84 | ||
|
|
54845a7627 | ||
|
|
2f6da0be7e | ||
|
|
cf87a5af3b | ||
|
|
207057c92d | ||
|
|
5300f180db | ||
|
|
ec7166eb00 | ||
|
|
36f041a1ff | ||
|
|
0dd2e9004d | ||
|
|
94f1c001f1 | ||
|
|
531a03562d | ||
|
|
3699f78e95 | ||
|
|
ebd2283b38 | ||
|
|
a7788570e5 | ||
|
|
2932ac9d86 | ||
|
|
888c211f44 | ||
|
|
61cf92ebdd | ||
|
|
80ec45e4f9 | ||
|
|
9e9851681f | ||
|
|
8394d4857c | ||
|
|
c700a017a0 | ||
|
|
c19eb94c52 | ||
|
|
597f24ca49 | ||
|
|
eb793b1601 | ||
|
|
07525c5b45 | ||
|
|
35df50c39d | ||
|
|
406ca20634 | ||
|
|
021e7861b8 | ||
|
|
5dfe4ef6f8 | ||
|
|
60a0fdc76b | ||
|
|
cd760454a5 | ||
|
|
c7144dbf96 | ||
|
|
c31e203104 | ||
|
|
6feefc5ce0 | ||
|
|
1d7f9e05ed | ||
|
|
6a858b29fd | ||
|
|
3f0e687558 | ||
|
|
ba412bc6cf | ||
|
|
31abcac4b4 | ||
|
|
d7f04ff6ee | ||
|
|
76fd81fad1 | ||
|
|
1cfcffc510 | ||
|
|
eaf9003ca2 | ||
|
|
751d6fe8a4 | ||
|
|
20ca6676d2 | ||
|
|
eded0f6c09 | ||
|
|
b5049651ad | ||
|
|
c95a6fe1e5 | ||
|
|
cd97c32444 | ||
|
|
70b204cb3f | ||
|
|
d2f1edfad8 | ||
|
|
2ddfcd5222 | ||
|
|
787ee411ce | ||
|
|
200d3577fa | ||
|
|
8ab99747f5 | ||
|
|
88f7c2041d | ||
|
|
43cb21329f | ||
|
|
b8a5f5fbf2 | ||
|
|
7ccd7b439f | ||
|
|
f0eceb03ed | ||
|
|
c2b9fc1a49 | ||
|
|
4d3a555b53 | ||
|
|
e35ab97b7e | ||
|
|
f9793e870f | ||
|
|
e48fedd526 | ||
|
|
b899ed3d64 | ||
|
|
e4adb881cb | ||
|
|
aa2450fae1 | ||
|
|
d9245566f5 | ||
|
|
16cc695115 | ||
|
|
f0e5ce7fd1 | ||
|
|
ea721d380b | ||
|
|
e193b6d870 | ||
|
|
766fc92a3b | ||
|
|
df6f2abfbe | ||
|
|
684245f24d | ||
|
|
01b081d809 | ||
|
|
a34d8852b6 | ||
|
|
18b6d48944 | ||
|
|
f3fa09fc91 | ||
|
|
cb2d8b4ba6 | ||
|
|
a9da370c0b | ||
|
|
b5620cb794 | ||
|
|
cf62e07bec | ||
|
|
ec4526b5f4 | ||
|
|
806f50eee3 | ||
|
|
6956eadaad | ||
|
|
9542c1d63c | ||
|
|
81bb02afb7 | ||
|
|
b8abf806ca | ||
|
|
0d10f5ba65 | ||
|
|
89409d0577 | ||
|
|
144283ca1c | ||
|
|
c56e2953cf | ||
|
|
e1d40348ce | ||
|
|
4def370980 | ||
|
|
54df52675b | ||
|
|
12ebdf27c0 | ||
|
|
de0f7ea5d4 | ||
|
|
72151bc123 | ||
|
|
8889abd5a4 | ||
|
|
7f7dcd2f33 | ||
|
|
95c3b1696e | ||
|
|
87cd1f87f4 | ||
|
|
21c4976a3a | ||
|
|
ec91e51142 | ||
|
|
f1c798e9ac | ||
|
|
f6996f4999 | ||
|
|
9ac5504377 | ||
|
|
91f3c722d6 | ||
|
|
31cf2c688c | ||
|
|
81d3ba5151 | ||
|
|
19daadbfd7 | ||
|
|
0acb371f62 | ||
|
|
18d990a034 | ||
|
|
85b6e352d4 | ||
|
|
c389aee112 | ||
|
|
725e57a27a | ||
|
|
9aab47de07 | ||
|
|
e6a6723d1f | ||
|
|
6e7c7771bd | ||
|
|
c609fc71bd | ||
|
|
b50f1657c3 | ||
|
|
bc1cc65ff2 | ||
|
|
697e59f9a4 | ||
|
|
61d6239c68 | ||
|
|
eba69f85c7 | ||
|
|
19b96e37fb | ||
|
|
14186f5c18 | ||
|
|
44593a3c8d | ||
|
|
4964a32c55 | ||
|
|
3d4ad94dcc | ||
|
|
6d5f98072d | ||
|
|
1a15c775f8 | ||
|
|
2d716b74b9 | ||
|
|
cc9fd4f4a4 | ||
|
|
43c5b72317 | ||
|
|
2856ea6370 | ||
|
|
67d65d878d | ||
|
|
e19bb63857 | ||
|
|
dbc5e54a92 | ||
|
|
b8913335b1 | ||
|
|
872ac8846e | ||
|
|
8c16171855 | ||
|
|
2e47ef38a9 | ||
|
|
81b4d919e5 | ||
|
|
f6d88f20f3 | ||
|
|
598c3aa336 | ||
|
|
ccefb99cbe | ||
|
|
35ce7dbeab | ||
|
|
74610aca6f | ||
|
|
02d4c5ac90 | ||
|
|
7f35dda5cc | ||
|
|
13355d42f7 | ||
|
|
77e82d7475 | ||
|
|
da2df4eb55 | ||
|
|
62fbdf3f63 | ||
|
|
554745a25a | ||
|
|
e12c0ca3dc | ||
|
|
a18e297a26 | ||
|
|
2d82e1bd6b | ||
|
|
7476d8dccc | ||
|
|
04b96e2cdc | ||
|
|
7375824ed4 | ||
|
|
06d47deef8 | ||
|
|
85d000e792 | ||
|
|
3ac27d8004 | ||
|
|
854a6e3787 | ||
|
|
a36dd5a8b9 | ||
|
|
7c620cbe24 | ||
|
|
7472b96c95 | ||
|
|
3ba42d3dcf | ||
|
|
eb3a40b07a | ||
|
|
f0cce1c902 | ||
|
|
9f26b7aa9c | ||
|
|
febf766be0 | ||
|
|
35d9fd4860 | ||
|
|
4581abb6d1 | ||
|
|
8d3c5114ba | ||
|
|
80b6954c29 | ||
|
|
da37548e2e | ||
|
|
c658bbca24 | ||
|
|
3b9a9db664 | ||
|
|
4e921cfdcf | ||
|
|
bd24224b76 | ||
|
|
a5ac388889 | ||
|
|
4be123dc84 | ||
|
|
0ebb1f685f | ||
|
|
805a25eb62 | ||
|
|
4ca12afc9d | ||
|
|
6cfa2572a8 | ||
|
|
04ec986bf7 | ||
|
|
ee453c4acc | ||
|
|
4d3524ba9d | ||
|
|
29beea28c2 | ||
|
|
e2a289d614 | ||
|
|
59d3483a06 | ||
|
|
a99161abdf | ||
|
|
79d48dc92b | ||
|
|
becb898a51 | ||
|
|
268464ccde | ||
|
|
57aa96c412 | ||
|
|
cb74de7472 | ||
|
|
d35849c418 | ||
|
|
a9a112e063 | ||
|
|
1e87fe6b49 | ||
|
|
ed64eba13a | ||
|
|
0fbd0ca416 | ||
|
|
0a31267b1b | ||
|
|
97c7b010ed | ||
|
|
b6d30f6734 | ||
|
|
b9e1c38773 | ||
|
|
22d769ecae | ||
|
|
45ecd1b127 | ||
|
|
3f053611bd | ||
|
|
1582b8bc96 | ||
|
|
432e7e9714 | ||
|
|
31e237729c | ||
|
|
d2b03f6a9b | ||
|
|
51fe5ec982 | ||
|
|
ce9b01ce84 | ||
|
|
024b489af9 | ||
|
|
f980c2e3de | ||
|
|
db0e1c9f31 | ||
|
|
260c518562 | ||
|
|
689603de82 | ||
|
|
b51049227f | ||
|
|
0a86efc631 | ||
|
|
f8467845d2 | ||
|
|
9098bb431c | ||
|
|
86ed4f4f99 | ||
|
|
7795224e70 | ||
|
|
b729cee9e4 | ||
|
|
15bc5c64f3 | ||
|
|
440a132c6b | ||
|
|
f8447521a8 | ||
|
|
c8050ea865 | ||
|
|
11d831e4d7 | ||
|
|
15faf01ec9 | ||
|
|
82192d1ead | ||
|
|
7ee495624e | ||
|
|
64bbbc8053 | ||
|
|
4d3d868d19 | ||
|
|
45cc2d7bd8 | ||
|
|
15b45af550 | ||
|
|
91ebfbc215 | ||
|
|
011342ac1b | ||
|
|
5577383a48 | ||
|
|
dc336010db | ||
|
|
88fbeaddb0 | ||
|
|
7612d8246e | ||
|
|
53bb16e799 | ||
|
|
ed04104250 | ||
|
|
ee40cb337a | ||
|
|
bdae290ec4 | ||
|
|
53c3d0d53d | ||
|
|
ed981ec8e8 | ||
|
|
723d757091 | ||
|
|
92ada6eb98 | ||
|
|
975d8ef0c7 | ||
|
|
65e9ff357b | ||
|
|
66495e6838 | ||
|
|
efe65e1ba8 | ||
|
|
6b500ee3e1 | ||
|
|
1ad6308f97 | ||
|
|
5d82bf946e | ||
|
|
c8fe56e57d | ||
|
|
88b6353465 | ||
|
|
4afddabe15 | ||
|
|
360ae9080e | ||
|
|
956aec1448 | ||
|
|
4bcd093bef | ||
|
|
f62b294a72 | ||
|
|
c2adf1dcd1 | ||
|
|
0932d2a0a6 | ||
|
|
e451284256 | ||
|
|
49f12695cf | ||
|
|
8d26961639 | ||
|
|
c9ec5043b9 | ||
|
|
03bfb8a46f | ||
|
|
0c5a7ab19b | ||
|
|
1561e4a89c | ||
|
|
b09cbb72fb | ||
|
|
d57f4a9527 | ||
|
|
0e015d52a5 | ||
|
|
7e0416ac97 | ||
|
|
98112497ba | ||
|
|
f952d65cfd | ||
|
|
182dec3e57 | ||
|
|
8ab6076566 | ||
|
|
6506cab55b | ||
|
|
97e483d684 | ||
|
|
fe638c88e0 | ||
|
|
951e9c8c4f | ||
|
|
89415e1731 | ||
|
|
f9ab020d1d | ||
|
|
1828ef6b68 | ||
|
|
4903d7b1e9 | ||
|
|
c67fdd9480 | ||
|
|
9b5979c83a | ||
|
|
7460b1eecb | ||
|
|
7d03ff2043 | ||
|
|
1da494a2a5 | ||
|
|
00ccafc90d | ||
|
|
b767e7ddec | ||
|
|
31da1362de | ||
|
|
e0c619519f | ||
|
|
f1f1e56f87 | ||
|
|
33e4b6db72 | ||
|
|
1eae7b92d1 | ||
|
|
5b7a10401f | ||
|
|
1b8c5b9727 | ||
|
|
548936c6ca | ||
|
|
7beb500fb4 | ||
|
|
9c110e7b9b | ||
|
|
129f8107d3 | ||
|
|
cb01996039 | ||
|
|
33b738d00b | ||
|
|
cf7f380568 | ||
|
|
7e36727ce6 | ||
|
|
c2dc3e8dec | ||
|
|
7d1599f9b0 | ||
|
|
3d264f5a55 | ||
|
|
9389abdb75 | ||
|
|
f8f15f526f | ||
|
|
48bae9d421 | ||
|
|
c29261487c | ||
|
|
c3229643d0 | ||
|
|
2b03e51bfc | ||
|
|
4cc72db463 | ||
|
|
d434490bdf | ||
|
|
2c15efbf48 | ||
|
|
c26dede97f | ||
|
|
1d899d387e | ||
|
|
ea3390d626 | ||
|
|
a04ded9cca | ||
|
|
55b4eb7b22 | ||
|
|
fbb1d6aa41 | ||
|
|
5440a65607 | ||
|
|
36c9f9abff | ||
|
|
8295ccdda7 | ||
|
|
65892ba555 | ||
|
|
78938df133 | ||
|
|
ac6dec91f1 | ||
|
|
373612a046 | ||
|
|
c1afa306c9 | ||
|
|
65a20a780d | ||
|
|
b29e121113 | ||
|
|
f45ebfa223 | ||
|
|
a033c439f3 | ||
|
|
66e4bc327f | ||
|
|
df362e4f81 | ||
|
|
82ee980172 | ||
|
|
463a8d338e | ||
|
|
d074bb65da | ||
|
|
b7c411470b | ||
|
|
e09e6c5524 | ||
|
|
fa73c8d15e | ||
|
|
6060ca127b | ||
|
|
ca4e0351cf | ||
|
|
f3be2ecc9d | ||
|
|
135305d975 | ||
|
|
34aa8ba3cc | ||
|
|
28fe91dfa7 | ||
|
|
883b519714 | ||
|
|
9dae590c64 | ||
|
|
d4159e8333 | ||
|
|
90d04082a1 | ||
|
|
a84c7271f2 | ||
|
|
f46ddf2273 | ||
|
|
b401869d30 | ||
|
|
cc55716879 | ||
|
|
ddad4f604b | ||
|
|
dc553ab309 | ||
|
|
a2d170385b | ||
|
|
a275726988 | ||
|
|
863d4d83a1 | ||
|
|
d50381c9c6 | ||
|
|
c9bbe1a735 | ||
|
|
18b9d1efd8 | ||
|
|
85f1845d15 | ||
|
|
c34fc416a2 | ||
|
|
7794ee5a88 | ||
|
|
65068479b9 | ||
|
|
655ae9d1cd | ||
|
|
76163fc37b | ||
|
|
92f3c5ae21 | ||
|
|
c3ec5d4d6a | ||
|
|
0aeb4d6fb5 | ||
|
|
edd4ac791f | ||
|
|
27577824de | ||
|
|
281b2261af | ||
|
|
3eaf0b7f23 | ||
|
|
76ad2c0fa4 | ||
|
|
efaadf54d3 | ||
|
|
6f71c6d950 | ||
|
|
ad0b0364b8 | ||
|
|
e8c2e17bc9 | ||
|
|
6fae291cc8 | ||
|
|
1e48be7ab7 | ||
|
|
0cd4c34101 | ||
|
|
02401ea9fd | ||
|
|
18c8f1b903 | ||
|
|
4171a0136a | ||
|
|
ba29a6ce4d | ||
|
|
d72a096fa5 | ||
|
|
ed99392001 | ||
|
|
50e9a12ca5 | ||
|
|
d6b259bb27 | ||
|
|
f94b046333 | ||
|
|
5e8e6dc458 | ||
|
|
9751efb709 | ||
|
|
7b2f53cc69 | ||
|
|
22e0175ab7 | ||
|
|
0ffb09aa40 | ||
|
|
8054d7ad66 | ||
|
|
d3e70acacd | ||
|
|
6f520cadb7 | ||
|
|
becdd64257 | ||
|
|
464a0634d6 | ||
|
|
391db61629 | ||
|
|
e6450acc03 | ||
|
|
e75ae17662 | ||
|
|
3b3c93f9b3 | ||
|
|
b4693870ba | ||
|
|
5aee2f172e | ||
|
|
6b8b42fb21 | ||
|
|
320e95aaae | ||
|
|
1bcf84f275 | ||
|
|
a8e0bd1f12 | ||
|
|
eb05e5a205 | ||
|
|
d316d9436b | ||
|
|
b2e4a4b2b1 | ||
|
|
e1d622c49f | ||
|
|
8bb77bcfd6 | ||
|
|
f4ecc0dfb9 | ||
|
|
111ae9812c | ||
|
|
a9070382e7 | ||
|
|
9e22641425 | ||
|
|
19007dd8eb | ||
|
|
03e1f5f8a4 | ||
|
|
6bb73e14b6 | ||
|
|
41f84dabcc | ||
|
|
cd79d12e5b | ||
|
|
a6162d3134 | ||
|
|
ba73845bad | ||
|
|
7ece28b981 | ||
|
|
34b28cd8d3 | ||
|
|
9a170791e4 | ||
|
|
5799a073fe | ||
|
|
44b559fb1d | ||
|
|
2167d12b3f | ||
|
|
e312c381d8 | ||
|
|
af6f7f244e | ||
|
|
f3f49cf80f | ||
|
|
85b1bc7929 | ||
|
|
de36172af3 | ||
|
|
f5fba51f5c | ||
|
|
a2185c7886 | ||
|
|
08c9152abd | ||
|
|
70fa7ad8d0 | ||
|
|
cd081ac077 | ||
|
|
197338a727 | ||
|
|
61e7791d31 | ||
|
|
de91d2c804 | ||
|
|
11f5550512 | ||
|
|
d99611e7dd | ||
|
|
c0933c90e8 | ||
|
|
21b3ba38e7 | ||
|
|
bac87a8aec | ||
|
|
fae5515350 | ||
|
|
68d5969d33 | ||
|
|
a235917d48 | ||
|
|
d2eb10d7ff | ||
|
|
89bf71b227 | ||
|
|
3773bceb46 | ||
|
|
7686b48723 | ||
|
|
4ecb4189b8 | ||
|
|
840dbbe8ba | ||
|
|
ab4863a2e0 | ||
|
|
8c2442eca5 | ||
|
|
8b78e18fb8 | ||
|
|
66640a206e | ||
|
|
15c90371e0 | ||
|
|
00fe7bb1bb | ||
|
|
a2728167bc | ||
|
|
6326605c99 | ||
|
|
7cd4f60922 | ||
|
|
af719fea40 | ||
|
|
0af458768f | ||
|
|
3ba46873ba | ||
|
|
f4cc91f0f5 | ||
|
|
f813671b67 | ||
|
|
d0d6170a71 | ||
|
|
74f8f2d956 | ||
|
|
f0ee4c3cef | ||
|
|
6878445319 | ||
|
|
de34dac6cc | ||
|
|
1c8d298f3f | ||
|
|
777af6e7ad | ||
|
|
82dbdf7c70 | ||
|
|
4cf05891d7 | ||
|
|
d4198223bc | ||
|
|
1b4bbcc891 | ||
|
|
0e9d99c603 | ||
|
|
3ed444b4b4 | ||
|
|
cd98e75a42 | ||
|
|
77793e17c0 | ||
|
|
552796286e | ||
|
|
3f031399cb | ||
|
|
e486442eb1 | ||
|
|
2035828042 | ||
|
|
0a16674f6e | ||
|
|
4fbedb6750 | ||
|
|
9365b8691e | ||
|
|
6b0c61ac6b | ||
|
|
0a13c794ab | ||
|
|
a53f286467 | ||
|
|
e4ed5ed264 | ||
|
|
74441f2f00 | ||
|
|
007de17702 | ||
|
|
ecf2e4e0ee | ||
|
|
33825ba0b4 | ||
|
|
67db1c6e9b | ||
|
|
03910453a9 | ||
|
|
30962dadbf | ||
|
|
33951dc1e4 | ||
|
|
893847c649 | ||
|
|
fbbc53838c | ||
|
|
abdf6f40a2 | ||
|
|
7bc0340106 | ||
|
|
bfb9f10234 | ||
|
|
3f34c73f09 | ||
|
|
92f420f77c | ||
|
|
78716c42d6 | ||
|
|
e6aed4fb8e | ||
|
|
064effe5fd | ||
|
|
93ac3c985f | ||
|
|
523765b968 | ||
|
|
fe97b63e0b | ||
|
|
3836e75c89 | ||
|
|
28ae78c579 | ||
|
|
bdd6d878f1 | ||
|
|
8134e9cae0 | ||
|
|
70ee41f252 | ||
|
|
491dffd9b7 | ||
|
|
ad42890a0b | ||
|
|
5a971a3731 | ||
|
|
ab4455c54c | ||
|
|
6cca06f641 | ||
|
|
c5d37a0831 | ||
|
|
57645386b2 | ||
|
|
b242347fa1 | ||
|
|
15b2aa660b | ||
|
|
4da4f2367f | ||
|
|
9612a69a64 | ||
|
|
4e9d8e1a83 | ||
|
|
6083bfea2f | ||
|
|
08264f8d11 | ||
|
|
8b93136773 | ||
|
|
c7a82a6a84 | ||
|
|
ef67e18161 | ||
|
|
08d2fb4ed4 | ||
|
|
836637621d | ||
|
|
6e7a2d0d96 | ||
|
|
101005f3d2 | ||
|
|
8f88543478 | ||
|
|
f1ac5bb667 | ||
|
|
8e5df2dd22 | ||
|
|
441525750f | ||
|
|
9acfc44ac9 | ||
|
|
d5bd09b983 | ||
|
|
45350179b4 | ||
|
|
d0f3d2b2a5 | ||
|
|
3742fe80fc | ||
|
|
e191966e69 | ||
|
|
2c8e122008 | ||
|
|
2729f466d5 | ||
|
|
f4b71fbec1 | ||
|
|
6695225492 | ||
|
|
4f74d1d877 | ||
|
|
bee84e9b00 | ||
|
|
faaa2d4472 | ||
|
|
917c6caa14 | ||
|
|
cd59c67c67 | ||
|
|
37f1b774b7 | ||
|
|
828636847e | ||
|
|
06b5f71075 | ||
|
|
8d37a4239a | ||
|
|
a925b08e80 | ||
|
|
46aefd8883 | ||
|
|
38a48de826 | ||
|
|
254e30bea1 | ||
|
|
a16692eeb0 | ||
|
|
485a232ae6 | ||
|
|
d3f8118874 | ||
|
|
0d544e2294 | ||
|
|
cb3131a166 | ||
|
|
3105f514e4 | ||
|
|
395b427787 | ||
|
|
4d63c37c38 | ||
|
|
a2776ade81 | ||
|
|
5f5b3f3d4c | ||
|
|
60b76c53fc | ||
|
|
e55ea8b126 | ||
|
|
889581e35b | ||
|
|
f7222d43c9 | ||
|
|
1e474c9689 | ||
|
|
50fb658776 | ||
|
|
dde0f3a403 | ||
|
|
597d7bd532 | ||
|
|
aa27f93a9c | ||
|
|
ae4b5231bb | ||
|
|
28ef42a424 | ||
|
|
70233ea903 | ||
|
|
63c15c2c8d | ||
|
|
3e1f387b2b | ||
|
|
77c777512c | ||
|
|
02059fb663 | ||
|
|
3205429d24 | ||
|
|
aa8eb9522f | ||
|
|
5030157246 | ||
|
|
a3bc9251bf | ||
|
|
5a325cfa7b | ||
|
|
583d3b5580 | ||
|
|
43e3663721 | ||
|
|
9f8a512eb5 | ||
|
|
3bcb322156 | ||
|
|
1e645cabd4 | ||
|
|
ee82763d45 | ||
|
|
42812078c5 | ||
|
|
83f0c3d1dd | ||
|
|
e5a70744c0 | ||
|
|
5eefdc6485 | ||
|
|
4d2bcbc583 | ||
|
|
299c4bda1b | ||
|
|
f4b9291d42 | ||
|
|
b7afc905ec | ||
|
|
01bd846433 | ||
|
|
08f717d4fc | ||
|
|
b9fd87b004 | ||
|
|
1615f2c79a | ||
|
|
f430db0de7 | ||
|
|
32eb603643 | ||
|
|
c3f387f22a | ||
|
|
8703caff5f | ||
|
|
c5d50c9e47 | ||
|
|
fed4e3e03a | ||
|
|
9d1f73e179 | ||
|
|
ba164d9488 | ||
|
|
3fe67eb646 | ||
|
|
12ba2c30b9 | ||
|
|
7c1b41019e | ||
|
|
707110e5a7 | ||
|
|
ba9a9cb016 | ||
|
|
c1c75c4b67 | ||
|
|
0446349b89 | ||
|
|
915cb44601 | ||
|
|
17a153fb6d | ||
|
|
e339e897ff | ||
|
|
6b57f163bd | ||
|
|
4ace4819ff | ||
|
|
ce893122af | ||
|
|
fb5188a8c6 | ||
|
|
c17f452fc7 | ||
|
|
71b0f54b7a | ||
|
|
d387d021fe | ||
|
|
d6fefc3603 | ||
|
|
efb8a29b5f | ||
|
|
ff100a499a | ||
|
|
d89dc65330 | ||
|
|
fa41527f85 | ||
|
|
f48d844e42 | ||
|
|
898df6287a | ||
|
|
2522d42c71 | ||
|
|
39c5e85564 | ||
|
|
1294c05a91 | ||
|
|
64f802d4f6 | ||
|
|
61e782389c | ||
|
|
7cfb230a5e | ||
|
|
0e2af40329 | ||
|
|
7f51ff0a8d | ||
|
|
d1a61c6dce | ||
|
|
c90d1fc8ef | ||
|
|
42cd046e90 | ||
|
|
52fa350138 | ||
|
|
4ea8357c6a | ||
|
|
dd0da70b06 | ||
|
|
d80f2a621d | ||
|
|
16082456df | ||
|
|
49cc69ecc5 | ||
|
|
a0c8e1959a | ||
|
|
c7010dac3c | ||
|
|
99bfc3aaa0 | ||
|
|
43b3922803 | ||
|
|
ca70ad1a9f | ||
|
|
a250419b59 | ||
|
|
291644f29d | ||
|
|
d4e97ab64c | ||
|
|
e92f98d3c6 | ||
|
|
999aac19de | ||
|
|
b2f6e5673d | ||
|
|
f0d1c962a7 | ||
|
|
6999b8d9b4 | ||
|
|
46e704d507 | ||
|
|
308e94ea79 | ||
|
|
0c839b2738 | ||
|
|
cff5859c59 | ||
|
|
6d95f5fc98 | ||
|
|
7684861249 | ||
|
|
9759cb7075 | ||
|
|
b3a5f9bef4 | ||
|
|
3e3b6cc1e1 | ||
|
|
474103dc82 | ||
|
|
092a8f2d05 | ||
|
|
2bcf0c354a | ||
|
|
73cc756cac | ||
|
|
6091c2dba0 | ||
|
|
2862a73253 | ||
|
|
850c3f2b6a | ||
|
|
98f3e4cbd3 | ||
|
|
efc8ed4845 | ||
|
|
0bc4c7d1a0 | ||
|
|
be2e754d78 | ||
|
|
817e72846e | ||
|
|
220768bffc | ||
|
|
3cd64bcb22 | ||
|
|
86bfb07c29 | ||
|
|
ad08bd62aa | ||
|
|
d79cab0680 | ||
|
|
d1d6f7d838 | ||
|
|
e31a7e5c9d | ||
|
|
e6f0d9887c | ||
|
|
2855d84fba | ||
|
|
3c19648a56 | ||
|
|
8db367c743 | ||
|
|
2c4fabba35 | ||
|
|
dff42ffb41 | ||
|
|
11d61a744d | ||
|
|
5cefdbf985 | ||
|
|
5485f96625 | ||
|
|
af0f46d23d | ||
|
|
c063fe0720 | ||
|
|
203ba7343c | ||
|
|
d889547b26 | ||
|
|
b66e19dc43 | ||
|
|
c84e3334bf | ||
|
|
d5eeb948d5 | ||
|
|
9670833a5d | ||
|
|
ca051e943f | ||
|
|
36d89d02e1 | ||
|
|
f4bfa77942 | ||
|
|
f2258d4202 | ||
|
|
d56bf34326 | ||
|
|
9fa3dee522 | ||
|
|
7ee2192c29 | ||
|
|
3a38292bab | ||
|
|
60cbb65d84 | ||
|
|
d118ab71e6 | ||
|
|
81a1aedeb9 | ||
|
|
1fd576436b | ||
|
|
fac05e7dcd | ||
|
|
eef3126f0b | ||
|
|
2df5a2a66d | ||
|
|
01e7e3c24a | ||
|
|
8112dee91e | ||
|
|
638f7a1c89 | ||
|
|
5de4c3cc3f | ||
|
|
6579007ca0 | ||
|
|
c596fb14bb | ||
|
|
116fc4e00d | ||
|
|
57f6f54f29 | ||
|
|
8640e6d1df | ||
|
|
801583fd07 | ||
|
|
4987534eba | ||
|
|
ed4bf1c13d | ||
|
|
5b24225251 | ||
|
|
886be8c452 | ||
|
|
269efb9c1e | ||
|
|
612448e4ae | ||
|
|
82db19d54c | ||
|
|
06aa3e6b8c | ||
|
|
abf985c69e | ||
|
|
c176b54f6b | ||
|
|
b24e691e7e | ||
|
|
c2e43dc1b0 | ||
|
|
7b7739c32c | ||
|
|
269172406f | ||
|
|
4c054d6de9 | ||
|
|
34a58369f2 | ||
|
|
59b4764a15 | ||
|
|
7ebba75fa2 | ||
|
|
b2e80efe3f | ||
|
|
b4b566318a | ||
|
|
27df896a9c | ||
|
|
8188a551f3 | ||
|
|
f13bff2a76 | ||
|
|
59f1c038fe | ||
|
|
006a409eb8 | ||
|
|
dec4ceabb5 | ||
|
|
4a902dbbbe | ||
|
|
8e488e2913 | ||
|
|
e58e27ce22 | ||
|
|
78c0926a64 | ||
|
|
4693069a06 | ||
|
|
70e529ef5d | ||
|
|
2a154f8c9a | ||
|
|
634ace552d | ||
|
|
751a1ba969 | ||
|
|
d95573741d | ||
|
|
faf4bcdd62 | ||
|
|
badae90051 | ||
|
|
a27c593a2a | ||
|
|
f699442bec | ||
|
|
8720931a53 | ||
|
|
600dcdfc58 | ||
|
|
a4b83327c2 | ||
|
|
8c1c49a45e | ||
|
|
b1f0014429 | ||
|
|
d984918c75 | ||
|
|
21ac4b5139 | ||
|
|
92eb7a0be4 | ||
|
|
b26b1c0820 | ||
|
|
a9dd6d6bdb | ||
|
|
d60c53cd12 | ||
|
|
6b0fe6e0a0 | ||
|
|
9c7ec55b40 | ||
|
|
28f1f4cbdc | ||
|
|
508b3ce730 | ||
|
|
87ecc61fb4 | ||
|
|
ce4d664abc | ||
|
|
45b41f0787 | ||
|
|
c47434634d | ||
|
|
b89355b9d8 | ||
|
|
3f40d6f7b7 | ||
|
|
155daac8ad | ||
|
|
f9d24d07dd | ||
|
|
d91e35e197 | ||
|
|
a967dd9d13 | ||
|
|
f4bb7bcbff | ||
|
|
0f6166da00 | ||
|
|
07e5b8295e | ||
|
|
9afdf86961 | ||
|
|
f4f1974be0 | ||
|
|
6f6399ca0f | ||
|
|
5018c0fbb8 | ||
|
|
188975ccbd | ||
|
|
5e1addc75e | ||
|
|
138beeac41 | ||
|
|
187364d94a | ||
|
|
6e74e76fcd | ||
|
|
615424d272 | ||
|
|
d40a7a609e | ||
|
|
0ec40f00a9 | ||
|
|
de054026f2 | ||
|
|
175b45d1ed | ||
|
|
e236b77817 | ||
|
|
21cd4a1b48 | ||
|
|
e9a50371f5 | ||
|
|
71f8a08e5f | ||
|
|
4419c93d80 | ||
|
|
fcc13ce02c | ||
|
|
941db04260 | ||
|
|
87d7a3053a | ||
|
|
ae11b2c281 | ||
|
|
f43577ec27 | ||
|
|
c648c38fb0 | ||
|
|
34d54fd309 | ||
|
|
891b1428cc | ||
|
|
453dd38e51 | ||
|
|
f59aefd3d9 | ||
|
|
451468d129 | ||
|
|
c0adf31da9 | ||
|
|
bb43c57b07 | ||
|
|
6c94bbc93f | ||
|
|
50422c608f | ||
|
|
7639157a49 | ||
|
|
f74cfaec5f | ||
|
|
d559b4a201 | ||
|
|
2d4064aca4 | ||
|
|
337d8ea78c | ||
|
|
4c0a26dbf9 | ||
|
|
885edd6261 | ||
|
|
011de75706 | ||
|
|
852e456d44 | ||
|
|
18136788e4 | ||
|
|
f1101808b7 | ||
|
|
791d5098a0 | ||
|
|
f2fd12fbe5 | ||
|
|
b5ab77908e | ||
|
|
25620081a1 | ||
|
|
5750e6cada | ||
|
|
394aa76a8f | ||
|
|
35c4deb884 | ||
|
|
0daccead12 | ||
|
|
e7c83a81c7 | ||
|
|
dcf8830752 | ||
|
|
3d519f478a | ||
|
|
3f0fc40ef8 | ||
|
|
820494c7a6 | ||
|
|
60b82d3690 | ||
|
|
57d9b1b332 | ||
|
|
67f8b78a7d | ||
|
|
aada997601 | ||
|
|
e5dd108bd7 | ||
|
|
ad91ab68ce | ||
|
|
66c3c32154 | ||
|
|
c9ce562369 | ||
|
|
a1f01529a7 | ||
|
|
38985dc4e2 | ||
|
|
78f150cfbc | ||
|
|
e74c52094f | ||
|
|
e05f1f95cf | ||
|
|
b63c5f2785 | ||
|
|
685c569eaf | ||
|
|
b6e3469519 | ||
|
|
16071f300e | ||
|
|
a8de999b7c | ||
|
|
0dcbcf93c2 | ||
|
|
06273e980e | ||
|
|
c879e5de44 | ||
|
|
890290fff0 | ||
|
|
cddae14352 | ||
|
|
b3e6a3c1e2 | ||
|
|
255b6a14a8 | ||
|
|
bafbf04164 | ||
|
|
fdefa101d8 | ||
|
|
7e40eeb52a | ||
|
|
e98804693d | ||
|
|
953c294778 | ||
|
|
20d2811cd5 | ||
|
|
207df132e6 | ||
|
|
40e0fe1d25 | ||
|
|
fe3f42ce10 | ||
|
|
b4ca5c133c | ||
|
|
8f1a579597 | ||
|
|
aa69b43e0e | ||
|
|
cadd6fdfff | ||
|
|
85b97014c9 | ||
|
|
50ca95735d | ||
|
|
b55c7f794b | ||
|
|
205279f7f8 | ||
|
|
934721a98d | ||
|
|
d02a8d733e | ||
|
|
d59b81f11d | ||
|
|
b11d2c376b | ||
|
|
6d83b5b68d | ||
|
|
0ad7c8f69e | ||
|
|
e24c769f8e | ||
|
|
7cf9b85a97 | ||
|
|
dd3d65cb1c | ||
|
|
5e19632f2d | ||
|
|
cc543d1ad2 | ||
|
|
35299ed7c3 | ||
|
|
7008cf3269 | ||
|
|
6979ea44f4 | ||
|
|
81775eebe2 | ||
|
|
e70802c6ad | ||
|
|
7112bcd134 | ||
|
|
d3d5ddfb43 | ||
|
|
5580fc1954 | ||
|
|
d0e85f05af | ||
|
|
c136741db6 | ||
|
|
5724be1f8e | ||
|
|
29e89d1152 | ||
|
|
95dfde99c6 | ||
|
|
59fd3861a7 | ||
|
|
69d627a58b | ||
|
|
2772c33563 | ||
|
|
78f655f877 | ||
|
|
75a73be60c | ||
|
|
690874fc6e | ||
|
|
96de38a1f0 | ||
|
|
cef0c2ebb2 | ||
|
|
5e0f5460f2 | ||
|
|
ff8c9cfd14 | ||
|
|
b5b033fe22 | ||
|
|
5dc2f43d1d | ||
|
|
f0acb9f01e | ||
|
|
b6ca9b6020 | ||
|
|
9c6446edbe | ||
|
|
d84f0030be | ||
|
|
61f554e0d7 | ||
|
|
3b252d98b8 | ||
|
|
f5315ed6a2 | ||
|
|
c1f36802a1 | ||
|
|
ba0f24bf70 | ||
|
|
522b92128f | ||
|
|
ea77043aaf | ||
|
|
91dd877c2d | ||
|
|
e2e160b8e8 | ||
|
|
89c74868aa | ||
|
|
45a5a04189 | ||
|
|
1f41b1837f | ||
|
|
c01e1b6c33 | ||
|
|
08e7c7b249 | ||
|
|
b103993ad1 | ||
|
|
e12f375624 | ||
|
|
8cd67a5d4a | ||
|
|
31e030af69 | ||
|
|
375986437a | ||
|
|
5f93157d5a | ||
|
|
8d3c201830 | ||
|
|
612ef90523 | ||
|
|
e64856e957 | ||
|
|
8595835aa0 | ||
|
|
4d2507263d | ||
|
|
4a671fe634 | ||
|
|
ed845d93bc | ||
|
|
595f3d99b4 | ||
|
|
1e2d5b0c77 | ||
|
|
1215618589 | ||
|
|
67533ee478 | ||
|
|
3acd5dcc07 | ||
|
|
c387902a44 | ||
|
|
ad51be7dd9 | ||
|
|
3514f805ea | ||
|
|
2690159f09 | ||
|
|
902151918c | ||
|
|
5dd1ea21af | ||
|
|
b37c8bf928 | ||
|
|
923ae14c47 | ||
|
|
7ec6550734 | ||
|
|
e622802c24 | ||
|
|
244c8edfca | ||
|
|
245c0c22cd | ||
|
|
93b90ab23a | ||
|
|
765f9ffcf0 | ||
|
|
bdfdd515d8 | ||
|
|
72c6d3e7ba | ||
|
|
869ce64f87 | ||
|
|
86117c8859 | ||
|
|
96c4c72ee3 | ||
|
|
4b0d32eb14 | ||
|
|
1d705b9805 | ||
|
|
8605aee533 | ||
|
|
e4de9ef903 | ||
|
|
e529635952 | ||
|
|
21b71401d3 | ||
|
|
80bdcbb3e6 | ||
|
|
b878b69028 | ||
|
|
48440e368c | ||
|
|
a7bf07b864 | ||
|
|
a80e59812b | ||
|
|
3f784a974a | ||
|
|
5ce3565109 | ||
|
|
b25054e891 | ||
|
|
2c316c788e | ||
|
|
2fe2cc5e65 | ||
|
|
5d6a7922c1 | ||
|
|
d23e830955 | ||
|
|
762de94900 | ||
|
|
d938c79385 | ||
|
|
987a369fec | ||
|
|
586e2ef2df | ||
|
|
5edec37d57 | ||
|
|
42ef6208c1 | ||
|
|
6f508b81d6 | ||
|
|
81e2f73c98 | ||
|
|
be43d3e0fd | ||
|
|
60fd27eca4 | ||
|
|
ca4a3c3cc0 | ||
|
|
dfad87f674 | ||
|
|
743e575701 | ||
|
|
be627c3f3a | ||
|
|
80fdf53cc3 | ||
|
|
759f297aae | ||
|
|
1a41c44c2f | ||
|
|
88b014e9a7 | ||
|
|
bd0b8d768e | ||
|
|
f422362747 | ||
|
|
34217f5f90 | ||
|
|
56e503149d | ||
|
|
04af502e3a | ||
|
|
f2c1fd2a48 | ||
|
|
a86fd554a3 | ||
|
|
ef0a8bcde0 | ||
|
|
c38fc0b906 | ||
|
|
2758d3c3eb | ||
|
|
521a5074c9 | ||
|
|
430c48e828 | ||
|
|
2e747bec6f | ||
|
|
d945789e1b | ||
|
|
de306cd7b8 | ||
|
|
aedde6600e | ||
|
|
9c8d732081 | ||
|
|
5d1e10522f | ||
|
|
ba0410dd24 | ||
|
|
da44cef9a3 | ||
|
|
e9227b0ffe | ||
|
|
9299aff2af | ||
|
|
a9377d6a3f | ||
|
|
b223b71d0d | ||
|
|
02cf85fa34 | ||
|
|
76a6e0b6fc | ||
|
|
2a2f032f17 | ||
|
|
e7ddd1a481 | ||
|
|
813ab2c5ed | ||
|
|
4acdf9e817 | ||
|
|
465da3a942 | ||
|
|
fb601cc280 | ||
|
|
25b9776bf6 | ||
|
|
5271232aa5 | ||
|
|
7fa70108c4 | ||
|
|
ff09b2131e | ||
|
|
df758f40ef | ||
|
|
1d8dd949dc | ||
|
|
9dd333a2f4 | ||
|
|
d53b3b242d | ||
|
|
8e0e4b7b92 | ||
|
|
adee24af6f | ||
|
|
1b161b0ee4 | ||
|
|
5aaa8f44a2 | ||
|
|
1fbc6fa431 | ||
|
|
6177ec9134 | ||
|
|
bd4867ef39 | ||
|
|
0189d06375 | ||
|
|
aafdeb9000 | ||
|
|
e9a264cb43 | ||
|
|
b996d82c5f | ||
|
|
c53794a8a2 | ||
|
|
170b2e4465 | ||
|
|
4e0fc81e5d | ||
|
|
3b865d432f | ||
|
|
646dce7765 | ||
|
|
88a68f96da | ||
|
|
32bdf42913 | ||
|
|
53a010d1d4 | ||
|
|
a9e19cb89e | ||
|
|
4878611987 | ||
|
|
2c777acb25 | ||
|
|
49df57df45 | ||
|
|
374c30999a | ||
|
|
2ab480188a | ||
|
|
c4c47f7777 | ||
|
|
c36b2ca915 | ||
|
|
e55a1a6aa8 | ||
|
|
21e5b05b14 | ||
|
|
ea845aeacd | ||
|
|
ee3b077f26 | ||
|
|
393cee6619 | ||
|
|
d9196bb82a | ||
|
|
5ecbbfc7df | ||
|
|
9244ee8094 | ||
|
|
0b7ca61958 | ||
|
|
c56b140387 | ||
|
|
3e7d3a20af | ||
|
|
2d2cab0aba | ||
|
|
364346b07c | ||
|
|
be15a395d8 | ||
|
|
845f7e9382 | ||
|
|
976d6baf6a | ||
|
|
f9271bb0bd | ||
|
|
06f03eb13a | ||
|
|
1a1c0ca72c | ||
|
|
73f2db09ae | ||
|
|
be533679dd | ||
|
|
e31903cbc9 | ||
|
|
33d417955d | ||
|
|
9f0f794a0a | ||
|
|
33e4d7c0d8 | ||
|
|
ad30834123 | ||
|
|
fba64e9451 | ||
|
|
9b5e270379 | ||
|
|
ea5131db88 | ||
|
|
7ae86d29d2 | ||
|
|
13ff9a897d | ||
|
|
4db38ec64a | ||
|
|
29c5a65e8f | ||
|
|
dd2ffae8bf | ||
|
|
ca969cd0ed | ||
|
|
4f95b44ff2 | ||
|
|
cb7aec0da5 | ||
|
|
62eebc86f6 | ||
|
|
5ea7196e78 | ||
|
|
fcc47e69e4 | ||
|
|
d2f1c37431 | ||
|
|
817fe8d485 | ||
|
|
1dbd85322d | ||
|
|
fe90096ace | ||
|
|
ad60309187 | ||
|
|
634a515ec1 | ||
|
|
01abb82c37 | ||
|
|
1c88a8d4a0 | ||
|
|
ce77f82966 | ||
|
|
573af9e366 | ||
|
|
774cc852d4 | ||
|
|
c0735a5ca2 | ||
|
|
fa752b0ac1 | ||
|
|
3f69fc70f1 | ||
|
|
137f56f994 | ||
|
|
e2cbcf7039 | ||
|
|
64f8df0a0d | ||
|
|
274d556ec1 | ||
|
|
f28c3b441e | ||
|
|
4eaa893e0b | ||
|
|
36e969fd79 | ||
|
|
d8f16960c8 | ||
|
|
101a7648ee | ||
|
|
70a104f24f | ||
|
|
0f27db6271 | ||
|
|
56ec55110a | ||
|
|
570497071d | ||
|
|
49dee6312f | ||
|
|
63a6b4a162 | ||
|
|
ec4dea0920 | ||
|
|
7270da6a28 | ||
|
|
8cd66145bd | ||
|
|
92ee27349c | ||
|
|
a88233a045 | ||
|
|
0259130cf3 | ||
|
|
d0a1f0b8f9 | ||
|
|
97dd350c61 | ||
|
|
dc3b09c8f3 | ||
|
|
c8c4513af9 | ||
|
|
3201a4bec5 | ||
|
|
9da8e30c37 | ||
|
|
6bfac1a907 | ||
|
|
d0c7c99d5e | ||
|
|
934bcf5336 | ||
|
|
5b88873530 | ||
|
|
b161b02c51 | ||
|
|
ddc544e00a | ||
|
|
97d5ffe056 | ||
|
|
13319985fd | ||
|
|
e3aaa39f9e | ||
|
|
b6efb02ed2 | ||
|
|
c358d8326e | ||
|
|
f40fedcad6 | ||
|
|
b4973ede44 | ||
|
|
81338e8086 | ||
|
|
4270e17bc4 | ||
|
|
1329662edc | ||
|
|
249427365b | ||
|
|
326644104c | ||
|
|
1520a26e0f | ||
|
|
6ffdc1b642 | ||
|
|
22d8523e76 | ||
|
|
2d3740b912 | ||
|
|
c8ab5db768 | ||
|
|
c656478230 | ||
|
|
3d02758fee | ||
|
|
f473b5abfb | ||
|
|
22d68ddf08 | ||
|
|
2cba88f1eb | ||
|
|
f132436af3 | ||
|
|
fae70bf0a7 | ||
|
|
ffd2faf8a0 | ||
|
|
64a8913385 | ||
|
|
266dcd44bb | ||
|
|
8851921d6d | ||
|
|
c76ff4249e | ||
|
|
dbfe748d27 | ||
|
|
4ee809bed6 | ||
|
|
4ebf6cda5d | ||
|
|
a04b6d178b | ||
|
|
c8b45c3449 | ||
|
|
1bb74a5a01 | ||
|
|
ef94072cee | ||
|
|
ff3ff2478d | ||
|
|
2757822cd8 | ||
|
|
2cb0cade7b | ||
|
|
191e01e3ec | ||
|
|
8c65e9cb4a | ||
|
|
79968ea07a | ||
|
|
7a0d9a4494 | ||
|
|
06d1cf83d2 | ||
|
|
de058901c2 | ||
|
|
a40b882d72 | ||
|
|
46242aeaae | ||
|
|
ad040a0b11 | ||
|
|
4eaba326ce | ||
|
|
fd48f9d173 | ||
|
|
104c45f6e6 | ||
|
|
f4a71db42d | ||
|
|
1ac2b93fd8 | ||
|
|
03c1419ad0 | ||
|
|
18938280f5 | ||
|
|
01334d761a | ||
|
|
446204f7d8 | ||
|
|
328900cf3b | ||
|
|
837dbb7a14 | ||
|
|
c9794439bc | ||
|
|
079c13e633 | ||
|
|
7b8c85cf3f | ||
|
|
c229f058b4 | ||
|
|
e2b10f52e0 | ||
|
|
2b8afd5580 | ||
|
|
7e89d816d7 | ||
|
|
77c87bcccf | ||
|
|
b7a655917e | ||
|
|
d8bfa5dd96 | ||
|
|
88eb19165b | ||
|
|
2828b8cf5c | ||
|
|
b1b9dbe55f | ||
|
|
579adb4897 | ||
|
|
a9d52cd296 | ||
|
|
c71422e909 | ||
|
|
03506bd6cf | ||
|
|
244936b0fd | ||
|
|
f430a24fa3 | ||
|
|
ecb1c78173 | ||
|
|
42631b5943 | ||
|
|
109f9eed61 | ||
|
|
c7fdc53795 | ||
|
|
602ca91700 | ||
|
|
4cfc0b1a64 | ||
|
|
3d5fdd086b | ||
|
|
223c4c7b9a | ||
|
|
f7e925beaa | ||
|
|
e588ea8a8b | ||
|
|
91cad21d33 | ||
|
|
d6b9c8b93d | ||
|
|
a7273668d8 | ||
|
|
b1f74f4ef4 | ||
|
|
c640d1c599 | ||
|
|
9d0f71bfcb | ||
|
|
bbca6be0f9 | ||
|
|
31e1e9cbfb | ||
|
|
6de823b532 | ||
|
|
69878ed628 | ||
|
|
27ae9c9d34 | ||
|
|
52e698cae6 | ||
|
|
6a0ac591d1 | ||
|
|
328685d2fb | ||
|
|
806c738923 | ||
|
|
9c2d48bcd4 | ||
|
|
d8802e4fc8 | ||
|
|
2c8a243ece | ||
|
|
3372fb0761 | ||
|
|
af7a31cd14 | ||
|
|
cb2996a40c | ||
|
|
ea237a97a1 | ||
|
|
530b1d038f | ||
|
|
a31981966e | ||
|
|
84183d9f7e | ||
|
|
805f8bf983 | ||
|
|
67c9b5d049 | ||
|
|
44b935f875 | ||
|
|
cade25a112 | ||
|
|
a4831b0f7c | ||
|
|
ec9418b262 | ||
|
|
cec2e92fe3 | ||
|
|
625438ff64 | ||
|
|
6cfaa5cba3 | ||
|
|
31fea1014e | ||
|
|
898fea1e0e | ||
|
|
4b3979df04 | ||
|
|
ac52bebb88 | ||
|
|
c568f7b8d1 | ||
|
|
d925ec6759 | ||
|
|
c136c142b8 | ||
|
|
38be2386dd | ||
|
|
643b2f9205 | ||
|
|
ffe429be65 | ||
|
|
4a56b77f64 | ||
|
|
9773ab7aab | ||
|
|
6c02fa40f1 | ||
|
|
632996f53e | ||
|
|
710448e73f | ||
|
|
084177a20c | ||
|
|
5a2c62466e | ||
|
|
b739f91caa | ||
|
|
b1590ad645 | ||
|
|
f49010bb12 | ||
|
|
6e1c3b6d48 | ||
|
|
bcdd75b817 | ||
|
|
0271f2a1ae | ||
|
|
0c1c386a0a | ||
|
|
a3acec9674 | ||
|
|
4eaddd1279 | ||
|
|
eb3dbcce7b | ||
|
|
3ecf1cab5e | ||
|
|
c536526196 | ||
|
|
b2de12442d | ||
|
|
8f5cb0c45a | ||
|
|
3b4503c971 | ||
|
|
c358aa2806 | ||
|
|
938d26ba3e | ||
|
|
b23751128b | ||
|
|
d54ff4fae5 | ||
|
|
720d3dcedc | ||
|
|
ce4fc30439 | ||
|
|
b39474456f | ||
|
|
25cd9b4983 | ||
|
|
2d65ce542c | ||
|
|
c2b691fd01 | ||
|
|
ae691bdc7c | ||
|
|
ba566fd64b | ||
|
|
85c07d57fe | ||
|
|
16fd618380 | ||
|
|
06f2979e04 | ||
|
|
1ff9d1afdd | ||
|
|
a776f69a59 | ||
|
|
a574290235 | ||
|
|
84c9ae04fc | ||
|
|
c74c41c3b8 | ||
|
|
7cf73e0533 | ||
|
|
03e9ed71b4 | ||
|
|
e7e79f7423 | ||
|
|
589af1f9ee | ||
|
|
e7f25b8466 | ||
|
|
2100441b54 | ||
|
|
082826ee44 | ||
|
|
93ad3eb6f1 | ||
|
|
177209b5e9 | ||
|
|
47d80df81c | ||
|
|
3d6fa049da | ||
|
|
16738dfe7f | ||
|
|
c28ba4be37 | ||
|
|
8bcdc24fcb | ||
|
|
b36eb9a91c | ||
|
|
989fbe70cd | ||
|
|
85cf25a2a8 | ||
|
|
7bb94f9993 | ||
|
|
1297c8d2ae | ||
|
|
ed28ef185e | ||
|
|
9d97cc2a1d | ||
|
|
23acf02d58 | ||
|
|
60311eb04b | ||
|
|
b9a8b9e47b | ||
|
|
b7bac45427 | ||
|
|
7d05b8e5fd | ||
|
|
d5ae9aedc4 | ||
|
|
1b216e6019 | ||
|
|
83256c9ccd | ||
|
|
9f029336ca | ||
|
|
95476cf33c | ||
|
|
bf7c96807a | ||
|
|
d4a6aa7801 | ||
|
|
b767bda410 | ||
|
|
a39d436f9f | ||
|
|
50dbe1e62d | ||
|
|
be81a40b2b | ||
|
|
bbcb237f5b | ||
|
|
33be244d8a | ||
|
|
ca75619518 | ||
|
|
82acfb75a1 | ||
|
|
fc9e6d289a | ||
|
|
6838342d62 | ||
|
|
69b25e490e | ||
|
|
b8d7647c48 | ||
|
|
989443a569 | ||
|
|
c50a311ff3 | ||
|
|
b37e5a426a | ||
|
|
5e76318e44 | ||
|
|
8ed3971ef0 | ||
|
|
c2b6f0bc28 | ||
|
|
2c42daf609 | ||
|
|
8e2446a2fc | ||
|
|
19bb9e0181 | ||
|
|
e9b2dacb61 | ||
|
|
3df5d854b8 | ||
|
|
9545a81166 | ||
|
|
1712eceaf9 | ||
|
|
7d8c6cb9ed | ||
|
|
f123809d29 | ||
|
|
7abcdd34af | ||
|
|
ead56c59e2 | ||
|
|
19a8dfaa5c | ||
|
|
fa45ea1d84 | ||
|
|
8789edb65d | ||
|
|
b6abc034cf | ||
|
|
c92ea70453 | ||
|
|
07eee20525 | ||
|
|
6c9130be46 | ||
|
|
51f00cda92 | ||
|
|
7973567a7c | ||
|
|
b81b18814e | ||
|
|
bbfe71b0c6 | ||
|
|
7404183830 | ||
|
|
43aea3ce38 | ||
|
|
cbaf4b7536 | ||
|
|
570d84c031 | ||
|
|
9eeccc087d | ||
|
|
7404a8ec1a | ||
|
|
bfae86bdb6 | ||
|
|
66f6a1d186 | ||
|
|
17a4aa983e | ||
|
|
2a0a06d74a | ||
|
|
ccaa93655a | ||
|
|
65bc363685 | ||
|
|
293d411efb | ||
|
|
2a287e6def | ||
|
|
d96f4340e8 | ||
|
|
d3cbbe029c | ||
|
|
a1ccacb825 | ||
|
|
b04915161b | ||
|
|
8cb968c4b0 | ||
|
|
f645c6f3a5 | ||
|
|
4c1c690816 | ||
|
|
537a7cf03d | ||
|
|
662e8f8a4c | ||
|
|
7397348b9c | ||
|
|
5c43013692 | ||
|
|
908875a052 | ||
|
|
41ce2da080 | ||
|
|
6add6ce79b | ||
|
|
0fe9c029ab | ||
|
|
a6522b34c7 | ||
|
|
229f99fe6b | ||
|
|
bdc269d445 | ||
|
|
ff63132a32 | ||
|
|
e7fb22edc1 | ||
|
|
3c59303b68 | ||
|
|
fb4504c5a3 | ||
|
|
db22578c16 | ||
|
|
300727b8cf | ||
|
|
448fb14fc4 | ||
|
|
c42e8613e5 | ||
|
|
12de30a2b1 | ||
|
|
6f93d9848c | ||
|
|
3d019fa2dd | ||
|
|
d5aeb13601 | ||
|
|
230f7ab6d4 | ||
|
|
d71dd21abc | ||
|
|
0d00b72569 | ||
|
|
00f6deb9bd | ||
|
|
cce5054aec | ||
|
|
544ef3bc58 | ||
|
|
124cc43962 | ||
|
|
4237dd457e | ||
|
|
88ebbd90d8 | ||
|
|
298acb9645 | ||
|
|
c4f4edd743 | ||
|
|
258a4e5627 | ||
|
|
e1400e5745 | ||
|
|
593688d539 | ||
|
|
aed6823051 | ||
|
|
96f9e51546 | ||
|
|
161ea6d51f | ||
|
|
88e755037b | ||
|
|
cde706748a | ||
|
|
3e833a5bcd | ||
|
|
475a39ff90 | ||
|
|
2dd2b755f1 | ||
|
|
a6516341c5 | ||
|
|
9a70c3c275 | ||
|
|
5998445841 | ||
|
|
d9c39d0c41 | ||
|
|
4038b78377 | ||
|
|
dce8516da4 | ||
|
|
bde429cff6 | ||
|
|
4c8d33d1eb | ||
|
|
fac7826efa | ||
|
|
d439dee736 | ||
|
|
295c08fcf3 | ||
|
|
efa11cbc74 | ||
|
|
4a8e222763 | ||
|
|
75b9c7d6b8 | ||
|
|
d1648fb258 | ||
|
|
34cda095fe | ||
|
|
dad7bc82c3 | ||
|
|
0ee65ee954 | ||
|
|
ae0780fe3f | ||
|
|
d2a65ab1be | ||
|
|
f4516826ef | ||
|
|
b894380f86 | ||
|
|
b48a9d3f75 | ||
|
|
21b398252a | ||
|
|
80e03268fd | ||
|
|
135b3cf1ef | ||
|
|
b9e083da61 | ||
|
|
74aa446190 | ||
|
|
0d7548a381 | ||
|
|
428b2cca21 | ||
|
|
28a316f73f | ||
|
|
0532d639c2 | ||
|
|
c17717c5db | ||
|
|
8b00913579 | ||
|
|
cf95c4878d | ||
|
|
4958e3b42c | ||
|
|
cc3edeb6f5 | ||
|
|
20c296ce53 | ||
|
|
5ccbcb44f7 | ||
|
|
adcf28af7c | ||
|
|
5b206cd4a7 | ||
|
|
1eb70b66d8 | ||
|
|
3c1ff1fc63 | ||
|
|
d711f2ad4c | ||
|
|
ae8a1c992a | ||
|
|
580d3db5a8 | ||
|
|
042cc96968 | ||
|
|
d177043c9f | ||
|
|
4c1e2c2dd8 | ||
|
|
7dcb0cc11b | ||
|
|
837d9a4df0 | ||
|
|
ec1d5ead93 | ||
|
|
f485ed174d | ||
|
|
e8560d56f5 | ||
|
|
3a937fb969 | ||
|
|
c1aa96ebf7 | ||
|
|
17e012afc6 | ||
|
|
1c6796f907 | ||
|
|
3e133bbfa3 | ||
|
|
6c8d1bdffe | ||
|
|
59fc495593 | ||
|
|
60827bdcc4 | ||
|
|
f2c73c0f45 | ||
|
|
78492d0037 | ||
|
|
2b08519f5a | ||
|
|
c86b35da70 | ||
|
|
b2003e2c3c | ||
|
|
8a08cceccb | ||
|
|
9284b60a79 | ||
|
|
b69f8a3f29 | ||
|
|
8ea7c08f43 | ||
|
|
78197aa625 | ||
|
|
f992feb71c | ||
|
|
fcb065bcb2 | ||
|
|
cf66e66db8 | ||
|
|
63aa3948e5 | ||
|
|
3940fa5659 | ||
|
|
7899ed6f69 | ||
|
|
5e44239657 | ||
|
|
697fbf33c5 | ||
|
|
901785663e | ||
|
|
9e4ff8ce25 | ||
|
|
5f1b2b4bf7 | ||
|
|
271d280c91 | ||
|
|
8f708fef9c | ||
|
|
38de059156 | ||
|
|
f0b40ac15e | ||
|
|
5c47c9ed95 | ||
|
|
dafac11aaa | ||
|
|
1fc81457a1 | ||
|
|
0d47bb6878 | ||
|
|
17cd452fff | ||
|
|
f6f7e7e8d2 | ||
|
|
004861fbb7 | ||
|
|
483d450af4 | ||
|
|
decc14c324 | ||
|
|
1134be8d30 | ||
|
|
fb65c54123 | ||
|
|
fe803135d8 | ||
|
|
908ebed9fd | ||
|
|
bdb6b0d237 | ||
|
|
73a0866ac4 | ||
|
|
e6b1b20565 | ||
|
|
e40ea62c7c | ||
|
|
7864579b1e | ||
|
|
b62eb665c5 | ||
|
|
c72716eca7 | ||
|
|
530d816df6 | ||
|
|
23c47f78ea | ||
|
|
8727f12b18 | ||
|
|
4a4c43bb2c | ||
|
|
0159b631fc | ||
|
|
e74361c4db | ||
|
|
21299c6fc1 | ||
|
|
eec42d3bb3 | ||
|
|
bd049eddd4 | ||
|
|
e762347080 | ||
|
|
53ad04cdc4 | ||
|
|
148c3f9dc5 | ||
|
|
6f1188f44f | ||
|
|
78868314a7 | ||
|
|
dc2f293089 | ||
|
|
c7a0526428 | ||
|
|
498c021aec | ||
|
|
5ee133843f | ||
|
|
9ad2902add | ||
|
|
ef75d27afb | ||
|
|
965c51c2d4 | ||
|
|
05604e4bd2 | ||
|
|
fd7d497cd1 | ||
|
|
a974d0d52d | ||
|
|
5a6b14f878 | ||
|
|
de9a7f0fa9 | ||
|
|
3009c88d24 | ||
|
|
c850a61a89 | ||
|
|
f3fa2d853a | ||
|
|
a982aecd5b | ||
|
|
52bd27a028 | ||
|
|
e5e16da8cb | ||
|
|
3a2fdec241 | ||
|
|
b0adb70ffc | ||
|
|
2f4c619d51 | ||
|
|
6e36820b1b | ||
|
|
209d06a8f7 | ||
|
|
4a6d050e22 | ||
|
|
b033597ada | ||
|
|
08b804cd2d | ||
|
|
89342ca9fb | ||
|
|
892f4b4182 | ||
|
|
de7891771d | ||
|
|
602768209b | ||
|
|
73b1f8fcdd | ||
|
|
e1b923ab7d | ||
|
|
bed9876d68 | ||
|
|
d184e2708f | ||
|
|
580c3f4ffe | ||
|
|
158b8aea38 | ||
|
|
d22766f458 | ||
|
|
70391243dc | ||
|
|
61eef68ff3 | ||
|
|
41cc2854c5 | ||
|
|
ec68ede79f | ||
|
|
478014f02a | ||
|
|
aff7c2d771 | ||
|
|
00dd52317a | ||
|
|
17522b31e9 | ||
|
|
163b1ee9f8 | ||
|
|
7b9a992f83 | ||
|
|
4efb258671 | ||
|
|
7532358806 | ||
|
|
13c05e7937 | ||
|
|
c6e9bca76c | ||
|
|
3706afbd01 | ||
|
|
bcd0802ea4 | ||
|
|
9360148829 | ||
|
|
a6165c00ce | ||
|
|
a5826fec25 | ||
|
|
839c6668cf | ||
|
|
e9a9fc5050 | ||
|
|
426668cd2e | ||
|
|
926d7c56a6 | ||
|
|
9934dbfe69 | ||
|
|
b6314c109d | ||
|
|
f8955f90b8 | ||
|
|
9248fc96b3 | ||
|
|
2875ee2423 | ||
|
|
36f707b25e | ||
|
|
2a05bd9ed6 | ||
|
|
b7db9944ec | ||
|
|
86f7d08483 | ||
|
|
1358a81c32 | ||
|
|
09ad48c12b | ||
|
|
aa137fb2d2 | ||
|
|
09284d512d | ||
|
|
02fd7e51ff | ||
|
|
b359b89aa1 | ||
|
|
873b20677e | ||
|
|
a049664219 | ||
|
|
64c6e25896 | ||
|
|
9f9122ab8e | ||
|
|
d0661cd4a3 | ||
|
|
707b19dc35 | ||
|
|
6fd15d66a7 | ||
|
|
8c9a814e18 | ||
|
|
74ef5f38e9 | ||
|
|
624a3ee1a7 | ||
|
|
fc658a25d7 | ||
|
|
308619a726 | ||
|
|
e87d8d9856 | ||
|
|
7594264725 | ||
|
|
69533ce8f5 | ||
|
|
9fac43a3a3 | ||
|
|
b00db39739 | ||
|
|
6f2a584279 | ||
|
|
de717268f7 | ||
|
|
e27b19c756 | ||
|
|
045cb461f1 | ||
|
|
088c0eedc6 | ||
|
|
e5137b03fc | ||
|
|
76d34a4d74 | ||
|
|
ae4f0e5728 | ||
|
|
5df59027ad | ||
|
|
c888e33f04 | ||
|
|
1a56ecaa39 | ||
|
|
d778c3cf19 | ||
|
|
8bf57c6226 | ||
|
|
3c762b8809 | ||
|
|
3561fd7c43 | ||
|
|
66bfc0ec64 | ||
|
|
7d0e576e3b | ||
|
|
aa330bc8c7 | ||
|
|
b4de492e0d | ||
|
|
5bf3114e6f | ||
|
|
d2b1a2e5a3 | ||
|
|
4b01f6359b | ||
|
|
bcfa62f45a | ||
|
|
e7e8a2ca5f | ||
|
|
5edeb0250b | ||
|
|
0c2657df78 | ||
|
|
e4c57e8031 | ||
|
|
6a25548097 | ||
|
|
db7cbdfc44 | ||
|
|
2f26badb84 | ||
|
|
f7e665c42f | ||
|
|
3bc44ee451 | ||
|
|
125de855ef | ||
|
|
3881ebcce0 | ||
|
|
bef5324fea | ||
|
|
c2655370e0 | ||
|
|
d017e34795 | ||
|
|
aa44da35ba | ||
|
|
5c4c6e68f6 | ||
|
|
a435363b94 | ||
|
|
d6634eb14e | ||
|
|
afee2cf71a | ||
|
|
79b0a6ba9c | ||
|
|
0c2438b36a | ||
|
|
d6da46f94b | ||
|
|
562e39c9c5 | ||
|
|
2d08e0955a | ||
|
|
baffa969d3 | ||
|
|
1fbc8739b9 | ||
|
|
b9dec84489 | ||
|
|
6feb864c38 | ||
|
|
b6db1898b1 | ||
|
|
de902d179e | ||
|
|
58b77e3427 | ||
|
|
cdcac86256 | ||
|
|
79b05e48e8 | ||
|
|
541146e8ce | ||
|
|
97b161c536 | ||
|
|
ba3a6ecb52 | ||
|
|
cf27b7440d | ||
|
|
407b9c9cd7 | ||
|
|
d0055310ba | ||
|
|
02f5fa32af | ||
|
|
b6590e95b5 | ||
|
|
ec65211d54 | ||
|
|
8dfa08f146 | ||
|
|
daef2d0546 | ||
|
|
15eaa80301 | ||
|
|
1e924a8530 | ||
|
|
23995ccb07 | ||
|
|
a4f2603454 | ||
|
|
cc860b4a76 | ||
|
|
0be82602ce | ||
|
|
b42c42692e | ||
|
|
7fcbff3ceb | ||
|
|
efec0af394 | ||
|
|
aafecd9d1e | ||
|
|
e37c43ea06 | ||
|
|
3fac7b5bb7 | ||
|
|
9b90114d03 | ||
|
|
7c5cfe6697 | ||
|
|
d1fd69337f | ||
|
|
3fc218111a | ||
|
|
c63fb0fc6b | ||
|
|
6fe4ac241d | ||
|
|
c385b80807 | ||
|
|
97f36fa46f | ||
|
|
856d4b39d1 | ||
|
|
01abd73a2a | ||
|
|
375c2a87c7 | ||
|
|
6ca507f8dd | ||
|
|
86b4a53858 | ||
|
|
8959ba9b82 | ||
|
|
a62a230d3d | ||
|
|
cc45129e9f | ||
|
|
bffc9d5aba | ||
|
|
f2b121cd58 | ||
|
|
fd30be7644 | ||
|
|
d9052c7e3c | ||
|
|
dcfe10b691 | ||
|
|
914a096b42 | ||
|
|
5fa9dcbc7e | ||
|
|
93ec01e0a1 | ||
|
|
bbc98db6b4 | ||
|
|
1f8d29a221 | ||
|
|
d267dd2515 | ||
|
|
51954c7f3d | ||
|
|
df842a8e8d | ||
|
|
60919488f1 | ||
|
|
4dbaaa63bb | ||
|
|
8cc4003837 | ||
|
|
35790c584b | ||
|
|
09d7cf2cc1 | ||
|
|
58d7c7f6ae | ||
|
|
cab24836d8 | ||
|
|
cf9ef615c9 | ||
|
|
8c96032b2b | ||
|
|
d256551907 | ||
|
|
e800e2db2b | ||
|
|
cc9f41df5f | ||
|
|
bb3784d8dd | ||
|
|
e5539c0d01 | ||
|
|
c997360b4a | ||
|
|
ea8621d6ec | ||
|
|
88fa18204f | ||
|
|
ac05a2ede7 | ||
|
|
808baf203d | ||
|
|
beeafc6bc5 | ||
|
|
6f8c977e73 | ||
|
|
9a2fbdde20 | ||
|
|
b325b2c003 | ||
|
|
feda23587c | ||
|
|
e8f3b7f853 | ||
|
|
e28341ca4b | ||
|
|
6c11a020ee | ||
|
|
7200c2ac0c | ||
|
|
732ca49b02 | ||
|
|
ee976ed460 | ||
|
|
06ac3e896a | ||
|
|
6ce0f6e806 | ||
|
|
e006586275 | ||
|
|
e9d6b17a00 | ||
|
|
bf2f199f37 | ||
|
|
718ca548be | ||
|
|
51c0dc070a | ||
|
|
d5e0d52bc4 | ||
|
|
f6b99db738 | ||
|
|
ef69294072 | ||
|
|
c56d395c90 | ||
|
|
778e9141d8 | ||
|
|
c70a4662b4 | ||
|
|
e441a31051 | ||
|
|
9f5ec5cc50 | ||
|
|
825ded2e08 | ||
|
|
a7c3776557 | ||
|
|
1e7cfc2644 | ||
|
|
c6fd32ffde | ||
|
|
5f551a8b47 | ||
|
|
f73ff57148 | ||
|
|
c538d7372e | ||
|
|
4e7804ca75 | ||
|
|
8fe715983a | ||
|
|
4a3fb07d5f | ||
|
|
f5062c0507 | ||
|
|
f0d7a17b72 | ||
|
|
b89d2d7580 | ||
|
|
7a7f57fa45 | ||
|
|
047dd31724 | ||
|
|
0ce1a200f7 | ||
|
|
0e0024218f | ||
|
|
4886d088e9 | ||
|
|
6eec6e2d65 | ||
|
|
6c12880f5b | ||
|
|
eb472111a8 | ||
|
|
8d8b7ed567 | ||
|
|
681dc70205 | ||
|
|
130cfbf231 | ||
|
|
940e4a6152 | ||
|
|
38f7d9ad37 | ||
|
|
e896d316f2 | ||
|
|
af690b64d6 | ||
|
|
699aad8626 | ||
|
|
4382e53acb | ||
|
|
832adf92a6 | ||
|
|
33e2582918 | ||
|
|
1c2f413211 | ||
|
|
2300581c93 | ||
|
|
03c4fba730 | ||
|
|
75a8b209f5 | ||
|
|
d554b41fea | ||
|
|
b0db1f827e | ||
|
|
3070baf04e | ||
|
|
72629ca511 | ||
|
|
1c39fc71d9 | ||
|
|
0bb59b66be | ||
|
|
31b177f8ad | ||
|
|
8ee5ced08d | ||
|
|
c96a1632f5 | ||
|
|
b3b2649814 | ||
|
|
95805d2e7c | ||
|
|
d4b92141fd | ||
|
|
829e915c90 | ||
|
|
aaffc7485d | ||
|
|
9ea1d6e8af | ||
|
|
17661b4b5f | ||
|
|
32874b89ca | ||
|
|
d4a038c437 | ||
|
|
59fed33797 | ||
|
|
feea137dbf | ||
|
|
70e4a2c4fa | ||
|
|
696359daba | ||
|
|
5695350e98 | ||
|
|
4e3a0720c3 | ||
|
|
dc56d8560d | ||
|
|
81ecea29c8 | ||
|
|
ad58697521 | ||
|
|
6ca7527ec9 | ||
|
|
8ced3699c2 | ||
|
|
c4de5b45df | ||
|
|
71056e1db1 | ||
|
|
f94faeeace | ||
|
|
80f56a1315 | ||
|
|
485f5a07ed | ||
|
|
821af482f0 | ||
|
|
b2d1fadf66 | ||
|
|
3b73e5223e | ||
|
|
6a964e7caf | ||
|
|
a6f06c2d94 | ||
|
|
a85f0a93c6 | ||
|
|
250387c7e5 | ||
|
|
dde7144f5a | ||
|
|
68733a2bc0 | ||
|
|
b2d3a11de8 | ||
|
|
a024e4c10a | ||
|
|
db6e4d1c32 | ||
|
|
8393c382c3 | ||
|
|
5c0e3688fe | ||
|
|
1e6c07246e | ||
|
|
e5370971d6 | ||
|
|
090d921006 | ||
|
|
39c0a7525b | ||
|
|
407b02bde8 | ||
|
|
da7912b879 | ||
|
|
02c08aed12 | ||
|
|
87668f7fa8 | ||
|
|
001734a725 | ||
|
|
276ab3eae3 | ||
|
|
1b976f30f3 | ||
|
|
019afe2a1a | ||
|
|
37ebcd3b4f | ||
|
|
5ad6eeccdf | ||
|
|
65cd33cb15 | ||
|
|
4aa59226d7 | ||
|
|
d53c98860d | ||
|
|
d022e1acce | ||
|
|
98100520eb | ||
|
|
42526fc2dc | ||
|
|
9c76afa2a3 | ||
|
|
943db4496b | ||
|
|
4a77b6673b | ||
|
|
62b31b1e4a | ||
|
|
ddde4e1965 | ||
|
|
96a99935ef | ||
|
|
60c003733e | ||
|
|
9def9a5cb7 | ||
|
|
145295302d | ||
|
|
302d2dbd51 | ||
|
|
49ba3ffee6 | ||
|
|
d83ce0863a | ||
|
|
cf844cb27c | ||
|
|
5ccef18d4e | ||
|
|
40bbdfdf6a | ||
|
|
ad8ad0ccd7 | ||
|
|
1cf012650b | ||
|
|
4f280b5497 | ||
|
|
5ee7009271 | ||
|
|
07f850ed15 | ||
|
|
1b6fbe3a2e | ||
|
|
b1b415ec5b | ||
|
|
8730bbac2f | ||
|
|
e8918ca149 | ||
|
|
82478eef09 | ||
|
|
892e9cd835 | ||
|
|
3dd6499ac4 | ||
|
|
109c66aea3 | ||
|
|
24b0f8e27e | ||
|
|
1a76e83fa4 | ||
|
|
2fa788b01b | ||
|
|
028cbdcffa | ||
|
|
8aefbd911b | ||
|
|
f37387de80 | ||
|
|
4f705fc3f8 | ||
|
|
5b727065cf | ||
|
|
952e466d91 | ||
|
|
c1cc76119c | ||
|
|
9d156141b1 | ||
|
|
8c92b0cd3a | ||
|
|
dd515da889 | ||
|
|
2862b69d56 | ||
|
|
db8e46184b | ||
|
|
99a7cd3cfb | ||
|
|
75746d714a | ||
|
|
983d6d3b42 | ||
|
|
060dd8b020 | ||
|
|
b5be0a2e3e | ||
|
|
46a687b0a6 | ||
|
|
4ff7aa4352 | ||
|
|
750d1f820d | ||
|
|
f046a34d34 | ||
|
|
0d165920bb | ||
|
|
9e3a4402e0 | ||
|
|
34d7aea1be | ||
|
|
e2abc0b727 | ||
|
|
a677a68ab7 | ||
|
|
df228237de | ||
|
|
fc2a038ee1 | ||
|
|
619b39f955 | ||
|
|
becaa3b920 | ||
|
|
c0b9ab930d | ||
|
|
f106b1db15 | ||
|
|
958217dd55 | ||
|
|
2f80fdae97 | ||
|
|
9507f191b0 | ||
|
|
92f5d8c8be | ||
|
|
8535eb7bf5 | ||
|
|
c9604eaabf | ||
|
|
3bc214e544 | ||
|
|
43cec4398d | ||
|
|
335394aaa1 | ||
|
|
c9615cc19c | ||
|
|
5315d8fe3f | ||
|
|
75dedb3345 | ||
|
|
134dfb8804 | ||
|
|
86f4a8d33a | ||
|
|
abeb924554 | ||
|
|
4da3933f24 | ||
|
|
c5703306ef | ||
|
|
a26774b99e | ||
|
|
9258593776 | ||
|
|
48604041e8 | ||
|
|
57ed9ec8e2 | ||
|
|
801ab611ed | ||
|
|
bc34167c84 | ||
|
|
decd0dc035 | ||
|
|
9ac9c693ad | ||
|
|
9b2cd69c0f | ||
|
|
b3f1a19db6 | ||
|
|
f3b6708a92 | ||
|
|
8848885d9a | ||
|
|
c1cab6789e | ||
|
|
907777eeb0 | ||
|
|
7889612edc | ||
|
|
e81e264988 | ||
|
|
eacc29ded0 | ||
|
|
ea04c93bfd | ||
|
|
8692977585 | ||
|
|
6a40f3ed60 | ||
|
|
dd1f631d9d | ||
|
|
15874ac45c | ||
|
|
761fc74a67 | ||
|
|
0e2239e50b | ||
|
|
e9a17517d3 | ||
|
|
94254e61c1 | ||
|
|
9f403a4d6b | ||
|
|
55792d5528 | ||
|
|
a3f726baa8 | ||
|
|
ebc2b23c3a | ||
|
|
e4ca3609d9 | ||
|
|
bc092d8d78 | ||
|
|
dd5933b048 | ||
|
|
5d6128b25c | ||
|
|
24cb04c346 | ||
|
|
f1b54cf0a4 | ||
|
|
e64a7b87a8 | ||
|
|
2b452ea3e8 | ||
|
|
66c6c6c7d1 | ||
|
|
5b4aa1afc2 | ||
|
|
89205276ab | ||
|
|
6d2ab6a7d6 | ||
|
|
e6da910ee2 | ||
|
|
2100ac3cfc | ||
|
|
c7476a1d96 | ||
|
|
d8c3033941 | ||
|
|
7e26ee5a73 | ||
|
|
a8e25ccfe6 | ||
|
|
b6267ec7c2 | ||
|
|
b6bd2884d5 | ||
|
|
767a235611 | ||
|
|
620fc06b6c | ||
|
|
0fa4c89a1b | ||
|
|
1bf046c142 | ||
|
|
52f8429218 | ||
|
|
932b414063 | ||
|
|
097b2390db | ||
|
|
ca8d252ad7 | ||
|
|
23a0dbe3cc | ||
|
|
545ece82b3 | ||
|
|
eaea947b74 | ||
|
|
f1b61d5882 | ||
|
|
e466d72058 | ||
|
|
385be35ed5 | ||
|
|
528b9b6a60 | ||
|
|
dd374eaebf | ||
|
|
9ea483d1dc | ||
|
|
b1813df61a | ||
|
|
8715f74d29 | ||
|
|
152224944b | ||
|
|
f86d12c8f4 | ||
|
|
94d4bc4bac | ||
|
|
c95488549d | ||
|
|
39613aa2d9 | ||
|
|
7c2aafd4ee | ||
|
|
e834c7fe3e | ||
|
|
f96958adf8 | ||
|
|
776074b24f | ||
|
|
c9d64d75f3 | ||
|
|
9651689bb8 | ||
|
|
75be0475ba | ||
|
|
e130b58b20 | ||
|
|
e44f0c497f | ||
|
|
2d0f96d28b | ||
|
|
34d1f79777 | ||
|
|
2185ccd4ea | ||
|
|
125634759d | ||
|
|
350e636e3d | ||
|
|
429140df97 | ||
|
|
a921deafd1 | ||
|
|
071fba2f71 | ||
|
|
9de729e3e7 | ||
|
|
7cf5bd30f2 | ||
|
|
e6cee2965a | ||
|
|
af2b263bc7 | ||
|
|
aa120e0478 | ||
|
|
b07c55a0d9 | ||
|
|
e7f1f496c5 | ||
|
|
823c0434eb | ||
|
|
22162635e7 | ||
|
|
8be0031602 | ||
|
|
29c5e74d31 | ||
|
|
3c8867a1e8 | ||
|
|
c439fe0437 | ||
|
|
b21db670b4 | ||
|
|
1f84aa9809 | ||
|
|
4ac249c26c | ||
|
|
85a29fce94 | ||
|
|
71ecb470b6 | ||
|
|
15a000bb45 | ||
|
|
18e4a7ac6e | ||
|
|
d0322f5fbd | ||
|
|
c1f01b7e35 | ||
|
|
71064f481b | ||
|
|
436293713b | ||
|
|
fc726bfb4b | ||
|
|
b0c3780667 | ||
|
|
3e431c65be | ||
|
|
5b0b90d1f5 | ||
|
|
d16714e84b | ||
|
|
1d5fff9f58 | ||
|
|
fbe8d6144a | ||
|
|
d5f59a57bf | ||
|
|
7465c79884 | ||
|
|
769195fcff | ||
|
|
dc786bd1cc | ||
|
|
960f4ed649 | ||
|
|
91ad3bf1b6 | ||
|
|
91b495d526 | ||
|
|
973d91d120 | ||
|
|
bf5deace8e | ||
|
|
0e4acf6299 | ||
|
|
a5cd0061c5 | ||
|
|
630e3903fa | ||
|
|
0b6a5fad70 | ||
|
|
67ca1b82cc | ||
|
|
9866053f0c | ||
|
|
89a2c1a09c | ||
|
|
70220d8b9c | ||
|
|
274f69526f | ||
|
|
d7026fe36b | ||
|
|
b9b65d7dfb | ||
|
|
1545c78d05 | ||
|
|
f0c292e77b | ||
|
|
62336127e3 | ||
|
|
5beaf8ac98 | ||
|
|
9efd484e27 | ||
|
|
44d5cf980a | ||
|
|
e53650d6c6 | ||
|
|
5bf9a9828c | ||
|
|
24f3bc9f0c | ||
|
|
c200e55f95 | ||
|
|
ea19d59fca | ||
|
|
d34d14c1f4 | ||
|
|
7b17306dbd | ||
|
|
6731564c2d | ||
|
|
194ba0cf8d | ||
|
|
6fc2429540 | ||
|
|
bdcbe61273 | ||
|
|
de5d7bfbd3 | ||
|
|
242878c45c | ||
|
|
c0911da887 | ||
|
|
6e0bf04276 | ||
|
|
56c460d932 | ||
|
|
f0933b66a9 | ||
|
|
422d633f89 | ||
|
|
0537be129c | ||
|
|
9b947c8370 | ||
|
|
14eb4326e7 | ||
|
|
89a3e8fcc7 | ||
|
|
6fd5133bed | ||
|
|
e010877490 | ||
|
|
f56371c79b | ||
|
|
c98f3c5d29 | ||
|
|
5c8de9d82f | ||
|
|
b9c8b6ba23 | ||
|
|
6cd968b53b | ||
|
|
2aa76d257c | ||
|
|
32c0be8bde | ||
|
|
88d283a89d | ||
|
|
66b370c421 | ||
|
|
20bf139b3f | ||
|
|
1da4602567 | ||
|
|
e7ef69e6e7 | ||
|
|
d55904d4db | ||
|
|
740dc59255 | ||
|
|
98b6362c07 | ||
|
|
cd829c096b | ||
|
|
f127c55802 | ||
|
|
13247a0e00 | ||
|
|
609752d331 | ||
|
|
413410d02d | ||
|
|
3528610a51 | ||
|
|
59ee045c24 | ||
|
|
b1dec12893 | ||
|
|
319b0acdd8 | ||
|
|
c605c46f83 | ||
|
|
9ef692b48c | ||
|
|
6da4dfe605 | ||
|
|
7b4cb31a10 | ||
|
|
03ce4d7e2b | ||
|
|
f55f5fa92a | ||
|
|
76757ad1f2 | ||
|
|
347e88dc65 | ||
|
|
746d032e58 | ||
|
|
b6d598a001 | ||
|
|
b35961c53c | ||
|
|
47fba5827b | ||
|
|
47f75ad8bf | ||
|
|
516167c0f8 | ||
|
|
238b3a97a8 | ||
|
|
d4ab8a5a5d | ||
|
|
6672299f8a | ||
|
|
ea5f515f18 | ||
|
|
ea6293544d | ||
|
|
cbe35281b9 | ||
|
|
51a0fc45e2 | ||
|
|
1e7e2bd34f | ||
|
|
fa2f3d136f | ||
|
|
92b6026695 | ||
|
|
c9a5f21344 | ||
|
|
0174bf3722 | ||
|
|
0d003e7450 | ||
|
|
8847838c43 | ||
|
|
9594ce9a8a | ||
|
|
4a91d09c86 | ||
|
|
06837bd32b | ||
|
|
b8d8887d5a | ||
|
|
256f8dd41b | ||
|
|
e0fdafc7e5 | ||
|
|
9a55df245f | ||
|
|
9800d95c50 | ||
|
|
c340416c94 | ||
|
|
9af8a1d30c | ||
|
|
4da96ee980 | ||
|
|
21637e033c | ||
|
|
9ebf2dc97b | ||
|
|
a04689b784 | ||
|
|
5066945adc | ||
|
|
e945c12856 | ||
|
|
0b062d0b8a | ||
|
|
9cc1eff15c | ||
|
|
327e5d06e1 | ||
|
|
a2dd0aea92 | ||
|
|
39da3459c7 | ||
|
|
c0be4c36e1 | ||
|
|
31e04378cd | ||
|
|
e80c99ad74 | ||
|
|
d9be443e53 | ||
|
|
6db323b15e | ||
|
|
5abbfd3f19 | ||
|
|
674215e9e6 | ||
|
|
dce6a5763d | ||
|
|
d89d6e8a01 | ||
|
|
6230dbed2d | ||
|
|
9641254443 | ||
|
|
95871fe13c | ||
|
|
0cecfceb14 | ||
|
|
13970280d9 | ||
|
|
55ce80aabe | ||
|
|
9536a490b3 | ||
|
|
405e07f0a2 | ||
|
|
d3ce91bd89 | ||
|
|
eb44201c5f | ||
|
|
73248aadd5 | ||
|
|
f6a4084f81 | ||
|
|
d5f89c165a | ||
|
|
a518471a27 | ||
|
|
dce5080e53 | ||
|
|
2221d4d612 | ||
|
|
5f73a46c05 | ||
|
|
b782c6bd16 | ||
|
|
ab8d8aa552 | ||
|
|
f955276694 | ||
|
|
77cc60faf2 | ||
|
|
a6f50b8c85 | ||
|
|
e2dfa1d72f | ||
|
|
9a61c0a6fc | ||
|
|
e10b25a12c | ||
|
|
0c1e803208 | ||
|
|
b57dcc74d1 | ||
|
|
f8c583636c | ||
|
|
e6521d1cf3 | ||
|
|
96785e8df1 | ||
|
|
d689778465 | ||
|
|
3c8f8b76aa | ||
|
|
c123fa5422 | ||
|
|
0ade8be7f7 | ||
|
|
a60c2f38c6 | ||
|
|
f1c0797780 | ||
|
|
701167bc12 | ||
|
|
a39e63e324 | ||
|
|
8401e9c2b4 | ||
|
|
c5fca0a1e7 | ||
|
|
e779335d06 | ||
|
|
26039adf5b | ||
|
|
c88286556a | ||
|
|
831b9d4433 | ||
|
|
e937e8fff9 | ||
|
|
f616b2d49a | ||
|
|
fe84dae313 | ||
|
|
9104933b18 | ||
|
|
79654635d3 | ||
|
|
f0edfca75c | ||
|
|
e845da2edc | ||
|
|
7a3050f2c0 | ||
|
|
9a6531e2a2 | ||
|
|
a6a17a85f3 | ||
|
|
9275fd16c2 | ||
|
|
904ae05810 | ||
|
|
54f95389a8 | ||
|
|
a8fc70a5b9 | ||
|
|
c374072822 | ||
|
|
0212de68a4 | ||
|
|
2ea9b6d409 | ||
|
|
823d71df28 | ||
|
|
7560918b32 | ||
|
|
fd87faa7a6 | ||
|
|
cb2d706fa1 | ||
|
|
6b638ac896 | ||
|
|
2bd5ae3617 | ||
|
|
39a5311315 | ||
|
|
9b6e46dc6b | ||
|
|
7311d1182d | ||
|
|
ea1d8b8104 | ||
|
|
fb03e3f00e | ||
|
|
c9c35255e7 | ||
|
|
99a578dbad | ||
|
|
e56c0a6251 | ||
|
|
66479e2aa7 | ||
|
|
6feddcbced | ||
|
|
db020ca1a3 | ||
|
|
6c93518ef7 | ||
|
|
eb02d01eed | ||
|
|
7a9093f634 | ||
|
|
1732b2dfe3 | ||
|
|
a62f891a60 | ||
|
|
a22ecd42ed | ||
|
|
b7cb2a2951 | ||
|
|
c490970324 | ||
|
|
5a9c8a9343 | ||
|
|
5ba7e5d7d7 | ||
|
|
f7b281a65f | ||
|
|
df5a1c59e6 | ||
|
|
0615709a7a | ||
|
|
d85a0130b7 | ||
|
|
71f17a233e | ||
|
|
25c35cbbe4 | ||
|
|
257ba570ee | ||
|
|
91e96b2314 | ||
|
|
c2ee7cbff5 | ||
|
|
27f813e043 | ||
|
|
00ed91f12e | ||
|
|
4cb5efc277 | ||
|
|
65a453d21b | ||
|
|
9c8cb4dea3 | ||
|
|
ed902fdb70 | ||
|
|
509067391b | ||
|
|
04b261057d | ||
|
|
852343f254 | ||
|
|
26e35344d0 | ||
|
|
d3a619659f | ||
|
|
979f2415fb | ||
|
|
28c1d219f6 | ||
|
|
5792f4bf07 | ||
|
|
3a0fa5cb29 | ||
|
|
074ee656f0 | ||
|
|
064f93185c | ||
|
|
4b5922f652 | ||
|
|
85be906b68 | ||
|
|
775285cdf8 | ||
|
|
42f96dc7a6 | ||
|
|
e96fa64625 | ||
|
|
8dc495c9bc | ||
|
|
5d70d889e9 | ||
|
|
999ae6eb49 | ||
|
|
ae1450bbcf | ||
|
|
dcd8a05809 | ||
|
|
62e466f774 | ||
|
|
70777a047d | ||
|
|
47fd95db9d | ||
|
|
7602d9e54b | ||
|
|
1ed0079882 | ||
|
|
b026be410c | ||
|
|
767a1711ff | ||
|
|
a93bd8d944 | ||
|
|
4e12f86e90 | ||
|
|
3007009fab | ||
|
|
2d886b65ce | ||
|
|
8700b87030 | ||
|
|
5a46f1229d | ||
|
|
9d5f8883a7 | ||
|
|
8e0b9b82e9 | ||
|
|
ea4b4ddce4 | ||
|
|
bd95c7b82a | ||
|
|
e20c5cf9cf | ||
|
|
6adb489cb3 | ||
|
|
0c2db1cea5 | ||
|
|
e33604887b | ||
|
|
410f37df1b | ||
|
|
c9f3883c8c | ||
|
|
4346d7605d | ||
|
|
a0a2c27973 | ||
|
|
3fb87ee76e | ||
|
|
242aeea24c | ||
|
|
c305dc8d22 | ||
|
|
f61b898633 | ||
|
|
2bdcd436bb | ||
|
|
a1bea7d7cc | ||
|
|
b2bdc73164 | ||
|
|
cbaf0a3864 | ||
|
|
103563b375 | ||
|
|
595429fdde | ||
|
|
b384b27546 | ||
|
|
0b996245d5 | ||
|
|
1ee91dd690 | ||
|
|
f9e602c44c | ||
|
|
4fa827ddfd | ||
|
|
1b3c73618c | ||
|
|
7b08f1cc8c | ||
|
|
7d392e5adb | ||
|
|
f44cc2b60c | ||
|
|
31680898e3 | ||
|
|
318fd7c9e3 | ||
|
|
b10b8ca3f4 | ||
|
|
b0c33308be | ||
|
|
1421c4e9d3 | ||
|
|
f55dfc9025 | ||
|
|
c218970890 | ||
|
|
bd27ce2520 | ||
|
|
44795b65f9 | ||
|
|
af84c4e6bd | ||
|
|
0a28207200 | ||
|
|
8ca2a5832c | ||
|
|
333534dfe7 | ||
|
|
d8ce0d0df0 | ||
|
|
28c3259449 | ||
|
|
0d0ede7b14 | ||
|
|
9ebf6bcc92 | ||
|
|
9d186e9ba3 | ||
|
|
3c3a4526bf | ||
|
|
3094c2ce17 | ||
|
|
393494a728 | ||
|
|
d2725c3f49 | ||
|
|
4d4425a5bf | ||
|
|
c11fbe0868 | ||
|
|
c676689df0 | ||
|
|
0f5625d721 | ||
|
|
6f5e6bd0f1 | ||
|
|
a0af092ecb | ||
|
|
3a07a194ab | ||
|
|
682ffa7cf5 | ||
|
|
dc4a4ca9d3 | ||
|
|
33ac85f637 | ||
|
|
efd9421dc9 | ||
|
|
9b696a872b | ||
|
|
aab515b16c | ||
|
|
f220628354 | ||
|
|
322bc5dc87 | ||
|
|
75c3bda866 | ||
|
|
b2b776169b | ||
|
|
8e8d7ae614 | ||
|
|
fa1af5a2cb | ||
|
|
a27e4072b4 | ||
|
|
fad26a49be | ||
|
|
1742159345 | ||
|
|
ed8d2cb482 | ||
|
|
8e1f022810 | ||
|
|
4c25072c18 | ||
|
|
564ec32520 | ||
|
|
a743feb040 | ||
|
|
b028667de1 | ||
|
|
41888f84bc | ||
|
|
6a918dbde9 | ||
|
|
578230e32c | ||
|
|
d486b03089 | ||
|
|
8559334339 | ||
|
|
968c6ed8be | ||
|
|
8893d9edc4 | ||
|
|
43753ec113 | ||
|
|
2ec3e4a912 | ||
|
|
a086745ec0 | ||
|
|
51156d0582 | ||
|
|
ea9925f489 | ||
|
|
026b96b8f2 | ||
|
|
136b2ae37f | ||
|
|
97009a2748 | ||
|
|
e123f7c743 | ||
|
|
e28bde6ccd | ||
|
|
a976f418e3 | ||
|
|
3c701e780c | ||
|
|
769dd174bd | ||
|
|
e92514ee63 | ||
|
|
8ebc517b41 | ||
|
|
91b03e52f1 | ||
|
|
a22406c9f8 | ||
|
|
ebc3666c6f | ||
|
|
f1fee1239b | ||
|
|
8769fa0156 | ||
|
|
f4d7338687 | ||
|
|
6a8c3f82ef | ||
|
|
05bcbfa28c | ||
|
|
6a55500c1b | ||
|
|
a5a5ac60ba | ||
|
|
6ed7f47b3f | ||
|
|
a3e94855f9 | ||
|
|
503da97a79 | ||
|
|
088a93e023 | ||
|
|
30f71ad632 | ||
|
|
0026849821 | ||
|
|
cf5a310286 | ||
|
|
72384ff2cb | ||
|
|
c33ef01af8 | ||
|
|
11116bdcb7 | ||
|
|
09a5cf4e68 | ||
|
|
a9172129d2 | ||
|
|
3ec06705ca | ||
|
|
5fb0d38ad8 | ||
|
|
9ed5cddb89 | ||
|
|
4e97fb0e58 | ||
|
|
1688e373bc | ||
|
|
cada0b6df1 | ||
|
|
91358010a0 | ||
|
|
51024d3821 | ||
|
|
220d6ed494 | ||
|
|
b5ed3edc8f | ||
|
|
73c96f35c1 | ||
|
|
b8d79d9132 | ||
|
|
507c71f64c | ||
|
|
ce0067d8eb | ||
|
|
c0b238b19a | ||
|
|
d961cf21fb | ||
|
|
1f7622e4c4 | ||
|
|
04fe7e61de | ||
|
|
3e6607585f | ||
|
|
0b2213dd41 | ||
|
|
08725c44c6 | ||
|
|
75a66b716e | ||
|
|
0c05e6593e | ||
|
|
c43ab50d66 | ||
|
|
26e7da0b96 | ||
|
|
98b1c7a38c | ||
|
|
ccdbec619c | ||
|
|
47001c3303 | ||
|
|
8e50fecccc | ||
|
|
938fcdd1a6 | ||
|
|
9ede57b228 | ||
|
|
e074d02714 | ||
|
|
e26de9c1d1 | ||
|
|
7dc3835a54 | ||
|
|
117a8cafca | ||
|
|
8a0a0aef5c | ||
|
|
4750e15c20 | ||
|
|
747ce9b1f1 | ||
|
|
7fa874d9ef | ||
|
|
5febc3e07d | ||
|
|
2244bf2ba2 | ||
|
|
29cb62af3d | ||
|
|
f92d2e3f7c | ||
|
|
a9b75d059b | ||
|
|
e6f289deb6 | ||
|
|
0e22618d93 | ||
|
|
c5bb074573 | ||
|
|
4e8fc6d198 | ||
|
|
618d673947 | ||
|
|
4cd5529027 | ||
|
|
5ac08ec3aa |
0
.gitignore
vendored
Executable file → Normal file
0
.gitignore
vendored
Executable file → Normal file
103
.gitlab-ci.yml
103
.gitlab-ci.yml
@@ -1,8 +1,7 @@
|
||||
# Select image from https://hub.docker.com/_/php/
|
||||
#image: php:7.2
|
||||
# Use a prepared Hubzilla image to optimise pipeline run
|
||||
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.2
|
||||
|
||||
#image: php:7.3
|
||||
# Use a prepared Hubzilla image to optimise pipeline duration
|
||||
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
|
||||
|
||||
stages:
|
||||
- test
|
||||
@@ -32,55 +31,29 @@ 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 ./composer.phar install --no-progress
|
||||
# php.ini settings
|
||||
- echo 'xdebug.mode=coverage' >> /usr/local/etc/php/php.ini
|
||||
|
||||
|
||||
# test PHP7 with MySQL 5.7
|
||||
php7.2_mysql 1/2:
|
||||
# hidden job definition with template for MySQL/MariaDB
|
||||
.job_template_mysql: &job_definition_mysql
|
||||
stage: test
|
||||
services:
|
||||
- mysql:5.7
|
||||
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
|
||||
|
||||
|
||||
# test PHP7 with MySQL latest (8)
|
||||
php7.2_mysql 2/2:
|
||||
stage: test
|
||||
services:
|
||||
- name: mysql:latest
|
||||
command: ["--default-authentication-plugin=mysql_native_password"]
|
||||
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
|
||||
|
||||
|
||||
# test PHP7 with MariaDB latest (10.3)
|
||||
php7.2_mariadb:
|
||||
stage: test
|
||||
services:
|
||||
- name: mariadb:latest
|
||||
alias: mysql
|
||||
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
|
||||
|
||||
|
||||
# test PHP7 with PostgreSQL latest
|
||||
php7.2_postgres:
|
||||
# hidden job definition with template for PostgreSQL
|
||||
.job_template_postgres: &job_definition_postgres
|
||||
stage: test
|
||||
services:
|
||||
- postgres:latest
|
||||
@@ -95,7 +68,10 @@ php7.2_postgres:
|
||||
#- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt;"
|
||||
# Run the actual tests
|
||||
- vendor/bin/phpunit --configuration tests/phpunit-pgsql.xml --testdox
|
||||
artifacts:
|
||||
|
||||
# hidden job definition with artifacts config template
|
||||
.artifacts_template:
|
||||
artifacts: &artifacts_template
|
||||
expire_in: 1 week
|
||||
# Gitlab should show the results, but has problems parsing PHPUnit's junit file.
|
||||
reports:
|
||||
@@ -106,7 +82,52 @@ php7.2_postgres:
|
||||
- tests/results/
|
||||
|
||||
|
||||
# Generate Doxygen API Documentation and deploy it at GitLab pages
|
||||
# PHP7.3 with MySQL 5.7
|
||||
php7.3_mysql5.7:
|
||||
<<: *job_definition_mysql
|
||||
services:
|
||||
- mysql:5.7
|
||||
|
||||
|
||||
# PHP7.3 with MySQL 8 (latest)
|
||||
php7.3_mysql8:
|
||||
<<: *job_definition_mysql
|
||||
services:
|
||||
- name: mysql:8
|
||||
command: ["--default-authentication-plugin=mysql_native_password"]
|
||||
|
||||
|
||||
# PHP7.3 with MariaDB 10.2
|
||||
php7.3_mariadb10.2:
|
||||
<<: *job_definition_mysql
|
||||
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
|
||||
|
||||
|
||||
# Generate Doxygen API Documentation and deploy it as GitLab pages
|
||||
pages:
|
||||
stage: deploy
|
||||
cache: {}
|
||||
|
||||
@@ -1,22 +1,10 @@
|
||||
# Hubzilla at Home next to your Router
|
||||
|
||||
Run hubzilla-setup.sh for an unattended installation of hubzilla.
|
||||
|
||||
The script is known to work without adjustments with
|
||||
|
||||
+ Hardware
|
||||
- Mini-PC with Debian-9.5-amd64, or
|
||||
- Rapberry 3 with Raspbian, Debian-9.5
|
||||
+ DynDNS
|
||||
- selfHOST.de
|
||||
- freedns.afraid.org
|
||||
# How to use
|
||||
|
||||
## Disclaimers
|
||||
|
||||
- This script does work with Debian 9 only.
|
||||
- This script has to be used on a fresh debian install only (it does not take account for a possibly already installed and configured webserver or sql implementation).
|
||||
|
||||
# Step-by-Step Overwiew
|
||||
- This script does work with Debian 10 only.
|
||||
- This script has to be used on a fresh debian install only (it does not take account for a possibly already installed and configured webserver or sql implementation). You may use it to install several hub/instances on the same server, though.
|
||||
|
||||
## Preconditions
|
||||
|
||||
@@ -28,97 +16,99 @@ Hardware
|
||||
|
||||
Software
|
||||
|
||||
+ Fresh installation of Debian 9 (Stretch)
|
||||
+ Router with open ports 80 and 443 for your Hub
|
||||
+ Fresh installation of Debian 10 (Buster)
|
||||
+ Router with open ports 80 and 443 for your web server
|
||||
|
||||
## The basic steps (quick overview)
|
||||
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
|
||||
- git clone https://framagit.org/hubzilla/core.git html (you can replace "html" with any folder name you like, which you'll have to do if you plan to have more than one hub/instance running on your server)
|
||||
- cd html/.homeinstall
|
||||
- cp hubzilla-config.txt.template hubzilla-config.txt
|
||||
- nano hubzilla-config.txt
|
||||
- cp zotserver-config.txt.template zotserver-config.txt
|
||||
- nano zotserver-config.txt
|
||||
- Read the comments carefully
|
||||
- Enter your values: db pass, domain, values for dyn DNS
|
||||
- Make sure your external drive (for backups) is mounted
|
||||
- hubzilla-setup.sh as root
|
||||
- ... wait, wait, wait until the script is finised
|
||||
- reboot
|
||||
+ Open your domain with a browser and step throught the initial configuration of hubzilla.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the check of the mail address fails when you try to register the very first user in the browser. Do...
|
||||
|
||||
cd /var/www/html
|
||||
util/config system.do_not_check_dns 1
|
||||
- Prepare your external disk for backups
|
||||
- ./zotserver-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 hub/instance.
|
||||
|
||||
## Optional - Set path to imagemagick
|
||||
|
||||
In Admin settings of hubzilla or via terminal
|
||||
In Admin settings of your hub/instance or via terminal
|
||||
|
||||
cd /var/www/html
|
||||
cd /var/www/html (or the custom path you chose)
|
||||
util/config system.imagick_convert_path /usr/bin/convert
|
||||
|
||||
# Step-by-Step in Detail
|
||||
## Optional - Switch verification of email on/off
|
||||
|
||||
## Preparations Hardware
|
||||
Do this just before you register the first user.
|
||||
|
||||
### Mini-PC
|
||||
In Admin settings of your hub/instance or via terminal
|
||||
|
||||
### Recommended: USB Drive for Backups
|
||||
cd /var/www/html
|
||||
|
||||
The installation will create a daily backup written to an external drive.
|
||||
Check the current setting
|
||||
|
||||
The USB drive must be compatible with the filesystems
|
||||
util/config system verify_email
|
||||
|
||||
- ext4 (if you do not want to encrypt the USB)
|
||||
- LUKS + ext4 (if you want to encrypt the USB)
|
||||
Switch the verification on/off (1/0)
|
||||
|
||||
The backup includes
|
||||
util/config system verify_email 0
|
||||
|
||||
- Hubzilla DB
|
||||
- Hubzilla installation /var/www/html
|
||||
- Certificates for letsencrypt
|
||||
## What the script will do for you...
|
||||
|
||||
## Preparations Software
|
||||
+ install everything required by your hub/instance, basically a web server (Apache or Nginx), PHP, a database (MySQL), certbot,...
|
||||
+ create a database
|
||||
+ run certbot to have everything for a secure connection (httpS)
|
||||
+ create a script for daily maintenance
|
||||
- backup to external disk (certificates, database, /var/www/)
|
||||
- renew certfificate (letsencrypt)
|
||||
- update of your hub/instance (git)
|
||||
- update of Debian
|
||||
- restart
|
||||
+ create cron jobs for
|
||||
- DynDNS (selfHOST.de or freedns.afraid.org) every 5 minutes
|
||||
- Run.php for your hub/instance every 10 minutes
|
||||
- daily maintenance script every day at 05:30
|
||||
|
||||
### Install Debian Linux on the Mini-PC
|
||||
The script is known to work without adjustments with
|
||||
|
||||
Download the stable Debian at https://www.debian.org/
|
||||
(Debian 8 is no longer supported.)
|
||||
+ Hardware
|
||||
- Mini-PC with Debian 10 (stretch), or
|
||||
- Rapberry 3 with Raspbian, Debian 10
|
||||
- Rapberry 4 with Raspbian, Debian 10
|
||||
+ DynDNS
|
||||
- selfHOST.de
|
||||
- freedns.afraid.org
|
||||
|
||||
Create bootable USB drive with Debian on it.You could use
|
||||
|
||||
- unetbootin, https://en.wikipedia.org/wiki/UNetbootin
|
||||
- or simply the linux command "dd"
|
||||
|
||||
Example for command dd...
|
||||
|
||||
su -
|
||||
dd if=2018-10-09-raspbian-stretch.img of=/dev/mmcblk0
|
||||
|
||||
Do not forget to unmount the SD card before and check if unmounted like in this example...
|
||||
|
||||
su -
|
||||
umount /dev/mmcblk0*
|
||||
df -h
|
||||
The script can install [Hubzilla](https://zotlabs.org/page/hubzilla/hubzilla-project), [Zap](https://zotlabs.com/zap/) and [Mistpark 2020, aka "Misty"](https://zotlabs.com/misty/). Make sure to use the correct GIT repositories.
|
||||
|
||||
|
||||
Switch off your mini pc, plug in your USB drive and start the mini pc from the
|
||||
stick. Install Debian. Follow the instructions of the installation.
|
||||
# Step-by-Step - some Details
|
||||
|
||||
### Configure your Router
|
||||
## Preparations
|
||||
|
||||
Open the ports 80 and 443 on your router for your Debian
|
||||
## 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
|
||||
|
||||
Your Hubzilla must be reachable by a domain that you can type in your browser
|
||||
Follow the instructions in .homeinstall/zotserver-config.txt.
|
||||
|
||||
In short...
|
||||
|
||||
Your Hubzilla server must be reachable by a domain that you can type in your browser
|
||||
|
||||
cooldomain.org
|
||||
|
||||
@@ -132,105 +122,13 @@ There are two ways to get a domain...
|
||||
|
||||
...for example buy at selfHOST.de
|
||||
|
||||
The cost are around 10,- € once and 1,50 € per month (2017).
|
||||
The cost is 1,50 € per month (2019).
|
||||
|
||||
### Method 2: Register a free subdomain
|
||||
|
||||
...for example register at freedns.afraid.org
|
||||
|
||||
Follow the instructions in .homeinstall/hubzilla-config.txt.
|
||||
|
||||
|
||||
## Install Hubzilla on your Debian
|
||||
|
||||
Login to your debian
|
||||
(Provided your username is "you" and the name of the mini pc is "debian". You
|
||||
could take the IP address instead of "debian")
|
||||
|
||||
ssh -X you@debian
|
||||
|
||||
Change to root user
|
||||
|
||||
su -l
|
||||
|
||||
Install git
|
||||
|
||||
apt-get install git
|
||||
|
||||
Make the directory for apache and change diretory to it
|
||||
|
||||
mkdir /var/www
|
||||
cd /var/www/
|
||||
|
||||
Clone hubzilla from git ("git pull" will update it later)
|
||||
|
||||
git clone https://framagit.org/hubzilla/core.git html
|
||||
|
||||
Change to the install script
|
||||
|
||||
cd html/.homeinstall/
|
||||
|
||||
Copy the template file
|
||||
|
||||
cp hubzilla-config.txt.template hubzilla-config.txt
|
||||
|
||||
Modify the file "hubzilla-config.txt". Read the instructions there carefully and enter your values.
|
||||
|
||||
nano hubzilla-config.txt
|
||||
|
||||
Make sure your external drive (for backups) is plugged in and can be mounted as configured in "hubzilla-config.txt". Otherwise the daily backups will not work.
|
||||
|
||||
Run the script
|
||||
|
||||
./hubzilla-setup.sh
|
||||
|
||||
Wait... The script should not finish with an error message.
|
||||
|
||||
In a webbrowser open your domain.
|
||||
Expected: A test page of hubzilla is shown. All checks there should be
|
||||
successfull. Go on...
|
||||
Expected: A page for the Hubzilla server configuration shows up.
|
||||
|
||||
Leave db server name "127.0.0.1" and port "0" untouched.
|
||||
|
||||
Enter
|
||||
|
||||
- DB user name = hubzilla
|
||||
- DB pass word = This is the password you entered in "hubzilla-config.txt"
|
||||
- DB name = hubzilla
|
||||
|
||||
Leave db type "MySQL" untouched.
|
||||
|
||||
Follow the instructions in the next pages.
|
||||
|
||||
Recommended: Set path to imagemagick
|
||||
|
||||
- in admin settings of hubzilla or
|
||||
- via terminal
|
||||
|
||||
util/config system.imagick_convert_path /usr/bin/convert
|
||||
|
||||
After the daily script was executed at 05:30 (am)
|
||||
|
||||
- look at /var/www/html/hubzilla-daily.log
|
||||
- check your backup on the external drive
|
||||
- optionally view the daily log under yourdomain.org/admin/logs/
|
||||
- set the logfile to var/www/html/hubzilla-daily.log
|
||||
|
||||
|
||||
## Install Hubzilla in a Virtual Machine for Test Purposes
|
||||
|
||||
Modify the file "hubzilla-config.txt".
|
||||
|
||||
nano hubzilla-config.txt
|
||||
|
||||
There use
|
||||
|
||||
le_domain=localhost
|
||||
|
||||
## Note for the Rasperry
|
||||
|
||||
The script was tested with an Raspberry 3 under Raspian (Debian 9.5, 2018-10-09-raspbian-stretch.img).
|
||||
## Note on Rasperry
|
||||
|
||||
It is recommended to run the Raspi without graphical frontend (X-Server). Use...
|
||||
|
||||
@@ -240,12 +138,6 @@ to boot the Rapsi to the client console.
|
||||
|
||||
DO NOT FORGET TO CHANGE THE DEFAULT PASSWORD FOR USER PI!
|
||||
|
||||
If the validation of the mail address fails for the very first registered user...
|
||||
This used to happen on some *bsd distros but there was some work to fix that a year ago (2017).
|
||||
|
||||
So if your system isn't registered in DNS or DNS isn't active do
|
||||
|
||||
cd /var/www/html
|
||||
util/config system.do_not_check_dns 1
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
###############################################
|
||||
### MANDATORY - database password #############
|
||||
#
|
||||
# Please give your database password
|
||||
# Example: db_pass=pass_word_with_no_blanks_in_it
|
||||
# Example: db_pass="this password has blanks in it"
|
||||
db_pass=
|
||||
|
||||
###############################################
|
||||
### MANDATORY - let's encrypt #################
|
||||
#
|
||||
# Hubilla 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
|
||||
#
|
||||
# Example: my.cooldomain.org
|
||||
# Example: cooldomain.org
|
||||
#
|
||||
# Example: localhost (test installation without certificates for httpS)
|
||||
#
|
||||
# Email is optional
|
||||
#
|
||||
#
|
||||
le_domain=
|
||||
le_email=
|
||||
|
||||
###############################################
|
||||
### OPTIONAL - selfHOST - dynamic IP address ##
|
||||
#
|
||||
# 1. Register a domain at selfhost.de
|
||||
# - choose offer "DOMAIN dynamisch" 1,50€/mon at 08.01.2016
|
||||
# 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 - Backup to external device ######
|
||||
#
|
||||
# The script can use an external device for the daily backup.
|
||||
# The file system of the device (USB stick for example) must be compatible with
|
||||
#
|
||||
# - encrypted LUKS + ext4, or
|
||||
# - ext4
|
||||
#
|
||||
# You should test to mount the device befor you run the script
|
||||
# (hubzilla-setup.sh).
|
||||
# How to find your (pluged-in) devices?
|
||||
#
|
||||
# fdisk -l
|
||||
#
|
||||
# Provided your device was listed as is /dev/sdb1. You could check with:
|
||||
#
|
||||
# blkid | grep /dev/sdb1
|
||||
#
|
||||
# Try to decrypt
|
||||
# (You might install cryptsetup befor using apt-get install.
|
||||
#
|
||||
# apt-get install cryptsetup
|
||||
# cryptsetup luksOpen /dev/sdb1 cryptobackup
|
||||
#
|
||||
# Try to mount
|
||||
# You might create the directory /media/hubzilla_backup it it does not exist
|
||||
# using mkdir.
|
||||
#
|
||||
# mkdir /media/hubzilla_backup
|
||||
# mount /dev/mapper/cryptobackup /media/hubzilla_backup
|
||||
#
|
||||
# Unmounting device goes like this
|
||||
#
|
||||
# umount /media/hubzilla_backup
|
||||
# cryptsetup luksClose cryptobackup
|
||||
#
|
||||
# To check if still mounted
|
||||
#
|
||||
# lsof /media/hubzilla_backup
|
||||
#
|
||||
# If you leave the following parameters
|
||||
#
|
||||
# - "backup_device_name" and
|
||||
# - "backup_device_pass"
|
||||
#
|
||||
# empty the script will create daily backups on the internal disk (which could
|
||||
# save you as well).
|
||||
#
|
||||
# Example: backup_device_name=/dev/sdc1
|
||||
#
|
||||
# Leave "backup_device_pass=" empty if the external device is not encrypted.
|
||||
#
|
||||
backup_device_name=
|
||||
backup_device_pass=
|
||||
|
||||
|
||||
###############################################
|
||||
### OPTIONAL - do not mess with things below ##
|
||||
# (...if you are not certain)
|
||||
#
|
||||
# Usually you are done here
|
||||
# Everything below is OPTIONAL
|
||||
#
|
||||
###############################################
|
||||
#
|
||||
# Database for hubzilla
|
||||
hubzilla_db_name=hubzilla
|
||||
hubzilla_db_user=hubzilla
|
||||
hubzilla_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
|
||||
|
||||
@@ -1,850 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# How to use
|
||||
# ----------
|
||||
#
|
||||
# This file automates the installation of hubzilla under Debian Linux
|
||||
#
|
||||
# 1) Copy the file "hubzilla-config.txt.template" to "hubzilla-config.txt"
|
||||
# Follow the instuctions there
|
||||
#
|
||||
# 2) Switch to user "root" by typing "su -"
|
||||
#
|
||||
# 3) Run with "./hubzilla-setup.sh"
|
||||
# If this fails check if you can execute the script.
|
||||
# - To make it executable type "chmod +x hubzilla-setup.sh"
|
||||
# - or run "bash hubzilla-setup.sh"
|
||||
#
|
||||
#
|
||||
# What does this script do basically?
|
||||
# -----------------------------------
|
||||
#
|
||||
# This file automates the installation of hubzilla under Debian Linux
|
||||
# - install
|
||||
# * apache webserer,
|
||||
# * php,
|
||||
# * mysql - the database for hubzilla,
|
||||
# * phpmyadmin,
|
||||
# * git to download and update hubzilla itself
|
||||
# - download hubzilla core and addons
|
||||
# - configure cron
|
||||
# * "poller.php" for regular background prozesses of hubzilla
|
||||
# * to_do "apt-get update" and "apt-get dist-upgrade" to keep linux
|
||||
# up-to-date
|
||||
# * to_do backup hubzillas database and files (rsnapshot)
|
||||
# - configure dynamic ip with cron
|
||||
# - to_do letsencrypt
|
||||
# - to_do redirection to https
|
||||
#
|
||||
#
|
||||
# Discussion
|
||||
# ----------
|
||||
#
|
||||
# Security - password is the same for mysql-server, phpmyadmin and hubzilla db
|
||||
# - The script runs into installation errors for phpmyadmin if it uses
|
||||
# different passwords. For the sake of simplicity one singel password.
|
||||
#
|
||||
# Security - suhosin for PHP
|
||||
# - The script does not install suhosin.
|
||||
# - Is the security package suhosin usefull or not usefull?
|
||||
#
|
||||
# Hubzilla - email verification
|
||||
# - The script switches off email verification off in all htconfig.tpl.
|
||||
# Example: /var/www/html/view/en/htconfig.tpl
|
||||
# - Is this a silly idea or not?
|
||||
#
|
||||
#
|
||||
# Remove Hubzilla (for a fresh start using the script)
|
||||
# ----------------------------------------------------
|
||||
#
|
||||
# You could use /var/www/hubzilla-remove.sh
|
||||
# that is created by hubzilla-setup.sh.
|
||||
#
|
||||
# The script will remove (almost everything) what was installed by the script.
|
||||
# After the removal you could run the script again to have a fresh install
|
||||
# of all applications including hubzilla and its database.
|
||||
#
|
||||
# How to restore from backup
|
||||
# --------------------------
|
||||
#
|
||||
# Daily backup
|
||||
# - - - - - -
|
||||
#
|
||||
# The installation
|
||||
# - writes a script /var/www/hubzilla-daily.sh
|
||||
# - creates a daily cron that runs the hubzilla-daily.sh
|
||||
#
|
||||
# hubzilla-daily.sh makes a (daily) backup of all relevant files
|
||||
# - /var/lib/mysql/ > hubzilla database
|
||||
# - /var/www/html/ > hubzilla from github
|
||||
# - /var/www/letsencrypt/ > certificates
|
||||
#
|
||||
# hubzilla-daily.sh writes the backup
|
||||
# - either to an external disk compatible to LUKS+ext4 (see hubzilla-config.txt)
|
||||
# - or to /var/cache/rsnapshot in case the external disk is not plugged in
|
||||
#
|
||||
# Restore backup
|
||||
# - - - - - - -
|
||||
#
|
||||
# This was not tested yet.
|
||||
# Bacically you can copy the files from the backup to the server.
|
||||
#
|
||||
# Credits
|
||||
# -------
|
||||
#
|
||||
# The script is based on Thomas Willinghams script "debian-setup.sh"
|
||||
# which he used to install the red#matrix.
|
||||
#
|
||||
# The script uses another script from https://github.com/lukas2511/letsencrypt.sh
|
||||
#
|
||||
# The documentation for bash is here
|
||||
# https://www.gnu.org/software/bash/manual/bash.html
|
||||
#
|
||||
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 9' /etc/issue
|
||||
then
|
||||
die "Linux 9 (stretch) 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
|
||||
# backup is important and should be checked
|
||||
if [ -n "$backup_device_name" ]
|
||||
then
|
||||
if [ ! -d "$backup_mount_point" ]
|
||||
then
|
||||
mkdir "$backup_mount_point"
|
||||
fi
|
||||
device_mounted=0
|
||||
if fdisk -l | grep -i "$backup_device_name.*linux"
|
||||
then
|
||||
print_info "ok - filesystem of external device is linux"
|
||||
if [ -n "$backup_device_pass" ]
|
||||
then
|
||||
echo "$backup_device_pass" | cryptsetup luksOpen $backup_device_name cryptobackup
|
||||
if mount /dev/mapper/cryptobackup /media/hubzilla_backup
|
||||
then
|
||||
device_mounted=1
|
||||
print_info "ok - could encrypt and mount external backup device"
|
||||
umount /media/hubzilla_backup
|
||||
else
|
||||
print_warn "backup to external device will fail because encryption failed"
|
||||
fi
|
||||
cryptsetup luksClose cryptobackup
|
||||
else
|
||||
if mount $backup_device_name /media/hubzilla_backup
|
||||
then
|
||||
device_mounted=1
|
||||
print_info "ok - could mount external backup device"
|
||||
umount /media/hubzilla_backup
|
||||
else
|
||||
print_warn "backup to external device will fail because mount failed"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
print_warn "backup to external device will fail because filesystem is either not linux or 'backup_device_name' is not correct in $configfile"
|
||||
fi
|
||||
if [ $device_mounted == 0 ]
|
||||
then
|
||||
die "backup device not ready"
|
||||
fi
|
||||
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
|
||||
print_info "updated and upgraded linux"
|
||||
}
|
||||
|
||||
function check_install {
|
||||
if [ -z "`which "$1" 2>/dev/null`" ]
|
||||
then
|
||||
# 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 -q -y install $2
|
||||
print_info "installed $2 installed for $1"
|
||||
else
|
||||
print_warn "$2 already installed"
|
||||
fi
|
||||
}
|
||||
|
||||
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_hubzilla {
|
||||
if [ -d /etc/apache2 ]
|
||||
then
|
||||
print_info "stopping apache webserver..."
|
||||
service apache2 stop
|
||||
fi
|
||||
if [ -f /etc/init.d/mysql ]
|
||||
then
|
||||
print_info "stopping mysql db..."
|
||||
/etc/init.d/mysql stop
|
||||
fi
|
||||
}
|
||||
|
||||
function install_apache {
|
||||
print_info "installing apache..."
|
||||
nocheck_install "apache2 apache2-utils"
|
||||
}
|
||||
|
||||
function install_imagemagick {
|
||||
print_info "installing imagemagick..."
|
||||
nocheck_install "imagemagick"
|
||||
}
|
||||
|
||||
function install_curl {
|
||||
print_info "installing curl..."
|
||||
nocheck_install "curl"
|
||||
}
|
||||
|
||||
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-mcrypt php-gd"
|
||||
sed -i "s/^upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php/7.0/apache2/php.ini
|
||||
sed -i "s/^post_max_size =.*/post_max_size = 100M/g" /etc/php/7.0/apache2/php.ini
|
||||
}
|
||||
|
||||
function install_mysql {
|
||||
# http://www.microhowto.info/howto/perform_an_unattended_installation_of_a_debian_package.html
|
||||
#
|
||||
# To determine the required package name, key and type you can perform
|
||||
# a trial installation then search the configuration database.
|
||||
#
|
||||
# debconf-get-selections | grep mysql-server
|
||||
#
|
||||
# The command debconf-get-selections is provided by the package
|
||||
# debconf-utils, which you may need to install.
|
||||
#
|
||||
# apt-get install debconf-utils
|
||||
#
|
||||
# If you want to supply an answer to a configuration question but do not
|
||||
# want to be prompted for it then this can be arranged by preseeding the
|
||||
# DebConf database with the required information.
|
||||
#
|
||||
# echo mysql-server mysql-server/root_password password xyzzy | debconf-set-selections
|
||||
# echo mysql-server mysql-server/root_password_again password xyzzy | debconf-set-selections
|
||||
#
|
||||
print_info "installing mysql..."
|
||||
if [ -z "$mysqlpass" ]
|
||||
then
|
||||
die "mysqlpass not set in $configfile"
|
||||
fi
|
||||
echo mysql-server mysql-server/root_password password $mysqlpass | debconf-set-selections
|
||||
echo mysql-server mysql-server/root_password_again password $mysqlpass | debconf-set-selections
|
||||
nocheck_install "php-mysql mysql-server mysql-client"
|
||||
}
|
||||
|
||||
function install_phpmyadmin {
|
||||
print_info "installing phpmyadmin..."
|
||||
if [ -z "$phpmyadminpass" ]
|
||||
then
|
||||
die "phpmyadminpass not set in $configfile"
|
||||
fi
|
||||
echo phpmyadmin phpmyadmin/setup-password password $phpmyadminpass | debconf-set-selections
|
||||
echo phpmyadmin phpmyadmin/mysql/app-pass password $phpmyadminpass | debconf-set-selections
|
||||
echo phpmyadmin phpmyadmin/app-password-confirm password $phpmyadminpass | debconf-set-selections
|
||||
echo phpmyadmin phpmyadmin/mysql/admin-pass password $phpmyadminpass | debconf-set-selections
|
||||
echo phpmyadmin phpmyadmin/password-confirm password $phpmyadminpass | debconf-set-selections
|
||||
echo phpmyadmin phpmyadmin/reconfigure-webserver multiselect apache2 | debconf-set-selections
|
||||
nocheck_install "phpmyadmin"
|
||||
|
||||
# It seems to be not neccessary to check rewrite.load because it comes
|
||||
# with the installation. To be sure you could check this manually by:
|
||||
#
|
||||
# nano /etc/apache2/mods-available/rewrite.load
|
||||
#
|
||||
# You should find the content:
|
||||
#
|
||||
# LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so
|
||||
|
||||
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
|
||||
if [ -z "`grep 'Include /etc/phpmyadmin/apache.conf' /etc/apache2/apache2.conf`" ]
|
||||
then
|
||||
echo "Include /etc/phpmyadmin/apache.conf" >> /etc/apache2/apache2.conf
|
||||
fi
|
||||
service apache2 restart
|
||||
/etc/init.d/mysql start
|
||||
}
|
||||
|
||||
function create_hubzilla_db {
|
||||
print_info "creating hubzilla database..."
|
||||
if [ -z "$hubzilla_db_name" ]
|
||||
then
|
||||
die "hubzilla_db_name not set in $configfile"
|
||||
fi
|
||||
if [ -z "$hubzilla_db_user" ]
|
||||
then
|
||||
die "hubzilla_db_user not set in $configfile"
|
||||
fi
|
||||
if [ -z "$hubzilla_db_pass" ]
|
||||
then
|
||||
die "hubzilla_db_pass not set in $configfile"
|
||||
fi
|
||||
Q1="CREATE DATABASE IF NOT EXISTS $hubzilla_db_name;"
|
||||
Q2="GRANT USAGE ON *.* TO $hubzilla_db_user@localhost IDENTIFIED BY '$hubzilla_db_pass';"
|
||||
Q3="GRANT ALL PRIVILEGES ON $hubzilla_db_name.* to $hubzilla_db_user@localhost identified by '$hubzilla_db_pass';"
|
||||
Q4="FLUSH PRIVILEGES;"
|
||||
SQL="${Q1}${Q2}${Q3}${Q4}"
|
||||
mysql -uroot -p$phpmyadminpass -e "$SQL"
|
||||
}
|
||||
|
||||
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 - https://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 https://freedns.afraid.org/dynamic/update.php?$freedns_key > /dev/null 2>&1" >> /etc/crontab
|
||||
echo "*/30 * * * * root wget --no-check-certificate -O - https://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 "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 '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
|
||||
# configure apache
|
||||
apache_le_conf=/etc/apache2/sites-available/le-default.conf
|
||||
if [ -f $apache_le_conf ]
|
||||
then
|
||||
print_info "$apache_le_conf exist already"
|
||||
else
|
||||
cat > $apache_le_conf <<END
|
||||
# letsencrypt default Apache configuration
|
||||
Alias /.well-known/acme-challenge /var/www/letsencrypt
|
||||
|
||||
<Directory /var/www/letsencrypt>
|
||||
Options FollowSymLinks
|
||||
Allow from all
|
||||
</Directory>
|
||||
END
|
||||
a2ensite le-default.conf
|
||||
service apache2 restart
|
||||
fi
|
||||
# download the shell script
|
||||
if [ -d $le_dir ]
|
||||
then
|
||||
print_info "letsenrypt exists already (nothing downloaded > no certificate created and registered)"
|
||||
return 0
|
||||
fi
|
||||
git clone https://github.com/lukas2511/dehydrated $le_dir
|
||||
cd $le_dir
|
||||
# create config file for letsencrypt.sh
|
||||
echo "WELLKNOWN=$le_dir" > $le_dir/config.sh
|
||||
if [ -n "$le_email" ]
|
||||
then
|
||||
echo "CONTACT_EMAIL=$le_email" >> $le_dir/config.sh
|
||||
fi
|
||||
# create domain file for letsencrypt.sh
|
||||
# WATCH THIS:
|
||||
# - It did not work wit "sub.domain.org www.sub.domain.org".
|
||||
# - So just use "sub.domain.org" only!
|
||||
echo "$le_domain" > $le_dir/domains.txt
|
||||
# test apache config for letsencrpyt
|
||||
url_http=http://$le_domain/.well-known/acme-challenge/domains.txt
|
||||
wget_output=$(wget -nv --spider --max-redirect 0 $url_http)
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
die "Failed to load $url_http"
|
||||
fi
|
||||
# accept terms of service of letsencrypt
|
||||
./dehydrated --register --accept-terms
|
||||
# run script dehydrated
|
||||
#
|
||||
./dehydrated --cron --config $le_dir/config.sh
|
||||
}
|
||||
|
||||
function configure_apache_for_https {
|
||||
print_info "configuring apache to use httpS ..."
|
||||
# letsencrypt.sh
|
||||
#
|
||||
# "${BASEDIR}/certs/${domain}/privkey.pem"
|
||||
# "${BASEDIR}/certs/${domain}/cert.pem"
|
||||
# "${BASEDIR}/certs/${domain}/fullchain.pem"
|
||||
#
|
||||
SSLCertificateFile=${le_dir}/certs/${le_domain}/cert.pem
|
||||
SSLCertificateKeyFile=${le_dir}/certs/${le_domain}/privkey.pem
|
||||
SSLCertificateChainFile=${le_dir}/certs/${le_domain}/fullchain.pem
|
||||
if [ ! -f $SSLCertificateFile ]
|
||||
then
|
||||
print_warn "Failed to configure apache for httpS: Missing certificate file $SSLCertificateFile"
|
||||
return 0
|
||||
fi
|
||||
# make sure that the ssl mode is enabled
|
||||
print_info "...configuring apache to use httpS - a2enmod ssl ..."
|
||||
a2enmod ssl
|
||||
# modify apach' ssl conf file
|
||||
if grep -i "ServerName" $sslconf
|
||||
then
|
||||
print_info "seems that apache was already configered to use httpS with $sslconf"
|
||||
else
|
||||
sed -i "s/ServerAdmin.*$/ServerAdmin webmaster@localhost\\n ServerName ${le_domain}/" $sslconf
|
||||
fi
|
||||
sed -i s#/etc/ssl/certs/ssl-cert-snakeoil.pem#$SSLCertificateFile# $sslconf
|
||||
sed -i s#/etc/ssl/private/ssl-cert-snakeoil.key#$SSLCertificateKeyFile# $sslconf
|
||||
sed -i s#/etc/apache2/ssl.crt/server-ca.crt#$SSLCertificateChainFile# $sslconf
|
||||
sed -i s/#SSLCertificateChainFile/SSLCertificateChainFile/ $sslconf
|
||||
# apply changes
|
||||
a2ensite default-ssl.conf
|
||||
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_hubzilla {
|
||||
print_info "installing hubzilla addons..."
|
||||
cd /var/www/html/
|
||||
util/add_addon_repo https://framagit.org/hubzilla/addons.git hzaddons
|
||||
mkdir -p "store/[data]/smarty3"
|
||||
chmod -R 777 store
|
||||
touch .htconfig.php
|
||||
chmod ou+w .htconfig.php
|
||||
cd /var/www/
|
||||
chown -R www-data:www-data html
|
||||
chown root:www-data /var/www/html/
|
||||
chown root:www-data /var/www/html/.htaccess
|
||||
chmod 0644 /var/www/html/.htaccess
|
||||
# try to switch off email registration
|
||||
sed -i "s/verify_email.*1/verify_email'] = 0/" /var/www/html/view/*/ht*
|
||||
if [ -n "`grep -r 'verify_email.*1' /var/www/html/view/`" ]
|
||||
then
|
||||
print_warn "Hubzillas registration prozess might have email verification switched on."
|
||||
fi
|
||||
print_info "installed hubzilla"
|
||||
}
|
||||
|
||||
function rewrite_to_https {
|
||||
print_info "configuring apache to redirect http to httpS ..."
|
||||
htaccessfile=/var/www/html/.htaccess
|
||||
if grep -i "https" $htaccessfile
|
||||
then
|
||||
print_info "...configuring apache to redirect http to httpS was already done in $htaccessfile"
|
||||
else
|
||||
sed -i "s#QSA]#QSA]\\n RewriteCond %{SERVER_PORT} !^443$\\n RewriteRule (.*) https://%{HTTP_HOST}/$1 [R=301,L]#" $htaccessfile
|
||||
fi
|
||||
service apache2 restart
|
||||
}
|
||||
|
||||
# This will allways overwrite both config files
|
||||
# - internal disk
|
||||
# - external disk (LUKS + ext4)
|
||||
# of rsnapshot for hubzilla
|
||||
function install_rsnapshot {
|
||||
print_info "installing rsnapshot..."
|
||||
nocheck_install "rsnapshot"
|
||||
# internal disk
|
||||
cp -f /etc/rsnapshot.conf $snapshotconfig
|
||||
sed -i "s/^cmd_cp/#cmd_cp/" $snapshotconfig
|
||||
sed -i "s/^backup/#backup/" $snapshotconfig
|
||||
echo "backup /var/lib/mysql/ localhost/" >> $snapshotconfig
|
||||
echo "backup /var/www/html/ localhost/" >> $snapshotconfig
|
||||
echo "backup /var/www/letsencrypt/ localhost/" >> $snapshotconfig
|
||||
# external disk
|
||||
if [ -n "$backup_device_name" ]
|
||||
then
|
||||
cp -f /etc/rsnapshot.conf $snapshotconfig_external_device
|
||||
sed -i "s#snapshot_root.*#snapshot_root $backup_mount_point#" $snapshotconfig_external_device
|
||||
sed -i "/alpha/s/6/30/" $snapshotconfig_external_device
|
||||
sed -i "s/^cmd_cp/#cmd_cp/" $snapshotconfig_external_device
|
||||
sed -i "s/^backup/#backup/" $snapshotconfig_external_device
|
||||
if [ -z "`grep 'letsencrypt' $snapshotconfig_external_device`" ]
|
||||
then
|
||||
echo "backup /var/lib/mysql/ localhost/" >> $snapshotconfig_external_device
|
||||
echo "backup /var/www/html/ localhost/" >> $snapshotconfig_external_device
|
||||
echo "backup /var/www/letsencrypt/ localhost/" >> $snapshotconfig_external_device
|
||||
fi
|
||||
else
|
||||
print_info "No backup configuration (rsnapshot) for external device configured. Reason: backup_device_name and/or backup_device_pass not given in $configfile"
|
||||
fi
|
||||
}
|
||||
|
||||
function install_cryptosetup {
|
||||
print_info "installing cryptsetup..."
|
||||
nocheck_install "cryptsetup"
|
||||
}
|
||||
|
||||
function configure_cron_daily {
|
||||
print_info "configuring cron..."
|
||||
# every 10 min for poller.php
|
||||
if [ -z "`grep 'poller.php' /etc/crontab`" ]
|
||||
then
|
||||
echo "*/10 * * * * www-data cd /var/www/html; php Zotlabs/Daemon/Master.php Cron >> /dev/null 2>&1" >> /etc/crontab
|
||||
fi
|
||||
# Run external script daily at 05:30
|
||||
# - stop apache and mysql-server
|
||||
# - backup hubzilla
|
||||
# - update hubzilla core and addon
|
||||
# - update and upgrade linux
|
||||
# - reboot
|
||||
echo "#!/bin/sh" > /var/www/$hubzilladaily
|
||||
echo "#" >> /var/www/$hubzilladaily
|
||||
echo "echo \" \"" >> /var/www/$hubzilladaily
|
||||
echo "echo \"+++ \$(date) +++\"" >> /var/www/$hubzilladaily
|
||||
echo "echo \" \"" >> /var/www/$hubzilladaily
|
||||
echo "echo \"\$(date) - renew certificate...\"" >> /var/www/$hubzilladaily
|
||||
echo "bash $le_dir/dehydrated --cron --config $le_dir/config.sh" >> /var/www/$hubzilladaily
|
||||
echo "#" >> /var/www/$hubzilladaily
|
||||
echo "# stop hubzilla" >> /var/www/$hubzilladaily
|
||||
echo "echo \"\$(date) - stoping apache and mysql...\"" >> /var/www/$hubzilladaily
|
||||
echo "service apache2 stop" >> /var/www/$hubzilladaily
|
||||
echo "/etc/init.d/mysql stop # to avoid inconsistancies" >> /var/www/$hubzilladaily
|
||||
echo "#" >> /var/www/$hubzilladaily
|
||||
echo "# backup" >> /var/www/$hubzilladaily
|
||||
echo "echo \"\$(date) - try to mount external device for backup...\"" >> /var/www/$hubzilladaily
|
||||
echo "backup_device_name=$backup_device_name" >> /var/www/$hubzilladaily
|
||||
echo "backup_device_pass=$backup_device_pass" >> /var/www/$hubzilladaily
|
||||
echo "backup_mount_point=$backup_mount_point" >> /var/www/$hubzilladaily
|
||||
echo "device_mounted=0" >> /var/www/$hubzilladaily
|
||||
echo "if [ -n \"$backup_device_name\" ]" >> /var/www/$hubzilladaily
|
||||
echo "then" >> /var/www/$hubzilladaily
|
||||
echo " if blkid | grep $backup_device_name" >> /var/www/$hubzilladaily
|
||||
echo " then" >> /var/www/$hubzilladaily
|
||||
if [ -n "$backup_device_pass" ]
|
||||
then
|
||||
echo " echo \"decrypting backup device...\"" >> /var/www/$hubzilladaily
|
||||
echo " echo "\"$backup_device_pass\"" | cryptsetup luksOpen $backup_device_name cryptobackup" >> /var/www/$hubzilladaily
|
||||
fi
|
||||
echo " if [ ! -d $backup_mount_point ]" >> /var/www/$hubzilladaily
|
||||
echo " then" >> /var/www/$hubzilladaily
|
||||
echo " mkdir $backup_mount_point" >> /var/www/$hubzilladaily
|
||||
echo " fi" >> /var/www/$hubzilladaily
|
||||
echo " echo \"mounting backup device...\"" >> /var/www/$hubzilladaily
|
||||
if [ -n "$backup_device_pass" ]
|
||||
then
|
||||
echo " if mount /dev/mapper/cryptobackup $backup_mount_point" >> /var/www/$hubzilladaily
|
||||
else
|
||||
echo " if mount $backup_device_name $backup_mount_point" >> /var/www/$hubzilladaily
|
||||
fi
|
||||
echo " then" >> /var/www/$hubzilladaily
|
||||
echo " device_mounted=1" >> /var/www/$hubzilladaily
|
||||
echo " echo \"device $backup_device_name is now mounted. Starting backup...\"" >> /var/www/$hubzilladaily
|
||||
echo " rsnapshot -c $snapshotconfig_external_device alpha" >> /var/www/$hubzilladaily
|
||||
echo " echo \"\$(date) - disk sizes...\"" >> /var/www/$hubzilladaily
|
||||
echo " df -h" >> /var/www/$hubzilladaily
|
||||
echo " echo \"\$(date) - db size...\"" >> /var/www/$hubzilladaily
|
||||
echo " du -h $backup_mount_point | grep mysql/hubzilla" >> /var/www/$hubzilladaily
|
||||
echo " echo \"unmounting backup device...\"" >> /var/www/$hubzilladaily
|
||||
echo " umount $backup_mount_point" >> /var/www/$hubzilladaily
|
||||
echo " else" >> /var/www/$hubzilladaily
|
||||
echo " echo \"failed to mount device $backup_device_name\"" >> /var/www/$hubzilladaily
|
||||
echo " fi" >> /var/www/$hubzilladaily
|
||||
if [ -n "$backup_device_pass" ]
|
||||
then
|
||||
echo " echo \"closing decrypted backup device...\"" >> /var/www/$hubzilladaily
|
||||
echo " cryptsetup luksClose cryptobackup" >> /var/www/$hubzilladaily
|
||||
fi
|
||||
echo " fi" >> /var/www/$hubzilladaily
|
||||
echo "fi" >> /var/www/$hubzilladaily
|
||||
echo "if [ \$device_mounted == 0 ]" >> /var/www/$hubzilladaily
|
||||
echo "then" >> /var/www/$hubzilladaily
|
||||
echo " echo \"device could not be mounted $backup_device_name. No backup written.\"" >> /var/www/$hubzilladaily
|
||||
echo "fi" >> /var/www/$hubzilladaily
|
||||
echo "#" >> /var/www/$hubzilladaily
|
||||
echo "echo \"\$(date) - db size...\"" >> /var/www/$hubzilladaily
|
||||
echo "du -h /var/lib/mysql/ | grep mysql/hubzilla" >> /var/www/$hubzilladaily
|
||||
echo "#" >> /var/www/$hubzilladaily
|
||||
echo "# update" >> /var/www/$hubzilladaily
|
||||
echo "echo \"\$(date) - updating dehydrated...\"" >> /var/www/$hubzilladaily
|
||||
echo "git -C /var/www/letsencrypt/ pull" >> /var/www/$hubzilladaily
|
||||
echo "echo \"\$(date) - updating hubhilla core...\"" >> /var/www/$hubzilladaily
|
||||
echo "(cd /var/www/html/ ; util/udall)" >> /var/www/$hubzilladaily
|
||||
echo "chown -R www-data:www-data /var/www/html/ # make all accessable for the webserver" >> /var/www/$hubzilladaily
|
||||
echo "chown root:www-data /var/www/html/.htaccess" >> /var/www/$hubzilladaily
|
||||
echo "chmod 0644 /var/www/html/.htaccess # www-data can read but not write it" >> /var/www/$hubzilladaily
|
||||
echo "echo \"\$(date) - updating linux...\"" >> /var/www/$hubzilladaily
|
||||
echo "apt-get -q -y update && apt-get -q -y dist-upgrade && apt-get -q -y autoremove # update linux and upgrade" >> /var/www/$hubzilladaily
|
||||
echo "echo \"\$(date) - Backup hubzilla and update linux finished. Rebooting...\"" >> /var/www/$hubzilladaily
|
||||
echo "#" >> /var/www/$hubzilladaily
|
||||
echo "reboot" >> /var/www/$hubzilladaily
|
||||
|
||||
if [ -z "`grep 'hubzilla-daily.sh' /etc/crontab`" ]
|
||||
then
|
||||
echo "30 05 * * * root /bin/bash /var/www/$hubzilladaily >> /var/www/html/hubzilla-daily.log 2>&1" >> /etc/crontab
|
||||
echo "0 0 1 * * root rm /var/www/html/hubzilla-daily.log" >> /etc/crontab
|
||||
fi
|
||||
|
||||
# This is active after either "reboot" or "/etc/init.d/cron reload"
|
||||
print_info "configured cron for updates/upgrades"
|
||||
}
|
||||
|
||||
function write_uninstall_script {
|
||||
print_info "writing uninstall script..."
|
||||
|
||||
cat > /var/www/hubzilla-remove.sh <<END
|
||||
#!/bin/sh
|
||||
#
|
||||
# This script removes Hubzilla.
|
||||
# You might do this for a fresh start using the script.
|
||||
# The script will remove (almost everything) what was installed by the script,
|
||||
# all applications including hubzilla and its database.
|
||||
#
|
||||
# Backup the certificates of letsencrypt (you never know)
|
||||
cp -a /var/www/letsencrypt/ ~/backup_le_certificats
|
||||
#
|
||||
# Removal
|
||||
apt-get remove apache2 apache2-utils libapache2-mod-php5 php5 php-pear php5-xcache php5-curl php5-mcrypt php5-gd php5-mysql mysql-server mysql-client phpmyadmin
|
||||
apt-get purge apache2 apache2-utils libapache2-mod-php5 php5 php-pear php5-xcache php5-curl php5-mcrypt php5-gd php5-mysql mysql-server mysql-client phpmyadmin
|
||||
apt-get autoremove
|
||||
apt-get clean
|
||||
rm /etc/rsnapshot_hubzilla.conf
|
||||
rm /etc/rsnapshot_hubzilla_external_device.conf
|
||||
rm -R /etc/apache2/
|
||||
rm -R /var/lib/mysql/
|
||||
rm -R /var/www
|
||||
rm -R /etc/selfhost/
|
||||
# uncomment the next line if you want to remove the backups
|
||||
# rm -R /var/cache/rsnapshot
|
||||
nano /etc/crontab # remove entries there manually
|
||||
END
|
||||
chmod -x /var/www/hubzilla-remove.sh
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# START OF PROGRAM
|
||||
########################################################################
|
||||
export PATH=/bin:/usr/bin:/sbin:/usr/sbin
|
||||
|
||||
check_sanity
|
||||
|
||||
# Read config file edited by user
|
||||
configfile=hubzilla-config.txt
|
||||
source $configfile
|
||||
|
||||
selfhostdir=/etc/selfhost
|
||||
selfhostscript=selfhost-updater.sh
|
||||
hubzilladaily=hubzilla-daily.sh
|
||||
plugins_update=.homeinstall/plugins_update.sh
|
||||
snapshotconfig=/etc/rsnapshot_hubzilla.conf
|
||||
snapshotconfig_external_device=/etc/rsnapshot_hubzilla_external_device.conf
|
||||
backup_mount_point=/media/hubzilla_backup
|
||||
le_dir=/var/www/letsencrypt
|
||||
sslconf=/etc/apache2/sites-available/default-ssl.conf
|
||||
|
||||
#set -x # activate debugging from here
|
||||
|
||||
check_config
|
||||
stop_hubzilla
|
||||
update_upgrade
|
||||
install_curl
|
||||
install_sendmail
|
||||
install_apache
|
||||
install_imagemagick
|
||||
install_php
|
||||
install_mysql
|
||||
install_phpmyadmin
|
||||
create_hubzilla_db
|
||||
run_freedns
|
||||
install_run_selfhost
|
||||
ping_domain
|
||||
configure_cron_freedns
|
||||
configure_cron_selfhost
|
||||
|
||||
if [ "$le_domain" != "localhost" ]
|
||||
then
|
||||
install_letsencrypt
|
||||
configure_apache_for_https
|
||||
check_https
|
||||
else
|
||||
print_info "is localhost - skipped installation of letsencrypt and configuration of apache for https"
|
||||
fi
|
||||
|
||||
install_hubzilla
|
||||
|
||||
if [ "$le_domain" != "localhost" ]
|
||||
then
|
||||
rewrite_to_https
|
||||
install_rsnapshot
|
||||
else
|
||||
print_info "is localhost - skipped rewrite to https and installation of rsnapshot"
|
||||
fi
|
||||
|
||||
configure_cron_daily
|
||||
|
||||
if [ "$le_domain" != "localhost" ]
|
||||
then
|
||||
install_cryptosetup
|
||||
write_uninstall_script
|
||||
else
|
||||
print_info "is localhost - skipped installation of cryptosetup"
|
||||
fi
|
||||
|
||||
#set +x # stop debugging from here
|
||||
|
||||
144
.homeinstall/nginx-zotserver.conf.template
Normal file
144
.homeinstall/nginx-zotserver.conf.template
Normal file
@@ -0,0 +1,144 @@
|
||||
##
|
||||
# Hubzilla/Zap/Mistpark/Osada Nginx block configuration template
|
||||
# based on the example created by Olaf Conradi
|
||||
#
|
||||
# The files generated with this template will be added to
|
||||
# /etc/nginx/sites-available & /etc/nginx/sites-enabled (symlink)
|
||||
##
|
||||
|
||||
##
|
||||
# You should look at the following URL's in order to grasp a solid understanding
|
||||
# of Nginx configuration files in order to fully unleash the power of Nginx.
|
||||
#
|
||||
# http://wiki.nginx.org/Pitfalls
|
||||
# http://wiki.nginx.org/QuickStart
|
||||
# http://wiki.nginx.org/Configuration
|
||||
##
|
||||
|
||||
##
|
||||
# This configuration assumes
|
||||
# You filled the zotserver-config.txt file
|
||||
# Your domain/subdomain is functionnal
|
||||
# You want all traffic to be https
|
||||
# You have PHP FastCGI Process Manager (php-fpm) running on localhost
|
||||
##
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name SERVER_NAME;
|
||||
|
||||
# HTTP > HTTPS #
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
##
|
||||
# Configure Red with SSL
|
||||
#
|
||||
# All requests are routed to the front controller
|
||||
# except for certain known file types like images, css, etc.
|
||||
# Those are served statically whenever possible with a
|
||||
# fall back to the front controller (needed for avatars, for example)
|
||||
##
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name SERVER_NAME;
|
||||
|
||||
ssl on;
|
||||
ssl_certificate /etc/letsencrypt/live/SERVER_NAME/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/SERVER_NAME/privkey.pem;
|
||||
ssl_session_timeout 5m;
|
||||
# DO WE NEED TO REVIEW THE FOLLOWING SETTINGS?
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
fastcgi_param HTTPS on;
|
||||
|
||||
charset utf-8;
|
||||
root INSTALL_PATH;
|
||||
index index.php;
|
||||
access_log /var/log/nginx/ZOTSERVER_LOG;
|
||||
|
||||
#Uncomment the following line to include a standard configuration file
|
||||
#Note that the most specific rule wins and your standard configuration
|
||||
#will therefore *add* to this file, but not override it.
|
||||
#include standard.conf
|
||||
|
||||
# allow uploads up to 20MB in size
|
||||
client_max_body_size 20m;
|
||||
client_body_buffer_size 128k;
|
||||
|
||||
include mime.types;
|
||||
|
||||
# rewrite to front controller as default rule
|
||||
location / {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^(.*)$ /index.php?req=$1;
|
||||
}
|
||||
}
|
||||
|
||||
# make sure webfinger and other well known services aren't blocked
|
||||
# by denying dot files and rewrite request to the front controller
|
||||
location ^~ /.well-known/ {
|
||||
allow all;
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^(.*)$ /index.php?req=$1;
|
||||
}
|
||||
}
|
||||
|
||||
# statically serve these file types when possible
|
||||
# otherwise fall back to front controller
|
||||
# allow browser to cache them
|
||||
# added .htm for advanced source code editor library
|
||||
# location ~* \.(jpg|jpeg|gif|png|ico|css|js|htm|html|map|ttf|woff|woff2|svg)$ {
|
||||
# expires 30d;
|
||||
# try_files $uri /index.php?req=$uri&$args;
|
||||
# }
|
||||
# SHOULD WE UNCOMMENT THE ABOVE LINES ?
|
||||
|
||||
# block these file types
|
||||
location ~* \.(tpl|md|tgz|log|out)$ {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
|
||||
# or a unix socket
|
||||
location ~* \.php$ {
|
||||
# IS THE FOLLOWING STILL RELEVANT AS OF AUGUST 2020?
|
||||
# Zero-day exploit defense.
|
||||
# http://forum.nginx.org/read.php?2,88845,page=3
|
||||
# Won't work properly (404 error) if the file is not stored on this
|
||||
# server, which is entirely possible with php-fpm/php-fcgi.
|
||||
# Comment the 'try_files' line out if you set up php-fpm/php-fcgi on
|
||||
# another machine. And then cross your fingers that you won't get hacked.
|
||||
try_files $uri =404;
|
||||
|
||||
# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
|
||||
# With php5-cgi alone:
|
||||
# fastcgi_pass 127.0.0.1:9000;
|
||||
|
||||
# With php-fpm:
|
||||
fastcgi_pass unix:PHP_FPM_SOCK;
|
||||
|
||||
include fastcgi_params;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
|
||||
# deny access to all dot files
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
}
|
||||
|
||||
#deny access to store
|
||||
|
||||
location ~ /store {
|
||||
deny all;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
178
.homeinstall/zotserver-config.txt.template
Normal file
178
.homeinstall/zotserver-config.txt.template
Normal file
@@ -0,0 +1,178 @@
|
||||
###############################################
|
||||
### 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 - Webserver choice ###############
|
||||
#
|
||||
# Please indicate if you want to choose Nginx
|
||||
# or Apache as your web server
|
||||
#
|
||||
# Valid strings are nginx or apache (lower case),
|
||||
# any other will stop the setup script.
|
||||
#
|
||||
webserver=apache
|
||||
|
||||
|
||||
###############################################
|
||||
### 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 - Backup to external device ######
|
||||
#
|
||||
# The script can use an external device for the daily backup.
|
||||
# The file system of the device (USB stick for example) must be compatible with
|
||||
#
|
||||
# - encrypted LUKS + ext4, or
|
||||
# - ext4
|
||||
#
|
||||
# You should test to mount the device before you run the script
|
||||
# (hubzilla-setup.sh).
|
||||
# How to find your (pluged-in) devices?
|
||||
#
|
||||
# fdisk -l
|
||||
#
|
||||
# Provided your device was listed as is /dev/sdb1. You could check with:
|
||||
#
|
||||
# blkid | grep /dev/sdb1
|
||||
#
|
||||
# Try to decrypt
|
||||
# (You might install cryptsetup befor using apt-get install.
|
||||
#
|
||||
# apt-get install cryptsetup
|
||||
# cryptsetup luksOpen /dev/sdb1 cryptobackup
|
||||
#
|
||||
# Try to mount
|
||||
# You might create the directory /media/hubzilla_backup it it does not exist
|
||||
# using mkdir.
|
||||
#
|
||||
# mkdir /media/hubzilla_backup
|
||||
# mount /dev/mapper/cryptobackup /media/hubzilla_backup
|
||||
#
|
||||
# Unmounting device goes like this
|
||||
#
|
||||
# umount /media/hubzilla_backup
|
||||
# cryptsetup luksClose cryptobackup
|
||||
#
|
||||
# To check if still mounted
|
||||
#
|
||||
# lsof /media/hubzilla_backup
|
||||
#
|
||||
# If you leave the following parameters
|
||||
#
|
||||
# - "backup_device_name" and
|
||||
# - "backup_device_pass"
|
||||
#
|
||||
# empty the script will create daily backups on the internal disk (which could
|
||||
# save you as well).
|
||||
#
|
||||
# Example: backup_device_name=/dev/sdc1
|
||||
#
|
||||
# Leave "backup_device_pass=" empty if the external device is not encrypted.
|
||||
#
|
||||
backup_device_name=
|
||||
backup_device_pass=
|
||||
|
||||
|
||||
###############################################
|
||||
### 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
|
||||
|
||||
793
.homeinstall/zotserver-setup.sh
Normal file
793
.homeinstall/zotserver-setup.sh
Normal file
@@ -0,0 +1,793 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# How to use
|
||||
# ----------
|
||||
#
|
||||
# This file automates the installation of
|
||||
# - hubzilla: https://zotlabs.org/page/hubzilla/hubzilla-project and
|
||||
# - zap: https://zotlabs.com/zap/
|
||||
# - misty : https://zotlabs.com/misty/
|
||||
# - osada : https://codeberg.org/zot/osada
|
||||
# - redmatrix : https://codeberg.org/zot/redmatrix
|
||||
# under Debian Linux "Buster"
|
||||
#
|
||||
# 1) Copy the file "zotserver-config.txt.template" to "zotserver-config.txt"
|
||||
# Follow the instuctions there
|
||||
#
|
||||
# 2) Switch to user "root" by typing "su -"
|
||||
#
|
||||
# 3) Run with "./zotserver-setup.sh"
|
||||
# If this fails check if you can execute the script.
|
||||
# - To make it executable type "chmod +x zotserver-setup.sh"
|
||||
# - or run "bash zotserver-setup.sh"
|
||||
#
|
||||
#
|
||||
# What does this script do basically?
|
||||
# -----------------------------------
|
||||
#
|
||||
# This file automates the installation of a Zot hub/instance under Debian Linux
|
||||
# - install
|
||||
# * apache webserver,
|
||||
# * php,
|
||||
# * mariadb - the database for zotserver,
|
||||
# * adminer,
|
||||
# * git to download and update addons
|
||||
# - configure cron
|
||||
# * "Run.php" for regular background processes of your Zot hub/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
|
||||
# * backup your server's database and files (rsync)
|
||||
# - run letsencrypt to create, register and use a certifacte for https
|
||||
#
|
||||
#
|
||||
# Discussion
|
||||
# ----------
|
||||
#
|
||||
# Security - password is the same for mysql-server, phpmyadmin and your hub/instance db
|
||||
# - The script runs into installation errors for phpmyadmin if it uses
|
||||
# different passwords. For the sake of simplicity one single password.
|
||||
#
|
||||
# How to restore from backup
|
||||
# --------------------------
|
||||
#
|
||||
# (Some explanations here would certainly be useful)
|
||||
#
|
||||
# Daily backup
|
||||
# ------------
|
||||
#
|
||||
# The installation
|
||||
# - writes a shell script in /var/www/
|
||||
# - creates a daily cron that runs this script
|
||||
#
|
||||
# The script makes a (daily) backup of all relevant files
|
||||
# - /var/lib/mysql/ > database
|
||||
# - /var/www/ > hubzilla/zap/misty from git repository
|
||||
# - /etc/letsencrypt/ > certificates
|
||||
#
|
||||
# The backup will be written on an external disk compatible to LUKS+ext4 (see zotserver-config.txt)
|
||||
#
|
||||
# How to restore from backup
|
||||
# --------------------------
|
||||
#
|
||||
# (Some explanations here would certainly be useful)
|
||||
#
|
||||
#
|
||||
# Credits
|
||||
# -------
|
||||
#
|
||||
# The script is based on Thomas Willinghams script "debian-setup.sh"
|
||||
# which he used to install the red#matrix.
|
||||
#
|
||||
# The documentation for bash is here
|
||||
# https://www.gnu.org/software/bash/manual/bash.html
|
||||
#
|
||||
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 10' /etc/issue
|
||||
then
|
||||
die "Linux 10 (buster) 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
|
||||
# backup is important and should be checked
|
||||
if [ -n "$backup_device_name" ]
|
||||
then
|
||||
if [ ! -d "$backup_mount_point" ]
|
||||
then
|
||||
mkdir "$backup_mount_point"
|
||||
fi
|
||||
device_mounted=0
|
||||
if fdisk -l | grep -i "$backup_device_name.*linux"
|
||||
then
|
||||
print_info "ok - filesystem of external device is linux"
|
||||
if [ -n "$backup_device_pass" ]
|
||||
then
|
||||
echo "$backup_device_pass" | cryptsetup luksOpen $backup_device_name cryptobackup
|
||||
if mount /dev/mapper/cryptobackup /media/zotserver_backup
|
||||
then
|
||||
device_mounted=1
|
||||
print_info "ok - could encrypt and mount external backup device"
|
||||
umount /media/zotserver_backup
|
||||
else
|
||||
print_warn "backup to external device will fail because encryption failed"
|
||||
fi
|
||||
cryptsetup luksClose cryptobackup
|
||||
else
|
||||
if mount $backup_device_name /media/zotserver_backup
|
||||
then
|
||||
device_mounted=1
|
||||
print_info "ok - could mount external backup device"
|
||||
umount /media/zotserver_backup
|
||||
else
|
||||
print_warn "backup to external device will fail because mount failed"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
print_warn "backup to external device will fail because filesystem is either not linux or 'backup_device_name' is not correct in $configfile"
|
||||
fi
|
||||
if [ $device_mounted == 0 ]
|
||||
then
|
||||
die "backup device not ready"
|
||||
fi
|
||||
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
|
||||
print_info "updated and upgraded linux"
|
||||
}
|
||||
|
||||
function check_install {
|
||||
if [ -z "`which "$1" 2>/dev/null`" ]
|
||||
then
|
||||
# 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 -q -y install $2
|
||||
print_info "installed $2 installed for $1"
|
||||
else
|
||||
print_warn "$2 already installed"
|
||||
fi
|
||||
}
|
||||
|
||||
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 {
|
||||
if [ $webserver = "nginx" ]
|
||||
then
|
||||
print_info "stopping nginx webserver..."
|
||||
systemctl stop nginx
|
||||
elif [ $webserver = "apache" ]
|
||||
then
|
||||
print_info "stopping apache webserver..."
|
||||
systemctl stop apache2
|
||||
fi
|
||||
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_nginx {
|
||||
print_info "installing nginx..."
|
||||
nocheck_install "nginx"
|
||||
systemctl restart nginx
|
||||
}
|
||||
|
||||
function add_vhost {
|
||||
print_info "adding apache vhost"
|
||||
echo "<VirtualHost *:80>" >> "/etc/apache2/sites-available/${le_domain}.conf"
|
||||
echo "ServerName ${le_domain}" >> "/etc/apache2/sites-available/${le_domain}.conf"
|
||||
echo "DocumentRoot $install_path" >> "/etc/apache2/sites-available/${le_domain}.conf"
|
||||
echo "</VirtualHost>" >> "/etc/apache2/sites-available/${le_domain}.conf"
|
||||
a2ensite $le_domain
|
||||
}
|
||||
|
||||
function add_nginx_block {
|
||||
print_info "adding nginx block"
|
||||
sed "s|SERVER_NAME|${le_domain}|g;s|INSTALL_PATH|${install_path}|g;s|ZOTSERVER_LOG|${install_folder}-${zotserver}.log|;s|PHP_FPM_SOCK|$(ls /var/run/php/*sock)|;" nginx-zotserver.conf.template >> /etc/nginx/sites-enabled/${le_domain}.conf
|
||||
ln -s /etc/nginx/sites-enabled/${le_domain}.conf /etc/nginx/sites-available/
|
||||
}
|
||||
|
||||
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..."
|
||||
if [ $webserver = "nginx" ]
|
||||
then
|
||||
nocheck_install "php php-pear php-curl php-gd php-mbstring php-xml php-zip php-fpm"
|
||||
sed -i "s/^upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php/7.3/fpm/php.ini
|
||||
sed -i "s/^post_max_size =.*/post_max_size = 100M/g" /etc/php/7.3/fpm/php.ini
|
||||
systemctl reload php7.3-fpm
|
||||
elif [ $webserver = "apache" ]
|
||||
then
|
||||
nocheck_install "libapache2-mod-php php php-pear php-curl php-gd php-mbstring php-xml php-zip"
|
||||
sed -i "s/^upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php/7.3/apache2/php.ini
|
||||
sed -i "s/^post_max_size =.*/post_max_size = 100M/g" /etc/php/7.3/apache2/php.ini
|
||||
fi
|
||||
}
|
||||
|
||||
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 "$zotserver_db_name" ]
|
||||
then
|
||||
zotserver_db_name=$zotserver
|
||||
fi
|
||||
if [ -z "$zotserver_db_user" ]
|
||||
then
|
||||
zotserver_db_user=$zotserver
|
||||
fi
|
||||
if [ -z "$zotserver_db_pass" ]
|
||||
then
|
||||
die "zotserver_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 $zotserver_db_name) ]
|
||||
then
|
||||
Q1="CREATE DATABASE IF NOT EXISTS $zotserver_db_name;"
|
||||
Q2="GRANT USAGE ON *.* TO $zotserver_db_user@localhost IDENTIFIED BY '$zotserver_db_pass';"
|
||||
Q3="GRANT ALL PRIVILEGES ON $zotserver_db_name.* to $zotserver_db_user@localhost identified by '$zotserver_db_pass';"
|
||||
Q4="FLUSH PRIVILEGES;"
|
||||
SQL="${Q1}${Q2}${Q3}${Q4}"
|
||||
mysql -uroot -p$mysqlpass -e "$SQL"
|
||||
else
|
||||
die "Can't write over an already existing database!"
|
||||
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
|
||||
if [ $webserver = "nginx" ]
|
||||
then
|
||||
nocheck_install "certbot"
|
||||
print_info "run certbot..."
|
||||
systemctl stop nginx
|
||||
certbot certonly --standalone -d $le_domain -m $le_email --agree-tos --non-interactive
|
||||
systemctl start nginx
|
||||
elif [ $webserver = "apache" ]
|
||||
then
|
||||
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
|
||||
fi
|
||||
}
|
||||
|
||||
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 zotserver_name {
|
||||
if git remote -v | grep -i "origin.*hubzilla.*"
|
||||
then
|
||||
zotserver=hubzilla
|
||||
elif git remote -v | grep -i "origin.*zap.*"
|
||||
then
|
||||
zotserver=zap
|
||||
elif git remote -v | grep -i "origin.*misty.*"
|
||||
then
|
||||
zotserver=misty
|
||||
elif git remote -v | grep -i "origin.*osada.*"
|
||||
then
|
||||
zotserver=osada
|
||||
elif git remote -v | grep -i "origin.*redmatrix.*"
|
||||
then
|
||||
zotserver=redmatrix
|
||||
else
|
||||
die "neither redmatrix, osada, misty, zap nor hubzilla repository > did not install redmatrix/osada/misty/zap/hubzilla"
|
||||
fi
|
||||
}
|
||||
|
||||
function install_zotserver {
|
||||
print_info "installing addons..."
|
||||
cd $install_path/
|
||||
if [ $zotserver = "hubzilla" ]
|
||||
then
|
||||
print_info "hubzilla"
|
||||
util/add_addon_repo https://framagit.org/hubzilla/addons hzaddons
|
||||
elif [ $zotserver = "zap" ]
|
||||
then
|
||||
print_info "zap"
|
||||
util/add_addon_repo https://codeberg.org/zot/zap-addons.git zaddons
|
||||
elif [ $zotserver = "misty" ]
|
||||
then
|
||||
print_info "misty"
|
||||
util/add_addon_repo https://codeberg.org/zot/misty-addons.git maddons
|
||||
elif [ $zotserver = "osada" ]
|
||||
then
|
||||
print_info "osada"
|
||||
util/add_addon_repo https://codeberg.org/zot/osada-addons.git oaddons
|
||||
elif [ $zotserver = "redmatrix" ]
|
||||
then
|
||||
print_info "redmatrix"
|
||||
util/add_addon_repo https://codeberg.org/zot/redmatrix-addons.git raddons
|
||||
else
|
||||
die "neither redmatrix, osada, misty, zap nor hubzilla repository > did not install addons or redmatrix/osada/misty/zap/hubzilla"
|
||||
fi
|
||||
mkdir -p "cache/smarty3"
|
||||
mkdir -p "store"
|
||||
chmod -R 777 store
|
||||
touch .htconfig.php
|
||||
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 install_rsync {
|
||||
print_info "installing rsync..."
|
||||
nocheck_install "rsync"
|
||||
}
|
||||
|
||||
function install_cryptosetup {
|
||||
print_info "installing cryptsetup..."
|
||||
nocheck_install "cryptsetup"
|
||||
}
|
||||
|
||||
function configure_zotserverdaily {
|
||||
echo "#!/bin/sh" >> /var/www/$zotserverdaily
|
||||
echo "#" >> /var/www/$zotserverdaily
|
||||
echo "# update of $le_domain Zot hub/instance" >> /var/www/$zotserverdaily
|
||||
echo "echo \"\$(date) - updating core and addons...\"" >> /var/www/$zotserverdaily
|
||||
echo "echo \"reaching git repository for $le_domain $zotserver hub/instance...\"" >> /var/www/$zotserverdaily
|
||||
echo "(cd $install_path ; util/udall)" >> /var/www/$zotserverdaily
|
||||
echo "chown -R www-data:www-data $install_path # make all accessible for the webserver" >> /var/www/$zotserverdaily
|
||||
if [ $webserver = "apache" ]
|
||||
then
|
||||
echo "chown root:www-data $install_path/.htaccess" >> /var/www/$zotserverdaily
|
||||
echo "chmod 0644 $install_path/.htaccess # www-data can read but not write it" >> /var/www/$zotserverdaily
|
||||
fi
|
||||
chmod a+x /var/www/$zotserverdaily
|
||||
}
|
||||
|
||||
function configure_cron_daily {
|
||||
print_info "configuring cron..."
|
||||
# every 10 min for poller.php
|
||||
if [ -z "`grep '$install_path.*Run.php' /etc/crontab`" ]
|
||||
then
|
||||
echo "*/10 * * * * www-data cd $install_path; php Zotlabs/Daemon/Run.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
|
||||
# - backup db, files ($install_path), certificates if letsencrypt
|
||||
# - update zotserver 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/$zotcron
|
||||
echo "#" >> /var/www/$zotcron
|
||||
echo "echo \" \"" >> /var/www/$zotcron
|
||||
echo "echo \"+++ \$(date) +++\"" >> /var/www/$zotcron
|
||||
echo "echo \" \"" >> /var/www/$zotcron
|
||||
echo "echo \"\$(date) - stopping $webserver and mysql...\"" >> /var/www/$zotcron
|
||||
if [ $webserver = "nginx" ]
|
||||
then
|
||||
echo "systemctl stop nginx" >> /var/www/$zotcron
|
||||
elif [ $webserver = "apache" ]
|
||||
then
|
||||
echo "service apache2 stop" >> /var/www/$zotcron
|
||||
fi
|
||||
echo "/etc/init.d/mysql stop # to avoid inconsistencies" >> /var/www/$zotcron
|
||||
echo "#" >> /var/www/$zotcron
|
||||
echo "echo \"\$(date) - renew certificate...\"" >> /var/www/$zotcron
|
||||
echo "certbot renew --noninteractive" >> /var/www/$zotcron
|
||||
echo "#" >> /var/www/$zotcron
|
||||
echo "# backup" >> /var/www/$zotcron
|
||||
echo "echo \"\$(date) - try to mount external device for backup...\"" >> /var/www/$zotcron
|
||||
echo "backup_device_name=$backup_device_name" >> /var/www/$zotcron
|
||||
echo "backup_device_pass=$backup_device_pass" >> /var/www/$zotcron
|
||||
echo "backup_mount_point=$backup_mount_point" >> /var/www/$zotcron
|
||||
echo "device_mounted=0" >> /var/www/$zotcron
|
||||
echo "if [ -n \"\$backup_device_name\" ]" >> /var/www/$zotcron
|
||||
echo "then" >> /var/www/$zotcron
|
||||
echo " if blkid | grep $backup_device_name" >> /var/www/$zotcron
|
||||
echo " then" >> /var/www/$zotcron
|
||||
if [ -n "$backup_device_pass" ]
|
||||
then
|
||||
echo " echo \"decrypting backup device...\"" >> /var/www/$zotcron
|
||||
echo " echo "\"$backup_device_pass\"" | cryptsetup luksOpen $backup_device_name cryptobackup" >> /var/www/$zotcron
|
||||
fi
|
||||
echo " if [ ! -d $backup_mount_point ]" >> /var/www/$zotcron
|
||||
echo " then" >> /var/www/$zotcron
|
||||
echo " mkdir $backup_mount_point" >> /var/www/$zotcron
|
||||
echo " fi" >> /var/www/$zotcron
|
||||
echo " echo \"mounting backup device...\"" >> /var/www/$zotcron
|
||||
if [ -n "$backup_device_pass" ]
|
||||
then
|
||||
echo " if mount /dev/mapper/cryptobackup $backup_mount_point" >> /var/www/$zotcron
|
||||
else
|
||||
echo " if mount $backup_device_name $backup_mount_point" >> /var/www/$zotcron
|
||||
fi
|
||||
echo " then" >> /var/www/$zotcron
|
||||
echo " device_mounted=1" >> /var/www/$zotcron
|
||||
echo " echo \"device $backup_device_name is now mounted. Starting backup...\"" >> /var/www/$zotcron
|
||||
echo " rsync -a --delete /var/lib/mysql/ /media/zotserver_backup/mysql" >> /var/www/$zotcron
|
||||
echo " rsync -a --delete /var/www/ /media/zotserver_backup/www" >> /var/www/$zotcron
|
||||
echo " rsync -a --delete /etc/letsencrypt/ /media/zotserver_backup/letsencrypt" >> /var/www/$zotcron
|
||||
echo " echo \"\$(date) - disk sizes...\"" >> /var/www/$zotcron
|
||||
echo " df -h" >> /var/www/$zotcron
|
||||
echo " echo \"\$(date) - db size...\"" >> /var/www/$zotcron
|
||||
echo " du -h $backup_mount_point | grep mysql/zotserver" >> /var/www/$zotcron
|
||||
echo " echo \"unmounting backup device...\"" >> /var/www/$zotcron
|
||||
echo " umount $backup_mount_point" >> /var/www/$zotcron
|
||||
echo " else" >> /var/www/$zotcron
|
||||
echo " echo \"failed to mount device $backup_device_name\"" >> /var/www/$zotcron
|
||||
echo " fi" >> /var/www/$zotcron
|
||||
if [ -n "$backup_device_pass" ]
|
||||
then
|
||||
echo " echo \"closing decrypted backup device...\"" >> /var/www/$zotcron
|
||||
echo " cryptsetup luksClose cryptobackup" >> /var/www/$zotcron
|
||||
fi
|
||||
echo " fi" >> /var/www/$zotcron
|
||||
echo "fi" >> /var/www/$zotcron
|
||||
echo "if [ \$device_mounted == 0 ]" >> /var/www/$zotcron
|
||||
echo "then" >> /var/www/$zotcron
|
||||
echo " echo \"device could not be mounted $backup_device_name. No backup written.\"" >> /var/www/$zotcron
|
||||
echo "fi" >> /var/www/$zotcron
|
||||
echo "#" >> /var/www/$zotcron
|
||||
echo "echo \"\$(date) - db size...\"" >> /var/www/$zotcron
|
||||
echo "du -h /var/lib/mysql/ | grep mysql/" >> /var/www/$zotcron
|
||||
echo "#" >> /var/www/$zotcron
|
||||
echo "cd /var/www" >> /var/www/$zotcron
|
||||
echo "for f in *-daily.sh; do \"./\${f}\"; done" >> /var/www/$zotcron
|
||||
echo "echo \"\$(date) - updating linux...\"" >> /var/www/$zotcron
|
||||
echo "apt-get -q -y update && apt-get -q -y dist-upgrade && apt-get -q -y autoremove # update linux and upgrade" >> /var/www/$zotcron
|
||||
echo "echo \"\$(date) - Backup and update finished. Rebooting...\"" >> /var/www/$zotcron
|
||||
echo "#" >> /var/www/$zotcron
|
||||
echo "shutdown -r now" >> /var/www/$zotcron
|
||||
|
||||
# If global cron job does not exist we add it to /etc/crontab
|
||||
if grep -q $zotcron /etc/crontab
|
||||
then
|
||||
echo "cron job already in /etc/crontab"
|
||||
else
|
||||
echo "30 05 * * * root /bin/bash /var/www/$zotcron >> /var/www/zot-daily.log 2>&1" >> /etc/crontab
|
||||
echo "0 0 1 * * root rm /var/www/zot-daily.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
|
||||
|
||||
zotserver_name
|
||||
print_info "We're installing a $zotserver instance"
|
||||
install_path="$(dirname "$(pwd)")"
|
||||
install_folder="$(basename $install_path)"
|
||||
|
||||
# Read config file edited by user
|
||||
configfile=zotserver-config.txt
|
||||
source $configfile
|
||||
|
||||
selfhostdir=/etc/selfhost
|
||||
selfhostscript=selfhost-updater.sh
|
||||
zotcron="zotcron.sh"
|
||||
zotserverdaily="${install_folder}-${zotserver}-daily.sh"
|
||||
backup_mount_point="/media/zotserver_backup"
|
||||
|
||||
#set -x # activate debugging from here
|
||||
|
||||
check_config
|
||||
stop_zotserver
|
||||
update_upgrade
|
||||
install_curl
|
||||
install_wget
|
||||
install_sendmail
|
||||
if [ $webserver = "nginx" ]
|
||||
then
|
||||
install_nginx
|
||||
elif [ $webserver = "apache" ]
|
||||
then
|
||||
install_apache
|
||||
else
|
||||
die "Failed to install a Web server: 'webserver' not set to \"apache\" or \"nginx\" in $configfile"
|
||||
fi
|
||||
install_imagemagick
|
||||
install_php
|
||||
if [ $webserver = "nginx" ]
|
||||
then
|
||||
add_nginx_block
|
||||
elif [ $webserver = "apache" ]
|
||||
then
|
||||
if [ "$install_path" != "/var/www/html" ]
|
||||
then
|
||||
add_vhost
|
||||
fi
|
||||
fi
|
||||
install_mysql
|
||||
if [ $webserver = "apache" ]
|
||||
then
|
||||
install_adminer
|
||||
fi
|
||||
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_zotserverdaily
|
||||
|
||||
configure_cron_daily
|
||||
|
||||
if [ "$le_domain" != "localhost" ]
|
||||
then
|
||||
install_cryptosetup
|
||||
install_rsync
|
||||
else
|
||||
print_info "is localhost - skipped installation of cryptosetup"
|
||||
fi
|
||||
|
||||
|
||||
#set +x # stop debugging from here
|
||||
0
.openshift/action_hooks/deploy
Executable file → Normal file
0
.openshift/action_hooks/deploy
Executable file → Normal file
0
.openshift/cron/weekly/chronograph
Executable file → Normal file
0
.openshift/cron/weekly/chronograph
Executable file → Normal file
752
CHANGELOG
752
CHANGELOG
@@ -1,3 +1,653 @@
|
||||
Hubzilla 5.6 (2021-05-11)
|
||||
- Improve postgres hubloc queries
|
||||
- Implement automatic duplicate singleton hubloc removal
|
||||
- Send author id, id_sig and key fields along with author/owner
|
||||
- Implement custom redis session backend
|
||||
- Improved registration workflow
|
||||
- Complete rewrite of the registration backend with many new features
|
||||
- Complete rewrite of the invite app
|
||||
- Update Spanish, Russian and Polish translations
|
||||
- Improved PHP8 compatibiliy (work in progress)
|
||||
|
||||
Bugfixes
|
||||
- Fix manual fetching of non-ascii domains
|
||||
- Fix revision not compared when importing items
|
||||
- Fix events not transformed to UTC when importing calendar
|
||||
- Fix timezone issue in mod cal
|
||||
- Fix profile photos not revalidated
|
||||
- Fix regression in caldav/carddav discovery
|
||||
- Fix caldav/carddav sync on remote access
|
||||
- Fix info and notice messages not bootstraped when using SSE
|
||||
- Fix URL including an @ treated like a webbie
|
||||
- Fix cover photo image issues on some mobile devices
|
||||
|
||||
Addons
|
||||
- Diaspora: improve performance when delivering public items
|
||||
- Diaspora: make sure to not process same contact more than once
|
||||
- Pubcrawl: make sure to not process same contact more than once
|
||||
- Pubcrawl: do not relay Like and Dislike activity
|
||||
- Pubcrawl: deprecate as_follow() in favour of core Activity::follow()
|
||||
- Pubcrawl: improved compatibility with mobilizon and xwiki
|
||||
- Pubcrawl: do not process follow/unfollw thread in as_create_note()
|
||||
- Diaspora: do not relay comments/likes of strangers if the thread-owner is local and does not allow comments/likes by strangers in the diaspora app settings
|
||||
|
||||
|
||||
Hubzilla 5.4.3 (2021-04-20)
|
||||
- Fix regression in mod notifications (only the last was visible)
|
||||
- Set Permissions-Policy: interest-cohort=() header by default
|
||||
- Fix xchans containing a % breaking get_forum_channels()
|
||||
- Fix webfinger if using a reverse proxy
|
||||
- Fix regression finding bookmarks
|
||||
- Fix zot6 hublocs import on channel import
|
||||
- Fix revision not checked in import_items()
|
||||
|
||||
|
||||
Hubzilla 5.4.2 (2021-03-15)
|
||||
- Fix translation plural variable
|
||||
- Fix issue with following/unfollowing a thread
|
||||
- Fix rendering of long text in xchan vcard
|
||||
- Fix local link for rss content if rewrite author is sets
|
||||
- Fix regression in mod display
|
||||
|
||||
Addons
|
||||
- Pubcrawl: fix issue with following/unfollowing a thread
|
||||
- Pubcrawl: more robust implementation of mastodon remote reply
|
||||
- Photocache: remove suffix from the cached photo URL
|
||||
|
||||
|
||||
Hubzilla 5.4.1 (2021-03-09)
|
||||
- Fix profile not found if not logged in
|
||||
- Fix summary not reset on cancel
|
||||
- Fix summary not saved with aoutosave draft
|
||||
- Fix unexpected trigger of buttons when pressing enter in input fields
|
||||
- Fix spanish plural expression
|
||||
- Fix undefined page-end on non dynamic pages
|
||||
|
||||
Addons
|
||||
- Pubcrawl: fix regression in pubcrawl_follow_mod_init()
|
||||
|
||||
|
||||
Hubzilla 5.4 (2021-03-08)
|
||||
- Add new connections to privacy group independend from the default privacy group settings
|
||||
- group_add() return group hash to save a lookup
|
||||
- Do not poll feeds if feed contacts setting is disabled
|
||||
- Deprecate sticky_kit library in favor of CSS position sticky solution
|
||||
- Trigger endless scroll loading at the end of content instead of end of window height
|
||||
- Implement experimental zap export compatibility
|
||||
- Deprecate the [summary] tag in favor of a separate input field for the summary
|
||||
- Adjust error reporting for PHP8
|
||||
- Rely on php.ini default value for pcre.backtrack_limit
|
||||
- PHP8 compatibility (experimental)
|
||||
- Introduce Lib/Crypto (ported from zap)
|
||||
- Introduce Lib/Keyutils which now implements phpseclib v2
|
||||
- Improve profile photo fetching
|
||||
- Introduce fetch_provider hook
|
||||
- Implement ActivityStreams discovery in mod profile
|
||||
- Implement ActivityStreams discovery in mod channel
|
||||
- Streamline OS folder and file permissions
|
||||
- Add polish translations
|
||||
- Implement ThreadListener in mod activity_match
|
||||
- Improve ThreadListener handling
|
||||
- Use mail envelope instead of lock icon for direct messages
|
||||
- Implement ASCollection in Libzot::fetch_conversation()
|
||||
- Invoke channel discovery by hash instead of address in mod channel
|
||||
- Implement manual public item import for zot6, activitypub and diaspora via search
|
||||
- Default photo storage to filesystem instead of DB for new installations
|
||||
- Support filesystem storage for xchan profile photos
|
||||
- Deprecate Daemon/Externals
|
||||
- Implement SQL query background caching
|
||||
- Process channel categories list in background
|
||||
- Port util/connect to Lib/Connect
|
||||
|
||||
Bugfixes
|
||||
- Fix visible/empty notifications for blocked contacts items
|
||||
- Fix issue where URL fragment was turned into hashtag if the hashtag existed elsewhere in the post (issue #1518)
|
||||
- Fix audio and video embeds for media sources without media format extension
|
||||
- Fix issue where zot package was saved in iconf instead of the decoded activity
|
||||
- Fix duplicate id in post preview
|
||||
- Fix display issue of restricted content in mod display
|
||||
- Fix issue where comments were not delivered to the public stream
|
||||
- Fix issue where profile photos were stored multiple times and remove duplicates
|
||||
- Fix pinned items sync between clones
|
||||
- Fix r_preview for list mode in mod channel and mod network
|
||||
|
||||
Addons
|
||||
- Pubcrawl: deal with mastodons remote replies
|
||||
- Diaspora: reduce xchan network confusion in several places
|
||||
- Diaspora: fix mentions if multiple xchan networks exists
|
||||
- Diaspora: fix comments on comments
|
||||
- Pubcrawl: do not re-use broken signed messages
|
||||
- Pubcrawl: fix parsing of images with description (core issue #1519)
|
||||
- Pubcrawl: use the signed message from the attachment (iconfig) when relaying
|
||||
- Diaspora: implement browser to browser encrypted messages as base64 encoded string
|
||||
- Pubcrawl: implement browser to browser encrypted messages as base64 encoded string
|
||||
- Diaspora: support post summary
|
||||
- Pubcrawl: fix summary aka content warning
|
||||
- PHP8 compatibility (experimental)
|
||||
- Tripleaes: removed
|
||||
- Reflect core crypto changes
|
||||
- Photocache: improve mimetype detection
|
||||
- Diaspora: implement mnanual fetch provider
|
||||
- Diaspora: implement send participation
|
||||
- Pubcrawl: deprecate pubcrawl_is_as_request() in favor of the core version in Lib/ActivityStreams
|
||||
- Diaspora: prefer zot identity for inbound comments if available
|
||||
- Pubcrawl: return zotfeed results in mod outbox
|
||||
- Queueworker: improved deduplication by adding a uuid
|
||||
- Superblock: fix syncing with clones regression
|
||||
- Queueworker: improve SQL query in GetWorkerCount()
|
||||
- Queueworker: fix issue in workersleep handling
|
||||
|
||||
|
||||
Hubzilla 5.2.2 (2021-02-13)
|
||||
- Fix issue with ping_site()
|
||||
|
||||
|
||||
Hubzilla 5.2.1 (2021-01-16)
|
||||
- Fix attach_upgrade() to catch all broken entries in attach
|
||||
- Fix collect_recipients() public policy filter for zot6
|
||||
- Fix leaking of duplicate tasks in queueworker
|
||||
|
||||
|
||||
Hubzilla 5.2 (2021-01-13)
|
||||
- Use libzotdir for directory
|
||||
- Streamline usage of channel url for keyId
|
||||
- Basic work on PHP8 compatibility
|
||||
- Improve performance for forum post edit action
|
||||
- Improve File App tiles view
|
||||
- Update es strings
|
||||
- Update ru strings
|
||||
- Implement directory download via zip files in Files App
|
||||
- Implement bulk file download via zip files in Files App
|
||||
- Deprecate ! and !! forum tags - use direct messages to post to forums
|
||||
- Do not show forums where we do not have permission to post in ACL selector
|
||||
- Implement lockview for Files App
|
||||
- Implement file and directory rename functionality Files App
|
||||
- Implement file and directory copy functionality in Files App
|
||||
- Implement file and directory move functionality in Files App
|
||||
- Implement categories for files and directories in Files App
|
||||
- Implement drag and drop move action for files and directories in Files App
|
||||
- Implement bulk file actions for move, copy, categories and permissions
|
||||
- Implement a files categories widget
|
||||
- Implement a breadcrumb view for file paths
|
||||
- Rewrite xchan_vcard template
|
||||
- Update composer libs
|
||||
|
||||
Bugfixes
|
||||
- Fix direct messages by forum channel turned into group item
|
||||
- Fix ID3Parser composer autoload
|
||||
- Fix issue where directory_fallback_servers were not accessible from static function
|
||||
- Fix missing constant defenition for HUBLOC_OFFLINE
|
||||
- Fix sync_directories() including known dead sites
|
||||
- Fix undefined variable in poller preventing Onedirsync from running
|
||||
- Fix w2w posts not editable/deletable from local server
|
||||
- Fix issue where categories were not saved on forum wall posts
|
||||
- Fix no channel_id provided for contact_remove() in reply_purge()
|
||||
- Fix typo in notifier command
|
||||
- Fix profile title/description allowed more than 191 characters
|
||||
|
||||
Addons
|
||||
- Queueworker: cleanup whitespace
|
||||
- Queueworker: add some tweaks to prevent deadlocks for postgresql
|
||||
- Flashcards: compatibility for the changes in the Files App
|
||||
- Openstreemap: fix hostname parsing from URL
|
||||
- Openstreemap: fix content security policy
|
||||
- Pubcrawl: fix peertube video display
|
||||
- Pubcrawl: deliver updates to anyone owning the item
|
||||
|
||||
|
||||
Hubzilla 5.0.8 (2020-12-30)
|
||||
- Fix single quotes not escaped in the notifications title (issue 1503)
|
||||
- Return zot6 xchans for random_profile()
|
||||
- Return zot6 entries in dirsearch
|
||||
- Fix comment sync issue
|
||||
- Fix duplicate entries in contact autocomplete
|
||||
- Fix issue where direct message notifications where not displayed for wall items
|
||||
- Do not revalidate cached photos
|
||||
- Implement imagemagic resource consumption limiting
|
||||
- Specify key in mod owa
|
||||
- Fix issue where array was passed to get_key()
|
||||
|
||||
|
||||
Hubzilla 5.0.7 (2020-12-21)
|
||||
- Fix CardDAV addressbook ID
|
||||
- Use Zot6 for CardDAV and CalDAV sync between clones
|
||||
- Handle owa with hubloc_id_url only
|
||||
- Fix attachment in comment not visible after commenting - issue #1499
|
||||
|
||||
|
||||
Hubzilla 5.0.6 (2020-12-17)
|
||||
- Fix zot hublocs with empty hubloc_id_url in DB caused by clone import bug
|
||||
- Only look for zot6 and zot hublocs in mod owa
|
||||
- Fix abconfig issue when cloning a channel
|
||||
- Call notifier with refresh_all instead of location when importing a clone
|
||||
- Use Libzot::encode_locations() instead of zot_encode_locations() in notifier
|
||||
- Fix missing zot hubloc hubloc_id_url when importing a clone
|
||||
- Implement Libzot::zot_record_preferred() in various places to prevent hubloc confusion
|
||||
|
||||
Addons
|
||||
- Fix wrong redirect path for check_form_security_token_redirectOnErr() in various addons
|
||||
|
||||
|
||||
Hubzilla 5.0.5 (2020-12-12)
|
||||
- Fix hubloc issue in mod getfile
|
||||
- Remove duplicate SQL query
|
||||
|
||||
|
||||
Hubzilla 5.0.4 (2020-12-01)
|
||||
- Fix regression updating the primary
|
||||
- Dismiss title in response activities
|
||||
|
||||
|
||||
Hubzilla 5.0.3 (2020-11-26)
|
||||
- Upgrade phpunit to version 9
|
||||
- Remove dbunit because its not maintained anymore
|
||||
- Add uuid version 5 based uuid's to imported feeds
|
||||
- Fix potential delivery loop
|
||||
- Fix actor of fetched activities not stored
|
||||
- Improve update mechanism for comments and likes to prevent page jumps
|
||||
- Fix issue where channel suggestions was suggesting oneself
|
||||
- Do not auto-update except for own actions
|
||||
- Introduce ctrl-enter shortcut to send comments and posts
|
||||
|
||||
Addons
|
||||
Pubcrawl: improve addressing of mentioned actors
|
||||
|
||||
|
||||
Hubzilla 5.0.2 (2020-11-16)
|
||||
- Fix edge case in acl selector
|
||||
- Fix ping_site()
|
||||
- Fix directory post url
|
||||
- Update russian translation
|
||||
- Implement polling for notifications (default) - SSE can be enabled in /admin/site if desired
|
||||
- DB update to remove hublocs with no hubloc_hash or no hubloc_callback
|
||||
- Do not save actors without an inbox
|
||||
- Fix import_hublocs()
|
||||
|
||||
Addons
|
||||
- Pubcrawl: do not save actors without an inbox
|
||||
- Diaspora: fix issue with diaspora where hublocs without hubloc_hash were stored
|
||||
|
||||
|
||||
Hubzilla 5.0.1 (2020-11-12)
|
||||
- Fix share title size
|
||||
- Fix issue where hublocs could get mixed up between different protocols
|
||||
|
||||
Addons
|
||||
- Pubcrawl: implement authenticated profile fetches which are now partly required in mastodon
|
||||
- Sse: call xchan_query() just once per item
|
||||
- Pubcrawl: reject messages where sender is not the author if ld-signature is not ok
|
||||
|
||||
|
||||
Hubzilla 5.0 (2020-11-05)
|
||||
- Remove unmaintained and deprecated schemas
|
||||
- Deprecate HTML5_Parser library
|
||||
- Implement results caching for public tag and profile categories fetching
|
||||
- Deprecate $a variable for *_plugin_admin() and *_plugin_admin_post()
|
||||
- Support remote host cache directives on profile photo fetching
|
||||
- Disable community tags until it is agreed upon on how to implement in zot6 or activitypub
|
||||
- Improve home install setup script
|
||||
- Cryptojs moved to addon
|
||||
- Implement "more encryption" feature with the SJCL library
|
||||
- Add minimum form lifetime check
|
||||
- Improve updateConvItems() performance
|
||||
- Improve infinite scroll experience
|
||||
- Introduce a software bill of material (wip)
|
||||
- Implement new forum behaviour (direct-message a forum to post on its wall) for cross-plattform forum compatibility
|
||||
- Allow apps to be both pinned and starred independently
|
||||
- Add images load status to spinnner if preload images is enabled
|
||||
- Deprecate premium channel app
|
||||
- Allow to set different values for left and right aside width in $theme.php
|
||||
- Display complete permission info only to channel owner
|
||||
- Set CURLOPT_ENCODING to empty string so that compressed content will be uncompressed
|
||||
- Force browser photo revalidation
|
||||
- Deprecate ACTIVITY_OBJ_FILE and ACTIVITY_OBJ_WIKI in favour of other object types (Document, Audio, Video)
|
||||
- Use $mid as plink to prevent to long plinks
|
||||
- Handle some basic friendica attachment bbcodes
|
||||
- Deprecate is_edit_activity()
|
||||
- Deprecate voting feature in favour of polls
|
||||
- Show event timezone if it differs from the channel timezone
|
||||
- Avoid multiple run of expiry procedure on large sites
|
||||
- Implement zot6 as the primary protocol
|
||||
- Introduce polls
|
||||
- Provide titles for wiki links markup
|
||||
- Implement DAV calendars sync with clones
|
||||
- Optional events and poll filters for mod network
|
||||
- Add new addon hook get_banner
|
||||
- Provide tools to deal with spam channels for primary directory admin
|
||||
- Introduce pinned posts for mod channel
|
||||
- Refactor notifications to utilize server sent events
|
||||
- Provide more descriptive connection status icons
|
||||
|
||||
|
||||
Bugfixes
|
||||
- Fix issue where interrupted uploads could not be resumed
|
||||
- Fix photo sync issue between clones
|
||||
- Fix issue where profile photos were fetched for unknown channels
|
||||
- Apply channel name changes to all associated xchans
|
||||
- Show unchecked box in connedit if value is false
|
||||
- Reset their_perms before setting new permissions to reflect permission retractions on local hub
|
||||
- Respect advanced profile setting in sexual preference selector
|
||||
- Do not allow invite codes to be reused unlimited times
|
||||
- Fix issue with quoted hashtags/mentions
|
||||
- Fix issue generating photo thumbnails when uploaded via davfs
|
||||
- Do not call System::get_platform_name() within t() unless needed
|
||||
- Fix wrong URL detection with Markdown support enabled
|
||||
- Fix once cached embedded content is used and stored forever
|
||||
|
||||
Addons
|
||||
- Pubcrawl: fix issue where http signatures were not verified
|
||||
- Pubcrawl: fix issue where private keys were lost from storage
|
||||
- Pubcrawl: add host to signed headers (required by mastodon 3.2.1)
|
||||
- Diaspora: remove relay@relay.iliketoat.net as default relay (it does not exist anymore)
|
||||
- Diaspora: provide UI for admin to configure relay
|
||||
- Diaspora: move the-federation.info registration to statistics addon
|
||||
- Deprecate $a variable for *_plugin_admin() and *_plugin_admin_post()
|
||||
- Chess: moved to addons-unmaintained repository
|
||||
- Introduce cryptojs addon to allow decryption of legacy e2ee notes
|
||||
- Introduce the hide aside addon - fade out aside areas after a while when using endless scroll
|
||||
- Gallery: only show first row of images in the preview
|
||||
- Gallery: restrict height of images to divmore_height
|
||||
- Diaspora: make sure we only provide strings for unxmlify()
|
||||
- Pubcrawl: fix federation with pixelfed
|
||||
- Pubcrawl: dismiss announce if we already have the original item
|
||||
- Gallery: implement view_storage permission for channel_apps()
|
||||
- Cart: order currency param must be added to the sdk script url
|
||||
- Diaspora utilize Lib/Connect
|
||||
- Cart: utilize Lib/Connect
|
||||
- Cart: add per item and per order customer data entry
|
||||
- Pubcrawl: implement polls
|
||||
- Chord Generator: moved to addons-unmaintained repository
|
||||
- Custom Home: moved to addons-unmaintained repository
|
||||
- Flattr Widget: moved to addons-unmaintained repository
|
||||
- Friendica Photo Migrator: moved to addons-unmaintained repository
|
||||
- Jappix Mini: moved to addons-unmaintained repository
|
||||
- Mahjongg: moved to addons-unmaintained repository
|
||||
- Torch: moved to addons-unmaintained repository
|
||||
- Tour: moved to addons-unmaintained repository
|
||||
- Introduce navbanner_option addon
|
||||
- Pubcrawl: support image description
|
||||
- Queueworker: fix duplicate checking
|
||||
- Diaspora: fix dreport
|
||||
- Move legacy zot/diaspora mail frontend from core to mail addon
|
||||
- Diaspora: improve sql queries performance
|
||||
- Pubcrawl: introduce pubcrawl_activity_mod_init()
|
||||
- Twitter: replace Hubzilla bookmark with unicode icon
|
||||
- Pubcrawl: add sys channel to local subscribers
|
||||
- Pubcrawl: deal with arrays in attributedTo
|
||||
- Workflow: various fixes
|
||||
- Channelreputation: various fixes
|
||||
- Introduce SSE addon to provide realtime notifications utilizing server sent events
|
||||
|
||||
|
||||
Hubzilla 4.6 (2019-12-04)
|
||||
- Improve opengraph support for channels
|
||||
- Add opengraph support for articles
|
||||
- Update abook_connected for RSS feeds only if handle_feed() returned success
|
||||
- Do not embed PDF files by default but allow to enabled this feature in security options
|
||||
- Check if file exists before we include it in the router
|
||||
- Update jquery to version 3.4.1
|
||||
- Update composer libraries
|
||||
- Remove old and unused javascript libraries
|
||||
- Improved BBcode to Markdown conversion
|
||||
- Introduce inline SVG support via BBcode
|
||||
- Sanitize title on Atom/RSS feed import
|
||||
- Improved HTTP headers cache support for photos
|
||||
- Add date headers to signed headers
|
||||
- Add check if item['tag'] is an array
|
||||
- Add hook comments_are_now_closed for addons to override date based comment closure
|
||||
- Change mysql schema for item.llink and item.plink for new installs from char(191) to text
|
||||
- Improved photo cache expiration
|
||||
- Improved plural function processing on translation strings creation from .po file with util/po2php utlility
|
||||
- Improved support for CDN/Infrastructure caching (especially profile images)
|
||||
- New japanese translation
|
||||
- Add connect button for non-zot networks not connected in current location
|
||||
- Allow to send forum channels wall2wall or sent by mentions post to external sites via addons
|
||||
- Allow addons to process forum posts published through mentions
|
||||
- Improved internal routing for ActivityPub messages
|
||||
- Improved admin documentation
|
||||
- Add ITEM_TYPE_CUSTOM and hooks to permit addons to create and distribute custom item types
|
||||
- Support "comment policy" in Zot6 communications
|
||||
- Add selected text as quote on reply if comment button is used
|
||||
- Add more nofollow tags to links to discourage backlink farmers
|
||||
- Improved conversion of emoji reactions from zot to zot6
|
||||
- Add CardDAV/CalDAV autodiscovery
|
||||
- Label source project of zotfeed since it is not completely compatible across projects
|
||||
- Update homeinstall script
|
||||
|
||||
Bugfixes
|
||||
- Fix once cached embedded content is used and stored forever
|
||||
- Fix wildcard tag issue
|
||||
- Fix duplicate attachment in jot fileupload
|
||||
- Fix regression with audio file upload
|
||||
- Fix can not edit menu name or title (#1402)
|
||||
- Fix pagination encoding issue for some server setups
|
||||
- Fix Zap->Hubzilla event title compatibility
|
||||
- Fix event timezones for Zot6
|
||||
- Fix missing summary in mod article_edit
|
||||
- Fix PHP warning failed to write session data using user defined save handler
|
||||
- Fix possible thumbnails distortion on rebuild with util/thumbrepair utility
|
||||
- Fix issues with image import to zot6
|
||||
- Fix attachment permissions on clonned channels sync
|
||||
- Fix entries without sitekey returned from DB in queue_deliver() and Lib/Queue
|
||||
|
||||
Addons
|
||||
- Twitter: send tweet even if attached image uploading was unsuccessful
|
||||
- Livejournal: add link to original post option
|
||||
- Flashcards: update to version 2.08
|
||||
- Pubcrawl: compatibility changes to support pixelfed
|
||||
- Cart: update paypal button to API v2
|
||||
- Photocache: rework for speed and lower memory consumption
|
||||
- Photocache: etag support for cached photos
|
||||
- Photocache: purge cache on addon uninstall
|
||||
- Openstreetmap: fix regression if no default values set
|
||||
- Livejournal: allow send posts from non channel owner
|
||||
- Pubcrawl: fix event timezones
|
||||
- Pubcrawl: better ActivityPub channel URL detection
|
||||
- Pubcrawl: fix comments delivery for other channels on the same hub
|
||||
- New addon "workflow" with initial basic "issue tracker" capability
|
||||
|
||||
|
||||
Hubzilla 4.4.1 (2019-08-16)
|
||||
- Fix wrong profile photo displayed when previewing and editing profiles
|
||||
- Fix regression from 4.4 which prevented encrypted signatures from being used for encrypted messages
|
||||
- Fix typo in queueworker addon which broke filtering of duplicate work
|
||||
|
||||
|
||||
Hubzilla 4.4 (2019-08-13)
|
||||
- Change primary directory from zotadel.net to hub.netzgemeinde.eu (requested by zotadel admin)
|
||||
- Add Russian context help files
|
||||
- Replace plink URL with share tag if possible
|
||||
- Catch and exclude trailing punctuation while URL embedding
|
||||
- Do not limit channel if service class property value is set to zero
|
||||
- Streamline keyId and creator/actor
|
||||
- Add daemon_master_summon hook
|
||||
- Serve static files directly if not caught by web server
|
||||
- Update cacert.pem
|
||||
- Calendar: allow different date/time format inputs
|
||||
- Calendar: hide timezone select for allday events
|
||||
⁻ Add opengraph meta info to channel page
|
||||
- Begin directory migration to zot6
|
||||
- Support zot and zot6 in social graph operations
|
||||
- Lowlevel support for zot6 direct messages
|
||||
- Consolidate HTTP signatures
|
||||
- Allow api login by address or url
|
||||
- Provide auto redirect from zot6 /item permalinks
|
||||
- Export all items except photos in channel_export_items_date()
|
||||
- Calendar: clicking a day or week number will now open the day or week view
|
||||
- Remove cached photo location directory on delete if empty
|
||||
- Include zot6 hubs in the Grid scope
|
||||
- Fix os_path replace for thumbnails
|
||||
- Avoid to process original images using storeThumbnail()
|
||||
|
||||
Bugfixes
|
||||
- Fix URLs on imported item taxonomy
|
||||
- Fix admin not allowed to delete any item
|
||||
- Fix webfiunger issue with URLs containing an @
|
||||
- Fix missing object in emoji reactions
|
||||
- Fix appschema to include diaspora:guid
|
||||
- Fix zotfinger in update_directory_entry()
|
||||
- Fix incorrect media type on links for photo objects
|
||||
- Fix mid not dbesc'd in item_store()
|
||||
- Fix calendar encoding issues
|
||||
|
||||
Addons
|
||||
- twitter: various rendering improvements
|
||||
- cavatar: fix wrong image mimetype
|
||||
- gravatar: fix wrong image mimetype
|
||||
- Add license file
|
||||
- pubcrawl: make repeats render like wall to wall posts
|
||||
- pubcrawl: fix pubcrawl_import_author() sometimes returning a non activitypub xchan
|
||||
- pubcrawl: use Lib/Activity for taxonomy en/decoding
|
||||
- pubcrawl: fix wrong uuid in like activity
|
||||
- pubcrawl: fix issue with encoding hashtags
|
||||
- openstreetmap: use https URLs by default
|
||||
- queueworker: refactor and efficiency improvements
|
||||
- pubcrawl: use unique IDs for follow and accept activities
|
||||
- pubcrawl: implement thread completion
|
||||
- pubcrawl: implement delete activity
|
||||
- photocache: reduce the size of the photo cache subdirectories tree
|
||||
- photocache: use html_entity_decode() for cached photo URL
|
||||
- diaspora: fix possible issue with diaspora relay not initializing
|
||||
|
||||
|
||||
Hubzilla 4.2.1 (2019-06-17)
|
||||
- Deprecate mod events
|
||||
- Revisit mod cal
|
||||
- Fix issues with deletion of linked items and resources
|
||||
- Fix zot6 delete issue
|
||||
- Fix attach sync issue
|
||||
- Remove sizeRangeSuffixes in justified gallery wrapper
|
||||
- Fix storageconv issue with postgres
|
||||
- Fix embedphotos image size
|
||||
- pubcrawl: use URI instead of object for actor url
|
||||
- diaspora: adjust loglevel
|
||||
- gallery: remove workaround for margin issue which has been fixed upstream
|
||||
- cart: warn about unsaved changes
|
||||
|
||||
|
||||
Hubzilla 4.2 (2019-06-04)
|
||||
- Introduce Calendar app which deprecates Events and CalDAV apps and streamlines the featuresets
|
||||
- Update mod cal to reflect changes in the calendar app
|
||||
- Improve timezone detection for CalDAV calendars
|
||||
- Add mention support to event description in channel calendar
|
||||
- Update jgrowl library
|
||||
- Do not try to oembed URLs without embed tags
|
||||
- Optimise pdf oembed processing
|
||||
- Add form security token to mod register
|
||||
- Replace URLs for mod gallery, mod photos and mod photo on cloned channel post sync
|
||||
- Update justified gallery library
|
||||
- Update bootstrap libraries
|
||||
- Use "cache" flag for bbcode() on content destined for zot6
|
||||
- Improve DB indexing
|
||||
- Drop deprecated columns from channel the table
|
||||
- Replace own image URL in clonned channel posts
|
||||
- Improve DB update handling
|
||||
- Improve item deletion when a contact was removed
|
||||
- Zot6 compatibility for emoji reactions
|
||||
- Add threaded comments support (disabled by default)
|
||||
- Improve xmlify()/unxmlify() performance
|
||||
- Update blueimp/jquery-file-uplad library
|
||||
- Update sabre/vobject library
|
||||
- Various doco updates
|
||||
- Implement remove profile photo button (reset to default photo)
|
||||
- Implement remove cover photo button
|
||||
- Update the homeinstall script
|
||||
- Add command line tool for photo thumbnails storage conversion
|
||||
- Implement option to store photo thumbnails in filesystem instead of DB
|
||||
|
||||
Bugfixes
|
||||
- Fix category widget when using articles
|
||||
- Fix live update not triggering in mod search
|
||||
- Fix encoded URLs in code blocks
|
||||
- Fix wiki headers not escaped
|
||||
- Fix possible xchan protocol confusion in new_contact()
|
||||
- Fix xchan_url not displayed if xchan_addr not available
|
||||
- Fix suggestion ordering in mod directory
|
||||
- Fix event attachment delivery to zot6
|
||||
|
||||
Addons
|
||||
- pubcrawl: improve friendica compatibility by adding the nonstandard diaspora:guid field
|
||||
- pubcrawl: initial suport for events
|
||||
- pubcrawl: improve permalink detection
|
||||
- flashcards: fix moving learn buttons if viewport sizes changes
|
||||
- flashcards: Move card details to the bottom of a card
|
||||
- upgrade_info: provide links to changelog
|
||||
- photocache: do not save filename for cached photos
|
||||
- pubcrawl: save local comment activitypub payload in iconfig to be used for relay
|
||||
- flashcards: UI improvements in box settings
|
||||
- pubcrawl: implement profile update messages
|
||||
- pubcrawl: use URI instead of object for actor
|
||||
- flashcards: fix jumping sync button
|
||||
- pubcrawl: add threaded comments support
|
||||
- pubcrawl: ignore target encoding errors
|
||||
- pubcrawl: format photo items for activitypub
|
||||
|
||||
|
||||
Hubzilla 4.0.3 (2019-04-26)
|
||||
- Add attachments to zot6 event objects
|
||||
- Add zot6 to federated transports
|
||||
- Update import/export to handle zot6 hublocs and xchans
|
||||
- Update fix_system_urls() to handle zot6 hublocs
|
||||
- Fix infinite loop using postgres as backend
|
||||
- Fix magic auth in combination with zot6
|
||||
- Fix check for required PHP version
|
||||
- Diaspora: favour diaspora protocol identities over others with same hubloc or xchan address
|
||||
|
||||
|
||||
Hubzilla 4.0.2 (2019-04-08)
|
||||
- Port cdav calendar to fullcalendar version 4
|
||||
- Fix perms_pending not evaluated correctly
|
||||
- Fix return wrong profile photo modification date by plugin
|
||||
- Fix suggestion widget using feature_enabled still
|
||||
- Fix check service class limits when syncing files
|
||||
- Remove xchan_instance_url from notifier query - it is not used anymore
|
||||
- Implement remove cover photo functionality
|
||||
- Fix z6_discover() and create a zot6 hubloc on import if applicable
|
||||
- Add backend support for connections ordering
|
||||
- Deduplicate items in item_store() by uuid if we got one otherwise by mid
|
||||
- Add ITEM_TYPE_CUSTOM support to mod display
|
||||
- Fix mod subthread on sys channel items
|
||||
- Fix "recipient not found" dreport spaming with own xchan
|
||||
- Fix wrong variables in dirsearch
|
||||
- Fix 48 hours timeframe check in mod changeaddr
|
||||
- Fix wrong variable in Libsync
|
||||
- Pubcrawl: revert adding additional receivers to comments
|
||||
- Diaspora: fix intro received when being banned
|
||||
- Pubcrawl: add diaspora:guid from friendica AP posts for deduplication
|
||||
- Diaspora: fix friendica plink
|
||||
- Photocache: fix issue with spaces and quotes in original filenames
|
||||
|
||||
|
||||
Hubzilla 4.0.1 (2019-03-21)
|
||||
- Fix permissions not getting decrypted on follow
|
||||
- Add option to add a poster to the video bbcode
|
||||
- Fix SQL performance issue with queries including thr_parent
|
||||
- Fix share encoding issue between hz and zap
|
||||
- Fix edge case in unsupported advisory privacy
|
||||
- Messagefilter enhancements
|
||||
- Fix XSS issues
|
||||
- Clone systems apps to the extent possible
|
||||
- Auto-configure imagick thumbnail binary during setup if possible
|
||||
- Fix array not unserialized in util/service_class
|
||||
- Add phpmd and phpcs to composer require-dev for code linting
|
||||
- Fix issue with email encoding
|
||||
- Fix signature issue for zot6 content imported from zotfeeds to hubzilla
|
||||
- Find unregistered z6 clones on hubzilla sites
|
||||
- Add zot6 to clonable networks
|
||||
- Add owner permission checks to AS item fetch
|
||||
- Perform zot6 discovery in import_author_xchan
|
||||
- Fix authenticated fetches
|
||||
- Port zot_record_preferred() from zap
|
||||
|
||||
Addons:
|
||||
- Pubcrawl: deliver comments to abook contacts and thread participants
|
||||
- Pubcrawl: fix can_comment_on_post()
|
||||
- Deliverynotice: do not save empty postopts
|
||||
- Gravatar: fix URL and use z_fetch_url()
|
||||
- Pubcrawl: improve SQL queries in pubcrawl_item_mod_init()
|
||||
- Pubcrawl: fix authenticated item fetch
|
||||
|
||||
|
||||
Hubzilla 4.0 (2019-03-08)
|
||||
- Add CURLOPT_CONNECTTIMEOUT option
|
||||
- Allow parameters as final path argument in API router
|
||||
@@ -202,7 +852,7 @@ Hubzilla 3.8.3 (2018-11-05)
|
||||
- Fix forum notifications count not correct
|
||||
- Fix gallery addon which broke mod apps in some situations
|
||||
- Fix wiki_list widget not working on every page respectively level
|
||||
|
||||
|
||||
|
||||
Hubzilla 3.8.2 (2018-10-29)
|
||||
- Merge unmerged changes from dev into master
|
||||
@@ -218,7 +868,7 @@ Hubzilla 3.8.2 (2018-10-29)
|
||||
- Look for for matches in the entire string when suggesting emojis
|
||||
- Add [summary] bbcode to autocomplete list
|
||||
- Update blueimp_upload to version 9.23
|
||||
- Update spanish strings
|
||||
- Update spanish strings
|
||||
|
||||
Addons
|
||||
- Cart: don't allow items to be added unless user is logged into the Grid.
|
||||
@@ -276,7 +926,7 @@ Hubzilla 3.8 (2018-10-19)
|
||||
- Sanitise vcard fields
|
||||
- Don't sync system apps
|
||||
|
||||
|
||||
|
||||
Bugfixes
|
||||
- Fix issue with timeago plurals
|
||||
- Fix issue with HTTP signatures
|
||||
@@ -335,7 +985,7 @@ Hubzilla 3.8 (2018-10-19)
|
||||
Hubzilla 3.6 (2018-07-25)
|
||||
- Update jquery.timeago library
|
||||
- Implement Hookable CSP
|
||||
- ActivityStreams: accept header changes to support plume
|
||||
- ActivityStreams: accept header changes to support plume
|
||||
- Streamline inconsistencies in addon naming
|
||||
- SECURITY: hash the session_id in logs
|
||||
- Update justified gallery library
|
||||
@@ -348,9 +998,9 @@ Hubzilla 3.6 (2018-07-25)
|
||||
- Make droping posts of removed connections more memory efficient
|
||||
- Refactor getOutainfo() for DAV storage
|
||||
- Optionally report total available space when uploading
|
||||
- SECURITY: provide option to disable the cloud 'root' directory and make the cloud module require a target channel nickname
|
||||
- SECURITY: provide option to disable the cloud 'root' directory and make the cloud module require a target channel nickname
|
||||
- Add plink and llink to viewsource
|
||||
- Add new 'filter by name' feature
|
||||
- Add new 'filter by name' feature
|
||||
- Remove network tabs
|
||||
- New activity filter widget
|
||||
- New activity order widget
|
||||
@@ -392,8 +1042,8 @@ Hubzilla 3.6 (2018-07-25)
|
||||
- Fix sys channels visible in dirsearch
|
||||
- Fix remote_self not working correctly
|
||||
- Fix photos not syncing properly if destination is a postgres site
|
||||
- Fix wrong hubloc_url for activitypub hublocs
|
||||
- Fix z_check_dns() for BSD
|
||||
- Fix wrong hubloc_url for activitypub hublocs
|
||||
- Fix z_check_dns() for BSD
|
||||
- Fix not null violation in oauth1
|
||||
- Fix DB issues with oauth2 on postgresql
|
||||
- Fix 'anybody authenticated' not correctly handled in can_comment_on_post()
|
||||
@@ -955,10 +1605,10 @@ Hubzilla 2.6.2 (2017-08-31)
|
||||
Hubzilla 2.6.1 (2017-08-18)
|
||||
- Fix a regression with dav clients
|
||||
- Raise install requirements
|
||||
|
||||
|
||||
Plugins/Addon
|
||||
- Diaspora: fix PHP warning
|
||||
- GNU-Social: fix PHP warning
|
||||
- GNU-Social: fix PHP warning
|
||||
|
||||
|
||||
Hubzilla 2.6 (2017-08-16)
|
||||
@@ -966,18 +1616,18 @@ Hubzilla 2.6 (2017-08-16)
|
||||
- Consolidate disable_discover_tab config
|
||||
- Fix some bbcode to markdown conversion issues
|
||||
- Improved finding of recursive attachment permissions
|
||||
- Smaller line-height for notification badges
|
||||
- Smaller line-height for notification badges
|
||||
- Bluegrid schema removed - will be added again if someone is willing to maintain it
|
||||
- Improved file_activity()
|
||||
- DB - add index for item.obj_type
|
||||
- Add options flag to bb_to_markdown() so we can distinguish between diaspora use and other use and therefore filter and adjust content selectively
|
||||
- Close the apps-menu if the notifications-menu is open and vice versa
|
||||
- Close the apps-menu if the notifications-menu is open and vice versa
|
||||
- Remove redundant call to jquery ready function in photo albums view
|
||||
- Remove borders from navbar toggler in mobile view
|
||||
- Improve the formatting of shares when converting from bbcode to markdown
|
||||
- Suppress fopen errors from dav
|
||||
- Make local channel (not our own) nav menus appear similar to what we are used from remote channels
|
||||
- Indicate the selected channel in the dropdown menu if the feature is enabled
|
||||
- Indicate the selected channel in the dropdown menu if the feature is enabled
|
||||
- Provide a mechanism to mark apps active in the app tray
|
||||
- Allow wildcard tag and category searches
|
||||
- Improved installer
|
||||
@@ -989,7 +1639,7 @@ Hubzilla 2.6 (2017-08-16)
|
||||
- Update htmlpurifier to version 4.9.3
|
||||
- Update sabre/http to version 4.2.3
|
||||
- Add optimize-autoloader to composer config
|
||||
- Missing abook_{my,their}_perms in pg schema and missing keys in mysql schema
|
||||
- Missing abook_{my,their}_perms in pg schema and missing keys in mysql schema
|
||||
- Provide a gender icon on the profile sidebar within reason
|
||||
- Provide more comprehensible information on the admin summary page
|
||||
- Upgrade blueimp from 9.8 to 9.18
|
||||
@@ -1055,7 +1705,7 @@ Hubzilla 2.6 (2017-08-16)
|
||||
Cdav addon moved to core
|
||||
head_add_css() needs a preceding '/' to find files in the addons dir
|
||||
New addon code syntax highlighting (moved from core to addon)
|
||||
Pubsubhubbub: specify a minimum number of records - otherwise it defaults to zero
|
||||
Pubsubhubbub: specify a minimum number of records - otherwise it defaults to zero
|
||||
|
||||
|
||||
Hubzilla 2.4 (2017-05-31)
|
||||
@@ -1409,10 +2059,10 @@ Hubzilla 1.14 (2016-10-13)
|
||||
- Start grouping addons by server_role
|
||||
|
||||
Hubzilla 1.12
|
||||
- extensible permissions so you can create a new permission rule such as "can write to my wiki" or "can see me naked".
|
||||
- guest access tokens can do anything you let them, including create posts and administer your channel
|
||||
- extensible permissions so you can create a new permission rule such as "can write to my wiki" or "can see me naked".
|
||||
- guest access tokens can do anything you let them, including create posts and administer your channel
|
||||
- ACLs can be set on files and directories prior to creation.
|
||||
- ACL tool can now be used in multiple forms within a page
|
||||
- ACL tool can now be used in multiple forms within a page
|
||||
- a myriad of new drag/drop features (drop files or photos into /cloud or a post, or drop link into a post or comment, etc.)
|
||||
- multiple file uploads
|
||||
- improvements to website import
|
||||
@@ -1437,7 +2087,7 @@ Hubzilla 1.10
|
||||
Wiki:
|
||||
Lots of enhanced functionality, usability improvements, and bugfixes from v1.8
|
||||
Turned into an optional feature (default on) but disabled in UNO
|
||||
Sync:
|
||||
Sync:
|
||||
Items are now relocated (links patched) when syncing to clones
|
||||
Access Tokens:
|
||||
New feature - allows members to create access controlled guest logins and create/share 'dropbox' style links to protected resources.
|
||||
@@ -1445,7 +2095,7 @@ Hubzilla 1.10
|
||||
Use icons instead of iconic text constructs
|
||||
Only request geolocation permission when creating a post, not on page load
|
||||
provide 'redeliver' option on Delivery Report page for when things really stuff up
|
||||
CalDAV/CardDAV management pages with heaps of functionality
|
||||
CalDAV/CardDAV management pages with heaps of functionality
|
||||
Lib:
|
||||
z_fetch_url() updated to accept different request methods and request bodies
|
||||
item_store(), item_store_update() now return the stored items
|
||||
@@ -1469,7 +2119,7 @@ Hubzilla 1.10
|
||||
issues with 'use existing photo' for profile photo
|
||||
layout editor "list all layouts" returned empty
|
||||
oembed - better detect video file URLs so they aren't loaded into memory.
|
||||
handcrafted bbcode tables could end up with way too much whitespace due to CRLF translation
|
||||
handcrafted bbcode tables could end up with way too much whitespace due to CRLF translation
|
||||
refresh permissions whitescreen in 1.8
|
||||
force immediate profile photo update on local site
|
||||
regression: 'save bookmarks' post action missing
|
||||
@@ -1494,7 +2144,7 @@ Hubzilla 1.8
|
||||
Documentation:
|
||||
Clarify privacy rights of commenters w/r/t conversation owners, as this policy is network dependent.
|
||||
Wiki (Git backed):
|
||||
Brand new feature. We'll call it experimental until it has undergone a bit more testing.
|
||||
Brand new feature. We'll call it experimental until it has undergone a bit more testing.
|
||||
Account Cloning:
|
||||
Regression on clone channel creation created a new channel name each time.
|
||||
New issue (fixed) with directory creation on cloned file content
|
||||
@@ -1517,7 +2167,7 @@ Hubzilla 1.8
|
||||
Experimental PDO database driver
|
||||
Creation of Daemon Master class and port all daemon (background task) interfaces to use it
|
||||
Create separate class for each of 'Cron', 'Cron daily', and 'Cron weekly'.
|
||||
Always run a Cron maintenance task if not run in the last four hours
|
||||
Always run a Cron maintenance task if not run in the last four hours
|
||||
Refactor the template classes
|
||||
Refactor the ConversationItem mess into ThreadItem and ThreadStream
|
||||
Refactor Apps, Enotify, and Chat library code
|
||||
@@ -1525,7 +2175,7 @@ Hubzilla 1.8
|
||||
Created WebServer class for top level
|
||||
Remove mcrypt dependencies (deprecated in PHP 7.1)
|
||||
Remove all reserved (including merely 'not recommended') words as DB table column names
|
||||
Provide mutex lock on DB logging to prevent recursion under rare failure modes.
|
||||
Provide mutex lock on DB logging to prevent recursion under rare failure modes.
|
||||
Bugfixes:
|
||||
Remove db_close function on page end - not needed and will not work with persistent DB connections.
|
||||
Undefined ref_session_write
|
||||
@@ -1545,7 +2195,7 @@ Hubzilla 1.8
|
||||
CalDAV/CardDAV plugin provided
|
||||
Issue sending Diaspora 'like' activities from sources that did not propagate the DCV
|
||||
Allow 'superblock' to work across API calls from third party clients
|
||||
statistics.json: use 'zot' as protocol
|
||||
statistics.json: use 'zot' as protocol
|
||||
Issues fixed during testing of ability to follow Diaspora tags
|
||||
Parse issue with Diaspora reshare content
|
||||
Chess: moved to main repo, ported to 1.8
|
||||
@@ -1557,7 +2207,7 @@ Hubzilla 1.6
|
||||
Plugin hook interface adapted to call static class methods
|
||||
Context help improved dramatically with content for the most accessed pages.
|
||||
Reverted a compatibility change to support GNU-social events. We copied their feed format and their feed format is wrong (XML namespace collisions).
|
||||
Provide a querystring attribute to CSS/JS resources to avoid caching issues when our code changes (which is often).
|
||||
Provide a querystring attribute to CSS/JS resources to avoid caching issues when our code changes (which is often).
|
||||
Fix javascript detection and allow either positive or negative detection.
|
||||
Refactor the plugin hook registration procedure, provide 'unregister all' ability.
|
||||
Fix RSD (Real Simple Discovery) which has been broken for some time.
|
||||
@@ -1566,7 +2216,7 @@ Hubzilla 1.6
|
||||
Update font-awesome to 4.6.1
|
||||
Update SabreDAV to 3.0 (PHP version requirements prevent us from pushing it further at this time)
|
||||
Help text added to cmdline utilities config and pconfig
|
||||
Reworking of the database logging facility to avoid the rare but troublesome recursion when the log facility needed to query the DB internally to obtain config parameters.
|
||||
Reworking of the database logging facility to avoid the rare but troublesome recursion when the log facility needed to query the DB internally to obtain config parameters.
|
||||
Implement singleton delivery (emulate nomadic identity to singleton networks and services)
|
||||
Fix empty album name in photo activities when photo is stored in top level folder.
|
||||
Allow engineering units to be used in service class data size restrictions (400M, 1G, etc.)
|
||||
@@ -1574,7 +2224,7 @@ Hubzilla 1.6
|
||||
Admin interface provided to manage external resource repositories
|
||||
Oembed security reworked. Now all sources are filtered by default unless blocked.
|
||||
Remove the date-string version and use only STD_VERSION
|
||||
Add categories and categorisation filtering and the ability to edit all apps (including system apps) for a given channel
|
||||
Add categories and categorisation filtering and the ability to edit all apps (including system apps) for a given channel
|
||||
Ensure the ability to translate names of all system apps (except those provided in addons)
|
||||
Provide ability to add categories to content from channel sources
|
||||
Lots of work on the presentation of the ACL widget to enhance usability and intuitiveness
|
||||
@@ -1588,24 +2238,24 @@ Hubzilla 1.6
|
||||
Provide some extra security checks to import data and files to prevent mischief
|
||||
Block CalDAV/CardDAV namespace reserved words from being used as a channel nickname/redress since Sabre is somewhat inflexible in this regard
|
||||
Plugins:
|
||||
Diaspora
|
||||
markdown translator work needed to eradicate the Diaspora Comment Virus.
|
||||
Diaspora
|
||||
markdown translator work needed to eradicate the Diaspora Comment Virus.
|
||||
upgrade all inbound paths with the most recent protocol changes (several of these)
|
||||
convert 'diaspora_meta' (Diaspora Comment Virus) to iconfig and eradicate from sites with Diaspora disabled
|
||||
implement social relay and allow following tags
|
||||
upgrade statistics.json to NodeInfo. Currently hubzilla sites are tagged as 'redmatrix' because the NodeInfo schema lacks extensibility and project names are used to designate protocol compatibility rather than protocol names.
|
||||
Std-embeds
|
||||
New addon to allow a handful of corporate providers to run unfiltered embed code (youtube, vimeo, soundcloud)
|
||||
New addon to allow a handful of corporate providers to run unfiltered embed code (youtube, vimeo, soundcloud)
|
||||
Various:
|
||||
upgrade font-awesome icons and adapt a few addons to Objects and the new hook interface and new controller interface
|
||||
|
||||
|
||||
Hubzilla 1.4
|
||||
[This list may appear brief, but encompasses a huge amount of re-writing and re-factoring
|
||||
of the internal code structure to gain long-term performance and stability and provide a standard
|
||||
interface to alternate protocol federation plugins which were made possible by the UNO configuration.
|
||||
UNO is a configuration of hubzilla introduced in 1.3 with reduced complexity and which provides
|
||||
improved protocol federation potential to other networks by virtue of removing nomadic identity
|
||||
(which is not possible to model or work around using other network protocols).]
|
||||
UNO is a configuration of hubzilla introduced in 1.3 with reduced complexity and which provides
|
||||
improved protocol federation potential to other networks by virtue of removing nomadic identity
|
||||
(which is not possible to model or work around using other network protocols).]
|
||||
|
||||
Implement channel move operation for UNO configuration
|
||||
Remove bookmark references in UNO (which has no bookmarks by default)
|
||||
@@ -1628,10 +2278,10 @@ Hubzilla 1.4
|
||||
Rework detection of JavaScript to avoid reload penalty under normal operation
|
||||
Changed primary directory server to a hubzilla server
|
||||
Plugins:
|
||||
Diaspora - switch to alternate XML parser to avoid storing compound objects
|
||||
Diaspora - switch to alternate XML parser to avoid storing compound objects
|
||||
GNU-Social - Huge amounts of work, federation somewhat working now, several issues remain
|
||||
Friendica - Initial federation work (not yet published)
|
||||
|
||||
|
||||
Hubzilla 1.3
|
||||
Admin Security configuration page created which consolidates several previously hidden settings:
|
||||
Communication white/black lists
|
||||
@@ -1647,26 +2297,26 @@ Hubzilla 1.3
|
||||
"pubsites" module UI reworked
|
||||
item-meta ("iconfig") created which implements arbitrary storage for item metadata for plugins
|
||||
abook-meta ("abconfig") created which implements arbitrary storage for connection metadata for plugins
|
||||
"Strict transport security header" made optional as it conflicts with some existing Apache/nginx configurations
|
||||
"Hubzilla UNO" (Hubzilla with radically simplified and locked site settings) implemented as an install configuration.
|
||||
"Strict transport security header" made optional as it conflicts with some existing Apache/nginx configurations
|
||||
"Hubzilla UNO" (Hubzilla with radically simplified and locked site settings) implemented as an install configuration.
|
||||
.well-known directory conflict worked out to support LetsEncrypt cert ownership checks without disrupting webfinger and other internal uses of .well-known
|
||||
Lots of work on 'zcards' which are self-contained HTML representations of a channel including cover photos, profile photos, and some text information
|
||||
Long standing bug uncovered which failed to properly restrict the lower time limit for public feed requests
|
||||
A number of fixes to "readmore" to fix page jumping
|
||||
Bugfix: persons other than the channel owner who have permission to upload photos to a channel could not do so if the js_upload plugin/addon was enabled
|
||||
Siteinfo incorrectly identifying secondary directory servers
|
||||
Allow admin to set and lock features when UNO is configured
|
||||
Allow admin to set and lock features when UNO is configured
|
||||
Atom feeds: alter how events are formatted to be compatible with GNU-social
|
||||
Allow guest/visitor access to view personal calendar
|
||||
Moved several more classes to "composer format" and provided an autoloader.
|
||||
Bugfix: require existing password to change password
|
||||
Bugfix: allow relative_date() to be translated to Polish which has more than two plural forms.
|
||||
Bugfix: allow relative_date() to be translated to Polish which has more than two plural forms.
|
||||
Plugin API: add "requires" keyword to module header to indicate dependent addons
|
||||
ActivityStreams improvements and cleanup: photo and file activities
|
||||
UI cleanup for editing profile when multiple profiles enabled
|
||||
Removed the "markdown" feature as there are numerous issues and no maintainer.
|
||||
Provide "footer" bbcode to ease theming of post footer content
|
||||
Bugfix: install issues caused by composer code refactor and typo in postgres load file
|
||||
Bugfix: install issues caused by composer code refactor and typo in postgres load file
|
||||
Plugins:
|
||||
keepout - "block public on steroids"
|
||||
pubsubhubbub - provides PuSH support to Atom feeds, required for GNU-social federation
|
||||
@@ -1674,7 +2324,7 @@ Hubzilla 1.3
|
||||
Diaspora protocol - some work to ease migration to the new signing format
|
||||
Diaspost - disabled; numerous issues and no maintainer
|
||||
smileybutton - theme work and fixed compatibility with other jot-tools plugins
|
||||
|
||||
|
||||
|
||||
Hubzilla 1.2
|
||||
Provide extra HTTP security headers (several of them).
|
||||
@@ -1684,7 +2334,7 @@ Hubzilla 1.2
|
||||
Add locked features to siteinfo report to aid remote debugging
|
||||
Provide version compatibility checking to plugins (minversion, maxversion, and minphpversion)
|
||||
Account config storage
|
||||
Provide optional integrated registration and channel create form
|
||||
Provide optional integrated registration and channel create form
|
||||
cli utility for managing addons
|
||||
issue with sharing photo "items"
|
||||
cover photo manager: upload, crop, and store
|
||||
@@ -1706,7 +2356,7 @@ Hubzilla 1.2
|
||||
proc_run modified to use exec() instead of proc_open() - causing issues on some PHP installations
|
||||
remote delegation failure under a specific set of circumstances which we were finally able to duplicate
|
||||
Delegation section of Channel Manager was missing names and contained useless notification icons.
|
||||
Change "expire" channel setting to show system limit if there is one.
|
||||
Change "expire" channel setting to show system limit if there is one.
|
||||
Regression: provide a one-click ignore of pending connection
|
||||
Config to control directory keyword generation on client and server.
|
||||
"Collections" renamed to "Privacy Groups", documentation improved
|
||||
@@ -1759,11 +2409,11 @@ Hubzilla 1.1
|
||||
Addons/Plugins:
|
||||
Pageheader addon ported from Friendica
|
||||
Hubwall (allow admin to send email to all accounts on this hub) created
|
||||
GNU-social - queueing added
|
||||
Diaspora - fixes for various failures to update profile photos, updates to queue API
|
||||
GNU-social - queueing added
|
||||
Diaspora - fixes for various failures to update profile photos, updates to queue API
|
||||
Cross Domain Authenticated Chess (Andrew Manning's repository)
|
||||
|
||||
And... the normal "lots of bugs fixed, translations updated, and documentation improved"
|
||||
|
||||
|
||||
|
||||
And... the normal "lots of bugs fixed, translations updated, and documentation improved"
|
||||
|
||||
|
||||
|
||||
|
||||
48
CHANGELOG.air
Normal file
48
CHANGELOG.air
Normal file
@@ -0,0 +1,48 @@
|
||||
"air" is a branch name for revision of Account-Invite-Register at the Hubzilla project
|
||||
|
||||
Invite:
|
||||
* Rewritten and now language template driven
|
||||
* Selectable templates for the invite mails
|
||||
* Invitor may add personal notes in the mailtext
|
||||
+ Invite codes are bound to the recipients email address
|
||||
* Invite mod never more creates accounts
|
||||
* new db scheme for table register
|
||||
* existing register table will be migrated to the new schema even when detected at runtime
|
||||
* Bugfix: creating invite codes when admin only calls Invite w/o any further action
|
||||
* account library revision also together with invite mod
|
||||
* Depending on config: Users may send invitations also
|
||||
* Invitations expires, controlled by the invitor
|
||||
* Changed and new configs:
|
||||
* * invitation_only As usual before
|
||||
* * invitation_also Beside other registration policies, invitations may be used also
|
||||
* * invitation_max_per_day defaults 50, may be changed in adminUI admin>site
|
||||
* * invitation_max_per_user defaults 4
|
||||
* Requirements:
|
||||
* * Addon language has to be installed
|
||||
|
||||
Register:
|
||||
* Register panel (form) and js interaction changed
|
||||
* Unused registrations expire
|
||||
* Depending on config, anonymous registrations (w/o email) are supported
|
||||
* :... dont't panic, that may let grow security
|
||||
* Even anonymous users have to confirm their registration
|
||||
* Registrations may be enabled / disabled time driven for each day in the week (dudy)
|
||||
* Unsoliced registration floods may be blocked
|
||||
* Limited registrations from one single source ip
|
||||
* Using one additional log file, to easy interfare with f2b
|
||||
|
||||
Account:
|
||||
* An user account always becomes created only if all depending conditions are satisfied
|
||||
* AdminUI for site configuration, accounts and registrations enhancements
|
||||
* Still untouched, but accountUI needs enhanced async control in case for mass delete
|
||||
with deep level of recursion cascade of the dependencies (channels etc). An open TODO
|
||||
since years for instances with many much users and channels.
|
||||
|
||||
History:
|
||||
2020.03 Hubzilla Prod version 4.6 (master branch) of hubzilla/core was the base for AIR
|
||||
that was assigned Version 4.6.2 at sn/core
|
||||
2021.02 Hubzilla Prod version 5.2.1 (master branch) of hubzilla/core was new base for AIR
|
||||
that was assigned version 5.2.2 at sn/core (air.5)
|
||||
plus adjustment of hubzilla 5.2.2 (master) to sn/core (air.5) version 5.2.9
|
||||
|
||||
|
||||
11
LICENSE
11
LICENSE
@@ -1,4 +1,5 @@
|
||||
Copyright (c) 2010-2018 the Hubzilla Community
|
||||
Copyright (c) 2019 Hubzilla Community
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -8,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
33
SBOM.md
Normal file
33
SBOM.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Hubzilla Software Bill of Materials - WIP
|
||||
|
||||
|Name|Version|License|Source|
|
||||
|----|-------|-------|------|
|
||||
|blueimp/jquery-file-upload|10.31.0.0|MIT|https://github.com/vkhramtsov/jQuery-File-Upload.git|
|
||||
|brick/math|0.9.2.0|MIT|https://github.com/brick/math.git|
|
||||
|bshaffer/oauth2-server-php|1.11.1.0|MIT|https://github.com/bshaffer/oauth2-server-php.git|
|
||||
|commerceguys/intl|1.0.7.0|MIT|https://github.com/commerceguys/intl.git|
|
||||
|desandro/imagesloaded|4.1.4.0|MIT|https://github.com/desandro/imagesloaded.git|
|
||||
|ezyang/htmlpurifier|4.13.0.0|LGPL-2.1-or-later|https://github.com/ezyang/htmlpurifier.git|
|
||||
|league/html-to-markdown|4.10.0.0|MIT|https://github.com/thephpleague/html-to-markdown.git|
|
||||
|lukasreschke/id3parser|0.0.3.0|GPL|https://github.com/LukasReschke/ID3Parser.git|
|
||||
|michelf/php-markdown|1.9.0.0|BSD-3-Clause|https://github.com/michelf/php-markdown.git|
|
||||
|pear/text_languagedetect|1.0.1.0|BSD-2-Clause|https://github.com/pear/Text_LanguageDetect.git|
|
||||
|phpseclib/phpseclib|2.0.30.0|MIT|https://github.com/phpseclib/phpseclib.git|
|
||||
|psr/log|1.1.3.0|MIT|https://github.com/php-fig/log.git|
|
||||
|ramsey/collection|1.1.3.0|MIT|https://github.com/ramsey/collection.git|
|
||||
|ramsey/uuid|4.1.1.0|MIT|https://github.com/ramsey/uuid.git|
|
||||
|sabre/dav|4.1.5.0|BSD-3-Clause|https://github.com/sabre-io/dav.git|
|
||||
|sabre/event|5.1.2.0|BSD-3-Clause|https://github.com/sabre-io/event.git|
|
||||
|sabre/http|5.1.1.0|BSD-3-Clause|https://github.com/sabre-io/http.git|
|
||||
|sabre/uri|2.2.1.0|BSD-3-Clause|https://github.com/sabre-io/uri.git|
|
||||
|sabre/vobject|4.3.5.0|BSD-3-Clause|https://github.com/sabre-io/vobject.git|
|
||||
|sabre/xml|2.2.3.0|BSD-3-Clause|https://github.com/sabre-io/xml.git|
|
||||
|simplepie/simplepie|1.5.6.0|BSD-3-Clause|https://github.com/simplepie/simplepie.git|
|
||||
|smarty/smarty|3.1.38.0|LGPL-3.0|https://github.com/smarty-php/smarty.git|
|
||||
|symfony/polyfill-ctype|1.22.0.0|MIT|https://github.com/symfony/polyfill-ctype.git|
|
||||
|twbs/bootstrap|4.6.0.0|MIT|https://github.com/twbs/bootstrap.git|
|
||||
|fullcalendar/fullcalendar|4.4.2.0|MIT|https://github.com/fullcalendar/fullcalendar.git|
|
||||
|miromannino/Justified-Gallery|3.8.1.0|MIT|https://github.com/miromannino/Justified-Gallery.git|
|
||||
|fengyuanchen/cropperjs|1.5.7.0|MIT|https://github.com/fengyuanchen/cropperjs.git|
|
||||
|ForkAwesome/Fork-Awesome|1.1.7.0|MIT,SIL OFL,CC BY 3.0|https://github.com/ForkAwesome/Fork-Awesome.git|
|
||||
|jquery/jquery|3.5.1.0|MIT|https://github.com/jquery/jquery.git|
|
||||
@@ -54,7 +54,7 @@ class AccessList {
|
||||
* * \e string \b channel_deny_gid => string of denied gids
|
||||
*/
|
||||
function __construct($channel) {
|
||||
if($channel) {
|
||||
if ($channel) {
|
||||
$this->allow_cid = $channel['channel_allow_cid'];
|
||||
$this->allow_gid = $channel['channel_allow_gid'];
|
||||
$this->deny_cid = $channel['channel_deny_cid'];
|
||||
@@ -99,7 +99,6 @@ class AccessList {
|
||||
$this->allow_gid = $arr['allow_gid'];
|
||||
$this->deny_cid = $arr['deny_cid'];
|
||||
$this->deny_gid = $arr['deny_gid'];
|
||||
|
||||
$this->explicit = $explicit;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Access;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Lib\PConfig;
|
||||
|
||||
/**
|
||||
@@ -39,10 +40,10 @@ class PermissionLimits {
|
||||
*/
|
||||
static public function Std_Limits() {
|
||||
$limits = [];
|
||||
$perms = Permissions::Perms();
|
||||
$perms = Permissions::Perms();
|
||||
|
||||
foreach($perms as $k => $v) {
|
||||
if(strstr($k, 'view'))
|
||||
foreach ($perms as $k => $v) {
|
||||
if (strstr($k, 'view'))
|
||||
$limits[$k] = PERMS_PUBLIC;
|
||||
else
|
||||
$limits[$k] = PERMS_SPECIFIC;
|
||||
@@ -77,14 +78,14 @@ class PermissionLimits {
|
||||
* * \b array with all permission limits, if $perm is not set
|
||||
*/
|
||||
static public function Get($channel_id, $perm = '') {
|
||||
if($perm) {
|
||||
if ($perm) {
|
||||
return intval(PConfig::Get($channel_id, 'perm_limits', $perm));
|
||||
}
|
||||
|
||||
PConfig::Load($channel_id);
|
||||
if(array_key_exists($channel_id, \App::$config)
|
||||
&& array_key_exists('perm_limits', \App::$config[$channel_id]))
|
||||
return \App::$config[$channel_id]['perm_limits'];
|
||||
if (array_key_exists($channel_id, App::$config)
|
||||
&& array_key_exists('perm_limits', App::$config[$channel_id]))
|
||||
return App::$config[$channel_id]['perm_limits'];
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ class PermissionRoles {
|
||||
'post_mail', 'post_like' , 'republish', 'chat'
|
||||
];
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
$ret['channel_type'] = 'group';
|
||||
|
||||
break;
|
||||
|
||||
@@ -113,6 +114,7 @@ class PermissionRoles {
|
||||
'view_pages', 'view_wiki', 'post_wall', 'post_comments', 'tag_deliver',
|
||||
'post_mail', 'post_like' , 'chat' ];
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
$ret['channel_type'] = 'group';
|
||||
|
||||
break;
|
||||
|
||||
@@ -132,6 +134,7 @@ class PermissionRoles {
|
||||
$ret['limits']['view_storage'] = PERMS_SPECIFIC;
|
||||
$ret['limits']['view_pages'] = PERMS_SPECIFIC;
|
||||
$ret['limits']['view_wiki'] = PERMS_SPECIFIC;
|
||||
$ret['channel_type'] = 'group';
|
||||
|
||||
break;
|
||||
|
||||
@@ -187,6 +190,7 @@ class PermissionRoles {
|
||||
'post_mail', 'post_like' , 'republish', 'chat', 'write_wiki'
|
||||
];
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
$ret['channel_type'] = 'group';
|
||||
|
||||
break;
|
||||
|
||||
@@ -214,13 +218,13 @@ class PermissionRoles {
|
||||
// set permissionlimits for this permission here, for example:
|
||||
|
||||
// if($perm === 'mynewperm')
|
||||
// \Zotlabs\Access\PermissionLimits::Set($uid,$perm,1);
|
||||
// PermissionLimits::Set($uid,$perm,1);
|
||||
|
||||
if($perm === 'view_wiki')
|
||||
\Zotlabs\Access\PermissionLimits::Set($uid, $perm, PERMS_PUBLIC);
|
||||
PermissionLimits::Set($uid, $perm, PERMS_PUBLIC);
|
||||
|
||||
if($perm === 'write_wiki')
|
||||
\Zotlabs\Access\PermissionLimits::Set($uid, $perm, PERMS_SPECIFIC);
|
||||
PermissionLimits::Set($uid, $perm, PERMS_SPECIFIC);
|
||||
|
||||
|
||||
// set autoperms here if applicable
|
||||
@@ -258,11 +262,11 @@ class PermissionRoles {
|
||||
case 'view_wiki':
|
||||
set_abconfig($uid,$ab['abook_xchan'],'my_perms',$perm,
|
||||
intval(get_abconfig($uid,$ab['abook_xchan'],'my_perms','view_pages')));
|
||||
|
||||
break;
|
||||
case 'write_wiki':
|
||||
set_abconfig($uid,$ab['abook_xchan'],'my_perms',$perm,
|
||||
intval(get_abconfig($uid,$ab['abook_xchan'],'my_perms','write_pages')));
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -313,4 +317,4 @@ class PermissionRoles {
|
||||
return $roles;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class Permissions {
|
||||
|
||||
$x = [
|
||||
'permissions' => $perms,
|
||||
'filter' => $filter
|
||||
'filter' => $filter
|
||||
];
|
||||
/**
|
||||
* @hooks permissions_list
|
||||
@@ -84,7 +84,7 @@ class Permissions {
|
||||
*/
|
||||
call_hooks('permissions_list', $x);
|
||||
|
||||
return($x['permissions']);
|
||||
return ($x['permissions']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,10 +96,10 @@ class Permissions {
|
||||
*/
|
||||
static public function BlockedAnonPerms() {
|
||||
|
||||
$res = [];
|
||||
$res = [];
|
||||
$perms = PermissionLimits::Std_limits();
|
||||
foreach($perms as $perm => $limit) {
|
||||
if($limit != PERMS_PUBLIC) {
|
||||
foreach ($perms as $perm => $limit) {
|
||||
if ($limit != PERMS_PUBLIC) {
|
||||
$res[] = $perm;
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ class Permissions {
|
||||
*/
|
||||
call_hooks('write_perms', $x);
|
||||
|
||||
return($x['permissions']);
|
||||
return ($x['permissions']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,20 +120,20 @@ class Permissions {
|
||||
* Converts [ 0 => 'view_stream', ... ]
|
||||
* to [ 'view_stream' => 1 ] for any permissions in $arr;
|
||||
* Undeclared permissions which exist in Perms() are added and set to 0.
|
||||
*
|
||||
*
|
||||
* @param array $arr
|
||||
* @return array
|
||||
*/
|
||||
static public function FilledPerms($arr) {
|
||||
if(is_null($arr)) {
|
||||
if (is_null($arr)) {
|
||||
btlogger('FilledPerms: null');
|
||||
$arr = [];
|
||||
}
|
||||
|
||||
$everything = self::Perms();
|
||||
$ret = [];
|
||||
foreach($everything as $k => $v) {
|
||||
if(in_array($k, $arr))
|
||||
$ret = [];
|
||||
foreach ($everything as $k => $v) {
|
||||
if (in_array($k, $arr))
|
||||
$ret[$k] = 1;
|
||||
else
|
||||
$ret[$k] = 0;
|
||||
@@ -155,9 +155,9 @@ class Permissions {
|
||||
*/
|
||||
static public function OPerms($arr) {
|
||||
$ret = [];
|
||||
if($arr) {
|
||||
foreach($arr as $k => $v) {
|
||||
$ret[] = [ 'name' => $k, 'value' => $v ];
|
||||
if ($arr) {
|
||||
foreach ($arr as $k => $v) {
|
||||
$ret[] = ['name' => $k, 'value' => $v];
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
@@ -170,15 +170,16 @@ class Permissions {
|
||||
* @return boolean|array
|
||||
*/
|
||||
static public function FilledAutoperms($channel_id) {
|
||||
if(! intval(get_pconfig($channel_id,'system','autoperms')))
|
||||
if (!intval(get_pconfig($channel_id, 'system', 'autoperms')))
|
||||
return false;
|
||||
|
||||
$arr = [];
|
||||
|
||||
$r = q("select * from pconfig where uid = %d and cat = 'autoperms'",
|
||||
intval($channel_id)
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
$arr[$rr['k']] = intval($rr['v']);
|
||||
}
|
||||
}
|
||||
@@ -193,11 +194,11 @@ class Permissions {
|
||||
* @return boolean true if all perms from $p1 exist also in $p2
|
||||
*/
|
||||
static public function PermsCompare($p1, $p2) {
|
||||
foreach($p1 as $k => $v) {
|
||||
if(! array_key_exists($k, $p2))
|
||||
foreach ($p1 as $k => $v) {
|
||||
if (!array_key_exists($k, $p2))
|
||||
return false;
|
||||
|
||||
if($p1[$k] != $p2[$k])
|
||||
if ($p1[$k] != $p2[$k])
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -214,18 +215,18 @@ class Permissions {
|
||||
*/
|
||||
static public function connect_perms($channel_id) {
|
||||
|
||||
$my_perms = [];
|
||||
$permcat = null;
|
||||
$my_perms = [];
|
||||
$permcat = null;
|
||||
$automatic = 0;
|
||||
|
||||
// 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);
|
||||
$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) {
|
||||
if ($permcat && $permcat['perms']) {
|
||||
foreach ($permcat['perms'] as $p) {
|
||||
$my_perms[$p['name']] = $p['value'];
|
||||
}
|
||||
}
|
||||
@@ -235,15 +236,15 @@ class Permissions {
|
||||
// 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) {
|
||||
$role = get_pconfig($channel_id, 'system', 'permissions_role');
|
||||
if ($role) {
|
||||
$xx = PermissionRoles::role_perms($role);
|
||||
if($xx['perms_auto'])
|
||||
if ($xx['perms_auto'])
|
||||
$automatic = 1;
|
||||
|
||||
if((! $my_perms) && ($xx['perms_connect'])) {
|
||||
if ((!$my_perms) && ($xx['perms_connect'])) {
|
||||
$default_perms = $xx['perms_connect'];
|
||||
$my_perms = Permissions::FilledPerms($default_perms);
|
||||
$my_perms = Permissions::FilledPerms($default_perms);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,11 +252,11 @@ class Permissions {
|
||||
// it is likely a custom permissions role. First see if there are any
|
||||
// automatic permissions.
|
||||
|
||||
if(! $my_perms) {
|
||||
if (!$my_perms) {
|
||||
$m = Permissions::FilledAutoperms($channel_id);
|
||||
if($m) {
|
||||
if ($m) {
|
||||
$automatic = 1;
|
||||
$my_perms = $m;
|
||||
$my_perms = $m;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,24 +264,35 @@ class Permissions {
|
||||
// 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) {
|
||||
if (!$my_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) {
|
||||
$my_perms[$xv['k']] = intval($xv['v']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ( [ 'perms' => $my_perms, 'automatic' => $automatic ] );
|
||||
return (['perms' => $my_perms, 'automatic' => $automatic]);
|
||||
}
|
||||
|
||||
}
|
||||
static public function serialise($p) {
|
||||
$n = [];
|
||||
if ($p) {
|
||||
foreach ($p as $k => $v) {
|
||||
if (intval($v)) {
|
||||
$n[] = $k;
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode(',', $n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
|
||||
class Addon {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
call_hooks('daemon_addon',$argv);
|
||||
call_hooks('daemon_addon', $argv);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
|
||||
class Cache_embeds {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
36
Zotlabs/Daemon/Cache_query.php
Normal file
36
Zotlabs/Daemon/Cache_query.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\Cache;
|
||||
|
||||
class Cache_query {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if(! $argc == 3)
|
||||
return;
|
||||
|
||||
$key = $argv[1];
|
||||
|
||||
$pid = get_config('procid', $key, false);
|
||||
if ($pid && (function_exists('posix_kill') ? posix_kill($pid, 0) : true)) {
|
||||
logger($key . ': procedure already run with pid ' . $pid, LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
$pid = getmypid();
|
||||
set_config('procid', $key, $pid);
|
||||
|
||||
array_shift($argv);
|
||||
array_shift($argv);
|
||||
|
||||
$arr = json_decode(base64_decode($argv[0]), true);
|
||||
|
||||
$r = call_user_func_array('q', $arr);
|
||||
if($r)
|
||||
Cache::set($key, serialize($r));
|
||||
|
||||
del_config('procid', $key);
|
||||
}
|
||||
}
|
||||
@@ -2,39 +2,39 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
|
||||
require_once('include/hubloc.php');
|
||||
|
||||
class Checksites {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
logger('checksites: start');
|
||||
|
||||
if(($argc > 1) && ($argv[1]))
|
||||
|
||||
if (($argc > 1) && ($argv[1]))
|
||||
$site_id = $argv[1];
|
||||
|
||||
if($site_id)
|
||||
if ($site_id)
|
||||
$sql_options = " and site_url = '" . dbesc($argv[1]) . "' ";
|
||||
|
||||
$days = intval(get_config('system','sitecheckdays'));
|
||||
if($days < 1)
|
||||
$days = intval(get_config('system', 'sitecheckdays'));
|
||||
if ($days < 1)
|
||||
$days = 30;
|
||||
|
||||
$r = q("select * from site where site_dead = 0 and site_update < %s - INTERVAL %s and site_type = %d $sql_options ",
|
||||
db_utcnow(), db_quoteinterval($days . ' DAY'),
|
||||
db_utcnow(),
|
||||
db_quoteinterval($days . ' DAY'),
|
||||
intval(SITE_TYPE_ZOT)
|
||||
);
|
||||
|
||||
if(! $r)
|
||||
if (!$r)
|
||||
return;
|
||||
|
||||
foreach($r as $rr) {
|
||||
if(! strcasecmp($rr['site_url'],z_root()))
|
||||
foreach ($r as $rr) {
|
||||
if (!strcasecmp($rr['site_url'], z_root()))
|
||||
continue;
|
||||
|
||||
$x = ping_site($rr['site_url']);
|
||||
if($x['success']) {
|
||||
if ($x['success']) {
|
||||
logger('checksites: ' . $rr['site_url']);
|
||||
q("update site set site_update = '%s' where site_url = '%s' ",
|
||||
dbesc(datetime_convert()),
|
||||
|
||||
58
Zotlabs/Daemon/Convo.php
Normal file
58
Zotlabs/Daemon/Convo.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\ASCollection;
|
||||
|
||||
class Convo {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
logger('convo invoked: ' . print_r($argv, true));
|
||||
|
||||
if ($argc != 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = $argv[1];
|
||||
$channel_id = intval($argv[2]);
|
||||
$contact_hash = $argv[3];
|
||||
|
||||
$channel = channelx_by_n($channel_id);
|
||||
if (!$channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
$r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1",
|
||||
intval($channel_id),
|
||||
dbesc($contact_hash)
|
||||
);
|
||||
if (!$r) {
|
||||
return;
|
||||
}
|
||||
|
||||
$contact = array_shift($r);
|
||||
|
||||
$obj = new ASCollection($id, $channel);
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,18 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
class Cron {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
$maxsysload = intval(get_config('system','maxloadavg'));
|
||||
if($maxsysload < 1)
|
||||
$maxsysload = intval(get_config('system', 'maxloadavg'));
|
||||
if ($maxsysload < 1)
|
||||
$maxsysload = 50;
|
||||
if(function_exists('sys_getloadavg')) {
|
||||
if (function_exists('sys_getloadavg')) {
|
||||
$load = sys_getloadavg();
|
||||
if(intval($load[0]) > $maxsysload) {
|
||||
if (intval($load[0]) > $maxsysload) {
|
||||
logger('system: load ' . $load . ' too high. Cron deferred to next scheduled run.');
|
||||
return;
|
||||
}
|
||||
@@ -19,28 +21,33 @@ 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))
|
||||
&& (! get_config('system','override_cron_lockfile'))) {
|
||||
if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600))
|
||||
&& (!get_config('system', 'override_cron_lockfile'))) {
|
||||
logger("cron: Already running");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Create a lockfile. Needs two vars, but $x doesn't need to contain anything.
|
||||
$x = '';
|
||||
file_put_contents($lockfile, $x);
|
||||
|
||||
logger('cron: start');
|
||||
|
||||
|
||||
// run queue delivery process in the background
|
||||
|
||||
Master::Summon(array('Queue'));
|
||||
|
||||
Master::Summon(array('Poller'));
|
||||
|
||||
// maintenance for mod sharedwithme - check for updated items and remove them
|
||||
/**
|
||||
* Chatpresence: if somebody hasn't pinged recently, they've most likely left the page
|
||||
* and shouldn't count as online anymore. We allow an expection for bots.
|
||||
*/
|
||||
q("delete from chatpresence where cp_last < %s - INTERVAL %s and cp_client != 'auto' ",
|
||||
db_utcnow(),
|
||||
db_quoteinterval('3 MINUTE')
|
||||
);
|
||||
|
||||
require_once('include/sharedwithme.php');
|
||||
apply_updates();
|
||||
|
||||
// expire any expired mail
|
||||
|
||||
q("delete from mail where expires > '%s' and expires < %s ",
|
||||
@@ -48,19 +55,25 @@ class Cron {
|
||||
db_utcnow()
|
||||
);
|
||||
|
||||
remove_expired_registrations();
|
||||
|
||||
$interval = get_config('system', 'delivery_interval', 3);
|
||||
|
||||
// expire any expired items
|
||||
|
||||
$r = q("select id,item_wall from item where expires > '2001-01-01 00:00:00' and expires < %s
|
||||
$r = q("select id,item_wall from item where expires > '2001-01-01 00:00:00' and expires < %s
|
||||
and item_deleted = 0 ",
|
||||
db_utcnow()
|
||||
);
|
||||
if($r) {
|
||||
if ($r) {
|
||||
require_once('include/items.php');
|
||||
foreach($r as $rr) {
|
||||
drop_item($rr['id'],false,(($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL));
|
||||
if($rr['item_wall']) {
|
||||
foreach ($r as $rr) {
|
||||
drop_item($rr['id'], false, (($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL));
|
||||
if ($rr['item_wall']) {
|
||||
// The notifier isn't normally invoked unless item_drop is interactive.
|
||||
Master::Summon( [ 'Notifier', 'drop', $rr['id'] ] );
|
||||
Master::Summon(['Notifier', 'drop', $rr['id']]);
|
||||
if ($interval)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,9 +85,9 @@ class Cron {
|
||||
dbesc(NULL_DATE),
|
||||
db_utcnow()
|
||||
);
|
||||
if($r) {
|
||||
if ($r) {
|
||||
require_once('include/security.php');
|
||||
foreach($r as $rr) {
|
||||
foreach ($r as $rr) {
|
||||
atoken_delete($rr['atoken_id']);
|
||||
}
|
||||
}
|
||||
@@ -84,111 +97,113 @@ class Cron {
|
||||
// or dead entries.
|
||||
|
||||
$r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s and channel_removed = 0",
|
||||
db_utcnow(),
|
||||
db_utcnow(),
|
||||
db_quoteinterval('30 DAY')
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
Master::Summon(array('Directory',$rr['channel_id'],'force'));
|
||||
if($interval)
|
||||
@time_sleep_until(microtime(true) + (float) $interval);
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
Master::Summon(array('Directory', $rr['channel_id'], 'force'));
|
||||
if ($interval)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Clean expired photos from cache
|
||||
|
||||
$age = get_config('system','active_expire_days', '30');
|
||||
|
||||
$r = q("SELECT DISTINCT xchan, content FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
|
||||
intval(PHOTO_CACHE),
|
||||
db_utcnow(),
|
||||
db_quoteinterval($age . ' DAY')
|
||||
db_utcnow(),
|
||||
db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY')
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
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')
|
||||
);
|
||||
foreach ($r as $rr) {
|
||||
$file = dbunescbin($rr['content']);
|
||||
if(is_file($file)) {
|
||||
if (is_file($file)) {
|
||||
@unlink($file);
|
||||
@rmdir(dirname($file));
|
||||
logger('info: deleted cached photo file ' . $file, LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
q("DELETE FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
|
||||
intval(PHOTO_CACHE),
|
||||
db_utcnow(),
|
||||
db_quoteinterval($age . ' DAY')
|
||||
);
|
||||
|
||||
// publish any applicable items that were set to be published in the future
|
||||
// (time travel posts). Restrict to items that have come of age in the last
|
||||
// couple of days to limit the query to something reasonable.
|
||||
// couple of days to limit the query to something reasonable.
|
||||
|
||||
$r = q("select id from item where item_delayed = 1 and created <= %s and created > '%s' ",
|
||||
db_utcnow(),
|
||||
dbesc(datetime_convert('UTC','UTC','now - 2 days'))
|
||||
dbesc(datetime_convert('UTC', 'UTC', 'now - 2 days'))
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
$x = q("update item set item_delayed = 0 where id = %d",
|
||||
intval($rr['id'])
|
||||
);
|
||||
if($x) {
|
||||
if ($x) {
|
||||
$z = q("select * from item where id = %d",
|
||||
intval($message_id)
|
||||
intval($rr['id'])
|
||||
);
|
||||
if($z) {
|
||||
if ($z) {
|
||||
xchan_query($z);
|
||||
$sync_item = fetch_post_tags($z);
|
||||
build_sync_packet($sync_item[0]['uid'],
|
||||
[
|
||||
'item' => [ encode_item($sync_item[0],true) ]
|
||||
Libsync::build_sync_packet($sync_item[0]['uid'],
|
||||
[
|
||||
'item' => [encode_item($sync_item[0], true)]
|
||||
]
|
||||
);
|
||||
}
|
||||
Master::Summon(array('Notifier','wall-new',$rr['id']));
|
||||
Master::Summon(array('Notifier', 'wall-new', $rr['id']));
|
||||
if ($interval)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check if any connections transitioned to zot6 and upgrade the connections to zot6 at this hub if so.
|
||||
require_once('include/connections.php');
|
||||
z6trans_connections();
|
||||
|
||||
require_once('include/attach.php');
|
||||
attach_upgrade();
|
||||
|
||||
$abandon_days = intval(get_config('system','account_abandon_days'));
|
||||
if($abandon_days < 1)
|
||||
$abandon_days = 0;
|
||||
|
||||
|
||||
// once daily run birthday_updates and then expire in background
|
||||
|
||||
// FIXME: add birthday updates, both locally and for xprof for use
|
||||
// by directory servers
|
||||
|
||||
$d1 = intval(get_config('system','last_expire_day'));
|
||||
$d2 = intval(datetime_convert('UTC','UTC','now','d'));
|
||||
$d1 = intval(get_config('system', 'last_expire_day'));
|
||||
$d2 = intval(datetime_convert('UTC', 'UTC', 'now', 'd'));
|
||||
|
||||
// Allow somebody to staggger daily activities if they have more than one site on their server,
|
||||
// or if it happens at an inconvenient (busy) hour.
|
||||
|
||||
$h1 = intval(get_config('system','cron_hour'));
|
||||
$h2 = intval(datetime_convert('UTC','UTC','now','G'));
|
||||
$h1 = intval(get_config('system', 'cron_hour'));
|
||||
$h2 = intval(datetime_convert('UTC', 'UTC', 'now', 'G'));
|
||||
|
||||
|
||||
if(($d2 != $d1) && ($h1 == $h2)) {
|
||||
if (($d2 != $d1) && ($h1 == $h2)) {
|
||||
Master::Summon(array('Cron_daily'));
|
||||
}
|
||||
|
||||
// update any photos which didn't get imported properly
|
||||
// This should be rare
|
||||
|
||||
$r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = ''
|
||||
$r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = ''
|
||||
and xchan_photo_date < %s - INTERVAL %s",
|
||||
db_utcnow(),
|
||||
db_utcnow(),
|
||||
db_quoteinterval('1 DAY')
|
||||
);
|
||||
if($r) {
|
||||
if ($r) {
|
||||
require_once('include/photo/photo_driver.php');
|
||||
foreach($r as $rr) {
|
||||
$photos = import_xchan_photo($rr['xchan_photo_l'],$rr['xchan_hash']);
|
||||
$x = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'
|
||||
foreach ($r as $rr) {
|
||||
$photos = import_xchan_photo($rr['xchan_photo_l'], $rr['xchan_hash'], false, true);
|
||||
q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'
|
||||
where xchan_hash = '%s'",
|
||||
dbesc($photos[0]),
|
||||
dbesc($photos[1]),
|
||||
@@ -202,33 +217,30 @@ class Cron {
|
||||
|
||||
// pull in some public posts
|
||||
|
||||
$disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false;
|
||||
if(! $disable_discover_tab)
|
||||
/* $disable_discover_tab = get_config('system', 'disable_discover_tab') || get_config('system', 'disable_discover_tab') === false;
|
||||
if (!$disable_discover_tab)
|
||||
Master::Summon(array('Externals'));
|
||||
*/
|
||||
|
||||
$generation = 0;
|
||||
$restart = false;
|
||||
|
||||
$restart = false;
|
||||
|
||||
if(($argc > 1) && ($argv[1] == 'restart')) {
|
||||
$restart = true;
|
||||
if (($argc > 1) && ($argv[1] == 'restart')) {
|
||||
$restart = true;
|
||||
$generation = intval($argv[2]);
|
||||
if(! $generation)
|
||||
killme();
|
||||
if (!$generation)
|
||||
return;
|
||||
}
|
||||
|
||||
reload_plugins();
|
||||
|
||||
$d = datetime_convert();
|
||||
|
||||
// TODO check to see if there are any cronhooks before wasting a process
|
||||
|
||||
if(! $restart)
|
||||
if (!$restart)
|
||||
Master::Summon(array('Cronhooks'));
|
||||
|
||||
set_config('system','lastcron',datetime_convert());
|
||||
set_config('system', 'lastcron', datetime_convert());
|
||||
|
||||
//All done - clear the lockfile
|
||||
//All done - clear the lockfile
|
||||
@unlink($lockfile);
|
||||
|
||||
return;
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\Libzotdir;
|
||||
|
||||
class Cron_daily {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
logger('cron_daily: start');
|
||||
|
||||
@@ -13,15 +15,12 @@ class Cron_daily {
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
require_once('include/dir_fns.php');
|
||||
check_upstream_directory();
|
||||
|
||||
Libzotdir::check_upstream_directory();
|
||||
|
||||
// Fire off the Cron_weekly process if it's the correct day.
|
||||
|
||||
$d3 = intval(datetime_convert('UTC','UTC','now','N'));
|
||||
if($d3 == 7) {
|
||||
|
||||
$d3 = intval(datetime_convert('UTC', 'UTC', 'now', 'N'));
|
||||
if ($d3 == 7) {
|
||||
Master::Summon(array('Cron_weekly'));
|
||||
}
|
||||
|
||||
@@ -44,6 +43,17 @@ class Cron_daily {
|
||||
db_utcnow(), db_quoteinterval('1 YEAR')
|
||||
);
|
||||
|
||||
// expire anonymous sse notification entries once a day
|
||||
|
||||
q("delete from xconfig where xchan like '%s'",
|
||||
dbesc('sse_id.%')
|
||||
);
|
||||
|
||||
// Clean up emdedded content cache
|
||||
q("DELETE FROM cache WHERE updated < %s - INTERVAL %s",
|
||||
db_utcnow(),
|
||||
db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY')
|
||||
);
|
||||
|
||||
//update statistics in config
|
||||
require_once('include/statistics_fns.php');
|
||||
@@ -56,8 +66,8 @@ class Cron_daily {
|
||||
|
||||
// expire old delivery reports
|
||||
|
||||
$keep_reports = intval(get_config('system','expire_delivery_reports'));
|
||||
if($keep_reports === 0)
|
||||
$keep_reports = intval(get_config('system', 'expire_delivery_reports'));
|
||||
if ($keep_reports === 0)
|
||||
$keep_reports = 10;
|
||||
|
||||
q("delete from dreport where dreport_time < %s - INTERVAL %s",
|
||||
@@ -69,26 +79,28 @@ class Cron_daily {
|
||||
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.
|
||||
// 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?
|
||||
// potential issue: how do we keep from creating an endless update loop?
|
||||
|
||||
$dirmode = get_config('system','directory_mode');
|
||||
$dirmode = get_config('system', 'directory_mode');
|
||||
|
||||
if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
|
||||
require_once('include/dir_fns.php');
|
||||
sync_directories($dirmode);
|
||||
if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
|
||||
Libzotdir::sync_directories($dirmode);
|
||||
}
|
||||
|
||||
|
||||
Master::Summon(array('Expire'));
|
||||
Master::Summon(array('Cli_suggest'));
|
||||
|
||||
remove_obsolete_hublocs();
|
||||
remove_duplicate_singleton_hublocs();
|
||||
|
||||
call_hooks('cron_daily',datetime_convert());
|
||||
z6_discover();
|
||||
|
||||
set_config('system','last_expire_day',intval(datetime_convert('UTC','UTC','now','d')));
|
||||
$date = datetime_convert();
|
||||
call_hooks('cron_daily', $date);
|
||||
|
||||
set_config('system', 'last_expire_day', intval(datetime_convert('UTC', 'UTC', 'now', 'd')));
|
||||
|
||||
/**
|
||||
* End Cron Daily
|
||||
|
||||
@@ -4,21 +4,22 @@ namespace Zotlabs\Daemon;
|
||||
|
||||
class Cron_weekly {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
/**
|
||||
* Cron Weekly
|
||||
*
|
||||
*
|
||||
* Actions in the following block are executed once per day only on Sunday (once per week).
|
||||
*
|
||||
*/
|
||||
|
||||
call_hooks('cron_weekly',datetime_convert());
|
||||
$date = datetime_convert();
|
||||
call_hooks('cron_weekly', $date);
|
||||
|
||||
z_check_cert();
|
||||
|
||||
prune_hub_reinstalls();
|
||||
|
||||
|
||||
mark_orphan_hubsxchans();
|
||||
|
||||
// Find channels that were removed in the last three weeks, but
|
||||
@@ -31,8 +32,8 @@ class Cron_weekly {
|
||||
db_utcnow(), db_quoteinterval('21 DAY'),
|
||||
db_utcnow(), db_quoteinterval('10 DAY')
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rv) {
|
||||
if ($r) {
|
||||
foreach ($r as $rv) {
|
||||
channel_remove_final($rv['channel_id']);
|
||||
}
|
||||
}
|
||||
@@ -43,14 +44,14 @@ class Cron_weekly {
|
||||
db_utcnow(), db_quoteinterval('14 DAY')
|
||||
);
|
||||
|
||||
$dirmode = intval(get_config('system','directory_mode'));
|
||||
if($dirmode === DIRECTORY_MODE_SECONDARY || $dirmode === DIRECTORY_MODE_PRIMARY) {
|
||||
logger('regdir: ' . print_r(z_fetch_url(get_directory_primary() . '/regdir?f=&url=' . urlencode(z_root()) . '&realm=' . urlencode(get_directory_realm())),true));
|
||||
$dirmode = intval(get_config('system', 'directory_mode'));
|
||||
if ($dirmode === DIRECTORY_MODE_SECONDARY || $dirmode === DIRECTORY_MODE_PRIMARY) {
|
||||
logger('regdir: ' . print_r(z_fetch_url(get_directory_primary() . '/regdir?f=&url=' . urlencode(z_root()) . '&realm=' . urlencode(get_directory_realm())), true));
|
||||
}
|
||||
|
||||
// Check for dead sites
|
||||
Master::Summon(array('Checksites'));
|
||||
|
||||
|
||||
// update searchable doc indexes
|
||||
Master::Summon(array('Importdoc'));
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use App;
|
||||
|
||||
// generate a curl compatible cookie file with an authenticated session for the given channel_id.
|
||||
// If this file is then used with curl and the destination url is sent through zid() or manually
|
||||
// manipulated to add a zid, it should allow curl to provide zot magic-auth across domains.
|
||||
@@ -10,15 +12,15 @@ namespace Zotlabs\Daemon;
|
||||
|
||||
class CurlAuth {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if($argc != 2)
|
||||
killme();
|
||||
if ($argc != 2)
|
||||
return;
|
||||
|
||||
\App::$session->start();
|
||||
App::$session->start();
|
||||
|
||||
$_SESSION['authenticated'] = 1;
|
||||
$_SESSION['uid'] = $argv[1];
|
||||
$_SESSION['uid'] = $argv[1];
|
||||
|
||||
$x = session_id();
|
||||
|
||||
@@ -29,14 +31,14 @@ class CurlAuth {
|
||||
|
||||
$output = '';
|
||||
|
||||
if($e) {
|
||||
if ($e) {
|
||||
$lines = file($f);
|
||||
if($lines) {
|
||||
foreach($lines as $line) {
|
||||
if(strlen($line) > 0 && $line[0] != '#' && substr_count($line, "\t") == 6) {
|
||||
if ($lines) {
|
||||
foreach ($lines as $line) {
|
||||
if (strlen($line) > 0 && $line[0] != '#' && substr_count($line, "\t") == 6) {
|
||||
$tokens = explode("\t", $line);
|
||||
$tokens = array_map('trim', $tokens);
|
||||
if($tokens[4] > time()) {
|
||||
if ($tokens[4] > time()) {
|
||||
$output .= $line . "\n";
|
||||
}
|
||||
}
|
||||
@@ -46,10 +48,10 @@ class CurlAuth {
|
||||
}
|
||||
}
|
||||
$t = time() + (24 * 3600);
|
||||
file_put_contents($f, $output . 'HttpOnly_' . \App::get_hostname() . "\tFALSE\t/\tTRUE\t$t\tPHPSESSID\t" . $x, (($e) ? FILE_APPEND : 0));
|
||||
file_put_contents($f, $output . 'HttpOnly_' . App::get_hostname() . "\tFALSE\t/\tTRUE\t$t\tPHPSESSID\t" . $x, (($e) ? FILE_APPEND : 0));
|
||||
|
||||
file_put_contents($c,$x);
|
||||
file_put_contents($c, $x);
|
||||
|
||||
killme();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,92 +2,32 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\DReport;
|
||||
|
||||
require_once('include/zot.php');
|
||||
require_once('include/queue_fn.php');
|
||||
|
||||
|
||||
class Deliver {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
if($argc < 2)
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if ($argc < 2)
|
||||
return;
|
||||
|
||||
logger('deliver: invoked: ' . print_r($argv,true), LOGGER_DATA);
|
||||
logger('deliver: invoked: ' . print_r($argv, true), LOGGER_DATA);
|
||||
|
||||
for($x = 1; $x < $argc; $x ++) {
|
||||
for ($x = 1; $x < $argc; $x++) {
|
||||
|
||||
if(! $argv[$x])
|
||||
if (!$argv[$x])
|
||||
continue;
|
||||
|
||||
$dresult = null;
|
||||
$r = q("select * from outq where outq_hash = '%s' limit 1",
|
||||
$r = q("select * from outq where outq_hash = '%s'",
|
||||
dbesc($argv[$x])
|
||||
);
|
||||
if($r) {
|
||||
|
||||
$notify = json_decode($r[0]['outq_notify'],true);
|
||||
|
||||
// Messages without an outq_msg will need to go via the web, even if it's a
|
||||
// local delivery. This includes conversation requests and refresh packets.
|
||||
|
||||
if(($r[0]['outq_posturl'] === z_root() . '/post') && ($r[0]['outq_msg'])) {
|
||||
logger('deliver: local delivery', LOGGER_DEBUG);
|
||||
|
||||
// local delivery
|
||||
// we should probably batch these and save a few delivery processes
|
||||
|
||||
if($r[0]['outq_msg']) {
|
||||
$m = json_decode($r[0]['outq_msg'],true);
|
||||
if(array_key_exists('message_list',$m)) {
|
||||
foreach($m['message_list'] as $mm) {
|
||||
$msg = array('body' => json_encode(array('success' => true, 'pickup' => array(array('notify' => $notify,'message' => $mm)))));
|
||||
zot_import($msg,z_root());
|
||||
}
|
||||
}
|
||||
else {
|
||||
$msg = array('body' => json_encode(array('success' => true, 'pickup' => array(array('notify' => $notify,'message' => $m)))));
|
||||
$dresult = zot_import($msg,z_root());
|
||||
}
|
||||
|
||||
remove_queue_item($r[0]['outq_hash']);
|
||||
|
||||
if($dresult && is_array($dresult)) {
|
||||
|
||||
// delivery reports for local deliveries do not require encryption
|
||||
|
||||
foreach($dresult as $xx) {
|
||||
if(is_array($xx) && array_key_exists('message_id',$xx)) {
|
||||
if(DReport::is_storable($xx)) {
|
||||
q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ",
|
||||
dbesc($xx['message_id']),
|
||||
dbesc($xx['location']),
|
||||
dbesc($xx['recipient']),
|
||||
dbesc(($xx['name']) ? $xx['name'] : EMPTY_STR),
|
||||
dbesc($xx['status']),
|
||||
dbesc(datetime_convert($xx['date'])),
|
||||
dbesc($xx['sender'])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
q("delete from dreport where dreport_queue = '%s'",
|
||||
dbesc($argv[$x])
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise it's a remote delivery - call queue_deliver() with the $immediate flag
|
||||
|
||||
queue_deliver($r[0],true);
|
||||
|
||||
if ($r) {
|
||||
queue_deliver($r[0], true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,21 +2,18 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
|
||||
class Deliver_hooks {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if($argc < 2)
|
||||
if ($argc < 2)
|
||||
return;
|
||||
|
||||
|
||||
$r = q("select * from item where id = '%d'",
|
||||
intval($argv[1])
|
||||
);
|
||||
if($r)
|
||||
call_hooks('notifier_normal',$r[0]);
|
||||
if ($r)
|
||||
call_hooks('notifier_normal', $r[0]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,47 +2,46 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
require_once('include/dir_fns.php');
|
||||
require_once('include/queue_fn.php');
|
||||
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Libzotdir;
|
||||
use Zotlabs\Lib\Queue;
|
||||
|
||||
class Directory {
|
||||
|
||||
static public function run($argc,$argv){
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if($argc < 2)
|
||||
if ($argc < 2)
|
||||
return;
|
||||
|
||||
$force = false;
|
||||
$force = false;
|
||||
$pushall = true;
|
||||
|
||||
if($argc > 2) {
|
||||
if($argv[2] === 'force')
|
||||
if ($argc > 2) {
|
||||
if ($argv[2] === 'force')
|
||||
$force = true;
|
||||
if($argv[2] === 'nopush')
|
||||
if ($argv[2] === 'nopush')
|
||||
$pushall = false;
|
||||
}
|
||||
}
|
||||
|
||||
logger('directory update', LOGGER_DEBUG);
|
||||
|
||||
$dirmode = get_config('system','directory_mode');
|
||||
if($dirmode === false)
|
||||
$dirmode = get_config('system', 'directory_mode');
|
||||
if ($dirmode === false)
|
||||
$dirmode = DIRECTORY_MODE_NORMAL;
|
||||
|
||||
$x = q("select * from channel where channel_id = %d limit 1",
|
||||
intval($argv[1])
|
||||
);
|
||||
if(! $x)
|
||||
if (!$x)
|
||||
return;
|
||||
|
||||
$channel = $x[0];
|
||||
|
||||
if($dirmode != DIRECTORY_MODE_NORMAL) {
|
||||
if ($dirmode != DIRECTORY_MODE_NORMAL) {
|
||||
|
||||
// this is an in-memory update and we don't need to send a network packet.
|
||||
|
||||
local_dir_update($argv[1],$force);
|
||||
Libzotdir::local_dir_update($argv[1], $force);
|
||||
|
||||
q("update channel set channel_dirdate = '%s' where channel_id = %d",
|
||||
dbesc(datetime_convert()),
|
||||
@@ -50,25 +49,26 @@ class Directory {
|
||||
);
|
||||
|
||||
// Now update all the connections
|
||||
if($pushall)
|
||||
Master::Summon(array('Notifier','refresh_all',$channel['channel_id']));
|
||||
if ($pushall)
|
||||
Master::Summon(array('Notifier', 'refresh_all', $channel['channel_id']));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise send the changes upstream
|
||||
|
||||
$directory = find_upstream_directory($dirmode);
|
||||
$url = $directory['url'] . '/post';
|
||||
$directory = Libzotdir::find_upstream_directory($dirmode);
|
||||
|
||||
$url = $directory['url'] . '/zot';
|
||||
|
||||
// ensure the upstream directory is updated
|
||||
|
||||
$packet = zot_build_packet($channel,(($force) ? 'force_refresh' : 'refresh'));
|
||||
$z = zot_zot($url,$packet);
|
||||
$packet = Libzot::build_packet($channel, (($force) ? 'force_refresh' : 'refresh'));
|
||||
$z = Libzot::zot($url, $packet, $channel);
|
||||
|
||||
// re-queue if unsuccessful
|
||||
|
||||
if(! $z['success']) {
|
||||
if (!$z['success']) {
|
||||
|
||||
/** @FIXME we aren't updating channel_dirdate if we have to queue
|
||||
* the directory packet. That means we'll try again on the next poll run.
|
||||
@@ -76,7 +76,7 @@ class Directory {
|
||||
|
||||
$hash = random_string();
|
||||
|
||||
queue_insert(array(
|
||||
Queue::insert(array(
|
||||
'hash' => $hash,
|
||||
'account_id' => $channel['channel_account_id'],
|
||||
'channel_id' => $channel['channel_id'],
|
||||
@@ -93,8 +93,8 @@ class Directory {
|
||||
}
|
||||
|
||||
// Now update all the connections
|
||||
if($pushall)
|
||||
Master::Summon(array('Notifier','refresh_all',$channel['channel_id']));
|
||||
if ($pushall)
|
||||
Master::Summon(array('Notifier', 'refresh_all', $channel['channel_id']));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,24 @@ namespace Zotlabs\Daemon;
|
||||
|
||||
class Expire {
|
||||
|
||||
static public function run($argc,$argv){
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
cli_startup();
|
||||
|
||||
$pid = get_config('procid', 'expire', false);
|
||||
if ($pid && (function_exists('posix_kill') ? posix_kill($pid, 0) : true)) {
|
||||
logger('procedure already run with pid ' . $pid, LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
$pid = getmypid();
|
||||
set_config('procid', 'expire', $pid);
|
||||
|
||||
// perform final cleanup on previously delete items
|
||||
|
||||
$r = q("select id from item where item_deleted = 1 and item_pending_remove = 0 and changed < %s - INTERVAL %s",
|
||||
db_utcnow(), db_quoteinterval('10 DAY')
|
||||
db_utcnow(),
|
||||
db_quoteinterval('10 DAY')
|
||||
);
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
@@ -23,23 +33,22 @@ class Expire {
|
||||
// physically remove anything that has been deleted for more than two months
|
||||
/** @FIXME - this is a wretchedly inefficient query */
|
||||
|
||||
$r = q("delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s",
|
||||
db_utcnow(), db_quoteinterval('36 DAY')
|
||||
q("delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s",
|
||||
db_utcnow(),
|
||||
db_quoteinterval('36 DAY')
|
||||
);
|
||||
|
||||
/** @FIXME make this optional as it could have a performance impact on large sites */
|
||||
|
||||
if (intval(get_config('system', 'optimize_items')))
|
||||
q("optimize table item");
|
||||
|
||||
logger('expire: start', LOGGER_DEBUG);
|
||||
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'));
|
||||
$commented_days = intval(get_config('system', 'active_expire_days'));
|
||||
|
||||
logger('site_expire: ' . $site_expire);
|
||||
|
||||
$r = q("SELECT channel_id, channel_system, channel_address, channel_expire_days from channel where true");
|
||||
$r = dbq("SELECT channel_id, channel_system, channel_address, channel_expire_days from channel where true");
|
||||
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
@@ -55,11 +64,12 @@ class Expire {
|
||||
$channel_expire = $service_class_expire;
|
||||
else
|
||||
$channel_expire = $site_expire;
|
||||
|
||||
|
||||
if (intval($channel_expire) && (intval($channel_expire) < intval($rr['channel_expire_days'])) ||
|
||||
intval($rr['channel_expire_days'] == 0)) {
|
||||
$expire_days = $channel_expire;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$expire_days = $rr['channel_expire_days'];
|
||||
}
|
||||
|
||||
@@ -84,11 +94,13 @@ class Expire {
|
||||
}
|
||||
|
||||
logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG);
|
||||
|
||||
|
||||
if ($expire_days)
|
||||
item_expire($x['channel_id'], $expire_days, $commented_days);
|
||||
|
||||
logger('Expire: sys: done', LOGGER_DEBUG);
|
||||
}
|
||||
|
||||
del_config('procid', 'expire');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,97 +2,180 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\ASCollection;
|
||||
|
||||
require_once('include/channel.php');
|
||||
|
||||
|
||||
class Externals {
|
||||
|
||||
static public function run($argc,$argv){
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
$total = 0;
|
||||
logger('externals: start');
|
||||
|
||||
$importer = get_sys_channel();
|
||||
$total = 0;
|
||||
$attempts = 0;
|
||||
|
||||
logger('externals: startup', LOGGER_DEBUG);
|
||||
|
||||
// pull in some public posts
|
||||
|
||||
while ($total == 0 && $attempts < 3) {
|
||||
$arr = ['url' => ''];
|
||||
call_hooks('externals_url_select', $arr);
|
||||
|
||||
while($total == 0 && $attempts < 3) {
|
||||
$arr = array('url' => '');
|
||||
call_hooks('externals_url_select',$arr);
|
||||
|
||||
if($arr['url']) {
|
||||
if ($arr['url']) {
|
||||
$url = $arr['url'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$randfunc = db_getfunc('RAND');
|
||||
|
||||
// fixme this query does not deal with directory realms.
|
||||
// fixme this query does not deal with directory realms.
|
||||
|
||||
$r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d and site_type = %d and site_dead = 0 order by $randfunc limit 1",
|
||||
$r = q("select site_url, site_pull from site where site_url != '%s'
|
||||
and site_flags != %d and site_type = %d
|
||||
and site_dead = 0 and site_project like '%s' and site_version > '5.3.1' order by $randfunc limit 1",
|
||||
dbesc(z_root()),
|
||||
intval(DIRECTORY_MODE_STANDALONE),
|
||||
intval(SITE_TYPE_ZOT)
|
||||
intval(SITE_TYPE_ZOT),
|
||||
dbesc('hubzilla%')
|
||||
);
|
||||
if($r)
|
||||
if ($r)
|
||||
$url = $r[0]['site_url'];
|
||||
}
|
||||
|
||||
$blacklisted = false;
|
||||
|
||||
if(! check_siteallowed($url)) {
|
||||
if (!check_siteallowed($url)) {
|
||||
logger('blacklisted site: ' . $url);
|
||||
$blacklisted = true;
|
||||
}
|
||||
|
||||
$attempts ++;
|
||||
$attempts++;
|
||||
|
||||
// make sure we can eventually break out if somebody blacklists all known sites
|
||||
|
||||
if($blacklisted) {
|
||||
if($attempts > 20)
|
||||
if ($blacklisted) {
|
||||
if ($attempts > 20)
|
||||
break;
|
||||
$attempts --;
|
||||
$attempts--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if($url) {
|
||||
if($r[0]['site_pull'] > NULL_DATE)
|
||||
$mindate = urlencode(datetime_convert('','',$r[0]['site_pull'] . ' - 1 day'));
|
||||
else {
|
||||
$days = get_config('externals','since_days');
|
||||
if($days === false)
|
||||
$days = 15;
|
||||
$mindate = urlencode(datetime_convert('','','now - ' . intval($days) . ' days'));
|
||||
}
|
||||
if ($url) {
|
||||
|
||||
$feedurl = $url . '/zotfeed?f=&mindate=' . $mindate;
|
||||
$max = intval(get_config('system', 'max_imported_posts', 30));
|
||||
if (intval($max)) {
|
||||
logger('externals: fetching outbox');
|
||||
|
||||
logger('externals: pulling public content from ' . $feedurl, LOGGER_DEBUG);
|
||||
$feed_url = $url . '/zotfeed';
|
||||
$obj = new ASCollection($feed_url, $importer, 0, $max);
|
||||
$messages = $obj->get();
|
||||
|
||||
$x = z_fetch_url($feedurl);
|
||||
if(($x) && ($x['success'])) {
|
||||
|
||||
q("update site set site_pull = '%s' where site_url = '%s'",
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($url)
|
||||
);
|
||||
|
||||
$j = json_decode($x['body'],true);
|
||||
if($j['success'] && $j['messages']) {
|
||||
$sys = get_sys_channel();
|
||||
foreach($j['messages'] as $message) {
|
||||
// on these posts, clear any route info.
|
||||
$message['route'] = '';
|
||||
$results = process_delivery(array('hash' => 'undefined'), get_item_elements($message),
|
||||
array(array('hash' => $sys['xchan_hash'])), false, true);
|
||||
$total ++;
|
||||
if ($messages) {
|
||||
foreach ($messages as $message) {
|
||||
if (is_string($message)) {
|
||||
$message = Activity::fetch($message, $importer);
|
||||
}
|
||||
$AS = new ActivityStreams($message);
|
||||
if ($AS->is_valid() && is_array($AS->obj)) {
|
||||
$item = Activity::decode_note($AS);
|
||||
Activity::store($importer, $importer['xchan_hash'], $AS, $item, true);
|
||||
$total++;
|
||||
}
|
||||
}
|
||||
logger('externals: import_public_posts: ' . $total . ' messages imported', LOGGER_DEBUG);
|
||||
}
|
||||
logger('externals: import_public_posts: ' . $total . ' messages imported', LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
/* $total = 0;
|
||||
$attempts = 0;
|
||||
|
||||
logger('externals: startup', LOGGER_DEBUG);
|
||||
|
||||
// pull in some public posts
|
||||
|
||||
while ($total == 0 && $attempts < 3) {
|
||||
$arr = ['url' => ''];
|
||||
call_hooks('externals_url_select', $arr);
|
||||
|
||||
if ($arr['url']) {
|
||||
$url = $arr['url'];
|
||||
}
|
||||
else {
|
||||
$randfunc = db_getfunc('RAND');
|
||||
|
||||
// fixme this query does not deal with directory realms.
|
||||
|
||||
$r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d and site_type = %d and site_dead = 0 order by $randfunc limit 1",
|
||||
dbesc(z_root()),
|
||||
intval(DIRECTORY_MODE_STANDALONE),
|
||||
intval(SITE_TYPE_ZOT)
|
||||
);
|
||||
if ($r)
|
||||
$url = $r[0]['site_url'];
|
||||
}
|
||||
|
||||
$blacklisted = false;
|
||||
|
||||
if (!check_siteallowed($url)) {
|
||||
logger('blacklisted site: ' . $url);
|
||||
$blacklisted = true;
|
||||
}
|
||||
|
||||
$attempts++;
|
||||
|
||||
// make sure we can eventually break out if somebody blacklists all known sites
|
||||
|
||||
if ($blacklisted) {
|
||||
if ($attempts > 20)
|
||||
break;
|
||||
$attempts--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($url) {
|
||||
if ($r[0]['site_pull'] > NULL_DATE)
|
||||
$mindate = urlencode(datetime_convert('', '', $r[0]['site_pull'] . ' - 1 day'));
|
||||
else {
|
||||
$days = get_config('externals', 'since_days');
|
||||
if ($days === false)
|
||||
$days = 15;
|
||||
$mindate = urlencode(datetime_convert('', '', 'now - ' . intval($days) . ' days'));
|
||||
}
|
||||
|
||||
$feedurl = $url . '/zotfeed?f=&mindate=' . $mindate;
|
||||
|
||||
logger('externals: pulling public content from ' . $feedurl, LOGGER_DEBUG);
|
||||
|
||||
$x = z_fetch_url($feedurl);
|
||||
if (($x) && ($x['success'])) {
|
||||
|
||||
q("update site set site_pull = '%s' where site_url = '%s'",
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($url)
|
||||
);
|
||||
|
||||
$j = json_decode($x['body'], true);
|
||||
if ($j['success'] && $j['messages']) {
|
||||
$sys = get_sys_channel();
|
||||
foreach ($j['messages'] as $message) {
|
||||
// on these posts, clear any route info.
|
||||
$message['route'] = '';
|
||||
process_delivery(['hash' => 'undefined'], get_item_elements($message),
|
||||
[['hash' => $sys['xchan_hash']]], false, true);
|
||||
$total++;
|
||||
}
|
||||
logger('externals: import_public_posts: ' . $total . ' messages imported', LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,32 +2,38 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Webfinger;
|
||||
use Zotlabs\Lib\Zotfinger;
|
||||
|
||||
// performs zot_finger on $argv[1], which is a hex_encoded webbie/reddress
|
||||
|
||||
class Gprobe {
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if($argc != 2)
|
||||
if ($argc != 2)
|
||||
return;
|
||||
|
||||
$url = hex2bin($argv[1]);
|
||||
|
||||
if(! strpos($url,'@'))
|
||||
if (!strpos($url, '@'))
|
||||
return;
|
||||
|
||||
$r = q("select * from hubloc where hubloc_addr = '%s' limit 1",
|
||||
$r = q("select * from hubloc where hubloc_addr = '%s' and hubloc_network = 'zot6' limit 1",
|
||||
dbesc($url)
|
||||
);
|
||||
|
||||
if(! $r) {
|
||||
$j = \Zotlabs\Zot\Finger::run($url,null);
|
||||
if($j['success']) {
|
||||
$y = import_xchan($j);
|
||||
if (!$r) {
|
||||
$href = Webfinger::zot_url(punify($url));
|
||||
if ($href) {
|
||||
$zf = Zotfinger::exec($href, null);
|
||||
}
|
||||
if (is_array($zf) && array_path_exists('signature/signer', $zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) {
|
||||
Libzot::import_xchan($zf['data']);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
14
Zotlabs/Daemon/Importdoc.php
Executable file → Normal file
14
Zotlabs/Daemon/Importdoc.php
Executable file → Normal file
@@ -5,7 +5,7 @@ namespace Zotlabs\Daemon;
|
||||
|
||||
class Importdoc {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
require_once('include/help.php');
|
||||
|
||||
@@ -16,20 +16,20 @@ class Importdoc {
|
||||
static public function update_docs_dir($s) {
|
||||
$f = basename($s);
|
||||
$d = dirname($s);
|
||||
if($s === 'doc/html')
|
||||
if ($s === 'doc/html')
|
||||
return;
|
||||
$files = glob("$d/$f");
|
||||
if($files) {
|
||||
foreach($files as $fi) {
|
||||
if($fi === 'doc/html') {
|
||||
if ($files) {
|
||||
foreach ($files as $fi) {
|
||||
if ($fi === 'doc/html') {
|
||||
continue;
|
||||
}
|
||||
if(is_dir($fi)) {
|
||||
if (is_dir($fi)) {
|
||||
self::update_docs_dir("$fi/*");
|
||||
}
|
||||
else {
|
||||
// don't update media content
|
||||
if(strpos(z_mime_content_type($fi),'text') === 0) {
|
||||
if (strpos(z_mime_content_type($fi), 'text') === 0) {
|
||||
store_doc_file($fi);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,25 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
class Importfile {
|
||||
|
||||
static public function run($argc,$argv){
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
logger('Importfile: ' . print_r($argv,true));
|
||||
logger('Importfile: ' . print_r($argv, true));
|
||||
|
||||
if($argc < 3)
|
||||
if ($argc < 3)
|
||||
return;
|
||||
|
||||
$channel = channelx_by_n($argv[1]);
|
||||
if(! $channel)
|
||||
if (!$channel)
|
||||
return;
|
||||
|
||||
$srcfile = $argv[2];
|
||||
$folder = (($argc > 3) ? $argv[3] : '');
|
||||
$dstname = (($argc > 4) ? $argv[4] : '');
|
||||
|
||||
$hash = random_string();
|
||||
$hash = random_string();
|
||||
|
||||
$arr = [
|
||||
'src' => $srcfile,
|
||||
@@ -33,15 +34,15 @@ class Importfile {
|
||||
'replace' => true
|
||||
];
|
||||
|
||||
if($folder)
|
||||
if ($folder)
|
||||
$arr['folder'] = $folder;
|
||||
|
||||
attach_store($channel,$channel['channel_hash'],'import',$arr);
|
||||
attach_store($channel, $channel['channel_hash'], 'import', $arr);
|
||||
|
||||
$sync = attach_export_data($channel, $hash);
|
||||
if ($sync)
|
||||
Libsync::build_sync_packet($channel['channel_id'], ['file' => [$sync]]);
|
||||
|
||||
$sync = attach_export_data($channel,$hash);
|
||||
if($sync)
|
||||
build_sync_packet($channel['channel_id'],array('file' => array($sync)));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,42 +2,57 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
if(array_search( __file__ , get_included_files()) === 0) {
|
||||
if (array_search(__file__, get_included_files()) === 0) {
|
||||
require_once('include/cli_startup.php');
|
||||
array_shift($argv);
|
||||
$argc = count($argv);
|
||||
|
||||
if($argc)
|
||||
Master::Release($argc,$argv);
|
||||
killme();
|
||||
if ($argc)
|
||||
Master::Release($argc, $argv);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Master {
|
||||
|
||||
static public function Summon($arr) {
|
||||
proc_run('php','Zotlabs/Daemon/Master.php',$arr);
|
||||
$hookinfo = [
|
||||
'argv' => $arr
|
||||
];
|
||||
|
||||
call_hooks('daemon_master_summon', $hookinfo);
|
||||
|
||||
$arr = $hookinfo['argv'];
|
||||
$argc = count($arr);
|
||||
|
||||
if ((!is_array($arr) || ($argc < 1))) {
|
||||
logger("Summon handled by hook.", LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
$phpbin = get_config('system', 'phpbin', 'php');
|
||||
proc_run($phpbin, 'Zotlabs/Daemon/Master.php', $arr);
|
||||
}
|
||||
|
||||
static public function Release($argc,$argv) {
|
||||
static public function Release($argc, $argv) {
|
||||
cli_startup();
|
||||
|
||||
$hookinfo = [
|
||||
'argv'=>$argv
|
||||
'argv' => $argv
|
||||
];
|
||||
|
||||
call_hooks ('daemon_master_release',$hookinfo);
|
||||
call_hooks('daemon_master_release', $hookinfo);
|
||||
|
||||
$argv = $hookinfo['argv'];
|
||||
$argc = count($argv);
|
||||
|
||||
if ((!is_array($argv) || (count($argv) < 1))) {
|
||||
if ((!is_array($argv) || ($argc < 1))) {
|
||||
logger("Release handled by hook.", LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
logger('Master: release: ' . json_encode($argv), LOGGER_ALL,LOG_DEBUG);
|
||||
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
|
||||
$cls::run($argc,$argv);
|
||||
logger('Master: release: ' . json_encode($argv), LOGGER_ALL, LOG_DEBUG);
|
||||
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
|
||||
$cls::run($argc, $argv);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,31 +2,34 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Libzotdir;
|
||||
|
||||
require_once('include/zot.php');
|
||||
require_once('include/dir_fns.php');
|
||||
|
||||
|
||||
class Onedirsync {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
logger('onedirsync: start ' . intval($argv[1]));
|
||||
|
||||
if(($argc > 1) && (intval($argv[1])))
|
||||
|
||||
if (($argc > 1) && (intval($argv[1])))
|
||||
$update_id = intval($argv[1]);
|
||||
|
||||
if(! $update_id) {
|
||||
if (!$update_id) {
|
||||
logger('onedirsync: no update');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$r = q("select * from updates where ud_id = %d limit 1",
|
||||
intval($update_id)
|
||||
);
|
||||
|
||||
if(! $r)
|
||||
if (!$r)
|
||||
return;
|
||||
if(($r[0]['ud_flags'] & UPDATE_FLAGS_UPDATED) || (! $r[0]['ud_addr']))
|
||||
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
|
||||
@@ -38,8 +41,8 @@ class Onedirsync {
|
||||
dbesc($r[0]['ud_date']),
|
||||
intval(UPDATE_FLAGS_UPDATED)
|
||||
);
|
||||
if($x) {
|
||||
$y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 and ud_date != '%s'",
|
||||
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),
|
||||
@@ -50,11 +53,14 @@ class Onedirsync {
|
||||
|
||||
// ignore doing an update if this ud_addr refers to a known dead hubloc
|
||||
|
||||
$h = q("select * from hubloc where hubloc_addr = '%s' limit 1",
|
||||
$h = q("select * from hubloc where hubloc_addr = '%s'",
|
||||
dbesc($r[0]['ud_addr'])
|
||||
);
|
||||
if(($h) && ($h[0]['hubloc_status'] & HUBLOC_OFFLINE)) {
|
||||
$y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 ",
|
||||
|
||||
$h = Libzot::zot_record_preferred($h);
|
||||
|
||||
if (($h) && ($h['hubloc_status'] & HUBLOC_OFFLINE)) {
|
||||
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 ",
|
||||
intval(UPDATE_FLAGS_UPDATED),
|
||||
dbesc($r[0]['ud_addr']),
|
||||
intval(UPDATE_FLAGS_UPDATED)
|
||||
@@ -63,13 +69,13 @@ class Onedirsync {
|
||||
return;
|
||||
}
|
||||
|
||||
// we might have to pull this out some day, but for now update_directory_entry()
|
||||
// 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[0]['hubloc_network'] !== 'zot')
|
||||
if ($h && !in_array($h['hubloc_network'], ['zot6', 'zot']))
|
||||
return;
|
||||
|
||||
update_directory_entry($r[0]);
|
||||
Libzotdir::update_directory_entry($r[0]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,84 +2,94 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
require_once('include/socgraph.php');
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\ASCollection;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
|
||||
require_once('include/socgraph.php');
|
||||
|
||||
class Onepoll {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
logger('onepoll: start');
|
||||
|
||||
if(($argc > 1) && (intval($argv[1])))
|
||||
|
||||
if (($argc > 1) && (intval($argv[1])))
|
||||
$contact_id = intval($argv[1]);
|
||||
|
||||
if(! $contact_id) {
|
||||
if (!$contact_id) {
|
||||
logger('onepoll: no contact');
|
||||
return;
|
||||
}
|
||||
|
||||
$d = datetime_convert();
|
||||
$sql_extra = '';
|
||||
$allow_feeds = get_config('system', 'feed_contacts');
|
||||
if(!$allow_feeds) {
|
||||
$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
|
||||
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)
|
||||
);
|
||||
);
|
||||
|
||||
if(! $contacts) {
|
||||
if (!$contacts) {
|
||||
logger('onepoll: abook_id not found: ' . $contact_id);
|
||||
return;
|
||||
}
|
||||
|
||||
$contact = $contacts[0];
|
||||
|
||||
$t = $contact['abook_updated'];
|
||||
|
||||
$contact = array_shift($contacts);
|
||||
$importer_uid = $contact['abook_channel'];
|
||||
|
||||
|
||||
$r = q("SELECT * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1",
|
||||
intval($importer_uid)
|
||||
);
|
||||
|
||||
if(! $r)
|
||||
if (!$r)
|
||||
return;
|
||||
|
||||
$importer = $r[0];
|
||||
|
||||
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')
|
||||
: datetime_convert('UTC','UTC',$contact['abook_updated'] . ' - 2 days')
|
||||
$last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= NULL_DATE))
|
||||
? datetime_convert('UTC', 'UTC', 'now - 7 days')
|
||||
: datetime_convert('UTC', 'UTC', $contact['abook_updated'] . ' - 2 days')
|
||||
);
|
||||
|
||||
if($contact['xchan_network'] === 'rss') {
|
||||
if ($contact['xchan_network'] === 'rss') {
|
||||
logger('onepoll: processing feed ' . $contact['xchan_name'], LOGGER_DEBUG);
|
||||
handle_feed($importer['channel_id'],$contact_id,$contact['xchan_hash']);
|
||||
q("update abook set abook_connected = '%s' where abook_id = %d",
|
||||
dbesc(datetime_convert()),
|
||||
intval($contact['abook_id'])
|
||||
);
|
||||
$alive = handle_feed($importer['channel_id'], $contact_id, $contact['xchan_hash']);
|
||||
if ($alive) {
|
||||
q("update abook set abook_connected = '%s' where abook_id = %d",
|
||||
dbesc(datetime_convert()),
|
||||
intval($contact['abook_id'])
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(! in_array($contact['xchan_network'],['zot','zot6']))
|
||||
|
||||
if (!in_array($contact['xchan_network'], ['zot', 'zot6']))
|
||||
return;
|
||||
|
||||
// update permissions
|
||||
|
||||
$x = zot_refresh($contact,$importer);
|
||||
if ($contact['xchan_network'] === 'zot6')
|
||||
$x = Libzot::refresh($contact, $importer);
|
||||
|
||||
if ($contact['xchan_network'] === 'zot')
|
||||
$x = zot_refresh($contact, $importer);
|
||||
|
||||
$responded = false;
|
||||
$updated = datetime_convert();
|
||||
$connected = datetime_convert();
|
||||
if(! $x) {
|
||||
if (!$x) {
|
||||
// mark for death by not updating abook_connected, this is caught in include/poller.php
|
||||
q("update abook set abook_updated = '%s' where abook_id = %d",
|
||||
dbesc($updated),
|
||||
@@ -95,83 +105,68 @@ class Onepoll {
|
||||
$responded = true;
|
||||
}
|
||||
|
||||
if(! $responded)
|
||||
if (!$responded)
|
||||
return;
|
||||
|
||||
if($contact['xchan_connurl']) {
|
||||
$fetch_feed = true;
|
||||
$x = null;
|
||||
$fetch_feed = true;
|
||||
$x = null;
|
||||
|
||||
// They haven't given us permission to see their stream
|
||||
// They haven't given us permission to see their stream
|
||||
|
||||
$can_view_stream = intval(get_abconfig($importer_uid,$contact['abook_xchan'],'their_perms','view_stream'));
|
||||
$can_view_stream = intval(get_abconfig($importer_uid, $contact['abook_xchan'], 'their_perms', 'view_stream'));
|
||||
|
||||
if(! $can_view_stream)
|
||||
$fetch_feed = false;
|
||||
if (!$can_view_stream)
|
||||
$fetch_feed = false;
|
||||
|
||||
// we haven't given them permission to send us their stream
|
||||
// we haven't given them permission to send us their stream
|
||||
|
||||
$can_send_stream = intval(get_abconfig($importer_uid,$contact['abook_xchan'],'my_perms','send_stream'));
|
||||
|
||||
if(! $can_send_stream)
|
||||
$fetch_feed = false;
|
||||
$can_send_stream = intval(get_abconfig($importer_uid, $contact['abook_xchan'], 'my_perms', 'send_stream'));
|
||||
|
||||
if($fetch_feed) {
|
||||
if (!$can_send_stream)
|
||||
$fetch_feed = false;
|
||||
|
||||
if(strpos($contact['xchan_connurl'],z_root()) === 0) {
|
||||
// local channel - save a network fetch
|
||||
$c = channelx_by_hash($contact['xchan_hash']);
|
||||
if($c) {
|
||||
$x = [
|
||||
'success' => true,
|
||||
'body' => json_encode( [
|
||||
'success' => true,
|
||||
'messages' => zot_feed($c['channel_id'], $importer['xchan_hash'], [ 'mindate' => $last_update ])
|
||||
])
|
||||
];
|
||||
}
|
||||
if ($fetch_feed && $contact['xchan_network'] !== 'zot') {
|
||||
|
||||
$max = intval(get_config('system', 'max_imported_posts', 30));
|
||||
|
||||
if (intval($max)) {
|
||||
$cl = get_xconfig($contact['abook_xchan'], 'activitypub', 'collections');
|
||||
|
||||
if (is_array($cl) && $cl) {
|
||||
$url = ((array_key_exists('outbox', $cl)) ? $cl['outbox'] : '');
|
||||
}
|
||||
else {
|
||||
// remote fetch
|
||||
|
||||
$feedurl = str_replace('/poco/','/zotfeed/',$contact['xchan_connurl']);
|
||||
$feedurl .= '?f=&mindate=' . urlencode($last_update) . '&zid=' . $importer['channel_address'] . '@' . \App::get_hostname();
|
||||
$recurse = 0;
|
||||
$x = z_fetch_url($feedurl, false, $recurse, [ 'session' => true ]);
|
||||
$url = str_replace('/poco/', '/zotfeed/', $contact['xchan_connurl']);
|
||||
}
|
||||
|
||||
logger('feed_update: ' . print_r($x,true), LOGGER_DATA);
|
||||
}
|
||||
|
||||
if(($x) && ($x['success'])) {
|
||||
$total = 0;
|
||||
logger('onepoll: feed update ' . $contact['xchan_name'] . ' ' . $feedurl);
|
||||
|
||||
$j = json_decode($x['body'],true);
|
||||
if($j['success'] && $j['messages']) {
|
||||
foreach($j['messages'] as $message) {
|
||||
$results = process_delivery(array('hash' => $contact['xchan_hash']), get_item_elements($message),
|
||||
array(array('hash' => $importer['xchan_hash'])), false);
|
||||
logger('onepoll: feed_update: process_delivery: ' . print_r($results,true), LOGGER_DATA);
|
||||
$total ++;
|
||||
if ($url) {
|
||||
logger('fetching outbox');
|
||||
$url = $url . '?date_begin=' . urlencode($last_update);
|
||||
$obj = new ASCollection($url, $importer, 0, $max);
|
||||
$messages = $obj->get();
|
||||
if ($messages) {
|
||||
foreach ($messages as $message) {
|
||||
if (is_string($message)) {
|
||||
$message = Activity::fetch($message, $importer);
|
||||
}
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
logger("onepoll: $total messages processed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// update the poco details for this connection
|
||||
|
||||
if($contact['xchan_connurl']) {
|
||||
$r = q("SELECT xlink_id from xlink
|
||||
where xlink_xchan = '%s' and xlink_updated > %s - INTERVAL %s and xlink_static = 0 limit 1",
|
||||
intval($contact['xchan_hash']),
|
||||
db_utcnow(), db_quoteinterval('1 DAY')
|
||||
);
|
||||
if(! $r) {
|
||||
poco_load($contact['xchan_hash'],$contact['xchan_connurl']);
|
||||
}
|
||||
$r = q("SELECT xlink_id from xlink where xlink_xchan = '%s' and xlink_updated > %s - INTERVAL %s and xlink_static = 0 limit 1",
|
||||
intval($contact['xchan_hash']),
|
||||
db_utcnow(), db_quoteinterval('1 DAY')
|
||||
);
|
||||
if (!$r) {
|
||||
poco_load($contact['xchan_hash'], $contact['xchan_connurl']);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -4,73 +4,67 @@ namespace Zotlabs\Daemon;
|
||||
|
||||
class Poller {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
$maxsysload = intval(get_config('system','maxloadavg'));
|
||||
if($maxsysload < 1)
|
||||
$maxsysload = intval(get_config('system', 'maxloadavg'));
|
||||
if ($maxsysload < 1)
|
||||
$maxsysload = 50;
|
||||
if(function_exists('sys_getloadavg')) {
|
||||
if (function_exists('sys_getloadavg')) {
|
||||
$load = sys_getloadavg();
|
||||
if(intval($load[0]) > $maxsysload) {
|
||||
if (intval($load[0]) > $maxsysload) {
|
||||
logger('system: load ' . $load . ' too high. Poller deferred to next scheduled run.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$interval = intval(get_config('system','poll_interval'));
|
||||
if(! $interval)
|
||||
$interval = ((get_config('system','delivery_interval') === false) ? 3 : intval(get_config('system','delivery_interval')));
|
||||
$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'))) {
|
||||
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);
|
||||
|
||||
logger('poller: start');
|
||||
|
||||
|
||||
$manual_id = 0;
|
||||
$generation = 0;
|
||||
$force = false;
|
||||
|
||||
$force = false;
|
||||
$restart = false;
|
||||
|
||||
if(($argc > 1) && ($argv[1] == 'force'))
|
||||
if (($argc > 1) && ($argv[1] == 'force'))
|
||||
$force = true;
|
||||
|
||||
if(($argc > 1) && ($argv[1] == 'restart')) {
|
||||
$restart = true;
|
||||
if (($argc > 1) && ($argv[1] == 'restart')) {
|
||||
$generation = intval($argv[2]);
|
||||
if(! $generation)
|
||||
killme();
|
||||
if (!$generation)
|
||||
return;
|
||||
}
|
||||
|
||||
if(($argc > 1) && intval($argv[1])) {
|
||||
if (($argc > 1) && intval($argv[1])) {
|
||||
$manual_id = intval($argv[1]);
|
||||
$force = true;
|
||||
}
|
||||
|
||||
|
||||
$sql_extra = (($manual_id) ? " AND abook_id = " . intval($manual_id) . " " : "");
|
||||
|
||||
reload_plugins();
|
||||
|
||||
$d = datetime_convert();
|
||||
|
||||
// Only poll from those with suitable relationships
|
||||
|
||||
$abandon_sql = (($abandon_days)
|
||||
? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days).' DAY'))
|
||||
: ''
|
||||
$abandon_days = intval(get_config('system', 'account_abandon_days', 0));
|
||||
$abandon_sql = (($abandon_days)
|
||||
? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days) . ' DAY'))
|
||||
: ''
|
||||
);
|
||||
|
||||
$randfunc = db_getfunc('RAND');
|
||||
|
||||
|
||||
$contacts = q("SELECT abook.abook_updated, abook.abook_connected, abook.abook_feed,
|
||||
abook.abook_channel, abook.abook_id, abook.abook_archived, abook.abook_pending,
|
||||
abook.abook_ignored, abook.abook_blocked,
|
||||
@@ -85,120 +79,120 @@ class Poller {
|
||||
intval(ACCOUNT_UNVERIFIED) // FIXME
|
||||
);
|
||||
|
||||
if($contacts) {
|
||||
if ($contacts) {
|
||||
foreach ($contacts as $contact) {
|
||||
|
||||
foreach($contacts as $contact) {
|
||||
|
||||
$update = false;
|
||||
$update = false;
|
||||
|
||||
$t = $contact['abook_updated'];
|
||||
$c = $contact['abook_connected'];
|
||||
|
||||
if(intval($contact['abook_feed'])) {
|
||||
$min = service_class_fetch($contact['abook_channel'],'minimum_feedcheck_minutes');
|
||||
if(! $min)
|
||||
$min = intval(get_config('system','minimum_feedcheck_minutes'));
|
||||
if(! $min)
|
||||
if (intval($contact['abook_feed'])) {
|
||||
$min = service_class_fetch($contact['abook_channel'], 'minimum_feedcheck_minutes');
|
||||
if (!$min)
|
||||
$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(array('Onepoll',$contact['abook_id']));
|
||||
if($interval)
|
||||
@time_sleep_until(microtime(true) + (float) $interval);
|
||||
$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);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if(! in_array($contact['xchan_network'],['zot','zot6']))
|
||||
if (!in_array($contact['xchan_network'], ['zot', 'zot6']))
|
||||
continue;
|
||||
|
||||
if($c == $t) {
|
||||
if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day"))
|
||||
if ($c == $t) {
|
||||
if (datetime_convert('UTC', 'UTC', 'now') > datetime_convert('UTC', 'UTC', $t . " + 1 day"))
|
||||
$update = true;
|
||||
}
|
||||
else {
|
||||
|
||||
|
||||
// if we've never connected with them, start the mark for death countdown from now
|
||||
|
||||
if($c <= NULL_DATE) {
|
||||
$r = q("update abook set abook_connected = '%s' where abook_id = %d",
|
||||
|
||||
if ($c <= NULL_DATE) {
|
||||
q("update abook set abook_connected = '%s' where abook_id = %d",
|
||||
dbesc(datetime_convert()),
|
||||
intval($contact['abook_id'])
|
||||
);
|
||||
$c = datetime_convert();
|
||||
$c = datetime_convert();
|
||||
$update = true;
|
||||
}
|
||||
|
||||
// He's dead, Jim
|
||||
|
||||
if(strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $c . " + 30 day")) > 0) {
|
||||
$r = q("update abook set abook_archived = 1 where abook_id = %d",
|
||||
if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 30 day")) > 0) {
|
||||
q("update abook set abook_archived = 1 where abook_id = %d",
|
||||
intval($contact['abook_id'])
|
||||
);
|
||||
$update = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(intval($contact['abook_archived'])) {
|
||||
$update = false;
|
||||
if (intval($contact['abook_archived'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// might be dead, so maybe don't poll quite so often
|
||||
|
||||
|
||||
// recently deceased, so keep up the regular schedule for 3 days
|
||||
|
||||
if((strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $c . " + 3 day")) > 0)
|
||||
&& (strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $t . " + 1 day")) > 0))
|
||||
|
||||
if ((strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 3 day")) > 0)
|
||||
&& (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 1 day")) > 0))
|
||||
$update = true;
|
||||
|
||||
// After that back off and put them on a morphine drip
|
||||
|
||||
if(strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $t . " + 2 day")) > 0) {
|
||||
if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 2 day")) > 0) {
|
||||
$update = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(intval($contact['abook_pending']) || intval($contact['abook_archived']) || intval($contact['abook_ignored']) || intval($contact['abook_blocked']))
|
||||
if (intval($contact['abook_pending']) || intval($contact['abook_archived']) || intval($contact['abook_ignored']) || intval($contact['abook_blocked']))
|
||||
continue;
|
||||
|
||||
if((! $update) && (! $force))
|
||||
continue;
|
||||
if ((!$update) && (!$force))
|
||||
continue;
|
||||
|
||||
Master::Summon(array('Onepoll',$contact['abook_id']));
|
||||
if($interval)
|
||||
@time_sleep_until(microtime(true) + (float) $interval);
|
||||
Master::Summon(['Onepoll', $contact['abook_id']]);
|
||||
if ($interval)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
|
||||
$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),
|
||||
dbesc(NULL_DATE),
|
||||
db_utcnow(), db_quoteinterval('7 DAY')
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
|
||||
// If they didn't respond when we attempted before, back off to once a day
|
||||
// After 7 days we won't bother anymore
|
||||
|
||||
if($rr['ud_last'] > NULL_DATE)
|
||||
if($rr['ud_last'] > datetime_convert('UTC','UTC', 'now - 1 day'))
|
||||
if ($rr['ud_last'] > NULL_DATE)
|
||||
if ($rr['ud_last'] > datetime_convert('UTC', 'UTC', 'now - 1 day'))
|
||||
continue;
|
||||
Master::Summon(array('Onedirsync',$rr['ud_id']));
|
||||
if($interval)
|
||||
@time_sleep_until(microtime(true) + (float) $interval);
|
||||
Master::Summon(['Onedirsync', $rr['ud_id']]);
|
||||
if ($interval)
|
||||
@time_sleep_until(microtime(true) + (float)$interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_config('system','lastpoll',datetime_convert());
|
||||
set_config('system', 'lastpoll', datetime_convert());
|
||||
|
||||
//All done - clear the lockfile
|
||||
|
||||
@unlink($lockfile);
|
||||
|
||||
return;
|
||||
|
||||
@@ -7,12 +7,12 @@ require_once('include/zot.php');
|
||||
|
||||
class Queue {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
require_once('include/items.php');
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
if($argc > 1)
|
||||
if ($argc > 1)
|
||||
$queue_id = $argv[1];
|
||||
else
|
||||
$queue_id = EMPTY_STR;
|
||||
@@ -25,11 +25,10 @@ class Queue {
|
||||
$r = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s",
|
||||
db_utcnow(), db_quoteinterval('3 DAY')
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$site_url = '';
|
||||
$h = parse_url($rr['outq_posturl']);
|
||||
$desturl = $h['scheme'] . '://' . $h['host'] . (($h['port']) ? ':' . $h['port'] : '');
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
$h = parse_url($rr['outq_posturl']);
|
||||
$desturl = $h['scheme'] . '://' . $h['host'] . (isset($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')
|
||||
@@ -37,11 +36,11 @@ class Queue {
|
||||
}
|
||||
}
|
||||
|
||||
$r = q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s",
|
||||
q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s",
|
||||
db_utcnow(), db_quoteinterval('3 DAY')
|
||||
);
|
||||
|
||||
if($queue_id) {
|
||||
if ($queue_id) {
|
||||
$r = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1",
|
||||
dbesc($queue_id)
|
||||
);
|
||||
@@ -54,7 +53,7 @@ class Queue {
|
||||
// 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
|
||||
@@ -67,7 +66,7 @@ class Queue {
|
||||
db_utcnow()
|
||||
);
|
||||
while ($r) {
|
||||
foreach($r as $rv) {
|
||||
foreach ($r as $rv) {
|
||||
queue_deliver($rv);
|
||||
}
|
||||
$r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1",
|
||||
@@ -75,10 +74,10 @@ class Queue {
|
||||
);
|
||||
}
|
||||
}
|
||||
if(! $r)
|
||||
if (!$r)
|
||||
return;
|
||||
|
||||
foreach($r as $rv) {
|
||||
foreach ($r as $rv) {
|
||||
queue_deliver($rv);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,11 @@ class Ratenotif {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
|
||||
// Deprecated
|
||||
return;
|
||||
|
||||
|
||||
require_once("datetime.php");
|
||||
require_once('include/items.php');
|
||||
|
||||
|
||||
@@ -5,30 +5,30 @@ namespace Zotlabs\Daemon;
|
||||
|
||||
class Thumbnail {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if(! $argc == 2)
|
||||
if (!$argc == 2)
|
||||
return;
|
||||
|
||||
$c = q("select * from attach where hash = '%s' ",
|
||||
dbesc($argv[1])
|
||||
);
|
||||
|
||||
if(! $c)
|
||||
if (!$c)
|
||||
return;
|
||||
|
||||
$attach = $c[0];
|
||||
|
||||
$preview_style = intval(get_config('system','thumbnail_security',0));
|
||||
$preview_width = intval(get_config('system','thumbnail_width',300));
|
||||
$preview_height = intval(get_config('system','thumbnail_height',300));
|
||||
$preview_style = intval(get_config('system', 'thumbnail_security', 0));
|
||||
$preview_width = intval(get_config('system', 'thumbnail_width', 300));
|
||||
$preview_height = intval(get_config('system', 'thumbnail_height', 300));
|
||||
|
||||
$p = [
|
||||
'attach' => $attach,
|
||||
'preview_style' => $preview_style,
|
||||
'preview_width' => $preview_width,
|
||||
'preview_height' => $preview_height,
|
||||
'thumbnail' => null
|
||||
'thumbnail' => null
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -40,39 +40,39 @@ class Thumbnail {
|
||||
* * \e string \b thumbnail
|
||||
*/
|
||||
|
||||
call_hooks('thumbnail',$p);
|
||||
if($p['thumbnail']) {
|
||||
call_hooks('thumbnail', $p);
|
||||
if ($p['thumbnail']) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$default_controller = null;
|
||||
|
||||
|
||||
$files = glob('Zotlabs/Thumbs/*.php');
|
||||
if($files) {
|
||||
foreach($files as $f) {
|
||||
$clsname = '\\Zotlabs\\Thumbs\\' . ucfirst(basename($f,'.php'));
|
||||
if(class_exists($clsname)) {
|
||||
if ($files) {
|
||||
foreach ($files as $f) {
|
||||
$clsname = '\\Zotlabs\\Thumbs\\' . ucfirst(basename($f, '.php'));
|
||||
if (class_exists($clsname)) {
|
||||
$x = new $clsname();
|
||||
if(method_exists($x,'Match')) {
|
||||
if (method_exists($x, 'Match')) {
|
||||
$matched = $x->Match($attach['filetype']);
|
||||
if($matched) {
|
||||
$x->Thumb($attach,$preview_style,$preview_width,$preview_height);
|
||||
if ($matched) {
|
||||
$x->Thumb($attach, $preview_style, $preview_width, $preview_height);
|
||||
}
|
||||
}
|
||||
if(method_exists($x,'MatchDefault')) {
|
||||
$default_matched = $x->MatchDefault(substr($attach['filetype'],0,strpos($attach['filetype'],'/')));
|
||||
if($default_matched) {
|
||||
if (method_exists($x, 'MatchDefault')) {
|
||||
$default_matched = $x->MatchDefault(substr($attach['filetype'], 0, strpos($attach['filetype'], '/')));
|
||||
if ($default_matched) {
|
||||
$default_controller = $x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(($default_controller)
|
||||
&& ((! file_exists(dbunescbin($attach['content']) . '.thumb'))
|
||||
if (($default_controller)
|
||||
&& ((!file_exists(dbunescbin($attach['content']) . '.thumb'))
|
||||
|| (filectime(dbunescbin($attach['content']) . 'thumb') < (time() - 60)))) {
|
||||
$default_controller->Thumb($attach,$preview_style,$preview_width,$preview_height);
|
||||
$default_controller->Thumb($attach, $preview_style, $preview_width, $preview_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Identity\BasicId;
|
||||
namespace Zotlabs\Identity;
|
||||
|
||||
class BasicId {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace Zotlabs\Identity\ProfilePhoto;
|
||||
namespace Zotlabs\Identity;
|
||||
|
||||
class ProfilePhoto {
|
||||
|
||||
|
||||
150
Zotlabs/Lib/ASCollection.php
Normal file
150
Zotlabs/Lib/ASCollection.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/**
|
||||
* Class for dealing with fetching ActivityStreams collections (ordered or unordered, normal or paged).
|
||||
* Construct with either an existing object or url and an optional channel to sign requests.
|
||||
* $direction is 0 (default) to fetch from the beginning, and 1 to fetch from the end and reverse order the resultant array.
|
||||
* An optional limit to the number of records returned may also be specified.
|
||||
* Use $class->get() to return an array of collection members.
|
||||
*/
|
||||
class ASCollection {
|
||||
|
||||
private $channel = null;
|
||||
private $nextpage = null;
|
||||
private $limit = 0;
|
||||
private $direction = 0; // 0 = forward, 1 = reverse
|
||||
private $data = [];
|
||||
private $history = [];
|
||||
|
||||
function __construct($obj, $channel = null, $direction = 0, $limit = 0) {
|
||||
|
||||
$this->channel = $channel;
|
||||
$this->direction = $direction;
|
||||
$this->limit = $limit;
|
||||
|
||||
if (is_array($obj)) {
|
||||
$data = $obj;
|
||||
}
|
||||
|
||||
if (is_string($obj)) {
|
||||
$data = Activity::fetch($obj, $channel);
|
||||
$this->history[] = $obj;
|
||||
}
|
||||
|
||||
if (!is_array($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!in_array($data['type'], ['Collection', 'OrderedCollection', 'OrderedCollectionPage'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->direction) {
|
||||
if (array_key_exists('last', $data) && $data['last']) {
|
||||
$this->nextpage = $data['last'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (array_key_exists('first', $data) && $data['first']) {
|
||||
$this->nextpage = $data['first'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['items']) && is_array($data['items'])) {
|
||||
$this->data = (($this->direction) ? array_reverse($data['items']) : $data['items']);
|
||||
}
|
||||
elseif (isset($data['orderedItems']) && is_array($data['orderedItems'])) {
|
||||
$this->data = (($this->direction) ? array_reverse($data['orderedItems']) : $data['orderedItems']);
|
||||
}
|
||||
|
||||
if ($this->limit) {
|
||||
if (count($this->data) > $limit) {
|
||||
$this->data = array_slice($this->data, 0, $limit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
$x = $this->next();
|
||||
} while ($x);
|
||||
}
|
||||
|
||||
function get() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
function next() {
|
||||
|
||||
if (!$this->nextpage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_array($this->nextpage)) {
|
||||
$data = $this->nextpage;
|
||||
}
|
||||
|
||||
if (is_string($this->nextpage)) {
|
||||
if (in_array($this->nextpage, $this->history)) {
|
||||
// recursion detected
|
||||
return false;
|
||||
}
|
||||
$data = Activity::fetch($this->nextpage, $this->channel);
|
||||
$this->history[] = $this->nextpage;
|
||||
}
|
||||
|
||||
if (!is_array($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!in_array($data['type'], ['CollectionPage', 'OrderedCollectionPage'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->setnext($data);
|
||||
|
||||
if (isset($data['items']) && is_array($data['items'])) {
|
||||
$this->data = array_merge($this->data, (($this->direction) ? array_reverse($data['items']) : $data['items']));
|
||||
}
|
||||
elseif (isset($data['orderedItems']) && is_array($data['orderedItems'])) {
|
||||
$this->data = array_merge($this->data, (($this->direction) ? array_reverse($data['orderedItems']) : $data['orderedItems']));
|
||||
}
|
||||
|
||||
if ($this->limit) {
|
||||
if (count($this->data) > $this->limit) {
|
||||
$this->data = array_slice($this->data, 0, $this->limit);
|
||||
$this->nextpage = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function setnext($data) {
|
||||
if ($this->direction) {
|
||||
if (array_key_exists('prev', $data) && $data['prev']) {
|
||||
$this->nextpage = $data['prev'];
|
||||
}
|
||||
elseif (array_key_exists('first', $data) && $data['first']) {
|
||||
$this->nextpage = $data['first'];
|
||||
}
|
||||
else {
|
||||
$this->nextpage = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (array_key_exists('next', $data) && $data['next']) {
|
||||
$this->nextpage = $data['next'];
|
||||
}
|
||||
elseif (array_key_exists('last', $data) && $data['last']) {
|
||||
$this->nextpage = $data['last'];
|
||||
}
|
||||
else {
|
||||
$this->nextpage = false;
|
||||
}
|
||||
}
|
||||
logger('nextpage: ' . $this->nextpage, LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
411
Zotlabs/Lib/AccessList.php
Normal file
411
Zotlabs/Lib/AccessList.php
Normal file
@@ -0,0 +1,411 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
|
||||
class AccessList {
|
||||
|
||||
static function add($uid,$name,$public = 0) {
|
||||
|
||||
$ret = false;
|
||||
if ($uid && $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 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.
|
||||
|
||||
$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 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);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
$hash = new_uuid();
|
||||
|
||||
$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 ($uid && $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'];
|
||||
}
|
||||
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)
|
||||
);
|
||||
if ($r) {
|
||||
$user_info = array_shift($r);
|
||||
$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;
|
||||
}
|
||||
|
||||
// returns the integer id of an access group owned by $uid and named $name
|
||||
// or false.
|
||||
|
||||
static function byname($uid,$name) {
|
||||
if (! ($uid && $name)) {
|
||||
return false;
|
||||
}
|
||||
$r = q("SELECT id FROM pgrp WHERE uid = %d AND gname = '%s' LIMIT 1",
|
||||
intval($uid),
|
||||
dbesc($name)
|
||||
);
|
||||
if ($r) {
|
||||
return $r[0]['id'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
if ($r) {
|
||||
return array_shift($r);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static function rec_byhash($uid,$hash) {
|
||||
if (! ( $uid && $hash)) {
|
||||
return false;
|
||||
}
|
||||
$r = q("SELECT * FROM pgrp WHERE uid = %d AND hash = '%s' LIMIT 1",
|
||||
intval($uid),
|
||||
dbesc($hash)
|
||||
);
|
||||
if ($r) {
|
||||
return array_shift($r);
|
||||
}
|
||||
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
|
||||
}
|
||||
else {
|
||||
$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($uid, $gid) {
|
||||
$ret = [];
|
||||
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($uid),
|
||||
intval($uid)
|
||||
);
|
||||
if ($r) {
|
||||
$ret = $r;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static function members_xchan($uid,$gid) {
|
||||
$ret = [];
|
||||
if (intval($gid)) {
|
||||
$r = q("SELECT xchan FROM pgrp_member WHERE gid = %d AND uid = %d",
|
||||
intval($gid),
|
||||
intval($uid)
|
||||
);
|
||||
if ($r) {
|
||||
foreach ($r as $rv) {
|
||||
$ret[] = $rv['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 $rv) {
|
||||
$ret[] = $rv['xchan'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static function select($uid,$group = '') {
|
||||
|
||||
$grps = [];
|
||||
|
||||
$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),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
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'),
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static function expand($g) {
|
||||
if (! (is_array($g) && count($g))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$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 $rv) {
|
||||
$ret[] = $rv['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 = [];
|
||||
if ($r) {
|
||||
foreach ($r as $rv)
|
||||
$ret[] = $rv['gid'];
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,25 +7,24 @@ namespace Zotlabs\Lib;
|
||||
*
|
||||
* Parses an ActivityStream JSON string.
|
||||
*/
|
||||
|
||||
class ActivityStreams {
|
||||
|
||||
public $raw = null;
|
||||
public $data = null;
|
||||
public $valid = false;
|
||||
public $deleted = false;
|
||||
public $id = '';
|
||||
public $parent_id = '';
|
||||
public $type = '';
|
||||
public $actor = null;
|
||||
public $obj = null;
|
||||
public $tgt = null;
|
||||
public $origin = null;
|
||||
public $owner = null;
|
||||
public $signer = null;
|
||||
public $ldsig = null;
|
||||
public $sigok = false;
|
||||
public $recips = null;
|
||||
public $raw = null;
|
||||
public $data = null;
|
||||
public $valid = false;
|
||||
public $deleted = false;
|
||||
public $id = '';
|
||||
public $parent_id = '';
|
||||
public $type = '';
|
||||
public $actor = null;
|
||||
public $obj = null;
|
||||
public $tgt = null;
|
||||
public $origin = null;
|
||||
public $owner = null;
|
||||
public $signer = null;
|
||||
public $ldsig = null;
|
||||
public $sigok = false;
|
||||
public $recips = null;
|
||||
public $raw_recips = null;
|
||||
|
||||
/**
|
||||
@@ -37,29 +36,29 @@ class ActivityStreams {
|
||||
*/
|
||||
function __construct($string) {
|
||||
|
||||
$this->raw = $string;
|
||||
$this->raw = $string;
|
||||
|
||||
if(is_array($string)) {
|
||||
if (is_array($string)) {
|
||||
$this->data = $string;
|
||||
}
|
||||
else {
|
||||
$this->data = json_decode($string, true);
|
||||
}
|
||||
|
||||
if($this->data) {
|
||||
if ($this->data) {
|
||||
|
||||
// verify and unpack JSalmon signature if present
|
||||
|
||||
if(is_array($this->data) && array_key_exists('signed',$this->data)) {
|
||||
|
||||
if (is_array($this->data) && array_key_exists('signed', $this->data)) {
|
||||
$ret = JSalmon::verify($this->data);
|
||||
$tmp = JSalmon::unpack($this->data['data']);
|
||||
if($ret && $ret['success']) {
|
||||
if($ret['signer']) {
|
||||
$saved = json_encode($this->data,JSON_UNESCAPED_SLASHES);
|
||||
$this->data = $tmp;
|
||||
$this->data['signer'] = $ret['signer'];
|
||||
if ($ret && $ret['success']) {
|
||||
if ($ret['signer']) {
|
||||
$saved = json_encode($this->data, JSON_UNESCAPED_SLASHES);
|
||||
$this->data = $tmp;
|
||||
$this->data['signer'] = $ret['signer'];
|
||||
$this->data['signed_data'] = $saved;
|
||||
if($ret['hubloc']) {
|
||||
if ($ret['hubloc']) {
|
||||
$this->data['hubloc'] = $ret['hubloc'];
|
||||
}
|
||||
}
|
||||
@@ -68,51 +67,57 @@ class ActivityStreams {
|
||||
|
||||
$this->valid = true;
|
||||
|
||||
if(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']) {
|
||||
if (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;
|
||||
$this->valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if($this->is_valid()) {
|
||||
if ($this->is_valid()) {
|
||||
$this->id = $this->get_property_obj('id');
|
||||
$this->type = $this->get_primary_type();
|
||||
$this->actor = $this->get_actor('actor','','');
|
||||
$this->actor = $this->get_actor('actor', '', '');
|
||||
$this->obj = $this->get_compound_property('object');
|
||||
$this->tgt = $this->get_compound_property('target');
|
||||
$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']);
|
||||
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']);
|
||||
}
|
||||
}
|
||||
|
||||
if(! $this->obj) {
|
||||
$this->obj = $this->data;
|
||||
if (!$this->obj) {
|
||||
$this->obj = $this->data;
|
||||
$this->type = 'Create';
|
||||
if(! $this->actor) {
|
||||
$this->actor = $this->get_actor('attributedTo',$this->obj);
|
||||
if (!$this->actor) {
|
||||
$this->actor = $this->get_actor('attributedTo', $this->obj);
|
||||
}
|
||||
}
|
||||
|
||||
if($this->obj && is_array($this->obj) && $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);
|
||||
|
||||
// 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']);
|
||||
}
|
||||
|
||||
if ($this->obj && is_array($this->obj) && $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)) {
|
||||
if ((!$this->parent_id) && is_array($this->obj)) {
|
||||
$this->parent_id = $this->obj['inReplyTo'];
|
||||
}
|
||||
if((! $this->parent_id) && is_array($this->obj)) {
|
||||
if ((!$this->parent_id) && is_array($this->obj)) {
|
||||
$this->parent_id = $this->obj['id'];
|
||||
}
|
||||
}
|
||||
@@ -140,15 +145,20 @@ class ActivityStreams {
|
||||
*/
|
||||
function collect_recips($base = '', $namespace = '') {
|
||||
$x = [];
|
||||
$fields = [ 'to', 'cc', 'bto', 'bcc', 'audience'];
|
||||
foreach($fields as $f) {
|
||||
$y = $this->get_compound_property($f, $base, $namespace);
|
||||
if($y) {
|
||||
$x = array_merge($x, $y);
|
||||
if(! is_array($this->raw_recips))
|
||||
$this->raw_recips = [];
|
||||
|
||||
$this->raw_recips[$f] = $x;
|
||||
$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);
|
||||
}
|
||||
}
|
||||
// not yet ready for prime time
|
||||
@@ -156,21 +166,21 @@ class ActivityStreams {
|
||||
return $x;
|
||||
}
|
||||
|
||||
function expand($arr,$base = '',$namespace = '') {
|
||||
function expand($arr, $base = '', $namespace = '') {
|
||||
$ret = [];
|
||||
|
||||
// right now use a hardwired recursion depth of 5
|
||||
|
||||
for($z = 0; $z < 5; $z ++) {
|
||||
if(is_array($arr) && $arr) {
|
||||
foreach($arr as $a) {
|
||||
if(is_array($a)) {
|
||||
for ($z = 0; $z < 5; $z++) {
|
||||
if (is_array($arr) && $arr) {
|
||||
foreach ($arr as $a) {
|
||||
if (is_array($a)) {
|
||||
$ret[] = $a;
|
||||
}
|
||||
else {
|
||||
$x = $this->get_compound_property($a,$base,$namespace);
|
||||
if($x) {
|
||||
$ret = array_merge($ret,$x);
|
||||
$x = $this->get_compound_property($a, $base, $namespace);
|
||||
if ($x) {
|
||||
$ret = array_merge($ret, $x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,33 +201,33 @@ class ActivityStreams {
|
||||
*/
|
||||
function get_namespace($base, $namespace) {
|
||||
|
||||
if(! $namespace)
|
||||
if (!$namespace)
|
||||
return '';
|
||||
|
||||
$key = null;
|
||||
|
||||
foreach( [ $this->data, $base ] as $b ) {
|
||||
if(! $b)
|
||||
foreach ([$this->data, $base] as $b) {
|
||||
if (!$b)
|
||||
continue;
|
||||
|
||||
if(array_key_exists('@context', $b)) {
|
||||
if(is_array($b['@context'])) {
|
||||
foreach($b['@context'] as $ns) {
|
||||
if(is_array($ns)) {
|
||||
foreach($ns as $k => $v) {
|
||||
if($namespace === $v)
|
||||
if (array_key_exists('@context', $b)) {
|
||||
if (is_array($b['@context'])) {
|
||||
foreach ($b['@context'] as $ns) {
|
||||
if (is_array($ns)) {
|
||||
foreach ($ns as $k => $v) {
|
||||
if ($namespace === $v)
|
||||
$key = $k;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if($namespace === $ns) {
|
||||
if ($namespace === $ns) {
|
||||
$key = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if($namespace === $b['@context']) {
|
||||
if ($namespace === $b['@context']) {
|
||||
$key = '';
|
||||
}
|
||||
}
|
||||
@@ -237,14 +247,14 @@ class ActivityStreams {
|
||||
*/
|
||||
function get_property_obj($property, $base = '', $namespace = '') {
|
||||
$prefix = $this->get_namespace($base, $namespace);
|
||||
if($prefix === null)
|
||||
if ($prefix === null)
|
||||
return null;
|
||||
|
||||
$base = (($base) ? $base : $this->data);
|
||||
$base = (($base) ? $base : $this->data);
|
||||
$propname = (($prefix) ? $prefix . ':' : '') . $property;
|
||||
|
||||
if(! is_array($base)) {
|
||||
btlogger('not an array: ' . print_r($base,true));
|
||||
if (!is_array($base)) {
|
||||
btlogger('not an array: ' . print_r($base, true));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -263,12 +273,19 @@ class ActivityStreams {
|
||||
return self::fetch($url);
|
||||
}
|
||||
|
||||
static function fetch($url,$channel = null) {
|
||||
return Activity::fetch($url,$channel);
|
||||
static function fetch($url, $channel = null) {
|
||||
return Activity::fetch($url, $channel);
|
||||
}
|
||||
|
||||
static function is_an_actor($s) {
|
||||
return(in_array($s,[ 'Application','Group','Organization','Person','Service' ]));
|
||||
return (in_array($s, ['Application', 'Group', 'Organization', 'Person', 'Service']));
|
||||
}
|
||||
|
||||
static function is_response_activity($s) {
|
||||
if (!$s) {
|
||||
return false;
|
||||
}
|
||||
return (in_array($s, ['Like', 'Dislike', 'Flag', 'Block', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact']));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,25 +297,25 @@ class ActivityStreams {
|
||||
* @return NULL|mixed
|
||||
*/
|
||||
|
||||
function get_actor($property,$base='',$namespace = '') {
|
||||
function get_actor($property, $base = '', $namespace = '') {
|
||||
$x = $this->get_property_obj($property, $base, $namespace);
|
||||
if($this->is_url($x)) {
|
||||
if ($this->is_url($x)) {
|
||||
|
||||
// SECURITY: If we have already stored the actor profile, re-generate it
|
||||
// SECURITY: If we have already stored the actor profile, re-generate it
|
||||
// from cached data - don't refetch it from the network
|
||||
|
||||
$r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' limit 1",
|
||||
dbesc($x)
|
||||
);
|
||||
if($r) {
|
||||
$y = Activity::encode_person($r[0]);
|
||||
if ($r) {
|
||||
$y = Activity::encode_person($r[0]);
|
||||
$y['cached'] = true;
|
||||
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->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;
|
||||
@@ -318,28 +335,31 @@ class ActivityStreams {
|
||||
*/
|
||||
function get_compound_property($property, $base = '', $namespace = '', $first = false) {
|
||||
$x = $this->get_property_obj($property, $base, $namespace);
|
||||
if($this->is_url($x)) {
|
||||
$x = $this->fetch_property($x);
|
||||
if ($this->is_url($x)) {
|
||||
$y = $this->fetch_property($x);
|
||||
if (is_array($y)) {
|
||||
$x = $y;
|
||||
}
|
||||
}
|
||||
|
||||
// verify and unpack JSalmon signature if present
|
||||
|
||||
if(is_array($x) && array_key_exists('signed',$x)) {
|
||||
|
||||
if (is_array($x) && array_key_exists('signed', $x)) {
|
||||
$ret = JSalmon::verify($x);
|
||||
$tmp = JSalmon::unpack($x['data']);
|
||||
if($ret && $ret['success']) {
|
||||
if($ret['signer']) {
|
||||
$saved = json_encode($x,JSON_UNESCAPED_SLASHES);
|
||||
$x = $tmp;
|
||||
$x['signer'] = $ret['signer'];
|
||||
if ($ret && $ret['success']) {
|
||||
if ($ret['signer']) {
|
||||
$saved = json_encode($x, JSON_UNESCAPED_SLASHES);
|
||||
$x = $tmp;
|
||||
$x['signer'] = $ret['signer'];
|
||||
$x['signed_data'] = $saved;
|
||||
if($ret['hubloc']) {
|
||||
if ($ret['hubloc']) {
|
||||
$x['hubloc'] = $ret['hubloc'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if($first && is_array($x) && array_key_exists(0,$x)) {
|
||||
if ($first && is_array($x) && array_key_exists(0, $x)) {
|
||||
return $x[0];
|
||||
}
|
||||
|
||||
@@ -353,7 +373,7 @@ class ActivityStreams {
|
||||
* @return boolean
|
||||
*/
|
||||
function is_url($url) {
|
||||
if(($url) && (! is_array($url)) && (strpos($url, 'http') === 0)) {
|
||||
if (($url) && (!is_array($url)) && (strpos($url, 'http') === 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -368,13 +388,13 @@ class ActivityStreams {
|
||||
* @return NULL|mixed
|
||||
*/
|
||||
function get_primary_type($base = '', $namespace = '') {
|
||||
if(! $base)
|
||||
if (!$base)
|
||||
$base = $this->data;
|
||||
|
||||
$x = $this->get_property_obj('type', $base, $namespace);
|
||||
if(is_array($x)) {
|
||||
foreach($x as $y) {
|
||||
if(strpos($y, ':') === false) {
|
||||
if (is_array($x)) {
|
||||
foreach ($x as $y) {
|
||||
if (strpos($y, ':') === false) {
|
||||
return $y;
|
||||
}
|
||||
}
|
||||
@@ -388,18 +408,33 @@ class ActivityStreams {
|
||||
return $x;
|
||||
}
|
||||
|
||||
static function is_as_request($channel = null) {
|
||||
|
||||
static function is_as_request() {
|
||||
$hookdata = [];
|
||||
if ($channel)
|
||||
$hookdata['channel'] = $channel;
|
||||
|
||||
$x = getBestSupportedMimeType([
|
||||
'application/ld+json;profile="https://www.w3.org/ns/activitystreams"',
|
||||
'application/activity+json',
|
||||
'application/ld+json;profile="http://www.w3.org/ns/activitystreams"'
|
||||
]);
|
||||
$hookdata['data'] = ['application/x-zot-activity+json'];
|
||||
|
||||
return(($x) ? true : false);
|
||||
call_hooks('is_as_request', $hookdata);
|
||||
|
||||
$x = getBestSupportedMimeType($hookdata['data']);
|
||||
return (($x) ? true : false);
|
||||
|
||||
}
|
||||
|
||||
static function get_accept_header_string($channel = null) {
|
||||
|
||||
}
|
||||
$hookdata = [];
|
||||
if ($channel)
|
||||
$hookdata['channel'] = $channel;
|
||||
|
||||
$hookdata['data'] = 'application/x-zot-activity+json';
|
||||
|
||||
call_hooks('get_accept_header_string', $hookdata);
|
||||
|
||||
return $hookdata['data'];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
require_once('include/plugin.php');
|
||||
require_once('include/channel.php');
|
||||
|
||||
@@ -70,11 +72,10 @@ class Apps {
|
||||
'Channel Home',
|
||||
'View Profile',
|
||||
'Photos',
|
||||
'Events',
|
||||
'Calendar',
|
||||
'Directory',
|
||||
'Search',
|
||||
'Help',
|
||||
'Mail',
|
||||
'Profile Photo'
|
||||
]);
|
||||
|
||||
@@ -306,7 +307,7 @@ class Apps {
|
||||
}
|
||||
}
|
||||
}
|
||||
if($ret) {
|
||||
if(isset($ret)) {
|
||||
if($translate)
|
||||
self::translate_system_apps($ret);
|
||||
|
||||
@@ -328,6 +329,7 @@ class Apps {
|
||||
'Bookmarks' => t('Bookmarks'),
|
||||
'Chatrooms' => t('Chatrooms'),
|
||||
'Content Filter' => t('Content Filter'),
|
||||
'Content Import' => t('Content Import'),
|
||||
'Connections' => t('Connections'),
|
||||
'Remote Diagnostics' => t('Remote Diagnostics'),
|
||||
'Suggest Channels' => t('Suggest Channels'),
|
||||
@@ -341,7 +343,7 @@ class Apps {
|
||||
'Channel Home' => t('Channel Home'),
|
||||
'View Profile' => t('View Profile'),
|
||||
'Photos' => t('Photos'),
|
||||
'Events' => t('Events'),
|
||||
'Calendar' => t('Calendar'),
|
||||
'Directory' => t('Directory'),
|
||||
'Help' => t('Help'),
|
||||
'Mail' => t('Mail'),
|
||||
@@ -362,7 +364,6 @@ class Apps {
|
||||
'Privacy Groups' => t('Privacy Groups'),
|
||||
'Notifications' => t('Notifications'),
|
||||
'Order Apps' => t('Order Apps'),
|
||||
'CalDAV' => t('CalDAV'),
|
||||
'CardDAV' => t('CardDAV'),
|
||||
'Channel Sources' => t('Channel Sources'),
|
||||
'Guest Access' => t('Guest Access'),
|
||||
@@ -371,7 +372,6 @@ class Apps {
|
||||
'OAuth2 Apps Manager' => t('OAuth2 Apps Manager'),
|
||||
'PDL Editor' => t('PDL Editor'),
|
||||
'Permission Categories' => t('Permission Categories'),
|
||||
'Premium Channel' => t('Premium Channel'),
|
||||
'Public Stream' => t('Public Stream'),
|
||||
'My Chatrooms' => t('My Chatrooms'),
|
||||
'Channel Export' => t('Channel Export')
|
||||
@@ -524,7 +524,7 @@ class Apps {
|
||||
}
|
||||
elseif(remote_channel()) {
|
||||
$observer = \App::get_observer();
|
||||
if($observer && $observer['xchan_network'] === 'zot') {
|
||||
if($observer && in_array($observer['xchan_network'], ['zot6', 'zot'])) {
|
||||
// some folks might have xchan_url redirected offsite, use the connurl
|
||||
$x = parse_url($observer['xchan_connurl']);
|
||||
if($x) {
|
||||
@@ -551,7 +551,7 @@ class Apps {
|
||||
'$app' => $papp,
|
||||
'$icon' => $icon,
|
||||
'$hosturl' => $hosturl,
|
||||
'$purchase' => (($papp['page'] && (! $installed)) ? t('Purchase') : ''),
|
||||
'$purchase' => ((isset($papp['page']) && (! $installed)) ? t('Purchase') : ''),
|
||||
'$installed' => $installed,
|
||||
'$action_label' => (($hosturl && in_array($mode, ['view','install'])) ? $install_action : ''),
|
||||
'$edit' => ((local_channel() && $installed && $mode == 'edit') ? t('Edit') : ''),
|
||||
@@ -559,12 +559,13 @@ class Apps {
|
||||
'$undelete' => ((local_channel() && $mode == 'edit') ? t('Undelete') : ''),
|
||||
'$settings_url' => ((local_channel() && $installed && $mode == 'list') ? $papp['settings_url'] : ''),
|
||||
'$deleted' => $papp['deleted'],
|
||||
'$feature' => (($papp['embed'] || $mode == 'edit') ? false : true),
|
||||
'$pin' => (($papp['embed'] || $mode == 'edit') ? false : true),
|
||||
'$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),
|
||||
'$navapps' => (($mode == 'nav') ? true : false),
|
||||
'$order' => (($mode == 'nav-order') ? true : false),
|
||||
'$order' => (($mode === 'nav-order' || $mode === 'nav-order-pinned') ? true : false),
|
||||
'$mode' => $mode,
|
||||
'$add' => t('Add to app-tray'),
|
||||
'$remove' => t('Remove from app-tray'),
|
||||
'$add_nav' => t('Pin to navbar'),
|
||||
@@ -604,7 +605,7 @@ class Apps {
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($r[0]['id'])
|
||||
);
|
||||
build_sync_packet($uid,array('app' => $r[0]));
|
||||
Libsync::build_sync_packet($uid,array('app' => $r[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -670,7 +671,7 @@ class Apps {
|
||||
);
|
||||
}
|
||||
if(! intval($x[0]['app_system'])) {
|
||||
build_sync_packet($uid,array('app' => $x));
|
||||
Libsync::build_sync_packet($uid,array('app' => $x));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -959,9 +960,6 @@ class Apps {
|
||||
if($list) {
|
||||
foreach($list as $li) {
|
||||
$papp = self::app_encode($li);
|
||||
if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false)
|
||||
continue;
|
||||
|
||||
$syslist[] = $papp;
|
||||
}
|
||||
}
|
||||
@@ -1278,58 +1276,58 @@ class Apps {
|
||||
|
||||
$ret['type'] = 'personal';
|
||||
|
||||
if($app['app_id'])
|
||||
if(!empty($app['app_id']))
|
||||
$ret['guid'] = $app['app_id'];
|
||||
|
||||
if($app['app_sig'])
|
||||
if(!empty($app['app_sig']))
|
||||
$ret['sig'] = $app['app_sig'];
|
||||
|
||||
if($app['app_author'])
|
||||
if(!empty($app['app_author']))
|
||||
$ret['author'] = $app['app_author'];
|
||||
|
||||
if($app['app_name'])
|
||||
if(!empty($app['app_name']))
|
||||
$ret['name'] = $app['app_name'];
|
||||
|
||||
if($app['app_desc'])
|
||||
if(!empty($app['app_desc']))
|
||||
$ret['desc'] = $app['app_desc'];
|
||||
|
||||
if($app['app_url'])
|
||||
if(!empty($app['app_url']))
|
||||
$ret['url'] = $app['app_url'];
|
||||
|
||||
if($app['app_photo'])
|
||||
if(!empty($app['app_photo']))
|
||||
$ret['photo'] = $app['app_photo'];
|
||||
|
||||
if($app['app_icon'])
|
||||
if(!empty($app['app_icon']))
|
||||
$ret['icon'] = $app['app_icon'];
|
||||
|
||||
if($app['app_version'])
|
||||
if(!empty($app['app_version']))
|
||||
$ret['version'] = $app['app_version'];
|
||||
|
||||
if($app['app_addr'])
|
||||
if(!empty($app['app_addr']))
|
||||
$ret['addr'] = $app['app_addr'];
|
||||
|
||||
if($app['app_price'])
|
||||
if(!empty($app['app_price']))
|
||||
$ret['price'] = $app['app_price'];
|
||||
|
||||
if($app['app_page'])
|
||||
if(!empty($app['app_page']))
|
||||
$ret['page'] = $app['app_page'];
|
||||
|
||||
if($app['app_requires'])
|
||||
if(!empty($app['app_requires']))
|
||||
$ret['requires'] = $app['app_requires'];
|
||||
|
||||
if($app['app_system'])
|
||||
if(!empty($app['app_system']))
|
||||
$ret['system'] = $app['app_system'];
|
||||
|
||||
if($app['app_options'])
|
||||
if(!empty($app['app_options']))
|
||||
$ret['options'] = $app['app_options'];
|
||||
|
||||
if($app['app_plugin'])
|
||||
if(!empty($app['app_plugin']))
|
||||
$ret['plugin'] = trim($app['app_plugin']);
|
||||
|
||||
if($app['app_deleted'])
|
||||
if(!empty($app['app_deleted']))
|
||||
$ret['deleted'] = $app['app_deleted'];
|
||||
|
||||
if($app['term']) {
|
||||
if(!empty($app['term']) && is_array($app['term'])) {
|
||||
$s = '';
|
||||
foreach($app['term'] as $t) {
|
||||
if($s)
|
||||
|
||||
@@ -7,12 +7,23 @@ namespace Zotlabs\Lib;
|
||||
*/
|
||||
|
||||
class Cache {
|
||||
public static function get($key) {
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
$r = q("SELECT v FROM cache WHERE k = '%s' limit 1",
|
||||
dbesc($hash)
|
||||
$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)
|
||||
@@ -40,12 +51,4 @@ class Cache {
|
||||
dbesc(datetime_convert()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function clear() {
|
||||
q("DELETE FROM cache WHERE updated < '%s'",
|
||||
dbesc(datetime_convert('UTC','UTC',"now - 30 days")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
/**
|
||||
* @brief A class with chatroom related static methods.
|
||||
*/
|
||||
@@ -91,7 +93,7 @@ class Chatroom {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
build_sync_packet($channel['channel_id'],array('chatroom' => $r));
|
||||
Libsync::build_sync_packet($channel['channel_id'],array('chatroom' => $r));
|
||||
|
||||
q("delete from chatroom where cr_id = %d",
|
||||
intval($r[0]['cr_id'])
|
||||
|
||||
312
Zotlabs/Lib/Connect.php
Normal file
312
Zotlabs/Lib/Connect.php
Normal file
@@ -0,0 +1,312 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Access\Permissions;
|
||||
use Zotlabs\Daemon\Master;
|
||||
|
||||
|
||||
|
||||
class Connect {
|
||||
|
||||
/**
|
||||
* Takes a $channel and a $url/handle and adds a new connection
|
||||
*
|
||||
* Returns array
|
||||
* $return['success'] boolean true if successful
|
||||
* $return['abook'] Address book entry joined with xchan if successful
|
||||
* $return['message'] error text if success is false.
|
||||
*
|
||||
* This function does NOT send sync packets to clones. The caller is responsible for doing this
|
||||
*/
|
||||
|
||||
static function connect($channel, $url, $sub_channel = false) {
|
||||
|
||||
$uid = $channel['channel_id'];
|
||||
|
||||
if (strpos($url,'@') === false && strpos($url,'/') === false) {
|
||||
$url = $url . '@' . App::get_hostname();
|
||||
}
|
||||
|
||||
$result = [ 'success' => false, 'message' => '' ];
|
||||
|
||||
$my_perms = false;
|
||||
$protocol = '';
|
||||
|
||||
if (substr($url,0,1) === '[') {
|
||||
$x = strpos($url,']');
|
||||
if ($x) {
|
||||
$protocol = substr($url,1,$x-1);
|
||||
$url = substr($url,$x+1);
|
||||
}
|
||||
}
|
||||
|
||||
if (! check_siteallowed($url)) {
|
||||
$result['message'] = t('Channel is blocked on this site.');
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (! $url) {
|
||||
$result['message'] = t('Channel location missing.');
|
||||
return $result;
|
||||
}
|
||||
|
||||
// check service class limits
|
||||
|
||||
$r = q("select count(*) as total from abook where abook_channel = %d and abook_self = 0 ",
|
||||
intval($uid)
|
||||
);
|
||||
if ($r) {
|
||||
$total_channels = $r[0]['total'];
|
||||
}
|
||||
|
||||
if (! service_class_allows($uid,'total_channels',$total_channels)) {
|
||||
$result['message'] = upgrade_message();
|
||||
return $result;
|
||||
}
|
||||
|
||||
$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 ",
|
||||
dbesc($url),
|
||||
dbesc($url),
|
||||
dbesc($url)
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
|
||||
// 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');
|
||||
|
||||
}
|
||||
|
||||
$singleton = false;
|
||||
$d = false;
|
||||
|
||||
if (! $r) {
|
||||
|
||||
// not in cache - try discovery
|
||||
|
||||
$wf = discover_by_webbie($url,$protocol);
|
||||
|
||||
if (! $wf) {
|
||||
$feeds = get_config('system','feed_contacts');
|
||||
|
||||
if (($feeds) && (in_array($protocol, [ '', 'feed', 'rss' ]))) {
|
||||
$d = discover_by_url($url);
|
||||
}
|
||||
else {
|
||||
$result['message'] = t('Remote channel or protocol unavailable.');
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($wf || $d) {
|
||||
|
||||
// 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($url),
|
||||
dbesc($url)
|
||||
);
|
||||
|
||||
// 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');
|
||||
}
|
||||
}
|
||||
|
||||
// if discovery was a success or the channel was already cached we should have an xchan record in $r
|
||||
|
||||
if ($r) {
|
||||
$xchan = $r;
|
||||
$xchan_hash = $r['xchan_hash'];
|
||||
$their_perms = EMPTY_STR;
|
||||
}
|
||||
|
||||
// failure case
|
||||
|
||||
if (! $xchan_hash) {
|
||||
$result['message'] = t('Channel discovery failed.');
|
||||
logger('follow: ' . $result['message']);
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (! check_channelallowed($xchan_hash)) {
|
||||
$result['message'] = t('Channel is blocked on this site.');
|
||||
logger('follow: ' . $result['message']);
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
$allowed = ((in_array($xchan['xchan_network'],['rss','zot','zot6'])) ? 1 : 0);
|
||||
|
||||
$hookdata = ['channel_id' => $uid, 'follow_address' => $url, 'xchan' => $xchan, 'allowed' => $allowed, 'singleton' => 0];
|
||||
call_hooks('follow_allow',$hookdata);
|
||||
|
||||
if(! $hookdata['allowed']) {
|
||||
$result['message'] = t('Protocol disabled.');
|
||||
return $result;
|
||||
}
|
||||
|
||||
$singleton = intval($hookdata['singleton']);
|
||||
|
||||
// Now start processing the new connection
|
||||
|
||||
$aid = $channel['channel_account_id'];
|
||||
$default_group = $channel['channel_default_group'];
|
||||
|
||||
if (in_array($xchan_hash, [$channel['channel_hash'], $channel['channel_portable_id']])) {
|
||||
$result['message'] = t('Cannot connect to yourself.');
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($xchan['xchan_network'] === 'rss') {
|
||||
|
||||
// check service class feed limits
|
||||
|
||||
$t = q("select count(*) as total from abook where abook_account = %d and abook_feed = 1 ",
|
||||
intval($aid)
|
||||
);
|
||||
if ($t) {
|
||||
$total_feeds = $t[0]['total'];
|
||||
}
|
||||
|
||||
if (! service_class_allows($uid,'total_feeds',$total_feeds)) {
|
||||
$result['message'] = upgrade_message();
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Always set these "remote" permissions for feeds since we cannot interact with them
|
||||
// to negotiate a suitable permission response
|
||||
|
||||
set_abconfig($uid,$xchan_hash,'their_perms','view_stream',1);
|
||||
set_abconfig($uid,$xchan_hash,'their_perms','republish',1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
$p = Permissions::connect_perms($uid);
|
||||
|
||||
// parent channels have unencumbered write permission
|
||||
|
||||
if ($sub_channel) {
|
||||
$p['perms']['post_wall'] = 1;
|
||||
$p['perms']['post_comments'] = 1;
|
||||
$p['perms']['write_storage'] = 1;
|
||||
$p['perms']['post_like'] = 1;
|
||||
$p['perms']['delegate'] = 0;
|
||||
$p['perms']['moderated'] = 0;
|
||||
}
|
||||
|
||||
$my_perms = $p['perms'];
|
||||
|
||||
$profile_assign = get_pconfig($uid,'system','profile_assign','');
|
||||
|
||||
|
||||
// See if we are already connected by virtue of having an abook record
|
||||
|
||||
$r = q("select abook_id, abook_xchan, abook_pending, abook_instance from abook
|
||||
where abook_xchan = '%s' and abook_channel = %d limit 1",
|
||||
dbesc($xchan_hash),
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
|
||||
$abook_instance = $r[0]['abook_instance'];
|
||||
|
||||
// If they are on a non-nomadic network, add them to this location
|
||||
|
||||
if (($singleton) && strpos($abook_instance,z_root()) === false) {
|
||||
if ($abook_instance) {
|
||||
$abook_instance .= ',';
|
||||
}
|
||||
$abook_instance .= z_root();
|
||||
|
||||
$x = q("update abook set abook_instance = '%s', abook_not_here = 0 where abook_id = %d",
|
||||
dbesc($abook_instance),
|
||||
intval($r[0]['abook_id'])
|
||||
);
|
||||
}
|
||||
|
||||
// if they have a pending connection, we just followed them so approve the connection request
|
||||
|
||||
if (intval($r[0]['abook_pending'])) {
|
||||
$x = q("update abook set abook_pending = 0 where abook_id = %d",
|
||||
intval($r[0]['abook_id'])
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// create a new abook record
|
||||
|
||||
$closeness = get_pconfig($uid,'system','new_abook_closeness',80);
|
||||
|
||||
$r = abook_store_lowlevel(
|
||||
[
|
||||
'abook_account' => intval($aid),
|
||||
'abook_channel' => intval($uid),
|
||||
'abook_closeness' => intval($closeness),
|
||||
'abook_xchan' => $xchan_hash,
|
||||
'abook_profile' => $profile_assign,
|
||||
'abook_feed' => intval(($xchan['xchan_network'] === 'rss') ? 1 : 0),
|
||||
'abook_created' => datetime_convert(),
|
||||
'abook_updated' => datetime_convert(),
|
||||
'abook_instance' => (($singleton) ? z_root() : '')
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (! $r) {
|
||||
logger('abook creation failed');
|
||||
$result['message'] = t('error saving data');
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Set suitable permissions to the connection
|
||||
|
||||
if($my_perms) {
|
||||
foreach($my_perms as $k => $v) {
|
||||
set_abconfig($uid,$xchan_hash,'my_perms',$k,$v);
|
||||
}
|
||||
}
|
||||
|
||||
// fetch the entire record
|
||||
|
||||
$r = q("select abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash
|
||||
where abook_xchan = '%s' and abook_channel = %d limit 1",
|
||||
dbesc($xchan_hash),
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
$result['abook'] = array_shift($r);
|
||||
Master::Summon([ 'Notifier', 'permission_create', $result['abook']['abook_id'] ]);
|
||||
}
|
||||
|
||||
$arr = [ 'channel_id' => $uid, 'channel' => $channel, 'abook' => $result['abook'] ];
|
||||
|
||||
call_hooks('follow', $arr);
|
||||
|
||||
/** If there is a default group for this channel, add this connection to it */
|
||||
|
||||
if ($default_group) {
|
||||
$g = AccessList::rec_byhash($uid,$default_group);
|
||||
if ($g) {
|
||||
AccessList::member_add($uid,'',$xchan_hash,$g['id']);
|
||||
}
|
||||
}
|
||||
|
||||
$result['success'] = true;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
206
Zotlabs/Lib/Crypto.php
Normal file
206
Zotlabs/Lib/Crypto.php
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Crypto {
|
||||
|
||||
public static $openssl_algorithms = [
|
||||
|
||||
// zot6 nickname, opensslname, keylength, ivlength
|
||||
|
||||
['aes256ctr', 'aes-256-ctr', 32, 16],
|
||||
['camellia256cfb', 'camellia-256-cfb', 32, 16],
|
||||
['cast5cfb', 'cast5-cfb', 16, 8],
|
||||
['aes256cbc', 'aes-256-cbc', 32, 16] // remove after legacy zot has been sunset
|
||||
|
||||
];
|
||||
|
||||
public static function methods() {
|
||||
$ret = [];
|
||||
|
||||
foreach (self::$openssl_algorithms as $ossl) {
|
||||
$ret[] = $ossl[0] . '.oaep';
|
||||
}
|
||||
|
||||
call_hooks('crypto_methods', $ret);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function signing_methods() {
|
||||
|
||||
$ret = ['sha256'];
|
||||
call_hooks('signing_methods', $ret);
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
public static function new_keypair($bits) {
|
||||
|
||||
$openssl_options = [
|
||||
'digest_alg' => 'sha1',
|
||||
'private_key_bits' => $bits,
|
||||
'encrypt_key' => false
|
||||
];
|
||||
|
||||
$conf = get_config('system', 'openssl_conf_file');
|
||||
|
||||
if ($conf) {
|
||||
$openssl_options['config'] = $conf;
|
||||
}
|
||||
|
||||
$result = openssl_pkey_new($openssl_options);
|
||||
|
||||
if (empty($result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get private key
|
||||
|
||||
$response = ['prvkey' => '', 'pubkey' => ''];
|
||||
|
||||
openssl_pkey_export($result, $response['prvkey']);
|
||||
|
||||
// Get public key
|
||||
$pkey = openssl_pkey_get_details($result);
|
||||
$response['pubkey'] = $pkey["key"];
|
||||
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
public static function sign($data, $key, $alg = 'sha256') {
|
||||
|
||||
if (!$key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sig = '';
|
||||
openssl_sign($data, $sig, $key, $alg);
|
||||
return $sig;
|
||||
}
|
||||
|
||||
public static function verify($data, $sig, $key, $alg = 'sha256') {
|
||||
|
||||
if (!$key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$verify = openssl_verify($data, $sig, $key, $alg);
|
||||
} catch (Exception $e) {
|
||||
$verify = (-1);
|
||||
}
|
||||
|
||||
if ($verify === (-1)) {
|
||||
while ($msg = openssl_error_string()) {
|
||||
logger('openssl_verify: ' . $msg, LOGGER_NORMAL, LOG_ERR);
|
||||
}
|
||||
btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR);
|
||||
}
|
||||
|
||||
return (($verify > 0) ? true : false);
|
||||
}
|
||||
|
||||
public static function encapsulate($data, $pubkey, $alg) {
|
||||
|
||||
if (!($alg && $pubkey)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$alg_base = $alg;
|
||||
$padding = OPENSSL_PKCS1_PADDING;
|
||||
|
||||
$exts = explode('.', $alg);
|
||||
if (count($exts) > 1) {
|
||||
switch ($exts[1]) {
|
||||
case 'oaep':
|
||||
$padding = OPENSSL_PKCS1_OAEP_PADDING;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
$alg_base = $exts[0];
|
||||
}
|
||||
|
||||
$method = null;
|
||||
|
||||
foreach (self::$openssl_algorithms as $ossl) {
|
||||
if ($ossl[0] === $alg_base) {
|
||||
$method = $ossl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($method) {
|
||||
$result = ['encrypted' => true];
|
||||
|
||||
$key = openssl_random_pseudo_bytes(256);
|
||||
$iv = openssl_random_pseudo_bytes(256);
|
||||
|
||||
$key1 = substr($key, 0, $method[2]);
|
||||
$iv1 = substr($iv, 0, $method[3]);
|
||||
|
||||
$result['data'] = base64url_encode(openssl_encrypt($data, $method[1], $key1, OPENSSL_RAW_DATA, $iv1), true);
|
||||
|
||||
openssl_public_encrypt($key, $k, $pubkey, $padding);
|
||||
openssl_public_encrypt($iv, $i, $pubkey, $padding);
|
||||
|
||||
$result['alg'] = $alg;
|
||||
$result['key'] = base64url_encode($k, true);
|
||||
$result['iv'] = base64url_encode($i, true);
|
||||
return $result;
|
||||
|
||||
}
|
||||
else {
|
||||
$x = ['data' => $data, 'pubkey' => $pubkey, 'alg' => $alg, 'result' => $data];
|
||||
call_hooks('crypto_encapsulate', $x);
|
||||
return $x['result'];
|
||||
}
|
||||
}
|
||||
|
||||
public static function unencapsulate($data, $prvkey) {
|
||||
|
||||
if (!(is_array($data) && array_key_exists('encrypted', $data) && array_key_exists('alg', $data) && $data['alg'])) {
|
||||
logger('not encrypted');
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
$alg_base = $data['alg'];
|
||||
$padding = OPENSSL_PKCS1_PADDING;
|
||||
|
||||
$exts = explode('.', $data['alg']);
|
||||
if (count($exts) > 1) {
|
||||
switch ($exts[1]) {
|
||||
case 'oaep':
|
||||
$padding = OPENSSL_PKCS1_OAEP_PADDING;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
$alg_base = $exts[0];
|
||||
}
|
||||
|
||||
$method = null;
|
||||
|
||||
foreach (self::$openssl_algorithms as $ossl) {
|
||||
if ($ossl[0] === $alg_base) {
|
||||
$method = $ossl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($method) {
|
||||
openssl_private_decrypt(base64url_decode($data['key']), $k, $prvkey, $padding);
|
||||
openssl_private_decrypt(base64url_decode($data['iv']), $i, $prvkey, $padding);
|
||||
return openssl_decrypt(base64url_decode($data['data']), $method[1], substr($k, 0, $method[2]), OPENSSL_RAW_DATA, substr($i, 0, $method[3]));
|
||||
}
|
||||
else {
|
||||
$x = ['data' => $data, 'prvkey' => $prvkey, 'alg' => $data['alg'], 'result' => $data];
|
||||
call_hooks('crypto_unencapsulate', $x);
|
||||
return $x['result'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,10 +58,15 @@ class DB_Upgrade {
|
||||
|
||||
|
||||
$c = new $cls();
|
||||
|
||||
$retval = $c->run();
|
||||
|
||||
if($retval != UPDATE_SUCCESS) {
|
||||
|
||||
|
||||
$source = t('Source code of failed update: ') . "\n\n" . @file_get_contents('Zotlabs/Update/' . $s . '.php');
|
||||
|
||||
|
||||
// Prevent sending hundreds of thousands of emails by creating
|
||||
// a lockfile.
|
||||
|
||||
@@ -86,7 +91,9 @@ class DB_Upgrade {
|
||||
'$sitename' => \App::$config['system']['sitename'],
|
||||
'$siteurl' => z_root(),
|
||||
'$update' => $x,
|
||||
'$error' => sprintf( t('Update %s failed. See error logs.'), $x)
|
||||
'$error' => sprintf( t('Update %s failed. See error logs.'), $x),
|
||||
'$baseurl' => z_root(),
|
||||
'$source' => $source
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -118,7 +118,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'] === $rxchan) && ($dr['status'] === 'recipient not found'))
|
||||
return false;
|
||||
|
||||
// If you have a private post with a recipient list, every single site is going to report
|
||||
|
||||
@@ -43,7 +43,7 @@ class Enotify {
|
||||
dbesc($params['to_xchan'])
|
||||
);
|
||||
}
|
||||
if ($x & $y) {
|
||||
if ($x && $y) {
|
||||
$sender = $x[0];
|
||||
$recip = $y[0];
|
||||
} else {
|
||||
@@ -64,7 +64,7 @@ class Enotify {
|
||||
$sitename = get_config('system','sitename');
|
||||
$site_admin = sprintf( t('%s Administrator'), $sitename);
|
||||
$opt_out1 = sprintf( t('This email was sent by %1$s at %2$s.'), t('$Projectname'), \App::get_hostname());
|
||||
$opt_out2 = sprintf( t('To stop receiving these messages, please adjust your Notification Settings at %s'), z_root() . '/settings');
|
||||
$opt_out2 = sprintf( t('To stop receiving these messages, please adjust your Notification Settings at %s'), z_root() . '/settings');
|
||||
$hopt_out2 = sprintf( t('To stop receiving these messages, please adjust your %s.'), '<a href="' . z_root() . '/settings' . '">' . t('Notification Settings') . '</a>');
|
||||
$sender_name = $product;
|
||||
$hostname = \App::get_hostname();
|
||||
@@ -80,7 +80,7 @@ class Enotify {
|
||||
$sender_email = get_config('system','from_email');
|
||||
if(! $sender_email)
|
||||
$sender_email = 'Administrator' . '@' . $hostname;
|
||||
|
||||
|
||||
$sender_name = get_config('system','from_email_name');
|
||||
if(! $sender_name)
|
||||
$sender_name = \Zotlabs\Lib\System::get_site_name();
|
||||
@@ -108,7 +108,7 @@ class Enotify {
|
||||
logger('notification invoked for an old item which may have been refetched.',LOGGER_DEBUG,LOG_INFO);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$title = $body = '';
|
||||
}
|
||||
@@ -143,19 +143,26 @@ class Enotify {
|
||||
|
||||
$action = t('commented on');
|
||||
|
||||
if(array_key_exists('item',$params) && in_array($params['item']['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
|
||||
if(array_key_exists('item',$params)) {
|
||||
|
||||
if(in_array($params['item']['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
|
||||
|
||||
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) {
|
||||
logger('notification: not a visible activity. Ignoring.');
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
if(activity_match($params['verb'], ACTIVITY_LIKE))
|
||||
$action = t('liked');
|
||||
|
||||
if(activity_match($params['verb'], ACTIVITY_DISLIKE))
|
||||
$action = t('disliked');
|
||||
|
||||
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) {
|
||||
logger('notification: not a visible activity. Ignoring.');
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
if(activity_match($params['verb'], ACTIVITY_LIKE))
|
||||
$action = t('liked');
|
||||
|
||||
if(activity_match($params['verb'], ACTIVITY_DISLIKE))
|
||||
$action = t('disliked');
|
||||
if($params['item']['obj_type'] === 'Answer')
|
||||
$action = t('voted on');
|
||||
|
||||
}
|
||||
|
||||
@@ -174,7 +181,7 @@ class Enotify {
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// if it's a post figure out who's post it is.
|
||||
|
||||
@@ -212,7 +219,7 @@ class Enotify {
|
||||
$itemlink,
|
||||
$p[0]['author']['xchan_name'],
|
||||
$item_post_type);
|
||||
|
||||
|
||||
// "your post"
|
||||
if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall']))
|
||||
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]your %4$s[/zrl]'),
|
||||
@@ -223,15 +230,15 @@ class Enotify {
|
||||
|
||||
// Some mail softwares relies on subject field for threading.
|
||||
// So, we cannot have different subjects for notifications of the same thread.
|
||||
// Before this we have the name of the replier on the subject rendering
|
||||
// Before this we have the name of the replier on the subject rendering
|
||||
// differents subjects for messages on the same thread.
|
||||
|
||||
if($moderated)
|
||||
$subject = sprintf( t('[$Projectname:Notify] Moderated Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
|
||||
else
|
||||
$subject = sprintf( t('[$Projectname:Notify] Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
|
||||
$preamble = sprintf( t('%1$s commented on an item/conversation you have been following.'), $sender['xchan_name']);
|
||||
$epreamble = $dest_str;
|
||||
$preamble = sprintf( t('%1$s commented on an item/conversation you have been following.'), $sender['xchan_name']);
|
||||
$epreamble = $dest_str;
|
||||
|
||||
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
@@ -240,7 +247,7 @@ class Enotify {
|
||||
$tsitelink .= "\n\n" . sprintf( t('Please visit %s to approve or reject this comment.'), z_root() . '/moderate' );
|
||||
$hsitelink .= "<br><br>" . sprintf( t('Please visit %s to approve or reject this comment.'), '<a href="' . z_root() . '/moderate">' . z_root() . '/moderate</a>' );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if ($params['type'] == NOTIFY_LIKE) {
|
||||
@@ -271,7 +278,7 @@ class Enotify {
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// if it's a post figure out who's post it is.
|
||||
|
||||
@@ -307,12 +314,12 @@ class Enotify {
|
||||
|
||||
// Some mail softwares relies on subject field for threading.
|
||||
// So, we cannot have different subjects for notifications of the same thread.
|
||||
// Before this we have the name of the replier on the subject rendering
|
||||
// Before this we have the name of the replier on the subject rendering
|
||||
// differents subjects for messages on the same thread.
|
||||
|
||||
$subject = sprintf( t('[$Projectname:Notify] Like received to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
|
||||
$preamble = sprintf( t('%1$s liked an item/conversation you created.'), $sender['xchan_name']);
|
||||
$epreamble = $dest_str;
|
||||
$preamble = sprintf( t('%1$s liked an item/conversation you created.'), $sender['xchan_name']);
|
||||
$epreamble = $dest_str;
|
||||
|
||||
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
@@ -328,7 +335,7 @@ class Enotify {
|
||||
|
||||
$epreamble = sprintf( t('%1$s posted to [zrl=%2$s]your wall[/zrl]') ,
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$params['link']);
|
||||
$params['link']);
|
||||
|
||||
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
@@ -348,12 +355,12 @@ class Enotify {
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$subject = sprintf( t('[$Projectname:Notify] %s tagged you') , $sender['xchan_name']);
|
||||
$preamble = sprintf( t('%1$s tagged you at %2$s') , $sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('%1$s [zrl=%2$s]tagged you[/zrl].') ,
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$params['link']);
|
||||
$params['link']);
|
||||
|
||||
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
@@ -366,7 +373,7 @@ class Enotify {
|
||||
$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].') ,
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$params['link']);
|
||||
$params['link']);
|
||||
|
||||
$subject = str_replace('poked', t($params['activity']), $subject);
|
||||
$preamble = str_replace('poked', t($params['activity']), $preamble);
|
||||
@@ -383,7 +390,7 @@ class Enotify {
|
||||
$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]') ,
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$itemlink);
|
||||
$itemlink);
|
||||
|
||||
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
@@ -393,10 +400,10 @@ class Enotify {
|
||||
|
||||
if ($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);
|
||||
$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.'),
|
||||
$siteurl . '/connections/ifpending',
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
|
||||
$body = sprintf( t('You may visit their profile at %s'),$sender['xchan_url']);
|
||||
|
||||
$sitelink = t('Please visit %s to approve or reject the connection request.');
|
||||
@@ -407,11 +414,11 @@ class Enotify {
|
||||
|
||||
if ($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);
|
||||
$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.'),
|
||||
$itemlink,
|
||||
'[zrl=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/zrl]',
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
|
||||
|
||||
$body = t('Name:') . ' ' . $params['item']['name'] . "\n";
|
||||
$body .= t('Photo:') . ' ' . $params['item']['photo'] . "\n";
|
||||
@@ -455,7 +462,7 @@ class Enotify {
|
||||
$sitelink = $h['sitelink'];
|
||||
$tsitelink = $h['tsitelink'];
|
||||
$hsitelink = $h['hsitelink'];
|
||||
$itemlink = $h['itemlink'];
|
||||
$itemlink = $h['itemlink'];
|
||||
|
||||
|
||||
require_once('include/html2bbcode.php');
|
||||
@@ -503,7 +510,7 @@ class Enotify {
|
||||
// Mark some notifications as seen right away
|
||||
// Note! The notification have to be created, because they are used to send emails
|
||||
// So easiest solution to hide them from Notices is to mark them as seen right away.
|
||||
// Another option would be to not add them to the DB, and change how emails are handled
|
||||
// Another option would be to not add them to the DB, and change how emails are handled
|
||||
// (probably would be better that way)
|
||||
|
||||
if (!$always_show_in_notices) {
|
||||
@@ -548,7 +555,12 @@ class Enotify {
|
||||
// wretched hack, but we don't want to duplicate all the preamble variations and we also don't want to screw up a translation
|
||||
|
||||
if ((\App::$language === 'en' || (! \App::$language)) && strpos($msg,', '))
|
||||
$msg = substr($msg,strpos($msg,', ')+1);
|
||||
$msg = substr($msg,strpos($msg,', ')+1);
|
||||
|
||||
$datarray['id'] = $notify_id;
|
||||
$datarray['msg'] = $msg;
|
||||
|
||||
call_hooks('enotify_store_end', $datarray);
|
||||
|
||||
$r = q("update notify set msg = '%s' where id = %d and uid = %d",
|
||||
dbesc($msg),
|
||||
@@ -575,12 +587,12 @@ class Enotify {
|
||||
$htmlversion = bbcode(stripslashes(str_replace(array("\\r","\\n"), array("","<br />\n"),$body)));
|
||||
|
||||
|
||||
// use $_SESSION['zid_override'] to force zid() to use
|
||||
// use $_SESSION['zid_override'] to force zid() to use
|
||||
// the recipient address instead of the current observer
|
||||
|
||||
$_SESSION['zid_override'] = channel_reddress($recip);
|
||||
$_SESSION['zrl_override'] = z_root() . '/channel/' . $recip['channel_address'];
|
||||
|
||||
|
||||
$textversion = zidify_links($textversion);
|
||||
$htmlversion = zidify_links($htmlversion);
|
||||
|
||||
@@ -742,7 +754,7 @@ class Enotify {
|
||||
return $params['result'];
|
||||
}
|
||||
|
||||
$fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8');
|
||||
$fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8');
|
||||
$messageSubject = email_header_encode(html_entity_decode($params['messageSubject'],ENT_QUOTES,'UTF-8'),'UTF-8');
|
||||
|
||||
// generate a mime boundary
|
||||
@@ -754,9 +766,9 @@ class Enotify {
|
||||
// generate a multipart/alternative message header
|
||||
$messageHeader =
|
||||
$params['additionalMailHeader'] .
|
||||
"From: $fromName <{$params['fromEmail']}>\n" .
|
||||
"Reply-To: $fromName <{$params['replyTo']}>\n" .
|
||||
"MIME-Version: 1.0\n" .
|
||||
"From: $fromName <{$params['fromEmail']}>" . PHP_EOL .
|
||||
"Reply-To: $fromName <{$params['replyTo']}>" . PHP_EOL .
|
||||
"MIME-Version: 1.0" . PHP_EOL .
|
||||
"Content-Type: multipart/alternative; boundary=\"{$mimeBoundary}\"";
|
||||
|
||||
// assemble the final multipart message body with the text and html types included
|
||||
@@ -764,15 +776,15 @@ class Enotify {
|
||||
$htmlBody = chunk_split(base64_encode($params['htmlVersion']));
|
||||
|
||||
$multipartMessageBody =
|
||||
"--" . $mimeBoundary . "\n" . // plain text section
|
||||
"Content-Type: text/plain; charset=UTF-8\n" .
|
||||
"Content-Transfer-Encoding: base64\n\n" .
|
||||
$textBody . "\n" .
|
||||
"--" . $mimeBoundary . "\n" . // text/html section
|
||||
"Content-Type: text/html; charset=UTF-8\n" .
|
||||
"Content-Transfer-Encoding: base64\n\n" .
|
||||
$htmlBody . "\n" .
|
||||
"--" . $mimeBoundary . "--\n"; // message ending
|
||||
"--" . $mimeBoundary . PHP_EOL . // plain text section
|
||||
"Content-Type: text/plain; charset=UTF-8" . PHP_EOL .
|
||||
"Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL .
|
||||
$textBody . PHP_EOL .
|
||||
"--" . $mimeBoundary . PHP_EOL . // text/html section
|
||||
"Content-Type: text/html; charset=UTF-8" . PHP_EOL .
|
||||
"Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL .
|
||||
$htmlBody . PHP_EOL .
|
||||
"--" . $mimeBoundary . "--" . PHP_EOL; // message ending
|
||||
|
||||
// send the message
|
||||
$res = mail(
|
||||
@@ -791,7 +803,7 @@ class Enotify {
|
||||
|
||||
require_once('include/conversation.php');
|
||||
|
||||
// Call localize_item to get a one line status for activities.
|
||||
// Call localize_item to get a one line status for activities.
|
||||
// This should set $item['localized'] to indicate we have a brief summary.
|
||||
// and perhaps $item['shortlocalized'] for an even briefer summary
|
||||
|
||||
@@ -805,8 +817,17 @@ class Enotify {
|
||||
}
|
||||
else {
|
||||
$itemem_text = (($item['item_thread_top'])
|
||||
? t('created a new post')
|
||||
: sprintf( t('commented on %s\'s post'), $item['owner']['xchan_name']));
|
||||
? (($item['obj_type'] === 'Question') ? t('created a new poll') : t('created a new post'))
|
||||
: (($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');
|
||||
}
|
||||
}
|
||||
|
||||
$edit = false;
|
||||
@@ -825,21 +846,24 @@ class Enotify {
|
||||
|
||||
// convert this logic into a json array just like the system notifications
|
||||
|
||||
$who = (($item['verb'] === ACTIVITY_SHARE) ? 'owner' : 'author');
|
||||
|
||||
$x = array(
|
||||
'notify_link' => $item['llink'],
|
||||
'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' => relative_date(($edit)? $item['edited'] : $item['created']),
|
||||
'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'],
|
||||
'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' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? 'b64.' . base64url_encode($item['thr_parent']) : 'b64.' . base64url_encode($item['mid'])),
|
||||
'notify_id' => 'undefined',
|
||||
'b64mid' => (($item['mid']) ? 'b64.' . base64url_encode($item['mid']) : ''),
|
||||
//'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? 'b64.' . base64url_encode($item['thr_parent']) : 'b64.' . base64url_encode($item['mid'])),
|
||||
'thread_top' => (($item['item_thread_top']) ? true : false),
|
||||
'message' => strip_tags(bbcode($itemem_text)),
|
||||
'message' => bbcode(escape_tags($itemem_text)),
|
||||
'body' => htmlentities(html2plain(bbcode($item['body']), 75, true), ENT_QUOTES, 'UTF-8', false),
|
||||
// these are for the superblock addon
|
||||
'hash' => $item['author']['xchan_hash'],
|
||||
'uid' => local_channel(),
|
||||
'hash' => $item[$who]['xchan_hash'],
|
||||
'uid' => $item['uid'],
|
||||
'display' => true
|
||||
);
|
||||
|
||||
@@ -851,4 +875,118 @@ class Enotify {
|
||||
return $x;
|
||||
}
|
||||
|
||||
static public function format_notify($tt) {
|
||||
|
||||
$message = trim(strip_tags(bbcode($tt['msg'])));
|
||||
|
||||
if(strpos($message, $tt['xname']) === 0)
|
||||
$message = substr($message, strlen($tt['xname']) + 1);
|
||||
|
||||
$mid = basename($tt['link']);
|
||||
|
||||
$b64mid = ((strpos($mid, 'b64.') === 0) ? $mid : 'b64.' . base64url_encode($mid));
|
||||
$x = [
|
||||
'notify_link' => z_root() . '/notify/view/' . $tt['id'],
|
||||
'name' => $tt['xname'],
|
||||
'url' => $tt['url'],
|
||||
'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 : ''),
|
||||
'notify_id' => (($tt['otype'] == 'item') ? $tt['id'] : ''),
|
||||
'message' => $message
|
||||
];
|
||||
|
||||
return $x;
|
||||
|
||||
}
|
||||
|
||||
static public function format_intros($rr) {
|
||||
|
||||
$x = [
|
||||
'notify_link' => z_root() . '/connections/ifpending',
|
||||
'name' => $rr['xchan_name'],
|
||||
'addr' => $rr['xchan_addr'],
|
||||
'url' => $rr['xchan_url'],
|
||||
'photo' => $rr['xchan_photo_s'],
|
||||
'when' => datetime_convert('UTC', date_default_timezone_get(), $rr['abook_created']),
|
||||
'hclass' => ('notify-unseen'),
|
||||
'message' => t('added your channel')
|
||||
];
|
||||
|
||||
return $x;
|
||||
|
||||
}
|
||||
|
||||
static public function format_files($rr) {
|
||||
|
||||
$x = [
|
||||
'notify_link' => z_root() . '/sharedwithme',
|
||||
'name' => $rr['author']['xchan_name'],
|
||||
'addr' => $rr['author']['xchan_addr'],
|
||||
'url' => $rr['author']['xchan_url'],
|
||||
'photo' => $rr['author']['xchan_photo_s'],
|
||||
'when' => datetime_convert('UTC', date_default_timezone_get(), $rr['created']),
|
||||
'hclass' => ('notify-unseen'),
|
||||
'message' => t('shared a file with you')
|
||||
];
|
||||
|
||||
return $x;
|
||||
|
||||
}
|
||||
|
||||
static public function format_mail($rr) {
|
||||
|
||||
$x = [
|
||||
'notify_link' => z_root() . '/mail/' . $rr['id'],
|
||||
'name' => $rr['xchan_name'],
|
||||
'addr' => $rr['xchan_addr'],
|
||||
'url' => $rr['xchan_url'],
|
||||
'photo' => $rr['xchan_photo_s'],
|
||||
'when' => datetime_convert('UTC', date_default_timezone_get(), $rr['created']),
|
||||
'hclass' => (intval($rr['mail_seen']) ? 'notify-seen' : 'notify-unseen'),
|
||||
'message' => t('sent you a private message'),
|
||||
];
|
||||
|
||||
return $x;
|
||||
|
||||
}
|
||||
|
||||
static public function format_all_events($rr) {
|
||||
|
||||
$bd_format = t('g A l F d') ; // 8 AM Friday January 18
|
||||
$strt = datetime_convert('UTC', (($rr['adjust']) ? date_default_timezone_get() : 'UTC'), $rr['dtstart']);
|
||||
$today = ((substr($strt, 0, 10) === datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d')) ? true : false);
|
||||
$when = day_translate(datetime_convert('UTC', (($rr['adjust']) ? date_default_timezone_get() : 'UTC'), $rr['dtstart'], $bd_format)) . (($today) ? ' ' . t('[today]') : '');
|
||||
|
||||
$x = [
|
||||
'notify_link' => z_root() . '/cdav/calendar/' . $rr['event_hash'],
|
||||
'name' => $rr['xchan_name'],
|
||||
'addr' => $rr['xchan_addr'],
|
||||
'url' => $rr['xchan_url'],
|
||||
'photo' => $rr['xchan_photo_s'],
|
||||
'when' => $when,
|
||||
'hclass' => (($today) ? 'notify-unseen bg-warning' : 'notify-unseen'),
|
||||
'message' => t('created an event')
|
||||
];
|
||||
|
||||
return $x;
|
||||
|
||||
}
|
||||
|
||||
static public function format_register($rr) {
|
||||
|
||||
$x = [
|
||||
'notify_link' => z_root() . '/admin/accounts',
|
||||
'name' => $rr['reg_did2'],
|
||||
//'addr' => '',
|
||||
'photo' => z_root() . '/' . get_default_profile_photo(48),
|
||||
'when' => datetime_convert('UTC', date_default_timezone_get(),$rr['reg_created']),
|
||||
'hclass' => ('notify-unseen'),
|
||||
'message' => t('status verified')
|
||||
];
|
||||
|
||||
return $x;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
55
Zotlabs/Lib/Hashpath.php
Normal file
55
Zotlabs/Lib/Hashpath.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/*
|
||||
* Zotlabs\Lib\Hashpath
|
||||
*
|
||||
* Creates hashed directory structures for fast access and resistance to overloading any single directory with files.
|
||||
*
|
||||
* Takes a $hash which could be any string
|
||||
* a $prefix which is where to place the hash directory in the filesystem, default is current directory
|
||||
* use an empty string for $prefix to place hash directories directly off the root directory
|
||||
* an optional $depth and $slice (default is 2) to indicate the hash level
|
||||
* $depth = 1, 256 directories, suitable for < 384K records/files
|
||||
* $depth = 2, 65536 directories, suitable for < 98M records/files
|
||||
* $depth = 3, 16777216 directories, suitable for < 2.5B records/files
|
||||
* ...
|
||||
* The total number of records anticipated divided by the number of hash directories should generally be kept to
|
||||
* less than 1500 entries for optimum performance though this varies by operating system and filesystem type.
|
||||
* ext4 uses 32 bit inode numbers (~4B record limit) so use caution or alternative filesystem types with $depth above 3.
|
||||
* an optional $mkdir (boolean) to recursively create the directory (ignoring errors) before returning
|
||||
*
|
||||
* examples: for a $hash of 'abcdefg' and prefix of 'path' the following paths are returned for $depth = 1 and $depth = 3
|
||||
* path/7d/7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a
|
||||
* path/7d/1a/54/7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a
|
||||
*
|
||||
* see also: boot.php:os_mkdir() - here we provide the equivalent of mkdir -p with permissions of 770.
|
||||
*
|
||||
*/
|
||||
|
||||
class Hashpath {
|
||||
|
||||
static function path($hash, $prefix = '.', $depth = 1, $slice = 2, $mkdir = true, $alg = false) {
|
||||
|
||||
if ($alg)
|
||||
$hash = hash($alg, $hash);
|
||||
|
||||
$start = 0;
|
||||
if ($depth < 1)
|
||||
$depth = 1;
|
||||
$sluglen = $depth * $slice;
|
||||
|
||||
do {
|
||||
$slug = substr($hash, $start, $slice);
|
||||
$prefix .= '/' . $slug;
|
||||
$start += $slice;
|
||||
$sluglen -= $slice;
|
||||
}
|
||||
while ($sluglen);
|
||||
|
||||
if ($mkdir)
|
||||
os_mkdir($prefix, STORAGE_DEFAULT_PERMISSIONS, true);
|
||||
|
||||
return $prefix . '/' . $hash;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Zot6\HTTPSig;
|
||||
use Zotlabs\Web\HTTPSig;
|
||||
|
||||
class JSalmon {
|
||||
|
||||
@@ -18,7 +18,7 @@ class JSalmon {
|
||||
|
||||
$precomputed = '.' . base64url_encode($data_type,true) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng';
|
||||
|
||||
$signature = base64url_encode(rsa_sign($data . $precomputed, $key), true);
|
||||
$signature = base64url_encode(Crypto::sign($data . $precomputed, $key), true);
|
||||
|
||||
return ([
|
||||
'signed' => true,
|
||||
@@ -40,21 +40,21 @@ class JSalmon {
|
||||
$ret = [ 'results' => [] ];
|
||||
|
||||
if(! is_array($x)) {
|
||||
return $false;
|
||||
return false;
|
||||
}
|
||||
if(! ( array_key_exists('signed',$x) && $x['signed'])) {
|
||||
return $false;
|
||||
return false;
|
||||
}
|
||||
|
||||
$signed_data = preg_replace('/\s+/','',$x['data']) . '.'
|
||||
. base64url_encode($x['data_type'],true) . '.'
|
||||
. base64url_encode($x['encoding'],true) . '.'
|
||||
$signed_data = preg_replace('/\s+/','',$x['data']) . '.'
|
||||
. base64url_encode($x['data_type'],true) . '.'
|
||||
. base64url_encode($x['encoding'],true) . '.'
|
||||
. base64url_encode($x['alg'],true);
|
||||
|
||||
$key = HTTPSig::get_key(EMPTY_STR,base64url_decode($x['sigs']['key_id']));
|
||||
$key = HTTPSig::get_key(EMPTY_STR,'zot6',base64url_decode($x['sigs']['key_id']));
|
||||
logger('key: ' . print_r($key,true));
|
||||
if($key['portable_id'] && $key['public_key']) {
|
||||
if(rsa_verify($signed_data,base64url_decode($x['sigs']['value']),$key['public_key'])) {
|
||||
if(Crypto::verify($signed_data,base64url_decode($x['sigs']['value']),$key['public_key'])) {
|
||||
logger('verified');
|
||||
$ret = [ 'success' => true, 'signer' => $key['portable_id'], 'hubloc' => $key['hubloc'] ];
|
||||
}
|
||||
@@ -69,4 +69,4 @@ class JSalmon {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
99
Zotlabs/Lib/Keyutils.php
Normal file
99
Zotlabs/Lib/Keyutils.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use phpseclib\Crypt\RSA;
|
||||
use phpseclib\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* Keyutils
|
||||
* Convert RSA keys between various formats
|
||||
*/
|
||||
class Keyutils {
|
||||
|
||||
/**
|
||||
* @param string $m modulo
|
||||
* @param string $e exponent
|
||||
* @return string
|
||||
*/
|
||||
public static function meToPem($m, $e) {
|
||||
|
||||
$rsa = new RSA();
|
||||
$rsa->loadKey([
|
||||
'e' => new BigInteger($e, 256),
|
||||
'n' => new BigInteger($m, 256)
|
||||
]);
|
||||
return $rsa->getPublicKey();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string key
|
||||
* @return string
|
||||
*/
|
||||
public static function rsaToPem($key) {
|
||||
|
||||
$rsa = new RSA();
|
||||
$rsa->setPublicKey($key);
|
||||
|
||||
return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS8);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string key
|
||||
* @return string
|
||||
*/
|
||||
public static function pemToRsa($key) {
|
||||
|
||||
$rsa = new RSA();
|
||||
$rsa->setPublicKey($key);
|
||||
|
||||
return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key key
|
||||
* @param string $m reference modulo
|
||||
* @param string $e reference exponent
|
||||
*/
|
||||
public static function pemToMe($key, &$m, &$e) {
|
||||
|
||||
$rsa = new RSA();
|
||||
$rsa->loadKey($key);
|
||||
$rsa->setPublicKey();
|
||||
|
||||
$m = $rsa->modulus->toBytes();
|
||||
$e = $rsa->exponent->toBytes();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $pubkey
|
||||
* @return string
|
||||
*/
|
||||
public static function salmonKey($pubkey) {
|
||||
self::pemToMe($pubkey, $m, $e);
|
||||
return 'RSA' . '.' . base64url_encode($m, true) . '.' . base64url_encode($e, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public static function convertSalmonKey($key) {
|
||||
if (strstr($key, ','))
|
||||
$rawkey = substr($key, strpos($key, ',') + 1);
|
||||
else
|
||||
$rawkey = substr($key, 5);
|
||||
|
||||
$key_info = explode('.', $rawkey);
|
||||
|
||||
$m = base64url_decode($key_info[1]);
|
||||
$e = base64url_decode($key_info[2]);
|
||||
|
||||
return self::meToPem($m, $e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@ class LDSignatures {
|
||||
$ohash = self::hash(self::signable_options($data['signature']));
|
||||
$dhash = self::hash(self::signable_data($data));
|
||||
|
||||
$x = rsa_verify($ohash . $dhash,base64_decode($data['signature']['signatureValue']), $pubkey);
|
||||
$x = Crypto::verify($ohash . $dhash,base64_decode($data['signature']['signatureValue']), $pubkey);
|
||||
logger('LD-verify: ' . intval($x));
|
||||
|
||||
return $x;
|
||||
@@ -29,17 +29,17 @@ class LDSignatures {
|
||||
$options = [
|
||||
'type' => 'RsaSignature2017',
|
||||
'nonce' => random_string(64),
|
||||
'creator' => z_root() . '/channel/' . $channel['channel_address'] . '/public_key_pem',
|
||||
'created' => datetime_convert('UTC','UTC', 'now', 'Y-m-d\Th:i:s\Z')
|
||||
'creator' => z_root() . '/channel/' . $channel['channel_address'],
|
||||
'created' => datetime_convert('UTC','UTC', 'now', 'Y-m-d\TH:i:s\Z')
|
||||
];
|
||||
|
||||
$ohash = self::hash(self::signable_options($options));
|
||||
$dhash = self::hash(self::signable_data($data));
|
||||
$options['signatureValue'] = base64_encode(rsa_sign($ohash . $dhash,$channel['channel_prvkey']));
|
||||
$options['signatureValue'] = base64_encode(Crypto::sign($ohash . $dhash,$channel['channel_prvkey']));
|
||||
|
||||
$signed = array_merge([
|
||||
'@context' => [
|
||||
ACTIVITYSTREAMS_JSONLD_REV,
|
||||
'@context' => [
|
||||
ACTIVITYSTREAMS_JSONLD_REV,
|
||||
'https://w3id.org/security/v1' ],
|
||||
],$options);
|
||||
|
||||
@@ -88,12 +88,13 @@ class LDSignatures {
|
||||
return '';
|
||||
|
||||
jsonld_set_document_loader('jsonld_document_loader');
|
||||
|
||||
|
||||
try {
|
||||
$d = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
logger('normalise error:' . print_r($e,true));
|
||||
// Don't log the exception - this can exhaust memory
|
||||
// logger('normalise error:' . print_r($e,true));
|
||||
logger('normalise error: ' . print_r($data,true));
|
||||
}
|
||||
|
||||
@@ -116,7 +117,7 @@ class LDSignatures {
|
||||
|
||||
$precomputed = '.' . base64url_encode($data_type,false) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng==';
|
||||
|
||||
$signature = base64url_encode(rsa_sign($data . $precomputed,$channel['channel_prvkey']));
|
||||
$signature = base64url_encode(Crypto::sign($data . $precomputed,$channel['channel_prvkey']));
|
||||
|
||||
return ([
|
||||
'id' => $arr['id'],
|
||||
@@ -124,7 +125,7 @@ class LDSignatures {
|
||||
'meDataType' => $data_type,
|
||||
'meEncoding' => $encoding,
|
||||
'meAlgorithm' => $algorithm,
|
||||
'meCreator' => z_root() . '/channel/' . $channel['channel_address'] . '/public_key_pem',
|
||||
'meCreator' => z_root() . '/channel/' . $channel['channel_address'],
|
||||
'meSignatureValue' => $signature
|
||||
]);
|
||||
|
||||
@@ -132,4 +133,4 @@ class LDSignatures {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Zotfinger;
|
||||
use Zotlabs\Lib\Webfinger;
|
||||
|
||||
require_once('include/permissions.php');
|
||||
|
||||
@@ -17,7 +19,6 @@ class Libzotdir {
|
||||
*/
|
||||
|
||||
static function find_upstream_directory($dirmode) {
|
||||
global $DIRECTORY_FALLBACK_SERVERS;
|
||||
|
||||
$preferred = get_config('system','directory_server');
|
||||
|
||||
@@ -29,7 +30,7 @@ class Libzotdir {
|
||||
);
|
||||
if(($r) && ($r[0]['site_flags'] & DIRECTORY_MODE_STANDALONE)) {
|
||||
$preferred = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,19 +41,21 @@ class Libzotdir {
|
||||
* from our list of directory servers. However, if we're a directory
|
||||
* server ourself, point at the local instance
|
||||
* We will then set this value so this should only ever happen once.
|
||||
* Ideally there will be an admin setting to change to a different
|
||||
* Ideally there will be an admin setting to change to a different
|
||||
* directory server if you don't like our choice or if circumstances change.
|
||||
*/
|
||||
|
||||
$directory_fallback_servers = get_directory_fallback_servers();
|
||||
|
||||
$dirmode = intval(get_config('system','directory_mode'));
|
||||
if ($dirmode == DIRECTORY_MODE_NORMAL) {
|
||||
$toss = mt_rand(0,count($DIRECTORY_FALLBACK_SERVERS));
|
||||
$preferred = $DIRECTORY_FALLBACK_SERVERS[$toss];
|
||||
$toss = mt_rand(0,count($directory_fallback_servers));
|
||||
$preferred = $directory_fallback_servers[$toss];
|
||||
if(! $preferred) {
|
||||
$preferred = DIRECTORY_FALLBACK_MASTER;
|
||||
}
|
||||
set_config('system','directory_server',$preferred);
|
||||
}
|
||||
}
|
||||
else {
|
||||
set_config('system','directory_server',z_root());
|
||||
}
|
||||
@@ -106,7 +109,7 @@ class Libzotdir {
|
||||
$ret = get_config('directory', $setting);
|
||||
|
||||
|
||||
// 'safemode' is the default if there is no observer or no established preference.
|
||||
// 'safemode' is the default if there is no observer or no established preference.
|
||||
|
||||
if($setting === 'safemode' && $ret === false)
|
||||
$ret = 1;
|
||||
@@ -173,8 +176,8 @@ class Libzotdir {
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* @param int $dirmode;
|
||||
@@ -186,16 +189,17 @@ class Libzotdir {
|
||||
return;
|
||||
|
||||
$realm = get_directory_realm();
|
||||
|
||||
if ($realm == DIRECTORY_REALM) {
|
||||
$r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_type = %d and ( site_realm = '%s' or site_realm = '') ",
|
||||
$r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_type = %d and ( site_realm = '%s' or site_realm = '') and site_dead = 0",
|
||||
intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY),
|
||||
dbesc(z_root()),
|
||||
intval(SITE_TYPE_ZOT),
|
||||
dbesc($realm)
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_realm like '%s' and site_type = %d ",
|
||||
$r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_realm like '%s' and site_type = %d and site_dead = 0",
|
||||
intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY),
|
||||
dbesc(z_root()),
|
||||
dbesc(protect_sprintf('%' . $realm . '%')),
|
||||
@@ -203,6 +207,8 @@ class Libzotdir {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// If there are no directory servers, setup the fallback master
|
||||
/** @FIXME What to do if we're in a different realm? */
|
||||
|
||||
@@ -212,14 +218,14 @@ class Libzotdir {
|
||||
[
|
||||
'site_url' => DIRECTORY_FALLBACK_MASTER,
|
||||
'site_flags' => DIRECTORY_MODE_PRIMARY,
|
||||
'site_update' => NULL_DATE,
|
||||
'site_update' => NULL_DATE,
|
||||
'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch',
|
||||
'site_realm' => DIRECTORY_REALM,
|
||||
'site_valid' => 1,
|
||||
]
|
||||
);
|
||||
|
||||
$r = q("select * from site where site_flags in (%d, %d) and site_url != '%s' and site_type = %d ",
|
||||
$r = q("select * from site where site_flags in (%d, %d) and site_url != '%s' and site_type = %d and site_dead = 0",
|
||||
intval(DIRECTORY_MODE_PRIMARY),
|
||||
intval(DIRECTORY_MODE_SECONDARY),
|
||||
dbesc(z_root()),
|
||||
@@ -243,7 +249,6 @@ class Libzotdir {
|
||||
|
||||
$syncdate = (($rr['site_sync'] <= NULL_DATE) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
|
||||
$x = z_fetch_url($rr['site_directory'] . '?f=&sync=' . urlencode($syncdate) . (($token) ? '&t=' . $token : ''));
|
||||
|
||||
if (! $x['success'])
|
||||
continue;
|
||||
|
||||
@@ -271,7 +276,7 @@ class Libzotdir {
|
||||
$ud_flags |= UPDATE_FLAGS_DELETED;
|
||||
if (is_array($t['flags']) && in_array('forced',$t['flags']))
|
||||
$ud_flags |= UPDATE_FLAGS_FORCED;
|
||||
|
||||
|
||||
$z = q("insert into updates ( ud_hash, ud_guid, ud_date, ud_flags, ud_addr )
|
||||
values ( '%s', '%s', '%s', %d, '%s' ) ",
|
||||
dbesc($t['hash']),
|
||||
@@ -306,12 +311,13 @@ class Libzotdir {
|
||||
|
||||
if ($ud['ud_addr'] && (! ($ud['ud_flags'] & UPDATE_FLAGS_DELETED))) {
|
||||
$success = false;
|
||||
$zf = [];
|
||||
|
||||
$href = \Zotlabs\Lib\Webfinger::zot_url(punify($url));
|
||||
$href = Webfinger::zot_url(punify($ud['ud_addr']));
|
||||
if($href) {
|
||||
$zf = \Zotlabs\Lib\Zotfinger::exec($href);
|
||||
$zf = Zotfinger::exec($href);
|
||||
}
|
||||
if(is_array($zf) && array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) {
|
||||
if(array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) {
|
||||
$xc = Libzot::import_xchan($zf['data'], 0, $ud);
|
||||
}
|
||||
else {
|
||||
@@ -336,10 +342,10 @@ class Libzotdir {
|
||||
|
||||
static function local_dir_update($uid, $force) {
|
||||
|
||||
|
||||
|
||||
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, channel_portable_id, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1",
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
@@ -348,10 +354,11 @@ class Libzotdir {
|
||||
|
||||
if ($p) {
|
||||
$hash = $p[0]['channel_hash'];
|
||||
$legacy_hash = $p[0]['channel_portable_id'];
|
||||
|
||||
$profile['description'] = $p[0]['pdesc'];
|
||||
$profile['birthday'] = $p[0]['dob'];
|
||||
if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],''))
|
||||
if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],''))
|
||||
$profile['age'] = $age;
|
||||
|
||||
$profile['gender'] = $p[0]['gender'];
|
||||
@@ -381,14 +388,15 @@ class Libzotdir {
|
||||
|
||||
logger('hidden: ' . $hidden);
|
||||
|
||||
$r = q("select xchan_hidden from xchan where xchan_hash = '%s' limit 1",
|
||||
$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'",
|
||||
$r = q("update xchan set xchan_hidden = %d where xchan_hash in ('%s', '%s')",
|
||||
intval($hidden),
|
||||
dbesc($p[0]['channel_hash'])
|
||||
dbesc($hash),
|
||||
dbesc($legacy_hash)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -402,14 +410,16 @@ class Libzotdir {
|
||||
}
|
||||
else {
|
||||
// they may have made it private
|
||||
$r = q("delete from xprof where xprof_hash = '%s'",
|
||||
dbesc($hash)
|
||||
q("delete from xprof where xprof_hash in ('%s', '%s')",
|
||||
dbesc($hash),
|
||||
dbesc($legacy_hash)
|
||||
);
|
||||
$r = q("delete from xtag where xtag_hash = '%s'",
|
||||
dbesc($hash)
|
||||
q("delete from xtag where xtag_hash in ('%s', '%s')",
|
||||
dbesc($hash),
|
||||
dbesc($legacy_hash)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
$ud_hash = random_string() . '@' . \App::get_hostname();
|
||||
@@ -440,7 +450,7 @@ class Libzotdir {
|
||||
$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_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) : '');
|
||||
@@ -635,7 +645,7 @@ class Libzotdir {
|
||||
dbesc(datetime_convert()),
|
||||
intval($flags),
|
||||
dbesc($addr)
|
||||
);
|
||||
);
|
||||
}
|
||||
else {
|
||||
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ",
|
||||
@@ -651,4 +661,4 @@ class Libzotdir {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class MessageFilter {
|
||||
|
||||
$lang = null;
|
||||
|
||||
if((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);
|
||||
}
|
||||
|
||||
@@ -39,10 +39,17 @@ class MessageFilter {
|
||||
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((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)
|
||||
return false;
|
||||
}
|
||||
@@ -60,10 +67,17 @@ class MessageFilter {
|
||||
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((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)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
define ( 'NWIKI_ITEM_RESOURCE_TYPE', 'nwiki' );
|
||||
|
||||
class NativeWiki {
|
||||
|
||||
|
||||
static public function listwikis($channel, $observer_hash) {
|
||||
public static function listwikis($channel, $observer_hash) {
|
||||
|
||||
$sql_extra = item_permissions_sql($channel['channel_id'], $observer_hash);
|
||||
$wikis = q("SELECT * FROM item
|
||||
@@ -38,7 +40,7 @@ class NativeWiki {
|
||||
}
|
||||
|
||||
|
||||
function create_wiki($channel, $observer_hash, $wiki, $acl) {
|
||||
public static function create_wiki($channel, $observer_hash, $wiki, $acl) {
|
||||
|
||||
$resource_id = new_uuid();
|
||||
$uuid = new_uuid();
|
||||
@@ -71,7 +73,7 @@ class NativeWiki {
|
||||
$arr['item_thread_top'] = 1;
|
||||
$arr['item_private'] = intval($acl->is_private());
|
||||
$arr['verb'] = ACTIVITY_CREATE;
|
||||
$arr['obj_type'] = ACTIVITY_OBJ_WIKI;
|
||||
$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);
|
||||
@@ -99,7 +101,8 @@ class NativeWiki {
|
||||
}
|
||||
}
|
||||
|
||||
function update_wiki($channel_id, $observer_hash, $arr, $acl) {
|
||||
|
||||
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'];
|
||||
@@ -154,8 +157,8 @@ class NativeWiki {
|
||||
}
|
||||
}
|
||||
|
||||
static public function sync_a_wiki_item($uid,$id,$resource_id) {
|
||||
|
||||
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),
|
||||
@@ -163,8 +166,8 @@ class NativeWiki {
|
||||
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'])
|
||||
);
|
||||
@@ -178,40 +181,47 @@ class NativeWiki {
|
||||
foreach($sync_item as $w) {
|
||||
$pkt[] = encode_item($w,true);
|
||||
}
|
||||
build_sync_packet($uid,array('wiki' => $pkt));
|
||||
Libsync::build_sync_packet($uid,array('wiki' => $pkt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function delete_wiki($channel_id,$observer_hash,$resource_id) {
|
||||
|
||||
public static function delete_wiki($channel_id,$observer_hash,$resource_id) {
|
||||
|
||||
$w = self::get_wiki($channel_id,$observer_hash,$resource_id);
|
||||
$item = $w['wiki'];
|
||||
if(! $item) {
|
||||
return array('item' => null, 'success' => false);
|
||||
}
|
||||
else {
|
||||
$drop = drop_item($item['id'], false, DROPITEM_NORMAL, true);
|
||||
if(! $w['wiki']) {
|
||||
return [ 'success' => false ];
|
||||
}
|
||||
else {
|
||||
|
||||
info( t('Wiki files deleted successfully'));
|
||||
$r = q("SELECT id FROM item WHERE uid = %s AND resource_id = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($resource_id)
|
||||
);
|
||||
|
||||
return array('item' => $item, 'item_id' => $item['id'], 'success' => (($drop === 1) ? true : false));
|
||||
$ids = array_column($r, 'id');
|
||||
drop_items($ids, true, DROPITEM_PHASE1);
|
||||
|
||||
info(t('Wiki files deleted successfully'));
|
||||
|
||||
return [ 'success' => true ];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static public function get_wiki($channel_id, $observer_hash, $resource_id) {
|
||||
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 limit 1",
|
||||
$sql_extra ORDER BY id LIMIT 1",
|
||||
intval($channel_id),
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
dbesc($resource_id)
|
||||
);
|
||||
if(! $item) {
|
||||
return array('wiki' => null);
|
||||
return [ 'wiki' => null ];
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -234,7 +244,7 @@ class NativeWiki {
|
||||
}
|
||||
|
||||
|
||||
static public function exists_by_name($uid, $urlName) {
|
||||
public static function exists_by_name($uid, $urlName) {
|
||||
|
||||
$sql_extra = item_permissions_sql($uid);
|
||||
|
||||
@@ -256,7 +266,8 @@ class NativeWiki {
|
||||
}
|
||||
|
||||
|
||||
static public function get_permissions($resource_id, $owner_id, $observer_hash) {
|
||||
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);
|
||||
|
||||
@@ -281,6 +292,7 @@ class NativeWiki {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function name_encode ($string) {
|
||||
|
||||
$string = html_entity_decode($string);
|
||||
@@ -296,6 +308,7 @@ class NativeWiki {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
public static function name_decode ($string) {
|
||||
|
||||
$encoding = mb_internal_encoding();
|
||||
|
||||
@@ -109,6 +109,7 @@ class NativeWikiPage {
|
||||
return [ 'success' => false, 'message' => t('Wiki page create failed.') ];
|
||||
}
|
||||
|
||||
|
||||
static public function rename_page($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
|
||||
@@ -163,11 +164,13 @@ class NativeWikiPage {
|
||||
return [ 'success' => true, 'page' => $page ];
|
||||
}
|
||||
|
||||
return [ 'success' => false, 'item_id' => $c['item_id'], 'message' => t('Page not found') ];
|
||||
return [ 'success' => false, 'message' => t('Page not found') ];
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function get_page_content($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'] : '');
|
||||
@@ -198,7 +201,9 @@ class NativeWikiPage {
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function 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'] : '');
|
||||
@@ -290,6 +295,7 @@ class NativeWikiPage {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
static public function load_page_history($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
|
||||
@@ -338,8 +344,8 @@ class NativeWikiPage {
|
||||
return null;
|
||||
}
|
||||
|
||||
static public function save_page($arr) {
|
||||
|
||||
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'] : '');
|
||||
@@ -385,19 +391,20 @@ class NativeWikiPage {
|
||||
$ret = item_store($item, false, false);
|
||||
|
||||
if($ret['item_id'])
|
||||
return array('message' => '', 'item_id' => $ret['item_id'], 'filename' => $filename, 'success' => true);
|
||||
return array('message' => '', 'item_id' => $ret['item_id'], 'filename' => $pageUrlName, 'success' => true);
|
||||
else
|
||||
return array('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);
|
||||
|
||||
$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 = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
|
||||
if(! $w['wiki']) {
|
||||
return [ 'success' => false, 'message' => t('Error reading wiki') ];
|
||||
}
|
||||
@@ -417,14 +424,16 @@ class NativeWikiPage {
|
||||
}
|
||||
|
||||
if($ids) {
|
||||
drop_items($ids);
|
||||
drop_items($ids, true, DROPITEM_PHASE1);
|
||||
return [ 'success' => true ];
|
||||
}
|
||||
|
||||
return [ 'success' => false, 'message' => t('Nothing deleted') ];
|
||||
}
|
||||
|
||||
|
||||
static public function revert_page($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
|
||||
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
|
||||
$commitHash = ((array_key_exists('commitHash',$arr)) ? $arr['commitHash'] : null);
|
||||
@@ -432,12 +441,12 @@ class NativeWikiPage {
|
||||
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
|
||||
|
||||
if (! $commitHash) {
|
||||
return array('content' => $content, 'message' => 'No commit was provided', 'success' => false);
|
||||
return array('message' => 'No commit was provided', 'success' => false);
|
||||
}
|
||||
|
||||
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if (!$w['wiki']) {
|
||||
return array('content' => $content, 'message' => 'Error reading wiki', 'success' => false);
|
||||
return array('message' => 'Error reading wiki', 'success' => false);
|
||||
}
|
||||
|
||||
$x = $arr;
|
||||
@@ -451,11 +460,13 @@ class NativeWikiPage {
|
||||
$content = $loaded['body'];
|
||||
return [ 'content' => $content, 'success' => true ];
|
||||
}
|
||||
return [ 'content' => $content, 'success' => false ];
|
||||
return [ 'success' => false ];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static public function compare_page($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
|
||||
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
|
||||
$currentCommit = ((array_key_exists('currentCommit',$arr)) ? $arr['currentCommit'] : (-1));
|
||||
@@ -491,6 +502,7 @@ class NativeWikiPage {
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function commit($arr) {
|
||||
|
||||
$commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : t('Page updated'));
|
||||
@@ -530,8 +542,11 @@ class NativeWikiPage {
|
||||
foreach ($match[1] as $m) {
|
||||
// TODO: Why do we need to double urlencode for this to work?
|
||||
//$pageURLs[] = urlencode(urlencode(escape_tags($m)));
|
||||
$pageURLs[] = Zlib\NativeWiki::name_encode(escape_tags($m));
|
||||
$pages[] = $m;
|
||||
$titleUri = explode('|',$m);
|
||||
$page = $titleUri[0] ?? '';
|
||||
$title = $titleUri[1] ?? $page;
|
||||
$pageURLs[] = Zlib\NativeWiki::name_encode(escape_tags($page));
|
||||
$pages[] = $title;
|
||||
}
|
||||
$idx = 0;
|
||||
while(strpos($s,'[[') !== false) {
|
||||
@@ -568,7 +583,6 @@ class NativeWikiPage {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Replace the instances of the string [toc] with a list element that will be populated by
|
||||
* a table of contents by the JavaScript library
|
||||
@@ -584,6 +598,7 @@ class NativeWikiPage {
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a select set of bbcode tags. Much of the code is copied from include/bbcode.php
|
||||
* @param string $s
|
||||
@@ -623,7 +638,9 @@ class NativeWikiPage {
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
static public function get_file_ext($arr) {
|
||||
|
||||
if($arr['mimetype'] === 'text/bbcode')
|
||||
return '.bb';
|
||||
elseif($arr['mimetype'] === 'text/markdown')
|
||||
|
||||
@@ -132,6 +132,7 @@ class PConfig {
|
||||
// manage array value
|
||||
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
|
||||
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
|
||||
$new = false;
|
||||
|
||||
$now = datetime_convert();
|
||||
if (! $updated) {
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Lib\Libzot;
|
||||
|
||||
|
||||
class Queue {
|
||||
|
||||
static function update($id, $add_priority = 0) {
|
||||
@@ -39,7 +36,7 @@ class Queue {
|
||||
// queue item is less than 12 hours old, we'll schedule for fifteen
|
||||
// minutes.
|
||||
|
||||
$r = q("UPDATE outq SET outq_scheduled = '%s' WHERE outq_posturl = '%s'",
|
||||
q("UPDATE outq SET outq_scheduled = '%s' WHERE outq_posturl = '%s'",
|
||||
dbesc(datetime_convert('UTC','UTC','now + 5 days')),
|
||||
dbesc($x[0]['outq_posturl'])
|
||||
);
|
||||
@@ -88,7 +85,7 @@ class Queue {
|
||||
|
||||
static function set_delivered($id,$channel = 0) {
|
||||
logger('queue: set delivered ' . $id,LOGGER_DEBUG);
|
||||
$sql_extra = (($channel_id) ? " and outq_channel = " . intval($channel_id) . " " : '');
|
||||
$sql_extra = (($channel['channel_id']) ? " and outq_channel = " . intval($channel['channel_id']) . " " : '');
|
||||
|
||||
// Set the next scheduled run date so far in the future that it will be expired
|
||||
// long before it ever makes it back into the delivery chain.
|
||||
@@ -116,10 +113,10 @@ class Queue {
|
||||
dbesc($arr['hash']),
|
||||
intval($arr['account_id']),
|
||||
intval($arr['channel_id']),
|
||||
dbesc(($arr['driver']) ? $arr['driver'] : 'zot'),
|
||||
dbesc(($arr['driver']) ? $arr['driver'] : 'zot6'),
|
||||
dbesc($arr['posturl']),
|
||||
intval(1),
|
||||
intval(($arr['priority']) ? $arr['priority'] : 0),
|
||||
intval(isset($arr['priority']) ? $arr['priority'] : 0),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc(datetime_convert()),
|
||||
@@ -137,7 +134,7 @@ class Queue {
|
||||
$base = null;
|
||||
$h = parse_url($outq['outq_posturl']);
|
||||
if($h !== false)
|
||||
$base = $h['scheme'] . '://' . $h['host'] . (($h['port']) ? ':' . $h['port'] : '');
|
||||
$base = $h['scheme'] . '://' . $h['host'] . (isset($h['port']) ? ':' . $h['port'] : '');
|
||||
|
||||
if(($base) && ($base !== z_root()) && ($immediate)) {
|
||||
$y = q("select site_update, site_dead from site where site_url = '%s' ",
|
||||
@@ -230,11 +227,10 @@ class Queue {
|
||||
|
||||
logger('deliver: dest: ' . $outq['outq_posturl'], LOGGER_DEBUG);
|
||||
|
||||
|
||||
if($outq['outq_posturl'] === z_root() . '/zot') {
|
||||
// local delivery
|
||||
$zot = new \Zotlabs\Zot6\Receiver(new \Zotlabs\Zot6\Zot6Handler(),$outq['outq_notify']);
|
||||
$result = $zot->run(true);
|
||||
$result = $zot->run();
|
||||
logger('returned_json: ' . json_encode($result,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES), LOGGER_DATA);
|
||||
logger('deliver: local zot delivery succeeded to ' . $outq['outq_posturl']);
|
||||
Libzot::process_response($outq['outq_posturl'],[ 'success' => true, 'body' => json_encode($result) ], $outq);
|
||||
@@ -250,7 +246,7 @@ class Queue {
|
||||
$host_crypto = null;
|
||||
|
||||
if($channel && $base) {
|
||||
$h = q("select hubloc_sitekey, site_crypto from hubloc left join site on hubloc_url = site_url where site_url = '%s' order by hubloc_id desc limit 1",
|
||||
$h = q("select hubloc_sitekey, site_crypto from hubloc left join site on hubloc_url = site_url where site_url = '%s' and hubloc_sitekey != '' order by hubloc_id desc limit 1",
|
||||
dbesc($base)
|
||||
);
|
||||
if($h) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
|
||||
class Share {
|
||||
|
||||
private $item = null;
|
||||
@@ -54,7 +53,7 @@ class Share {
|
||||
if(! $this->item)
|
||||
return $obj;
|
||||
|
||||
$obj['asld'] = $this->item['mid'];
|
||||
$obj['asld'] = Activity::fetch_item( [ 'id' => $this->item['mid'] ] );
|
||||
$obj['type'] = $this->item['obj_type'];
|
||||
$obj['id'] = $this->item['mid'];
|
||||
$obj['content'] = $this->item['body'];
|
||||
@@ -127,12 +126,12 @@ class Share {
|
||||
"' profile='" . $this->item['author']['xchan_url'] .
|
||||
"' avatar='" . $this->item['author']['xchan_photo_s'] .
|
||||
"' link='" . $this->item['plink'] .
|
||||
"' auth='" . (($this->item['author']['network'] === 'zot') ? 'true' : 'false') .
|
||||
"' auth='" . ((in_array($this->item['author']['xchan_network'], ['zot6', 'zot'])) ? 'true' : 'false') .
|
||||
"' posted='" . $this->item['created'] .
|
||||
"' message_id='" . $this->item['mid'] .
|
||||
"']";
|
||||
if($this->item['title'])
|
||||
$bb .= '[b]'.$this->item['title'].'[/b]'."\r\n";
|
||||
$bb .= '[h3][b]'.$this->item['title'].'[/b][/h3]'."\r\n";
|
||||
$bb .= (($is_photo) ? $photo_bb . "\r\n" . $this->item['body'] : $this->item['body']);
|
||||
$bb .= "[/share]";
|
||||
}
|
||||
|
||||
150
Zotlabs/Lib/SvgSanitizer.php
Normal file
150
Zotlabs/Lib/SvgSanitizer.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
use DomDocument;
|
||||
|
||||
/**
|
||||
* SVGSantiizer
|
||||
*
|
||||
* Whitelist-based PHP SVG sanitizer.
|
||||
*
|
||||
* @link https://github.com/alister-/SVG-Sanitizer}
|
||||
* @author Alister Norris
|
||||
* @copyright Copyright (c) 2013 Alister Norris
|
||||
* @license http://opensource.org/licenses/mit-license.php The MIT License
|
||||
* @package svgsanitizer
|
||||
*/
|
||||
|
||||
class SvgSanitizer {
|
||||
|
||||
private $xmlDoc; // PHP XML DOMDocument
|
||||
|
||||
private $removedattrs = [];
|
||||
|
||||
private static $allowed_functions = [ 'matrix', 'url', 'translate', 'rgb' ];
|
||||
|
||||
// defines the whitelist of elements and attributes allowed.
|
||||
private static $whitelist = [
|
||||
'a' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'href', 'xlink:href', 'xlink:title' ],
|
||||
'circle' => [ 'class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'r', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
|
||||
'clipPath' => [ 'class', 'clipPathUnits', 'id' ],
|
||||
'defs' => [ ],
|
||||
'style' => [ 'type' ],
|
||||
'desc' => [ ],
|
||||
'ellipse' => [ 'class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
|
||||
'feGaussianBlur' => [ 'class', 'color-interpolation-filters', 'id', 'requiredFeatures', 'stdDeviation' ],
|
||||
'filter' => [ 'class', 'color-interpolation-filters', 'filterRes', 'filterUnits', 'height', 'id', 'primitiveUnits', 'requiredFeatures', 'width', 'x', 'xlink:href', 'y' ],
|
||||
'foreignObject' => [ 'class', 'font-size', 'height', 'id', 'opacity', 'requiredFeatures', 'style', 'transform', 'width', 'x', 'y' ],
|
||||
'g' => [ 'class', 'clip-path', 'clip-rule', 'id', 'display', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'font-family', 'font-size', 'font-style', 'font-weight', 'text-anchor' ],
|
||||
'image' => [ 'class', 'clip-path', 'clip-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'style', 'systemLanguage', 'transform', 'width', 'x', 'xlink:href', 'xlink:title', 'y' ],
|
||||
'line' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'x1', 'x2', 'y1', 'y2' ],
|
||||
'linearGradient' => [ 'class', 'id', 'gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'xlink:href', 'y1', 'y2' ],
|
||||
'marker' => [ 'id', 'class', 'markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'systemLanguage', 'viewBox' ],
|
||||
'mask' => [ 'class', 'height', 'id', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y' ],
|
||||
'metadata' => [ 'class', 'id' ],
|
||||
'path' => [ 'class', 'clip-path', 'clip-rule', 'd', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
|
||||
'pattern' => [ 'class', 'height', 'id', 'patternContentUnits', 'patternTransform', 'patternUnits', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xlink:href', 'y' ],
|
||||
'polygon' => [ 'class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'class', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
|
||||
'polyline' => [ 'class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
|
||||
'radialGradient' => [ 'class', 'cx', 'cy', 'fx', 'fy', 'gradientTransform', 'gradientUnits', 'id', 'r', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'xlink:href' ],
|
||||
'rect' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'width', 'x', 'y' ],
|
||||
'stop' => [ 'class', 'id', 'offset', 'requiredFeatures', 'stop-color', 'stop-opacity', 'style', 'systemLanguage' ],
|
||||
'svg' => [ 'class', 'clip-path', 'clip-rule', 'filter', 'id', 'height', 'mask', 'preserveAspectRatio', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xmlns', 'xmlns:se', 'xmlns:xlink', 'y' ],
|
||||
'switch' => [ 'class', 'id', 'requiredFeatures', 'systemLanguage' ],
|
||||
'symbol' => [ 'class', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'opacity', 'preserveAspectRatio', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'viewBox' ],
|
||||
'text' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'transform', 'x', 'xml:space', 'y' ],
|
||||
'textPath' => [ 'class', 'id', 'method', 'requiredFeatures', 'spacing', 'startOffset', 'style', 'systemLanguage', 'transform', 'xlink:href' ],
|
||||
'title' => [ ],
|
||||
'tspan' => [ 'class', 'clip-path', 'clip-rule', 'dx', 'dy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'rotate', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'textLength', 'transform', 'x', 'xml:space', 'y' ],
|
||||
'use' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'transform', 'width', 'x', 'xlink:href', 'y' ],
|
||||
];
|
||||
|
||||
function __construct() {
|
||||
$this->xmlDoc = new DOMDocument('1.0','UTF-8');
|
||||
$this->xmlDoc->preserveWhiteSpace = false;
|
||||
libxml_use_internal_errors(true);
|
||||
}
|
||||
|
||||
// load XML SVG
|
||||
function load($file) {
|
||||
$this->xmlDoc->load($file);
|
||||
}
|
||||
|
||||
function loadXML($str) {
|
||||
if (! $this->xmlDoc->loadXML($str)) {
|
||||
logger('loadxml: ' . print_r(libxml_get_errors(),true), LOGGER_DEBUG);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function sanitize()
|
||||
{
|
||||
// all elements in xml doc
|
||||
$allElements = $this->xmlDoc->getElementsByTagName('*');
|
||||
|
||||
// loop through all elements
|
||||
for($i = 0; $i < $allElements->length; $i++)
|
||||
{
|
||||
$this->removedattrs = [];
|
||||
|
||||
$currentNode = $allElements->item($i);
|
||||
|
||||
// logger('current_node: ' . print_r($currentNode,true));
|
||||
|
||||
// array of allowed attributes in specific element
|
||||
$whitelist_attr_arr = self::$whitelist[$currentNode->tagName];
|
||||
|
||||
// does element exist in whitelist?
|
||||
if(isset($whitelist_attr_arr)) {
|
||||
$total = $currentNode->attributes->length;
|
||||
|
||||
for($x = 0; $x < $total; $x++) {
|
||||
|
||||
// get attributes name
|
||||
$attrName = $currentNode->attributes->item($x)->nodeName;
|
||||
|
||||
// logger('checking: ' . print_r($currentNode->attributes->item($x),true));
|
||||
$matches = false;
|
||||
|
||||
// check if attribute isn't in whitelist
|
||||
if(! in_array($attrName, $whitelist_attr_arr)) {
|
||||
$this->removedattrs[] = $attrName;
|
||||
}
|
||||
// check for disallowed functions
|
||||
elseif (preg_match_all('/([a-zA-Z0-9]+)[\s]*\(/',
|
||||
$currentNode->attributes->item($x)->textContent,$matches,PREG_SET_ORDER)) {
|
||||
if ($attrName === 'text') {
|
||||
continue;
|
||||
}
|
||||
foreach ($matches as $match) {
|
||||
if(! in_array($match[1],self::$allowed_functions)) {
|
||||
logger('queue_remove_function: ' . $match[1],LOGGER_DEBUG);
|
||||
$this->removedattrs[] = $attrName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->removedattrs) {
|
||||
foreach ($this->removedattrs as $attr) {
|
||||
$currentNode->removeAttribute($attr);
|
||||
logger('removed: ' . $attr, LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// else remove element
|
||||
else {
|
||||
logger('remove_node: ' . print_r($currentNode,true));
|
||||
$currentNode->parentNode->removeChild($currentNode);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function saveSVG() {
|
||||
$this->xmlDoc->formatOutput = true;
|
||||
return($this->xmlDoc->saveXML());
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,14 @@ namespace Zotlabs\Lib;
|
||||
class System {
|
||||
|
||||
static public function get_platform_name() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && array_key_exists('platform_name',\App::$config['system']))
|
||||
return \App::$config['system']['platform_name'];
|
||||
return PLATFORM_NAME;
|
||||
static $platform_name = '';
|
||||
if(empty($platform_name)) {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && array_key_exists('platform_name',\App::$config['system']))
|
||||
$platform_name = \App::$config['system']['platform_name'];
|
||||
else
|
||||
$platform_name = PLATFORM_NAME;
|
||||
}
|
||||
return $platform_name;
|
||||
}
|
||||
|
||||
static public function get_site_name() {
|
||||
|
||||
@@ -35,14 +35,15 @@ class ThreadItem {
|
||||
|
||||
|
||||
public function __construct($data) {
|
||||
|
||||
|
||||
$this->data = $data;
|
||||
$this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
|
||||
$this->threaded = get_config('system','thread_allow');
|
||||
|
||||
$observer = \App::get_observer();
|
||||
|
||||
// Prepare the children
|
||||
if($data['children']) {
|
||||
if(isset($data['children'])) {
|
||||
foreach($data['children'] as $item) {
|
||||
|
||||
/*
|
||||
@@ -77,7 +78,7 @@ class ThreadItem {
|
||||
*/
|
||||
|
||||
public function get_template_data($conv_responses, $thread_level=1, $conv_flags = []) {
|
||||
|
||||
|
||||
$result = array();
|
||||
|
||||
$item = $this->get_data();
|
||||
@@ -94,13 +95,14 @@ class ThreadItem {
|
||||
$total_children = $this->count_descendants();
|
||||
$unseen_comments = (($item['real_uid']) ? 0 : $this->count_unseen_descendants());
|
||||
|
||||
$conv = $this->get_conversation();
|
||||
$conv = $this->get_conversation();
|
||||
$observer = $conv->get_observer();
|
||||
|
||||
$lock = ((($item['item_private'] == 1) || (($item['uid'] == local_channel()) && (strlen($item['allow_cid']) || strlen($item['allow_gid'])
|
||||
$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'];
|
||||
|
||||
$shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false);
|
||||
|
||||
@@ -109,10 +111,10 @@ class ThreadItem {
|
||||
$shareable = true;
|
||||
|
||||
$privacy_warning = false;
|
||||
if(($item['item_private'] == 1) && ($item['owner']['xchan_network'] === 'activitypub')) {
|
||||
if(intval($item['item_private']) && ($item['owner']['xchan_network'] === 'activitypub')) {
|
||||
$recips = get_iconfig($item['parent'], 'activitypub', 'recips');
|
||||
|
||||
if(! in_array($observer['xchan_url'], $recips['to']))
|
||||
if(! is_array($recips['to']) || ! in_array($observer['xchan_url'], $recips['to']))
|
||||
$privacy_warning = true;
|
||||
}
|
||||
|
||||
@@ -149,9 +151,11 @@ class ThreadItem {
|
||||
$edpost = false;
|
||||
|
||||
|
||||
if($observer['xchan_hash'] == $this->get_data_value('author_xchan')
|
||||
|| $observer['xchan_hash'] == $this->get_data_value('owner_xchan')
|
||||
|| $this->get_data_value('uid') == local_channel())
|
||||
if($observer && $observer['xchan_hash']
|
||||
&& ($observer['xchan_hash'] == $this->get_data_value('author_xchan')
|
||||
|| $observer['xchan_hash'] == $this->get_data_value('owner_xchan')
|
||||
|| $observer['xchan_hash'] == $this->get_data_value('source_xchan')
|
||||
|| $this->get_data_value('uid') == local_channel()))
|
||||
$dropping = true;
|
||||
|
||||
|
||||
@@ -166,15 +170,15 @@ class ThreadItem {
|
||||
'dropping' => $dropping,
|
||||
'delete' => t('Delete'),
|
||||
);
|
||||
}
|
||||
}
|
||||
elseif(is_site_admin()) {
|
||||
$drop = [ 'dropping' => true, 'delete' => t('Admin Delete') ];
|
||||
}
|
||||
|
||||
// FIXME
|
||||
if($observer_is_pageowner) {
|
||||
if($observer_is_pageowner) {
|
||||
$multidrop = array(
|
||||
'select' => t('Select'),
|
||||
'select' => t('Select'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -203,6 +207,10 @@ class ThreadItem {
|
||||
}
|
||||
}
|
||||
|
||||
if($item['obj_type'] === 'Question') {
|
||||
$response_verbs[] = 'answer';
|
||||
}
|
||||
|
||||
$consensus = (intval($item['item_consensus']) ? true : false);
|
||||
if($consensus) {
|
||||
$response_verbs[] = 'agree';
|
||||
@@ -216,7 +224,7 @@ class ThreadItem {
|
||||
|
||||
if(! feature_enabled($conv->get_profile_owner(),'dislike'))
|
||||
unset($conv_responses['dislike']);
|
||||
|
||||
|
||||
$responses = get_responses($conv_responses,$response_verbs,$this,$item);
|
||||
|
||||
$my_responses = [];
|
||||
@@ -247,7 +255,7 @@ class ThreadItem {
|
||||
}
|
||||
|
||||
$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'))
|
||||
$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']) : '');
|
||||
|
||||
/*
|
||||
@@ -257,7 +265,7 @@ 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))) {
|
||||
@@ -268,7 +276,7 @@ class ThreadItem {
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$is_comment = true;
|
||||
}
|
||||
@@ -280,12 +288,16 @@ class ThreadItem {
|
||||
|
||||
$settings = '';
|
||||
|
||||
$tagger = [];
|
||||
|
||||
// FIXME - check this permission
|
||||
if($conv->get_profile_owner() == local_channel()) {
|
||||
/* disable until we agree on how to implemnt this in zot6/activitypub
|
||||
$tagger = array(
|
||||
'tagit' => t("Add Tag"),
|
||||
'classtagger' => "",
|
||||
);
|
||||
*/
|
||||
|
||||
$settings = t('Conversation Tools');
|
||||
}
|
||||
@@ -305,6 +317,7 @@ class ThreadItem {
|
||||
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"));
|
||||
}
|
||||
|
||||
if ($shareable) {
|
||||
@@ -331,25 +344,20 @@ class ThreadItem {
|
||||
if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0)
|
||||
$is_new = true;
|
||||
|
||||
|
||||
localize_item($item);
|
||||
|
||||
$body = prepare_body($item,true);
|
||||
|
||||
// $viewthread (below) is only valid in list mode. If this is a channel page, build the thread viewing link
|
||||
// since we can't depend on llink or plink pointing to the right local location.
|
||||
|
||||
|
||||
$owner_address = substr($item['owner']['xchan_addr'],0,strpos($item['owner']['xchan_addr'],'@'));
|
||||
$viewthread = $item['llink'];
|
||||
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('%d unseen',$unseen_comments) : '');
|
||||
|
||||
|
||||
|
||||
|
||||
$list_unseen_txt = (($unseen_comments) ? sprintf( t('%d unseen'),$unseen_comments) : '');
|
||||
|
||||
$children = $this->get_children();
|
||||
|
||||
@@ -359,11 +367,28 @@ class ThreadItem {
|
||||
call_hooks('dropdown_extras',$dropdown_extras_arr);
|
||||
$dropdown_extras = $dropdown_extras_arr['dropdown_extras'];
|
||||
|
||||
$midb64 = 'b64.' . base64url_encode($item['mid']);
|
||||
$mids = [ $midb64 ];
|
||||
$response_mids = [];
|
||||
foreach($response_verbs as $v) {
|
||||
if(isset($conv_responses[$v]['mids'][$item['mid']])) {
|
||||
$response_mids = array_merge($response_mids, $conv_responses[$v]['mids'][$item['mid']]);
|
||||
}
|
||||
}
|
||||
|
||||
$mids = array_merge($mids, $response_mids);
|
||||
$json_mids = json_encode($mids);
|
||||
|
||||
// Pinned item processing
|
||||
$allowed_type = (in_array($item['item_type'], get_config('system', 'pin_types', [ ITEM_TYPE_POST ])) ? true : false);
|
||||
$pinned_items = ($allowed_type ? get_pconfig($item['uid'], 'pinned', $item['item_type'], []) : []);
|
||||
$pinned = ((!empty($pinned_items) && in_array($midb64, $pinned_items)) ? true : false);
|
||||
|
||||
$tmp_item = array(
|
||||
'template' => $this->get_template(),
|
||||
'mode' => $mode,
|
||||
'item_type' => intval($item['item_type']),
|
||||
'type' => implode("",array_slice(explode("/",$item['verb']),-1)),
|
||||
'item_type' => intval($item['item_type']),
|
||||
//'type' => implode("",array_slice(explode("/",$item['verb']),-1)),
|
||||
'body' => $body['html'],
|
||||
'tags' => $body['tags'],
|
||||
'categories' => $body['categories'],
|
||||
@@ -372,14 +397,17 @@ class ThreadItem {
|
||||
'folders' => $body['folders'],
|
||||
'text' => strip_tags($body['html']),
|
||||
'id' => $this->get_id(),
|
||||
'mid' => $item['mid'],
|
||||
'mid' => $midb64,
|
||||
'mids' => $json_mids,
|
||||
'parent' => $item['parent'],
|
||||
'author_id' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']),
|
||||
'isevent' => $isevent,
|
||||
'attend' => $attend,
|
||||
'consensus' => $consensus,
|
||||
'conlabels' => $conlabels,
|
||||
'canvote' => $canvote,
|
||||
'linktitle' => sprintf( t('View %s\'s profile - %s'), $profile_name, $item['author']['xchan_addr']),
|
||||
'olinktitle' => sprintf( t('View %s\'s profile - %s'), $this->get_owner_name(), $item['owner']['xchan_addr']),
|
||||
'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'],
|
||||
'viewthread' => $viewthread,
|
||||
'to' => t('to'),
|
||||
@@ -397,7 +425,7 @@ class ThreadItem {
|
||||
'sparkle' => $sparkle,
|
||||
'title' => $item['title'],
|
||||
'title_tosource' => get_pconfig($conv->get_profile_owner(),'system','title_tosource'),
|
||||
'ago' => relative_date($item['created']),
|
||||
//'ago' => relative_date($item['created']),
|
||||
'app' => $item['app'],
|
||||
'str_app' => sprintf( t('from %s'), $item['app']),
|
||||
'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'),
|
||||
@@ -405,6 +433,8 @@ class ThreadItem {
|
||||
'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''),
|
||||
'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''),
|
||||
'lock' => $lock,
|
||||
'locktype' => $locktype,
|
||||
'delayed' => $item['item_delayed'],
|
||||
'privacy_warning' => $privacy_warning,
|
||||
'verified' => $verified,
|
||||
'unverified' => $unverified,
|
||||
@@ -425,9 +455,11 @@ class ThreadItem {
|
||||
'has_tags' => $has_tags,
|
||||
'reactions' => $this->reactions,
|
||||
// Item toolbar buttons
|
||||
'emojis' => (($this->is_toplevel() && $this->is_commentable() && $observer && feature_enabled($conv->get_profile_owner(),'emojis')) ? '1' : ''),
|
||||
'emojis' => (($this->is_toplevel() && $this->is_commentable() && $observer && feature_enabled($conv->get_profile_owner(),'emojis')) ? '1' : ''),
|
||||
'like' => $like,
|
||||
'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
|
||||
'reply_to' => (((! $this->is_toplevel()) && feature_enabled($conv->get_profile_owner(),'reply_to')) ? $reply_to : ''),
|
||||
'top_hint' => t("Go to previous comment"),
|
||||
'share' => $share,
|
||||
'embed' => $embed,
|
||||
'rawmid' => $item['mid'],
|
||||
@@ -436,13 +468,15 @@ class ThreadItem {
|
||||
'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts') && ($item['item_type'] == ITEM_TYPE_POST)) ? $star : ''),
|
||||
'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''),
|
||||
'filer' => ((feature_enabled($conv->get_profile_owner(),'filing') && ($item['item_type'] == ITEM_TYPE_POST)) ? $filer : ''),
|
||||
'pinned' => ($pinned ? t('Pinned post') : ''),
|
||||
'pinnable' => (($this->is_toplevel() && local_channel() && $item['owner_xchan'] == $observer['xchan_hash'] && $allowed_type && $item['item_private'] == 0 && $item['item_delayed'] == 0) ? '1' : ''),
|
||||
'pinme' => ($pinned ? t('Unpin from the top') : t('Pin to the top')),
|
||||
'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,
|
||||
'dropdown_extras' => $dropdown_extras,
|
||||
// end toolbar buttons
|
||||
|
||||
'unseen_comments' => $unseen_comments,
|
||||
'comment_count' => $total_children,
|
||||
'comment_count_txt' => $comment_count_txt,
|
||||
@@ -463,13 +497,13 @@ class ThreadItem {
|
||||
'modal_dismiss' => t('Close'),
|
||||
'showlike' => $showlike,
|
||||
'showdislike' => $showdislike,
|
||||
'comment' => $this->get_comment_box($indent),
|
||||
'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box($indent)),
|
||||
'previewing' => ($conv->is_preview() ? true : false ),
|
||||
'preview_lbl' => t('This is an unsaved preview'),
|
||||
'wait' => t('Please wait'),
|
||||
'submid' => str_replace(['+','='], ['',''], base64_encode($item['mid'])),
|
||||
'thread_level' => $thread_level,
|
||||
'settings' => $settings
|
||||
'settings' => $settings,
|
||||
'thr_parent' => (($item['parent_mid'] != $item['thr_parent']) ? 'b64.' . base64url_encode($item['thr_parent']) : '')
|
||||
);
|
||||
|
||||
$arr = array('item' => $item, 'output' => $tmp_item);
|
||||
@@ -486,8 +520,8 @@ class ThreadItem {
|
||||
|
||||
// needed for scroll to comment from notification but needs more work
|
||||
// as we do not want to open all comments unless there is actually an #item_xx anchor
|
||||
// and the url fragment is not sent to the server.
|
||||
// if(in_array(\App::$module,['display','update_display']))
|
||||
// and the url fragment is not sent to the server.
|
||||
// if(in_array(\App::$module,['display','update_display']))
|
||||
// $visible_comments = 99999;
|
||||
|
||||
if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
|
||||
@@ -507,7 +541,7 @@ class ThreadItem {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$result['private'] = $item['item_private'];
|
||||
$result['toplevel'] = ($this->is_toplevel() ? 'toplevel_item' : '');
|
||||
|
||||
@@ -522,7 +556,7 @@ class ThreadItem {
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
public function get_id() {
|
||||
return $this->get_data_value('id');
|
||||
}
|
||||
@@ -577,7 +611,7 @@ class ThreadItem {
|
||||
if(activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$item->set_parent($this);
|
||||
$this->children[] = $item;
|
||||
return end($this->children);
|
||||
@@ -651,7 +685,7 @@ class ThreadItem {
|
||||
*/
|
||||
public function set_conversation($conv) {
|
||||
$previous_mode = ($this->conversation ? $this->conversation->get_mode() : '');
|
||||
|
||||
|
||||
$this->conversation = $conv;
|
||||
|
||||
// Set it on our children too
|
||||
@@ -760,7 +794,7 @@ class ThreadItem {
|
||||
if(!$this->is_toplevel() && !get_config('system','thread_allow')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
$comment_box = '';
|
||||
$conv = $this->get_conversation();
|
||||
|
||||
@@ -776,8 +810,6 @@ class ThreadItem {
|
||||
$arr = array('comment_buttons' => '','id' => $this->get_id());
|
||||
call_hooks('comment_buttons',$arr);
|
||||
$comment_buttons = $arr['comment_buttons'];
|
||||
|
||||
$feature_auto_save_draft = ((feature_enabled($conv->get_profile_owner(), 'auto_save_draft')) ? "true" : "false");
|
||||
|
||||
$comment_box = replace_macros($template,array(
|
||||
'$return_path' => '',
|
||||
@@ -813,8 +845,7 @@ class ThreadItem {
|
||||
'$anoncomments' => ((($conv->get_mode() === 'channel' || $conv->get_mode() === 'display') && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false),
|
||||
'$anonname' => [ 'anonname', t('Your full name (required)') ],
|
||||
'$anonmail' => [ 'anonmail', t('Your email address (required)') ],
|
||||
'$anonurl' => [ 'anonurl', t('Your website URL (optional)') ],
|
||||
'$auto_save_draft' => $feature_auto_save_draft,
|
||||
'$anonurl' => [ 'anonurl', t('Your website URL (optional)') ]
|
||||
));
|
||||
|
||||
return $comment_box;
|
||||
@@ -836,7 +867,7 @@ class ThreadItem {
|
||||
|
||||
if($conv->get_mode() === 'channel')
|
||||
return;
|
||||
|
||||
|
||||
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'];
|
||||
@@ -865,8 +896,4 @@ class ThreadItem {
|
||||
return $this->visiting;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class ThreadStream {
|
||||
private $preview = false;
|
||||
private $prepared_item = '';
|
||||
public $reload = '';
|
||||
private $cipher = 'aes256';
|
||||
private $cipher = 'AES-128-CCM';
|
||||
|
||||
// $prepared_item is for use by alternate conversation structures such as photos
|
||||
// wherein we've already prepared a top level item which doesn't look anything like
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Zotlabs\Lib;
|
||||
|
||||
class Verify {
|
||||
|
||||
function create($type,$channel_id,$token,$meta) {
|
||||
public static function create($type,$channel_id,$token,$meta) {
|
||||
return q("insert into verify ( vtype, channel, token, meta, created ) values ( '%s', %d, '%s', '%s', '%s' )",
|
||||
dbesc($type),
|
||||
intval($channel_id),
|
||||
@@ -15,7 +15,7 @@ class Verify {
|
||||
);
|
||||
}
|
||||
|
||||
function match($type,$channel_id,$token,$meta) {
|
||||
public static function match($type,$channel_id,$token,$meta) {
|
||||
$r = q("select id from verify where vtype = '%s' and channel = %d and token = '%s' and meta = '%s' limit 1",
|
||||
dbesc($type),
|
||||
intval($channel_id),
|
||||
@@ -31,7 +31,7 @@ class Verify {
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_meta($type,$channel_id,$token) {
|
||||
public static function get_meta($type,$channel_id,$token) {
|
||||
$r = q("select id, meta from verify where vtype = '%s' and channel = %d and token = '%s' limit 1",
|
||||
dbesc($type),
|
||||
intval($channel_id),
|
||||
@@ -52,7 +52,7 @@ class Verify {
|
||||
* @param string $type Verify type
|
||||
* @param string $interval SQL compatible time interval
|
||||
*/
|
||||
function purge($type, $interval) {
|
||||
public static function purge($type, $interval) {
|
||||
q("delete from verify where vtype = '%s' and created < %s - INTERVAL %s",
|
||||
dbesc($type),
|
||||
db_utcnow(),
|
||||
|
||||
@@ -106,4 +106,4 @@ class Webfinger {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Zot6\HTTPSig;
|
||||
use Zotlabs\Web\HTTPSig;
|
||||
|
||||
|
||||
class ZotURL {
|
||||
@@ -21,9 +21,8 @@ class ZotURL {
|
||||
}
|
||||
|
||||
$portable_url = substr($url,6);
|
||||
$u = explode('/',$portable_url);
|
||||
$u = explode('/',$portable_url);
|
||||
$portable_id = $u[0];
|
||||
|
||||
$hosts = self::lookup($portable_id);
|
||||
|
||||
if(! $hosts) {
|
||||
@@ -39,8 +38,8 @@ class ZotURL {
|
||||
|
||||
if($channel && $m) {
|
||||
|
||||
$headers = [
|
||||
'Accept' => 'application/x-zot+json',
|
||||
$headers = [
|
||||
'Accept' => 'application/x-zot+json',
|
||||
'Content-Type' => 'application/x-zot+json',
|
||||
'X-Zot-Token' => random_string(),
|
||||
'Digest' => HTTPSig::generate_digest_header($data),
|
||||
@@ -50,9 +49,9 @@ class ZotURL {
|
||||
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false);
|
||||
}
|
||||
else {
|
||||
$h = [ 'Accept: application/x-zot+json' ];
|
||||
$h = [ 'Accept: application/x-zot+json' ];
|
||||
}
|
||||
|
||||
|
||||
$result = [];
|
||||
|
||||
$redirects = 0;
|
||||
@@ -66,7 +65,7 @@ class ZotURL {
|
||||
|
||||
}
|
||||
|
||||
static public function is_zoturl($s) {
|
||||
static public function is_zoturl($url) {
|
||||
|
||||
if(strpos($url,'x-zot:') === 0) {
|
||||
return true;
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Zotlabs\Zot6\HTTPSig;
|
||||
use Zotlabs\Web\HTTPSig;
|
||||
|
||||
class Zotfinger {
|
||||
|
||||
static function exec($resource,$channel = null) {
|
||||
static function exec($resource,$channel = null, $verify = true) {
|
||||
|
||||
if(! $resource) {
|
||||
return false;
|
||||
@@ -18,8 +18,8 @@ class Zotfinger {
|
||||
|
||||
if($channel && $m) {
|
||||
|
||||
$headers = [
|
||||
'Accept' => 'application/x-zot+json',
|
||||
$headers = [
|
||||
'Accept' => 'application/x-zot+json',
|
||||
'Content-Type' => 'application/x-zot+json',
|
||||
'X-Zot-Token' => random_string(),
|
||||
'Digest' => HTTPSig::generate_digest_header($data),
|
||||
@@ -29,11 +29,10 @@ class Zotfinger {
|
||||
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false);
|
||||
}
|
||||
else {
|
||||
$h = [ 'Accept: application/x-zot+json' ];
|
||||
$h = [ 'Accept: application/x-zot+json' ];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
$result = [];
|
||||
|
||||
$redirects = 0;
|
||||
$x = z_post_url($resource,$data,$redirects, [ 'headers' => $h ] );
|
||||
@@ -41,13 +40,14 @@ class Zotfinger {
|
||||
logger('fetch: ' . print_r($x,true));
|
||||
|
||||
if($x['success']) {
|
||||
|
||||
$result['signature'] = HTTPSig::verify($x);
|
||||
|
||||
if ($verify) {
|
||||
$result['signature'] = HTTPSig::verify($x, EMPTY_STR, 'zot6');
|
||||
}
|
||||
|
||||
$result['data'] = json_decode($x['body'],true);
|
||||
|
||||
if($result['data'] && is_array($result['data']) && array_key_exists('encrypted',$result['data']) && $result['data']['encrypted']) {
|
||||
$result['data'] = json_decode(crypto_unencapsulate($result['data'],get_config('system','prvkey')),true);
|
||||
$result['data'] = json_decode(Crypto::unencapsulate($result['data'],get_config('system','prvkey')),true);
|
||||
}
|
||||
|
||||
logger('decrypted: ' . print_r($result,true));
|
||||
@@ -60,4 +60,4 @@ class Zotfinger {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use Zotlabs\Lib\Libzotdir;
|
||||
|
||||
require_once 'include/acl_selectors.php';
|
||||
require_once 'include/group.php';
|
||||
|
||||
@@ -42,24 +44,24 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
|
||||
|
||||
// $type =
|
||||
// 'm' => autocomplete private mail recipient (checks post_mail permission)
|
||||
// 'm' => autocomplete private mail recipient (checks post_mail permission and displays only zot, diaspora, friendica-over-diaspora xchan_network xchan's)
|
||||
// 'a' => autocomplete connections (mod_connections, mod_poke, mod_sources, mod_photos)
|
||||
// 'x' => nav search bar autocomplete (match any xchan)
|
||||
// $_REQUEST['query'] contains autocomplete search text.
|
||||
|
||||
// List of channels whose connections to also suggest,
|
||||
|
||||
// List of channels whose connections to also suggest,
|
||||
// e.g. currently viewed channel or channels mentioned in a post
|
||||
|
||||
$extra_channels = (x($_REQUEST,'extra_channels') ? $_REQUEST['extra_channels'] : array());
|
||||
|
||||
|
||||
// The different autocomplete libraries use different names for the search text
|
||||
// parameter. Internally we'll use $search to represent the search text no matter
|
||||
// what request variable it was attached to.
|
||||
|
||||
// what request variable it was attached to.
|
||||
|
||||
if(array_key_exists('query',$_REQUEST)) {
|
||||
$search = $_REQUEST['query'];
|
||||
}
|
||||
|
||||
|
||||
if( (! local_channel()) && (! in_array($type, [ 'x', 'c', 'f' ])))
|
||||
killme();
|
||||
|
||||
@@ -68,7 +70,7 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
if(in_array($type, [ 'm', 'a', 'c', 'f' ])) {
|
||||
|
||||
// These queries require permission checking. We'll create a simple array of xchan_hash for those with
|
||||
// the requisite permissions which we can check against.
|
||||
// the requisite permissions which we can check against.
|
||||
|
||||
$x = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = '%s' and v = '1'",
|
||||
intval(local_channel()),
|
||||
@@ -85,34 +87,34 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
$sql_extra2 = "AND ( xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'%" . dbesc(punify($search)) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") ";
|
||||
$sql_extra2_xchan = "AND ( xchan_name LIKE " . protect_sprintf( "'" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'" . dbesc(punify($search)) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") ";
|
||||
|
||||
// This horrible mess is needed because position also returns 0 if nothing is found.
|
||||
// This horrible mess is needed because position also returns 0 if nothing is found.
|
||||
// Would be MUCH easier if it instead returned a very large value
|
||||
// Otherwise we could just
|
||||
// Otherwise we could just
|
||||
// order by LEAST(POSITION($search IN xchan_name),POSITION($search IN xchan_addr)).
|
||||
|
||||
$order_extra2 = "CASE WHEN xchan_name LIKE "
|
||||
. protect_sprintf( "'%" . dbesc($search) . "%'" )
|
||||
. " then POSITION('" . protect_sprintf(dbesc($search))
|
||||
$order_extra2 = "CASE WHEN xchan_name LIKE "
|
||||
. protect_sprintf( "'%" . dbesc($search) . "%'" )
|
||||
. " then POSITION('" . protect_sprintf(dbesc($search))
|
||||
. "' IN xchan_name) else position('" . protect_sprintf(dbesc(punify($search))) . "' IN xchan_addr) end, ";
|
||||
|
||||
$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();
|
||||
|
||||
|
||||
if($type == '' || $type == 'g') {
|
||||
|
||||
// virtual groups based on private profile viewing ability
|
||||
|
||||
$r = q("select id, profile_guid, profile_name from profile where is_default = 0 and uid = %d",
|
||||
intval(local_channel())
|
||||
);
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rv) {
|
||||
$groups[] = array(
|
||||
@@ -130,19 +132,19 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
// Normal privacy groups
|
||||
|
||||
$r = q("SELECT pgrp.id, pgrp.hash, pgrp.gname
|
||||
FROM pgrp, pgrp_member
|
||||
WHERE pgrp.deleted = 0 AND pgrp.uid = %d
|
||||
FROM pgrp, pgrp_member
|
||||
WHERE pgrp.deleted = 0 AND pgrp.uid = %d
|
||||
AND pgrp_member.gid = pgrp.id
|
||||
$sql_extra
|
||||
GROUP BY pgrp.id
|
||||
ORDER BY pgrp.gname
|
||||
ORDER BY pgrp.gname
|
||||
LIMIT %d OFFSET %d",
|
||||
intval(local_channel()),
|
||||
intval($count),
|
||||
intval($start)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
if($r) {
|
||||
foreach($r as $g){
|
||||
// logger('acl: group: ' . $g['gname'] . ' members: ' . group_get_members_xchan($g['id']));
|
||||
$groups[] = array(
|
||||
@@ -157,22 +159,22 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if($type == '' || $type == 'c' || $type === 'f') {
|
||||
|
||||
$extra_channels_sql = '';
|
||||
$extra_channels_sql = '';
|
||||
|
||||
// Only include channels who allow the observer to view their connections
|
||||
if($extra_channels) {
|
||||
foreach($extra_channels as $channel) {
|
||||
if(perm_is_allowed(intval($channel), get_observer_hash(),'view_contacts')) {
|
||||
if($extra_channel_sql)
|
||||
if($extra_channels_sql)
|
||||
$extra_channels_sql .= ',';
|
||||
$extra_channels_sql .= intval($channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Getting info from the abook is better for local users because it contains info about permissions
|
||||
if(local_channel()) {
|
||||
if($extra_channels_sql != '')
|
||||
@@ -199,7 +201,7 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
$r2 = array();
|
||||
foreach($r1 as $rr) {
|
||||
$x = atoken_xchan($rr);
|
||||
$r2[] = [
|
||||
$r2[] = [
|
||||
'id' => 'a' . $rr['atoken_id'] ,
|
||||
'hash' => $x['xchan_hash'],
|
||||
'name' => $x['xchan_name'],
|
||||
@@ -211,12 +213,12 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
'abook_self' => 0
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add connections
|
||||
|
||||
$r = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, xchan_pubforum, abook_flags, abook_self
|
||||
FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
|
||||
$r = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, xchan_pubforum, abook_flags, abook_self
|
||||
FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
WHERE (abook_channel = %d $extra_channels_sql) AND abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc" ,
|
||||
intval(local_channel())
|
||||
);
|
||||
@@ -225,28 +227,28 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
|
||||
}
|
||||
else { // Visitors
|
||||
$r = q("SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self
|
||||
$r = q("SELECT xchan_hash as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self
|
||||
FROM xchan left join xlink on xlink_link = xchan_hash
|
||||
WHERE xlink_xchan = '%s' AND xchan_deleted = 0 $sql_extra2_xchan order by $order_extra2 xchan_name asc" ,
|
||||
dbesc(get_observer_hash())
|
||||
);
|
||||
|
||||
|
||||
// Find contacts of extra channels
|
||||
// This is probably more complicated than it needs to be
|
||||
if($extra_channels_sql) {
|
||||
// Build a list of hashes that we got previously so we don't get them again
|
||||
$known_hashes = array("'".get_observer_hash()."'");
|
||||
if($r)
|
||||
foreach($r as $rr)
|
||||
foreach($r as $rr)
|
||||
$known_hashes[] = "'".$rr['hash']."'";
|
||||
$known_hashes_sql = 'AND xchan_hash not in ('.join(',',$known_hashes).')';
|
||||
|
||||
$r2 = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, abook_flags, abook_self
|
||||
FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
|
||||
$r2 = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, abook_flags, abook_self
|
||||
FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
WHERE abook_channel IN ($extra_channels_sql) $known_hashes_sql AND abook_blocked = 0 and abook_pending = 0 and abook_hidden = 0 and xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc");
|
||||
if($r2)
|
||||
$r = array_merge($r,$r2);
|
||||
|
||||
|
||||
// Sort accoring to match position, then alphabetically. This could be avoided if the above two SQL queries could be combined into one, and the sorting could be done on the SQl server (like in the case of a local user)
|
||||
$matchpos = function($x) use($search) {
|
||||
$namepos = strpos($x['name'],$search);
|
||||
@@ -269,23 +271,24 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
}
|
||||
if((count($r) < 100) && $type == 'c') {
|
||||
$r2 = q("SELECT substr(xchan_hash,1,18) as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self
|
||||
FROM xchan
|
||||
$r2 = q("SELECT substr(xchan_hash,1,18) as id, xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self
|
||||
FROM xchan
|
||||
WHERE xchan_deleted = 0 and not xchan_network in ('rss','anon','unknown') $sql_extra2_xchan order by $order_extra2 xchan_name asc"
|
||||
);
|
||||
if($r2) {
|
||||
$r = array_merge($r,$r2);
|
||||
$r = unique_multidim_array($r,'hash');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif($type == 'm') {
|
||||
|
||||
$r = array();
|
||||
$z = q("SELECT xchan_hash as hash, xchan_name as name, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url
|
||||
$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
|
||||
FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
WHERE abook_channel = %d
|
||||
WHERE abook_channel = %d
|
||||
and xchan_deleted = 0
|
||||
and xchan_network IN ('zot', 'diaspora', 'friendica-over-diaspora')
|
||||
$sql_extra3
|
||||
ORDER BY xchan_name ASC ",
|
||||
intval(local_channel())
|
||||
@@ -297,18 +300,18 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
elseif($type == 'a') {
|
||||
|
||||
$r = q("SELECT abook_id as id, xchan_name as name, xchan_hash as hash, xchan_addr as nick, xchan_photo_s as micro, xchan_network as network, 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 FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
WHERE abook_channel = %d
|
||||
and xchan_deleted = 0
|
||||
$sql_extra3
|
||||
ORDER BY xchan_name ASC ",
|
||||
intval(local_channel())
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
elseif($type == 'x') {
|
||||
$r = $this->navbar_complete($a);
|
||||
@@ -322,7 +325,7 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$o = array(
|
||||
'start' => $start,
|
||||
'count' => $count,
|
||||
@@ -333,27 +336,34 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
else
|
||||
$r = array();
|
||||
|
||||
|
||||
if($r) {
|
||||
$i = count($contacts);
|
||||
$x = [];
|
||||
foreach($r as $g) {
|
||||
|
||||
if(in_array($g['network'],['rss','anon','unknown']) && ($type != 'a'))
|
||||
|
||||
if(in_array($g['net'],['rss','anon','unknown']) && ($type != 'a'))
|
||||
continue;
|
||||
|
||||
$g['hash'] = urlencode($g['hash']);
|
||||
|
||||
|
||||
if(! $g['nick']) {
|
||||
$g['nick'] = $g['url'];
|
||||
}
|
||||
|
||||
$clink = ($g['nick']) ? $g['nick'] : $g['url'];
|
||||
$lkey = md5($clink);
|
||||
if (! array_key_exists($lkey, $x))
|
||||
$x[$lkey] = $i;
|
||||
|
||||
if(in_array($g['hash'],$permitted) && $type === 'f' && (! $noforums)) {
|
||||
$contacts[] = array(
|
||||
$contacts[$i] = array(
|
||||
"type" => "c",
|
||||
"photo" => "images/twopeople.png",
|
||||
"name" => $g['name'],
|
||||
"id" => urlencode($g['id']),
|
||||
"xid" => $g['hash'],
|
||||
"link" => (($g['nick']) ? $g['nick'] : $g['url']),
|
||||
"link" => $clink,
|
||||
"nick" => substr($g['nick'],0,strpos($g['nick'],'@')),
|
||||
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
|
||||
"taggable" => 'taggable',
|
||||
@@ -361,24 +371,28 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
);
|
||||
}
|
||||
if($type !== 'f') {
|
||||
$contacts[] = array(
|
||||
"type" => "c",
|
||||
"photo" => $g['micro'],
|
||||
"name" => $g['name'],
|
||||
"id" => urlencode($g['id']),
|
||||
"xid" => $g['hash'],
|
||||
"link" => (($g['nick']) ? $g['nick'] : $g['url']),
|
||||
"nick" => ((strpos($g['nick'],'@')) ? substr($g['nick'],0,strpos($g['nick'],'@')) : $g['nick']),
|
||||
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
|
||||
"taggable" => '',
|
||||
"label" => '',
|
||||
);
|
||||
if (! array_key_exists($x[$lkey], $contacts) || ($contacts[$x[$lkey]]['net'] !== 'zot6' && ($g['net'] == 'zot6' || $g['net'] == 'zot'))) {
|
||||
$contacts[$x[$lkey]] = array(
|
||||
"type" => "c",
|
||||
"photo" => $g['micro'],
|
||||
"name" => $g['name'],
|
||||
"id" => urlencode($g['id']),
|
||||
"xid" => $g['hash'],
|
||||
"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']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$items = array_merge($groups, $contacts);
|
||||
|
||||
|
||||
$o = array(
|
||||
'start' => $start,
|
||||
'count' => $count,
|
||||
@@ -392,50 +406,50 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
|
||||
|
||||
function navbar_complete(&$a) {
|
||||
|
||||
|
||||
// logger('navbar_complete');
|
||||
|
||||
|
||||
if(observer_prohibited()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$dirmode = intval(get_config('system','directory_mode'));
|
||||
$search = ((x($_REQUEST,'search')) ? htmlentities($_REQUEST['search'],ENT_COMPAT,'UTF-8',false) : '');
|
||||
if(! $search || mb_strlen($search) < 2)
|
||||
return array();
|
||||
|
||||
|
||||
$star = false;
|
||||
$address = false;
|
||||
|
||||
|
||||
if(substr($search,0,1) === '@')
|
||||
$search = substr($search,1);
|
||||
|
||||
|
||||
if(substr($search,0,1) === '*') {
|
||||
$star = true;
|
||||
$search = substr($search,1);
|
||||
}
|
||||
|
||||
|
||||
if(strpos($search,'@') !== false) {
|
||||
$address = true;
|
||||
}
|
||||
|
||||
|
||||
if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) {
|
||||
$url = z_root() . '/dirsearch';
|
||||
}
|
||||
|
||||
|
||||
if(! $url) {
|
||||
require_once("include/dir_fns.php");
|
||||
$directory = find_upstream_directory($dirmode);
|
||||
$directory = Libzotdir::find_upstream_directory($dirmode);
|
||||
$url = $directory['url'] . '/dirsearch';
|
||||
}
|
||||
|
||||
$token = get_config('system','realm_token');
|
||||
|
||||
|
||||
$count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 100);
|
||||
if($url) {
|
||||
$query = $url . '?f=' . (($token) ? '&t=' . urlencode($token) : '');
|
||||
$query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode(punify($search)) : '');
|
||||
|
||||
|
||||
$x = z_fetch_url($query);
|
||||
if($x['success']) {
|
||||
$t = 0;
|
||||
|
||||
288
Zotlabs/Module/Activity.php
Normal file
288
Zotlabs/Module/Activity.php
Normal file
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use Zotlabs\Lib\IConfig;
|
||||
use Zotlabs\Lib\Enotify;
|
||||
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;
|
||||
use App;
|
||||
|
||||
|
||||
class Activity extends Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
if (Libzot::is_zot_request()) {
|
||||
|
||||
$item_id = argv(1);
|
||||
if (! $item_id)
|
||||
http_status_exit(404, 'Not found');
|
||||
|
||||
$portable_id = EMPTY_STR;
|
||||
|
||||
$item_normal_extra = sprintf(" and not verb in ('%s', '%s') ",
|
||||
dbesc(ACTIVITY_FOLLOW),
|
||||
dbesc(ACTIVITY_UNFOLLOW)
|
||||
);
|
||||
|
||||
$item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 $item_normal_extra ";
|
||||
|
||||
$i = null;
|
||||
|
||||
// do we have the item (at all)?
|
||||
|
||||
$r = q("select * from item where mid = '%s' $item_normal limit 1",
|
||||
dbesc(z_root() . '/activity/' . $item_id)
|
||||
);
|
||||
|
||||
if (! $r) {
|
||||
http_status_exit(404,'Not found');
|
||||
}
|
||||
|
||||
// process an authenticated fetch
|
||||
|
||||
$sigdata = HTTPSig::verify(EMPTY_STR);
|
||||
if($sigdata['portable_id'] && $sigdata['header_valid']) {
|
||||
$portable_id = $sigdata['portable_id'];
|
||||
observer_auth($portable_id);
|
||||
|
||||
// first see if we have a copy of this item's parent owned by the current signer
|
||||
// include xchans for all zot-like networks - these will have the same guid and public key
|
||||
|
||||
$x = q("select * from xchan where xchan_hash = '%s'",
|
||||
dbesc($sigdata['portable_id'])
|
||||
);
|
||||
|
||||
if ($x) {
|
||||
$xchans = q("select xchan_hash from xchan where xchan_hash = '%s' OR ( xchan_guid = '%s' AND xchan_pubkey = '%s' ) ",
|
||||
dbesc($sigdata['portable_id']),
|
||||
dbesc($x[0]['xchan_guid']),
|
||||
dbesc($x[0]['xchan_pubkey'])
|
||||
);
|
||||
|
||||
if ($xchans) {
|
||||
$hashes = ids_to_querystr($xchans,'xchan_hash',true);
|
||||
$i = q("select id as item_id from item where mid = '%s' $item_normal and owner_xchan in ( " . protect_sprintf($hashes) . " ) limit 1",
|
||||
dbesc($r[0]['parent_mid'])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we don't have a parent id belonging to the signer see if we can obtain one as a visitor that we have permission to access
|
||||
// with a bias towards those items owned by channels on this site (item_wall = 1)
|
||||
|
||||
$sql_extra = item_permissions_sql(0);
|
||||
|
||||
if (! $i) {
|
||||
$i = q("select id as item_id from item where mid = '%s' $item_normal $sql_extra order by item_wall desc limit 1",
|
||||
dbesc($r[0]['parent_mid'])
|
||||
);
|
||||
}
|
||||
|
||||
if(! $i) {
|
||||
http_status_exit(403,'Forbidden');
|
||||
}
|
||||
|
||||
$parents_str = ids_to_querystr($i,'item_id');
|
||||
|
||||
$items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal ",
|
||||
dbesc($parents_str)
|
||||
);
|
||||
|
||||
if(! $items) {
|
||||
http_status_exit(404, 'Not found');
|
||||
}
|
||||
|
||||
xchan_query($items,true);
|
||||
$items = fetch_post_tags($items,true);
|
||||
|
||||
$observer = App::get_observer();
|
||||
$parent = $items[0];
|
||||
$recips = (($parent['owner']['xchan_network'] === 'activitypub') ? get_iconfig($parent['id'],'activitypub','recips', []) : []);
|
||||
$to = (($recips && array_key_exists('to',$recips) && is_array($recips['to'])) ? $recips['to'] : null);
|
||||
$nitems = [];
|
||||
foreach($items as $i) {
|
||||
|
||||
$mids = [];
|
||||
|
||||
if(intval($i['item_private'])) {
|
||||
if(! $observer) {
|
||||
continue;
|
||||
}
|
||||
// ignore private reshare, possibly from hubzilla
|
||||
if($i['verb'] === 'Announce') {
|
||||
if(! in_array($i['thr_parent'],$mids)) {
|
||||
$mids[] = $i['thr_parent'];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// also ignore any children of the private reshares
|
||||
if(in_array($i['thr_parent'],$mids)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if((! $to) || (! in_array($observer['xchan_url'],$to))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
$nitems[] = $i;
|
||||
}
|
||||
|
||||
if(! $nitems)
|
||||
http_status_exit(404, 'Not found');
|
||||
|
||||
$chan = channelx_by_n($nitems[0]['uid']);
|
||||
|
||||
if(! $chan)
|
||||
http_status_exit(404, 'Not found');
|
||||
|
||||
if(! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream'))
|
||||
http_status_exit(403, 'Forbidden');
|
||||
|
||||
$i = ZlibActivity::encode_item_collection($nitems,'conversation/' . $item_id,'OrderedCollection');
|
||||
if($portable_id && (! intval($items[0]['item_private']))) {
|
||||
ThreadListener::store(z_root() . '/activity/' . $item_id, $portable_id);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
if(ActivityStreams::is_as_request()) {
|
||||
|
||||
$item_id = argv(1);
|
||||
|
||||
if (! $item_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ob_authorise = false;
|
||||
$item_uid = 0;
|
||||
|
||||
$bear = ZlibActivity::token_from_request();
|
||||
if ($bear) {
|
||||
logger('bear: ' . $bear, LOGGER_DEBUG);
|
||||
$t = q("select item.uid, iconfig.v from iconfig left join item on iid = item.id where cat = 'ocap' and item.uuid = '%s'",
|
||||
dbesc($item_id)
|
||||
);
|
||||
if ($t) {
|
||||
foreach ($t as $token) {
|
||||
if ($token['v'] === $bear) {
|
||||
$ob_authorize = true;
|
||||
$item_uid = $token['uid'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$item_normal_extra = sprintf(" and not verb in ('%s', '%s') ",
|
||||
dbesc(ACTIVITY_FOLLOW),
|
||||
dbesc(ACTIVITY_UNFOLLOW)
|
||||
);
|
||||
|
||||
$item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 $item_normal_extra ";
|
||||
|
||||
$sigdata = HTTPSig::verify(EMPTY_STR);
|
||||
if ($sigdata['portable_id'] && $sigdata['header_valid']) {
|
||||
$portable_id = $sigdata['portable_id'];
|
||||
if (! check_channelallowed($portable_id)) {
|
||||
http_status_exit(403, 'Permission denied');
|
||||
}
|
||||
if (! check_siteallowed($sigdata['signer'])) {
|
||||
http_status_exit(403, 'Permission denied');
|
||||
}
|
||||
observer_auth($portable_id);
|
||||
}
|
||||
|
||||
// if passed an owner_id of 0 to item_permissions_sql(), we force "guest access" or observer checking
|
||||
// Give ocap tokens priority
|
||||
|
||||
if ($ob_authorize) {
|
||||
$sql_extra = " and item.uid = " . intval($token['uid']) . " ";
|
||||
}
|
||||
else {
|
||||
$sql_extra = item_permissions_sql(0);
|
||||
}
|
||||
|
||||
$r = q("select * from item where uuid = '%s' $item_normal $sql_extra limit 1",
|
||||
dbesc($item_id)
|
||||
);
|
||||
|
||||
if (! $r) {
|
||||
$r = q("select * from item where uuid = '%s' $item_normal limit 1",
|
||||
dbesc($item_id)
|
||||
);
|
||||
if($r) {
|
||||
http_status_exit(403, 'Forbidden');
|
||||
}
|
||||
http_status_exit(404, 'Not found');
|
||||
}
|
||||
|
||||
xchan_query($r,true);
|
||||
$items = fetch_post_tags($r,false);
|
||||
|
||||
if ($portable_id && (! intval($items[0]['item_private']))) {
|
||||
$c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s'",
|
||||
intval($items[0]['uid']),
|
||||
dbesc($portable_id)
|
||||
);
|
||||
if (! $c) {
|
||||
ThreadListener::store(z_root() . '/activity/' . $item_id, $portable_id);
|
||||
}
|
||||
}
|
||||
|
||||
$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();
|
||||
|
||||
}
|
||||
|
||||
goaway(z_root() . '/item/' . argv(1));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -101,11 +101,14 @@ class Admin extends \Zotlabs\Web\Controller {
|
||||
|
||||
// pending registrations
|
||||
|
||||
$pdg = q("SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d ) > 0 ",
|
||||
intval(ACCOUNT_PENDING)
|
||||
// $pdg = q("SELECT account.*, register.reg_hash from account left join register on account_id = register.reg_uid // where (account_flags & %d ) > 0 ",
|
||||
// intval(ACCOUNT_PENDING)
|
||||
// );
|
||||
$pdg = q("SELECT COUNT(*) AS pdg FROM register WHERE reg_vital = 1 AND reg_expires > '%s' ",
|
||||
dbesc(date('Y-m-d H:i:s'))
|
||||
);
|
||||
|
||||
$pending = (($pdg) ? count($pdg) : 0);
|
||||
$pending = ($pdg ? $pdg[0]['pdg'] : 0);
|
||||
|
||||
// available channels, primary and clones
|
||||
$channels = array();
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
class Accounts {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handle POST actions on accounts admin page.
|
||||
*
|
||||
@@ -15,14 +15,105 @@ class Accounts {
|
||||
*
|
||||
*/
|
||||
|
||||
const MYP = 'ZAR'; // ZAR2x
|
||||
const VERSION = '2.0.0';
|
||||
|
||||
function post() {
|
||||
|
||||
$pending = ( x($_POST, 'pending') ? $_POST['pending'] : array() );
|
||||
$users = ( x($_POST, 'user') ? $_POST['user'] : array() );
|
||||
$blocked = ( x($_POST, 'blocked') ? $_POST['blocked'] : array() );
|
||||
|
||||
|
||||
check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts');
|
||||
|
||||
|
||||
$isajax = is_ajax();
|
||||
$rc = 0;
|
||||
|
||||
If (!is_site_admin()) {
|
||||
if ($isajax) {
|
||||
killme();
|
||||
exit;
|
||||
}
|
||||
goaway(z_root() . '/');
|
||||
}
|
||||
|
||||
if ($isajax) {
|
||||
//$debug = print_r($_SESSION[self::MYP],true);
|
||||
$zarop = (x($_POST['zardo']) && preg_match('/^[ad]{1,1}$/', $_POST['zardo']) )
|
||||
? $_POST['zardo'] : '';
|
||||
// zarat arrives with leading underscore _n
|
||||
$zarat = (x($_POST['zarat']) && preg_match('/^_{1,1}[0-9]{1,6}$/', $_POST['zarat']) )
|
||||
? substr($_POST['zarat'],1) : '';
|
||||
$zarse = (x($_POST['zarse']) && preg_match('/^[0-9a-f]{8,8}$/', $_POST['zarse']) )
|
||||
? hex2bin($_POST['zarse']) : '';
|
||||
|
||||
if ($zarop && $zarat >= 0 && $zarse && $zarse == $_SESSION[self::MYP]['h'][$zarat]) {
|
||||
|
||||
//
|
||||
if ($zarop == 'd') {
|
||||
$rd = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d AND SUBSTR(reg_hash,1,4) = '%s' ",
|
||||
intval($_SESSION[self::MYP]['i'][$zarat]),
|
||||
dbesc($_SESSION[self::MYP]['h'][$zarat])
|
||||
);
|
||||
$rc = '×';
|
||||
}
|
||||
elseif ($zarop == 'a') {
|
||||
// approval, REGISTER_DENIED by user 0x0040, REGISTER_AGREED by user 0x0020 @Regate
|
||||
$rd = q("UPDATE register SET reg_flags = (reg_flags & ~ 16), "
|
||||
. " reg_vital = (CASE (reg_flags & ~ 48) WHEN 0 THEN 0 ELSE 1 END) "
|
||||
. " WHERE reg_vital = 1 AND reg_id = %d AND SUBSTR(reg_hash,1,4) = '%s' ",
|
||||
intval($_SESSION[self::MYP]['i'][$zarat]),
|
||||
dbesc($_SESSION[self::MYP]['h'][$zarat])
|
||||
);
|
||||
$rc = 0;
|
||||
$rs = q("SELECT * from register WHERE reg_id = %d ",
|
||||
intval($_SESSION[self::MYP]['i'][$zarat])
|
||||
);
|
||||
if ($rs && ($rs[0]['reg_flags'] & ~ 48) == 0) {
|
||||
// create account
|
||||
$rc = 'ok'.$rs[0]['reg_id'];
|
||||
$ac = create_account_from_register($rs[0]);
|
||||
if ( $ac['success'] ) {
|
||||
$rc .= '✔';
|
||||
|
||||
$auto_create = get_config('system','auto_channel_create',1);
|
||||
|
||||
if($auto_create) {
|
||||
$reonar = json_decode($rs[0]['reg_stuff'], true);
|
||||
// prepare channel creation
|
||||
if($reonar['chan.name'])
|
||||
set_aconfig($ac['account']['account_id'], 'register', 'channel_name', $reonar['chan.name']);
|
||||
|
||||
if($reonar['chan.did1'])
|
||||
set_aconfig($ac['account']['account_id'], 'register', 'channel_address', $reonar['chan.did1']);
|
||||
|
||||
$permissions_role = get_config('system','default_permissions_role');
|
||||
if($permissions_role)
|
||||
set_aconfig($ac['account']['account_id'], 'register', 'permissions_role', $permissions_role);
|
||||
|
||||
// create channel
|
||||
$new_channel = auto_channel_create($ac['account']['account_id']);
|
||||
|
||||
if($new_channel['success']) {
|
||||
$rc .= ' c,ok' . $new_channel['channel']['channel_id'] . '✔';
|
||||
}
|
||||
else {
|
||||
$rc .= ' c ×';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
$rc='oh ×';
|
||||
}
|
||||
}
|
||||
echo json_encode(array('re' => $zarop, 'at' => '_' . $zarat, 'rc' => $rc));
|
||||
}
|
||||
killme();
|
||||
exit;
|
||||
}
|
||||
|
||||
// change to switch structure?
|
||||
// account block/unblock button was submitted
|
||||
if (x($_POST, 'page_accounts_block')) {
|
||||
@@ -55,7 +146,7 @@ class Accounts {
|
||||
account_deny($hash);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
goaway(z_root() . '/admin/accounts' );
|
||||
}
|
||||
|
||||
@@ -75,19 +166,21 @@ class Accounts {
|
||||
$account = q("SELECT * FROM account WHERE account_id = %d",
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
|
||||
if (! $account) {
|
||||
notice( t('Account not found') . EOL);
|
||||
goaway(z_root() . '/admin/accounts' );
|
||||
}
|
||||
|
||||
|
||||
check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts', 't');
|
||||
|
||||
|
||||
$debug = '';
|
||||
|
||||
switch (argv(2)){
|
||||
case 'delete':
|
||||
// delete user
|
||||
account_remove($uid,true,false);
|
||||
|
||||
|
||||
notice( sprintf(t("Account '%s' deleted"), $account[0]['account_email']) . EOL);
|
||||
break;
|
||||
case 'block':
|
||||
@@ -95,7 +188,7 @@ class Accounts {
|
||||
intval(ACCOUNT_BLOCKED),
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
|
||||
notice( sprintf( t("Account '%s' blocked") , $account[0]['account_email']) . EOL);
|
||||
break;
|
||||
case 'unblock':
|
||||
@@ -103,27 +196,74 @@ class Accounts {
|
||||
intval(ACCOUNT_BLOCKED),
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
|
||||
notice( sprintf( t("Account '%s' unblocked"), $account[0]['account_email']) . EOL);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
goaway(z_root() . '/admin/accounts' );
|
||||
}
|
||||
|
||||
/* get pending */
|
||||
$pending = q("SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d )>0 ",
|
||||
intval(ACCOUNT_PENDING)
|
||||
);
|
||||
|
||||
|
||||
$tao = 'tao.zar.zarax = ' . "'" . '<img src="' . z_root() . '/images/zapax16.gif">' . "';\n";
|
||||
|
||||
|
||||
// by default we will only return verified results. if reg_all is set we will return everything''
|
||||
$get_all = isset($_REQUEST['get_all']);
|
||||
$pending = get_pending_accounts($get_all);
|
||||
|
||||
unset($_SESSION[self::MYP]);
|
||||
|
||||
if ($pending) {
|
||||
// collect and group all ip
|
||||
$atips = dbq("SELECT reg_atip AS atip, COUNT(reg_atip) AS atips FROM register
|
||||
WHERE reg_vital = 1 GROUP BY reg_atip"
|
||||
);
|
||||
|
||||
(($atips) ? $atipn = array_column($atips, 'atips', 'atip') : $atipn = ['' => 0]);
|
||||
|
||||
$tao .= 'tao.zar.zarar = {';
|
||||
foreach ($pending as $n => $v) {
|
||||
|
||||
$stuff = json_decode($v['reg_stuff'], true);
|
||||
|
||||
if(isset($stuff['msg'])) {
|
||||
$pending[$n]['msg'] = $stuff['msg'];
|
||||
}
|
||||
|
||||
if (array_key_exists($v['reg_atip'], $atipn)) {
|
||||
$pending[$n]['reg_atip'] = $v['reg_atip'];
|
||||
$pending[$n]['reg_atip_n'] = $atipn[$v['reg_atip']];
|
||||
}
|
||||
|
||||
$pending[$n]['status'] = '';
|
||||
if($pending[$n]['reg_flags'] & ACCOUNT_UNVERIFIED > 0)
|
||||
$pending[$n]['status'] = [t('Unverified'), 'bg-warning'];
|
||||
|
||||
if($pending[$n]['status'] && $pending[$n]['reg_expires'] < datetime_convert())
|
||||
$pending[$n]['status'] = [t('Expired'), 'bg-danger text-white'];
|
||||
|
||||
// timezone adjust date_time for display
|
||||
$pending[$n]['reg_created'] = datetime_convert('UTC', date_default_timezone_get(), $pending[$n]['reg_created']);
|
||||
$pending[$n]['reg_startup'] = datetime_convert('UTC', date_default_timezone_get(), $pending[$n]['reg_startup']);
|
||||
$pending[$n]['reg_expires'] = datetime_convert('UTC', date_default_timezone_get(), $pending[$n]['reg_expires']);
|
||||
|
||||
// better secure
|
||||
$tao .= $n . ": '" . substr(bin2hex($v['reg_hash']),0,8) . "',";
|
||||
$_SESSION[self::MYP]['h'][] = substr($v['reg_hash'],0,4);
|
||||
$_SESSION[self::MYP]['i'][] = $v['reg_id'];
|
||||
}
|
||||
$tao = rtrim($tao,',') . '};' . "\n";
|
||||
}
|
||||
// <- hilmar]
|
||||
|
||||
/* get accounts */
|
||||
|
||||
|
||||
$total = q("SELECT count(*) as total FROM account");
|
||||
if (count($total)) {
|
||||
\App::set_pager_total($total[0]['total']);
|
||||
\App::set_pager_itemspage(100);
|
||||
}
|
||||
|
||||
|
||||
$serviceclass = (($_REQUEST['class']) ? " and account_service_class = '" . dbesc($_REQUEST['class']) . "' " : '');
|
||||
|
||||
$key = (($_REQUEST['key']) ? dbesc($_REQUEST['key']) : 'account_id');
|
||||
@@ -134,8 +274,8 @@ class Accounts {
|
||||
$base = z_root() . '/admin/accounts?f=';
|
||||
$odir = (($dir === 'asc') ? '0' : '1');
|
||||
|
||||
$users = q("SELECT account_id , account_email, account_lastlog, account_created, account_expires, account_service_class, ( account_flags & %d ) > 0 as blocked,
|
||||
(SELECT %s FROM channel as ch WHERE ch.channel_account_id = ac.account_id and ch.channel_removed = 0 ) as channels FROM account as ac
|
||||
$users = q("SELECT account_id , account_email, account_lastlog, account_created, account_expires, account_service_class, ( account_flags & %d ) > 0 as blocked,
|
||||
(SELECT %s FROM channel as ch WHERE ch.channel_account_id = ac.account_id and ch.channel_removed = 0 ) as channels FROM account as ac
|
||||
where true $serviceclass and account_flags != %d order by $key $dir limit %d offset %d ",
|
||||
intval(ACCOUNT_BLOCKED),
|
||||
db_concat('ch.channel_address', ' '),
|
||||
@@ -143,15 +283,15 @@ class Accounts {
|
||||
intval(\App::$pager['itemspage']),
|
||||
intval(\App::$pager['start'])
|
||||
);
|
||||
|
||||
|
||||
// function _setup_users($e){
|
||||
// $accounts = Array(
|
||||
// t('Normal Account'),
|
||||
// t('Normal Account'),
|
||||
// t('Soapbox Account'),
|
||||
// t('Community/Celebrity Account'),
|
||||
// t('Automatic Friend Account')
|
||||
// );
|
||||
|
||||
|
||||
// $e['page_flags'] = $accounts[$e['page-flags']];
|
||||
// $e['register_date'] = relative_date($e['register_date']);
|
||||
// $e['login_date'] = relative_date($e['login_date']);
|
||||
@@ -159,49 +299,57 @@ class Accounts {
|
||||
// return $e;
|
||||
// }
|
||||
// $users = array_map("_setup_users", $users);
|
||||
|
||||
|
||||
$t = get_markup_template('admin_accounts.tpl');
|
||||
$o = replace_macros($t, array(
|
||||
// strings //
|
||||
'$debug' => $debug,
|
||||
'$title' => t('Administration'),
|
||||
'$page' => t('Accounts'),
|
||||
'$submit' => t('Submit'),
|
||||
'$select_all' => t('select all'),
|
||||
'$h_pending' => t('Registrations waiting for confirm'),
|
||||
'$th_pending' => array( t('Request date'), t('Email') ),
|
||||
'$no_pending' => t('No registrations.'),
|
||||
'$get_all' => (($get_all) ? t('Show verified registrations') : t('Show all registrations')),
|
||||
'$get_all_link' => (($get_all) ? z_root() .'/admin/accounts' : z_root() .'/admin/accounts?get_all'),
|
||||
'$sel_tall' => t('Select toggle'),
|
||||
'$sel_deny' => t('Deny selected'),
|
||||
'$sel_aprv' => t('Approve selected'),
|
||||
'$h_pending' => (($get_all) ? t('All registrations') : t('Verified registrations waiting for approval')),
|
||||
'$th_pending' => array(t('Request date'), 'dId2', t('Email'), 'IP', t('Requests')),
|
||||
'$no_pending' => (($get_all) ? t('No registrations available') : t('No verified registrations available')),
|
||||
'$approve' => t('Approve'),
|
||||
'$deny' => t('Deny'),
|
||||
'$delete' => t('Delete'),
|
||||
'$block' => t('Block'),
|
||||
'$unblock' => t('Unblock'),
|
||||
'$verified' => t('Verified'),
|
||||
'$not_verified' => t('Not yet verified'),
|
||||
'$odir' => $odir,
|
||||
'$base' => $base,
|
||||
'$h_users' => t('Accounts'),
|
||||
'$th_users' => array(
|
||||
'$th_users' => array(
|
||||
[ t('ID'), 'account_id' ],
|
||||
[ t('Email'), 'account_email' ],
|
||||
[ t('All Channels'), 'channels' ],
|
||||
[ t('All channels'), 'channels' ],
|
||||
[ t('Register date'), 'account_created' ],
|
||||
[ t('Last login'), 'account_lastlog' ],
|
||||
[ t('Expires'), 'account_expires' ],
|
||||
[ t('Service Class'), 'account_service_class'] ),
|
||||
|
||||
'$confirm_delete_multi' => t('Selected accounts will be deleted!\n\nEverything these accounts had posted on this site will be permanently deleted!\n\nAre you sure?'),
|
||||
'$confirm_delete' => t('The account {0} will be deleted!\n\nEverything this account has posted on this site will be permanently deleted!\n\nAre you sure?'),
|
||||
|
||||
[ t('Service class'), 'account_service_class'] ),
|
||||
|
||||
'$confirm_delete_multi' => p2j(t('Selected accounts will be deleted!\n\nEverything these accounts had posted on this site will be permanently deleted!\n\nAre you sure?')),
|
||||
'$confirm_delete' => p2j(t('The account {0} will be deleted!\n\nEverything this account has posted on this site will be permanently deleted!\n\nAre you sure?')),
|
||||
|
||||
'$form_security_token' => get_form_security_token("admin_accounts"),
|
||||
|
||||
|
||||
// values //
|
||||
'$baseurl' => z_root(),
|
||||
|
||||
'$pending' => $pending,
|
||||
'$users' => $users,
|
||||
'$baseurl' => z_root(),
|
||||
'$tao' => $tao,
|
||||
'$pending' => $pending,
|
||||
'$users' => $users,
|
||||
'$msg' => t('Message')
|
||||
));
|
||||
$o .= paginate($a);
|
||||
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
use App;
|
||||
use \Zotlabs\Storage\GitRepo;
|
||||
use \Michelf\MarkdownExtra;
|
||||
|
||||
@@ -17,7 +18,7 @@ class Addons {
|
||||
@include_once("addon/" . argv(2) . "/" . argv(2) . ".php");
|
||||
if(function_exists(argv(2).'_plugin_admin_post')) {
|
||||
$func = argv(2) . '_plugin_admin_post';
|
||||
$func($a);
|
||||
$func();
|
||||
}
|
||||
|
||||
goaway(z_root() . '/admin/addons/' . argv(2) );
|
||||
@@ -253,14 +254,14 @@ class Addons {
|
||||
* Single plugin
|
||||
*/
|
||||
|
||||
if (\App::$argc == 3){
|
||||
$plugin = \App::$argv[2];
|
||||
if (App::$argc == 3){
|
||||
$plugin = App::$argv[2];
|
||||
if (!is_file("addon/$plugin/$plugin.php")){
|
||||
notice( t("Item not found.") );
|
||||
return '';
|
||||
}
|
||||
|
||||
$enabled = in_array($plugin,\App::$plugins);
|
||||
$enabled = in_array($plugin,App::$plugins);
|
||||
$info = get_plugin_info($plugin);
|
||||
$x = check_plugin_versions($info);
|
||||
|
||||
@@ -268,11 +269,11 @@ class Addons {
|
||||
|
||||
if($enabled && ! $x) {
|
||||
$enabled = false;
|
||||
$idz = array_search($plugin, \App::$plugins);
|
||||
$idz = array_search($plugin, App::$plugins);
|
||||
if ($idz !== false) {
|
||||
unset(\App::$plugins[$idz]);
|
||||
unset(App::$plugins[$idz]);
|
||||
uninstall_plugin($plugin);
|
||||
set_config("system","addon", implode(", ",\App::$plugins));
|
||||
set_config("system","addon", implode(", ",App::$plugins));
|
||||
}
|
||||
}
|
||||
$info['disabled'] = 1-intval($x);
|
||||
@@ -281,19 +282,19 @@ class Addons {
|
||||
check_form_security_token_redirectOnErr('/admin/addons', 'admin_addons', 't');
|
||||
$pinstalled = false;
|
||||
// Toggle plugin status
|
||||
$idx = array_search($plugin, \App::$plugins);
|
||||
$idx = array_search($plugin, App::$plugins);
|
||||
if ($idx !== false){
|
||||
unset(\App::$plugins[$idx]);
|
||||
unset(App::$plugins[$idx]);
|
||||
uninstall_plugin($plugin);
|
||||
$pinstalled = false;
|
||||
info( sprintf( t("Plugin %s disabled."), $plugin ) );
|
||||
} else {
|
||||
\App::$plugins[] = $plugin;
|
||||
App::$plugins[] = $plugin;
|
||||
install_plugin($plugin);
|
||||
$pinstalled = true;
|
||||
info( sprintf( t("Plugin %s enabled."), $plugin ) );
|
||||
}
|
||||
set_config("system","addon", implode(", ",\App::$plugins));
|
||||
set_config("system","addon", implode(", ",App::$plugins));
|
||||
|
||||
if($pinstalled) {
|
||||
@require_once("addon/$plugin/$plugin.php");
|
||||
@@ -305,7 +306,7 @@ class Addons {
|
||||
|
||||
// display plugin details
|
||||
|
||||
if (in_array($plugin, \App::$plugins)){
|
||||
if (in_array($plugin, App::$plugins)){
|
||||
$status = 'on';
|
||||
$action = t('Disable');
|
||||
} else {
|
||||
@@ -331,7 +332,7 @@ class Addons {
|
||||
@require_once("addon/$plugin/$plugin.php");
|
||||
if(function_exists($plugin.'_plugin_admin')) {
|
||||
$func = $plugin.'_plugin_admin';
|
||||
$func($a, $admin_form);
|
||||
$func($admin_form);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,18 +381,18 @@ class Addons {
|
||||
|
||||
list($tmp, $id) = array_map('trim', explode('/', $file));
|
||||
$info = get_plugin_info($id);
|
||||
$enabled = in_array($id,\App::$plugins);
|
||||
$enabled = in_array($id,App::$plugins);
|
||||
$x = check_plugin_versions($info);
|
||||
|
||||
// disable plugins which are installed but incompatible versions
|
||||
|
||||
if($enabled && ! $x) {
|
||||
$enabled = false;
|
||||
$idz = array_search($id, \App::$plugins);
|
||||
$idz = array_search($id, App::$plugins);
|
||||
if ($idz !== false) {
|
||||
unset(\App::$plugins[$idz]);
|
||||
unset(App::$plugins[$idz]);
|
||||
uninstall_plugin($id);
|
||||
set_config("system","addon", implode(", ",\App::$plugins));
|
||||
set_config("system","addon", implode(", ",App::$plugins));
|
||||
}
|
||||
}
|
||||
$info['disabled'] = 1-intval($x);
|
||||
|
||||
@@ -173,4 +173,4 @@ class Channels {
|
||||
return $o;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,47 @@ class Dbsync {
|
||||
info( t('Update has been marked successful') . EOL);
|
||||
goaway(z_root() . '/admin/dbsync');
|
||||
}
|
||||
|
||||
if(argc() > 3 && intval(argv(3)) && argv(2) === 'verify') {
|
||||
|
||||
$s = '_' . intval(argv(3));
|
||||
$cls = '\\Zotlabs\Update\\' . $s ;
|
||||
if(class_exists($cls)) {
|
||||
$c = new $cls();
|
||||
if(method_exists($c,'verify')) {
|
||||
$retval = $c->verify();
|
||||
if($retval === UPDATE_FAILED) {
|
||||
$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);
|
||||
set_config('database',$s, 'success');
|
||||
}
|
||||
else
|
||||
$o .= sprintf( t('Verifying update %s did not return a status. Unknown if it succeeded.'), $s);
|
||||
}
|
||||
else {
|
||||
$o .= sprintf( t('Update %s does not contain a verification function.'), $s );
|
||||
}
|
||||
}
|
||||
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))) {
|
||||
$x = intval(argv(2));
|
||||
$s = '_' . $x;
|
||||
@@ -28,14 +68,14 @@ class Dbsync {
|
||||
$c = new $cls();
|
||||
$retval = $c->run();
|
||||
if($retval === UPDATE_FAILED) {
|
||||
$o .= sprintf( t('Executing %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);
|
||||
set_config('database',$s, 'success');
|
||||
}
|
||||
else
|
||||
$o .= sprintf( t('Update %s did not return a status. Unknown if it succeeded.'), $s);
|
||||
$o .= sprintf( t('Update %s did not return a status. It cannot be determined if it was successful.'), $s);
|
||||
}
|
||||
else
|
||||
$o .= sprintf( t('Update function %s could not be found.'), $s);
|
||||
@@ -59,6 +99,7 @@ class Dbsync {
|
||||
'$banner' => t('Failed Updates'),
|
||||
'$desc' => '',
|
||||
'$mark' => t('Mark success (if update was manually applied)'),
|
||||
'$verify' => t('Attempt to verify this update if a verification procedure exists'),
|
||||
'$apply' => t('Attempt to execute this update step automatically'),
|
||||
'$failed' => $failed
|
||||
));
|
||||
|
||||
@@ -43,6 +43,12 @@ class Security {
|
||||
|
||||
$be = $this->trim_array_elems(explode("\n",$_POST['embed_deny']));
|
||||
set_config('system','embed_deny',$be);
|
||||
|
||||
$thumbnail_security = ((x($_POST,'thumbnail_security')) ? intval($_POST['thumbnail_security']) : 0);
|
||||
set_config('system', 'thumbnail_security' , $thumbnail_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);
|
||||
@@ -86,7 +92,7 @@ class Security {
|
||||
$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.");
|
||||
|
||||
|
||||
$t = get_markup_template('admin_security.tpl');
|
||||
return replace_macros($t, array(
|
||||
'$title' => t('Administration'),
|
||||
@@ -106,7 +112,9 @@ class Security {
|
||||
'$embed_sslonly' => array('embed_sslonly',t('Only allow embeds from secure (SSL) websites and links.'), intval(get_config('system','embed_sslonly')),''),
|
||||
'$embed_allow' => array('embed_allow', t('Allow unfiltered embedded HTML content only from these domains'), $whiteembeds_str, t('One site per line. By default embedded content is filtered.')),
|
||||
'$embed_deny' => array('embed_deny', t('Block embedded HTML from these domains'), $blackembeds_str, ''),
|
||||
|
||||
'$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), '' ],
|
||||
|
||||
// '$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')
|
||||
@@ -128,4 +136,4 @@ class Security {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,25 @@ namespace Zotlabs\Module\Admin;
|
||||
|
||||
class Site {
|
||||
|
||||
|
||||
/**
|
||||
* @brief POST handler for Admin Site Page.
|
||||
*
|
||||
*/
|
||||
function post(){
|
||||
// [hilmar->
|
||||
$this->isajax = is_ajax();
|
||||
$this->eol = $this->isajax ? "\n" : EOL;
|
||||
// ]
|
||||
if (!x($_POST, 'page_site')) {
|
||||
return;
|
||||
// [
|
||||
if (!$this->isajax)
|
||||
// ]
|
||||
return;
|
||||
}
|
||||
// [
|
||||
$this->msgbg = '';
|
||||
// <-hilmar]
|
||||
|
||||
check_form_security_token_redirectOnErr('/admin/site', 'admin_site');
|
||||
|
||||
@@ -24,14 +35,17 @@ class Site {
|
||||
$siteinfo = ((x($_POST,'siteinfo')) ? trim($_POST['siteinfo']) : '');
|
||||
$language = ((x($_POST,'language')) ? notags(trim($_POST['language'])) : '');
|
||||
$theme = ((x($_POST,'theme')) ? notags(trim($_POST['theme'])) : '');
|
||||
// $theme_mobile = ((x($_POST,'theme_mobile')) ? notags(trim($_POST['theme_mobile'])) : '');
|
||||
// $site_channel = ((x($_POST,'site_channel')) ? notags(trim($_POST['site_channel'])) : '');
|
||||
// $theme_mobile = ((x($_POST,'theme_mobile')) ? notags(trim($_POST['theme_mobile'])) : '');
|
||||
// $site_channel = ((x($_POST,'site_channel')) ? notags(trim($_POST['site_channel'])) : '');
|
||||
$maximagesize = ((x($_POST,'maximagesize')) ? intval(trim($_POST['maximagesize'])) : 0);
|
||||
|
||||
$register_policy = ((x($_POST,'register_policy')) ? intval(trim($_POST['register_policy'])) : 0);
|
||||
$register_wo_email = ((x($_POST,'register_wo_email')) ? intval(trim($_POST['register_wo_email'])) : 0);
|
||||
$minimum_age = ((x($_POST,'minimum_age')) ? intval(trim($_POST['minimum_age'])) : 13);
|
||||
$access_policy = ((x($_POST,'access_policy')) ? intval(trim($_POST['access_policy'])) : 0);
|
||||
$invite_only = ((x($_POST,'invite_only')) ? True : False);
|
||||
$reg_autochannel = ((x($_POST,'auto_channel_create')) ? True : False);
|
||||
$invitation_only = ((x($_POST,'invitation_only')) ? True : False);
|
||||
$invitation_also = ((x($_POST,'invitation_also')) ? True : False);
|
||||
$abandon_days = ((x($_POST,'abandon_days')) ? intval(trim($_POST['abandon_days'])) : 0);
|
||||
|
||||
$register_text = ((x($_POST,'register_text')) ? notags(trim($_POST['register_text'])) : '');
|
||||
@@ -62,6 +76,9 @@ class Site {
|
||||
$from_email = ((array_key_exists('from_email',$_POST) && trim($_POST['from_email'])) ? trim($_POST['from_email']) : 'Administrator@' . \App::get_hostname());
|
||||
$from_email_name = ((array_key_exists('from_email_name',$_POST) && trim($_POST['from_email_name'])) ? trim($_POST['from_email_name']) : \Zotlabs\Lib\System::get_site_name());
|
||||
|
||||
|
||||
$sse_enabled = ((x($_POST,'sse_enabled')) ? true : false);
|
||||
|
||||
$verifyssl = ((x($_POST,'verifyssl')) ? True : False);
|
||||
$proxyuser = ((x($_POST,'proxyuser')) ? notags(trim($_POST['proxyuser'])) : '');
|
||||
$proxy = ((x($_POST,'proxy')) ? notags(trim($_POST['proxy'])) : '');
|
||||
@@ -72,14 +89,52 @@ class Site {
|
||||
$maxloadavg = ((x($_POST,'maxloadavg')) ? intval(trim($_POST['maxloadavg'])) : 50);
|
||||
$feed_contacts = ((x($_POST,'feed_contacts')) ? intval($_POST['feed_contacts']) : 0);
|
||||
$verify_email = ((x($_POST,'verify_email')) ? 1 : 0);
|
||||
$register_perday = ((x($_POST,'register_perday')) ? intval(trim($_POST['register_perday'])) : 50);
|
||||
$register_sameip = ((x($_POST,'register_sameip')) ? intval(trim($_POST['register_sameip'])) : 3);
|
||||
|
||||
$regdelayn = ((x($_POST,'zardelayn')) ? intval(trim($_POST['zardelayn'])) : 0);
|
||||
$regdelayu = ((x($_POST,'zardelay')) ? notags(trim($_POST['zardelay'])) : '');
|
||||
$reg_delay = (preg_match('/^[a-z]{1,1}$/', $regdelayu) ? $regdelayn . $regdelayu : '');
|
||||
$regexpiren = ((x($_POST,'zarexpiren')) ? intval(trim($_POST['zarexpiren'])) : 0);
|
||||
$regexpireu = ((x($_POST,'zarexpire')) ? notags(trim($_POST['zarexpire'])) : '');
|
||||
$reg_expire = (preg_match('/^[a-z]{1,1}$/', $regexpireu) ? $regexpiren . $regexpireu : '');
|
||||
|
||||
$imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : '');
|
||||
$thumbnail_security = ((x($_POST,'thumbnail_security')) ? intval($_POST['thumbnail_security']) : 0);
|
||||
$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']));
|
||||
|
||||
$permissions_role = escape_tags(trim($_POST['permissions_role']));
|
||||
|
||||
// [hilmar->
|
||||
$this->register_duty = ((x($_POST,'register_duty')) ? notags(trim($_POST['register_duty'])) : '');
|
||||
if (! preg_match('/^[0-9 .,:\-]{0,191}$/', $this->register_duty)) {
|
||||
$this->msgbg .= 'ZAR0131E,' . t('Invalid input') . $this->eol;
|
||||
$this->error++;
|
||||
} else {
|
||||
|
||||
$this->duty();
|
||||
|
||||
if ($this->isajax) {
|
||||
echo json_encode(array('msgbg' => $this->msgbg, 'me' => 'zar'));
|
||||
// that mission is complete
|
||||
killme();
|
||||
exit;
|
||||
|
||||
} else {
|
||||
|
||||
//logger( print_r( $this->msgbg, true) );
|
||||
//logger( print_r( $this->joo, true) );
|
||||
if ($this->error === 0) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
// <-hilmar]
|
||||
|
||||
set_config('system', 'feed_contacts', $feed_contacts);
|
||||
set_config('system', 'delivery_interval', $delivery_interval);
|
||||
set_config('system', 'delivery_batch_count', $delivery_batch_count);
|
||||
@@ -94,13 +149,16 @@ class Site {
|
||||
set_config('system', 'login_on_homepage', $login_on_homepage);
|
||||
set_config('system', 'enable_context_help', $enable_context_help);
|
||||
set_config('system', 'verify_email', $verify_email);
|
||||
set_config('system', 'max_daily_registrations', $register_perday);
|
||||
set_config('system', 'register_sameip', $register_sameip);
|
||||
set_config('system', 'register_delay', $reg_delay);
|
||||
set_config('system', 'register_expire', $reg_expire);
|
||||
set_config('system', 'default_expire_days', $default_expire_days);
|
||||
set_config('system', 'active_expire_days', $active_expire_days);
|
||||
set_config('system', 'reply_address', $reply_address);
|
||||
set_config('system', 'from_email', $from_email);
|
||||
set_config('system', 'from_email_name' , $from_email_name);
|
||||
set_config('system', 'imagick_convert_path' , $imagick_path);
|
||||
set_config('system', 'thumbnail_security' , $thumbnail_security);
|
||||
set_config('system', 'default_permissions_role', $permissions_role);
|
||||
set_config('system', 'pubstream_incl',$pub_incl);
|
||||
set_config('system', 'pubstream_excl',$pub_excl);
|
||||
@@ -125,17 +183,20 @@ class Site {
|
||||
set_config('system','siteinfo',$siteinfo);
|
||||
set_config('system', 'language', $language);
|
||||
set_config('system', 'theme', $theme);
|
||||
// if ( $theme_mobile === '---' ) {
|
||||
// del_config('system', 'mobile_theme');
|
||||
// } else {
|
||||
// set_config('system', 'mobile_theme', $theme_mobile);
|
||||
// }
|
||||
// set_config('system','site_channel', $site_channel);
|
||||
// if ( $theme_mobile === '---' ) {
|
||||
// del_config('system', 'mobile_theme');
|
||||
// } else {
|
||||
// set_config('system', 'mobile_theme', $theme_mobile);
|
||||
// }
|
||||
// set_config('system','site_channel', $site_channel);
|
||||
set_config('system','maximagesize', $maximagesize);
|
||||
|
||||
set_config('system','register_policy', $register_policy);
|
||||
set_config('system','register_wo_email', $register_wo_email);
|
||||
set_config('system','minimum_age', $minimum_age);
|
||||
set_config('system','invitation_only', $invite_only);
|
||||
set_config('system','auto_channel_create', $reg_autochannel);
|
||||
set_config('system', 'invitation_only', $invitation_only);
|
||||
set_config('system', 'invitation_also', $invitation_also);
|
||||
set_config('system','access_policy', $access_policy);
|
||||
set_config('system','account_abandon_days', $abandon_days);
|
||||
set_config('system','register_text', $register_text);
|
||||
@@ -153,6 +214,9 @@ class Site {
|
||||
|
||||
set_config('system','no_community_page', $no_community_page);
|
||||
set_config('system','no_utf', $no_utf);
|
||||
|
||||
set_config('system','sse_enabled', $sse_enabled);
|
||||
|
||||
set_config('system','verifyssl', $verifyssl);
|
||||
set_config('system','proxyuser', $proxyuser);
|
||||
set_config('system','proxy', $proxy);
|
||||
@@ -256,6 +320,8 @@ class Site {
|
||||
REGISTER_APPROVE => t("Yes - with approval"),
|
||||
REGISTER_OPEN => t("Yes")
|
||||
);
|
||||
$this->register_duty = get_config('system', 'register_duty', '-:-');
|
||||
$register_perday = get_config('system','max_daily_registrations', 50);
|
||||
|
||||
/* Acess policy */
|
||||
$access_choices = Array(
|
||||
@@ -282,9 +348,66 @@ class Site {
|
||||
$homelogin = get_config('system','login_on_homepage');
|
||||
$enable_context_help = get_config('system','enable_context_help');
|
||||
|
||||
// for reuse reg_delay and reg_expire
|
||||
$reg_rabots = array(
|
||||
'i' => t('Minute(s)'),
|
||||
'h' => t('Hour(s)') ,
|
||||
'd' => t('Day(s)') ,
|
||||
'w' => t('Week(s)') ,
|
||||
'm' => t('Month(s)') ,
|
||||
'y' => t('Year(s)')
|
||||
);
|
||||
$regdelay_n = $regdelay_u = false;
|
||||
$regdelay = get_config('system','register_delay');
|
||||
if ($regdelay)
|
||||
list($regdelay_n, $regdelay_u) = array(substr($regdelay,0,-1),substr($regdelay,-1));
|
||||
$reg_delay = replace_macros(get_markup_template('field_duration.qmc.tpl'),
|
||||
array(
|
||||
'label' => t('Register verification delay'),
|
||||
'qmc' => 'zar',
|
||||
'qmcid' => '',
|
||||
'help' => t('Time to wait before a registration can be verified'),
|
||||
'field' => array(
|
||||
'name' => 'delay',
|
||||
'title' => t('duration up from now'),
|
||||
'value' => ($regdelay_n === false ? 0 : $regdelay_n),
|
||||
'min' => '0',
|
||||
'max' => '99',
|
||||
'size' => '2',
|
||||
'default' => ($regdelay_u === false ? 'i' : $regdelay_u)
|
||||
),
|
||||
'rabot' => $reg_rabots
|
||||
)
|
||||
);
|
||||
$regexpire_n = $regexpire_u = false;
|
||||
$regexpire = get_config('system','register_expire');
|
||||
if ($regexpire)
|
||||
list($regexpire_n, $regexpire_u) = array(substr($regexpire,0,-1),substr($regexpire,-1));
|
||||
$reg_expire = replace_macros(get_markup_template('field_duration.qmc.tpl'),
|
||||
array(
|
||||
'label' => t('Register verification expiration time'),
|
||||
'qmc' => 'zar',
|
||||
'qmcid' => '',
|
||||
'help' => t('Time before an unverified registration will expire'),
|
||||
'field' => array(
|
||||
'name' => 'expire',
|
||||
'title' => t('duration up from now'),
|
||||
'value' => ($regexpire_n === false ? 3 : $regexpire_n),
|
||||
'min' => '0',
|
||||
'max' => '99',
|
||||
'size' => '2',
|
||||
'default' => ($regexpire_u === false ? 'd' : $regexpire_u)
|
||||
),
|
||||
'rabot' => $reg_rabots
|
||||
)
|
||||
);
|
||||
|
||||
$tao = '';
|
||||
$t = get_markup_template("admin_site.tpl");
|
||||
return replace_macros($t, array(
|
||||
'$title' => t('Administration'),
|
||||
// interfacing js vars
|
||||
'$tao' => $tao,
|
||||
'$page' => t('Site'),
|
||||
'$submit' => t('Submit'),
|
||||
'$registration' => t('Registration'),
|
||||
@@ -301,21 +424,87 @@ class Site {
|
||||
'$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")),
|
||||
// '$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")),
|
||||
'$feed_contacts' => array('feed_contacts', t('Allow Feeds as Connections'),get_config('system','feed_contacts'),t('(Heavy system resource usage)')),
|
||||
'$maximagesize' => array('maximagesize', t("Maximum image size"), intval(get_config('system','maximagesize')), t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")),
|
||||
'$register_policy' => array('register_policy', t("Does this site allow new member registration?"), get_config('system','register_policy'), "", $register_choices),
|
||||
'$invite_only' => array('invite_only', t("Invitation only"), get_config('system','invitation_only'), t("Only allow new member registrations with an invitation code. Above register policy must be set to Yes.")),
|
||||
'$minimum_age' => array('minimum_age', t("Minimum age"), (x(get_config('system','minimum_age'))?get_config('system','minimum_age'):13), t("Minimum age (in years) for who may register on this site.")),
|
||||
'$access_policy' => array('access_policy', t("Which best describes the types of account offered by this hub?"), get_config('system','access_policy'), t("This is displayed on the public server site list."), $access_choices),
|
||||
'$register_text' => array('register_text', t("Register text"), htmlspecialchars(get_config('system','register_text'), ENT_QUOTES, 'UTF-8'), t("Will be displayed prominently on the registration page.")),
|
||||
|
||||
// Register
|
||||
// [hilmar->
|
||||
'$register_text' => [
|
||||
'register_text',
|
||||
t("Register text"),
|
||||
htmlspecialchars(get_config('system','register_text'), ENT_QUOTES, 'UTF-8'),
|
||||
t("This text will be displayed prominently at the registration page")
|
||||
],
|
||||
'$register_policy' => [
|
||||
'register_policy',
|
||||
t("Does this site allow new member registration?"),
|
||||
get_config('system','register_policy'),
|
||||
"",
|
||||
$register_choices,
|
||||
],
|
||||
'$register_duty' => [
|
||||
'register_duty',
|
||||
t('Configure the registration open days/hours'),
|
||||
get_config('system', 'register_duty', '-:-'),
|
||||
t('Empty or \'-:-\' value will keep registration open 24/7 (default)') . EOL .
|
||||
t('Weekdays and hours must be separated by colon \':\', From-To ranges with a dash `-` example: 1:800-1200') . EOL .
|
||||
t('Weekday:Hour pairs must be separated by space \' \' example: 1:900-1700 2:900-1700') . EOL .
|
||||
t('From-To ranges must be separated by comma \',\' example: 1:800-1200,1300-1700 or 1-2,4-5:900-1700') . EOL .
|
||||
t('Advanced examples:') . ' 1-5:0900-1200,1300-1700 6:900-1230 ' . t('or') . ' 1-2,4-5:800-1800<br>' . EOL .
|
||||
'<a id="zar083a" class="btn btn-sm btn-outline-secondary zuia">' . t('Check your configuration') . '</a>'. EOL
|
||||
],
|
||||
'$register_perday' => [
|
||||
'register_perday',
|
||||
t('Max account registrations per day'),
|
||||
get_config('system', 'max_daily_registrations', 50),
|
||||
t('Unlimited if zero or no value - default 50')
|
||||
],
|
||||
'$register_sameip' => [
|
||||
'register_sameip',
|
||||
t('Max account registrations from same IP'),
|
||||
get_config('system', 'register_sameip', 3),
|
||||
t('Unlimited if zero or no value - default 3')
|
||||
],
|
||||
'$reg_delay' => $reg_delay,
|
||||
'$reg_expire' => $reg_expire,
|
||||
'$reg_autochannel' => [
|
||||
'auto_channel_create',
|
||||
t("Auto channel create"),
|
||||
get_config('system','auto_channel_create', 1),
|
||||
t("If disabled the channel will be created in a separate step during the registration process")
|
||||
],
|
||||
'$invitation_only' => [
|
||||
'invitation_only',
|
||||
t("Require invite code"),
|
||||
$invitation_only
|
||||
],
|
||||
'$invitation_also' => [
|
||||
'invitation_also',
|
||||
t("Allow invite code"),
|
||||
$invitation_also
|
||||
],
|
||||
'$verify_email' => [
|
||||
'verify_email',
|
||||
t("Require email address"),
|
||||
get_config('system','verify_email'),
|
||||
t("The provided email address will be verified (recommended)")
|
||||
],
|
||||
'$abandon_days' => [
|
||||
'abandon_days',
|
||||
t('Abandon account after x days'),
|
||||
get_config('system','account_abandon_days'),
|
||||
t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.')
|
||||
],
|
||||
// <-hilmar]
|
||||
|
||||
'$role' => $role,
|
||||
'$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')),
|
||||
'$abandon_days' => array('abandon_days', t('Accounts abandoned after x days'), get_config('system','account_abandon_days'), t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.')),
|
||||
'$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")),
|
||||
'$verify_email' => array('verify_email', t("Verify Email Addresses"), get_config('system','verify_email'), t("Check to verify email addresses used in account registration (recommended).")),
|
||||
'$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.')),
|
||||
@@ -333,6 +522,8 @@ class Site {
|
||||
|
||||
'$directory_server' => (($dir_choices) ? array('directory_server', t("Directory Server URL"), get_config('system','directory_server'), t("Default directory server"), $dir_choices) : null),
|
||||
|
||||
'$sse_enabled' => array('sse_enabled', t('Enable SSE Notifications'), get_config('system', 'sse_enabled', 0), t('If disabled, traditional polling will be used. Warning: this setting might not be suited for shared hosting')),
|
||||
|
||||
'$proxyuser' => array('proxyuser', t("Proxy user"), get_config('system','proxyuser'), ""),
|
||||
'$proxy' => array('proxy', t("Proxy URL"), get_config('system','proxy'), ""),
|
||||
'$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).")),
|
||||
@@ -341,19 +532,187 @@ class Site {
|
||||
'$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")),
|
||||
'$thumbnail_security' => array('thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system','thumbnail_security',0), t("WARNING: SVG images may contain malicious code.")),
|
||||
'$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')),
|
||||
'$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')),
|
||||
|
||||
'$location' => array('site_location', t('Optional: site location'), get_config('system','site_location',''), t('Region or country')),
|
||||
|
||||
|
||||
'$form_security_token' => get_form_security_token("admin_site"),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Admin page site common post submit and ajax interaction
|
||||
* @author hilmar runge
|
||||
* @since 2020-02-04
|
||||
* Configure register office duty weekdays and hours
|
||||
* Syntax: weekdays:hours [weekdays:hours]
|
||||
* [.d[,d-d.]]]:hhmm-hhmm[,hhmm-hhmm...]
|
||||
* ranges are between blanks, days are 1-7, where 1 = Monday
|
||||
* hours are [h]hmm 3-4digit 24 clock values
|
||||
* ie 0900-1200,1300-1800 for hours
|
||||
* ie 1-2,4,5 for weekdays
|
||||
* ie 1-2:900-1800 monday and tuesday open from 9 to 18h
|
||||
*
|
||||
* @var $register_duty is the input field from the admin -> site page
|
||||
* @return the results are in the class vars $error, $msgbg and $jsoo
|
||||
* $jsoo is
|
||||
*/
|
||||
|
||||
// 3-4 digit 24h clock regex
|
||||
const regxTime34 = '/^(?:2[0-3]|[01][0-9]|[0-9])[0-5][0-9]$/';
|
||||
var $wdconst = array('','mo','tu','we','th','fr','sa','so');
|
||||
|
||||
// in
|
||||
var $register_duty;
|
||||
// intermediate
|
||||
var $isajax;
|
||||
// return
|
||||
var $jsoo;
|
||||
var $msgbg;
|
||||
var $error = 0;
|
||||
var $msgfg = '';
|
||||
|
||||
private function duty() {
|
||||
|
||||
$aro=array_fill(1, 7, 0);
|
||||
|
||||
if ($this->isajax) {
|
||||
$op = (preg_match('/[a-z]{2,4}/', $_REQUEST['zarop'])) ? $_REQUEST['zarop'] : '';
|
||||
if ($op == 'zar083') {
|
||||
$this->msgbg = 'Testmode:' . $this->eol . $this->msgbg;
|
||||
} else {
|
||||
killme();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$ranges = preg_split('/\s+/', $this->register_duty);
|
||||
$this->msgbg .= '..ranges: ' . print_r(count($ranges),true) . $this->eol;
|
||||
|
||||
foreach ($ranges as $rn => $range) {
|
||||
list($ws,$hs,) = explode(':', $range);
|
||||
|
||||
$ws ? $arw = explode( ',', $ws) : $arw=array();
|
||||
$this->msgbg .= ($rn+1).'.weekday ranges: ' . count($arw) . $this->eol;
|
||||
// $this->msgbg .= print_r($arw,true);
|
||||
$hs ? $arh = explode( ',', $hs) : $arh=array();
|
||||
$this->msgbg .= ($rn+1).'.hour ranges: ' . count($arh) . $this->eol;
|
||||
|
||||
$this->msgbg .= ($rn+1).'.wdays: ' . ( $ws ? print_r($ws,true) : 'none') . ' : '
|
||||
. ' hours: ' . print_r($hs,true) . $this->eol;
|
||||
|
||||
// several hs may belog to one wd
|
||||
// aro[0] is tmp store
|
||||
foreach ($arh as $hs) {
|
||||
list($ho,$hc,) = explode( '-', $hs );
|
||||
|
||||
// no value forces open very early, and be sure having valid hhmm values
|
||||
!$ho ? $ho = "0000" : '';
|
||||
!$hc ? $hc = "0000" : ''; // pseudo
|
||||
if (preg_match(self::regxTime34, $ho)
|
||||
&& preg_match(self::regxTime34, $hc)) {
|
||||
|
||||
// fix pseudo, allow no reverse range
|
||||
$hc == "0000" || $ho > $hc ? $hc = "2400" : '';
|
||||
|
||||
$aro[0][$ho] = 0;
|
||||
$aro[0][$hc] = 1;
|
||||
|
||||
$this->msgbg .= ($ho ? ' .open:' . $ho : '') . ($hc ? ' close:' . $hc : '') .$this->eol;
|
||||
} else {
|
||||
$this->msgbg .= ' .' . t('Invalid 24h time value (hhmm/hmm)') . $this->eol;
|
||||
$this->msgfg .= ' .ZAR0132E,' . t('Invalid 24h time value (hhmm/hmm)') . $this->eol;
|
||||
$this->error++;
|
||||
}
|
||||
}
|
||||
|
||||
// the weekday(s) values or ranges
|
||||
foreach ($arw as $ds) {
|
||||
$wd=explode('-', $ds);
|
||||
array_key_exists("1", $wd) && $wd[1]=="" ? $wd[1] = "7" : ''; // a case 3-
|
||||
array_key_exists("1", $wd) && $wd[0]=="" ? $wd[0] = "1" : ''; // a case -3
|
||||
!array_key_exists("1", $wd) ? $wd[1] = $wd[0] : ''; // a case 3
|
||||
if ($wd[0] > $wd[1]) continue; // reverse order will be ignored // a case 5-3
|
||||
if (preg_match('/^[1-7]{1}$/', $wd[0])) {
|
||||
if (preg_match('/^[1-7]{1}$/', $wd[1])) {
|
||||
// $this->msgbg .= print_r($wd,true);
|
||||
for ($i=$wd[0]; $i<=$wd[1]; $i++) {
|
||||
// take the tmp store for the selected day(s)
|
||||
$aro[$i]=$aro[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//$this->msgbg .= 'aro0: ' . print_r($aro,true) . $this->eol; // 4devels
|
||||
// clear the tmp store
|
||||
$aro[0]=array();
|
||||
}
|
||||
// discart the tmp store
|
||||
unset($aro[0]);
|
||||
// not configured days close at the beginning 0000h
|
||||
for ($i=1;$i<=7;$i++) { is_array($aro[$i]) ? '' : $aro[$i] = array("0000" => 1); }
|
||||
// $this->msgbg .= 'aro: ' . print_r($aro,true) . $this->eol; // 4devels
|
||||
|
||||
if ($this->isajax) {
|
||||
// tell what we have
|
||||
// $this->msgbg .= 'aro0: ' . print_r($aro,true) . $this->eol; // 4devels
|
||||
$this->msgbg .= 'Duty time table:' . $this->eol;
|
||||
foreach ($aro as $dow => $hrs) {
|
||||
$this->msgbg .= ' ' . $this->wdconst[$dow] . ' ';
|
||||
// $this->msgbg .= '**' . print_r($hrs,true);
|
||||
foreach ($hrs as $h => $o) {
|
||||
$this->msgbg .= ((!$o) ? $h . ':open' : $h . ':close') . ', ';
|
||||
}
|
||||
$this->msgbg = rtrim($this->msgbg, ', ') . $this->eol;
|
||||
}
|
||||
|
||||
$this->msgbg .= 'Generating 6 random times to check duty hours: ' . $this->eol;
|
||||
// we only need some random dates from anyway in past or future
|
||||
// because only the weekday and the clock is to test
|
||||
for ($i=0; $i<6; $i++) {
|
||||
$adow = rand(1, 7); // 1 to 7 (days)
|
||||
$cdow = $this->wdconst[$adow];
|
||||
// below is the essential algo to verify a date (of format Hi) meets an open or closed condition
|
||||
$t = date('Hi', ( rand(time(), 60*60*24+time()) ) );
|
||||
$how='close';
|
||||
foreach ($aro[$adow] as $o => $v) {
|
||||
// $this->msgbg .= 'debug: ' . $o . ' gt ' . $t . ' / ' . $v . $this->eol; // 4devels
|
||||
if ($o > $t) {
|
||||
$how = ($v ? 'open' : 'close');
|
||||
break;
|
||||
}
|
||||
}
|
||||
// now we know
|
||||
$this->msgbg .= ' ' . $cdow . '.' . $t . '=' . $how . ', ';
|
||||
}
|
||||
$this->msgbg = rtrim($this->msgbg, ', ') . $this->eol;
|
||||
}
|
||||
|
||||
/*
|
||||
//$jov1 = array( 'view1' => $aro, 'view2' => '');
|
||||
$jov2=array();
|
||||
foreach ($aro as $d => $ts) {
|
||||
foreach ($ts as $t => $ft) {
|
||||
$jov2['view2'][$ft][] = $d.$t;
|
||||
//$ft=="1" && $t=="0000" ? $jov2['view2']["0"][] = $d."2400" : '';
|
||||
}
|
||||
}
|
||||
$this->msgbg .= print_r($jov2, true) . $this->eol; // 4devels
|
||||
*/
|
||||
|
||||
$this->joo = json_encode($aro);
|
||||
// $this->msgbg .= $this->joo . $this->eol; // 4devels
|
||||
// $this->msgbg .= print_r($aro, true) . $this->eol; // 4devels
|
||||
$okko = (json_decode($this->joo, true) ? true : false);
|
||||
if (!$okko) {
|
||||
$this->msgbg .= 'ZAR0139D,json 4 duty KO crash' . $this->eol;
|
||||
$this->msgfg .= 'ZAR0139D,json 4 duty KO crash' . $this->eol;
|
||||
$this->error++;
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -34,9 +34,9 @@ class Apporder extends \Zotlabs\Web\Controller {
|
||||
|
||||
foreach($syslist as $app) {
|
||||
if($l === 'nav_pinned_app') {
|
||||
$navbar_apps[] = Zlib\Apps::app_render($app,'nav-order');
|
||||
$navbar_apps[] = Zlib\Apps::app_render($app,'nav-order-pinned');
|
||||
}
|
||||
elseif(strpos($app['categories'],'nav_pinned_app') === false) {
|
||||
else {
|
||||
$nav_apps[] = Zlib\Apps::app_render($app,'nav-order');
|
||||
}
|
||||
}
|
||||
|
||||
66
Zotlabs/Module/Apschema.php
Normal file
66
Zotlabs/Module/Apschema.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
|
||||
class Apschema extends \Zotlabs\Web\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);
|
||||
killme();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -63,9 +63,9 @@ class Article_edit extends \Zotlabs\Web\Controller {
|
||||
|
||||
if ($catsenabled){
|
||||
$itm = fetch_post_tags($itm);
|
||||
|
||||
|
||||
$cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
|
||||
|
||||
|
||||
foreach ($cats as $cat) {
|
||||
if (strlen($category))
|
||||
$category .= ', ';
|
||||
@@ -85,10 +85,9 @@ class Article_edit extends \Zotlabs\Web\Controller {
|
||||
|
||||
$mimetype = $itm[0]['mimetype'];
|
||||
|
||||
$summary = (($itm[0]['summary']) ? '[summary]' . $itm[0]['summary'] . '[/summary]' . "\r\n" : '');
|
||||
$content = $itm[0]['body'];
|
||||
|
||||
|
||||
|
||||
$rp = 'articles/' . $channel['channel_address'];
|
||||
|
||||
$x = array(
|
||||
@@ -110,10 +109,11 @@ class Article_edit extends \Zotlabs\Web\Controller {
|
||||
'ptyp' => $itm[0]['type'],
|
||||
'mimeselect' => false,
|
||||
'mimetype' => $itm[0]['mimetype'],
|
||||
'body' => undo_post_tagging($content),
|
||||
'body' => $summary . 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'])),
|
||||
|
||||
@@ -9,12 +9,13 @@ 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);
|
||||
|
||||
@@ -27,13 +28,13 @@ class Articles extends Controller {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
profile_load($which);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function get($update = 0, $load = false) {
|
||||
|
||||
|
||||
if(observer_prohibited(true)) {
|
||||
return login();
|
||||
}
|
||||
@@ -55,7 +56,7 @@ class Articles extends Controller {
|
||||
|
||||
nav_set_selected('Articles');
|
||||
|
||||
head_add_link([
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/json+oembed',
|
||||
'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string),
|
||||
@@ -64,7 +65,7 @@ class Articles extends Controller {
|
||||
|
||||
|
||||
$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));
|
||||
}
|
||||
@@ -73,24 +74,24 @@ class Articles extends Controller {
|
||||
$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) {
|
||||
@@ -104,7 +105,7 @@ class Articles extends Controller {
|
||||
else {
|
||||
$channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if(perm_is_allowed($owner,$ob_hash,'write_pages')) {
|
||||
@@ -113,16 +114,15 @@ class Articles extends Controller {
|
||||
'webpage' => ITEM_TYPE_ARTICLE,
|
||||
'is_owner' => true,
|
||||
'content_label' => t('Add Article'),
|
||||
'button' => t('Create'),
|
||||
'button' => t('Save'),
|
||||
'nickname' => $channel['channel_address'],
|
||||
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid']
|
||||
'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,
|
||||
'acl' => (($is_owner) ? populate_acl($channel_acl, false,
|
||||
PermissionDescription::fromGlobalPermission('view_pages')) : ''),
|
||||
'permissions' => $channel_acl,
|
||||
'showacl' => (($is_owner) ? true : false),
|
||||
'visitor' => true,
|
||||
'body' => '[summary][/summary]',
|
||||
'hide_location' => false,
|
||||
'hide_voting' => false,
|
||||
'profile_uid' => intval($owner),
|
||||
@@ -146,12 +146,12 @@ class Articles extends Controller {
|
||||
else {
|
||||
$editor = '';
|
||||
}
|
||||
|
||||
|
||||
$itemspage = get_pconfig(local_channel(),'system','itemspage');
|
||||
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20));
|
||||
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 = '';
|
||||
|
||||
@@ -175,8 +175,8 @@ class Articles extends Controller {
|
||||
$sql_extra2 .= " and item.item_thread_top != 0 ";
|
||||
}
|
||||
|
||||
$r = q("select * from item
|
||||
where item.uid = %d and item_type = %d
|
||||
$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)
|
||||
@@ -192,7 +192,7 @@ class Articles extends Controller {
|
||||
|
||||
$parents_str = ids_to_querystr($r,'id');
|
||||
|
||||
$items = q("SELECT item.*, item.id AS item_id
|
||||
$r = q("SELECT item.*, item.id AS item_id
|
||||
FROM item
|
||||
WHERE item.uid = %d $item_normal
|
||||
AND item.parent IN ( %s )
|
||||
@@ -200,17 +200,20 @@ class Articles extends Controller {
|
||||
intval(App::$profile['profile_uid']),
|
||||
dbesc($parents_str)
|
||||
);
|
||||
if($items) {
|
||||
xchan_query($items);
|
||||
$items = fetch_post_tags($items, true);
|
||||
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
|
||||
|
||||
@@ -1,61 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use ZipArchive;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\Verify;
|
||||
|
||||
require_once('include/security.php');
|
||||
require_once('include/attach.php');
|
||||
|
||||
class Attach extends Controller {
|
||||
|
||||
class Attach extends \Zotlabs\Web\Controller {
|
||||
function post() {
|
||||
|
||||
function init() {
|
||||
|
||||
if(argc() < 2) {
|
||||
notice( t('Item not available.') . EOL);
|
||||
$attach_ids = ((x($_REQUEST, 'attach_ids')) ? $_REQUEST['attach_ids'] : []);
|
||||
$attach_path = ((x($_REQUEST, 'attach_path')) ? $_REQUEST['attach_path'] : '');
|
||||
$channel_id = ((x($_REQUEST, 'channel_id')) ? intval($_REQUEST['channel_id']) : 0);
|
||||
$channel = channelx_by_n($channel_id);
|
||||
|
||||
if (!$channel) {
|
||||
notice(t('Channel not found.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$r = attach_by_hash(argv(1),get_observer_hash(),((argc() > 2) ? intval(argv(2)) : 0));
|
||||
|
||||
if(! $r['success']) {
|
||||
notice( $r['message'] . EOL);
|
||||
|
||||
$strip_str = '/cloud/' . $channel['channel_address'] . '/';
|
||||
$count = strlen($strip_str);
|
||||
$attach_path = substr($attach_path, $count);
|
||||
|
||||
if ($attach_ids) {
|
||||
|
||||
$zip_dir = 'store/[data]/' . $channel['channel_address'] . '/tmp';
|
||||
if (!is_dir($zip_dir))
|
||||
mkdir($zip_dir, STORAGE_DEFAULT_PERMISSIONS, true);
|
||||
|
||||
$token = random_string(32);
|
||||
|
||||
$zip_file = 'download_' . $token . '.zip';
|
||||
$zip_path = $zip_dir . '/' . $zip_file;
|
||||
|
||||
$zip = new ZipArchive();
|
||||
|
||||
if ($zip->open($zip_path, ZipArchive::CREATE) === true) {
|
||||
|
||||
$zip_filename = self::zip_archive_handler($zip, $attach_ids, $attach_path);
|
||||
|
||||
$zip->close();
|
||||
|
||||
$meta = [
|
||||
'zip_filename' => $zip_filename,
|
||||
'zip_path' => $zip_path
|
||||
];
|
||||
|
||||
Verify::create('zip_token', 0, $token, json_encode($meta));
|
||||
|
||||
json_return_and_die([
|
||||
'success' => true,
|
||||
'token' => $token
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function get() {
|
||||
|
||||
if (argc() < 2) {
|
||||
notice(t('Item not available.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$token = ((x($_REQUEST, 'token')) ? $_REQUEST['token'] : '');
|
||||
|
||||
if (argv(1) === 'download') {
|
||||
$meta = Verify::get_meta('zip_token', 0, $token);
|
||||
|
||||
if (!$meta)
|
||||
killme();
|
||||
|
||||
$meta = json_decode($meta, true);
|
||||
|
||||
header('Content-Type: application/zip');
|
||||
header('Content-Disposition: attachment; filename="' . $meta['zip_filename'] . '"');
|
||||
header('Content-Length: ' . filesize($meta['zip_path']));
|
||||
|
||||
$istream = fopen($meta['zip_path'], 'rb');
|
||||
$ostream = fopen('php://output', 'wb');
|
||||
if ($istream && $ostream) {
|
||||
pipe_streams($istream, $ostream);
|
||||
fclose($istream);
|
||||
fclose($ostream);
|
||||
}
|
||||
|
||||
unlink($meta['zip_path']);
|
||||
killme();
|
||||
}
|
||||
|
||||
$r = attach_by_hash(argv(1), get_observer_hash(), ((argc() > 2) ? intval(argv(2)) : 0));
|
||||
|
||||
if (!$r['success']) {
|
||||
notice($r['message'] . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$c = q("select channel_address from channel where channel_id = %d limit 1",
|
||||
intval($r['data']['uid'])
|
||||
);
|
||||
|
||||
if(! $c)
|
||||
|
||||
if (!$c)
|
||||
return;
|
||||
|
||||
|
||||
$unsafe_types = array('text/html','text/css','application/javascript');
|
||||
|
||||
if(in_array($r['data']['filetype'],$unsafe_types) && (! channel_codeallowed($r['data']['uid']))) {
|
||||
header('Content-type: text/plain');
|
||||
|
||||
$unsafe_types = array('text/html', 'text/css', 'application/javascript');
|
||||
|
||||
if (in_array($r['data']['filetype'], $unsafe_types) && (!channel_codeallowed($r['data']['uid']))) {
|
||||
header('Content-Type: text/plain');
|
||||
}
|
||||
else {
|
||||
header('Content-type: ' . $r['data']['filetype']);
|
||||
header('Content-Type: ' . $r['data']['filetype']);
|
||||
}
|
||||
|
||||
header('Content-disposition: attachment; filename="' . $r['data']['filename'] . '"');
|
||||
if(intval($r['data']['os_storage'])) {
|
||||
$fname = dbunescbin($r['data']['content']);
|
||||
if(strpos($fname,'store') !== false)
|
||||
$istream = fopen($fname,'rb');
|
||||
|
||||
header('Content-Disposition: attachment; filename="' . $r['data']['filename'] . '"');
|
||||
if (intval($r['data']['os_storage'])) {
|
||||
$fname = $r['data']['content'];
|
||||
if (strpos($fname, 'store') !== false)
|
||||
$istream = fopen($fname, 'rb');
|
||||
else
|
||||
$istream = fopen('store/' . $c[0]['channel_address'] . '/' . $fname,'rb');
|
||||
$ostream = fopen('php://output','wb');
|
||||
if($istream && $ostream) {
|
||||
pipe_streams($istream,$ostream);
|
||||
$istream = fopen('store/' . $c[0]['channel_address'] . '/' . $fname, 'rb');
|
||||
$ostream = fopen('php://output', 'wb');
|
||||
if ($istream && $ostream) {
|
||||
pipe_streams($istream, $ostream);
|
||||
fclose($istream);
|
||||
fclose($ostream);
|
||||
}
|
||||
}
|
||||
else
|
||||
echo dbunescbin($r['data']['content']);
|
||||
echo $r['data']['content'];
|
||||
killme();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function zip_archive_handler($zip, $attach_ids, $attach_path, $pass = 1) {
|
||||
|
||||
$observer_hash = get_observer_hash();
|
||||
$single = ((count($attach_ids) == 1) ? true : false);
|
||||
$download_name = 'download.zip';
|
||||
|
||||
foreach ($attach_ids as $attach_id) {
|
||||
|
||||
$r = attach_by_id($attach_id, $observer_hash);
|
||||
|
||||
if (!$r['success']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($r['data']['is_dir'] && $single && $pass === 1)
|
||||
$download_name = $r['data']['filename'] . '.zip';
|
||||
|
||||
$zip_path = $r['data']['display_path'];
|
||||
|
||||
if ($attach_path) {
|
||||
$strip_str = $attach_path . '/';
|
||||
$count = strlen($strip_str);
|
||||
$zip_path = substr($r['data']['display_path'], $count);
|
||||
}
|
||||
|
||||
if ($r['data']['is_dir']) {
|
||||
$zip->addEmptyDir($zip_path);
|
||||
|
||||
$d = q("SELECT id FROM attach WHERE folder = '%s'",
|
||||
dbesc($r['data']['hash'])
|
||||
);
|
||||
|
||||
$attach_ids = ids_to_array($d);
|
||||
self::zip_archive_handler($zip, $attach_ids, $attach_path, $pass++);
|
||||
}
|
||||
else {
|
||||
$file_path = $r['data']['content'];
|
||||
$zip->addFile($file_path, $zip_path);
|
||||
// compressing can be ressource intensive - just store the data
|
||||
$zip->setCompressionName($zip_path, ZipArchive::CM_STORE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $download_name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
203
Zotlabs/Module/Attach_edit.php
Normal file
203
Zotlabs/Module/Attach_edit.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
/**
|
||||
* @file Zotlabs/Module/Attach_edit.php
|
||||
*
|
||||
*/
|
||||
|
||||
use App;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
use Zotlabs\Access\AccessList;
|
||||
|
||||
class Attach_edit extends Controller {
|
||||
|
||||
function post() {
|
||||
|
||||
if (!local_channel() && !remote_channel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attach_ids = ((x($_POST, 'attach_ids')) ? $_POST['attach_ids'] : []);
|
||||
$attach_id = ((x($_POST, 'attach_id')) ? intval($_POST['attach_id']) : 0);
|
||||
$channel_id = ((x($_POST, 'channel_id')) ? intval($_POST['channel_id']) : 0);
|
||||
$dnd = ((x($_POST, 'dnd')) ? intval($_POST['dnd']) : 0);
|
||||
$permissions = ((x($_POST, 'permissions')) ? intval($_POST['permissions']) : 0);
|
||||
$return_path = ((x($_POST, 'return_path')) ? notags($_POST['return_path']) : 'cloud');
|
||||
$delete = ((x($_POST, 'delete')) ? intval($_POST['delete']) : 0);
|
||||
$newfolder = ((x($_POST, 'newfolder_' . $attach_id)) ? notags($_POST['newfolder_' . $attach_id]) : '');
|
||||
if(! $newfolder)
|
||||
$newfolder = ((x($_POST, 'newfolder')) ? notags($_POST['newfolder']) : '');
|
||||
$newfilename = ((x($_POST, 'newfilename_' . $attach_id)) ? notags($_POST['newfilename_' . $attach_id]) : '');
|
||||
$recurse = ((x($_POST, 'recurse_' . $attach_id)) ? intval($_POST['recurse_' . $attach_id]) : 0);
|
||||
if(! $recurse)
|
||||
$recurse = ((x($_POST, 'recurse')) ? intval($_POST['recurse']) : 0);
|
||||
$notify = ((x($_POST, 'notify_edit_' . $attach_id)) ? intval($_POST['notify_edit_' . $attach_id]) : 0);
|
||||
$copy = ((x($_POST, 'copy_' . $attach_id)) ? intval($_POST['copy_' . $attach_id]) : 0);
|
||||
if(! $copy)
|
||||
$copy = ((x($_POST, 'copy')) ? intval($_POST['copy']) : 0);
|
||||
|
||||
$categories = ((x($_POST, 'categories_' . $attach_id)) ? notags($_POST['categories_' . $attach_id]) : '');
|
||||
if(! $categories)
|
||||
$categories = ((x($_POST, 'categories')) ? notags($_POST['categories']) : '');
|
||||
|
||||
if($attach_id)
|
||||
$attach_ids[] = $attach_id;
|
||||
|
||||
$single = ((count($attach_ids) === 1) ? true : false);
|
||||
|
||||
$channel = channelx_by_n($channel_id);
|
||||
|
||||
if (! $channel) {
|
||||
notice(t('Channel not found.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$nick = $channel['channel_address'];
|
||||
$observer = App::get_observer();
|
||||
$observer_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
$is_owner = ((local_channel() == $channel_id) ? true : false);
|
||||
|
||||
$ids_str = implode(',', $attach_ids);
|
||||
|
||||
$r = q("SELECT id, uid, hash, creator, folder, filename, is_photo, is_dir FROM attach WHERE id IN ( %s ) AND uid = %d",
|
||||
dbesc($ids_str),
|
||||
intval($channel_id)
|
||||
);
|
||||
|
||||
if (! $r) {
|
||||
notice(t('File not found.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($r as $rr) {
|
||||
$actions_done = '';
|
||||
$attach_id = $rr['id'];
|
||||
$resource = $rr['hash'];
|
||||
$creator = $rr['creator'];
|
||||
$folder = $rr['folder'];
|
||||
$filename = $rr['filename'];
|
||||
$is_photo = intval($rr['is_photo']);
|
||||
$is_dir = intval($rr['is_dir']);
|
||||
$admin_delete = false;
|
||||
|
||||
$is_creator = (($creator == $observer_hash) ? true : false);
|
||||
$move = ((! $copy && ($folder !== $newfolder || (($single) ? $filename !== $newfilename : false))) ? true : false);
|
||||
|
||||
$perms = get_all_perms($channel_id, $observer_hash);
|
||||
|
||||
if (! ($perms['view_storage'] || is_site_admin())) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $perms['write_storage']) {
|
||||
if (is_site_admin()) {
|
||||
$admin_delete = true;
|
||||
}
|
||||
else {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$is_owner && !$admin_delete) {
|
||||
if(! $is_creator) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($delete) {
|
||||
attach_delete($channel_id, $resource, $is_photo);
|
||||
$actions_done .= 'delete,';
|
||||
}
|
||||
|
||||
if ($copy) {
|
||||
if($is_dir && $resource == $newfolder) {
|
||||
notice( t('Can not copy folder into itself.') . EOL);
|
||||
continue;
|
||||
}
|
||||
$x = attach_copy($channel_id, $resource, $newfolder, (($single) ? $newfilename : ''));
|
||||
if ($x['success'])
|
||||
$resource = $x['resource_id'];
|
||||
|
||||
$actions_done .= 'copy,';
|
||||
|
||||
}
|
||||
|
||||
if ($move) {
|
||||
if($is_dir && $resource == $newfolder) {
|
||||
notice( sprintf(t('Can not move folder "%s" into itself.'), $filename) . EOL);
|
||||
continue;
|
||||
}
|
||||
$x = attach_move($channel_id, $resource, $newfolder, (($single) ? $newfilename : ''));
|
||||
|
||||
$actions_done .= 'move,';
|
||||
|
||||
}
|
||||
|
||||
if(! $delete && ! $dnd) {
|
||||
if ($single || (! $single && $categories)) {
|
||||
q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d",
|
||||
intval($channel_id),
|
||||
intval($attach_id),
|
||||
intval(TERM_OBJ_FILE)
|
||||
);
|
||||
$cat = explode(',', $categories);
|
||||
if ($cat) {
|
||||
foreach($cat as $term) {
|
||||
$term = trim(escape_tags($term));
|
||||
if ($term) {
|
||||
$term_link = z_root() . '/cloud/' . $nick . '/?cat=' . $term;
|
||||
store_item_tag($channel_id, $attach_id, TERM_OBJ_FILE, TERM_CATEGORY, $term, $term_link);
|
||||
}
|
||||
}
|
||||
$actions_done .= 'cat_add,';
|
||||
}
|
||||
}
|
||||
else {
|
||||
q("DELETE FROM term WHERE uid = %d AND oid = %d AND otype = %d",
|
||||
intval($channel_id),
|
||||
intval($attach_id),
|
||||
intval(TERM_OBJ_FILE)
|
||||
);
|
||||
$actions_done .= 'cat_remove,';
|
||||
}
|
||||
|
||||
if ($is_owner && ($single || (! $single && $permissions))) {
|
||||
$acl = new AccessList($channel);
|
||||
$acl->set_from_array($_REQUEST);
|
||||
$x = $acl->get();
|
||||
|
||||
attach_change_permissions($channel_id, $resource, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], $recurse, true);
|
||||
$actions_done .= 'permissions,';
|
||||
|
||||
if ($notify) {
|
||||
attach_store_item($channel, $observer, $resource);
|
||||
$actions_done .= 'notify,';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! $admin_delete && $actions_done) {
|
||||
$sync = attach_export_data($channel, $resource, (($delete) ? true : false));
|
||||
|
||||
if ($sync) {
|
||||
Libsync::build_sync_packet($channel_id, ['file' => [$sync]]);
|
||||
}
|
||||
}
|
||||
|
||||
logger('attach_edit: ' . $actions_done);
|
||||
|
||||
}
|
||||
|
||||
if($dnd || $delete) {
|
||||
json_return_and_die([ 'success' => true ]);
|
||||
}
|
||||
|
||||
goaway($return_path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,8 +16,8 @@ class Bookmarks extends \Zotlabs\Web\Controller {
|
||||
|
||||
nav_set_selected('Bookmarks');
|
||||
|
||||
$item_id = intval($_REQUEST['item']);
|
||||
$burl = trim($_REQUEST['burl']);
|
||||
$item_id = (isset($_REQUEST['item']) ? $_REQUEST['item'] : false);
|
||||
$burl = (isset($_REQUEST['burl']) ? trim($_REQUEST['burl']) : '');
|
||||
|
||||
if(! $item_id)
|
||||
return;
|
||||
@@ -38,7 +38,7 @@ class Bookmarks extends \Zotlabs\Web\Controller {
|
||||
|
||||
$item = $i[0];
|
||||
|
||||
$terms = get_terms_oftype($item['term'],TERM_BOOKMARK);
|
||||
$terms = (x($item, 'term') ? get_terms_oftype($item['term'],TERM_BOOKMARK) : false);
|
||||
|
||||
if($terms) {
|
||||
require_once('include/bookmarks.php');
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
|
||||
use App;
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
require_once('include/conversation.php');
|
||||
require_once('include/bbcode.php');
|
||||
require_once('include/datetime.php');
|
||||
@@ -9,351 +13,204 @@ require_once('include/items.php');
|
||||
require_once('include/html2plain.php');
|
||||
|
||||
|
||||
class Cal extends \Zotlabs\Web\Controller {
|
||||
class Cal extends Controller {
|
||||
|
||||
function init() {
|
||||
if(observer_prohibited()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$o = '';
|
||||
|
||||
|
||||
if(argc() > 1) {
|
||||
$nick = argv(1);
|
||||
|
||||
|
||||
profile_load($nick);
|
||||
|
||||
|
||||
$channelx = channelx_by_nick($nick);
|
||||
|
||||
if(! $channelx)
|
||||
|
||||
if(! $channelx) {
|
||||
notice( t('Channel not found.') . EOL);
|
||||
return;
|
||||
|
||||
\App::$data['channel'] = $channelx;
|
||||
|
||||
$observer = \App::get_observer();
|
||||
\App::$data['observer'] = $observer;
|
||||
|
||||
$observer_xchan = (($observer) ? $observer['xchan_hash'] : '');
|
||||
|
||||
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::$data['channel'] = $channelx;
|
||||
|
||||
$observer = App::get_observer();
|
||||
App::$data['observer'] = $observer;
|
||||
|
||||
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>" ;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
|
||||
if(observer_prohibited()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$channel = null;
|
||||
|
||||
if(argc() > 1) {
|
||||
$channel = channelx_by_nick(argv(1));
|
||||
}
|
||||
|
||||
|
||||
if(! $channel) {
|
||||
notice( t('Channel not found.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$channel = App::$data['channel'];
|
||||
|
||||
// since we don't currently have an event permission - use the stream permission
|
||||
|
||||
|
||||
if(! perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_stream')) {
|
||||
notice( t('Permissions denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
nav_set_selected('Calendar');
|
||||
|
||||
$sql_extra = permissions_sql($channel['channel_id'],get_observer_hash(),'event');
|
||||
|
||||
$first_day = feature_enabled($channel['channel_id'], 'events_cal_first_day');
|
||||
|
||||
head_add_css('/library/fullcalendar/packages/core/main.min.css');
|
||||
head_add_css('/library/fullcalendar/packages/daygrid/main.min.css');
|
||||
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'])
|
||||
$sql_extra .= " and etype != 'birthday' ";
|
||||
|
||||
$first_day = feature_enabled($channel['channel_id'], 'cal_first_day');
|
||||
$first_day = (($first_day) ? $first_day : 0);
|
||||
|
||||
$htpl = get_markup_template('event_head.tpl');
|
||||
\App::$page['htmlhead'] .= replace_macros($htpl,array(
|
||||
'$baseurl' => z_root(),
|
||||
'$module_url' => '/cal/' . $channel['channel_address'],
|
||||
'$modparams' => 2,
|
||||
'$lang' => \App::$language,
|
||||
'$first_day' => $first_day
|
||||
));
|
||||
|
||||
$o = '';
|
||||
|
||||
$mode = 'view';
|
||||
$y = 0;
|
||||
$m = 0;
|
||||
$ignored = ((x($_REQUEST,'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : '');
|
||||
|
||||
// logger('args: ' . print_r(\App::$argv,true));
|
||||
|
||||
if(argc() > 3 && intval(argv(2)) && intval(argv(3))) {
|
||||
$mode = 'view';
|
||||
$y = intval(argv(2));
|
||||
$m = intval(argv(3));
|
||||
}
|
||||
if(argc() <= 3) {
|
||||
$mode = 'view';
|
||||
$event_id = argv(2);
|
||||
}
|
||||
|
||||
if($mode == 'view') {
|
||||
|
||||
/* edit/create form */
|
||||
if($event_id) {
|
||||
$r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
|
||||
dbesc($event_id),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
if(count($r))
|
||||
$orig_event = $r[0];
|
||||
}
|
||||
|
||||
|
||||
// Passed parameters overrides anything found in the DB
|
||||
if(!x($orig_event))
|
||||
$orig_event = array();
|
||||
|
||||
|
||||
|
||||
$tz = date_default_timezone_get();
|
||||
if(x($orig_event))
|
||||
$tz = (($orig_event['adjust']) ? date_default_timezone_get() : 'UTC');
|
||||
|
||||
$syear = datetime_convert('UTC', $tz, $sdt, 'Y');
|
||||
$smonth = datetime_convert('UTC', $tz, $sdt, 'm');
|
||||
$sday = datetime_convert('UTC', $tz, $sdt, 'd');
|
||||
$shour = datetime_convert('UTC', $tz, $sdt, 'H');
|
||||
$sminute = datetime_convert('UTC', $tz, $sdt, 'i');
|
||||
|
||||
$stext = datetime_convert('UTC',$tz,$sdt);
|
||||
$stext = substr($stext,0,14) . "00:00";
|
||||
|
||||
$fyear = datetime_convert('UTC', $tz, $fdt, 'Y');
|
||||
$fmonth = datetime_convert('UTC', $tz, $fdt, 'm');
|
||||
$fday = datetime_convert('UTC', $tz, $fdt, 'd');
|
||||
$fhour = datetime_convert('UTC', $tz, $fdt, 'H');
|
||||
$fminute = datetime_convert('UTC', $tz, $fdt, 'i');
|
||||
|
||||
$ftext = datetime_convert('UTC',$tz,$fdt);
|
||||
$ftext = substr($ftext,0,14) . "00:00";
|
||||
|
||||
$type = ((x($orig_event)) ? $orig_event['etype'] : 'event');
|
||||
|
||||
$f = get_config('system','event_input_format');
|
||||
if(! $f)
|
||||
$f = 'ymd';
|
||||
|
||||
$catsenabled = feature_enabled($channel['channel_id'],'categories');
|
||||
|
||||
|
||||
$show_bd = perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_contacts');
|
||||
if(! $show_bd) {
|
||||
$sql_extra .= " and event.etype != 'birthday' ";
|
||||
}
|
||||
|
||||
|
||||
$category = '';
|
||||
|
||||
$thisyear = datetime_convert('UTC',date_default_timezone_get(),'now','Y');
|
||||
$thismonth = datetime_convert('UTC',date_default_timezone_get(),'now','m');
|
||||
if(! $y)
|
||||
$y = intval($thisyear);
|
||||
if(! $m)
|
||||
$m = intval($thismonth);
|
||||
|
||||
// Put some limits on dates. The PHP date functions don't seem to do so well before 1900.
|
||||
// An upper limit was chosen to keep search engines from exploring links millions of years in the future.
|
||||
|
||||
if($y < 1901)
|
||||
$y = 1900;
|
||||
if($y > 2099)
|
||||
$y = 2100;
|
||||
|
||||
$nextyear = $y;
|
||||
$nextmonth = $m + 1;
|
||||
if($nextmonth > 12) {
|
||||
$nextmonth = 1;
|
||||
$nextyear ++;
|
||||
}
|
||||
|
||||
$prevyear = $y;
|
||||
if($m > 1)
|
||||
$prevmonth = $m - 1;
|
||||
else {
|
||||
$prevmonth = 12;
|
||||
$prevyear --;
|
||||
}
|
||||
|
||||
$dim = get_dim($y,$m);
|
||||
$start = sprintf('%d-%d-%d %d:%d:%d',$y,$m,1,0,0,0);
|
||||
$finish = sprintf('%d-%d-%d %d:%d:%d',$y,$m,$dim,23,59,59);
|
||||
|
||||
|
||||
if (argv(2) === 'json'){
|
||||
if (x($_GET,'start')) $start = $_GET['start'];
|
||||
if (x($_GET,'end')) $finish = $_GET['end'];
|
||||
}
|
||||
|
||||
$start = datetime_convert('UTC','UTC',$start);
|
||||
$finish = datetime_convert('UTC','UTC',$finish);
|
||||
|
||||
$adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start);
|
||||
$adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish);
|
||||
|
||||
|
||||
if(! perm_is_allowed(\App::$profile['uid'],get_observer_hash(),'view_contacts'))
|
||||
$sql_extra .= " and etype != 'birthday' ";
|
||||
$start = '';
|
||||
$finish = '';
|
||||
|
||||
if (x($_GET,'id')){
|
||||
$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan
|
||||
from event left join item on resource_id = event_hash where resource_type = 'event' and event.uid = %d and event.id = %d $sql_extra limit 1",
|
||||
intval($channel['channel_id']),
|
||||
intval($_GET['id'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
// fixed an issue with "nofinish" events not showing up in the calendar.
|
||||
// There's still an issue if the finish date crosses the end of month.
|
||||
// Noting this for now - it will need to be fixed here and in Friendica.
|
||||
// Ultimately the finish date shouldn't be involved in the query.
|
||||
|
||||
$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan
|
||||
from event left join item on event_hash = resource_id
|
||||
where resource_type = 'event' and event.uid = %d and event.uid = item.uid $ignored
|
||||
AND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )
|
||||
OR ( adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) $sql_extra ",
|
||||
intval($channel['channel_id']),
|
||||
dbesc($start),
|
||||
dbesc($finish),
|
||||
dbesc($adjust_start),
|
||||
dbesc($adjust_finish)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
$links = array();
|
||||
|
||||
if($r) {
|
||||
xchan_query($r);
|
||||
$r = fetch_post_tags($r,true);
|
||||
|
||||
$r = sort_by_date($r);
|
||||
}
|
||||
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j'));
|
||||
if(! x($links,$j))
|
||||
$links[$j] = z_root() . '/' . \App::$cmd . '#link-' . $j;
|
||||
if (argv(2) === 'json') {
|
||||
if (x($_GET,'start')) $start = $_GET['start'];
|
||||
if (x($_GET,'end')) $finish = $_GET['end'];
|
||||
}
|
||||
|
||||
$start = datetime_convert('UTC','UTC',$start);
|
||||
$finish = datetime_convert('UTC','UTC',$finish);
|
||||
$adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start);
|
||||
$adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish);
|
||||
|
||||
if (x($_GET, 'id')) {
|
||||
$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id
|
||||
from event left join item on item.resource_id = event.event_hash
|
||||
where item.resource_type = 'event' and event.uid = %d and event.id = %d $sql_extra limit 1",
|
||||
intval($channel['channel_id']),
|
||||
intval($_GET['id'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
// fixed an issue with "nofinish" events not showing up in the calendar.
|
||||
// There's still an issue if the finish date crosses the end of month.
|
||||
// Noting this for now - it will need to be fixed here and in Friendica.
|
||||
// Ultimately the finish date shouldn't be involved in the query.
|
||||
$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id
|
||||
from event left join item on event.event_hash = item.resource_id
|
||||
where item.resource_type = 'event' and event.uid = %d and event.uid = item.uid
|
||||
AND (( event.adjust = 0 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' )
|
||||
OR ( event.adjust = 1 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' ))
|
||||
$sql_extra",
|
||||
intval($channel['channel_id']),
|
||||
dbesc($start),
|
||||
dbesc($finish),
|
||||
dbesc($adjust_start),
|
||||
dbesc($adjust_finish)
|
||||
);
|
||||
}
|
||||
|
||||
if($r) {
|
||||
xchan_query($r);
|
||||
$r = fetch_post_tags($r,true);
|
||||
$r = sort_by_date($r);
|
||||
}
|
||||
|
||||
$events = [];
|
||||
|
||||
if($r) {
|
||||
|
||||
foreach($r as $rr) {
|
||||
|
||||
$start = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'c'));
|
||||
if ($rr['nofinish']){
|
||||
$end = null;
|
||||
} else {
|
||||
$end = (($rr['adjust']) ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtend'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtend'], 'c'));
|
||||
}
|
||||
}
|
||||
|
||||
$events=array();
|
||||
|
||||
$last_date = '';
|
||||
$fmt = t('l, F j');
|
||||
|
||||
if($r) {
|
||||
|
||||
foreach($r as $rr) {
|
||||
|
||||
$j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j'));
|
||||
$d = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], $fmt) : datetime_convert('UTC','UTC',$rr['dtstart'],$fmt));
|
||||
$d = day_translate($d);
|
||||
|
||||
$start = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'c') : datetime_convert('UTC','UTC',$rr['dtstart'],'c'));
|
||||
if ($rr['nofinish']){
|
||||
$end = null;
|
||||
} else {
|
||||
$end = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtend'], 'c') : datetime_convert('UTC','UTC',$rr['dtend'],'c'));
|
||||
}
|
||||
|
||||
|
||||
$is_first = ($d !== $last_date);
|
||||
|
||||
$last_date = $d;
|
||||
|
||||
$edit = false;
|
||||
|
||||
$drop = false;
|
||||
|
||||
$title = strip_tags(html_entity_decode(bbcode($rr['summary']),ENT_QUOTES,'UTF-8'));
|
||||
if(! $title) {
|
||||
list($title, $_trash) = explode("<br",bbcode($rr['desc']),2);
|
||||
$title = strip_tags(html_entity_decode($title,ENT_QUOTES,'UTF-8'));
|
||||
}
|
||||
|
||||
$html = '';
|
||||
if (x($_GET,'id')) {
|
||||
$rr['timezone'] = $tz;
|
||||
$html = format_event_html($rr);
|
||||
$rr['desc'] = zidify_links(smilies(bbcode($rr['desc'])));
|
||||
$rr['description'] = htmlentities(html2plain(bbcode($rr['description'])),ENT_COMPAT,'UTF-8',false);
|
||||
$rr['location'] = zidify_links(smilies(bbcode($rr['location'])));
|
||||
$events[] = array(
|
||||
'id'=>$rr['id'],
|
||||
'hash' => $rr['event_hash'],
|
||||
'start'=> $start,
|
||||
'end' => $end,
|
||||
'drop' => $drop,
|
||||
'allDay' => false,
|
||||
'title' => $title,
|
||||
|
||||
'j' => $j,
|
||||
'd' => $d,
|
||||
'edit' => $edit,
|
||||
'is_first'=>$is_first,
|
||||
'item'=>$rr,
|
||||
'html'=>$html,
|
||||
'plink' => array($rr['plink'],t('Link to Source'),'',''),
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
$tz = get_iconfig($rr, 'event', 'timezone');
|
||||
if(! $tz)
|
||||
$tz = 'UTC';
|
||||
|
||||
$events[] = array(
|
||||
'calendar_id' => 'channel_calendar',
|
||||
'rw' => true,
|
||||
'id'=>$rr['id'],
|
||||
'uri' => $rr['event_hash'],
|
||||
'timezone' => $tz,
|
||||
'start'=> $start,
|
||||
'end' => $end,
|
||||
'drop' => false,
|
||||
'allDay' => (($rr['adjust']) ? 0 : 1),
|
||||
'title' => html_entity_decode($rr['summary'], ENT_COMPAT, 'UTF-8'),
|
||||
'editable' => false,
|
||||
'item' => $rr,
|
||||
'plink' => [$rr['plink'], t('Link to source')],
|
||||
'description' => html_entity_decode($rr['description'], ENT_COMPAT, 'UTF-8'),
|
||||
'location' => html_entity_decode($rr['location'], ENT_COMPAT, 'UTF-8'),
|
||||
'allow_cid' => expand_acl($rr['allow_cid']),
|
||||
'allow_gid' => expand_acl($rr['allow_gid']),
|
||||
'deny_cid' => expand_acl($rr['deny_cid']),
|
||||
'deny_gid' => expand_acl($rr['deny_gid']),
|
||||
'html' => $html
|
||||
);
|
||||
}
|
||||
|
||||
if (argv(2) === 'json'){
|
||||
echo json_encode($events); killme();
|
||||
}
|
||||
|
||||
// links: array('href', 'text', 'extra css classes', 'title')
|
||||
if (x($_GET,'id')){
|
||||
$tpl = get_markup_template("event_cal.tpl");
|
||||
}
|
||||
else {
|
||||
$tpl = get_markup_template("events_cal-js.tpl");
|
||||
}
|
||||
|
||||
$nick = $channel['channel_address'];
|
||||
|
||||
$o = replace_macros($tpl, array(
|
||||
'$baseurl' => z_root(),
|
||||
'$new_event' => array(z_root().'/cal',(($event_id) ? t('Edit Event') : t('Create Event')),'',''),
|
||||
'$previus' => array(z_root()."/cal/$nick/$prevyear/$prevmonth",t('Previous'),'',''),
|
||||
'$next' => array(z_root()."/cal/$nick/$nextyear/$nextmonth",t('Next'),'',''),
|
||||
'$export' => array(z_root()."/cal/$nick/$y/$m/export",t('Export'),'',''),
|
||||
'$calendar' => cal($y,$m,$links, ' eventcal'),
|
||||
'$events' => $events,
|
||||
'$upload' => t('Import'),
|
||||
'$submit' => t('Submit'),
|
||||
'$prev' => t('Previous'),
|
||||
'$next' => t('Next'),
|
||||
'$today' => t('Today'),
|
||||
'$form' => $form,
|
||||
'$expandform' => ((x($_GET,'expandform')) ? true : false)
|
||||
));
|
||||
|
||||
if (x($_GET,'id')){ echo $o; killme(); }
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
if (argv(2) === 'json') {
|
||||
echo json_encode($events);
|
||||
killme();
|
||||
}
|
||||
|
||||
if (x($_GET,'id')) {
|
||||
$o = replace_macros(get_markup_template("cal_event.tpl"), [
|
||||
'$events' => $events
|
||||
]);
|
||||
echo $o;
|
||||
killme();
|
||||
}
|
||||
|
||||
$nick = $channel['channel_address'];
|
||||
|
||||
$sources = '{
|
||||
id: \'channel_calendar\',
|
||||
url: \'/cal/' . $nick . '/json/\',
|
||||
color: \'#3a87ad\'
|
||||
}';
|
||||
|
||||
$o = replace_macros(get_markup_template("cal_calendar.tpl"), [
|
||||
'$sources' => $sources,
|
||||
'$lang' => App::$language,
|
||||
'$timezone' => date_default_timezone_get(),
|
||||
'$first_day' => $first_day,
|
||||
'$prev' => t('Previous'),
|
||||
'$next' => t('Next'),
|
||||
'$today' => t('Today'),
|
||||
'$title' => '',
|
||||
'$dtstart' => '',
|
||||
'$dtend' => '',
|
||||
'$nick' => $nick
|
||||
]);
|
||||
|
||||
return $o;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -63,9 +63,9 @@ class Card_edit extends \Zotlabs\Web\Controller {
|
||||
|
||||
if ($catsenabled){
|
||||
$itm = fetch_post_tags($itm);
|
||||
|
||||
|
||||
$cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
|
||||
|
||||
|
||||
foreach ($cats as $cat) {
|
||||
if (strlen($category))
|
||||
$category .= ', ';
|
||||
@@ -114,6 +114,7 @@ class Card_edit extends \Zotlabs\Web\Controller {
|
||||
'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'])),
|
||||
|
||||
@@ -110,7 +110,7 @@ class Cards extends Controller {
|
||||
'webpage' => ITEM_TYPE_CARD,
|
||||
'is_owner' => true,
|
||||
'content_label' => t('Add Card'),
|
||||
'button' => t('Create'),
|
||||
'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'),
|
||||
@@ -145,7 +145,7 @@ class Cards extends Controller {
|
||||
|
||||
|
||||
$itemspage = get_pconfig(local_channel(),'system','itemspage');
|
||||
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20));
|
||||
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10));
|
||||
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start']));
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,7 @@ class Changeaddr extends \Zotlabs\Web\Controller {
|
||||
|
||||
if($account['account_password_changed'] > NULL_DATE) {
|
||||
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
|
||||
if($account['account_password_changed'] > d1) {
|
||||
if($account['account_password_changed'] > $d1) {
|
||||
notice( t('Channel name changes are not allowed within 48 hours of changing the account password.') . EOL);
|
||||
return;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ class Changeaddr extends \Zotlabs\Web\Controller {
|
||||
|
||||
if(check_webbie(array($new_address)) !== $new_address) {
|
||||
notice( t('Nickname has unsupported characters or is already being used on this site.') . EOL);
|
||||
return $ret;
|
||||
return;
|
||||
}
|
||||
|
||||
channel_change_address($channel,$new_address);
|
||||
|
||||
@@ -4,103 +4,136 @@ namespace Zotlabs\Module;
|
||||
|
||||
|
||||
use App;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\PermissionDescription;
|
||||
use Zotlabs\Zot6\HTTPSig;
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\Crypto;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\PermissionDescription;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Web\HTTPSig;
|
||||
|
||||
require_once('include/items.php');
|
||||
require_once('include/security.php');
|
||||
require_once('include/conversation.php');
|
||||
require_once('include/acl_selectors.php');
|
||||
require_once('include/opengraph.php');
|
||||
|
||||
|
||||
/**
|
||||
* @brief Channel Controller
|
||||
*
|
||||
*/
|
||||
|
||||
class Channel extends Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
if(in_array(substr($_GET['search'],0,1),[ '@', '!', '?']))
|
||||
goaway('search' . '?f=&search=' . $_GET['search']);
|
||||
if (array_key_exists('search', $_GET) && (in_array(substr($_GET['search'], 0, 1), ['@', '!', '?']) || strpos($_GET['search'], 'https://') === 0))
|
||||
goaway(z_root() . '/search?f=&search=' . $_GET['search']);
|
||||
|
||||
$which = null;
|
||||
if(argc() > 1)
|
||||
if (argc() > 1)
|
||||
$which = argv(1);
|
||||
if(! $which) {
|
||||
if(local_channel()) {
|
||||
if (!$which) {
|
||||
if (local_channel()) {
|
||||
$channel = App::get_channel();
|
||||
if($channel && $channel['channel_address'])
|
||||
$which = $channel['channel_address'];
|
||||
if ($channel && $channel['channel_address'])
|
||||
$which = $channel['channel_address'];
|
||||
}
|
||||
}
|
||||
if(! $which) {
|
||||
notice( t('You must be logged in to see this page.') . EOL );
|
||||
if (!$which) {
|
||||
notice(t('You must be logged in to see this page.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$profile = 0;
|
||||
$channel = App::get_channel();
|
||||
|
||||
if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) {
|
||||
$which = $channel['channel_address'];
|
||||
$profile = argv(1);
|
||||
}
|
||||
if ((local_channel()) && (argc() > 2) && (argv(2) === 'view')) {
|
||||
$which = $channel['channel_address'];
|
||||
$profile = argv(1);
|
||||
}
|
||||
|
||||
$channel = channelx_by_nick($which);
|
||||
if(! $channel) {
|
||||
http_status_exit(404, 'Not found');
|
||||
}
|
||||
if (!$channel) {
|
||||
http_status_exit(404, 'Not found');
|
||||
}
|
||||
|
||||
// handle zot6 channel discovery
|
||||
// handle zot6 channel discovery
|
||||
|
||||
if(Libzot::is_zot_request()) {
|
||||
|
||||
$sigdata = HTTPSig::verify(file_get_contents('php://input'));
|
||||
if (Libzot::is_zot_request()) {
|
||||
|
||||
if($sigdata && $sigdata['signer'] && $sigdata['header_valid']) {
|
||||
$data = json_encode(Libzot::zotinfo([ 'address' => $channel['channel_address'], 'target_url' => $sigdata['signer'] ]));
|
||||
$s = q("select site_crypto, hubloc_sitekey from site left join hubloc on hubloc_url = site_url where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
|
||||
$sigdata = HTTPSig::verify(file_get_contents('php://input'), EMPTY_STR, 'zot6');
|
||||
|
||||
if ($sigdata && $sigdata['signer'] && $sigdata['header_valid']) {
|
||||
$data = json_encode(Libzot::zotinfo(['address' => $channel['channel_address'], 'target_url' => $sigdata['signer']]));
|
||||
$s = q("select site_crypto, hubloc_sitekey from site left join hubloc on hubloc_url = site_url where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
|
||||
dbesc($sigdata['signer'])
|
||||
);
|
||||
|
||||
if($s) {
|
||||
$data = json_encode(crypto_encapsulate($data,$s[0]['hubloc_sitekey'],Libzot::best_algorithm($s[0]['site_crypto'])));
|
||||
if ($s) {
|
||||
$data = json_encode(Crypto::encapsulate($data, $s[0]['hubloc_sitekey'], Libzot::best_algorithm($s[0]['site_crypto'])));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$data = json_encode(Libzot::zotinfo([ 'address' => $channel['channel_address'] ]));
|
||||
$data = json_encode(Libzot::zotinfo(['guid_hash' => $channel['channel_hash']]));
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'Content-Type' => 'application/x-zot+json',
|
||||
$headers = [
|
||||
'Content-Type' => 'application/x-zot+json',
|
||||
'Digest' => HTTPSig::generate_digest_header($data),
|
||||
'(request-target)' => strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']
|
||||
];
|
||||
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel));
|
||||
];
|
||||
$h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel));
|
||||
HTTPSig::set_headers($h);
|
||||
echo $data;
|
||||
killme();
|
||||
}
|
||||
|
||||
if (ActivityStreams::is_as_request($channel)) {
|
||||
|
||||
if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) {
|
||||
$which = $channel['channel_address'];
|
||||
// Somebody may attempt an ActivityStreams fetch on one of our message permalinks
|
||||
// Make it do the right thing.
|
||||
|
||||
$mid = ((x($_REQUEST, 'mid')) ? $_REQUEST['mid'] : '');
|
||||
if ($mid && strpos($mid, 'b64.') === 0) {
|
||||
$decoded = @base64url_decode(substr($mid, 4));
|
||||
if ($decoded) {
|
||||
$mid = $decoded;
|
||||
}
|
||||
}
|
||||
if ($mid) {
|
||||
$obj = null;
|
||||
if (strpos($mid, z_root() . '/item/') === 0) {
|
||||
App::$argc = 2;
|
||||
App::$argv = ['item', basename($mid)];
|
||||
$obj = new Item();
|
||||
}
|
||||
if (strpos($mid, z_root() . '/activity/') === 0) {
|
||||
App::$argc = 2;
|
||||
App::$argv = ['activity', basename($mid)];
|
||||
$obj = new Activity();
|
||||
}
|
||||
if ($obj) {
|
||||
$obj->init();
|
||||
}
|
||||
}
|
||||
as_return_and_die(Activity::encode_person($channel, true), $channel);
|
||||
}
|
||||
|
||||
if ((local_channel()) && (argc() > 2) && (argv(2) === 'view')) {
|
||||
$which = $channel['channel_address'];
|
||||
$profile = argv(1);
|
||||
}
|
||||
|
||||
head_add_link( [
|
||||
'rel' => 'alternate',
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Posts and comments'),
|
||||
'href' => z_root() . '/feed/' . $which
|
||||
]);
|
||||
|
||||
head_add_link( [
|
||||
'rel' => 'alternate',
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Only posts'),
|
||||
'href' => z_root() . '/feed/' . $which . '?f=&top=1'
|
||||
@@ -109,115 +142,121 @@ class Channel extends Controller {
|
||||
|
||||
// Run profile_load() here to make sure the theme is set before
|
||||
// we start loading content
|
||||
profile_load($which, $profile);
|
||||
|
||||
profile_load($which,$profile);
|
||||
// Add Opengraph markup
|
||||
$mid = ((x($_REQUEST, 'mid')) ? $_REQUEST['mid'] : '');
|
||||
if (strpos($mid, 'b64.') === 0)
|
||||
$mid = @base64url_decode(substr($mid, 4));
|
||||
|
||||
if ($mid)
|
||||
$r = q("SELECT * FROM item WHERE mid = '%s' AND uid = %d AND item_private = 0 LIMIT 1",
|
||||
dbesc($mid),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
|
||||
opengraph_add_meta((isset($r) && count($r) ? $r[0] : []), $channel);
|
||||
}
|
||||
|
||||
function get($update = 0, $load = false) {
|
||||
|
||||
$noscript_content = get_config('system', 'noscript_content', '1');
|
||||
|
||||
if($load)
|
||||
$_SESSION['loadtime'] = datetime_convert();
|
||||
|
||||
$category = $datequery = $datequery2 = '';
|
||||
|
||||
$mid = ((x($_REQUEST,'mid')) ? $_REQUEST['mid'] : '');
|
||||
$mid = ((x($_REQUEST, 'mid')) ? $_REQUEST['mid'] : '');
|
||||
|
||||
if(strpos($mid,'b64.') === 0)
|
||||
$decoded = @base64url_decode(substr($mid,4));
|
||||
if($decoded)
|
||||
if (strpos($mid, 'b64.') === 0)
|
||||
$decoded = @base64url_decode(substr($mid, 4));
|
||||
if (isset($decoded))
|
||||
$mid = $decoded;
|
||||
|
||||
$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']) : '');
|
||||
$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']) : '');
|
||||
|
||||
if(observer_prohibited(true)) {
|
||||
if (observer_prohibited(true)) {
|
||||
return login();
|
||||
}
|
||||
|
||||
$category = ((x($_REQUEST,'cat')) ? $_REQUEST['cat'] : '');
|
||||
$hashtags = ((x($_REQUEST,'tag')) ? $_REQUEST['tag'] : '');
|
||||
$order = ((x($_GET,'order')) ? notags($_GET['order']) : 'post');
|
||||
$static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0);
|
||||
$search = ((x($_GET,'search')) ? $_GET['search'] : EMPTY_STR);
|
||||
$category = ((x($_REQUEST, 'cat')) ? $_REQUEST['cat'] : '');
|
||||
$hashtags = ((x($_REQUEST, 'tag')) ? $_REQUEST['tag'] : '');
|
||||
$order = ((x($_GET, 'order')) ? notags($_GET['order']) : 'post');
|
||||
$search = ((x($_GET, 'search')) ? $_GET['search'] : EMPTY_STR);
|
||||
|
||||
$groups = array();
|
||||
$groups = [];
|
||||
|
||||
$o = '';
|
||||
|
||||
if($update) {
|
||||
if ($update) {
|
||||
// Ensure we've got a profile owner if updating.
|
||||
App::$profile['profile_uid'] = App::$profile_uid = $update;
|
||||
}
|
||||
|
||||
$is_owner = (((local_channel()) && (App::$profile['profile_uid'] == local_channel())) ? true : false);
|
||||
|
||||
$channel = App::get_channel();
|
||||
$channel = App::get_channel();
|
||||
$observer = App::get_observer();
|
||||
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
|
||||
$perms = get_all_perms(App::$profile['profile_uid'],$ob_hash);
|
||||
$perms = get_all_perms(App::$profile['profile_uid'], $ob_hash);
|
||||
|
||||
if(! $perms['view_stream']) {
|
||||
if (!$perms['view_stream']) {
|
||||
// We may want to make the target of this redirect configurable
|
||||
if($perms['view_profile']) {
|
||||
notice( t('Insufficient permissions. Request redirected to profile page.') . EOL);
|
||||
goaway (z_root() . "/profile/" . App::$profile['channel_address']);
|
||||
if ($perms['view_profile']) {
|
||||
notice(t('Insufficient permissions. Request redirected to profile page.') . EOL);
|
||||
goaway(z_root() . "/profile/" . App::$profile['channel_address']);
|
||||
}
|
||||
notice( t('Permission denied.') . EOL);
|
||||
notice(t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(! $update) {
|
||||
if (!$update) {
|
||||
|
||||
nav_set_selected('Channel Home');
|
||||
|
||||
$static = channel_manual_conv_update(App::$profile['profile_uid']);
|
||||
|
||||
// search terms header
|
||||
if($search) {
|
||||
$o .= replace_macros(get_markup_template("section_title.tpl"),array(
|
||||
'$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT,'UTF-8')
|
||||
));
|
||||
if ($search) {
|
||||
$o .= replace_macros(get_markup_template("section_title.tpl"), [
|
||||
'$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8')
|
||||
]);
|
||||
}
|
||||
|
||||
if($channel && $is_owner) {
|
||||
$channel_acl = array(
|
||||
if ($channel && $is_owner) {
|
||||
$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']
|
||||
);
|
||||
'deny_cid' => $channel['channel_deny_cid'],
|
||||
'deny_gid' => $channel['channel_deny_gid']
|
||||
];
|
||||
}
|
||||
else {
|
||||
$channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ];
|
||||
$channel_acl = ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => ''];
|
||||
}
|
||||
|
||||
|
||||
if($perms['post_wall']) {
|
||||
if ($perms['post_wall']) {
|
||||
|
||||
$x = array(
|
||||
'is_owner' => $is_owner,
|
||||
'allow_location' => ((($is_owner || $observer) && (intval(get_pconfig(App::$profile['profile_uid'],'system','use_browser_location')))) ? true : false),
|
||||
'default_location' => (($is_owner) ? App::$profile['channel_location'] : ''),
|
||||
'nickname' => App::$profile['channel_address'],
|
||||
'lockstate' => (((strlen(App::$profile['channel_allow_cid'])) || (strlen(App::$profile['channel_allow_gid'])) || (strlen(App::$profile['channel_deny_cid'])) || (strlen(App::$profile['channel_deny_gid']))) ? 'lock' : 'unlock'),
|
||||
'acl' => (($is_owner) ? populate_acl($channel_acl,true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post') : ''),
|
||||
'permissions' => $channel_acl,
|
||||
'showacl' => (($is_owner) ? 'yes' : ''),
|
||||
'bang' => '',
|
||||
'visitor' => (($is_owner || $observer) ? true : false),
|
||||
'profile_uid' => App::$profile['profile_uid'],
|
||||
$x = [
|
||||
'is_owner' => $is_owner,
|
||||
'allow_location' => ((($is_owner || $observer) && (intval(get_pconfig(App::$profile['profile_uid'], 'system', 'use_browser_location')))) ? true : false),
|
||||
'default_location' => (($is_owner) ? App::$profile['channel_location'] : ''),
|
||||
'nickname' => App::$profile['channel_address'],
|
||||
'lockstate' => (((strlen(App::$profile['channel_allow_cid'])) || (strlen(App::$profile['channel_allow_gid'])) || (strlen(App::$profile['channel_deny_cid'])) || (strlen(App::$profile['channel_deny_gid']))) ? 'lock' : 'unlock'),
|
||||
'acl' => (($is_owner) ? populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post') : ''),
|
||||
'permissions' => $channel_acl,
|
||||
'showacl' => (($is_owner) ? 'yes' : ''),
|
||||
'bang' => '',
|
||||
'visitor' => (($is_owner || $observer) ? true : false),
|
||||
'profile_uid' => App::$profile['profile_uid'],
|
||||
'editor_autocomplete' => true,
|
||||
'bbco_autocomplete' => 'bbcode',
|
||||
'bbcode' => true,
|
||||
'jotnets' => true,
|
||||
'reset' => t('Reset form')
|
||||
);
|
||||
'bbco_autocomplete' => 'bbcode',
|
||||
'bbcode' => true,
|
||||
'jotnets' => true,
|
||||
'reset' => t('Reset form')
|
||||
];
|
||||
|
||||
$o .= status_editor($a,$x,false,'Channel');
|
||||
$o .= status_editor($a, $x, false, 'Channel');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -227,23 +266,29 @@ class Channel extends Controller {
|
||||
* Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups
|
||||
*/
|
||||
|
||||
$item_normal = item_normal();
|
||||
$item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0
|
||||
and item.item_unpublished = 0 and item.item_pending_remove = 0
|
||||
and item.item_blocked = 0 ";
|
||||
if (!$is_owner)
|
||||
$item_normal .= "and item.item_delayed = 0 ";
|
||||
$item_normal_update = item_normal_update();
|
||||
$sql_extra = item_permissions_sql(App::$profile['profile_uid']);
|
||||
$sql_extra = item_permissions_sql(App::$profile['profile_uid']);
|
||||
|
||||
if(feature_enabled(App::$profile['profile_uid'], 'channel_list_mode') && (! $mid))
|
||||
if (feature_enabled(App::$profile['profile_uid'], 'channel_list_mode') && (!$mid))
|
||||
$page_mode = 'list';
|
||||
else
|
||||
$page_mode = 'client';
|
||||
|
||||
$abook_uids = " and abook.abook_channel = " . intval(App::$profile['profile_uid']) . " ";
|
||||
|
||||
$simple_update = (($update) ? " AND item_unseen = 1 " : '');
|
||||
$simple_update = '';
|
||||
if ($update && $_SESSION['loadtime'])
|
||||
$simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime']) . "' ) ";
|
||||
|
||||
if($search) {
|
||||
if ($search) {
|
||||
$search = escape_tags($search);
|
||||
if(strpos($search,'#') === 0) {
|
||||
$sql_extra .= term_query('item',substr($search,1),TERM_HASHTAG,TERM_COMMUNITYTAG);
|
||||
if (strpos($search, '#') === 0) {
|
||||
$sql_extra .= term_query('item', substr($search, 1), TERM_HASHTAG, TERM_COMMUNITYTAG);
|
||||
}
|
||||
else {
|
||||
$sql_extra .= sprintf(" AND (item.body like '%s' OR item.title like '%s') ",
|
||||
@@ -253,31 +298,21 @@ class Channel extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
head_add_link([
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/json+oembed',
|
||||
'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . App::$query_string),
|
||||
'title' => 'oembed'
|
||||
]);
|
||||
|
||||
if($update && $_SESSION['loadtime'])
|
||||
$simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) ";
|
||||
if($load)
|
||||
$simple_update = '';
|
||||
if (($update) && (!$load)) {
|
||||
|
||||
if($static && $simple_update)
|
||||
$simple_update .= " and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' ";
|
||||
|
||||
if(($update) && (! $load)) {
|
||||
|
||||
if($mid) {
|
||||
$r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal_update
|
||||
if ($mid) {
|
||||
$r = q("SELECT parent AS item_id from item where mid = '%s' and uid = %d $item_normal_update
|
||||
AND item_wall = 1 $simple_update $sql_extra limit 1",
|
||||
dbesc($mid . '%'),
|
||||
dbesc($mid),
|
||||
intval(App::$profile['profile_uid'])
|
||||
);
|
||||
$_SESSION['loadtime'] = datetime_convert();
|
||||
}
|
||||
else {
|
||||
$r = q("SELECT parent AS item_id from item
|
||||
@@ -289,73 +324,68 @@ class Channel extends Controller {
|
||||
ORDER BY created DESC",
|
||||
intval(App::$profile['profile_uid'])
|
||||
);
|
||||
$_SESSION['loadtime'] = datetime_convert();
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
if(x($category)) {
|
||||
$sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'],'item', $category, TERM_CATEGORY));
|
||||
$sql_extra2 = '';
|
||||
if (x($category)) {
|
||||
$sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'], 'item', $category, TERM_CATEGORY));
|
||||
}
|
||||
if(x($hashtags)) {
|
||||
$sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'],'item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG));
|
||||
if (x($hashtags)) {
|
||||
$sql_extra2 .= protect_sprintf(term_item_parent_query(App::$profile['profile_uid'], 'item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG));
|
||||
}
|
||||
|
||||
if($datequery) {
|
||||
$sql_extra2 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery))));
|
||||
$order = 'post';
|
||||
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 ($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 ";
|
||||
}
|
||||
|
||||
if($order === 'post')
|
||||
if ($order === 'post')
|
||||
$ordering = "created";
|
||||
else
|
||||
$ordering = "commented";
|
||||
|
||||
|
||||
$itemspage = get_pconfig(local_channel(),'system','itemspage');
|
||||
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20));
|
||||
$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']));
|
||||
|
||||
if($noscript_content || $load) {
|
||||
if($mid) {
|
||||
$r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal
|
||||
if ($noscript_content || $load) {
|
||||
if ($mid) {
|
||||
$r = q("SELECT parent AS item_id from item where mid = '%s' and uid = %d $item_normal
|
||||
AND item_wall = 1 $sql_extra limit 1",
|
||||
dbesc($mid . '%'),
|
||||
dbesc($mid),
|
||||
intval(App::$profile['profile_uid'])
|
||||
);
|
||||
if (! $r) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
if (!$r) {
|
||||
notice(t('Permission denied.') . EOL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$r = q("SELECT DISTINCT item.parent AS item_id, $ordering FROM item
|
||||
$r = q("SELECT DISTINCT item.parent AS item_id, $ordering FROM item
|
||||
left join abook on ( item.author_xchan = abook.abook_xchan $abook_uids )
|
||||
WHERE true and item.uid = %d $item_normal
|
||||
AND (abook.abook_blocked = 0 or abook.abook_flags is null)
|
||||
AND item.item_wall = 1 AND item.item_thread_top = 1
|
||||
$sql_extra $sql_extra2
|
||||
ORDER BY $ordering DESC $pager_sql ",
|
||||
$sql_extra $sql_extra2
|
||||
ORDER BY $ordering DESC, item_id $pager_sql ",
|
||||
intval(App::$profile['profile_uid'])
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$r = array();
|
||||
$r = [];
|
||||
}
|
||||
}
|
||||
if($r) {
|
||||
if ($r) {
|
||||
|
||||
$parents_str = ids_to_querystr($r,'item_id');
|
||||
$parents_str = ids_to_querystr($r, 'item_id');
|
||||
|
||||
$items = q("SELECT item.*, item.id AS item_id
|
||||
$r = q("SELECT item.*, item.id AS item_id
|
||||
FROM item
|
||||
WHERE item.uid = %d $item_normal
|
||||
AND item.parent IN ( %s )
|
||||
@@ -364,112 +394,89 @@ class Channel extends Controller {
|
||||
dbesc($parents_str)
|
||||
);
|
||||
|
||||
xchan_query($items);
|
||||
$items = fetch_post_tags($items, true);
|
||||
$items = conv_sort($items,$ordering);
|
||||
xchan_query($r);
|
||||
$items = fetch_post_tags($r, true);
|
||||
$items = conv_sort($items, $ordering);
|
||||
|
||||
if($load && $mid && (! count($items))) {
|
||||
if ($load && $mid && (!count($items))) {
|
||||
// This will happen if we don't have sufficient permissions
|
||||
// to view the parent item (or the item itself if it is toplevel)
|
||||
notice( t('Permission denied.') . EOL);
|
||||
notice(t('Permission denied.') . EOL);
|
||||
}
|
||||
|
||||
} else {
|
||||
$items = array();
|
||||
}
|
||||
else {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
if((! $update) && (! $load)) {
|
||||
// Add pinned content
|
||||
if (!x($_REQUEST, 'mid') && !$search) {
|
||||
$pinned = new \Zotlabs\Widget\Pinned;
|
||||
$r = $pinned->widget(intval(App::$profile['profile_uid']), [ITEM_TYPE_POST]);
|
||||
$o .= $r['html'];
|
||||
}
|
||||
|
||||
if($decoded)
|
||||
$mode = (($search) ? 'search' : 'channel');
|
||||
|
||||
if ((!$update) && (!$load)) {
|
||||
|
||||
if (isset($decoded))
|
||||
$mid = 'b64.' . base64url_encode($mid);
|
||||
|
||||
// This is ugly, but we can't pass the profile_uid through the session to the ajax updater,
|
||||
// because browser prefetching might change it on us. We have to deliver it with the page.
|
||||
|
||||
$maxheight = get_pconfig(App::$profile['profile_uid'],'system','channel_divmore_height');
|
||||
if(! $maxheight)
|
||||
$maxheight = get_pconfig(App::$profile['profile_uid'], 'system', 'channel_divmore_height');
|
||||
if (!$maxheight)
|
||||
$maxheight = 400;
|
||||
|
||||
$o .= '<div id="live-channel"></div>' . "\r\n";
|
||||
$o .= "<script> var profile_uid = " . App::$profile['profile_uid']
|
||||
. "; var netargs = '?f='; var profile_page = " . App::$pager['page']
|
||||
. "; divmore_height = " . intval($maxheight) . "; </script>\r\n";
|
||||
|
||||
App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array(
|
||||
'$baseurl' => z_root(),
|
||||
'$pgtype' => 'channel',
|
||||
'$uid' => ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : '0'),
|
||||
'$gid' => '0',
|
||||
'$cid' => '0',
|
||||
'$cmin' => '(-1)',
|
||||
'$cmax' => '(-1)',
|
||||
'$star' => '0',
|
||||
'$liked' => '0',
|
||||
'$conv' => '0',
|
||||
'$spam' => '0',
|
||||
'$nouveau' => '0',
|
||||
'$wall' => '1',
|
||||
'$fh' => '0',
|
||||
'$static' => $static,
|
||||
'$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1),
|
||||
'$search' => $search,
|
||||
'$xchan' => '',
|
||||
'$order' => $order,
|
||||
'$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0),
|
||||
'$file' => '',
|
||||
'$cats' => (($category) ? urlencode($category) : ''),
|
||||
'$tags' => (($hashtags) ? urlencode($hashtags) : ''),
|
||||
'$mid' => $mid,
|
||||
'$verb' => '',
|
||||
'$net' => '',
|
||||
'$dend' => $datequery,
|
||||
'$dbegin' => $datequery2
|
||||
));
|
||||
. "; divmore_height = " . intval($maxheight) . ";</script>\r\n";
|
||||
|
||||
App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), [
|
||||
'$baseurl' => z_root(),
|
||||
'$pgtype' => 'channel',
|
||||
'$uid' => ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : '0'),
|
||||
'$gid' => '0',
|
||||
'$cid' => '0',
|
||||
'$cmin' => '(-1)',
|
||||
'$cmax' => '(-1)',
|
||||
'$star' => '0',
|
||||
'$liked' => '0',
|
||||
'$conv' => '0',
|
||||
'$spam' => '0',
|
||||
'$nouveau' => '0',
|
||||
'$wall' => '1',
|
||||
'$fh' => '0',
|
||||
'$dm' => '0',
|
||||
'$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1),
|
||||
'$search' => $search,
|
||||
'$xchan' => '',
|
||||
'$order' => (($order) ? urlencode($order) : ''),
|
||||
'$list' => ((x($_REQUEST, 'list')) ? intval($_REQUEST['list']) : 0),
|
||||
'$file' => '',
|
||||
'$cats' => (($category) ? urlencode($category) : ''),
|
||||
'$tags' => (($hashtags) ? urlencode($hashtags) : ''),
|
||||
'$mid' => (($mid) ? urlencode($mid) : ''),
|
||||
'$verb' => '',
|
||||
'$net' => '',
|
||||
'$dend' => $datequery,
|
||||
'$dbegin' => $datequery2,
|
||||
'$conv_mode' => 'channel',
|
||||
'$page_mode' => $page_mode
|
||||
]);
|
||||
}
|
||||
|
||||
$update_unseen = '';
|
||||
|
||||
if($page_mode === 'list') {
|
||||
|
||||
/**
|
||||
* in "list mode", only mark the parent item and any like activities as "seen".
|
||||
* We won't distinguish between comment likes and post likes. The important thing
|
||||
* is that the number of unseen comments will be accurate. The SQL to separate the
|
||||
* comment likes could also get somewhat hairy.
|
||||
*/
|
||||
|
||||
if($parents_str) {
|
||||
$update_unseen = " AND ( id IN ( " . dbesc($parents_str) . " )";
|
||||
$update_unseen .= " OR ( parent IN ( " . dbesc($parents_str) . " ) AND verb in ( '" . dbesc(ACTIVITY_LIKE) . "','" . dbesc(ACTIVITY_DISLIKE) . "' ))) ";
|
||||
}
|
||||
}
|
||||
else {
|
||||
if($parents_str) {
|
||||
$update_unseen = " AND parent IN ( " . dbesc($parents_str) . " )";
|
||||
}
|
||||
}
|
||||
|
||||
if($is_owner && $update_unseen) {
|
||||
$x = [ 'channel_id' => local_channel(), 'update' => 'unset' ];
|
||||
call_hooks('update_unseen',$x);
|
||||
if($x['update'] === 'unset' || intval($x['update'])) {
|
||||
$r = q("UPDATE item SET item_unseen = 0 where item_unseen = 1 and item_wall = 1 AND uid = %d $update_unseen",
|
||||
intval(local_channel())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$mode = (($search) ? 'search' : 'channel');
|
||||
|
||||
if($update) {
|
||||
$o .= conversation($items,$mode,$update,$page_mode);
|
||||
if ($update) {
|
||||
$o .= conversation($items, $mode, $update, $page_mode);
|
||||
}
|
||||
else {
|
||||
|
||||
$o .= '<noscript>';
|
||||
if($noscript_content) {
|
||||
$o .= conversation($items,$mode,$update,'traditional');
|
||||
if ($noscript_content) {
|
||||
$o .= conversation($items, $mode, $update, 'traditional');
|
||||
$o .= alt_pager(count($items));
|
||||
}
|
||||
else {
|
||||
@@ -477,16 +484,18 @@ class Channel extends Controller {
|
||||
}
|
||||
$o .= '</noscript>';
|
||||
|
||||
$o .= conversation($items,$mode,$update,$page_mode);
|
||||
$o .= conversation($items, $mode, $update, $page_mode);
|
||||
|
||||
if ($mid && $items[0]['title'])
|
||||
if ($mid && count($items) > 0 && isset($items[0]['title']))
|
||||
App::$page['title'] = $items[0]['title'] . " - " . App::$page['title'];
|
||||
|
||||
}
|
||||
|
||||
if($mid)
|
||||
if ($mid)
|
||||
$o .= '<div id="content-complete"></div>';
|
||||
|
||||
$_SESSION['loadtime'] = datetime_convert();
|
||||
|
||||
return $o;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user