Compare commits
4624 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc2b948f1f | ||
|
|
ff9442474d | ||
|
|
92996e550c | ||
|
|
c3c4254882 | ||
|
|
751c4c3169 | ||
|
|
f783d594cc | ||
|
|
6e32be7e43 | ||
|
|
4471c45fb7 | ||
|
|
a165303602 | ||
|
|
7251ce6e17 | ||
|
|
a894456550 | ||
|
|
1eba847e99 | ||
|
|
729d52f10a | ||
|
|
79e8e4599e | ||
|
|
ba5cfd8b25 | ||
|
|
288905c361 | ||
|
|
3071f3b71c | ||
|
|
07d443a159 | ||
|
|
1acfe53f74 | ||
|
|
265eb2b440 | ||
|
|
69e145a630 | ||
|
|
1a28720c56 | ||
|
|
805cae7b31 | ||
|
|
1070f3ed6b | ||
|
|
2467698045 | ||
|
|
b6c4e6a6d0 | ||
|
|
8e3e90ddd2 | ||
|
|
8a69a5c3c7 | ||
|
|
e819727b09 | ||
|
|
4b1f87050f | ||
|
|
919de44a71 | ||
|
|
d59095c2e6 | ||
|
|
65e18f5b8f | ||
|
|
e9243e9660 | ||
|
|
59645f51a9 | ||
|
|
dd3e530a63 | ||
|
|
3e2e60fb86 | ||
|
|
9bab858fd1 | ||
|
|
0ef8c5bda8 | ||
|
|
d4be96cd03 | ||
|
|
3d0c6700db | ||
|
|
5012baba15 | ||
|
|
41c67fa345 | ||
|
|
c75852455b | ||
|
|
27fa15e95d | ||
|
|
175e222927 | ||
|
|
55924f5c5b | ||
|
|
956f0043fc | ||
|
|
70cdbe4816 | ||
|
|
6644dc4861 | ||
|
|
3e144baacd | ||
|
|
bbacfbdd6a | ||
|
|
cbc0e5b934 | ||
|
|
81d556d32e | ||
|
|
5ad5a0fa07 | ||
|
|
e54ba7ecbc | ||
|
|
1c1d1f1185 | ||
|
|
bc1812840a | ||
|
|
74def75ca3 | ||
|
|
924b353e5a | ||
|
|
31618a4310 | ||
|
|
21e3481810 | ||
|
|
d7ba2c7168 | ||
|
|
6f811fd7f9 | ||
|
|
573846707c | ||
|
|
02e487f306 | ||
|
|
73b28b5d94 | ||
|
|
2a7ec29cd1 | ||
|
|
d4f8a8dedd | ||
|
|
f23c380cae | ||
|
|
7151467db7 | ||
|
|
16a8416495 | ||
|
|
5db3b71c6a | ||
|
|
8897c5763a | ||
|
|
07d92796d2 | ||
|
|
1f39c16d99 | ||
|
|
c8aeb5b160 | ||
|
|
69b08a2062 | ||
|
|
05fd5eb8be | ||
|
|
8ed0913df6 | ||
|
|
d85bcb2e37 | ||
|
|
09e5732f8a | ||
|
|
d7fcf31be5 | ||
|
|
f63aaa5429 | ||
|
|
321241da02 | ||
|
|
7c72886b1b | ||
|
|
2a52592292 | ||
|
|
ccdfbc721f | ||
|
|
a3436aea02 | ||
|
|
f62b2fc114 | ||
|
|
baf612bd59 | ||
|
|
f3490440b4 | ||
|
|
5cc1c30d7f | ||
|
|
0017de46e4 | ||
|
|
1cd1328b0c | ||
|
|
8ff9ad7636 | ||
|
|
1ba1c5b44e | ||
|
|
7db55d8db2 | ||
|
|
e058b19f53 | ||
|
|
bc3605a502 | ||
|
|
ce4daad431 | ||
|
|
0360d42d30 | ||
|
|
1fb37f93cc | ||
|
|
fce33402e7 | ||
|
|
abadd0bd34 | ||
|
|
16f27d0004 | ||
|
|
242eede258 | ||
|
|
8dc349caac | ||
|
|
91819bfc2d | ||
|
|
6ee691e019 | ||
|
|
28f0833237 | ||
|
|
eb415fd869 | ||
|
|
ace0a1cb75 | ||
|
|
6ceaea8478 | ||
|
|
30659aef50 | ||
|
|
46d0e23e7b | ||
|
|
2d3e20d96e | ||
|
|
723b51c931 | ||
|
|
a6160e3026 | ||
|
|
ecca69442d | ||
|
|
b2bae867d0 | ||
|
|
cfe843b870 | ||
|
|
1cef3f15d5 | ||
|
|
1bb56309a6 | ||
|
|
6468b7a914 | ||
|
|
6a083e8452 | ||
|
|
27cac334e1 | ||
|
|
947478ca76 | ||
|
|
c75357faa3 | ||
|
|
e9a27393b8 | ||
|
|
5d12c56510 | ||
|
|
00cfc4be44 | ||
|
|
70edcabca1 | ||
|
|
dce5e8d0cc | ||
|
|
59baf34170 | ||
|
|
5aa1146fe9 | ||
|
|
8059260c18 | ||
|
|
91bc8d473f | ||
|
|
3d4f2d146e | ||
|
|
ce61a64bd2 | ||
|
|
e2a498c8ca | ||
|
|
758e7b92eb | ||
|
|
cf4cdffcc1 | ||
|
|
e70c87ed41 | ||
|
|
d8472cc9f5 | ||
|
|
56a08f0d17 | ||
|
|
eb3685f537 | ||
|
|
201a6b8742 | ||
|
|
e2ee4ae024 | ||
|
|
23e3e2c504 | ||
|
|
ab698305bf | ||
|
|
143ae2b788 | ||
|
|
baf50ea84a | ||
|
|
73845e01ed | ||
|
|
178e3d5bc8 | ||
|
|
a94254a375 | ||
|
|
fc4083346e | ||
|
|
f79a11cbf7 | ||
|
|
edc0a7ef91 | ||
|
|
dce9a70ff5 | ||
|
|
631a8278d3 | ||
|
|
bae37c8e6f | ||
|
|
a5c1685ea0 | ||
|
|
d0a0051d85 | ||
|
|
8e4960f31d | ||
|
|
dd2a5e6b28 | ||
|
|
7e1e9ac94a | ||
|
|
29daf1ecee | ||
|
|
c62df39ce3 | ||
|
|
a91e7cbe8d | ||
|
|
159f02ba07 | ||
|
|
2daafe80a5 | ||
|
|
028b2a655d | ||
|
|
fd52cefe09 | ||
|
|
0e0dee43ab | ||
|
|
e2ee565f05 | ||
|
|
63e06578c4 | ||
|
|
a1f03015ec | ||
|
|
8220a6584c | ||
|
|
d4b51c491f | ||
|
|
3f66d9f8cd | ||
|
|
852073fc68 | ||
|
|
17082cda3c | ||
|
|
7ffa98118f | ||
|
|
c3bada3c32 | ||
|
|
23303391e0 | ||
|
|
8ac8acc49c | ||
|
|
94290102b6 | ||
|
|
f94c244b9f | ||
|
|
ec823196e0 | ||
|
|
957647bf06 | ||
|
|
df5524979d | ||
|
|
0ae3e50bd4 | ||
|
|
db710cd2df | ||
|
|
61407ad6c8 | ||
|
|
2e0180ba55 | ||
|
|
d476b7477b | ||
|
|
ec037abf92 | ||
|
|
1a56045b42 | ||
|
|
bf820beb79 | ||
|
|
cde7a3f66c | ||
|
|
4f99d641c5 | ||
|
|
f4aca35a88 | ||
|
|
732065bf13 | ||
|
|
d3d540a28b | ||
|
|
a6cbebe03c | ||
|
|
e1b54b6843 | ||
|
|
aadbd5a92d | ||
|
|
7b065365e0 | ||
|
|
d0827dbb3c | ||
|
|
fab103524a | ||
|
|
15be542e56 | ||
|
|
eebea70019 | ||
|
|
b58d56826b | ||
|
|
e770768b79 | ||
|
|
17011a7637 | ||
|
|
f891860408 | ||
|
|
3424c7dc87 | ||
|
|
dbf7ff9791 | ||
|
|
9addcfe827 | ||
|
|
d5d67708ac | ||
|
|
5aa0017e91 | ||
|
|
f90d5f3dc8 | ||
|
|
8b5793e629 | ||
|
|
de668f5999 | ||
|
|
606463114b | ||
|
|
40242ff7dc | ||
|
|
e411c7e8b3 | ||
|
|
80e1130bb5 | ||
|
|
d9a5b11c9a | ||
|
|
22839e48d0 | ||
|
|
f7f39cf6c0 | ||
|
|
54826808cf | ||
|
|
b30e799847 | ||
|
|
b4aaa1953c | ||
|
|
d64dcd298e | ||
|
|
02e8058c50 | ||
|
|
93057fae8c | ||
|
|
5bc6941b57 | ||
|
|
45dbd31d28 | ||
|
|
45a9eca792 | ||
|
|
b43064ce6e | ||
|
|
660c658c85 | ||
|
|
7804ae6a78 | ||
|
|
c916906716 | ||
|
|
1db57c498d | ||
|
|
54f7cd5302 | ||
|
|
9e70d54300 | ||
|
|
7465ca269b | ||
|
|
508157f07f | ||
|
|
33aa373b3f | ||
|
|
d96a7bc1fa | ||
|
|
dad8397763 | ||
|
|
e26295b6fa | ||
|
|
c8239c739d | ||
|
|
6c4f7caba1 | ||
|
|
cb47354a4f | ||
|
|
61588a4b77 | ||
|
|
142dd0053a | ||
|
|
0d9e12737a | ||
|
|
dfafa81bfe | ||
|
|
cda284424f | ||
|
|
610699d3c4 | ||
|
|
17f1d39fcf | ||
|
|
85766126e3 | ||
|
|
3bafa05ad1 | ||
|
|
1627297143 | ||
|
|
951376852c | ||
|
|
4d3647066f | ||
|
|
9e509aa25b | ||
|
|
d2226c0e9b | ||
|
|
1d65bc6382 | ||
|
|
b513662b52 | ||
|
|
8794106e85 | ||
|
|
88caa14030 | ||
|
|
bb233a9872 | ||
|
|
9acd3ebd42 | ||
|
|
0368b6730f | ||
|
|
ece1dd1d7e | ||
|
|
f57926d9ec | ||
|
|
96f196febd | ||
|
|
4295b1945e | ||
|
|
5ce96b9b95 | ||
|
|
7dfadfea4a | ||
|
|
d4e58e94f6 | ||
|
|
6e504b7bc9 | ||
|
|
7f944515de | ||
|
|
b6595a44d1 | ||
|
|
335d9af8dc | ||
|
|
8478ecc675 | ||
|
|
e5662dc893 | ||
|
|
0ef2cd8719 | ||
|
|
c32bc9dda4 | ||
|
|
d7080defd3 | ||
|
|
b20cce1408 | ||
|
|
9682c48b5b | ||
|
|
6a242f0d08 | ||
|
|
b1f3ccc6ee | ||
|
|
a36d74dad5 | ||
|
|
128d47430d | ||
|
|
e843d27f8c | ||
|
|
4f1e4ffa70 | ||
|
|
7249eebc75 | ||
|
|
8ea0b2051a | ||
|
|
c8678ba5a9 | ||
|
|
2ace4c57d0 | ||
|
|
8ef84e2aa7 | ||
|
|
bb0f3afb71 | ||
|
|
9fb8661eef | ||
|
|
91b81d6a2c | ||
|
|
88ac326caf | ||
|
|
bdd420a08e | ||
|
|
76daa03df0 | ||
|
|
62ad4dd109 | ||
|
|
41dc40dd84 | ||
|
|
2fc47cc52b | ||
|
|
9ad44a2060 | ||
|
|
5092cb75f4 | ||
|
|
f2f6a05342 | ||
|
|
52441dd754 | ||
|
|
38dd1c342a | ||
|
|
0f89db3b46 | ||
|
|
120afb30c1 | ||
|
|
47bc573de5 | ||
|
|
a6272fcf76 | ||
|
|
b8f0e565f5 | ||
|
|
83c9753507 | ||
|
|
91e0733e8e | ||
|
|
dd97d84c19 | ||
|
|
b2a4075e12 | ||
|
|
96e668a5b9 | ||
|
|
c9f4184126 | ||
|
|
6f1209ea49 | ||
|
|
89f0ecf25d | ||
|
|
eca70d81c8 | ||
|
|
639966622f | ||
|
|
be7e249630 | ||
|
|
d1493e6b70 | ||
|
|
fb059bfdc3 | ||
|
|
a3c45d47e6 | ||
|
|
6db2561eda | ||
|
|
ba2d0fae0e | ||
|
|
7f9e91f1c7 | ||
|
|
2312302dea | ||
|
|
dfede52048 | ||
|
|
a87d12f167 | ||
|
|
ec18c3770b | ||
|
|
3e0ea9727f | ||
|
|
e64378f94f | ||
|
|
888a7aa6b2 | ||
|
|
9cf2f424ad | ||
|
|
a1b35fb7d1 | ||
|
|
982b596fa4 | ||
|
|
1ce8168d72 | ||
|
|
93ce2728b7 | ||
|
|
0364bf4913 | ||
|
|
48813c55d5 | ||
|
|
a9a35ccb6b | ||
|
|
4d5c20ce17 | ||
|
|
ced84a2b01 | ||
|
|
6a0055f69f | ||
|
|
07b299abd2 | ||
|
|
ae563e2b1c | ||
|
|
1ceca4ae19 | ||
|
|
fa611b3616 | ||
|
|
bc0d4f28ef | ||
|
|
369a29ac90 | ||
|
|
b88a735e54 | ||
|
|
dca4de80fd | ||
|
|
bead3e6095 | ||
|
|
1436b075bf | ||
|
|
9831b358f8 | ||
|
|
7b11e634aa | ||
|
|
dce5c7e249 | ||
|
|
7f60ab49b2 | ||
|
|
d1d3dcf1a0 | ||
|
|
ce87a0c1d3 | ||
|
|
75067524ae | ||
|
|
1cd779459b | ||
|
|
e2eb0b2eac | ||
|
|
709bcedbed | ||
|
|
444e938e2d | ||
|
|
9820baf82e | ||
|
|
7f2c7cacd3 | ||
|
|
62a911a57e | ||
|
|
fcd34e41ca | ||
|
|
ba8a5ac82b | ||
|
|
e5a6c0a94d | ||
|
|
a9c8d4a216 | ||
|
|
6c7da13cd3 | ||
|
|
2ba23ebb13 | ||
|
|
74d69bdedc | ||
|
|
8a074dedb6 | ||
|
|
3dd4bf9955 | ||
|
|
2e2254371a | ||
|
|
693f7366a2 | ||
|
|
b673c804be | ||
|
|
03a1086ba3 | ||
|
|
e8a2ad4315 | ||
|
|
c9cbd2f4f6 | ||
|
|
90a99fc221 | ||
|
|
a649c3b72e | ||
|
|
caec5d81eb | ||
|
|
e4ec976a1f | ||
|
|
13768fdb9b | ||
|
|
46e4bd5fe0 | ||
|
|
95683c6239 | ||
|
|
db9ea66069 | ||
|
|
ef02464e3c | ||
|
|
d29bd4b054 | ||
|
|
17b7042a25 | ||
|
|
75afb37987 | ||
|
|
528a66fc97 | ||
|
|
b245311a7e | ||
|
|
4b2ef39648 | ||
|
|
286c3eafe3 | ||
|
|
3bb6c667db | ||
|
|
b530d1d449 | ||
|
|
fdbb5b67a8 | ||
|
|
67f09fb1d6 | ||
|
|
1a5dd5c87c | ||
|
|
c2a189ab18 | ||
|
|
56219f9f61 | ||
|
|
c38e7ee3e3 | ||
|
|
5e490ed8b7 | ||
|
|
9f9b1d3ce6 | ||
|
|
35ed9b9b72 | ||
|
|
c2830c4a98 | ||
|
|
3b9b03cf86 | ||
|
|
ef39c1e94b | ||
|
|
4ab2d46113 | ||
|
|
1e53528363 | ||
|
|
b46a8344d3 | ||
|
|
5d8334d7f4 | ||
|
|
c9a6e0155b | ||
|
|
7a9fab8f57 | ||
|
|
4bcc1f5adb | ||
|
|
3629468d47 | ||
|
|
d7f65ca125 | ||
|
|
c5d6f58988 | ||
|
|
a7a3ce7245 | ||
|
|
6cb527a937 | ||
|
|
007ba2bee4 | ||
|
|
2ce6d47519 | ||
|
|
f2bfdfdedd | ||
|
|
9d49faca61 | ||
|
|
bb85e139f1 | ||
|
|
c87d025902 | ||
|
|
c37b8f7f9d | ||
|
|
1ee5dba9b3 | ||
|
|
e8be8139cb | ||
|
|
fcc92299eb | ||
|
|
0c23cd62db | ||
|
|
04472bd730 | ||
|
|
c1f1d4e2ce | ||
|
|
e91a4bebed | ||
|
|
3d811a17fe | ||
|
|
f3ec818a66 | ||
|
|
a270ca5101 | ||
|
|
493aa9f20d | ||
|
|
6ff462abf3 | ||
|
|
4103344e48 | ||
|
|
193705a08a | ||
|
|
b59230067f | ||
|
|
7b7fa5f77b | ||
|
|
27b4c17f06 | ||
|
|
6491e30a80 | ||
|
|
4d478a3306 | ||
|
|
90dfa2ccaf | ||
|
|
c968d11943 | ||
|
|
e0918816fb | ||
|
|
66e84b68fc | ||
|
|
3d18f1447e | ||
|
|
d4ab74b25e | ||
|
|
d2cc7df7db | ||
|
|
231b70b987 | ||
|
|
1798ebd395 | ||
|
|
b1f4ea6206 | ||
|
|
350627988e | ||
|
|
87248c9f47 | ||
|
|
3fe616b8fe | ||
|
|
a12b2c8d33 | ||
|
|
468fb3c77a | ||
|
|
3a4bd27333 | ||
|
|
ac9974fd64 | ||
|
|
1e1b51bc1c | ||
|
|
127772e1b4 | ||
|
|
3ad3d3037f | ||
|
|
9c5f2de4ec | ||
|
|
59772ef772 | ||
|
|
96260ba26d | ||
|
|
ca2e2f5f45 | ||
|
|
92f3d863fa | ||
|
|
5970df0606 | ||
|
|
e1819a874f | ||
|
|
0c665e11f1 | ||
|
|
827d4c5fb0 | ||
|
|
e46fcc2893 | ||
|
|
ae8129f1fd | ||
|
|
6eed7eb540 | ||
|
|
7c86f11167 | ||
|
|
dc34bf18bb | ||
|
|
6bde70d60a | ||
|
|
b8370cffb9 | ||
|
|
b863447c2b | ||
|
|
7403f9f870 | ||
|
|
5252211c92 | ||
|
|
1d63654662 | ||
|
|
f83fc2e35c | ||
|
|
6cf17c7bcc | ||
|
|
19d7cfd66b | ||
|
|
a89772652d | ||
|
|
95485a0da2 | ||
|
|
f67acc82cf | ||
|
|
e2f1ce7758 | ||
|
|
4744e50244 | ||
|
|
61f105da6f | ||
|
|
273ab304b9 | ||
|
|
1d3af68f29 | ||
|
|
2a2a4b96c7 | ||
|
|
b630a01131 | ||
|
|
e70dbec6b9 | ||
|
|
f25189fc74 | ||
|
|
00fc66d8e8 | ||
|
|
97f17374f2 | ||
|
|
f16b19faf3 | ||
|
|
4ce16e9e91 | ||
|
|
be4bbd7b9b | ||
|
|
bae28965ab | ||
|
|
9daa97f279 | ||
|
|
4094c47e29 | ||
|
|
63efbdffe6 | ||
|
|
a7af6d8a3c | ||
|
|
ee1fe95ee5 | ||
|
|
f79c619065 | ||
|
|
028935a318 | ||
|
|
62353191e8 | ||
|
|
cbffd32d8e | ||
|
|
0394a3e939 | ||
|
|
8e400e9e40 | ||
|
|
66a53c6d3c | ||
|
|
f111c5c325 | ||
|
|
c4a9b61da6 | ||
|
|
a65ebbb319 | ||
|
|
bc78cf8a87 | ||
|
|
23823811e9 | ||
|
|
2bcc41c851 | ||
|
|
dce03a36d0 | ||
|
|
b24b3d21e1 | ||
|
|
556e507f68 | ||
|
|
29bf6e5d32 | ||
|
|
0927af40c3 | ||
|
|
c434d98365 | ||
|
|
0979f11cc3 | ||
|
|
68bdd26426 | ||
|
|
342c4384e4 | ||
|
|
73a41b16be | ||
|
|
e00062ea8f | ||
|
|
ca44ce5f8c | ||
|
|
812ac259e2 | ||
|
|
b8da386e55 | ||
|
|
3c0358c9cf | ||
|
|
c9aca2a13d | ||
|
|
ed9362a98c | ||
|
|
6b86d834a9 | ||
|
|
a2c5e30011 | ||
|
|
e06647b54e | ||
|
|
0f7d36cfa0 | ||
|
|
9e27559bdb | ||
|
|
0a21ffbfec | ||
|
|
542e487b69 | ||
|
|
bfc6d95a7e | ||
|
|
65a26958f7 | ||
|
|
2dfb4d0c17 | ||
|
|
368ad332a0 | ||
|
|
bde32eb7df | ||
|
|
dc64556902 | ||
|
|
0315077daf | ||
|
|
7237bacec0 | ||
|
|
9bf8e89192 | ||
|
|
5fa6b07e1b | ||
|
|
6668fe02aa | ||
|
|
82d09c288d | ||
|
|
8029f56d1c | ||
|
|
0a6916e1b9 | ||
|
|
e419a034b7 | ||
|
|
cafa5217ed | ||
|
|
bc16a1bcc4 | ||
|
|
f5737a6354 | ||
|
|
2865ad5281 | ||
|
|
33d75ad7b0 | ||
|
|
1a3222d357 | ||
|
|
5a17f86f1a | ||
|
|
c989a94916 | ||
|
|
8b57add891 | ||
|
|
5833bb3bd4 | ||
|
|
d17abedc7d | ||
|
|
b407074281 | ||
|
|
c4d6189b55 | ||
|
|
07706b41f4 | ||
|
|
346a48d4c2 | ||
|
|
77db84b4c8 | ||
|
|
b90b9bb472 | ||
|
|
d019187895 | ||
|
|
b3963456c0 | ||
|
|
bccf371aa9 | ||
|
|
641e9ff508 | ||
|
|
abb045e1ff | ||
|
|
ae97afd806 | ||
|
|
c09ee7d714 | ||
|
|
4047e871e5 | ||
|
|
4c1701ca77 | ||
|
|
8ba5cbc2b9 | ||
|
|
fbf13dde21 | ||
|
|
bdd713413a | ||
|
|
e896592261 | ||
|
|
d9312dbefb | ||
|
|
5cb638bd41 | ||
|
|
df23ef36c7 | ||
|
|
e1ffacbe32 | ||
|
|
6d9fe04aa8 | ||
|
|
16e48a859c | ||
|
|
6346c00527 | ||
|
|
3f6fe4f2be | ||
|
|
bb7680dc51 | ||
|
|
161572b292 | ||
|
|
3b3da24823 | ||
|
|
1f91c2fe12 | ||
|
|
b98fbcce0c | ||
|
|
b12d4c03cb | ||
|
|
bd4bdab81c | ||
|
|
792527d456 | ||
|
|
1a103662e9 | ||
|
|
25ea754502 | ||
|
|
3152d039a5 | ||
|
|
a33a5771ed | ||
|
|
5bedf3618d | ||
|
|
153cd0a205 | ||
|
|
f5f1b9602a | ||
|
|
7347b72010 | ||
|
|
5aeb8305fc | ||
|
|
53c950b235 | ||
|
|
8e6ff32c97 | ||
|
|
23acd2738b | ||
|
|
6bcc039e01 | ||
|
|
e776e79c1e | ||
|
|
e2660eaad3 | ||
|
|
5b6e731f37 | ||
|
|
ce0ba8edbd | ||
|
|
ec97464007 | ||
|
|
1ff6e4db9d | ||
|
|
59aae20aea | ||
|
|
97f9dedaa6 | ||
|
|
7f730b81ff | ||
|
|
e793fc9973 | ||
|
|
73876e5774 | ||
|
|
97e70d62bf | ||
|
|
47a356ff52 | ||
|
|
4e6a9fb202 | ||
|
|
141b8495e2 | ||
|
|
de503bf5c4 | ||
|
|
c94fbe5eeb | ||
|
|
523f7652f2 | ||
|
|
6c5795b519 | ||
|
|
6552ccc477 | ||
|
|
adb9f31803 | ||
|
|
d7f655dfc5 | ||
|
|
83a4999dbe | ||
|
|
abf1d7da23 | ||
|
|
dfaf116449 | ||
|
|
41362e2b6e | ||
|
|
731b6ebfa7 | ||
|
|
3f838e1dbc | ||
|
|
c845eed114 | ||
|
|
5b09829959 | ||
|
|
2e5a993f88 | ||
|
|
1fe1194ef4 | ||
|
|
19d0919451 | ||
|
|
afffbb6487 | ||
|
|
4964a1519b | ||
|
|
53e4b55ea3 | ||
|
|
9b3c00e0b6 | ||
|
|
7b31e5918e | ||
|
|
50a8ba8c18 | ||
|
|
b32bce9be2 | ||
|
|
8c87f32b38 | ||
|
|
0cf6c6c132 | ||
|
|
d757dd4e17 | ||
|
|
175ae58563 | ||
|
|
f32f7d2308 | ||
|
|
36acd34874 | ||
|
|
3ed6d82367 | ||
|
|
793047919d | ||
|
|
6a786342f9 | ||
|
|
d184b3fa86 | ||
|
|
91e97468a0 | ||
|
|
4a9317db6e | ||
|
|
b66aeea384 | ||
|
|
90f3cc7328 | ||
|
|
f9b732482d | ||
|
|
924f796eb8 | ||
|
|
2528f35f00 | ||
|
|
683a09781a | ||
|
|
d8774cda8e | ||
|
|
1ce4eda0b9 | ||
|
|
5bcb373c00 | ||
|
|
678239424a | ||
|
|
369dc295b1 | ||
|
|
7470b1180e | ||
|
|
99a4bb63c7 | ||
|
|
713a34c68e | ||
|
|
165a6d34b2 | ||
|
|
d96ab7c867 | ||
|
|
7de4f1d96f | ||
|
|
68fd1c28b0 | ||
|
|
e7a4aeca6f | ||
|
|
86002a2f7b | ||
|
|
08a4bbb7d0 | ||
|
|
ae4cf98458 | ||
|
|
1596391a2e | ||
|
|
0ff24b6f21 | ||
|
|
6cf8ca5e43 | ||
|
|
4bb6b5a547 | ||
|
|
f14ef10e48 | ||
|
|
f76046b612 | ||
|
|
69e1f6e4ba | ||
|
|
2fce010f30 | ||
|
|
5ba8749a38 | ||
|
|
5c5d45f52b | ||
|
|
a6d47ca772 | ||
|
|
11b4d98f37 | ||
|
|
6e016c439c | ||
|
|
d1af4a5582 | ||
|
|
9b5e6a1f7a | ||
|
|
060982adb4 | ||
|
|
bc95b2fc77 | ||
|
|
f926915e56 | ||
|
|
69123590fb | ||
|
|
66a35e973c | ||
|
|
9d1443bbfe | ||
|
|
f4f3769225 | ||
|
|
38543ad202 | ||
|
|
dda2ea8fed | ||
|
|
2293f9dad6 | ||
|
|
abb3341030 | ||
|
|
81e51dcafd | ||
|
|
e84e2c7258 | ||
|
|
d9b46587fe | ||
|
|
36322acb6d | ||
|
|
12b39feba3 | ||
|
|
16281c626b | ||
|
|
d24e867603 | ||
|
|
d5c14a513d | ||
|
|
f9b836027d | ||
|
|
3147b7c62b | ||
|
|
0ceb75ea09 | ||
|
|
3cb138ea41 | ||
|
|
9016ba0d61 | ||
|
|
3b6248cb64 | ||
|
|
5a71984b38 | ||
|
|
1274889931 | ||
|
|
ed7e715812 | ||
|
|
4d12af8396 | ||
|
|
6f45fb6e14 | ||
|
|
9de15616a5 | ||
|
|
5c0ef950cc | ||
|
|
0b0e0f8f43 | ||
|
|
a444c860a4 | ||
|
|
20db8bbe2e | ||
|
|
154923ca7d | ||
|
|
a34d636366 | ||
|
|
89ad259050 | ||
|
|
db1ae80877 | ||
|
|
7aa712a481 | ||
|
|
748c894450 | ||
|
|
0e8b13841a | ||
|
|
d284b8e0a0 | ||
|
|
4ce1cccc4a | ||
|
|
91c803ff65 | ||
|
|
7a17ee9f8c | ||
|
|
1ad8f20bce | ||
|
|
de421d02ef | ||
|
|
da390ff573 | ||
|
|
14cfa90d63 | ||
|
|
90a97ae35f | ||
|
|
501bd814c3 | ||
|
|
0f5a166cce | ||
|
|
d59f450819 | ||
|
|
e0a702df76 | ||
|
|
e990a35a91 | ||
|
|
3e286f4f19 | ||
|
|
e0a83b4b94 | ||
|
|
b9728a96ce | ||
|
|
537510f081 | ||
|
|
dc411ca889 | ||
|
|
9055d0910c | ||
|
|
1985502894 | ||
|
|
c3e3c32bdc | ||
|
|
f5ad1dfbc5 | ||
|
|
9eab9512a3 | ||
|
|
078db2dd80 | ||
|
|
a3796f9baa | ||
|
|
bdc279a49b | ||
|
|
544330345b | ||
|
|
4bf1d0373d | ||
|
|
192df273cc | ||
|
|
cf5c803fe0 | ||
|
|
3c302bae5a | ||
|
|
103cd2b7a1 | ||
|
|
7763643f2e | ||
|
|
61d2bed019 | ||
|
|
52cbbed6a0 | ||
|
|
4b624ab5da | ||
|
|
640d15dba5 | ||
|
|
69509f2a2a | ||
|
|
da5b9d5c74 | ||
|
|
af11525338 | ||
|
|
98b71bc63a | ||
|
|
271244bb36 | ||
|
|
ebef7d6348 | ||
|
|
20497f1be3 | ||
|
|
74947d1c1c | ||
|
|
db3d230ad9 | ||
|
|
fabf278633 | ||
|
|
743cfa13fd | ||
|
|
127ed1b188 | ||
|
|
b80674bf2e | ||
|
|
f74f7d543e | ||
|
|
72caf2ef9d | ||
|
|
5ef05d3a63 | ||
|
|
26aaa62048 | ||
|
|
c427fe94da | ||
|
|
7cd7837e08 | ||
|
|
66f7437965 | ||
|
|
cb32640f70 | ||
|
|
66c07a8b33 | ||
|
|
b326ab78b3 | ||
|
|
8811ca9e9e | ||
|
|
471d88c1d2 | ||
|
|
4a738024cc | ||
|
|
8ac32cf4d6 | ||
|
|
b541351a0a | ||
|
|
92f5a2b6a6 | ||
|
|
ae092efb49 | ||
|
|
94065fde33 | ||
|
|
aab7bc5282 | ||
|
|
9e2ccbd2a7 | ||
|
|
6a6494d947 | ||
|
|
cb9e944de1 | ||
|
|
1f92c594c8 | ||
|
|
f91031bd65 | ||
|
|
5d3e04216d | ||
|
|
055d55b71b | ||
|
|
ca948d66a9 | ||
|
|
fa8cb40af0 | ||
|
|
b13fb1cca9 | ||
|
|
01b8ed97ed | ||
|
|
863ee9e6dd | ||
|
|
4ebd604ca9 | ||
|
|
581ef6e18d | ||
|
|
321a32050b | ||
|
|
db6a207afe | ||
|
|
9b42bc860d | ||
|
|
6174aa01e0 | ||
|
|
fb7692cf9c | ||
|
|
9279840860 | ||
|
|
9c05f37c0f | ||
|
|
b5c72611b4 | ||
|
|
2b3b5d2c8a | ||
|
|
d46fe20986 | ||
|
|
9e46409517 | ||
|
|
d42da529ec | ||
|
|
c1c96e01fa | ||
|
|
aee2742951 | ||
|
|
c3af36d33b | ||
|
|
3885aa1e8c | ||
|
|
33b1c57092 | ||
|
|
441d3bf1ed | ||
|
|
bd83936e5d | ||
|
|
88a68b941f | ||
|
|
084b41fc2c | ||
|
|
7b713e2576 | ||
|
|
25982f0475 | ||
|
|
2d2ac98b3d | ||
|
|
f1fc201862 | ||
|
|
681f45943b | ||
|
|
5dc8c54b8d | ||
|
|
20194bed42 | ||
|
|
06bbf494bb | ||
|
|
21b919a76b | ||
|
|
39f0707201 | ||
|
|
8d52a278a9 | ||
|
|
5fbba27d17 | ||
|
|
5c891bcfb5 | ||
|
|
547c700764 | ||
|
|
d30892ea60 | ||
|
|
0b5d550780 | ||
|
|
ac5ad0b9ce | ||
|
|
823c0ebb47 | ||
|
|
8fc87411fa | ||
|
|
5fb173149a | ||
|
|
02cf7274d2 | ||
|
|
43de0e7f16 | ||
|
|
f84530c285 | ||
|
|
6bf92979a2 | ||
|
|
c5dcac4dbb | ||
|
|
bb1b6c906d | ||
|
|
aa9fef7778 | ||
|
|
351f21c89f | ||
|
|
ca97130a29 | ||
|
|
9fea44cbc3 | ||
|
|
5e761a1068 | ||
|
|
afff751ff1 | ||
|
|
222186d5f7 | ||
|
|
632ad7d7f9 | ||
|
|
331412ddff | ||
|
|
04ac04e0ad | ||
|
|
29340152b6 | ||
|
|
8a34f7ca7b | ||
|
|
e2e3b81f32 | ||
|
|
b4b5eb5bab | ||
|
|
edde785219 | ||
|
|
b71e148cea | ||
|
|
0d939b5ab0 | ||
|
|
b983cf8b5f | ||
|
|
81f3a5cf94 | ||
|
|
3726b546d5 | ||
|
|
2702b82bc3 | ||
|
|
aee4f8d2fe | ||
|
|
c79c1b3913 | ||
|
|
3da72c1ff1 | ||
|
|
b926a4c67e | ||
|
|
29320f8aad | ||
|
|
2a59f45a3b | ||
|
|
2db7b2d948 | ||
|
|
d55fcd055d | ||
|
|
9e3032e919 | ||
|
|
c2e2994403 | ||
|
|
b92e3ca3ee | ||
|
|
2abea94f8e | ||
|
|
fcc648ecfd | ||
|
|
2e1046220a | ||
|
|
a532bd9cf1 | ||
|
|
f2d1f1efd6 | ||
|
|
745515b11f | ||
|
|
a7fd4e96f1 | ||
|
|
17091bd38c | ||
|
|
437aa168d1 | ||
|
|
f31ef3420d | ||
|
|
b37184ae57 | ||
|
|
716a83d1f7 | ||
|
|
6c7972470b | ||
|
|
40ce6724a9 | ||
|
|
e46e3027fa | ||
|
|
5ea46444e9 | ||
|
|
47bfb681c0 | ||
|
|
3f7f5f9429 | ||
|
|
a9cae7c9bd | ||
|
|
ebd92d736a | ||
|
|
96b7bfb32c | ||
|
|
5d9cf6a6d7 | ||
|
|
47db3452f4 | ||
|
|
40e03a05be | ||
|
|
bd70e6ae6d | ||
|
|
b9eb74a705 | ||
|
|
daaefed61b | ||
|
|
7b41839ea8 | ||
|
|
6532972e61 | ||
|
|
48026efddf | ||
|
|
e314510005 | ||
|
|
facc6ee6b3 | ||
|
|
9a2f86e9ad | ||
|
|
513b8959f5 | ||
|
|
8a00b62cba | ||
|
|
9bb847bb07 | ||
|
|
40bfce463d | ||
|
|
fc7d29edd3 | ||
|
|
4f7d14dfb2 | ||
|
|
cfc61a69ef | ||
|
|
ce0f98989c | ||
|
|
cf547be1d6 | ||
|
|
745ac240d7 | ||
|
|
16da1a4e81 | ||
|
|
73d67bb16a | ||
|
|
050c0752f9 | ||
|
|
ae62d30811 | ||
|
|
f59a0192b4 | ||
|
|
f56b4773cb | ||
|
|
1a4a8f1ef7 | ||
|
|
03aacc35b3 | ||
|
|
d5cd6f9a51 | ||
|
|
205bc96827 | ||
|
|
2bd61aed7a | ||
|
|
6706bed676 | ||
|
|
81e704648f | ||
|
|
c4debca11d | ||
|
|
e75b0cb743 | ||
|
|
29617737ca | ||
|
|
26cc73118a | ||
|
|
47e91e0660 | ||
|
|
c6eecb06d5 | ||
|
|
896b46d18e | ||
|
|
18ef8ea271 | ||
|
|
ad26eec9f2 | ||
|
|
3b422406a9 | ||
|
|
78c63c480a | ||
|
|
79a068e92b | ||
|
|
717a532c09 | ||
|
|
b2cc2e6765 | ||
|
|
af13e5fa4a | ||
|
|
8eac8132e3 | ||
|
|
50f579d301 | ||
|
|
02c72e59fa | ||
|
|
e7233c0c94 | ||
|
|
2bdf135cbc | ||
|
|
134b9fc466 | ||
|
|
10863a5949 | ||
|
|
bf02e04283 | ||
|
|
49fd53ee67 | ||
|
|
48f70e55aa | ||
|
|
3250d75320 | ||
|
|
1ad4d26f31 | ||
|
|
87ee48bd84 | ||
|
|
24f9bb41df | ||
|
|
2a02b6de44 | ||
|
|
21a0498a30 | ||
|
|
411d7aa6c4 | ||
|
|
d0e9b4ce9f | ||
|
|
50c5f8c389 | ||
|
|
619c79df27 | ||
|
|
246b2c0d1b | ||
|
|
55c67c7870 | ||
|
|
26ea11c44f | ||
|
|
de1825e54b | ||
|
|
e259503933 | ||
|
|
c089d30915 | ||
|
|
4e07b4c0e8 | ||
|
|
ce6e81c682 | ||
|
|
c44acb3575 | ||
|
|
bad5057a74 | ||
|
|
4b91d4b5c3 | ||
|
|
fe7b7773ba | ||
|
|
cef1aa6d1b | ||
|
|
2aa8979522 | ||
|
|
e93fdefd72 | ||
|
|
5b10db6f91 | ||
|
|
5dc9de41eb | ||
|
|
76c2de38ff | ||
|
|
5cd4e340eb | ||
|
|
816802774d | ||
|
|
541e40f29c | ||
|
|
b7559c1df6 | ||
|
|
1af56b1025 | ||
|
|
27d5b9cfd0 | ||
|
|
bbf3d960b2 | ||
|
|
e1df151d9b | ||
|
|
a2ccfc1e50 | ||
|
|
771d87781e | ||
|
|
883828c6cc | ||
|
|
fa94644bcf | ||
|
|
ad309f1036 | ||
|
|
56b12f6555 | ||
|
|
e48323775d | ||
|
|
0f10fc8458 | ||
|
|
2c1cd99738 | ||
|
|
07df5833be | ||
|
|
2728cdaf23 | ||
|
|
4117ada2fd | ||
|
|
3727bea29a | ||
|
|
c410ffd10b | ||
|
|
bb5b19c2fb | ||
|
|
b0dbb6708e | ||
|
|
5d2cf3d23c | ||
|
|
3c9809bfe6 | ||
|
|
c530b4fb97 | ||
|
|
fcd7dc8744 | ||
|
|
f9f1b16e76 | ||
|
|
fe3e4bd0ec | ||
|
|
5b6c2c32bf | ||
|
|
245c2d4eed | ||
|
|
095e2bf0b3 | ||
|
|
ffee413d2d | ||
|
|
80b655fa7e | ||
|
|
db95e6eba5 | ||
|
|
dab3495751 | ||
|
|
f0e8c9ead9 | ||
|
|
d92e9f38f8 | ||
|
|
71632ac2d2 | ||
|
|
68d9d1cec2 | ||
|
|
eff2e6c795 | ||
|
|
3bf0a27e45 | ||
|
|
68cb4baeb3 | ||
|
|
c28ba24525 | ||
|
|
fc18bea4bd | ||
|
|
01842a563d | ||
|
|
44a2cc872c | ||
|
|
ac6c43b5fb | ||
|
|
4663278f52 | ||
|
|
cacdac16aa | ||
|
|
5716556766 | ||
|
|
cccffc77cd | ||
|
|
eae9774cb6 | ||
|
|
8bf03d21cd | ||
|
|
62229d0a49 | ||
|
|
6586b97a54 | ||
|
|
7e59c70a9f | ||
|
|
bba7fe24e9 | ||
|
|
fb9544badd | ||
|
|
bfc2552841 | ||
|
|
39dc4fc992 | ||
|
|
14e2a5cc39 | ||
|
|
c04c57ea0f | ||
|
|
8333d41dbd | ||
|
|
dca4db9d4d | ||
|
|
4650458157 | ||
|
|
ce41710a7c | ||
|
|
97d472380f | ||
|
|
9ab6029280 | ||
|
|
7b90b0dfd9 | ||
|
|
24ddc8e026 | ||
|
|
9981cbb72c | ||
|
|
be6c4019f6 | ||
|
|
8b8712c15b | ||
|
|
4ffa408420 | ||
|
|
c7ffe606a6 | ||
|
|
26138ac46d | ||
|
|
ed457ac694 | ||
|
|
56aa568124 | ||
|
|
73c781a0cf | ||
|
|
3edbb564fc | ||
|
|
4e85bc66b8 | ||
|
|
d1c9701ccf | ||
|
|
ec5cc08fab | ||
|
|
b5d093e5ca | ||
|
|
10a52977f8 | ||
|
|
c43eccf591 | ||
|
|
d9dc7f0f38 | ||
|
|
4511f8855b | ||
|
|
007836f514 | ||
|
|
83dd1c7be2 | ||
|
|
8ed9d915ad | ||
|
|
f812866665 | ||
|
|
fde46ca78c | ||
|
|
ab59479a0c | ||
|
|
299c46f118 | ||
|
|
73bd65ead5 | ||
|
|
4b691703fe | ||
|
|
85bf025adc | ||
|
|
8fd8ddcbc1 | ||
|
|
0011b8fd48 | ||
|
|
0cba82ce9c | ||
|
|
eb7db62a64 | ||
|
|
8d0a0674c6 | ||
|
|
6c4f9f324b | ||
|
|
2863c35ab5 | ||
|
|
44d945cd08 | ||
|
|
0754da58da | ||
|
|
31df7af61f | ||
|
|
f55636bcb5 | ||
|
|
1fd8c7ac42 | ||
|
|
b0f9cd3022 | ||
|
|
0add06380f | ||
|
|
81624a601a | ||
|
|
045cd48687 | ||
|
|
56f66ce001 | ||
|
|
7121d8e427 | ||
|
|
57dc362d5d | ||
|
|
661558dafc | ||
|
|
6467ce1a97 | ||
|
|
5680a88c59 | ||
|
|
04a76371fc | ||
|
|
a90a0874b8 | ||
|
|
2d83ea86dc | ||
|
|
956dab69b4 | ||
|
|
abaf752a9b | ||
|
|
d63cfb41f1 | ||
|
|
9cfd0dd9d8 | ||
|
|
1e8fec9385 | ||
|
|
35f1055739 | ||
|
|
98d5ae91f5 | ||
|
|
83b89b9576 | ||
|
|
4c89f5d397 | ||
|
|
c2f83639d4 | ||
|
|
77e865fc8e | ||
|
|
1fa7e2994a | ||
|
|
0d7eb186d1 | ||
|
|
b6115d3c56 | ||
|
|
628187db16 | ||
|
|
de03f7f9ce | ||
|
|
6e149a2dd3 | ||
|
|
884bb60c7d | ||
|
|
57033bb599 | ||
|
|
005186bf4a | ||
|
|
95d24f1d30 | ||
|
|
dbb4ccbcc0 | ||
|
|
ed213c4d6d | ||
|
|
abb7695624 | ||
|
|
2cadda657c | ||
|
|
bedc7b7b69 | ||
|
|
d7d46def9d | ||
|
|
1639f5b83d | ||
|
|
2226d708ba | ||
|
|
ba33c51b8c | ||
|
|
aaa7d6a0ec | ||
|
|
483952eb78 | ||
|
|
5fa43c41eb | ||
|
|
00fb997995 | ||
|
|
c10acb1fb0 | ||
|
|
ecb44ad572 | ||
|
|
a3171cd429 | ||
|
|
2b2f1f2746 | ||
|
|
5a1887ed17 | ||
|
|
0998a108ea | ||
|
|
8b17a6ddd1 | ||
|
|
a7eae1031c | ||
|
|
df91b489c4 | ||
|
|
f17eb946f6 | ||
|
|
904881e207 | ||
|
|
b00d084243 | ||
|
|
75c1e7a193 | ||
|
|
5897ed896a | ||
|
|
47dd1da6fb | ||
|
|
b3efdf2109 | ||
|
|
e5c077243c | ||
|
|
af87038150 | ||
|
|
720f1d7123 | ||
|
|
678148b9aa | ||
|
|
549943fb10 | ||
|
|
2940f9591b | ||
|
|
2ebb8851f6 | ||
|
|
24192ff1ef | ||
|
|
6adb180911 | ||
|
|
fa8febbb31 | ||
|
|
8ba1bf02a4 | ||
|
|
4bdb028499 | ||
|
|
d18a8e849e | ||
|
|
c3fdd00aa4 | ||
|
|
13c7fe46cd | ||
|
|
ea0be8ea1a | ||
|
|
38ea71c6c9 | ||
|
|
37ad734cea | ||
|
|
e9462ba145 | ||
|
|
2b9322fc7d | ||
|
|
b775a1aa0e | ||
|
|
bf3a409569 | ||
|
|
68f6baf938 | ||
|
|
5994fadebb | ||
|
|
c6c4c53c8b | ||
|
|
16b7df3717 | ||
|
|
9bd8384a57 | ||
|
|
85d8300421 | ||
|
|
d03c66c924 | ||
|
|
b485d09847 | ||
|
|
ad5c93d673 | ||
|
|
202b757bc4 | ||
|
|
603e64154d | ||
|
|
52b80711f6 | ||
|
|
aec67e6be2 | ||
|
|
8b6230726f | ||
|
|
4050ff7c1f | ||
|
|
6b4cfe4f18 | ||
|
|
810d9fefd9 | ||
|
|
fb13e69b8e | ||
|
|
689f232243 | ||
|
|
f16b6406eb | ||
|
|
760427fc43 | ||
|
|
8931adc048 | ||
|
|
b752acdeef | ||
|
|
ba224f382d | ||
|
|
f2ff6f394b | ||
|
|
391d7fed52 | ||
|
|
07e28a9757 | ||
|
|
b05474fc30 | ||
|
|
47bd97b55d | ||
|
|
381b1a066d | ||
|
|
69ca279101 | ||
|
|
de1e39add9 | ||
|
|
ebd3b965fc | ||
|
|
40b2e24c6d | ||
|
|
ddfc5209d3 | ||
|
|
aea2fa1629 | ||
|
|
8e243edd20 | ||
|
|
a8b42fc21e | ||
|
|
86dd67f57d | ||
|
|
97d7a523a0 | ||
|
|
1d455c21d4 | ||
|
|
8f631d0693 | ||
|
|
002c203913 | ||
|
|
8a2b96c2f9 | ||
|
|
c6b9eb7855 | ||
|
|
38ea8bee93 | ||
|
|
a969f18137 | ||
|
|
0381102c27 | ||
|
|
e81dccb14e | ||
|
|
5c3e6307b4 | ||
|
|
8d94402d25 | ||
|
|
35d12b9e59 | ||
|
|
a81da0ec34 | ||
|
|
f1fbcd7c02 | ||
|
|
5c32f42fe9 | ||
|
|
a780252552 | ||
|
|
798b80e486 | ||
|
|
aa0384bcec | ||
|
|
dd654b9766 | ||
|
|
4c840d70a4 | ||
|
|
238621ee92 | ||
|
|
206054678b | ||
|
|
a5035dee74 | ||
|
|
d3369384d1 | ||
|
|
420aa4bc44 | ||
|
|
305e0538d2 | ||
|
|
8aee932525 | ||
|
|
8b737e9610 | ||
|
|
0b16a5531a | ||
|
|
b7fbd0ee50 | ||
|
|
2afdb7854b | ||
|
|
1bd784cf12 | ||
|
|
17fa2d8801 | ||
|
|
b89c869e7c | ||
|
|
1e6a491400 | ||
|
|
38ca3bac40 | ||
|
|
1a506ad49c | ||
|
|
b0d3c17f19 | ||
|
|
e967bc9c45 | ||
|
|
d6d21cb5f6 | ||
|
|
d177cf94da | ||
|
|
f4507d878d | ||
|
|
a3e0e67953 | ||
|
|
7045b920ef | ||
|
|
9a057623d6 | ||
|
|
b6a545b4a2 | ||
|
|
d4ef3c183c | ||
|
|
5c3b06b8a9 | ||
|
|
4d34d9c032 | ||
|
|
4f62d7a78f | ||
|
|
d39cf23b2f | ||
|
|
1c61e316b4 | ||
|
|
2d42d58738 | ||
|
|
80ed05d45d | ||
|
|
4cf172d46f | ||
|
|
d1fa63b389 | ||
|
|
9fe4bae52f | ||
|
|
cf93d9c3b4 | ||
|
|
40a7d38210 | ||
|
|
e4244c0cac | ||
|
|
3c88f3e6ee | ||
|
|
fa98f4c55d | ||
|
|
f2e87a204d | ||
|
|
c98b91f514 | ||
|
|
15d9bf4ebe | ||
|
|
6dd4e9ac60 | ||
|
|
2b0c2891e3 | ||
|
|
7939588702 | ||
|
|
99c4d932bf | ||
|
|
537401cf27 | ||
|
|
b2eede891a | ||
|
|
a2873c18ca | ||
|
|
70d38fe5b3 | ||
|
|
f15456f4ab | ||
|
|
9fc4246e1d | ||
|
|
291b0edbe0 | ||
|
|
452c4d13b0 | ||
|
|
70150718c5 | ||
|
|
8a89cfb158 | ||
|
|
86d2daf473 | ||
|
|
fbb357ac47 | ||
|
|
38de583db0 | ||
|
|
7fc254a81c | ||
|
|
752bb169ed | ||
|
|
4b101c2240 | ||
|
|
055ee75302 | ||
|
|
f95011a565 | ||
|
|
4facae674d | ||
|
|
39005634c6 | ||
|
|
2e452f5b27 | ||
|
|
9e9e0ebb1c | ||
|
|
dd8d20f089 | ||
|
|
6bb5ea7a81 | ||
|
|
efcde8f3dd | ||
|
|
057266653b | ||
|
|
8cbffdf0b4 | ||
|
|
000861da0d | ||
|
|
7ae097ef6b | ||
|
|
6868403383 | ||
|
|
d1cb925b59 | ||
|
|
229cc2ac43 | ||
|
|
cb9ac6dd34 | ||
|
|
2e7f4c1870 | ||
|
|
993b182f81 | ||
|
|
82d61f194a | ||
|
|
2755c74c29 | ||
|
|
541d0dce90 | ||
|
|
5ad5afe63b | ||
|
|
2c3843ee4c | ||
|
|
6f0ac133cc | ||
|
|
e5d1dd111e | ||
|
|
1b90e851f9 | ||
|
|
125713e938 | ||
|
|
e128ff4e8f | ||
|
|
2c8a82713e | ||
|
|
165f442d70 | ||
|
|
5365e9b3a5 | ||
|
|
7d2a17ea6e | ||
|
|
91b8c769bd | ||
|
|
2aa1450ab2 | ||
|
|
dc3cc655db | ||
|
|
e4bdc92834 | ||
|
|
87a74a44d3 | ||
|
|
ebbe18a426 | ||
|
|
6bc5dd75bd | ||
|
|
0257d660ad | ||
|
|
0637a71669 | ||
|
|
c63deda71a | ||
|
|
3ba4b2c1c1 | ||
|
|
45dc995967 | ||
|
|
e985436b3b | ||
|
|
a8a3812890 | ||
|
|
6c672d2575 | ||
|
|
99c5aca78b | ||
|
|
d8240a40b7 | ||
|
|
b587bdf863 | ||
|
|
adf34fb201 | ||
|
|
82e867a9a8 | ||
|
|
8706cbe1c4 | ||
|
|
7a557d31e0 | ||
|
|
de12503fad | ||
|
|
5238a27ab3 | ||
|
|
12162f53b4 | ||
|
|
ec3ca11d0d | ||
|
|
07dca90352 | ||
|
|
3338f3c5b2 | ||
|
|
de455e4cd0 | ||
|
|
5243dd153b | ||
|
|
e1659b0725 | ||
|
|
89c026924b | ||
|
|
4f0b138692 | ||
|
|
b6db0f72f5 | ||
|
|
9068ae68ad | ||
|
|
1bff63bd2e | ||
|
|
531baa8fc4 | ||
|
|
e67f5bc6bb | ||
|
|
316b090433 | ||
|
|
b2298d44a4 | ||
|
|
c029839971 | ||
|
|
e7e73e6fd1 | ||
|
|
bed0a5773f | ||
|
|
53a2262fef | ||
|
|
709206accd | ||
|
|
6ba142fd33 | ||
|
|
eeabf514ea | ||
|
|
faf1045ef5 | ||
|
|
6a82ff871f | ||
|
|
7e5428c697 | ||
|
|
036b72757c | ||
|
|
120e9a1e4c | ||
|
|
b49cfb2efd | ||
|
|
0340160ba7 | ||
|
|
4ede3fd771 | ||
|
|
3783c1af3e | ||
|
|
51c610de73 | ||
|
|
92862f93f3 | ||
|
|
05a9f0aa14 | ||
|
|
64810405ef | ||
|
|
382ce4cc61 | ||
|
|
178c983871 | ||
|
|
908e15bc90 | ||
|
|
df27a48e72 | ||
|
|
b959641ca8 | ||
|
|
c50bfa07ca | ||
|
|
416adeb169 | ||
|
|
61f591cc88 | ||
|
|
8f2106da2b | ||
|
|
1e988b1fea | ||
|
|
0febfce268 | ||
|
|
c4fd0af16d | ||
|
|
dd83f6f356 | ||
|
|
69354e808f | ||
|
|
3a7d3e3a54 | ||
|
|
4c76b31684 | ||
|
|
55eda16b61 | ||
|
|
3d0c90cbc5 | ||
|
|
9b9621e10d | ||
|
|
86eb923f29 | ||
|
|
14ca376902 | ||
|
|
77a9be845d | ||
|
|
9dd9e27fa8 | ||
|
|
74c68f09e5 | ||
|
|
b60e36ea7a | ||
|
|
fae7993f93 | ||
|
|
215659a234 | ||
|
|
99afd0a449 | ||
|
|
c0e0379bab | ||
|
|
c761531947 | ||
|
|
42b718b3e0 | ||
|
|
1e3a645abe | ||
|
|
7c47557554 | ||
|
|
b5ea20ac86 | ||
|
|
da9b6690e5 | ||
|
|
32366284a8 | ||
|
|
f17f51a9c1 | ||
|
|
d858bd9265 | ||
|
|
287e9c8d68 | ||
|
|
5e5ec5a66a | ||
|
|
ae5c10a71c | ||
|
|
4d5202353f | ||
|
|
142bcd6806 | ||
|
|
7075697f60 | ||
|
|
3a320462fa | ||
|
|
856133b07d | ||
|
|
0b2d809309 | ||
|
|
ec383aca03 | ||
|
|
8925e0c6c9 | ||
|
|
bc74425872 | ||
|
|
b15a53b672 | ||
|
|
ed7e4df014 | ||
|
|
3bf2935ee3 | ||
|
|
35f17acb38 | ||
|
|
58cf5f310d | ||
|
|
2d916b531b | ||
|
|
12952c9821 | ||
|
|
4ce8f965aa | ||
|
|
47e1c4e059 | ||
|
|
47119ddc7d | ||
|
|
500ee4c1bf | ||
|
|
fc105cf141 | ||
|
|
18f0961caa | ||
|
|
e65949f594 | ||
|
|
b05c1a9829 | ||
|
|
9fa3956aa8 | ||
|
|
02fc082e45 | ||
|
|
be1ffca6f4 | ||
|
|
987eb90e18 | ||
|
|
932aeebcf1 | ||
|
|
5f3a8cbe93 | ||
|
|
f808f1601b | ||
|
|
e5a3179468 | ||
|
|
672c3d7c6d | ||
|
|
72479041ae | ||
|
|
e286b510f1 | ||
|
|
315dafbe12 | ||
|
|
80e4338314 | ||
|
|
db176eec40 | ||
|
|
02f88fc7b2 | ||
|
|
56957b60e2 | ||
|
|
7126fd4b31 | ||
|
|
560af7a5b8 | ||
|
|
e38880a686 | ||
|
|
29dd4ad39b | ||
|
|
54ecf0f45f | ||
|
|
6900dd34a4 | ||
|
|
11b97af250 | ||
|
|
70dae328b5 | ||
|
|
5d4245ff01 | ||
|
|
24d28bc23a | ||
|
|
d786eca126 | ||
|
|
2fb4952137 | ||
|
|
dcf2c07f29 | ||
|
|
da512cef64 | ||
|
|
f41b037fbe | ||
|
|
0f5f1c98ca | ||
|
|
01338a7610 | ||
|
|
063b4286e7 | ||
|
|
271f85be3b | ||
|
|
b5f2b4af35 | ||
|
|
9967f2ab28 | ||
|
|
52c3960946 | ||
|
|
1b1170bcf6 | ||
|
|
eb03877aa4 | ||
|
|
4c4d185937 | ||
|
|
6998bb1f23 | ||
|
|
3a60bef2b6 | ||
|
|
eb1eb38c01 | ||
|
|
3c5598407e | ||
|
|
ed82cb108f | ||
|
|
4ffc4ee70a | ||
|
|
52db649022 | ||
|
|
38e46fff54 | ||
|
|
744ad84714 | ||
|
|
7d897a3f03 | ||
|
|
79eeeaee95 | ||
|
|
2c1b432613 | ||
|
|
0c3d5e99a2 | ||
|
|
dca8a05026 | ||
|
|
5cfc286972 | ||
|
|
bc46f70a90 | ||
|
|
5a63ddd645 | ||
|
|
8566f91303 | ||
|
|
b63aff77df | ||
|
|
5a1eb65ed6 | ||
|
|
98a2dcad90 | ||
|
|
77a021025f | ||
|
|
63123759ed | ||
|
|
6167291488 | ||
|
|
cf0b1f1f15 | ||
|
|
824dedbe9d | ||
|
|
da707736a0 | ||
|
|
8f57bb95fe | ||
|
|
0c3543ac43 | ||
|
|
d2e5b7cb76 | ||
|
|
6a56a509d3 | ||
|
|
dffa08adcc | ||
|
|
779885f9af | ||
|
|
d504197a78 | ||
|
|
542fcaff9a | ||
|
|
bc8c74eb42 | ||
|
|
3affb2e817 | ||
|
|
df0cd4dbc7 | ||
|
|
d54ad98802 | ||
|
|
1d90c4ea99 | ||
|
|
4da005e209 | ||
|
|
c3149a8d59 | ||
|
|
fd507ff597 | ||
|
|
8c2b93da72 | ||
|
|
ca82b45fe4 | ||
|
|
43def0873e | ||
|
|
da49befb18 | ||
|
|
e0d9d30bcf | ||
|
|
99f7dd0fd4 | ||
|
|
0c7ad924a8 | ||
|
|
a3e5307b93 | ||
|
|
ae6256f95a | ||
|
|
a961d5e6c8 | ||
|
|
237cca7a0d | ||
|
|
2c9938f0d5 | ||
|
|
ea83032863 | ||
|
|
6a4573b935 | ||
|
|
9421e42dad | ||
|
|
f4b658d7b1 | ||
|
|
7272b97e9a | ||
|
|
8a9f062b95 | ||
|
|
eac35c05e9 | ||
|
|
07b96f2430 | ||
|
|
269b3cef72 | ||
|
|
99cf2cbaa9 | ||
|
|
b63165b6e0 | ||
|
|
32ce790717 | ||
|
|
b5b57523f1 | ||
|
|
7524948a97 | ||
|
|
f9b67d3630 | ||
|
|
3beb94dc52 | ||
|
|
4371e6ad97 | ||
|
|
f44ca74e99 | ||
|
|
8bbe2569dc | ||
|
|
10fa5c20e7 | ||
|
|
f7833411a1 | ||
|
|
de4f9d68bd | ||
|
|
571b8cc85b | ||
|
|
883fc5d753 | ||
|
|
25b2a6e0cf | ||
|
|
4febdc3bcd | ||
|
|
c3617f5d1e | ||
|
|
fc78ba7c2f | ||
|
|
279359c1bd | ||
|
|
dc9fa7cf64 | ||
|
|
d6b28cdc57 | ||
|
|
75fb065526 | ||
|
|
6c5086a933 | ||
|
|
2b674d4cd9 | ||
|
|
5d0ddb3123 | ||
|
|
2d4b75428a | ||
|
|
f39d20ac97 | ||
|
|
db2b6f1268 | ||
|
|
99354ac576 | ||
|
|
e7b8531751 | ||
|
|
ff2f599142 | ||
|
|
f396b1bf73 | ||
|
|
e237dfc660 | ||
|
|
641029ab18 | ||
|
|
065ef29de2 | ||
|
|
aac0fa2b5f | ||
|
|
17edec8e4a | ||
|
|
8e667866fd | ||
|
|
01fe7d6620 | ||
|
|
f3aff45042 | ||
|
|
d6d94d9427 | ||
|
|
acb78205c8 | ||
|
|
dcb3c2c299 | ||
|
|
9048f8cffe | ||
|
|
4a2fb8336e | ||
|
|
34a16e0ab9 | ||
|
|
c2779b6f33 | ||
|
|
2e32b5f467 | ||
|
|
da5ec98f98 | ||
|
|
d54f5a3831 | ||
|
|
5e475acb85 | ||
|
|
f666d8a083 | ||
|
|
7ee7f00bf3 | ||
|
|
397a23499d | ||
|
|
d660bde324 | ||
|
|
94bd53c0f1 | ||
|
|
f70f4a4e85 | ||
|
|
f3eb9af046 | ||
|
|
0edf248cd1 | ||
|
|
514ffb74aa | ||
|
|
e6c8614801 | ||
|
|
05a9f2f0f5 | ||
|
|
115ef3249c | ||
|
|
a2461d9816 | ||
|
|
191cd21028 | ||
|
|
b3a785711c | ||
|
|
503b420292 | ||
|
|
0aa205044b | ||
|
|
00afe56cad | ||
|
|
1fd65c934d | ||
|
|
960e9edff5 | ||
|
|
b72720f6b6 | ||
|
|
f60a0c5ce0 | ||
|
|
ba903e21ed | ||
|
|
75b943b98a | ||
|
|
18565600b2 | ||
|
|
f4e4e734de | ||
|
|
6424bac47c | ||
|
|
bdd7d24ac1 | ||
|
|
5131759823 | ||
|
|
1e4ef81244 | ||
|
|
0fa807e7ad | ||
|
|
b6987b4287 | ||
|
|
ed0e2b52d7 | ||
|
|
168d35747d | ||
|
|
a01baab4f0 | ||
|
|
17c3e12eab | ||
|
|
71b001fdb7 | ||
|
|
833de9180e | ||
|
|
fb36561a68 | ||
|
|
67c60229ca | ||
|
|
f4bcb82041 | ||
|
|
983ccef87c | ||
|
|
a849dcadb6 | ||
|
|
d5afd0d7c2 | ||
|
|
ecc06aad24 | ||
|
|
cc83983ae5 | ||
|
|
ba5183b244 | ||
|
|
3d3584b36c | ||
|
|
c5e534c0cb | ||
|
|
a338a97d5b | ||
|
|
e11219888d | ||
|
|
e6638b4715 | ||
|
|
c9db8c6857 | ||
|
|
917a465ccd | ||
|
|
ce5adbf51e | ||
|
|
50e581d88a | ||
|
|
47d7fc70e8 | ||
|
|
e2574cf069 | ||
|
|
806ca4c842 | ||
|
|
ee1ec0428b | ||
|
|
3b17dca252 | ||
|
|
eef40cb3fd | ||
|
|
9f413ed174 | ||
|
|
233cfc29d6 | ||
|
|
e11330a5c8 | ||
|
|
0b2ad9deca | ||
|
|
227320f6f0 | ||
|
|
3bee6543fb | ||
|
|
581a3c5323 | ||
|
|
a59e84cadd | ||
|
|
d4627a0b1c | ||
|
|
95e45bbeac | ||
|
|
08a4763bff | ||
|
|
537f30f707 | ||
|
|
ad954d01de | ||
|
|
e3d70e6b62 | ||
|
|
cbf009a95d | ||
|
|
35cc763a92 | ||
|
|
cf05111622 | ||
|
|
e6224898d2 | ||
|
|
d566ffa678 | ||
|
|
6bf2e8a108 | ||
|
|
805ecde6a5 | ||
|
|
2a26b0ae91 | ||
|
|
916e088462 | ||
|
|
c918bbba25 | ||
|
|
57226b2e13 | ||
|
|
f46eecc1e7 | ||
|
|
5c062aaec4 | ||
|
|
b87106b6fe | ||
|
|
523e7b5084 | ||
|
|
41fa2d6c69 | ||
|
|
5947467339 | ||
|
|
0a11991340 | ||
|
|
0630609d6e | ||
|
|
205924ff29 | ||
|
|
7371e08625 | ||
|
|
bb5ec8cfb8 | ||
|
|
e6c8de5e4c | ||
|
|
bd403276f2 | ||
|
|
7f8dcf4f12 | ||
|
|
eef8f3b417 | ||
|
|
0fd8eeec23 | ||
|
|
7124c0aee5 | ||
|
|
852b2659e9 | ||
|
|
b41357e2a1 | ||
|
|
1cc816f662 | ||
|
|
bfe84a9ff7 | ||
|
|
339e620738 | ||
|
|
9ef710c557 | ||
|
|
df4ff26845 | ||
|
|
2e93a09d83 | ||
|
|
05aba0b4dd | ||
|
|
f098600c41 | ||
|
|
ad83825d4f | ||
|
|
3102440d40 | ||
|
|
715b1667d9 | ||
|
|
7d62e087c6 | ||
|
|
dcc65bbacb | ||
|
|
feaad50b6c | ||
|
|
7b2d54dffa | ||
|
|
e170217c33 | ||
|
|
aff58934c0 | ||
|
|
9f576369a9 | ||
|
|
7d7f43c205 | ||
|
|
30a5fe3061 | ||
|
|
b155e93ab1 | ||
|
|
3704ff57cb | ||
|
|
ee1d527497 | ||
|
|
f40f7b4d6e | ||
|
|
911510f999 | ||
|
|
45f5ac560d | ||
|
|
fe7020e2f8 | ||
|
|
17e161006a | ||
|
|
85d2ad4aea | ||
|
|
5b9cd0af64 | ||
|
|
8d298d5a06 | ||
|
|
3035c792dc | ||
|
|
2029b2b9ed | ||
|
|
900d8f3b0a | ||
|
|
e2de2f65d5 | ||
|
|
81da9f99e4 | ||
|
|
216f034b6d | ||
|
|
b96edd8b9a | ||
|
|
27ee95106d | ||
|
|
dac3138fd1 | ||
|
|
e0a7637626 | ||
|
|
8c4481733f | ||
|
|
d20e62dcd3 | ||
|
|
7805a3c527 | ||
|
|
0df3978cc5 | ||
|
|
bd4a88cd3e | ||
|
|
2dc1236dca | ||
|
|
2528d97f52 | ||
|
|
241b257556 | ||
|
|
83a42afddf | ||
|
|
780f83a118 | ||
|
|
ec3651d216 | ||
|
|
6154fc7686 | ||
|
|
1924459abd | ||
|
|
f66576f366 | ||
|
|
7470fc7f24 | ||
|
|
0e041a3b64 | ||
|
|
0406e3a7dd | ||
|
|
c8ae04a96a | ||
|
|
c809b6f95e | ||
|
|
1577efa25e | ||
|
|
fb93ae2128 | ||
|
|
25c9e9ef93 | ||
|
|
812dc9e898 | ||
|
|
bf4b698573 | ||
|
|
5162c55e5d | ||
|
|
096619dbbe | ||
|
|
fa02a09107 | ||
|
|
51e2ef39c2 | ||
|
|
b19bbf5473 | ||
|
|
e5c66d94f2 | ||
|
|
2a32713dfc | ||
|
|
acf26d5c63 | ||
|
|
39b14b6b81 | ||
|
|
ac81a3a5ef | ||
|
|
ab628eb2a3 | ||
|
|
b8c5a91940 | ||
|
|
f7d2c99a3a | ||
|
|
32408ed6a3 | ||
|
|
515b054a6e | ||
|
|
cef77ce5bb | ||
|
|
1267d995ef | ||
|
|
ec8091a102 | ||
|
|
5536df51f5 | ||
|
|
f48b12ff52 | ||
|
|
415a8d1e01 | ||
|
|
2a840460dd | ||
|
|
12f114c4be | ||
|
|
b593c3a9b9 | ||
|
|
8fa26db1b3 | ||
|
|
ecae0b3d97 | ||
|
|
63423c8ee1 | ||
|
|
ed16660867 | ||
|
|
9c9d6363af | ||
|
|
3a43b1d85d | ||
|
|
76f07a7f97 | ||
|
|
4d219df04e | ||
|
|
01bfadaeaa | ||
|
|
670d12219b | ||
|
|
da2c0a22f9 | ||
|
|
953ac6f3c7 | ||
|
|
545219b839 | ||
|
|
985fb44424 | ||
|
|
17c5502330 | ||
|
|
aa88837a31 | ||
|
|
a3ec9f3940 | ||
|
|
8cd9a1e4fc | ||
|
|
f4d9d34bbc | ||
|
|
20b4fc9198 | ||
|
|
e40112b40d | ||
|
|
cc09f9a7a5 | ||
|
|
4b505948f0 | ||
|
|
3cf6f1f79c | ||
|
|
905374c86e | ||
|
|
953ca2c21e | ||
|
|
8b42834c3f | ||
|
|
fb61c4fb34 | ||
|
|
30841d9470 | ||
|
|
7a4efcf67f | ||
|
|
6e0eb532a5 | ||
|
|
bfaabfb7b5 | ||
|
|
4578649f75 | ||
|
|
b6324246ff | ||
|
|
53e4713932 | ||
|
|
76e11b46b7 | ||
|
|
3dad4a9ff1 | ||
|
|
310f370b55 | ||
|
|
5b92922516 | ||
|
|
dbb0a0283f | ||
|
|
08f108e6cf | ||
|
|
5c947c7519 | ||
|
|
178b440f05 | ||
|
|
755dd67ec7 | ||
|
|
99e8499722 | ||
|
|
2e7606f569 | ||
|
|
22edd00211 | ||
|
|
c81f6b9f6f | ||
|
|
d0f0a99909 | ||
|
|
2793086c0d | ||
|
|
80d3a831f3 | ||
|
|
961539258b | ||
|
|
9749bbcedc | ||
|
|
aa5ac9dc3f | ||
|
|
0324bc5cf0 | ||
|
|
f6d7628254 | ||
|
|
e5f4d80bbe | ||
|
|
aa77b04860 | ||
|
|
5b479d63d6 | ||
|
|
8924f94d05 | ||
|
|
a873b7b6e8 | ||
|
|
7baa20d559 | ||
|
|
2b7f114c2d | ||
|
|
951bf5ec2e | ||
|
|
28a7458e48 | ||
|
|
e25bd28b73 | ||
|
|
a2a87fec54 | ||
|
|
d457f11717 | ||
|
|
bc5f73e6c3 | ||
|
|
1dc35db1fe | ||
|
|
427badcae9 | ||
|
|
e0b705fd54 | ||
|
|
854c23a751 | ||
|
|
fa48de33c2 | ||
|
|
fc7c4e64ff | ||
|
|
d9aa16aa82 | ||
|
|
976e32d3ae | ||
|
|
476116a972 | ||
|
|
f2dda646ec | ||
|
|
05ed20d336 | ||
|
|
b0476be36f | ||
|
|
a005088228 | ||
|
|
ef1c01d968 | ||
|
|
47fc0c7958 | ||
|
|
a9f690260a | ||
|
|
688171d016 | ||
|
|
a16dcc877c | ||
|
|
9a0dff08bc | ||
|
|
77dedf6648 | ||
|
|
212d8b6cfd | ||
|
|
9eb23a1208 | ||
|
|
ccfd028919 | ||
|
|
e5a919c32e | ||
|
|
eeaafe9f39 | ||
|
|
6430a232f8 | ||
|
|
24569a18db | ||
|
|
5c458e9111 | ||
|
|
0ef2622621 | ||
|
|
1ff9abe1b4 | ||
|
|
8e5948716e | ||
|
|
1e68d4fb75 | ||
|
|
1bec739195 | ||
|
|
025a7d2f0f | ||
|
|
ebf238be5d | ||
|
|
98484f0def | ||
|
|
892f061ad6 | ||
|
|
8bfb620a13 | ||
|
|
b6d425838f | ||
|
|
a83f13269f | ||
|
|
e963714ad6 | ||
|
|
6d4188f05e | ||
|
|
3e804757c1 | ||
|
|
cc43a49b6d | ||
|
|
b18b9464a4 | ||
|
|
71d4f65cb2 | ||
|
|
b5933e9d44 | ||
|
|
7abfe716b4 | ||
|
|
81b3c59711 | ||
|
|
410f5389ae | ||
|
|
290a14d29e | ||
|
|
a1183bf09a | ||
|
|
e109abbef7 | ||
|
|
43055e0199 | ||
|
|
1789c3242a | ||
|
|
f41380de77 | ||
|
|
0e0a6f5f8d | ||
|
|
ef95c68b4f | ||
|
|
0cada39c8a | ||
|
|
bdbbe00bdf | ||
|
|
a1241daa41 | ||
|
|
1f43da2ade | ||
|
|
1614dbf6ae | ||
|
|
96cd63cf1a | ||
|
|
974ca9d526 | ||
|
|
86985b454f | ||
|
|
c55a7b5f36 | ||
|
|
beb55235c6 | ||
|
|
9d5d3a9468 | ||
|
|
cf415a4312 | ||
|
|
bb3d56a0b2 | ||
|
|
8f181bb188 | ||
|
|
135d84ba37 | ||
|
|
77442dfcdc | ||
|
|
ed597146fa | ||
|
|
7a242f829f | ||
|
|
7fc2b13fe6 | ||
|
|
ee5372784e | ||
|
|
0dcea87b99 | ||
|
|
d5d5d78e3a | ||
|
|
a10f5e9e06 | ||
|
|
85e82e919e | ||
|
|
376ad1f024 | ||
|
|
9b96fe66a6 | ||
|
|
11045b92fb | ||
|
|
2ad5010dc3 | ||
|
|
40bba93a31 | ||
|
|
9410b63bbc | ||
|
|
4234c9a194 | ||
|
|
d43a814385 | ||
|
|
e46e7002a8 | ||
|
|
e6d20877b8 | ||
|
|
5ef8199dae | ||
|
|
e86e666c22 | ||
|
|
cf180f6142 | ||
|
|
67665a8b9d | ||
|
|
a50e555515 | ||
|
|
04518702d2 | ||
|
|
75b97cafb0 | ||
|
|
037cd74e8e | ||
|
|
82a81cd01f | ||
|
|
655290b022 | ||
|
|
b886a40471 | ||
|
|
c0d80a5828 | ||
|
|
adecd2960e | ||
|
|
9afecaaffe | ||
|
|
2af8105b46 | ||
|
|
4528becf4c | ||
|
|
d2a7f83bb5 | ||
|
|
b8b50bdb5a | ||
|
|
4485142a25 | ||
|
|
2a6afeb9a7 | ||
|
|
e2245446a3 | ||
|
|
08a9553ccc | ||
|
|
0a3fbdd128 | ||
|
|
cfbd2fc85c | ||
|
|
dd6718c2cd | ||
|
|
d5f72165fd | ||
|
|
c1a24d44a3 | ||
|
|
bf438f67e1 | ||
|
|
81f6511d34 | ||
|
|
a6012af00d | ||
|
|
f89b7ac9e1 | ||
|
|
a0d339f208 | ||
|
|
6cd348f155 | ||
|
|
2c7ce20ccf | ||
|
|
4bc4fd5b7e | ||
|
|
a92241d3cf | ||
|
|
344c293424 | ||
|
|
d20fb3a31b | ||
|
|
71cd6ef24b | ||
|
|
9b87a249b9 | ||
|
|
b84f7cd37f | ||
|
|
b5d8443f59 | ||
|
|
c08f428b5e | ||
|
|
20a79c7acf | ||
|
|
a4474b49cb | ||
|
|
0333dca550 | ||
|
|
af4352adf2 | ||
|
|
1877df0e70 | ||
|
|
a7e5e3d574 | ||
|
|
e81ac9e063 | ||
|
|
624f4641e2 | ||
|
|
dcba30a6aa | ||
|
|
25aded6b9b | ||
|
|
8d6c2b5d4a | ||
|
|
29a0a11b7e | ||
|
|
7582802f03 | ||
|
|
f8949ed5d1 | ||
|
|
7ae376b6cd | ||
|
|
390ce207db | ||
|
|
b93e398674 | ||
|
|
b70c680964 | ||
|
|
f9075e2a2f | ||
|
|
e596bf025b | ||
|
|
4a3ec65409 | ||
|
|
0f1fcd9743 | ||
|
|
f23bdde464 | ||
|
|
05d9e1bee5 | ||
|
|
a9d7acda27 | ||
|
|
1f5529752f | ||
|
|
b4eb9f2a11 | ||
|
|
c1039977f1 | ||
|
|
b1259876bf | ||
|
|
dfb6255f59 | ||
|
|
00b4843425 | ||
|
|
701acf59e2 | ||
|
|
380f65d309 | ||
|
|
316fee93f7 | ||
|
|
ca78374f30 | ||
|
|
670e83b300 | ||
|
|
44d3dadb03 | ||
|
|
9e9f2e13fe | ||
|
|
6602ff83dd | ||
|
|
8d284bab47 | ||
|
|
490ab9e2c5 | ||
|
|
0757dbf07f | ||
|
|
f35609d26c | ||
|
|
82ec40dd80 | ||
|
|
a52cdcb241 | ||
|
|
d1efb59fcd | ||
|
|
1523e116b9 | ||
|
|
90f2959076 | ||
|
|
6d4adfcedc | ||
|
|
f2ebe41a50 | ||
|
|
8f0c3f0e9b | ||
|
|
3b2679db29 | ||
|
|
4b350b9090 | ||
|
|
a3dfdd9d38 | ||
|
|
00d32f6b94 | ||
|
|
fad27fc1e7 | ||
|
|
3e6af5c876 | ||
|
|
75b169f391 | ||
|
|
df7772e301 | ||
|
|
63a97ff6fc | ||
|
|
cac6cef495 | ||
|
|
aab9766c53 | ||
|
|
ab54bf5149 | ||
|
|
55b587002e | ||
|
|
819683a073 | ||
|
|
7393dccde8 | ||
|
|
66effbfe08 | ||
|
|
ae94e8a855 | ||
|
|
f884fa6678 | ||
|
|
ac4688eac0 | ||
|
|
4691c3ec01 | ||
|
|
ca78ebce6d | ||
|
|
97e6b7c4ba | ||
|
|
ab6eb1c4b2 | ||
|
|
0919c1eb61 | ||
|
|
d554681174 | ||
|
|
551cf8ee94 | ||
|
|
61304d80d2 | ||
|
|
1bce285eca | ||
|
|
096fdfc61b | ||
|
|
8e4889bdf1 | ||
|
|
7b91e551c4 | ||
|
|
9d7abd58ac | ||
|
|
24360fd191 | ||
|
|
9017dcd0fd | ||
|
|
30d0f21079 | ||
|
|
d78bc12cbe | ||
|
|
2a9c1db0c9 | ||
|
|
4716627453 | ||
|
|
2f64684299 | ||
|
|
66b6f8c0ff | ||
|
|
e559f8b6a1 | ||
|
|
0d8dcdbbc9 | ||
|
|
da19ac98dd | ||
|
|
45568bf097 | ||
|
|
9908a7193a | ||
|
|
929d33fb22 | ||
|
|
a3e617987f | ||
|
|
c37eaff263 | ||
|
|
80f2ba640e | ||
|
|
f9a295a236 | ||
|
|
516c43ba15 | ||
|
|
84ba6393ad | ||
|
|
e00b8a7082 | ||
|
|
29ba891809 | ||
|
|
25357b208a | ||
|
|
80b422bdbe | ||
|
|
4e01956b33 | ||
|
|
1215638540 | ||
|
|
bbc71343bd | ||
|
|
a1aa3d9061 | ||
|
|
1aa3051e97 | ||
|
|
20f444f5f2 | ||
|
|
2d06663490 | ||
|
|
5e0698ba87 | ||
|
|
d7d347469c | ||
|
|
de006771c7 | ||
|
|
b4e5d303b5 | ||
|
|
a36bef7979 | ||
|
|
2fdd148598 | ||
|
|
3dc131757d | ||
|
|
1cb311cef9 | ||
|
|
ed56b6e67b | ||
|
|
049147a9d7 | ||
|
|
5c1f23b61a | ||
|
|
4d4e49f4d8 | ||
|
|
5f2baa59f5 | ||
|
|
c6aa42773a | ||
|
|
7a526fa8a9 | ||
|
|
598baa1b32 | ||
|
|
ef9b42aeb3 | ||
|
|
4979a45120 | ||
|
|
b66bacff9f | ||
|
|
9480e9b68b | ||
|
|
bf05012150 | ||
|
|
d8ace38041 | ||
|
|
bc20cf9fa3 | ||
|
|
019250eff8 | ||
|
|
ac095c89f1 | ||
|
|
f4da365abd | ||
|
|
b2f0d2d085 | ||
|
|
aefeda8c41 | ||
|
|
fdece3b102 | ||
|
|
cae380f068 | ||
|
|
3aa6e96904 | ||
|
|
a97e7b2758 | ||
|
|
2cda18f354 | ||
|
|
2e83c17e2d | ||
|
|
2f7d40d8db | ||
|
|
f228bf4555 | ||
|
|
a2cec8899a | ||
|
|
014168a29b | ||
|
|
39bc0664a7 | ||
|
|
853322e7d2 | ||
|
|
9cb1ac3de5 | ||
|
|
5b2474238e | ||
|
|
3c5cb15432 | ||
|
|
6e7d7c5017 | ||
|
|
1f2bd00d93 | ||
|
|
c7cad6ab60 | ||
|
|
0f5eb65210 | ||
|
|
fc239cb8a2 | ||
|
|
3fe7bd8bcc | ||
|
|
6b69184554 | ||
|
|
d5ca889cf5 | ||
|
|
062cb77539 | ||
|
|
b55e2776cc | ||
|
|
50d1d06b03 | ||
|
|
3eca1c3696 | ||
|
|
93a7df5a1b | ||
|
|
905432c7ae | ||
|
|
3355210878 | ||
|
|
ada26dd2cb | ||
|
|
f4b31dcb3a | ||
|
|
7abb214eaf | ||
|
|
77eb9bcfa0 | ||
|
|
c17b47518d | ||
|
|
1f7e6cae82 | ||
|
|
37d14f3a1d | ||
|
|
166d63ff60 | ||
|
|
d38851023e | ||
|
|
e35bd5d251 | ||
|
|
c77732b8ed | ||
|
|
16f79b70e4 | ||
|
|
43c2b22fca | ||
|
|
9a64c6b9f7 | ||
|
|
20cb4130d4 | ||
|
|
bfbe6c1660 | ||
|
|
dc78ab1c77 | ||
|
|
51edd472c2 | ||
|
|
1977ab35c0 | ||
|
|
296957b5b5 | ||
|
|
0d04a1221a | ||
|
|
6ac6537f10 | ||
|
|
883b1ff513 | ||
|
|
de1527eba8 | ||
|
|
2dcedd6951 | ||
|
|
c8322e89c6 | ||
|
|
18a9831cd3 | ||
|
|
9f77180af8 | ||
|
|
0bd4939695 | ||
|
|
605c05fc8b | ||
|
|
fa3aed4e59 | ||
|
|
217db8f9b2 | ||
|
|
a0dd03ea0e | ||
|
|
894114bfbd | ||
|
|
90353d90dc | ||
|
|
c9ce5664d4 | ||
|
|
e8ad16cf2a | ||
|
|
c3090d5480 | ||
|
|
f48aee5975 | ||
|
|
60f6b827f4 | ||
|
|
f05b4637b9 | ||
|
|
fffeca59d0 | ||
|
|
b14dd0e066 | ||
|
|
ec81ef7b8d | ||
|
|
e02c658eb5 | ||
|
|
0800bd1e19 | ||
|
|
0503b11840 | ||
|
|
b497faee27 | ||
|
|
7ee31276f5 | ||
|
|
5897f62fa9 | ||
|
|
0baab50191 | ||
|
|
0db1594f95 | ||
|
|
1e00fa79b3 | ||
|
|
573dea42d0 | ||
|
|
269055e71c | ||
|
|
2f222546da | ||
|
|
522fec5763 | ||
|
|
f2fc7d25c5 | ||
|
|
2d79e75788 | ||
|
|
781716277b | ||
|
|
9f57bfb5df | ||
|
|
4d00c48026 | ||
|
|
8fe8341d93 | ||
|
|
57273db8a6 | ||
|
|
bc9f88f3f9 | ||
|
|
70d413ab04 | ||
|
|
710d6ebd49 | ||
|
|
ac1ec99684 | ||
|
|
8e5718bf04 | ||
|
|
89561bec4f | ||
|
|
fae78b947f | ||
|
|
d35dfc12ce | ||
|
|
69112a17ac | ||
|
|
32ad8bbaac | ||
|
|
913d07a4a7 | ||
|
|
b40d8070f3 | ||
|
|
1fb21b781f | ||
|
|
25df2080ea | ||
|
|
5632022d79 | ||
|
|
d968fc51ea | ||
|
|
c7698e4dc3 | ||
|
|
4dd3839c41 | ||
|
|
9caaa9397e | ||
|
|
915bd2ec77 | ||
|
|
ba64b11ac0 | ||
|
|
40e3d37a72 | ||
|
|
2882eef42f | ||
|
|
0b02a6d123 | ||
|
|
40b5b6e9d2 | ||
|
|
78b40e6363 | ||
|
|
45c19e365d | ||
|
|
2469853175 | ||
|
|
0c5434d5e3 | ||
|
|
8ab7707898 | ||
|
|
baa7020036 | ||
|
|
f658a3cae1 | ||
|
|
a674b05e96 | ||
|
|
b7e7ef0bf3 | ||
|
|
0b8a7f1bd0 | ||
|
|
9c8cf7d433 | ||
|
|
180731c162 | ||
|
|
ea1173f8f6 | ||
|
|
ef97e5a063 | ||
|
|
61909d2480 | ||
|
|
7aeff7505b | ||
|
|
a3e94591bc | ||
|
|
24c1dc528d | ||
|
|
6f486a3393 | ||
|
|
c8f686b8a5 | ||
|
|
89c65de863 | ||
|
|
94accd8a4c | ||
|
|
b6e0d8dee0 | ||
|
|
2a14c71128 | ||
|
|
d714cd76dd | ||
|
|
5ac262bd61 | ||
|
|
174484a51c | ||
|
|
09ef30feb0 | ||
|
|
e7a65c1f8d | ||
|
|
f73a74967e | ||
|
|
bbbae3f42d | ||
|
|
0746794e81 | ||
|
|
2b77c9a74b | ||
|
|
75128e8f68 | ||
|
|
284d86ad76 | ||
|
|
852ff39856 | ||
|
|
7f974a543f | ||
|
|
6950100ff4 | ||
|
|
e9e2159b3b | ||
|
|
b08fc746d8 | ||
|
|
4a36834d3f | ||
|
|
5882714e23 | ||
|
|
7101bbedcb | ||
|
|
712b2b1bb2 | ||
|
|
3011d3768c | ||
|
|
5508feb6ce | ||
|
|
2e3da0cbbb | ||
|
|
f38c8e5eca | ||
|
|
0314624aeb | ||
|
|
2174cdcd0e | ||
|
|
6ce939491b | ||
|
|
8ffdc4859b | ||
|
|
9b19a51fc6 | ||
|
|
9eac9ef2b9 | ||
|
|
566667a263 | ||
|
|
8cb06e7af8 | ||
|
|
1b6bc5394b | ||
|
|
50fb525b28 | ||
|
|
8c9a773a90 | ||
|
|
e1e56936c9 | ||
|
|
33a8d845c1 | ||
|
|
9fe33bb67d | ||
|
|
0db8d3f6c6 | ||
|
|
7c57da3a28 | ||
|
|
1685548a4c | ||
|
|
191298ec93 | ||
|
|
e4a2aacd1d | ||
|
|
5686ee13b4 | ||
|
|
45c7a03a0d | ||
|
|
3df0bb5522 | ||
|
|
7fe697d896 | ||
|
|
0bdcaff00f | ||
|
|
8088185a43 | ||
|
|
2db86b950e | ||
|
|
9619d02be9 | ||
|
|
dccdeedb75 | ||
|
|
b371c028ad | ||
|
|
b017f8f2ab | ||
|
|
2b7b26f4c0 | ||
|
|
ae39217a7e | ||
|
|
78e42a75f1 | ||
|
|
e0e76ce82c | ||
|
|
559ed3f0a8 | ||
|
|
a10fe5f13e | ||
|
|
bd2f11ed8b | ||
|
|
5e458491f1 | ||
|
|
4ed5d6573c | ||
|
|
c2d15e6c3b | ||
|
|
f284558007 | ||
|
|
23bb4e8e15 | ||
|
|
cd518625bf | ||
|
|
b1ae4d776c | ||
|
|
95b9669213 | ||
|
|
b4bf71dd06 | ||
|
|
b35e69273d | ||
|
|
4560298b98 | ||
|
|
d892a47ac9 | ||
|
|
fa80a5c113 | ||
|
|
7fac859fbd | ||
|
|
02d8363019 | ||
|
|
84d93cca6e | ||
|
|
9446d0cbb4 | ||
|
|
8a41e2a011 | ||
|
|
36fe2ac87e | ||
|
|
3fe8fc0aa8 | ||
|
|
3d9fcee075 | ||
|
|
917bcb55fd | ||
|
|
026787fc73 | ||
|
|
d43c7603bf | ||
|
|
17dba9542a | ||
|
|
c96b20c559 | ||
|
|
8189a7c693 | ||
|
|
2f74f9eb40 | ||
|
|
ab17b2e0d6 | ||
|
|
8f16e9ad33 | ||
|
|
7d380570df | ||
|
|
80c9a8b8f6 | ||
|
|
8544819be1 | ||
|
|
45654ffc5c | ||
|
|
45512e6aba | ||
|
|
d7a64845fe | ||
|
|
4c47d22f4b | ||
|
|
931a4fafe3 | ||
|
|
83a0e82d5c | ||
|
|
e7fbf1b017 | ||
|
|
b5b21ecad7 | ||
|
|
9d079e5d2b | ||
|
|
18f505608b | ||
|
|
b797528b78 | ||
|
|
d8aa0ac36c | ||
|
|
49b957f7ea | ||
|
|
e01b90c4c6 | ||
|
|
1fdac57d61 | ||
|
|
0788d37c60 | ||
|
|
776b7074ea | ||
|
|
bb96f44861 | ||
|
|
30a6ae3daa | ||
|
|
37a852d2d1 | ||
|
|
59777333dd | ||
|
|
eb85f8572f | ||
|
|
b664af748e | ||
|
|
f108838ca9 | ||
|
|
2ddd03f597 | ||
|
|
bdef71bda0 | ||
|
|
d4eb5e7c6d | ||
|
|
8e8df26066 | ||
|
|
c91e43af34 | ||
|
|
b13c21f872 | ||
|
|
f975d9dfe4 | ||
|
|
a6baa5a6da | ||
|
|
26131ffc91 | ||
|
|
aeda293bd0 | ||
|
|
a67fa2651d | ||
|
|
509f25e46a | ||
|
|
f99daf8ff9 | ||
|
|
607125e795 | ||
|
|
f34dcffbd9 | ||
|
|
ce50a429fa | ||
|
|
3446e68ac2 | ||
|
|
e508ad37c1 | ||
|
|
f55d9e8c62 | ||
|
|
563e82feff | ||
|
|
5553df862a | ||
|
|
2e7028c976 | ||
|
|
78e4b64c8b | ||
|
|
d62f490814 | ||
|
|
6bf7716518 | ||
|
|
ae02624793 | ||
|
|
c3b0c0f32a | ||
|
|
6aef5593d5 | ||
|
|
975140634c | ||
|
|
f336f38ad5 | ||
|
|
f027bf81cd | ||
|
|
d67d8b6d6e | ||
|
|
02ee7f17e8 | ||
|
|
78320ee3a6 | ||
|
|
a9d926886e | ||
|
|
6291c08e01 | ||
|
|
4b9c3cc998 | ||
|
|
fdf7120aec | ||
|
|
e5e68d7350 | ||
|
|
d914290002 | ||
|
|
42ee3ab21c | ||
|
|
15cfd6fda7 | ||
|
|
0e34811886 | ||
|
|
5b3f536613 | ||
|
|
ce45a1cf94 | ||
|
|
79c63e3cf4 | ||
|
|
df2990b27e | ||
|
|
9d698c0887 | ||
|
|
1e27038b73 | ||
|
|
7e30c1dd82 | ||
|
|
78bf4564f2 | ||
|
|
665a517a47 | ||
|
|
57110cbe76 | ||
|
|
b0a2e5d3f7 | ||
|
|
f3eae7132f | ||
|
|
0463df62f0 | ||
|
|
a8823ae7d8 | ||
|
|
fdef224da1 | ||
|
|
59c642ed19 | ||
|
|
2b91962b32 | ||
|
|
61e695e143 | ||
|
|
aab9218558 | ||
|
|
c250cb955e | ||
|
|
f111275ddf | ||
|
|
2726c68b60 | ||
|
|
1288a72963 | ||
|
|
d66f6c8e8c | ||
|
|
a3ce194bf5 | ||
|
|
5a427dcee3 | ||
|
|
7d45f63b78 | ||
|
|
540800da95 | ||
|
|
1ff189ee90 | ||
|
|
692e41c41e | ||
|
|
cb8c83a42b | ||
|
|
bd11b1d61a | ||
|
|
96d19c09f3 | ||
|
|
d7f4bfedd5 | ||
|
|
391ed8655e | ||
|
|
aa0c70e198 | ||
|
|
82de68c3d3 | ||
|
|
4669107634 | ||
|
|
cf79c4ea48 | ||
|
|
ae932922c4 | ||
|
|
bacd05ea9e | ||
|
|
657b34c012 | ||
|
|
5eb594706b | ||
|
|
2b0a04ea9e | ||
|
|
f00a701ad1 | ||
|
|
dff9e18c1e | ||
|
|
d2f67bf249 | ||
|
|
8f64b28fb9 | ||
|
|
0d91f363cc | ||
|
|
23180ae078 | ||
|
|
b96eb1c823 | ||
|
|
635580091a | ||
|
|
a768bb1ab2 | ||
|
|
59799ba2b4 | ||
|
|
8147e6203f | ||
|
|
2641980ac2 | ||
|
|
683da1aa77 | ||
|
|
21b0128e9e | ||
|
|
d07afb54e4 | ||
|
|
1e8b3fe749 | ||
|
|
9040ee53e4 | ||
|
|
974390d5d2 | ||
|
|
8a57b845ae | ||
|
|
324771f858 | ||
|
|
dd7fdf0c2b | ||
|
|
bb7b756974 | ||
|
|
e805f589aa | ||
|
|
6941fbb182 | ||
|
|
7594796ee1 | ||
|
|
f53478f142 | ||
|
|
2a4e8972e0 | ||
|
|
2a61817bad | ||
|
|
e47a473e2c | ||
|
|
094d4cc325 | ||
|
|
a49fa5f87c | ||
|
|
ca59ecd107 | ||
|
|
f761ea5fc9 | ||
|
|
1698732cff | ||
|
|
a9f68e4d2a | ||
|
|
f12af17238 | ||
|
|
d1ecf3f323 | ||
|
|
9cddbc9a47 | ||
|
|
966773cdbb | ||
|
|
d2e61122c5 | ||
|
|
7229cd56ed | ||
|
|
521d404013 | ||
|
|
5591eac973 | ||
|
|
c8dec1fae5 | ||
|
|
c366bbaa54 | ||
|
|
72e7c50968 | ||
|
|
f4cca21fdf | ||
|
|
d7fe48d1b6 | ||
|
|
fddacf0a5a | ||
|
|
08a651dc9d | ||
|
|
65571249b2 | ||
|
|
b4205554a8 | ||
|
|
0865d0ef51 | ||
|
|
b57f69d14d | ||
|
|
07650b4646 | ||
|
|
ce582ccada | ||
|
|
ee5163ce64 | ||
|
|
bbe33f9503 | ||
|
|
2cb04ccb8f | ||
|
|
aaa327ca05 | ||
|
|
ce34c4086d | ||
|
|
fe9df64fc2 | ||
|
|
f124f4a9f5 | ||
|
|
a1af2cbb30 | ||
|
|
ec9e914798 | ||
|
|
ec5bd9d679 | ||
|
|
c38c79d71c | ||
|
|
45a854762b | ||
|
|
1806da0851 | ||
|
|
521641f3a8 | ||
|
|
a29c0371f1 | ||
|
|
a8f7af2079 | ||
|
|
7ae7fac234 | ||
|
|
d1fb4e9b03 | ||
|
|
ed0bff798b | ||
|
|
3f0bb1bb1d | ||
|
|
9a0b61e4af | ||
|
|
b81291cd5c | ||
|
|
a8a0ca8291 | ||
|
|
1c61be4113 | ||
|
|
91cc365143 | ||
|
|
f4a27afee9 | ||
|
|
be654f1769 | ||
|
|
73628db7e2 | ||
|
|
571c72f853 | ||
|
|
b788b38edf | ||
|
|
09861abab7 | ||
|
|
202035fc68 | ||
|
|
5ab1d509d2 | ||
|
|
1b83d88fe6 | ||
|
|
482962648f | ||
|
|
326f3cfc85 | ||
|
|
4ba8526745 | ||
|
|
0269825bc0 | ||
|
|
5cb3143c04 | ||
|
|
d1a2aecfa0 | ||
|
|
0fe4957274 | ||
|
|
e9f1bac062 | ||
|
|
58692e3565 | ||
|
|
7db733c9a0 | ||
|
|
abfbe9c937 | ||
|
|
f437705007 | ||
|
|
46ed79c87a | ||
|
|
e2b4d33627 | ||
|
|
7690dca67b | ||
|
|
c0bdcfedeb | ||
|
|
9b66b5eee3 | ||
|
|
2db59f3b76 | ||
|
|
44e21f6198 | ||
|
|
279f32b13d | ||
|
|
3a6187af3d | ||
|
|
516f776c94 | ||
|
|
cc62793d6f | ||
|
|
d87daaedd1 | ||
|
|
cf84ff3344 | ||
|
|
e419b2c18c | ||
|
|
58324473a9 | ||
|
|
62d842de31 | ||
|
|
aa678ba0ff | ||
|
|
e4391e6336 | ||
|
|
aa0412d83b | ||
|
|
861f5232d3 | ||
|
|
9831ad515d | ||
|
|
6650916435 | ||
|
|
64cc0b794a | ||
|
|
29363a185d | ||
|
|
e41082dccc | ||
|
|
97ccbf1bfb | ||
|
|
a20ef706fd | ||
|
|
adad8f2ebc | ||
|
|
0533db5d38 | ||
|
|
fb9c12df15 | ||
|
|
5a2a8717e3 | ||
|
|
0d50d29538 | ||
|
|
54aa998b52 | ||
|
|
10f1e2d51b | ||
|
|
9fd8634b62 | ||
|
|
fd07940b10 | ||
|
|
6f6051f7e3 | ||
|
|
f9778fda83 | ||
|
|
ea3fb53e4b | ||
|
|
a7d5bf6854 | ||
|
|
0e7a963173 | ||
|
|
4aa2733699 | ||
|
|
1eb6067c31 | ||
|
|
bd29551f9a | ||
|
|
32ccdd1f57 | ||
|
|
deaf0e99fd | ||
|
|
d99a1d8de0 | ||
|
|
0012030826 | ||
|
|
ec41170121 | ||
|
|
1d5ea5efae | ||
|
|
01ad485f65 | ||
|
|
447c59fd9b | ||
|
|
3de40bdf04 | ||
|
|
ba6c346b46 | ||
|
|
1c9f773900 | ||
|
|
ec651f219e | ||
|
|
ee8a9384d5 | ||
|
|
bba34a7a8e | ||
|
|
dfa12b2919 | ||
|
|
9b9f29a0b2 | ||
|
|
4a9aad2a33 | ||
|
|
69617b5c34 | ||
|
|
222ace3770 | ||
|
|
2e7d2a63c3 | ||
|
|
0523b4b2f1 | ||
|
|
a703835b5c | ||
|
|
bf3f3564e0 | ||
|
|
102d41ccb9 | ||
|
|
cfad5ba8b8 | ||
|
|
b4c1baada1 | ||
|
|
1ca3eeedff | ||
|
|
298b53e93a | ||
|
|
f13096a6f3 | ||
|
|
470f14b919 | ||
|
|
53311a30ad | ||
|
|
5634a4bb61 | ||
|
|
738e14348d | ||
|
|
787e2ef961 | ||
|
|
db4e9aeaad | ||
|
|
7850b04853 | ||
|
|
b6f18a9a0b | ||
|
|
b8d8be7013 | ||
|
|
84feff9500 | ||
|
|
81ba293561 | ||
|
|
8b3c099a12 | ||
|
|
2772a72095 | ||
|
|
e361ee9253 | ||
|
|
3edd4ce78f | ||
|
|
381cc3e6f8 | ||
|
|
d09694587d | ||
|
|
63451db3f6 | ||
|
|
7660afdb0f | ||
|
|
72d995d35f | ||
|
|
2a32df75f1 | ||
|
|
e16da7a166 | ||
|
|
5b55726a93 | ||
|
|
bbe93d0e1d | ||
|
|
5dd422e2d8 | ||
|
|
a564f495a7 | ||
|
|
995555f357 | ||
|
|
40f3857691 | ||
|
|
5d884c2f3d | ||
|
|
8f006a98c4 | ||
|
|
ebac0bdcca | ||
|
|
3575ef8a39 | ||
|
|
60bb8f25f7 | ||
|
|
2d3dd673e4 | ||
|
|
9d47de65d9 | ||
|
|
c6d9100649 | ||
|
|
44283dbbbb | ||
|
|
0cda431456 | ||
|
|
bbfe4de310 | ||
|
|
36b388ab8c | ||
|
|
c0b3d7e1b4 | ||
|
|
9abd95fad3 | ||
|
|
50d9b29347 | ||
|
|
90a5ba01a3 | ||
|
|
6c2673ae2a | ||
|
|
256c228efd | ||
|
|
3fdd110e07 | ||
|
|
1cd3b41825 | ||
|
|
4148211086 | ||
|
|
4ba9a5ef75 | ||
|
|
6a6dbec033 | ||
|
|
df891f4ad2 | ||
|
|
08461c7049 | ||
|
|
5462453bf2 | ||
|
|
e4f3605054 | ||
|
|
f65ede9c38 | ||
|
|
a6ad37c691 | ||
|
|
ba0812c447 | ||
|
|
dbde9a0940 | ||
|
|
d3f38c9f43 | ||
|
|
971e7321c6 | ||
|
|
eeb1c463e3 | ||
|
|
99d9456b3a | ||
|
|
97eef2b88c | ||
|
|
c25ef1d9a8 | ||
|
|
661b0084f3 | ||
|
|
f714e97d63 | ||
|
|
a507063174 | ||
|
|
3caf51b075 | ||
|
|
92ec46b62e | ||
|
|
9b06d952a2 | ||
|
|
531710142c | ||
|
|
59827b6dfd | ||
|
|
61bef7d4b0 | ||
|
|
f200dd4395 | ||
|
|
a8ac0ed549 | ||
|
|
6f2ba0c619 | ||
|
|
0a14ac1f60 | ||
|
|
02140b2f76 | ||
|
|
c6b89b99b9 | ||
|
|
51a840f46a | ||
|
|
b081c3e94b | ||
|
|
ec99e3ed62 | ||
|
|
97e1a6dfde | ||
|
|
56c86b6567 | ||
|
|
28599fe652 | ||
|
|
d3e7ef70e8 | ||
|
|
fea532af4d | ||
|
|
f5b22dfd5b | ||
|
|
48e62bb50a | ||
|
|
513078e1f7 | ||
|
|
23151100de | ||
|
|
eb68b66c58 | ||
|
|
1ddb43b0d3 | ||
|
|
9fcd470aca | ||
|
|
23419e4c26 | ||
|
|
34b42566b6 | ||
|
|
f7ff48f806 | ||
|
|
30cf56bdb5 | ||
|
|
77094f8d2b | ||
|
|
51cd4e8519 | ||
|
|
bd249b276d | ||
|
|
b6ae2bff01 | ||
|
|
21c1f89eba | ||
|
|
df61970b39 | ||
|
|
f82afca84d | ||
|
|
eca119d695 | ||
|
|
40162cd6b7 | ||
|
|
a41a05e6c0 | ||
|
|
a83cdbeb39 | ||
|
|
6d4fe5c56f | ||
|
|
3a8f6e6576 | ||
|
|
38eb79705e | ||
|
|
f7ddd44c2e | ||
|
|
2165733ddc | ||
|
|
d22b21c56f | ||
|
|
4188419b30 | ||
|
|
d98d7003ed | ||
|
|
1692380ac7 | ||
|
|
01a28ddf8d | ||
|
|
e8d19659c0 | ||
|
|
b673b6835d | ||
|
|
ef7494359f | ||
|
|
b8e8517725 | ||
|
|
def0454952 | ||
|
|
2f5862713e | ||
|
|
6729c27b77 | ||
|
|
fbfa391965 | ||
|
|
38beabb508 | ||
|
|
a8d5c83251 | ||
|
|
370a007ee2 | ||
|
|
7747a23a78 | ||
|
|
c573670821 | ||
|
|
a752d44313 | ||
|
|
9406ff714b | ||
|
|
cc29e27acc | ||
|
|
b2563528ee | ||
|
|
70ad69d38c | ||
|
|
cb0c43bc68 | ||
|
|
7efffbf102 | ||
|
|
2d8deb6082 | ||
|
|
5de79cdb10 | ||
|
|
6837c2ef1e | ||
|
|
4e9440396c | ||
|
|
32c23ef562 | ||
|
|
107f80892a | ||
|
|
fbcb711945 | ||
|
|
e95a494041 | ||
|
|
7732532964 | ||
|
|
bb09b8a385 | ||
|
|
b9b46a3f88 | ||
|
|
7bb461380e | ||
|
|
f5f79b39b8 | ||
|
|
14e55e8bd6 | ||
|
|
bc9d5f5a48 | ||
|
|
c2cdd41e81 | ||
|
|
256cd6baac | ||
|
|
843cc1481a | ||
|
|
cf4dc2caa8 | ||
|
|
1258f9bb21 | ||
|
|
5f41d06bb9 | ||
|
|
8f4fe106ef | ||
|
|
d74274c7e4 | ||
|
|
d59268a5d8 | ||
|
|
f364343b55 | ||
|
|
9debfa348a | ||
|
|
32e903e956 | ||
|
|
bd8fe768d3 | ||
|
|
848c038970 | ||
|
|
24fa201218 | ||
|
|
1a77fb4c1d | ||
|
|
76467b5a35 | ||
|
|
d5db25808a | ||
|
|
6824fb9f93 | ||
|
|
b2216f8203 | ||
|
|
0799a0a3be | ||
|
|
a2e0574fce | ||
|
|
68d7ab6b55 | ||
|
|
4a2c805270 | ||
|
|
7d5b266227 | ||
|
|
1f56ff5407 | ||
|
|
95f5f3d44d | ||
|
|
7a5213cc8e | ||
|
|
f1f19372ad | ||
|
|
276f44328e | ||
|
|
da3f1326e0 | ||
|
|
a013ddbb91 | ||
|
|
1c6faa3a00 | ||
|
|
b7f15ccac1 | ||
|
|
1fb7b9baae | ||
|
|
a1e4940bea | ||
|
|
287ffe93f6 | ||
|
|
2407379623 | ||
|
|
7162403292 | ||
|
|
004468f231 | ||
|
|
2d7e307e74 | ||
|
|
d7466ade46 | ||
|
|
01c0e5c46c | ||
|
|
86f840843f | ||
|
|
9a0f0181a5 | ||
|
|
25ebac8d0b | ||
|
|
edf6da7189 | ||
|
|
059da4d017 | ||
|
|
a59d1b22f6 | ||
|
|
dd4a66353a | ||
|
|
2cf54c465d | ||
|
|
d3491c9165 | ||
|
|
8ca877bdd3 | ||
|
|
b704f977da | ||
|
|
59e04aed1b | ||
|
|
436e91a1e1 | ||
|
|
6f4c45a489 | ||
|
|
157bbb14b5 | ||
|
|
5c22f59c4f | ||
|
|
aa62bad07b | ||
|
|
d3f2d2a2db | ||
|
|
c5d08fd5fd | ||
|
|
22729da2a2 | ||
|
|
c97cc1487c | ||
|
|
428b4dbad6 | ||
|
|
264abef817 | ||
|
|
548bf884a4 | ||
|
|
a0baa480e3 | ||
|
|
765dd5569b | ||
|
|
c19a1a6b82 | ||
|
|
8b61e546fe | ||
|
|
7206e8af3a | ||
|
|
7808689198 | ||
|
|
966d1586c0 | ||
|
|
edffd9f183 | ||
|
|
7edc505c2f | ||
|
|
4df78b06cf | ||
|
|
492609d20b | ||
|
|
0f76af261c | ||
|
|
9d00db06b2 | ||
|
|
141f3e21b2 | ||
|
|
fe6cbb414c | ||
|
|
e13d65eea3 | ||
|
|
4d6050f8e5 | ||
|
|
0ebce75c7d | ||
|
|
91bb4bdf50 | ||
|
|
be2b7c0b5f | ||
|
|
2d45f9f385 | ||
|
|
44b2503572 | ||
|
|
3f90da5643 | ||
|
|
d27a627c4f | ||
|
|
55ab5360d9 | ||
|
|
bfeeb76143 | ||
|
|
f2d3be2e67 | ||
|
|
60b818eb24 | ||
|
|
ebe70a41b9 | ||
|
|
da79662081 | ||
|
|
72f3f7c172 | ||
|
|
713a796d9f | ||
|
|
69c6d8b617 | ||
|
|
e084a85e79 | ||
|
|
a1f046992d | ||
|
|
cd5630476f | ||
|
|
f7f20c5917 | ||
|
|
44797fe988 | ||
|
|
c5333b5187 | ||
|
|
2d045a12bf | ||
|
|
a2fe22412f | ||
|
|
df69465357 | ||
|
|
e4361cb202 | ||
|
|
58cf9d832c | ||
|
|
cf5383fdbe | ||
|
|
6126070a1d | ||
|
|
459166116d | ||
|
|
776bc51172 | ||
|
|
df57c90d2d | ||
|
|
5a563b8e76 | ||
|
|
0653a2e0a9 | ||
|
|
d19dfc87a6 | ||
|
|
dc8c7a2d2e | ||
|
|
ba2ede0a8f | ||
|
|
6892ba4cd8 | ||
|
|
5ad9b48f1d | ||
|
|
7972de13ca | ||
|
|
ea54987ca4 | ||
|
|
b2474334a7 | ||
|
|
2a0d4a2011 | ||
|
|
be27fb5644 | ||
|
|
ebfa1a12b9 | ||
|
|
21f2df399d | ||
|
|
43521bb10b | ||
|
|
c95d7c69eb | ||
|
|
f12b0fe316 | ||
|
|
8944d7a7a2 | ||
|
|
6300f47cdc | ||
|
|
2b95580cc0 | ||
|
|
93b84f1262 | ||
|
|
1ffae1327f | ||
|
|
01a8292d65 | ||
|
|
9d3ce55978 | ||
|
|
7e6febe2a6 | ||
|
|
e73df9ed1d | ||
|
|
b101a8f6fb | ||
|
|
ddeab48f9b | ||
|
|
d074c53889 | ||
|
|
039eb58524 | ||
|
|
29c9e7c0a2 | ||
|
|
f15a06c0c2 | ||
|
|
ab89eb050a | ||
|
|
eca1f84328 | ||
|
|
3bde7b8f18 | ||
|
|
9db1148650 | ||
|
|
502fed198d | ||
|
|
411c510778 | ||
|
|
dd6e3d873d | ||
|
|
f9b092c619 | ||
|
|
edc7c0837b | ||
|
|
f535d438b2 | ||
|
|
119ffd2d2d | ||
|
|
2c3bec3d7c | ||
|
|
f1564b4cff | ||
|
|
c9252d49f7 | ||
|
|
411abdf8a7 | ||
|
|
1fcc863298 | ||
|
|
e37c1ed981 | ||
|
|
0c5a2925ad | ||
|
|
272ffe6983 | ||
|
|
368bb18935 | ||
|
|
22f172d697 | ||
|
|
fe9abcfaae | ||
|
|
dfbe2eaf98 | ||
|
|
a9711895cf | ||
|
|
8882ffc0de | ||
|
|
fdd7faab51 | ||
|
|
d004017b01 | ||
|
|
f2d27d543d | ||
|
|
1db3409f36 | ||
|
|
cfa2ac2d0a | ||
|
|
ebc66d7008 | ||
|
|
3f0eb84ecf | ||
|
|
fbc6c60805 | ||
|
|
02e82e496e | ||
|
|
8e586008dd | ||
|
|
50c7c33ed1 | ||
|
|
ca52d39abe | ||
|
|
28d07fd6b2 | ||
|
|
5a7b994e59 | ||
|
|
4512a4cdd4 | ||
|
|
342fda94e4 | ||
|
|
cc264b2d60 | ||
|
|
33c34984e6 | ||
|
|
fe392a3144 | ||
|
|
eeed7077ca | ||
|
|
dba1854837 | ||
|
|
c107bcfbd9 | ||
|
|
912ebfebff | ||
|
|
76bf892f9f | ||
|
|
12a9b49c6d | ||
|
|
aecf9e32ef | ||
|
|
879bc71927 | ||
|
|
4da7fcb41d | ||
|
|
3a84e7051e | ||
|
|
161050df07 | ||
|
|
29284319a5 | ||
|
|
86e73fd313 | ||
|
|
62e141a89c | ||
|
|
cd8b7687c1 | ||
|
|
4436cd9ade | ||
|
|
ddbe3c7426 | ||
|
|
b8c7b2f81d | ||
|
|
696c6e88ea | ||
|
|
a83eb14ff6 | ||
|
|
8e6dc0b66b | ||
|
|
41ec5403e1 | ||
|
|
77eb6c5761 | ||
|
|
89c6cda303 | ||
|
|
d49afead67 | ||
|
|
0a10215371 | ||
|
|
64f5d80ff2 | ||
|
|
28a593e1ce | ||
|
|
f738902107 | ||
|
|
364972a292 | ||
|
|
0eb0256502 | ||
|
|
679fb7d0d2 | ||
|
|
45247d1595 | ||
|
|
3be6ef6bfc | ||
|
|
c5827c8b4f | ||
|
|
bb381daae3 | ||
|
|
d0090f09fb | ||
|
|
afb86aa908 | ||
|
|
3b97a9f766 | ||
|
|
d3c779f635 | ||
|
|
4f285911f0 | ||
|
|
8ccddfefd9 | ||
|
|
4351a83d16 | ||
|
|
ddefe3b3ac | ||
|
|
b77c5ae61e | ||
|
|
dd2d12350c | ||
|
|
ade91f8190 | ||
|
|
d04aef4c44 | ||
|
|
689a6dc085 | ||
|
|
0b487122f6 | ||
|
|
ac4a8fde3a | ||
|
|
31aaf40ade | ||
|
|
af7c7642bb | ||
|
|
d939173c0e | ||
|
|
a87efe45e0 | ||
|
|
55515a25d1 | ||
|
|
7d7a254238 | ||
|
|
564a5ea4d5 | ||
|
|
d9c59a19fb | ||
|
|
83b5adf194 | ||
|
|
a26d1794fe | ||
|
|
06a1258a9a | ||
|
|
d31d3df4ea | ||
|
|
91801d2e2c | ||
|
|
95c686de6e | ||
|
|
bcb94f8238 | ||
|
|
1d2260b44e | ||
|
|
81c10fc739 | ||
|
|
191a2fd5be | ||
|
|
97be144913 | ||
|
|
8c99228c8a | ||
|
|
f984502499 | ||
|
|
315ce6d880 | ||
|
|
3da8f9b13c | ||
|
|
a14b87baf2 | ||
|
|
90fd23e0cd | ||
|
|
d96e985be0 | ||
|
|
c8788204a9 | ||
|
|
be99c7fe12 | ||
|
|
48963f62f9 | ||
|
|
00ae6bdac6 | ||
|
|
a606173e07 | ||
|
|
d8e6fd5df0 | ||
|
|
ec950de205 | ||
|
|
62e7fa6a23 | ||
|
|
2935c5fe1a | ||
|
|
021584d782 | ||
|
|
a5c111ef4f | ||
|
|
7868e47643 | ||
|
|
b4e3cd000f | ||
|
|
9c3568800c | ||
|
|
9cf105f8ea | ||
|
|
a8456782b5 | ||
|
|
2afdba48f0 | ||
|
|
4250895243 | ||
|
|
bfeb89075f | ||
|
|
721f61a71d | ||
|
|
a6e5d26aea | ||
|
|
d586f64e40 | ||
|
|
a0900ba052 | ||
|
|
40b00ab362 | ||
|
|
18208fab84 | ||
|
|
8f810a3dcf | ||
|
|
c598bbb8a9 | ||
|
|
e6f388d869 | ||
|
|
24e2eedd0f | ||
|
|
c75f76f740 | ||
|
|
006efbd906 | ||
|
|
28386d7471 | ||
|
|
f9545dc359 | ||
|
|
e527a52f6a | ||
|
|
a7ed50ecd4 | ||
|
|
06dcaaa821 | ||
|
|
36ac48779c | ||
|
|
88669fa033 | ||
|
|
225aa28a1b | ||
|
|
f1245206ce | ||
|
|
7f453949a1 | ||
|
|
38f385f67d | ||
|
|
5f5342c0d3 | ||
|
|
15e003f030 | ||
|
|
5edbc00865 | ||
|
|
723a49ccee | ||
|
|
ff487a0271 | ||
|
|
337735094b | ||
|
|
01b5b13475 | ||
|
|
fd9f792f90 | ||
|
|
e8ded61efb | ||
|
|
b0e098bb68 | ||
|
|
f26c698f4a | ||
|
|
a341c889b7 | ||
|
|
0cd82ec680 | ||
|
|
1f87fef968 | ||
|
|
c8d0a2b4bb | ||
|
|
385709853b | ||
|
|
0405a49df1 | ||
|
|
76484f1541 | ||
|
|
b6543beca2 | ||
|
|
da198fdda5 | ||
|
|
24f912e0b2 | ||
|
|
d7abacb7b2 | ||
|
|
08aeeadd71 | ||
|
|
c214692f66 | ||
|
|
35a9a468ce | ||
|
|
28943af494 | ||
|
|
9abff11f86 | ||
|
|
5f1eb18da4 | ||
|
|
ae3ca2b7df | ||
|
|
d49c5b3d8e | ||
|
|
9ea904c12d | ||
|
|
9a4cbc05a6 | ||
|
|
08fbe17627 | ||
|
|
c766cf9565 | ||
|
|
722d7ff38d | ||
|
|
5d6657f77a | ||
|
|
d83b907cdc | ||
|
|
202817740a | ||
|
|
9b08051703 | ||
|
|
9c71b74d37 | ||
|
|
535a54040f | ||
|
|
68030d12cf | ||
|
|
3494fddd7c | ||
|
|
424af13891 | ||
|
|
e4674142c6 | ||
|
|
9081ddb455 | ||
|
|
1ccf836ebb | ||
|
|
5e9e1b2c91 | ||
|
|
baed7d339e | ||
|
|
6759a28579 | ||
|
|
0db2fe6e39 | ||
|
|
845945e70a | ||
|
|
089509ab87 | ||
|
|
ae6ae88151 | ||
|
|
7b73a689e1 | ||
|
|
0d119d83b0 | ||
|
|
945689d01c | ||
|
|
d70f69566f | ||
|
|
48495f41e8 | ||
|
|
47f7750f26 | ||
|
|
81e3aa4fc8 | ||
|
|
8f6e01a70b | ||
|
|
b0d5b9fd2c | ||
|
|
75468c255a | ||
|
|
e52f64e3d2 | ||
|
|
7d7ef5ac4e | ||
|
|
b368e0a1f9 | ||
|
|
dff2cdc650 | ||
|
|
dcaef756e7 | ||
|
|
2be515e7bb | ||
|
|
699d5d1081 | ||
|
|
8847e39fb7 | ||
|
|
55e9bd3660 | ||
|
|
fb1e6ca6c1 | ||
|
|
88d29a7b4b | ||
|
|
cbedb50cb3 | ||
|
|
78b2a4af1a | ||
|
|
457cc48043 | ||
|
|
b711c050db | ||
|
|
928f1bfa7b | ||
|
|
2b3b6268dd | ||
|
|
97ebf9021c | ||
|
|
c49e4f52ae | ||
|
|
1d89198444 | ||
|
|
8c5d5b8e1c | ||
|
|
d3bec0b2f9 | ||
|
|
c15e3de637 | ||
|
|
24bffd4418 | ||
|
|
da6abe5462 | ||
|
|
1e523414f8 | ||
|
|
190f78fb5a | ||
|
|
64acf3acbc | ||
|
|
a076a248bf | ||
|
|
1ca47074bc | ||
|
|
b380b618bc | ||
|
|
a036d88a4e | ||
|
|
2a57f716d3 | ||
|
|
86598705ef | ||
|
|
5d24f62b95 | ||
|
|
dd3f69eaf3 | ||
|
|
fc54f54b98 | ||
|
|
80b88d819e | ||
|
|
24c10ae733 | ||
|
|
3b18c82c88 | ||
|
|
be83855acf | ||
|
|
10ed334e8c | ||
|
|
c4a14c27cf | ||
|
|
819a331c4a | ||
|
|
9fa6593d7f | ||
|
|
256472f9eb | ||
|
|
9b3b2efe9a | ||
|
|
da9a8d54de | ||
|
|
af8449cef8 | ||
|
|
0365f5d52c | ||
|
|
110ef6201e | ||
|
|
0d5daea93d | ||
|
|
983e170f20 | ||
|
|
f66c6bfebf | ||
|
|
fe77ab4d71 | ||
|
|
763c700372 | ||
|
|
2498df68c7 | ||
|
|
d36aa4fc89 | ||
|
|
f4e1b2123a | ||
|
|
63ee0daac5 | ||
|
|
b58177e3c6 | ||
|
|
f888548e0e | ||
|
|
6ff192c4ea | ||
|
|
64753effd1 | ||
|
|
be9442e7b3 | ||
|
|
ef2890fe51 | ||
|
|
614dcb8bce | ||
|
|
3ba81c8bb6 | ||
|
|
588d022fbb | ||
|
|
96990fc1c5 | ||
|
|
4506d80198 | ||
|
|
72353bf044 | ||
|
|
2696deb2a1 | ||
|
|
9f9fdc1434 | ||
|
|
e2b79c34bc | ||
|
|
69573c6afe | ||
|
|
5c937c5642 | ||
|
|
e500a08f15 | ||
|
|
82aec69302 | ||
|
|
9584ca080c | ||
|
|
f510cd3111 | ||
|
|
e1ddee6c26 | ||
|
|
86b550e43e | ||
|
|
3bf9dc9f18 | ||
|
|
0ca7aa8c09 | ||
|
|
58efd7553c | ||
|
|
0484f5b0dc | ||
|
|
961540160f | ||
|
|
93096a9db8 | ||
|
|
f757285ea8 | ||
|
|
baedd25309 | ||
|
|
bbc1a1f1fb | ||
|
|
addf696db8 | ||
|
|
9327ac0ba7 | ||
|
|
a8a5cb05a7 | ||
|
|
bff33a98ce | ||
|
|
66c8658898 | ||
|
|
d6b2c8f296 | ||
|
|
4e89895391 | ||
|
|
0f5da31eb2 | ||
|
|
3206a46a92 | ||
|
|
42b35c5ac9 | ||
|
|
04180985cb | ||
|
|
42433c0b44 | ||
|
|
f4d47f825d | ||
|
|
44137be88d | ||
|
|
a524f0c2c1 | ||
|
|
d79d6c7055 | ||
|
|
e51035ba9a | ||
|
|
d4645ec609 | ||
|
|
51f34b6b07 | ||
|
|
9cbbc92062 | ||
|
|
23c7086605 | ||
|
|
b00c22916a | ||
|
|
41057ac364 | ||
|
|
1c982315dd | ||
|
|
bf99906386 | ||
|
|
3c88184177 | ||
|
|
f4968e9f45 | ||
|
|
100412267a | ||
|
|
00b98730f7 | ||
|
|
242da95eed | ||
|
|
0eb5dd6d21 | ||
|
|
0fa34c6880 | ||
|
|
bfe8e1e2e9 | ||
|
|
761afd029d | ||
|
|
93565ea768 | ||
|
|
1f82beb2ef | ||
|
|
c076e72cbf | ||
|
|
26139ee06f | ||
|
|
bd394ba6dc | ||
|
|
1f0e603992 | ||
|
|
f5c10f249c | ||
|
|
69f9842ec9 | ||
|
|
d2d71b8630 | ||
|
|
ff3aa6c663 | ||
|
|
dcb5f62413 | ||
|
|
c3e2505b66 | ||
|
|
c7cc595906 | ||
|
|
2dd9e27a44 | ||
|
|
26ed0b0f34 | ||
|
|
374aadc97e | ||
|
|
86dd4e7ee0 | ||
|
|
714a93022f | ||
|
|
35fd21531a | ||
|
|
ca00ae2e23 | ||
|
|
b8655dd3bc | ||
|
|
9e6f43e3ff | ||
|
|
9ec258b74d | ||
|
|
8ca4fb3784 | ||
|
|
d860dd753e | ||
|
|
b4ccc62596 | ||
|
|
07f5bdde60 | ||
|
|
5b0a17359d | ||
|
|
1ad90b8662 | ||
|
|
54ceef0ba1 | ||
|
|
c38edfcb32 | ||
|
|
5b9afd7555 | ||
|
|
e665afcc2a | ||
|
|
b0ec0e9eac | ||
|
|
49e4016ba2 | ||
|
|
9ee7d60afe | ||
|
|
1964fa05e1 | ||
|
|
caf3a69d99 | ||
|
|
769ad4a836 | ||
|
|
062c03c9f7 | ||
|
|
2cf4ac26fd | ||
|
|
405725875d | ||
|
|
5f61f57b2c | ||
|
|
fbeb33479b | ||
|
|
ffd4557447 | ||
|
|
cf5df259e5 | ||
|
|
3d6359f2e7 | ||
|
|
5a229234e7 | ||
|
|
b1a77b2235 | ||
|
|
6db6fef29f | ||
|
|
31fbd19c86 | ||
|
|
a085f4df7f | ||
|
|
cd2c6779b7 | ||
|
|
d39cd088a1 | ||
|
|
e98fcf68f0 | ||
|
|
7d5e066056 | ||
|
|
ba920260b8 | ||
|
|
2988d96cf0 | ||
|
|
36717e5f32 | ||
|
|
4dff54bcb8 | ||
|
|
7a9fe0907a | ||
|
|
bc5a675cdf | ||
|
|
f23e8f8d41 | ||
|
|
61fa97baab | ||
|
|
047163b884 | ||
|
|
6409237b29 | ||
|
|
598a428c6b | ||
|
|
1cfb83f003 | ||
|
|
2aad4c2cf9 | ||
|
|
e2692a4baa | ||
|
|
474a1267d7 | ||
|
|
ed5ca9e4cc | ||
|
|
8d61efa0e8 | ||
|
|
b816a575eb | ||
|
|
a99d28dea9 | ||
|
|
628e565a6c | ||
|
|
84bf705811 | ||
|
|
a721f39945 | ||
|
|
1b9722c65a | ||
|
|
62a60e5542 | ||
|
|
539d06f02d | ||
|
|
9c23fe5ab6 | ||
|
|
f78e9001f3 | ||
|
|
dee1841394 | ||
|
|
a5201a90e8 | ||
|
|
5f5e4ee6b9 | ||
|
|
cf6519da99 | ||
|
|
63eaf643a8 | ||
|
|
a57a3c709e | ||
|
|
bcebd1d5b9 | ||
|
|
4cacfe59bd | ||
|
|
f3ad7b9a16 | ||
|
|
b909c878b1 | ||
|
|
b155f2260f | ||
|
|
63256bc9b2 | ||
|
|
87519e107a | ||
|
|
a38710aa4e | ||
|
|
020fea3031 | ||
|
|
b4102489aa | ||
|
|
871ee0f571 | ||
|
|
f0d90005ea | ||
|
|
933e105f32 | ||
|
|
9b4e6e1ec0 | ||
|
|
46e4d2d573 | ||
|
|
2aa3acae6b | ||
|
|
8514789da9 | ||
|
|
20dcf37976 | ||
|
|
38d513181d | ||
|
|
f121b74bec | ||
|
|
cb8cccc537 | ||
|
|
2924b9182e | ||
|
|
5a59cd9b8c | ||
|
|
ba463afbdc | ||
|
|
6f2a5117dc | ||
|
|
b13241564b | ||
|
|
76816de3aa | ||
|
|
49108c230b | ||
|
|
b32bba47c8 | ||
|
|
3b1ab5e79c | ||
|
|
dcf7946673 | ||
|
|
fdd2da905a | ||
|
|
2cceca49e9 | ||
|
|
bb0e4044bf | ||
|
|
7244dfce1d | ||
|
|
fb5ac37ca2 | ||
|
|
f0a588547d | ||
|
|
04d3ac9aaf | ||
|
|
565126a888 | ||
|
|
395268da22 | ||
|
|
ac3075bef7 | ||
|
|
1f4c596841 | ||
|
|
6613275cc2 | ||
|
|
e7d9863432 | ||
|
|
b9af0645c2 | ||
|
|
9fb7a12849 | ||
|
|
4633273279 | ||
|
|
d0482133a5 | ||
|
|
4ff31d0a41 | ||
|
|
f73c82632f | ||
|
|
8389d8677d | ||
|
|
7fa944ed95 | ||
|
|
2340092008 | ||
|
|
6cd3296c08 | ||
|
|
d86bc0b6cf | ||
|
|
e01d4f289b | ||
|
|
8e1e301764 | ||
|
|
bd37f59829 | ||
|
|
72edc9cd6c | ||
|
|
93123e1fa8 | ||
|
|
4b6dcbb057 | ||
|
|
47f7165b07 | ||
|
|
ae2c0e6552 | ||
|
|
200eabe052 | ||
|
|
5042200492 | ||
|
|
2e73e6bfb6 | ||
|
|
c1d10bbbcb | ||
|
|
746acc21f7 | ||
|
|
f5226a748f | ||
|
|
6a3995014e | ||
|
|
534a537319 | ||
|
|
10eefc4eed | ||
|
|
3d4b3b6a92 | ||
|
|
0f4ceedbb4 | ||
|
|
656e5fd052 | ||
|
|
5735cad457 | ||
|
|
648f972cf0 | ||
|
|
baab9b6fbc | ||
|
|
60b0f9af90 | ||
|
|
9d04406f9a | ||
|
|
29ac533cc9 | ||
|
|
79340c5147 | ||
|
|
b9f5076cfc | ||
|
|
241607e7d7 | ||
|
|
bd7d851758 | ||
|
|
5de1a1ce10 | ||
|
|
8b974f8f74 | ||
|
|
96d94551f9 | ||
|
|
ba8d9c0f0b | ||
|
|
68cbebe1a5 | ||
|
|
0174cb10ae | ||
|
|
293df6308c | ||
|
|
af29822f04 | ||
|
|
0df4f9c114 | ||
|
|
69c9f4588c | ||
|
|
53627c89a7 | ||
|
|
f7f0d2b265 | ||
|
|
2d799f2c11 | ||
|
|
aa5b7eb98a | ||
|
|
1c22e8ae7e | ||
|
|
4d301bc5a6 | ||
|
|
4ccd9ae6da | ||
|
|
c8c9916b59 | ||
|
|
5490919557 | ||
|
|
3f920da413 | ||
|
|
2393db3943 | ||
|
|
a88ec1b1af | ||
|
|
553b3a5c6c | ||
|
|
a6cb25020b | ||
|
|
26c465ad0c | ||
|
|
f2d7083183 | ||
|
|
ef09a29854 | ||
|
|
c23ce16caf | ||
|
|
78a70fed2f | ||
|
|
9dfe5dee64 | ||
|
|
36ef1d1bc6 | ||
|
|
10db51e6c2 | ||
|
|
61aa33af1d | ||
|
|
d8dc055dce | ||
|
|
ce686d2c06 | ||
|
|
7119976199 | ||
|
|
dd7edc8ced | ||
|
|
4428f70ea2 | ||
|
|
0bb7589418 | ||
|
|
0513463256 | ||
|
|
8a9d743f6f | ||
|
|
4cab74c18c | ||
|
|
615c97132d | ||
|
|
4b707db4b3 | ||
|
|
70c5f347c4 | ||
|
|
5217d89543 | ||
|
|
ca55bbdaed | ||
|
|
366e735ad8 | ||
|
|
c363fc71eb | ||
|
|
9f3ea71d5d | ||
|
|
75058e54b8 | ||
|
|
ed5405d4f7 | ||
|
|
8dd4270a6a | ||
|
|
c29f622b90 | ||
|
|
bc0e415926 | ||
|
|
c48965ea2f | ||
|
|
bb995f1bb6 | ||
|
|
857e13e9b8 | ||
|
|
593423434c | ||
|
|
25caeee854 | ||
|
|
20ce7a2d9f | ||
|
|
1ba74d824b | ||
|
|
be7b706486 | ||
|
|
78e18fcdcf | ||
|
|
706598313e | ||
|
|
b96f09bb3a | ||
|
|
4b191fb558 | ||
|
|
de1abf9981 | ||
|
|
c69f6ca1f4 | ||
|
|
d4b04d22ed | ||
|
|
b1f4b9759b | ||
|
|
650f882265 | ||
|
|
a2747403f2 | ||
|
|
3f9412add2 | ||
|
|
29877963be | ||
|
|
302c5afdee | ||
|
|
9f90806027 | ||
|
|
519862115c | ||
|
|
4d97ea106c | ||
|
|
d3aa50c9c3 | ||
|
|
1708769d42 | ||
|
|
4e223f816a | ||
|
|
8cf6b4064f | ||
|
|
17907f11a3 | ||
|
|
cb7df797e1 | ||
|
|
7db86a4e05 | ||
|
|
122d7281f7 | ||
|
|
4bfd34b479 | ||
|
|
962d9f36d0 | ||
|
|
216d41cd64 | ||
|
|
4af104384b | ||
|
|
aa78d51521 | ||
|
|
16e7ec7bfe | ||
|
|
fce1dc083b | ||
|
|
bcfa72ed31 | ||
|
|
b92cc852d1 | ||
|
|
6762af4158 | ||
|
|
f691e8981a | ||
|
|
6875a540ec | ||
|
|
e569f5d6eb | ||
|
|
61a5e81bc6 | ||
|
|
ee153f64d4 | ||
|
|
4c4db6f73c | ||
|
|
fa5a382c4b | ||
|
|
a73c385831 | ||
|
|
91b35c5349 | ||
|
|
056db37c66 | ||
|
|
5cb71fa5da | ||
|
|
55d00243fb | ||
|
|
8a269823d2 | ||
|
|
5ec5b127d0 | ||
|
|
0cf9e498b9 | ||
|
|
c407e72dcc | ||
|
|
8e3b796a2f | ||
|
|
17e7b276f6 | ||
|
|
306ce3819e | ||
|
|
f80aff4a65 | ||
|
|
c7cbd41c16 | ||
|
|
41542e6886 | ||
|
|
a039869ba0 | ||
|
|
192e85ffa4 | ||
|
|
b334201797 | ||
|
|
3ea84d7eab | ||
|
|
a80c45940e | ||
|
|
ab71e33d83 | ||
|
|
fe57e7059b | ||
|
|
2de65ab39f | ||
|
|
0559db9cf8 | ||
|
|
bae7b034e6 | ||
|
|
74a40adece | ||
|
|
1767a07f4d | ||
|
|
af7549d2f6 | ||
|
|
e785b723aa | ||
|
|
f520cd92d0 | ||
|
|
eb6fd311df | ||
|
|
5a12944b39 | ||
|
|
987619130b | ||
|
|
2b2723cb74 | ||
|
|
e485ecb3b4 | ||
|
|
8c5203f7e1 | ||
|
|
dfaf1a1075 | ||
|
|
bf5c0e0b0d | ||
|
|
167e754a1b | ||
|
|
34b804e9dc | ||
|
|
7338649fa5 | ||
|
|
ae1103c5a3 | ||
|
|
a7e4553ea7 | ||
|
|
6f27e8db38 | ||
|
|
292b02b58c | ||
|
|
e60f527cb0 | ||
|
|
b5a5a91b13 | ||
|
|
4c84a2ce61 | ||
|
|
18e0e4b597 | ||
|
|
74b574d0b1 | ||
|
|
9ba1b8d720 | ||
|
|
d726c921eb | ||
|
|
bd45bb3ad3 | ||
|
|
fee8503093 | ||
|
|
fabf7081d3 | ||
|
|
6083cd2559 | ||
|
|
d11e80e1b3 | ||
|
|
6942c715d0 | ||
|
|
427652e9bd | ||
|
|
106b4fdd2d | ||
|
|
f164ddf155 | ||
|
|
d9ddf7561f | ||
|
|
37368bda65 | ||
|
|
de1b3d5113 | ||
|
|
830b01bd2a | ||
|
|
90187eae37 | ||
|
|
7399f7a087 | ||
|
|
a3e47d26f6 | ||
|
|
d3bef2adc3 | ||
|
|
7b459ec355 | ||
|
|
301072a86c | ||
|
|
3b7b3ef2bb | ||
|
|
b6e4611a91 | ||
|
|
2929bb672a | ||
|
|
a4765bf954 | ||
|
|
0c2045beff | ||
|
|
22f6687f44 | ||
|
|
e56633d5b0 | ||
|
|
a44795e6c3 | ||
|
|
5805a54097 | ||
|
|
350519d429 | ||
|
|
8f94c721bf | ||
|
|
c7652fc29c | ||
|
|
e537d53389 | ||
|
|
a890ee6864 | ||
|
|
4f81d64517 | ||
|
|
b5875c3af9 | ||
|
|
ed87dbc141 | ||
|
|
059886a1e4 | ||
|
|
ba52af786e | ||
|
|
929f524e77 | ||
|
|
cd49ec92e3 | ||
|
|
ee39e3cf72 | ||
|
|
a17f280f7d | ||
|
|
7b4cfffc03 | ||
|
|
db9c2992e3 | ||
|
|
b23284cba2 | ||
|
|
0c163c4d03 | ||
|
|
e4145deeb7 | ||
|
|
33258803ea | ||
|
|
179730e485 | ||
|
|
106b3257e4 | ||
|
|
312cc415ce | ||
|
|
1f2a408be0 | ||
|
|
0d232c612b | ||
|
|
68da4d90dc | ||
|
|
545dc5cf65 | ||
|
|
a089064588 | ||
|
|
a06f7fbe2e | ||
|
|
7c4d9519c8 | ||
|
|
6fa0807550 | ||
|
|
c545ada746 | ||
|
|
868a0d8a90 | ||
|
|
5079dff338 | ||
|
|
9e3d130ad9 | ||
|
|
b531486dab | ||
|
|
6df98f080b | ||
|
|
69487389d3 | ||
|
|
0cb5f009b4 | ||
|
|
c7b2ec8bba | ||
|
|
12b0a9f35f | ||
|
|
fff30b1c3d | ||
|
|
6ad35e3d84 | ||
|
|
3d80073a0e | ||
|
|
959c3ba89d | ||
|
|
1029939f56 | ||
|
|
fd5d1416d9 | ||
|
|
def5edf93c | ||
|
|
6bdde985ce | ||
|
|
dba38821bc | ||
|
|
9d6a4c1d05 | ||
|
|
94bef08f15 | ||
|
|
0178840c19 | ||
|
|
d04c73ae00 | ||
|
|
495f21c08c | ||
|
|
d238cbea19 | ||
|
|
5d0646c9e1 | ||
|
|
93e5449c08 | ||
|
|
4872a30e2b | ||
|
|
f5fcdf1de4 | ||
|
|
888bf76e4b | ||
|
|
22a650ade8 | ||
|
|
391807b46c | ||
|
|
921a2e0dc4 | ||
|
|
5afe8e5aa1 | ||
|
|
ba84d6ff7c | ||
|
|
d786502f19 | ||
|
|
63063450b3 | ||
|
|
90463ee67d | ||
|
|
6e04dd96a1 | ||
|
|
f7c837aad0 | ||
|
|
bd3d323849 | ||
|
|
e26b558e53 | ||
|
|
85e5cd7f98 | ||
|
|
1d8cac6e73 | ||
|
|
52f6bd03b7 | ||
|
|
75c10eaff5 | ||
|
|
53a796afcc | ||
|
|
f94a8698dd | ||
|
|
ffb8059c24 | ||
|
|
bc7c0ed844 | ||
|
|
ac9250fc4a | ||
|
|
f1492945ec | ||
|
|
10ccdbacba | ||
|
|
d683573402 | ||
|
|
ddce0412ac | ||
|
|
609e42cdd8 | ||
|
|
7235c20854 | ||
|
|
e7bedf4433 | ||
|
|
23d30ab2c5 | ||
|
|
741afeea41 | ||
|
|
cd989977b8 | ||
|
|
1216508dc2 | ||
|
|
5c7dfcfe43 | ||
|
|
cff866172a | ||
|
|
31a612aa89 | ||
|
|
b782c46e51 | ||
|
|
eaa47760bc | ||
|
|
7ec6879887 | ||
|
|
c4a3487f0c | ||
|
|
e6de9bfa4e | ||
|
|
c0260ff701 | ||
|
|
fba2603304 | ||
|
|
7d5acef20a | ||
|
|
cda8fb3380 | ||
|
|
862a310075 | ||
|
|
98146eae07 | ||
|
|
c277039fb4 | ||
|
|
92577ddaed | ||
|
|
b99b6b5ae9 | ||
|
|
cda81e35db | ||
|
|
271a41b7dd | ||
|
|
69aeddd185 | ||
|
|
99bd69adfe | ||
|
|
115a53225e | ||
|
|
36c196e18c | ||
|
|
0c8219e683 | ||
|
|
de9c44fc56 | ||
|
|
d1dbc50a1c | ||
|
|
0fbea73581 | ||
|
|
10f7199c63 | ||
|
|
49d6605377 | ||
|
|
11c8cabf28 | ||
|
|
00132cd977 | ||
|
|
5e1b61eb02 | ||
|
|
dfb49558f4 | ||
|
|
4e1c308162 | ||
|
|
5c2692a8ea | ||
|
|
9c240de303 | ||
|
|
2536dc39b5 | ||
|
|
cf0bff8582 | ||
|
|
9c12ba5708 | ||
|
|
8639eda70f | ||
|
|
1874346c46 | ||
|
|
16040063dc | ||
|
|
2b0b03eebc | ||
|
|
df2b687a95 | ||
|
|
44fa4d1081 | ||
|
|
ce4bb8e5ec | ||
|
|
8154453803 | ||
|
|
b29fe50f70 | ||
|
|
cce46722d8 | ||
|
|
85ae07e7c8 | ||
|
|
e3da813360 | ||
|
|
9a42d63516 | ||
|
|
4620eb32d2 | ||
|
|
1c8f63f1e5 | ||
|
|
c40f6060da | ||
|
|
5776af30e8 | ||
|
|
30d083ac3c | ||
|
|
4ca0c2cfd1 | ||
|
|
12db46dcad | ||
|
|
9e6547a887 | ||
|
|
017ea0d624 | ||
|
|
a29b8b369e | ||
|
|
6b26b4e3b8 | ||
|
|
7df92ba62c | ||
|
|
9cad4c11fe | ||
|
|
5b42edb42a | ||
|
|
a26d8609a9 | ||
|
|
001e4276d3 | ||
|
|
94437a8fcd | ||
|
|
91f751d105 | ||
|
|
0db5a8673d | ||
|
|
91f2e05d5d | ||
|
|
58fe4f5cb3 | ||
|
|
2ebd9a2774 | ||
|
|
8db428458a | ||
|
|
f8b612a2c0 | ||
|
|
76043d97bd | ||
|
|
07bd396837 | ||
|
|
f7468ab473 | ||
|
|
1213893a71 | ||
|
|
5f1ccc25b6 | ||
|
|
42c0f83ec5 | ||
|
|
5dfaeaf526 | ||
|
|
1214469311 | ||
|
|
f4a9529968 | ||
|
|
3d55134953 | ||
|
|
1c0fe89368 | ||
|
|
73bef17365 | ||
|
|
9398e49eb7 | ||
|
|
50cf095c00 | ||
|
|
5d64fb5946 | ||
|
|
bf55a8e98c | ||
|
|
59e0a0fd7c | ||
|
|
d15be53034 | ||
|
|
b6b2420ff7 | ||
|
|
4bd0e083d1 | ||
|
|
d9c7b39b88 | ||
|
|
583f7df9c8 | ||
|
|
8377aa72a8 | ||
|
|
c57926d799 | ||
|
|
380e3e64cb | ||
|
|
77a03e376c | ||
|
|
5dade2b608 | ||
|
|
877b3361c8 | ||
|
|
2fbfc2a8f1 | ||
|
|
e09b49288f | ||
|
|
6aa98a1e91 | ||
|
|
14b74069f8 | ||
|
|
c1cfcc78fe | ||
|
|
2c299a9f37 | ||
|
|
288697cda2 | ||
|
|
9fa771f9fe | ||
|
|
d1aa672388 | ||
|
|
b21f6fd619 | ||
|
|
39245238fd | ||
|
|
43d8090182 | ||
|
|
d13dae6b06 | ||
|
|
0998ef0c4e | ||
|
|
543bf51eff | ||
|
|
5513feb15b | ||
|
|
e03faaeb8f | ||
|
|
966dd03195 | ||
|
|
5041774fd3 | ||
|
|
c5453d476d | ||
|
|
54eae7830e | ||
|
|
4e74c7e3b1 | ||
|
|
fe52400b75 | ||
|
|
18feef0753 | ||
|
|
6ec021aafa | ||
|
|
954957bd8a | ||
|
|
14e92f53f1 | ||
|
|
d3183467e0 | ||
|
|
b62614bbd4 | ||
|
|
618155e6ab | ||
|
|
bd75d32c1b | ||
|
|
5acfef8edb | ||
|
|
7ce3ebc46d | ||
|
|
0353b0e04f | ||
|
|
ca81ab784a | ||
|
|
3262d819de | ||
|
|
a8cdbd44e8 | ||
|
|
04820ba09e | ||
|
|
52120b367a | ||
|
|
d6d96e8c89 | ||
|
|
2b34dd7373 | ||
|
|
7a87343c71 | ||
|
|
5f80121637 | ||
|
|
5920a96da7 | ||
|
|
0acaa839b9 | ||
|
|
f8b767ed32 | ||
|
|
d0b0d0ec72 | ||
|
|
e0463f5a74 | ||
|
|
1b30210120 | ||
|
|
a1e28bb669 | ||
|
|
dc4bd531a4 | ||
|
|
c5aedb14a9 | ||
|
|
ec28dee728 | ||
|
|
3e459ed434 | ||
|
|
d66be62516 | ||
|
|
98fd919647 | ||
|
|
0a9b2b6b15 | ||
|
|
bb469deb85 | ||
|
|
96402a2354 | ||
|
|
4fec7aa991 | ||
|
|
b5dfc54be3 | ||
|
|
848e3f6c5d | ||
|
|
1f5818cec8 | ||
|
|
06ec5424c7 | ||
|
|
b9551862b4 | ||
|
|
e0a75f68d5 | ||
|
|
a5f1fa3168 | ||
|
|
1375ed6aea | ||
|
|
f1a3b281d4 | ||
|
|
59da3dfe03 | ||
|
|
52f06cabd2 | ||
|
|
612efe56f1 | ||
|
|
07c0bc7b04 | ||
|
|
f5774bba6f | ||
|
|
714832bb49 | ||
|
|
3bc8d4d0eb | ||
|
|
8083a86581 | ||
|
|
b3528c314a | ||
|
|
65587be737 | ||
|
|
9c24e1ccfb | ||
|
|
0147186565 | ||
|
|
8924cb0960 | ||
|
|
869735e4b5 | ||
|
|
605381f83d | ||
|
|
71f70b4377 | ||
|
|
c6daf4d41c | ||
|
|
3f1dc22c0e | ||
|
|
02527552fd | ||
|
|
ac5b9f2382 | ||
|
|
a2931cd17d | ||
|
|
fc65496d1d | ||
|
|
be98466b0d | ||
|
|
0fd60d1e80 | ||
|
|
35857931ef | ||
|
|
a4c3058f84 | ||
|
|
6cb7afcbc0 | ||
|
|
32a0f5b04a | ||
|
|
3bc6190bdd | ||
|
|
4fb9571136 | ||
|
|
e70bb371d9 | ||
|
|
2099ac0615 | ||
|
|
5e28b1216e | ||
|
|
e858ee811b | ||
|
|
a498fc8e72 | ||
|
|
7063b6cb6d | ||
|
|
917d0ced4a | ||
|
|
969418ce73 | ||
|
|
7f3df847a2 | ||
|
|
e0424fe6cc | ||
|
|
b67397861c | ||
|
|
de15a2ace1 | ||
|
|
4369ee2ca7 | ||
|
|
a1163b70a6 | ||
|
|
2030aea175 | ||
|
|
969b66b315 | ||
|
|
4442da306a | ||
|
|
911fd6c654 | ||
|
|
9cee3dda1f | ||
|
|
cc0f026cec | ||
|
|
f1b5c333ff | ||
|
|
123fe58c26 | ||
|
|
fd1e163bf4 | ||
|
|
ab8b4d5c36 | ||
|
|
4a044351a9 | ||
|
|
e0a2268b0e | ||
|
|
3432771150 | ||
|
|
768615f2f1 | ||
|
|
9ae215291f | ||
|
|
1cf206d764 | ||
|
|
4c406ec9c0 | ||
|
|
500b896d50 | ||
|
|
18a239e808 | ||
|
|
5aa07bda7c | ||
|
|
379fd33484 | ||
|
|
ed6629002f | ||
|
|
bcb4ac7aae | ||
|
|
5395b173aa | ||
|
|
735f9c7a45 | ||
|
|
9cbca24f7a | ||
|
|
6f8d29ad80 | ||
|
|
eb947acebe | ||
|
|
01e84edd0a | ||
|
|
b7538b6ada | ||
|
|
232742a5e6 | ||
|
|
a33ddf373b | ||
|
|
b57e735199 | ||
|
|
38816d7e41 | ||
|
|
3a89bb0db2 | ||
|
|
865c38995d | ||
|
|
beae28f2db | ||
|
|
81b2c5ac68 | ||
|
|
35b5f60f99 | ||
|
|
13d0db5f9a | ||
|
|
96f23758b4 | ||
|
|
f7601756e9 | ||
|
|
54059b2f15 | ||
|
|
f00887ea42 | ||
|
|
08a5884c9f | ||
|
|
0f3b7dd6e9 | ||
|
|
bab73edcef | ||
|
|
42d26f01d6 | ||
|
|
8aadc2837c | ||
|
|
ea37882b95 | ||
|
|
599a0c66b2 | ||
|
|
57e0bfc5a9 | ||
|
|
9f424bb208 | ||
|
|
4e5b813aa8 | ||
|
|
6a2c32bd23 | ||
|
|
febcb90ece | ||
|
|
4b300f8274 | ||
|
|
8da8e02dc8 | ||
|
|
3e9b6a330d | ||
|
|
b54c4df74d | ||
|
|
f0a33c00bc | ||
|
|
8631b28130 | ||
|
|
9b3662e5ad | ||
|
|
3fb690a0f6 | ||
|
|
76d41fc250 | ||
|
|
15fa300e04 | ||
|
|
47c86828c6 | ||
|
|
6eaf35a142 | ||
|
|
ea8364eba5 | ||
|
|
fc4b1693f9 | ||
|
|
52f3d6722d | ||
|
|
126628546b | ||
|
|
2540ecc0ac | ||
|
|
b49972a623 | ||
|
|
b82a9a6594 | ||
|
|
be701677d6 | ||
|
|
1e8fc8b062 | ||
|
|
b6c765c9dc | ||
|
|
1d2797dc1f | ||
|
|
4b984f9819 | ||
|
|
e8882a5167 | ||
|
|
4f0490f26f | ||
|
|
7fe6bc52d0 | ||
|
|
b7408fa39e | ||
|
|
7ca5b5c928 | ||
|
|
6f5f613d9f | ||
|
|
52f2509280 | ||
|
|
c07f694a07 | ||
|
|
c49198fd71 | ||
|
|
864bad0ebb | ||
|
|
051858300e | ||
|
|
75d16e4852 | ||
|
|
a756c0b182 | ||
|
|
550cab41fb | ||
|
|
45bd81fe8c | ||
|
|
3f7ee613fc | ||
|
|
93f061f78a | ||
|
|
7517c76ae4 | ||
|
|
e26f771cbe | ||
|
|
bf5a1f662a | ||
|
|
d9753989bf | ||
|
|
391b98f72c | ||
|
|
d40a087ec2 | ||
|
|
138c14d43f | ||
|
|
dc6a594277 | ||
|
|
9f5c61b1fd | ||
|
|
3bca640521 | ||
|
|
73082a338b | ||
|
|
1593ebec1f | ||
|
|
2413968021 | ||
|
|
44b542814a | ||
|
|
2848f5dab4 | ||
|
|
f0f5a8f2b5 | ||
|
|
0563ce93a1 | ||
|
|
6a927be76c | ||
|
|
c8e3ea955d | ||
|
|
b33a9a71f6 | ||
|
|
dc80431438 | ||
|
|
8af3dc140e | ||
|
|
3c7d2d4cea | ||
|
|
de697a4267 | ||
|
|
ca35db76b8 | ||
|
|
b6eded1119 | ||
|
|
c8d8ae89d7 | ||
|
|
b2d949ce9a | ||
|
|
f12ebffa39 | ||
|
|
9b4d32b68c | ||
|
|
f4e708a02f | ||
|
|
b4e83b6537 | ||
|
|
c7637a0c53 | ||
|
|
9b1195c896 | ||
|
|
732c6134dd | ||
|
|
e98c871781 | ||
|
|
d616099de6 | ||
|
|
e3070eb170 | ||
|
|
319b02a757 | ||
|
|
64cdbb8e3b | ||
|
|
edeba4ca34 | ||
|
|
7e03f612b3 | ||
|
|
578f1b8ece | ||
|
|
e2a15d0ec9 | ||
|
|
3c74aec973 | ||
|
|
61e3ef2552 | ||
|
|
136e5fdf95 | ||
|
|
75fe9f9cc4 | ||
|
|
73dad85867 | ||
|
|
91895c52ca | ||
|
|
fc28af8518 | ||
|
|
3545626950 | ||
|
|
6a7079a594 | ||
|
|
42db2bc93d | ||
|
|
a2f5b55d10 | ||
|
|
8e7e9b7040 | ||
|
|
43eefb5929 | ||
|
|
dcdcba9f1f | ||
|
|
dec37b3dbe | ||
|
|
9431112f77 | ||
|
|
7bdd5c9e0d | ||
|
|
c963aa98e8 | ||
|
|
0f6e1c1c05 | ||
|
|
ce8d49a47d | ||
|
|
70dc305f12 | ||
|
|
df45374041 | ||
|
|
22a5a91bd5 | ||
|
|
d5788ba53a | ||
|
|
4d8a1c67c3 | ||
|
|
ea34e4bad8 | ||
|
|
258e2d197a | ||
|
|
e75e09dae8 | ||
|
|
4aee8ba27f | ||
|
|
90c6276fe2 | ||
|
|
1cae3baaea | ||
|
|
209882d5a9 | ||
|
|
2999096221 | ||
|
|
d076255c98 | ||
|
|
b2f2deffe6 | ||
|
|
b31aa4aacb | ||
|
|
85962a3eb1 | ||
|
|
a9696abdce | ||
|
|
e22448fb5c | ||
|
|
db0a102703 | ||
|
|
9034765790 | ||
|
|
3377b29957 | ||
|
|
5390594f40 | ||
|
|
e95753b184 | ||
|
|
6bd0d57c1e | ||
|
|
192b5a41f0 | ||
|
|
c76da2e77d | ||
|
|
8bf45536d3 | ||
|
|
e1fca2a1ab | ||
|
|
f5fe37ab09 | ||
|
|
08813f1650 | ||
|
|
8f7dd2f858 | ||
|
|
05050381d3 | ||
|
|
95d6d2644f | ||
|
|
dbd981939c | ||
|
|
2e45b41902 | ||
|
|
7776c56c6c | ||
|
|
4decceea76 | ||
|
|
26b97722c6 | ||
|
|
38fe928d06 | ||
|
|
c229223e01 | ||
|
|
ab59e41e1a | ||
|
|
392ee70fd5 | ||
|
|
7abad2ff99 | ||
|
|
d337cc6618 | ||
|
|
3a8bff228e | ||
|
|
5ba39fef9a | ||
|
|
c7160e3d25 | ||
|
|
6171d164d7 | ||
|
|
b1e0018b74 | ||
|
|
9b8aa8d09e | ||
|
|
3bed94f4ad | ||
|
|
a895a0d79e | ||
|
|
32522b61f2 | ||
|
|
1da3828a09 | ||
|
|
982ff8733f | ||
|
|
ebe374d572 | ||
|
|
dd45e7b4eb | ||
|
|
63eb0e3a38 | ||
|
|
f472071f6f | ||
|
|
7b695aa684 | ||
|
|
de4b5978c3 | ||
|
|
f23f8f7f5d | ||
|
|
f64acadd58 | ||
|
|
68ea1d0289 | ||
|
|
1574043a9e | ||
|
|
a2a5580e68 | ||
|
|
2696cf9b9e | ||
|
|
d9f962ee6f | ||
|
|
7468d8a000 | ||
|
|
270bb4e2d9 | ||
|
|
d3d9b06eb7 | ||
|
|
e7131bc8f8 | ||
|
|
1df1c15e48 | ||
|
|
986244eeea | ||
|
|
526bc36b40 | ||
|
|
9bdb7bef5d | ||
|
|
54e7d5d260 | ||
|
|
5265c190d4 | ||
|
|
4b7c052ed8 | ||
|
|
b62e7e7d8b | ||
|
|
fd290882fd | ||
|
|
09015f8924 | ||
|
|
b5f382f0ea | ||
|
|
ccd13dd643 | ||
|
|
d66715648a | ||
|
|
e07ff7d3fb | ||
|
|
0d60789f69 | ||
|
|
e769fbaffb | ||
|
|
5721f9f3a9 | ||
|
|
cca9f2392e | ||
|
|
928fa44154 | ||
|
|
a795f03c48 | ||
|
|
8f7b869c08 | ||
|
|
c5294f994e | ||
|
|
34f52e47eb | ||
|
|
f950d27b3d | ||
|
|
2a2da48d18 | ||
|
|
0b1cd826b0 | ||
|
|
11c1573b55 | ||
|
|
b3525b8657 | ||
|
|
8d5556764c | ||
|
|
f965209eee | ||
|
|
5c526995d8 | ||
|
|
50ee82e8bd | ||
|
|
8a3c909c74 | ||
|
|
3a8e1e0484 | ||
|
|
3b53cd56ef | ||
|
|
30e88a4f6b | ||
|
|
e5f47cd14b | ||
|
|
6368b09337 | ||
|
|
b97102da01 | ||
|
|
dbeae16dc1 | ||
|
|
edbcba6f11 | ||
|
|
ed876a9c52 | ||
|
|
67fe8fc4a7 | ||
|
|
55b530f0fd | ||
|
|
3a2c3ea8fa | ||
|
|
aae51a9b23 | ||
|
|
cd2b811555 | ||
|
|
15d1a6c937 | ||
|
|
9ab335626c | ||
|
|
451db7dd98 | ||
|
|
d6293730fc | ||
|
|
8e410f14b1 | ||
|
|
49000dadda | ||
|
|
9bde18492a | ||
|
|
b22fa9f7f4 | ||
|
|
a7b2d23e9b | ||
|
|
ba94f824b9 | ||
|
|
8aec437c23 | ||
|
|
98f656cfcb | ||
|
|
e558ddae3f | ||
|
|
2c665e5e4c | ||
|
|
2fe3d16bbe | ||
|
|
2844f276b9 | ||
|
|
414b5ea6f3 | ||
|
|
c42dcd5ac9 | ||
|
|
b9723aa85b | ||
|
|
03fb13e749 | ||
|
|
e04f04ef0f | ||
|
|
0f3c2c4b24 | ||
|
|
8a94a4938f | ||
|
|
b553ffd55d | ||
|
|
e45958b967 | ||
|
|
006ca4421a | ||
|
|
0bfdb45b41 | ||
|
|
8cbccb20ef | ||
|
|
f506f73d3f | ||
|
|
7734bbfe4d | ||
|
|
d35c5bcb2f | ||
|
|
c699d3a17a | ||
|
|
d16ac62a51 | ||
|
|
2fa5b8c0a2 | ||
|
|
15feeb7bfe | ||
|
|
02ecc93998 | ||
|
|
34291d5938 | ||
|
|
e726adc27b | ||
|
|
642f460dca | ||
|
|
1d7d9818fa | ||
|
|
c2dcc68803 | ||
|
|
d7c9f2ccba | ||
|
|
7dd61c05dc | ||
|
|
1c1155513a | ||
|
|
f84151e1fe | ||
|
|
5ec3d3e36d | ||
|
|
4b13f0e025 | ||
|
|
2869ccfe39 | ||
|
|
8ead4745f8 | ||
|
|
15b241cc3e | ||
|
|
2b07a69b93 | ||
|
|
5068ce0569 | ||
|
|
0b1b11ea53 | ||
|
|
ea9f169a6c | ||
|
|
cbd7f89066 | ||
|
|
146ff6f703 | ||
|
|
326ae5ff6b | ||
|
|
8c16f12d7a | ||
|
|
477228d4ff | ||
|
|
9baaa6fa56 | ||
|
|
49d11ed9f8 | ||
|
|
7834274bff | ||
|
|
d2565d0f61 | ||
|
|
4fde0bf62e | ||
|
|
a679081993 | ||
|
|
177292de13 | ||
|
|
efcb4abfd0 | ||
|
|
1501d1c570 | ||
|
|
59a56ac77f | ||
|
|
35f09f4f74 | ||
|
|
421dfce599 | ||
|
|
b240c15014 | ||
|
|
9de8df496a | ||
|
|
4f65066572 | ||
|
|
c38687725d | ||
|
|
099f236f45 | ||
|
|
996cd7e2ab | ||
|
|
77df590f65 | ||
|
|
ce064188c6 | ||
|
|
5d6f59a598 | ||
|
|
f4a4cf1402 | ||
|
|
032ef0ca34 | ||
|
|
5a7e8d8520 | ||
|
|
63146821d7 | ||
|
|
9ed8eac8c6 | ||
|
|
40bb1699a5 | ||
|
|
74827133b2 | ||
|
|
7c8706757d | ||
|
|
3dc066a143 | ||
|
|
43251ed8ce | ||
|
|
1a16822987 | ||
|
|
fdd4c1b9d1 | ||
|
|
1bd1050160 | ||
|
|
525a0478b0 | ||
|
|
ce28760c2a | ||
|
|
438cb1d7e0 | ||
|
|
87c8cf4892 | ||
|
|
f2fefb91d5 | ||
|
|
4d05d477f8 | ||
|
|
cc95c3e9c8 | ||
|
|
f0a7a6a1e2 | ||
|
|
4090843fef | ||
|
|
c6c18261a0 | ||
|
|
d9e83eb53a | ||
|
|
1cf404177d | ||
|
|
ed9ffec5ba | ||
|
|
582607c0f0 | ||
|
|
6cd5826d21 | ||
|
|
b7e953f50e | ||
|
|
9f630b6653 | ||
|
|
024af91df8 | ||
|
|
3017dbc8c1 | ||
|
|
5d33b34be7 | ||
|
|
f2104b0a5a | ||
|
|
84b004399e | ||
|
|
e8a01f0916 | ||
|
|
4f1d579ba1 | ||
|
|
c34d8286a3 | ||
|
|
4eb389f2d9 | ||
|
|
999ac60a87 | ||
|
|
30af694e30 | ||
|
|
fa76da1dab | ||
|
|
1500691cc2 | ||
|
|
fae5ea749a | ||
|
|
c2dac55edb | ||
|
|
4c5a74284b | ||
|
|
d0a3cfe765 | ||
|
|
580f1f1213 | ||
|
|
b87d164f2f | ||
|
|
f3c8f342ff | ||
|
|
63a1d8d0db | ||
|
|
93f5cc1e60 | ||
|
|
68a503049b | ||
|
|
fae43010c5 | ||
|
|
30351f5ade | ||
|
|
325a5b4af4 | ||
|
|
021c81cb62 | ||
|
|
7099e09a5c | ||
|
|
97b990c9bf | ||
|
|
db3d99f7c7 | ||
|
|
8fb4f37670 | ||
|
|
dfd4115f7a | ||
|
|
e58ad35fb0 | ||
|
|
31db5078c1 | ||
|
|
40c78d23e6 | ||
|
|
ec1d6d537e | ||
|
|
06bfd2b502 | ||
|
|
fd708b28b8 | ||
|
|
03e3281beb | ||
|
|
f247b9ce20 | ||
|
|
67909b55fc | ||
|
|
43ba690eda | ||
|
|
3425267368 | ||
|
|
0c3d9af333 | ||
|
|
67c2b309d1 | ||
|
|
df0fa9883c | ||
|
|
9821a39549 | ||
|
|
692e117c8a | ||
|
|
d96ca61f2b | ||
|
|
fc58b8cc0d | ||
|
|
c8503daf5a | ||
|
|
45e1f93e56 | ||
|
|
77807d439e | ||
|
|
e062c4e6b5 | ||
|
|
755edd2110 | ||
|
|
f51124397f | ||
|
|
e42111545b | ||
|
|
5112b4b963 | ||
|
|
99c5c7ab28 | ||
|
|
edb1473f13 | ||
|
|
f2171173fe | ||
|
|
7f30863df2 | ||
|
|
a6a2c02b6a | ||
|
|
d3ee99d677 | ||
|
|
4abfd4053a | ||
|
|
05e0ed5173 | ||
|
|
00b480527a | ||
|
|
e4231efd20 | ||
|
|
922d2165b3 | ||
|
|
9cee52c668 | ||
|
|
50e32c3d8a | ||
|
|
5e576bff63 | ||
|
|
e1338f5a7f | ||
|
|
672143d3a2 | ||
|
|
2d94a038a5 | ||
|
|
174d55cf3d | ||
|
|
f2889d24e6 | ||
|
|
db58b62235 | ||
|
|
f3b560b453 | ||
|
|
43e064749c | ||
|
|
4af339da63 | ||
|
|
4cd6e7ec91 | ||
|
|
197b5de9da | ||
|
|
cdfddc0fc5 | ||
|
|
f4d3ec908d | ||
|
|
918aaa64e2 | ||
|
|
2417396a93 | ||
|
|
0a051ff2cd | ||
|
|
d702133ded | ||
|
|
2aaee311f3 | ||
|
|
e8d431c2f6 | ||
|
|
0f4c012321 | ||
|
|
f13a8f725c | ||
|
|
e6690c818d | ||
|
|
5d32aa94d9 | ||
|
|
d413b6eba8 | ||
|
|
f0847e6f32 | ||
|
|
50d7554ccd | ||
|
|
b28d3015fe | ||
|
|
75d521d42a | ||
|
|
1ebaacfd5e | ||
|
|
52cd8652bb | ||
|
|
c6bdf7e891 | ||
|
|
11e19a06b7 | ||
|
|
880ca35681 | ||
|
|
650e98b2a9 | ||
|
|
f84946c135 | ||
|
|
508969a183 | ||
|
|
c39e3613a8 | ||
|
|
4eec9a2fba | ||
|
|
6d1b64065a | ||
|
|
5751437c93 | ||
|
|
9fa0490c44 | ||
|
|
6711ceb662 | ||
|
|
dc7677f5fa | ||
|
|
78fb6f55ed | ||
|
|
c5981cbc9a | ||
|
|
2f52b6c164 | ||
|
|
5d59462e49 | ||
|
|
1b09c64856 | ||
|
|
24b90b475b | ||
|
|
2312f03eac | ||
|
|
1d09e4b620 | ||
|
|
6ea1cf71b5 | ||
|
|
302fe94705 | ||
|
|
38ea8d1d86 | ||
|
|
67ed7e40be | ||
|
|
20f211b70a | ||
|
|
ebb07def7e | ||
|
|
55ab968edf | ||
|
|
3f6d6745de | ||
|
|
d1daae1b59 | ||
|
|
350ea2bf28 | ||
|
|
2f73d24ab1 | ||
|
|
1a05696a84 | ||
|
|
fc804751a2 | ||
|
|
938ef930d4 | ||
|
|
ee21d87d1b | ||
|
|
adfc748bfa | ||
|
|
015c7243b9 | ||
|
|
d376d2a590 | ||
|
|
2ce1e3678a | ||
|
|
365d690d7d | ||
|
|
5cb2fa5981 | ||
|
|
e854562803 | ||
|
|
fad9594b31 | ||
|
|
4fe3b5c6a6 | ||
|
|
1b82fb7349 | ||
|
|
092e7378cd | ||
|
|
e50e687191 | ||
|
|
dd5023e494 | ||
|
|
07cd0c870f | ||
|
|
2b1e672316 | ||
|
|
5adb41a0ed | ||
|
|
98fa996b3f | ||
|
|
07e890a79a | ||
|
|
3f9e39a9e3 | ||
|
|
b51bcba263 | ||
|
|
17d004d52b | ||
|
|
d94e61a7b6 |
18
.gitignore
vendored
18
.gitignore
vendored
@@ -14,6 +14,8 @@
|
||||
*.rej
|
||||
# OSX .DS_Store files
|
||||
.DS_Store
|
||||
# version scripts (repo master only)
|
||||
.version*
|
||||
Thumbs.db
|
||||
|
||||
|
||||
@@ -27,6 +29,8 @@ custom/
|
||||
apps/
|
||||
# default startpage
|
||||
home.html
|
||||
# page header plugin
|
||||
pageheader.html
|
||||
# Ignore site TOS
|
||||
doc/SiteTOS.md
|
||||
# themes except for redbasic
|
||||
@@ -56,11 +60,13 @@ nbproject/
|
||||
.idea/
|
||||
|
||||
|
||||
# composer files (at the moment composer is not officially supported and only used to add SabreDAV, we should add these)
|
||||
composer.*
|
||||
|
||||
# When we include composer we should exclude vendor/
|
||||
## composer
|
||||
# locally installed composer binary
|
||||
composer.phar
|
||||
# vendor/ is managed by composer, no need to include in our repository
|
||||
# requires new deployment and needs discussion first
|
||||
#vendor/
|
||||
# Exclude at least some vendor test files, examples, etc.
|
||||
vendor/sabre/*/tests/
|
||||
# Exclude at least some vendor test files, examples, etc. so far
|
||||
vendor/**/tests/
|
||||
vendor/**/Test/
|
||||
vendor/sabre/*/examples/
|
||||
|
||||
164
.homeinstall/README.md
Normal file
164
.homeinstall/README.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Hubzilla at Home next to your Router
|
||||
|
||||
Run hubzilla-setup.sh for an unattended installation of hubzilla.
|
||||
|
||||
The script is known to work with Debian 8.3 stable (Jessie)
|
||||
|
||||
+ Home-PC (Debian-8.3.0-amd64)
|
||||
+ DigitalOcean droplet (Debian 8.3 x64 / 512 MB Memory / 20 GB Disk / NYC3)
|
||||
|
||||
# Step-by-Step Overwiew
|
||||
|
||||
## Preconditions
|
||||
|
||||
Hardware
|
||||
|
||||
+ Internet connection and router at home
|
||||
+ Mini-pc connected to your router
|
||||
+ USB drive for backups
|
||||
|
||||
Software
|
||||
|
||||
+ Fresh installation of Debian on your mini-pc
|
||||
+ Router with open ports 80 and 443 for your Debian
|
||||
|
||||
## The basic steps (quick overview)
|
||||
|
||||
+ Register your own domain (for example at selfHOST) or a free subdomain (for example at freeDNS)
|
||||
+ Log on to your new debian (server)
|
||||
- apt-get install git
|
||||
- mkdir -p /var/www
|
||||
- cd /var/www
|
||||
- git clone https://github.com/redmatrix/hubzilla.git html
|
||||
- cp .homeinstall/hubzilla-config.txt.template .homeinstall/hubzilla-config.txt
|
||||
- nano .homeinstall/hubzilla-config.txt
|
||||
- Enter your values there: db pass, domain, values for dyn DNS
|
||||
- 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.
|
||||
|
||||
# Step-by-Step in Detail
|
||||
|
||||
## Preparations Hardware
|
||||
|
||||
### Mini-PC
|
||||
|
||||
### Recommended: USB Drive for Backups
|
||||
|
||||
The installation will create a daily backup.
|
||||
|
||||
If the backup process does not find an external device than the backup goes to
|
||||
the internal disk.
|
||||
|
||||
The USB drive must be compatible with an encrpyted filesystem LUKS + ext4.
|
||||
|
||||
## Preparations Software
|
||||
|
||||
### Install Debian Linux on the Mini-PC
|
||||
|
||||
Download the stable Debian at https://www.debian.org/
|
||||
|
||||
Create bootable USB drive with Debian on it. You could use the programm
|
||||
unetbootin, https://en.wikipedia.org/wiki/UNetbootin
|
||||
|
||||
Switch of your mini pc, plug in your USB drive and start the mini pc from the
|
||||
stick. Install Debian. Follow the instructions of the installation.
|
||||
|
||||
### Configure your Router
|
||||
|
||||
Open the ports 80 and 443 on your router for your Debian
|
||||
|
||||
## Preparations Dynamic IP Address
|
||||
|
||||
Your Hubzilla must be reachable by a domain that you can type in your browser
|
||||
|
||||
cooldomain.org
|
||||
|
||||
You can use subdomains as well
|
||||
|
||||
my.cooldomain.org
|
||||
|
||||
There are two way to get a domain
|
||||
|
||||
- buy a domain (recommended) or
|
||||
- register a free subdomain
|
||||
|
||||
### Method 1: Get yourself an own Domain (recommended)
|
||||
|
||||
...for example at selfHOST.de
|
||||
|
||||
### Method 2 Register a (free) Subdomain
|
||||
|
||||
Register a free subdomain for example at
|
||||
|
||||
- freeDNS
|
||||
- selfHOST
|
||||
|
||||
WATCH THIS: A free subdomain is not the prefered way to get a domain name. Why?
|
||||
|
||||
Let's encrpyt issues a limited number of certificates each
|
||||
day. Possibly other users of this domain will try to issue a certificate
|
||||
at the same day as you do. So make sure you choose a domain with as less subdomains as
|
||||
possible.
|
||||
|
||||
## 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://github.com/redmatrix/hubzilla html
|
||||
|
||||
Change to the install script
|
||||
|
||||
cd html/.homeinstall/
|
||||
|
||||
Copy the template file
|
||||
|
||||
cp hubzilla-config.txt.template hubzilla-config.txt
|
||||
|
||||
Change the file "hubzilla-config.txt". Read the instructions there and enter your values.
|
||||
|
||||
nano hubzilla-config.txt
|
||||
|
||||
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 shoulg 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.
|
||||
|
||||
177
.homeinstall/hubzilla-config.txt.template
Normal file
177
.homeinstall/hubzilla-config.txt.template
Normal file
@@ -0,0 +1,177 @@
|
||||
###############################################
|
||||
### 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
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# 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
|
||||
#
|
||||
backup_device_name=
|
||||
backup_device_pass=
|
||||
|
||||
|
||||
###############################################
|
||||
### OPTIONAL - Owncloud - deprecated ##########
|
||||
#
|
||||
# To install owncloud: owncloud=y
|
||||
# Leave empty if you don't want to install owncloud
|
||||
#
|
||||
#owncloud=
|
||||
|
||||
|
||||
|
||||
###############################################
|
||||
### 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
|
||||
|
||||
# TODO Prepare hubzilla for programmers
|
||||
# - install eclipse and plugins
|
||||
# - install xdebug to debug the php with eclipse
|
||||
# - weaken permissions on /var/www/html
|
||||
# - manual steps after this script
|
||||
# * in eclipse: install plugins for php git hub
|
||||
# * in eclipse: configure firefox (chrome,...) as browser to run with the php debuger
|
||||
# * in eclipse: switch php debugger from zend to xdebug
|
||||
# * in eclipse: add local hubzilla github repository
|
||||
#
|
||||
# Which user will use eclipse?
|
||||
# Leave this empty if you do not want to prepare hubzilla for debugging
|
||||
#
|
||||
#developer_name=
|
||||
|
||||
909
.homeinstall/hubzilla-setup.sh
Executable file
909
.homeinstall/hubzilla-setup.sh
Executable file
@@ -0,0 +1,909 @@
|
||||
#!/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 "Ubuntu is not supported"
|
||||
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
|
||||
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 [ ! -d /media/hubzilla_backup ]
|
||||
then
|
||||
mkdir /media/hubzilla_backup
|
||||
fi
|
||||
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_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-php5
|
||||
# to_to: php5-suhosin
|
||||
print_info "installing php..."
|
||||
nocheck_install "libapache2-mod-php5 php5 php-pear php5-xcache php5-curl php5-mcrypt php5-gd"
|
||||
php5enmod mcrypt
|
||||
}
|
||||
|
||||
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-5.5 mysql-server/root_password password xyzzy | debconf-set-selections
|
||||
# echo mysql-server-5.5 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-5.5 mysql-server/root_password password $mysqlpass | debconf-set-selections
|
||||
echo mysql-server-5.5 mysql-server/root_password_again password $mysqlpass | debconf-set-selections
|
||||
nocheck_install "php5-mysql mysql-server mysql-client"
|
||||
php5enmod mcrypt
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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_git {
|
||||
print_info "installing git..."
|
||||
nocheck_install "git"
|
||||
}
|
||||
|
||||
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
|
||||
# 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..."
|
||||
# rm -R /var/www/html/ # for "stand alone" usage
|
||||
cd /var/www/
|
||||
# git clone https://github.com/redmatrix/hubzilla html # for "stand alone" usage
|
||||
cd html/
|
||||
git clone https://github.com/redmatrix/hubzilla-addons addon
|
||||
mkdir -p "store/[data]/smarty3"
|
||||
chmod -R 777 store
|
||||
touch .htconfig.php
|
||||
chmod ou+w .htconfig.php
|
||||
install_hubzilla_plugins
|
||||
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 install_hubzilla_plugins {
|
||||
print_info "installing hubzilla plugins..."
|
||||
cd /var/www/html
|
||||
plugin_install=.homeinstall/plugin_install.txt
|
||||
theme_install=.homeinstall/theme_install.txt
|
||||
# overwrite script to update the plugin and themes
|
||||
rm -f $plugins_update
|
||||
echo "cd /var/www/html" >> $plugins_update
|
||||
###################
|
||||
# write plugin file
|
||||
if [ ! -f "$plugin_install" ]
|
||||
then
|
||||
echo "# To install a plugin" >> $plugin_install
|
||||
echo "# 1. add the plugin in a new line and run" >> $plugin_install
|
||||
echo "# 2. run" >> $plugin_install
|
||||
echo "# cd /var/www/html/.homeinstall" >> $plugin_install
|
||||
echo "# ./hubzilla-setup.sh" >> $plugin_install
|
||||
echo "https://gitlab.com/zot/ownmapp.git ownMapp" >> $plugin_install
|
||||
echo "https://gitlab.com/zot/hubzilla-chess.git chess" >> $plugin_install
|
||||
fi
|
||||
# install plugins
|
||||
while read -r line; do
|
||||
[[ "$line" =~ ^#.*$ ]] && continue
|
||||
p_url=$(echo $line | awk -F' ' '{print $1}')
|
||||
p_name=$(echo $line | awk -F' ' '{print $2}')
|
||||
# basic check of format
|
||||
if [ ${#p_url} -ge 1 ] && [ ${#p_name} -ge 1 ]
|
||||
then
|
||||
# install addon
|
||||
util/add_addon_repo $line
|
||||
util/update_addon_repo $p_name # not sure if this line is neccessary
|
||||
echo "util/update_addon_repo $p_name" >> $plugins_update
|
||||
else
|
||||
print_info "skipping installation of a plugin from file $plugin_install - something wrong with format in line: $line"
|
||||
fi
|
||||
done < "$plugin_install"
|
||||
###################
|
||||
# write theme file
|
||||
if [ ! -f "$theme_install" ]
|
||||
then
|
||||
echo "# To install a theme" >> $theme_install
|
||||
echo "# 1. add the theme in a new line and run" >> $theme_install
|
||||
echo "# 2. run" >> $theme_install
|
||||
echo "# cd /var/www/html/.homeinstall" >> $theme_install
|
||||
echo "# ./hubzilla-setup.sh" >> $theme_install
|
||||
echo "https://github.com/DeadSuperHero/hubzilla-themes.git DeadSuperHeroThemes" >> $theme_install
|
||||
|
||||
fi
|
||||
# install plugins
|
||||
while read -r line; do
|
||||
[[ "$line" =~ ^#.*$ ]] && continue
|
||||
p_url=$(echo $line | awk -F' ' '{print $1}')
|
||||
p_name=$(echo $line | awk -F' ' '{print $2}')
|
||||
# basic check of format
|
||||
if [ ${#p_url} -ge 1 ] && [ ${#p_name} -ge 1 ]
|
||||
then
|
||||
# install addon
|
||||
util/add_theme_repo $line
|
||||
util/update_theme_repo $p_name # not sure if this line is neccessary
|
||||
echo "util/update_theme_repo $p_name" >> $plugins_update
|
||||
else
|
||||
print_info "skipping installation of a theme from file $theme_install - something wrong with format in line: $line"
|
||||
fi
|
||||
done < "$theme_install"
|
||||
print_info "installed hubzilla plugins and themes"
|
||||
}
|
||||
|
||||
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 "/hourly/s/retain/#retain/" $snapshotconfig
|
||||
sed -i "/monthly/s/#retain/retain/" $snapshotconfig
|
||||
sed -i "s/^cmd_cp/#cmd_cp/" $snapshotconfig
|
||||
sed -i "s/^backup/#backup/" $snapshotconfig
|
||||
if [ -z "`grep 'letsencrypt' $snapshotconfig`" ]
|
||||
then
|
||||
echo "backup /var/lib/mysql/ localhost/" >> $snapshotconfig
|
||||
echo "backup /var/www/html/ localhost/" >> $snapshotconfig
|
||||
echo "backup /var/www/letsencrypt/ localhost/" >> $snapshotconfig
|
||||
fi
|
||||
# external disk
|
||||
if [ -n "$backup_device_name" ] && [ -n "$backup_device_pass" ]
|
||||
then
|
||||
cp -f /etc/rsnapshot.conf $snapshotconfig_external_device
|
||||
sed -i "s#snapshot_root.*#snapshot_root $backup_mount_point#" $snapshotconfig_external_device
|
||||
sed -i "/hourly/s/retain/#retain/" $snapshotconfig_external_device
|
||||
sed -i "/monthly/s/#retain/retain/" $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 include/poller.php >> /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 daily" >> /var/www/$hubzilladaily
|
||||
echo " rsnapshot -c $snapshotconfig_external_device weekly" >> /var/www/$hubzilladaily
|
||||
echo " rsnapshot -c $snapshotconfig_external_device monthly" >> /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. Using internal disk for backup...\"" >> /var/www/$hubzilladaily
|
||||
echo " rsnapshot -c $snapshotconfig daily" >> /var/www/$hubzilladaily
|
||||
echo " rsnapshot -c $snapshotconfig weekly" >> /var/www/$hubzilladaily
|
||||
echo " rsnapshot -c $snapshotconfig monthly" >> /var/www/$hubzilladaily
|
||||
echo "fi" >> /var/www/$hubzilladaily
|
||||
echo "#" >> /var/www/$hubzilladaily
|
||||
echo "echo \"\$(date) - db size...\"" >> /var/www/$hubzilladaily
|
||||
echo "du -h /var/cache/rsnapshot/ | 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 "git -C /var/www/html/ pull" >> /var/www/$hubzilladaily
|
||||
echo "echo \"\$(date) - updating hubhilla addons...\"" >> /var/www/$hubzilladaily
|
||||
echo "git -C /var/www/html/addon/ pull" >> /var/www/$hubzilladaily
|
||||
echo "bash /var/www/html/$plugins_update" >> /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 # 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_php
|
||||
install_mysql
|
||||
install_phpmyadmin
|
||||
create_hubzilla_db
|
||||
run_freedns
|
||||
install_run_selfhost
|
||||
ping_domain
|
||||
configure_cron_freedns
|
||||
configure_cron_selfhost
|
||||
install_git
|
||||
install_letsencrypt
|
||||
configure_apache_for_https
|
||||
check_https
|
||||
install_hubzilla
|
||||
rewrite_to_https
|
||||
install_rsnapshot
|
||||
configure_cron_daily
|
||||
install_cryptosetup
|
||||
write_uninstall_script
|
||||
|
||||
#set +x # stop debugging from here
|
||||
|
||||
@@ -24,7 +24,11 @@ AddType audio/ogg .oga
|
||||
# Also place auth information into REMOTE_USER for sites running
|
||||
# in CGI mode.
|
||||
|
||||
RewriteCond %{REQUEST_URI} ^/\.well\-known/.*
|
||||
RewriteRule ^(.*)$ index.php?q=$1 [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?q=$1 [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
|
||||
</IfModule>
|
||||
|
||||
|
||||
94
.openshift/README.md
Normal file
94
.openshift/README.md
Normal file
@@ -0,0 +1,94 @@
|
||||
#Hubzilla on OpenShift
|
||||
You will notice a new .openshift folder when you fetch from upstream, i.e. from https://github.com/redmatrix/hubzilla.git , which contains a deploy script to set up Hubzilla on OpenShift.
|
||||
|
||||
Create an account on OpenShift, then use the registration e-mail and password to create your first Hubzilla instance. Install git and RedHat's command line tools - rhc - if you have not already done so.
|
||||
|
||||
```
|
||||
rhc app-create your_app_name php-5.4 mysql-5.5 cron phpmyadmin --namespace your_domain --from-code https://github.com/redmatrix/hubzilla.git -l your@email.address -p your_account_password
|
||||
```
|
||||
|
||||
Make a note of the database username and password OpenShift creates for your instance, and use these at https://your_app_name-your_domain.rhcloud.com/ to complete the setup.
|
||||
|
||||
NOTE: PostgreSQL is NOT supported by the deploy script yet.
|
||||
|
||||
Update
|
||||
To update, consider your own workflow first. I have forked Hubzilla code into my GitHub account to be able to try things out, this remote repo is called origin. Here is how I fetch new code from upstream, merge into my local repo, then push the updated code both into origin and the remote repo called openshift.
|
||||
|
||||
```
|
||||
git fetch upstream;git checkout master;git merge upstream/master;git push origin;git push openshift HEAD
|
||||
```
|
||||
|
||||
##Administration
|
||||
Symptoms of need for MySQL database administration are:
|
||||
- you can visit your domain and see the Hubzilla frontpage, but trying to login throws you back to login. This can mean your session table is marked as crashed.
|
||||
- you can login, but your channel posts are not visible. This can mean your item table is marked as crashed.
|
||||
- you can login and you can see your channel posts, but apparently nobody is getting your posts, comments, likes and so on. This can mean your outq table is marked as crashed.
|
||||
|
||||
You can check your OpenShift logs by doing
|
||||
|
||||
```
|
||||
rhc tail -a your_app_name -n your_domain -l your@email.address -p your_account_password
|
||||
```
|
||||
|
||||
and you might be able to confirm the above suspicions about crashed tables, or other problems you need to fix.
|
||||
|
||||
###How to fix crashed tables in MySQL
|
||||
Using MySQL and the MyISAM database engine can result in table indexes coming out of sync, and you have at least two options for fixing tables marked as crashed.
|
||||
- Use the database username and password OpenShift creates for your instance at https://your_app_name-your_domain.rhcloud.com/phpmyadmin/ to login via the web into your phpMyAdmin web interface, click your database in the left column, in the right column scroll down to the bottom of the list of tables and click the checkbox for marking all tables, then select Check tables from the drop down menu. This will check the tables for problems, and you can then checkmark only those tables with problems, and select Repair table from the same drop down menu at the bottom.
|
||||
- You can login to your instance with SSH - see OpenShift for details - then
|
||||
|
||||
```
|
||||
cd mysql/data/your_database
|
||||
myisamchk -r *.MYI
|
||||
```
|
||||
|
||||
or if you get
|
||||
|
||||
```
|
||||
Can't create new tempfile
|
||||
```
|
||||
|
||||
check your OpenShift's gear quota with
|
||||
|
||||
```
|
||||
quota -gus
|
||||
```
|
||||
|
||||
and if you are short on space, then locally (not SSH) do
|
||||
|
||||
```
|
||||
rhc app-tidy your_app_name -l your_login -p your_password
|
||||
```
|
||||
|
||||
to have rhc delete temporary files and OpenShift logs to free space first, then check the size of your local repo dir and execute
|
||||
|
||||
```
|
||||
git gc
|
||||
```
|
||||
|
||||
against it and check the size again, and then to minimize your remote repo connect via SSH to your application gear and execute the same command against it by changing to the remote repo directory - your repo should be in
|
||||
|
||||
```
|
||||
~/git/your_app_name.git
|
||||
```
|
||||
|
||||
(if not, do find -size +1M to find it), then do
|
||||
|
||||
```
|
||||
cd ~/mysql/data/yourdatabase
|
||||
myisamchk -r -v -f*.MYI
|
||||
```
|
||||
|
||||
and hopefully your database tables are now okay.
|
||||
|
||||
##NOTES
|
||||
Note 1: definitely DO turn off feeds and discovery by default if you are on the Free or Bronze plan on OpenShift with a single 1Gb gear by visiting https://your-app-name.rhcloud.com/admin/site when logged in as administrator of your Hubzilla site.
|
||||
Note 2: DO add the above defaults into the deploy script.
|
||||
Note 3: DO add git gc to the deploy script to clean up git.
|
||||
Note 4: MAYBE DO add myisamchk - only checking? to the end of the deploy script.
|
||||
|
||||
The OpenShift `php` cartridge documentation can be found at:
|
||||
http://openshift.github.io/documentation/oo_cartridge_guide.html#php
|
||||
|
||||
For information about .openshift directory, consult the documentation:
|
||||
http://openshift.github.io/documentation/oo_user_guide.html#the-openshift-directory
|
||||
3
.openshift/action_hooks/README.md
Normal file
3
.openshift/action_hooks/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
For information about action hooks, consult the documentation:
|
||||
|
||||
http://openshift.github.io/documentation/oo_user_guide.html#action-hooks
|
||||
218
.openshift/action_hooks/deploy
Executable file
218
.openshift/action_hooks/deploy
Executable file
@@ -0,0 +1,218 @@
|
||||
#!/bin/bash
|
||||
# This deploy hook gets executed after dependencies are resolved and the
|
||||
# build hook has been run but before the application has been started back
|
||||
# up again. This script gets executed directly, so it could be python, php,
|
||||
# ruby, etc.
|
||||
|
||||
# Bash help: http://www.panix.com/~elflord/unix/bash-tute.html
|
||||
|
||||
# For information about action hooks supported by OpenShift, consult the documentation:
|
||||
# http://openshift.github.io/documentation/oo_user_guide.html#the-openshift-directory
|
||||
|
||||
####
|
||||
|
||||
# Hubzilla specific deploy script
|
||||
|
||||
# Place this file in /.openshift/action_hooks/ (The .openshift folder will be in the root of your repo)
|
||||
# The file name should be "deploy" such that you have:
|
||||
# .openshift/action_hooks/deploy
|
||||
|
||||
# Conventions: Vars in curley braces have the slash after implied so no need to add it.
|
||||
# e.g. ${OPENSHIFT_REPO_DIR}php/foobar = /repo/php/foobar
|
||||
# See all OpenShift vars here:
|
||||
# https://www.openshift.com/developers/openshift-environment-variables
|
||||
|
||||
# HME - NOTE - leftover from original openshift-drupal-deploy
|
||||
# In config.php you can leverage the enviroment variables like this:
|
||||
# // Define env vars.
|
||||
# if (array_key_exists('OPENSHIFT_APP_NAME', $_SERVER)) {
|
||||
# $src = $_SERVER;
|
||||
# } else {
|
||||
# $src = $_ENV;
|
||||
# }
|
||||
#
|
||||
# $conf["file_private_path"] = $src['OPENSHIFT_DATA_DIR'] . "private";
|
||||
# $conf["file_temporary_path"] = $src['OPENSHIFT_DATA_DIR'] . "tmp";
|
||||
|
||||
|
||||
####
|
||||
|
||||
# Start Deploy
|
||||
|
||||
echo "Starting Deploy..."
|
||||
|
||||
# Let's create the Hubzilla files directory in the Openshift data folder ($OPENSHIFT_DATA_DIR).
|
||||
|
||||
echo "Check for the files directory called store, if not created - create it"
|
||||
|
||||
if [ ! -d ${OPENSHIFT_DATA_DIR}store ]; then
|
||||
mkdir -p ${OPENSHIFT_DATA_DIR}"store/[data]/smarty3"
|
||||
echo "Done creating files directory"
|
||||
|
||||
else
|
||||
|
||||
echo "The files directory called store already exists"
|
||||
|
||||
fi
|
||||
|
||||
####
|
||||
|
||||
# Set permissions on the files directory.
|
||||
|
||||
echo "Now chmod 777 -R files"
|
||||
|
||||
chmod -R 777 ${OPENSHIFT_DATA_DIR}store
|
||||
|
||||
echo "chmod done, permissions set to 777"
|
||||
|
||||
####
|
||||
|
||||
# Symlink our files folder to the repo.
|
||||
|
||||
# Note the "php" directory below seems to be the best way to serve OpenShift files.
|
||||
# This is good as that allows us for directories one level above such as tmp and private
|
||||
|
||||
echo "Create sym links for writeable directories"
|
||||
|
||||
ln -sf ${OPENSHIFT_DATA_DIR}store ${OPENSHIFT_REPO_DIR}store
|
||||
|
||||
echo "Files sym links created"
|
||||
|
||||
####
|
||||
|
||||
# Copy .htconfig.php from the repo, rename it and place it in the data directory.
|
||||
# if it's there already, skip it.
|
||||
|
||||
if [ ! -f ${OPENSHIFT_DATA_DIR}.htconfig.php ];
|
||||
|
||||
then
|
||||
|
||||
cp ${OPENSHIFT_REPO_DIR}.htconfig.php ${OPENSHIFT_DATA_DIR}.htconfig.php
|
||||
|
||||
echo ".htconfig.php copied."
|
||||
|
||||
else
|
||||
|
||||
echo "Looks like the .htconfig.php file is already there, we won't overwrite it."
|
||||
|
||||
fi
|
||||
|
||||
####
|
||||
|
||||
# symlink the .htconfig.php file.
|
||||
|
||||
echo "Create sym link for .htconfig.php"
|
||||
|
||||
ln -sf ${OPENSHIFT_DATA_DIR}.htconfig.php ${OPENSHIFT_REPO_DIR}.htconfig.php
|
||||
|
||||
echo ".htconfig.php symlink created"
|
||||
|
||||
####
|
||||
# Copy .htaccess from the repo, rename it and place it in the data directory.
|
||||
# if it's there already, skip it.
|
||||
|
||||
if [ ! -f ${OPENSHIFT_DATA_DIR}.htaccess ];
|
||||
|
||||
then
|
||||
|
||||
cp ${OPENSHIFT_REPO_DIR}.htaccess ${OPENSHIFT_DATA_DIR}.htaccess
|
||||
|
||||
echo ".htaccess copied."
|
||||
|
||||
else
|
||||
|
||||
echo "Looks like the .htaccess file is already there, we won't overwrite it."
|
||||
|
||||
fi
|
||||
|
||||
####
|
||||
|
||||
# symlink the .htaccess file.
|
||||
|
||||
echo "Create sym link for .htaccess"
|
||||
|
||||
ln -sf ${OPENSHIFT_DATA_DIR}.htaccess ${OPENSHIFT_REPO_DIR}.htaccess
|
||||
|
||||
echo ".htaccess symlink created"
|
||||
|
||||
####
|
||||
|
||||
echo "Check for the poller at .openshift/cron/minutely/poller , if not created - create it"
|
||||
|
||||
if [ ! -f ${OPENSHIFT_REPO_DIR}.openshift/cron/minutely/poller ]; then
|
||||
printf '%s\n' '#!/bin/bash' 'if [ ! -f $OPENSHIFT_DATA_DIR/last_run ]; then' ' touch $OPENSHIFT_DATA_DIR/last_run' 'fi' 'if [[ $(find $OPENSHIFT_DATA_DIR/last_run -mmin +9) ]]; then #run every 10 mins' ' rm -f $OPENSHIFT_DATA_DIR/last_run' ' touch $OPENSHIFT_DATA_DIR/last_run' ' # The command(s) that you want to run every 10 minutes' 'cd /var/lib/openshift/${OPENSHIFT_APP_UUID}/app-root/repo; /opt/rh/php54/root/usr/bin/php include/poller.php' 'fi' >${OPENSHIFT_REPO_DIR}.openshift/cron/minutely/poller
|
||||
echo "Done creating file .openshift/cron/minutely/poller"
|
||||
|
||||
else
|
||||
|
||||
echo "The poller already exists"
|
||||
|
||||
fi
|
||||
|
||||
####
|
||||
|
||||
# Set permissions on the poller script to make it executable.
|
||||
|
||||
echo "Now chmod 777 -R poller"
|
||||
|
||||
chmod -R 777 ${OPENSHIFT_REPO_DIR}.openshift/cron/minutely/poller
|
||||
|
||||
echo "chmod done, permissions set to 777 on poller script."
|
||||
|
||||
####
|
||||
|
||||
### echo "Check for the hot deploy marker at .openshift/markers/hot_deploy , if not created - create it"
|
||||
|
||||
### if [ ! -f ${OPENSHIFT_REPO_DIR}.openshift/markers/hot_deploy ]; then
|
||||
|
||||
### touch ${OPENSHIFT_REPO_DIR}.openshift/markers/hot_deploy
|
||||
|
||||
### echo "Done creating file .openshift/markers/hot_deploy"
|
||||
|
||||
### else
|
||||
|
||||
### echo "The hot deploy marker already exists"
|
||||
|
||||
### fi
|
||||
|
||||
####
|
||||
|
||||
# Hubzilla configuration - changes to default settings
|
||||
# to make Hubzilla on OpenShift a more pleasant experience
|
||||
echo "Changing default configuration to conserve space and autocreate a social private channel upon account registration"
|
||||
cd ${OPENSHIFT_REPO_DIR}
|
||||
util/config system auto_channel_create
|
||||
util/config system default_permissions_role social_private
|
||||
util/config system workflow_channel_next channel
|
||||
util/config system expire_delivery_reports 3
|
||||
util/config system feed_contacts 0
|
||||
util/config system diaspora_enabled 0
|
||||
util/config system disable_discover_tab 1
|
||||
util/config directory safemode 0
|
||||
util/config directory globaldir 1
|
||||
util/config directory pubforums 0
|
||||
|
||||
# Hubzill addons
|
||||
echo "Try to add or update Hubzilla addons"
|
||||
cd ${OPENSHIFT_REPO_DIR}
|
||||
util/add_addon_repo https://github.com/redmatrix/hubzilla-addons.git HubzillaAddons
|
||||
|
||||
# Hubzilla themes - unofficial repo
|
||||
echo "Try to add or update Hubzilla themes - unofficial repo"
|
||||
cd ${OPENSHIFT_REPO_DIR}
|
||||
util/add_theme_repo https://github.com/DeadSuperHero/hubzilla-themes.git DeadSuperHeroThemes insecure
|
||||
|
||||
# Hubzilla ownMapp - unofficial repo
|
||||
echo "Try to add or update Hubzilla ownMapp - unofficial repo"
|
||||
cd ${OPENSHIFT_REPO_DIR}
|
||||
util/add_addon_repo https://gitlab.com/zot/ownmapp.git ownMapp insecure
|
||||
|
||||
# Hubzilla Chess - unofficial repo
|
||||
echo "Try to add or update Hubzilla chess - unofficial repo"
|
||||
cd ${OPENSHIFT_REPO_DIR}
|
||||
util/add_addon_repo https://gitlab.com/zot/hubzilla-chess.git Chess insecure
|
||||
|
||||
# Hubzilla Hubsites - unofficial repo
|
||||
echo "Try to add or update Hubzilla Hubsites - unofficial repo"
|
||||
cd ${OPENSHIFT_REPO_DIR}
|
||||
util/add_addon_repo https://gitlab.com/zot/hubsites.git Hubsites insecure
|
||||
27
.openshift/cron/README.cron
Normal file
27
.openshift/cron/README.cron
Normal file
@@ -0,0 +1,27 @@
|
||||
Run scripts or jobs on a periodic basis
|
||||
=======================================
|
||||
Any scripts or jobs added to the minutely, hourly, daily, weekly or monthly
|
||||
directories will be run on a scheduled basis (frequency is as indicated by the
|
||||
name of the directory) using run-parts.
|
||||
|
||||
run-parts ignores any files that are hidden or dotfiles (.*) or backup
|
||||
files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved}
|
||||
|
||||
The presence of two specially named files jobs.deny and jobs.allow controls
|
||||
how run-parts executes your scripts/jobs.
|
||||
jobs.deny ===> Prevents specific scripts or jobs from being executed.
|
||||
jobs.allow ===> Only execute the named scripts or jobs (all other/non-named
|
||||
scripts that exist in this directory are ignored).
|
||||
|
||||
The principles of jobs.deny and jobs.allow are the same as those of cron.deny
|
||||
and cron.allow and are described in detail at:
|
||||
http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/ch-Automating_System_Tasks.html#s2-autotasks-cron-access
|
||||
|
||||
See: man crontab or above link for more details and see the the weekly/
|
||||
directory for an example.
|
||||
|
||||
PLEASE NOTE: The Cron cartridge must be installed in order to run the configured jobs.
|
||||
|
||||
For more information about cron, consult the documentation:
|
||||
http://openshift.github.io/documentation/oo_cartridge_guide.html#cron
|
||||
http://openshift.github.io/documentation/oo_user_guide.html#cron
|
||||
0
.openshift/cron/daily/.gitignore
vendored
Normal file
0
.openshift/cron/daily/.gitignore
vendored
Normal file
0
.openshift/cron/hourly/.gitignore
vendored
Normal file
0
.openshift/cron/hourly/.gitignore
vendored
Normal file
0
.openshift/cron/minutely/.gitignore
vendored
Normal file
0
.openshift/cron/minutely/.gitignore
vendored
Normal file
0
.openshift/cron/monthly/.gitignore
vendored
Normal file
0
.openshift/cron/monthly/.gitignore
vendored
Normal file
16
.openshift/cron/weekly/README
Normal file
16
.openshift/cron/weekly/README
Normal file
@@ -0,0 +1,16 @@
|
||||
Run scripts or jobs on a weekly basis
|
||||
=====================================
|
||||
Any scripts or jobs added to this directory will be run on a scheduled basis
|
||||
(weekly) using run-parts.
|
||||
|
||||
run-parts ignores any files that are hidden or dotfiles (.*) or backup
|
||||
files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved} and handles
|
||||
the files named jobs.deny and jobs.allow specially.
|
||||
|
||||
In this specific example, the chronograph script is the only script or job file
|
||||
executed on a weekly basis (due to white-listing it in jobs.allow). And the
|
||||
README and chrono.dat file are ignored either as a result of being black-listed
|
||||
in jobs.deny or because they are NOT white-listed in the jobs.allow file.
|
||||
|
||||
For more details, please see ../README.cron file.
|
||||
|
||||
1
.openshift/cron/weekly/chrono.dat
Normal file
1
.openshift/cron/weekly/chrono.dat
Normal file
@@ -0,0 +1 @@
|
||||
Time And Relative D...n In Execution (Open)Shift!
|
||||
3
.openshift/cron/weekly/chronograph
Executable file
3
.openshift/cron/weekly/chronograph
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "`date`: `cat $(dirname \"$0\")/chrono.dat`"
|
||||
12
.openshift/cron/weekly/jobs.allow
Normal file
12
.openshift/cron/weekly/jobs.allow
Normal file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# Script or job files listed in here (one entry per line) will be
|
||||
# executed on a weekly-basis.
|
||||
#
|
||||
# Example: The chronograph script will be executed weekly but the README
|
||||
# and chrono.dat files in this directory will be ignored.
|
||||
#
|
||||
# The README file is actually ignored due to the entry in the
|
||||
# jobs.deny which is checked before jobs.allow (this file).
|
||||
#
|
||||
chronograph
|
||||
|
||||
7
.openshift/cron/weekly/jobs.deny
Normal file
7
.openshift/cron/weekly/jobs.deny
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Any script or job files listed in here (one entry per line) will NOT be
|
||||
# executed (read as ignored by run-parts).
|
||||
#
|
||||
|
||||
README
|
||||
|
||||
4
.openshift/markers/README.md
Normal file
4
.openshift/markers/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
For information about markers, consult the documentation:
|
||||
|
||||
http://openshift.github.io/documentation/oo_user_guide.html#markers
|
||||
http://openshift.github.io/documentation/oo_cartridge_guide.html#php-markers
|
||||
0
.openshift/pear.txt
Normal file
0
.openshift/pear.txt
Normal file
44
.travis.yml
Normal file
44
.travis.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
# see http://about.travis-ci.org/docs/user/languages/php/ for more hints
|
||||
language: php
|
||||
|
||||
# list any PHP version you want to test against
|
||||
php:
|
||||
# using major version aliases
|
||||
|
||||
# aliased to a recent 5.6.x version
|
||||
- 5.6
|
||||
# aliased to a recent 7.x version
|
||||
- 7.0
|
||||
# aliased to a recent hhvm version
|
||||
- hhvm
|
||||
|
||||
# optionally specify a list of environments, for example to test different RDBMS
|
||||
#env:
|
||||
# - DB=mysql
|
||||
# - DB=pgsql
|
||||
|
||||
# optionally set up exclutions and allowed failures in the matrix
|
||||
matrix:
|
||||
# exclude:
|
||||
# - php: hhvm
|
||||
# env: DB=pgsql # PDO driver for pgsql is unsupported by HHVM (3rd party install for support)
|
||||
allow_failures:
|
||||
- php: hhvm
|
||||
|
||||
# execute any number of scripts before the test run, custom env's are available as variables
|
||||
#before_script:
|
||||
# - if [[ "$DB" == "pgsql" ]]; then psql -c "DROP DATABASE IF EXISTS hello_world_test;" -U postgres; fi
|
||||
# - if [[ "$DB" == "pgsql" ]]; then psql -c "create database hello_world_test;" -U postgres; fi
|
||||
# - if [[ "$DB" == "mysql" ]]; then mysql -e "create database IF NOT EXISTS hello_world_test;" -uroot; fi
|
||||
|
||||
install:
|
||||
- composer require phpunit/phpunit
|
||||
|
||||
# omitting "script:" will default to phpunit
|
||||
# use the $DB env variable to determine the phpunit.xml to use
|
||||
script: vendor/bin/phpunit tests/unit/
|
||||
|
||||
# configure notifications (email, IRC, campfire etc)
|
||||
notifications:
|
||||
# irc: "irc.freenode.org#yourfavouriteroomfortravis"
|
||||
# a plugin/script to post to a hubzilla channel would be neat here
|
||||
632
CHANGELOG
Normal file
632
CHANGELOG
Normal file
@@ -0,0 +1,632 @@
|
||||
Hubzilla 2.2 (2017-03-08)
|
||||
- Don't collapse long nsfw content if nsfw plugin is already collapsing it
|
||||
- Provide version compatibility check for themes (minversion, maxversion)
|
||||
- Use chanlink_hash() instead of chanlink_url() where appropriate
|
||||
- Use head_add_link() for feed discovery
|
||||
- Provide HTTP header parser which honours continuation lines
|
||||
- Numerous doco improvements
|
||||
- Implement virtual privacy groups from restricted profile access list
|
||||
- Implement permission roles
|
||||
- Implement app-tray
|
||||
- Default to manual conversation updates
|
||||
- Implement channel move for all server roles
|
||||
- Implement nav login modal
|
||||
- Rename bb2diaspora.php to markdown.php
|
||||
- Remove obsolete module 'match'
|
||||
- Move firefox social api configuration to plugin
|
||||
- Move rsd service to twitter_api plugin
|
||||
- Add build_pagehead hook
|
||||
- Move opensearch to plugins
|
||||
- Move dreamhost hack to plugin
|
||||
- Add wiki permissions
|
||||
- Introduce hubloc_store_lowlevel() and xchan_store_lowlevel()
|
||||
- Move diaspora account import to the diaspora plugin
|
||||
- Allow export of single data sets instead of always exporting everything we know about in channel export
|
||||
- Queue optimisations for sites that have lingered in the queue for more than a couple of days
|
||||
- Add affinity slider tool settings for min and max defaults in settings/featured
|
||||
- Provide lowlevel xchan storage function to ensure that all non-null rows are initialised
|
||||
- Implement native wiki
|
||||
- Block well-known from oembed
|
||||
- Implement observer.language bbcode and observer.language comanche conditional
|
||||
- Implement daemon_addon hook to let plugins create custom background processes
|
||||
- Implement profile vcards
|
||||
- Implement connection vcards
|
||||
- Implement 'click to call' in address book
|
||||
- Default cover photo
|
||||
- Remove fullscreen functionality in photo album view
|
||||
- Update fontawesome lib to version 4.7.0
|
||||
- Implement a menu to select a section to be open by default in connedit
|
||||
- Improve comanche conditionals
|
||||
- Add enclosures and categories to atom feed parsing
|
||||
- Allow the atom_entry hook to change the results
|
||||
- Set 'adjust for viewer timezone' as the default for new events
|
||||
- Allow event creation in other timezones than your own
|
||||
- Update fullcalendar lib to version 3.1
|
||||
- Move api version call back to core
|
||||
- Create first webpage as 'home' if none exist
|
||||
- Show webpages link to visitors if a 'home' page exists
|
||||
|
||||
Bugfixes
|
||||
- Fix schema not saved if session theme != selected theme and schema select display issue
|
||||
- Fix no acl not detected in post_activity_item()
|
||||
- Fix find_folder_hash_by_path() was not safe against multiple attach structures with the same filename but in different directories
|
||||
- Fix don't search on empty filename - we shouldn't find it. The reason why this change is being made is because we actually did find it due to a development glitch
|
||||
- Fix several places where head_add_(css|js) functions have been used incorrectly.
|
||||
- Fix webpage import tool
|
||||
- Fix numerous bugs with the addon repo management GUI
|
||||
- Fix attach_delete() to remove photo resources even if the attach table row wasn't found
|
||||
- Fix choking if photo_factory() returns null
|
||||
- Fix embedimage if an albumname contains quotes
|
||||
- Fix chat member list when one or more members are connected via access tokens
|
||||
- Fix issue #636 - some localised (e.g. Italian) strings have single quotes which throw JS errors when used in single quoted template constructs
|
||||
- Fix issues #629 and #635 - edited post arriving from downstream source was not being rejected
|
||||
- Fix peoplefind widget not honouring directory option settings
|
||||
- Fix issue with HTML in code blocks in markdown in wiki
|
||||
- Fix issue with post signatures if posted from api and logged in locally with a different identity
|
||||
|
||||
Plugins/Addon
|
||||
- Add experimental webmention plugin
|
||||
- NSFW: Use button instead of text link
|
||||
- Diaspora: gracefully handle multiple photos per post
|
||||
- Diaspora: change profile photo permission call
|
||||
- Logrotate: don't throw an error if another server process renamed the logfile before we got to it
|
||||
- Chess: the channel owner must be one of the players, so only require selecting one connection for an opponent
|
||||
- Move firefox social api configuration to plugin from core
|
||||
- Move rsd service to twitter_api plugin from core
|
||||
- Move opensearch to plugins from core
|
||||
- Move dreamhost hack to plugin from
|
||||
- Move diaspora account import to addon from core
|
||||
- Reflect hubloc store changes in plugins
|
||||
- Reflect xchan store changes in plugins
|
||||
- Rendezvous: Fixed marker creation bug
|
||||
- Rendezvous: Center on marker if specified in URL, and update browser address bar with shareable link when selecting markers on the map
|
||||
- Rendezvous: Set default value of 0 for priximity alert when making new markers
|
||||
- Move gitwiki to plugins from core which has been replaced by native wiki
|
||||
- Openclipatar: reflect changes to files and photos which were unified in core some time ago
|
||||
- Reintroduce gnusocial plugin after security/functionality review
|
||||
- Twitter_api: hubzilla core issue 638 - unsupported message-id field not available in all twitter api functions
|
||||
- Superblock: update to reflect core changes
|
||||
- Rendezvous: implement static marker proximity alert
|
||||
- Phpmailer: security update
|
||||
|
||||
Hubzilla 2.0 (2016-12-23)
|
||||
- Deprecate bb_iframe
|
||||
- Note widget: resize the textarea to reveal full content
|
||||
- Implement fixed left aside
|
||||
- Implement lockview for wikilist
|
||||
- Simplify wikilist widget
|
||||
- Router error reporting
|
||||
- Setup changes to check for shell_exec and exec functions
|
||||
- Extensible permissions upgrade handling for channels with custom permission roles
|
||||
- Allow plugins to cancel item_store() and item_store_update()
|
||||
- ZOT version 1.2 provides negotiation of cryptographic algorithms
|
||||
- Provide a fresh new look and cleaner layout and more relevant information to siteinfo
|
||||
- Introduce highlight bbcode [hl]
|
||||
- Implement wiki mimetypes markdown or bbcode
|
||||
- Doc pages refactoring
|
||||
- Update webpages and wiki context help
|
||||
- Make a git commit when a new wiki page is created
|
||||
- Prev-next navigation for mod_connedit to ease bulk connection edits
|
||||
- Move the remote user homebutton to the user menu
|
||||
- Do not render maps/locations for Diaspora destinations
|
||||
- Provide 'per-page' caching for is_matrix_url() results to reduce duplicate queries
|
||||
- Don't send notification for posts/comments on old conversations that were refetched after having expired
|
||||
- Numerous wiki UI improvements
|
||||
- Move twitter api to addon
|
||||
- Cleanup and re-organise the voting and attendance buttons
|
||||
- Reorganise emoticons
|
||||
- Collapse navbar-collapse-1 if avatar menu is clicked.
|
||||
- New display setting: static page update as opposed to live update
|
||||
- Command line administrative channel connect utility
|
||||
- Modernise chanview
|
||||
- Implement edit activities to share post/comment edits with protocols which do not support them (e.g. Diaspora)
|
||||
- Wiki export
|
||||
- Numerous postgres compatibility fixes
|
||||
- Remove requirement that imported profile photos be in the profile photos album
|
||||
- Change event behaviour - share by default.
|
||||
- Use PDO database driver exclusively (deprecate drivers that are separately maintained)
|
||||
- Zot API re-write and extended
|
||||
|
||||
Bugfixes
|
||||
- Fix z_fetch_url() incorrect variable
|
||||
- Fix SQL error with app categories
|
||||
- Fix do not show revert buttons if we do not have write perms
|
||||
- Fix dropdown positions
|
||||
- Fix do not increase opacity to more than 1
|
||||
- Fix clone sync missing for some item delete operations
|
||||
- Fix embed-image for fullscreen mode
|
||||
- Fix attach_list_files()
|
||||
- Fix full screen for embedded videos
|
||||
- Fix the forum widget for forums with custom perms
|
||||
- Fix issue #607 parens not recognised inside urls
|
||||
- Fix pubsites: don't list dead sites
|
||||
- Fix issue #596 silence headers already sent warning
|
||||
- Fix missing plugins in zot-info
|
||||
- Fix notification issue
|
||||
- Fix issue #594 like of thing appears as profile owner like
|
||||
- Fix export issue
|
||||
- Fix checklist bbcode - only turn [] and [x] into checkboxes if it is found inside a checklist
|
||||
- Fix wiki permissions issues
|
||||
- Fix public calendar leaks connection information (birthdays) when view_contacts is not allowed
|
||||
- Fix attach_rename: flaw in duplicate filename detection resulted in filename(1)(1)(1).ext
|
||||
- Fix a fatal error with incorrect DB object access
|
||||
- Provide /locs link on settings page if there is more than one hubloc for this channnel *that isn't deleted*.
|
||||
- Fix issue #577 if connecting to a channel that is already pending, undo the pending and set connect permissions accordingly
|
||||
- Fix issue #575, when 'nofinish' is set on an event, invalid date was generated/stored
|
||||
- Fix bbcode event formatting issue
|
||||
- Fix zot_finger from navbar people search looping
|
||||
- Fix fromStandalonePermission()
|
||||
|
||||
Plugins
|
||||
- GNU Social: removed from addons for security reasons - it might be re-implemented once it is properly reviewed
|
||||
- Diaspora: missing item author when diaspora public comment received from relay
|
||||
- Superblock: refactoring
|
||||
- New addon: tripleaes for pro
|
||||
- Cdav: "if not exists" only supported starting with postgresql v. 9.5 debian stable has 9.4
|
||||
- Rendezvous: added markers and members export tool at /rendezvous/[group_id]/export/{markers,members}
|
||||
- Twitter: move twitter api to addon
|
||||
- New addon: b2tbtn (back to top button)
|
||||
- Diaspora: import public diaspora messages to sys if applicable
|
||||
- Diaspora: try and handle singletons better and simplify the associated notifier decisions
|
||||
- Rendezvous: add proximity alert feature to members to issue notification when member is within a specified distance.
|
||||
- New addon: diaspora_reconnect to refriend diaspora/friendica connections from a clone or channel move
|
||||
- Diaspora: change the logic for deciding between upstream and downstream message flow for notifier plugins
|
||||
- Rendezvous: prompt member to share their location by activating the GPS control using a tooltip and pulsing visibility
|
||||
- statistics_json: fix nodeinfo
|
||||
- Rendezvous: restored the lost gps-icon.png and corrected the OpenStreetMap tile server URL to avoid insecure content warnings
|
||||
- Rendezvous: use observer name if available
|
||||
- std_embeds: missing backslash
|
||||
- Diaspora: postgres fixes issue #31
|
||||
- Rendezvous: added marker list with centering buttons and popup open.
|
||||
- Rendezvous: added control to see list of members sharing their location, with buttons to pan the map to center them
|
||||
- Diaspora: system level diaspora toggle
|
||||
- Rendezvous: added control that displays members.
|
||||
- Diaspora: rename diaspora2bb() to markdown_to_bb() in core
|
||||
- Hubwall: remove illegal unescaped angle chars
|
||||
- Rendezvous: Add control to delete member if not updated in over 14 minutes
|
||||
|
||||
Hubzilla 1.14 (2016-10-13)
|
||||
- New hook bbcode_filter
|
||||
- Unify the various mail sending instance to enotify::send() and z_mail()
|
||||
- Provide ability for admin to change account password
|
||||
- Replace deprecated Sabre functions
|
||||
- Add plugin hook for 'get_profile_photo'
|
||||
- Convert NULL_DATE to a legal date for compatibility with MySQL strict mode
|
||||
- Allow a site to over-ride the help table-of-contents files
|
||||
- Autoscroll to target post/comment when in single-thread mode
|
||||
- Indicator for own response verb activity
|
||||
- Add server role documentation
|
||||
- Pro: remove 'Additional Features' link for techlevel 0
|
||||
- Upgrade fullcalendar library to version 3
|
||||
- Whitelist button tag in htmlpurifier
|
||||
- Upgrade justifiedGallery library to version 3.6.3
|
||||
- Pubsites improvements
|
||||
- Upgrade foundation library to version 6.2.3
|
||||
- Ability to move photos to another album
|
||||
- Submodules for settings page
|
||||
- Submodules for admin page
|
||||
- Remove chatroom suggestions
|
||||
- Revamped and improved theme select backend
|
||||
- Theme preview
|
||||
- Implement techlevels for pro server role
|
||||
- BBcode checklist
|
||||
- Improve save to folder modal dialog
|
||||
- Case insensitive sort apps
|
||||
- Add authors to post distribution
|
||||
- Redirect to plugin page after enabling to show configuration settings if applicable
|
||||
- Move allowed email domains to admin->security page
|
||||
- Display text around the searched query in documentation search
|
||||
- Comanche observer conditionals
|
||||
- Remove ratings
|
||||
- Context help for /connedit
|
||||
- Provide configurable sidebar table-of-contents indexes for different levels of the help hierarchy
|
||||
- Comanche conditionals
|
||||
- Cover photo enhancements (does not disappear after initial scrolldown)
|
||||
- Website import/export
|
||||
- Server roles (basic, standard and pro)
|
||||
|
||||
Bugfixes
|
||||
- Fix connected time not shown on ajax loaded connections
|
||||
- API issues
|
||||
- Fix readmore.js collapsing on scrolldirection change in some mobile browsers
|
||||
- Personalize Server Emails
|
||||
- Audio player doesn't automatically show for m4a files
|
||||
- Fix ajax page update with /channel?f=&mid=hash
|
||||
- Angle bracket characters in DB password not recognised
|
||||
- Regression: files/photos were not synchronising to channel clones properly
|
||||
- Missing categories in preview mode
|
||||
- attach_store() sql issue
|
||||
- Rename id share_container to distr_container - share_container seem to be blacklisted in various security browser plugins
|
||||
- Add 'map' extension to files served natively by nginx without using the project controller
|
||||
- Zot discovery wasn't returning in all cases (after discovering zot)
|
||||
- Do not show hidden channels in /randprof
|
||||
- Numerous postgres fixes
|
||||
- Illegal offset errors in include/conversation:status_editor() when no permissions array is passed
|
||||
- Patch foundation-6.2.3 to work with jquery-3.1
|
||||
- Custom/expert permissions bug
|
||||
- Mail: return array instead of object
|
||||
- Don't send purge_all notification to self
|
||||
- Saved search: tags and connection searches weren't being saved
|
||||
- Do not allow PERMS_PUBLIC as a choice for writable permission limits
|
||||
- Force cover photos as well as profile photos to be public. As a side effect 'thing' photos will also be considered public
|
||||
- Make lock switching actually work with multiple acl forms
|
||||
- Create smarty dir before any templates can be initialised
|
||||
- Fix aconfig
|
||||
- Broken doc search
|
||||
- Public forum check with custom/expert permissions
|
||||
|
||||
Plugins
|
||||
- Standard Embed: update to convert old corporate bbcodes
|
||||
- Cdav security: fix rw permission check
|
||||
- Cdav: add partial support for recurring events in the browser client (editing/creating is not implemented)
|
||||
- New plugin phpmailer: use phpmailer class instead of php's built-in mail() function
|
||||
- Diaspora: third party on other network comment issue
|
||||
- Diaspora: comment fix (hubzilla originated comment with plugin activated by comment author not making it to Diaspora)
|
||||
- Cdav: provide calendar list view
|
||||
- Diaspora: allow comments on public diaspora posts which were imported by subscribing to public tags.
|
||||
- Wppost: add blog_id parameter for WordPress MU sites such as WordPress.com
|
||||
- Wppost: don't log the password in normal mode
|
||||
- Hubwall: provide choice of sender addresses, the real admin email, postmaster, or noreply.
|
||||
- Chord: General cleanup of chord app
|
||||
- Chord: Update chord binary for modern linux systems
|
||||
- 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
|
||||
- ACLs can be set on files and directories prior to creation.
|
||||
- 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
|
||||
- UNO replaced with extensible server roles
|
||||
- select bbcode elements (such as baseurl) supported in wiki pages
|
||||
- addons:
|
||||
Diaspora Protocol - additional updates to maintain compatibility with 0.6.0.0 and stop showing likes as wall-to-wall comments (except when the liker does not have any Diaspora protocol ability)
|
||||
Cdav - continued improvements to the web UI
|
||||
Pong - the classic pong game
|
||||
Dfedfix - removed, no longer needed
|
||||
Openid - moved from core to addon
|
||||
- bugfixes
|
||||
unable to delete privacy groups
|
||||
weird display interaction with code blocks and escaped base64 content containing 8 - O
|
||||
workaround WordPress oembeds which are almost completely javascript and therefore filtered
|
||||
restrict oembed cache url to 254 chars to avoid spurious failures caching google map urls
|
||||
"Page not found" appeared twice
|
||||
birthdays weren't being automatically added to event calendar
|
||||
some iCal entries had malformed descriptions
|
||||
|
||||
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:
|
||||
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.
|
||||
UI:
|
||||
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
|
||||
Lib:
|
||||
z_fetch_url() updated to accept different request methods and request bodies
|
||||
item_store(), item_store_update() now return the stored items
|
||||
vcard microformat changes to remain spec compliant
|
||||
microformat meta tags added to post/comments
|
||||
AbConfig API changed to use channel_id rather than channel_hash, which was overly complicated to use
|
||||
SuperCurl class added to provide a framework for re-use of obscure CURL options
|
||||
Allow absolute links to CSS/JS files on CDN
|
||||
Add Let'sEncrypt intermediate cert to lib in case you forget to install it on the server
|
||||
Update fullcalendar and jquery (3.1) libs
|
||||
Update sabre/dav to 3.2.0
|
||||
Change content export from a month/year system to begin/end
|
||||
Use streaming I/O for delivering large photos
|
||||
Allow multiple App description files in a single plugin directory
|
||||
optimise a couple of troublesome/inefficient SQL queries
|
||||
avoid sending clone sync packets to dead sites
|
||||
Resolved Issues:
|
||||
channel home page not providing content to clients with javascript disabled
|
||||
Replace '@' obfuscation with html entity rather than the unicode look-alike
|
||||
xchan_query() failing to detect duplicates, resulting in inefficient queries
|
||||
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
|
||||
refresh permissions whitescreen in 1.8
|
||||
force immediate profile photo update on local site
|
||||
regression: 'save bookmarks' post action missing
|
||||
|
||||
Hubzilla 1.8
|
||||
Administration:
|
||||
Cleanup and resolve some edge cases with addon repository manager
|
||||
Provide sort field and direction on all fields of account and channel administration tables
|
||||
Rename 'user' administration to account administration to reflect its true purpose
|
||||
'safemode' tool to quickly disable and re-enable addons during a hypothetical upgrade crisis
|
||||
Security:
|
||||
Edited comments to private posts could lose their privacy settings under some circumstances
|
||||
Provide zot-finger signatures to prevent a possible but rare exploit involving DNS spoofing and phishing
|
||||
ACL selections:
|
||||
Various improvements to the ACL editor to further simplify the concepts and make it more intuitive
|
||||
Chat:
|
||||
Notifications of chatroom activity using standard browser notification interfaces.
|
||||
Themes:
|
||||
Allow a theme:schema string to represent a valid theme name. This fixes issues with setting schemas on site themes.
|
||||
Pubsites:
|
||||
Show server role (identify UNO or basic sites as opposed to hubzilla pro) and link to statistics
|
||||
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.
|
||||
Account Cloning:
|
||||
Regression on clone channel creation created a new channel name each time.
|
||||
New issue (fixed) with directory creation on cloned file content
|
||||
Content Rendering:
|
||||
Add inline code (in addition to the existing code blocks) to BBcode
|
||||
Add emoji reactions
|
||||
Add emojis as extended smilies with auto-complete support
|
||||
Emoji added as feature so it can be enabled/disabled and locked
|
||||
Ability to configure the standard reactions available on a site basis
|
||||
Disable 'convenience' ajax autoload on pgdn key, as it could lead to premature memory exhaustion
|
||||
Photos:
|
||||
Change album sort ordering, allow widgets and plugins to define other orderings
|
||||
Apps:
|
||||
Synchronise app list with changes to system apps
|
||||
Preserve existing app categories on app updates/edits
|
||||
Regression: fixed translated system app names
|
||||
Architecture:
|
||||
Provide autoloaded class files and libraries for plugins.
|
||||
Further refactoring of session driver to sort out some cookie anomolies
|
||||
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
|
||||
Refactor the template classes
|
||||
Refactor the ConversationItem mess into ThreadItem and ThreadStream
|
||||
Refactor Apps, Enotify, and Chat library code
|
||||
Refactor the various Config libraries (Config, PConfig, XConfig, AConfig, AbConfig, and IConfig)
|
||||
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.
|
||||
Bugfixes:
|
||||
Remove db_close function on page end - not needed and will not work with persistent DB connections.
|
||||
Undefined ref_session_write
|
||||
Some session functions needed to be static to work with CalDAV/CardDAV
|
||||
CLI interface: argc and argv were reversed
|
||||
HTML entities double encoded in edited titles
|
||||
Prevent delivering to empty recipients
|
||||
Sabre library setting some security headers for SAML after we've emitted HTML content
|
||||
Always initialise miniApp (caused obscure warning message if not set)
|
||||
Block 'sys' channels from being 'random profile' candidates
|
||||
DB update failed email could be sent in the wrong language under rare circumstances
|
||||
Openid remote authentication used incorrect namespace
|
||||
URL attached to profile "things" was not linked, always showing the "thing" manage page
|
||||
New connection wasn't added to default privacy group when "auto-accept" was enabled
|
||||
Regression: iconfig sharing wasn't working properly
|
||||
Plugins:
|
||||
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
|
||||
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
|
||||
|
||||
Hubzilla 1.6
|
||||
Cleanup and standardise the interfaces to the "jot" editor
|
||||
Router re-written to support calling class object methods as controllers
|
||||
All existing modules (160+) re-written as object classes
|
||||
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).
|
||||
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.
|
||||
Update smarty library to 3.1.29
|
||||
Update jquery.textcomplete to 1.3.4
|
||||
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.
|
||||
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.)
|
||||
Lots of work on bbcode auto-completion
|
||||
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
|
||||
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
|
||||
Allow somebody to follow a channel from a pasted redress containing a Unicode lookalike of the @ sign.
|
||||
Add conditional syntax to Comanche (if/then/else)
|
||||
Convert Comanche to an object class
|
||||
Removed IE6 compatibility code
|
||||
Explicitly close DB on shutdown/exit instead of allowing it to close naturally
|
||||
Allowed delayed publish of webpages
|
||||
Show current repository versions of master and dev on admin page and warn if your installation has fallen behind master
|
||||
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.
|
||||
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)
|
||||
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).]
|
||||
|
||||
Implement channel move operation for UNO configuration
|
||||
Remove bookmark references in UNO (which has no bookmarks by default)
|
||||
UI cleanup profiles/chat/manage
|
||||
Refactor webfinger probes and salmon backend for GNU-social federation
|
||||
SECURITY: DAV authentication exploit
|
||||
Context help added
|
||||
More help pages
|
||||
Provide 'posts only' feed
|
||||
Refactor App to remove globals
|
||||
Refactor Session to remove globals
|
||||
provide a fullscreen mode for selected modules and functions
|
||||
Regression: some addon routes broken
|
||||
fix "remember me"
|
||||
Autocomplete tool extended to bbcode/comanche
|
||||
Clone sync of file/photo updates
|
||||
system rename (e.g. http to https or DNS name change) missing some connection photos
|
||||
calendar module not blocked to public whhen block_public enabled
|
||||
Use timeago.js in reshare content so that timestamps will be correct on federated reshares
|
||||
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
|
||||
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
|
||||
Channel white/black lists
|
||||
OEmbed white/black lists
|
||||
Admin Profile Fields page created which manages the availability and order of standard profile fields and allows new fields to be created/managed
|
||||
"Poke" module reworked - page UI updated and "poke basic" setting introduced which limits the available poke "verbs".
|
||||
"Mood" module UI reworked
|
||||
"profile_photo" module UI reworked
|
||||
"cover_photo" module UI reworked
|
||||
"new_channel" module UI reworked
|
||||
"register" module UI reworked
|
||||
"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.
|
||||
.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
|
||||
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.
|
||||
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
|
||||
Plugins:
|
||||
keepout - "block public on steroids"
|
||||
pubsubhubbub - provides PuSH support to Atom feeds, required for GNU-social federation
|
||||
GNUsocial protocol - under development
|
||||
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).
|
||||
Allow a site to disable delivery reports if disk space is limited
|
||||
Regression: Wrong theme when viewing single post as non-member
|
||||
Some Diaspora profile photos use relative URLs - force absolute
|
||||
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
|
||||
cli utility for managing addons
|
||||
issue with sharing photo "items"
|
||||
cover photo manager: upload, crop, and store
|
||||
cover photo widget created
|
||||
rework the connections list page and provide a few management features there
|
||||
fixed issue with Comanche layout definitions loaded by plugins
|
||||
provide ability to separate delivery functions from item_store() and item_store_update() - some forum messages were being redelivered when cloned.
|
||||
call build_sync_packet() on pdledit changes
|
||||
Abstract the project name and version so these can be customised or removed
|
||||
Allow hiding the ratings links on a per-site basis
|
||||
db_type not present in international setup templates - was unable to choose postgres.
|
||||
item_photo_menu logically divided into a) actions on the post, b) actions related to the author
|
||||
bug: default channel not reset to 0 when last channel removed
|
||||
create widget containing only the contact block
|
||||
regression: public forums granted send stream permissions to connections
|
||||
workaround Firefox's refusal to honour disabling autocomplete of passwords
|
||||
regression: photo's uploaded to a channel by a guest (with file write permissions) not saved correctly.
|
||||
provide mechanisms for custom .well-known handlers (needed for LetsEncrypt ownership verification)
|
||||
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.
|
||||
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
|
||||
widget_item - allow use of page title instead of message id
|
||||
Add site black/white list checking to all .well-known services
|
||||
reduce incidents of screen jumping when "showmore" is activated
|
||||
add oembed provider for photos
|
||||
|
||||
Addons:
|
||||
|
||||
CSS theming of pageheader plugin
|
||||
xmpp addon ported from Friendica
|
||||
Diaspora private mail issues after the third reply
|
||||
Occasional issue with Diaspora connection requests
|
||||
Add notification email to Diaspora PMs
|
||||
Allow anonymising platform and version for statistics
|
||||
msgfooter addon created
|
||||
removed embedly plugin
|
||||
sync clones after superblock addition
|
||||
"keepout" plugin created
|
||||
|
||||
|
||||
Hubzilla 1.1
|
||||
|
||||
Rewrote and simplified the Queue manager and delivery system
|
||||
Rewrote and simplified the outer layers of the Zot protocol
|
||||
Use a standard version numbering scheme in addition to the snapshot tags
|
||||
Provide a channel blacklist for blocking channels with abusive or illegal content at the hub level
|
||||
Make the black/white lists pluggable
|
||||
Update template library
|
||||
Support for letsencrypt certs in various places
|
||||
Cleanup of login and register pages
|
||||
Better error responses for permission denied on channel file repositories
|
||||
Disabled the public stream by default for new installs (can be enabled if desired)
|
||||
Cleanup of API authentication and rework the old OAuth1 stuff
|
||||
Add API "status with media" support compatible with Twitter and conflicting method for GNU-social
|
||||
Rework photo ActivityStreams objects to align better with ActivityStreams producers/consumers
|
||||
Several minor API fixes to work better with AndStatus client
|
||||
Invitation only site - experimental support added, needs more work
|
||||
Fix delivery loop condition due to corrupted data which resulted in recursive upstream delivery
|
||||
Provide more support for external (git) widget collections.
|
||||
Extend the Queue API to 3rd-party network addons which have experienced downtime recently.
|
||||
Regression: Inherited permissions were not explicitly set
|
||||
Regression: "Xyz posted on your wall" notification sent when creating webpages at another channel
|
||||
Regression: Custom permissions not pre-populated on channel creation with named role.
|
||||
Provide "Public" string when a post can be made public, instead of "visible to default audience"
|
||||
Allow hub admin to specify a default role type for the first channel created, reducing complexity
|
||||
Ability for a hub admin to set feature defaults and lock them, reducing complexity
|
||||
Change default expiration of delivery reports to 10 days to accomodate sites with reduced resources
|
||||
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
|
||||
Cross Domain Authenticated Chess (Andrew Manning's repository)
|
||||
|
||||
And... the normal "lots of bugs fixed, translations updated, and documentation improved"
|
||||
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2010-2015 Hubzilla
|
||||
Copyright (c) 2010-2017 the Hubzilla Community
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
44
README.md
44
README.md
@@ -1,39 +1,29 @@
|
||||

|
||||
|
||||
Hubzilla
|
||||
========
|
||||
Hubzilla - Community Server
|
||||
===========================
|
||||
|
||||
###Websites. Redefined.
|
||||
<p align="center" markdown="1">
|
||||
<em><a href="https://github.com/redmatrix/hubzilla/blob/master/install/INSTALL.txt">Installing Hubzilla</a></em>
|
||||
</p>
|
||||
|
||||
|
||||

|
||||
**What is Hubzilla?**
|
||||
|
||||
**What are Hubs?**
|
||||
Hubzilla is a general purpose communication server integrated with a web publishing system and a decentralised permission system. If this sounds like a bunch of technical mumbo-jumbo to you, just think of it as an independent platform for sharing stuff online.
|
||||
|
||||
Hubs are independent general-purpose websites that not only connect with their associated members and viewers, but also connect together to exchange personal communications and other information with each other.
|
||||
This allows hub members on any hub to securely and privately share anything; with anybody, on any hub - anywhere; or share stuff publicly with anybody on the internet if desired.
|
||||
Hubzilla contains some social network bits, some cloud storage bits, some blog and forum bits, and some content management bits. These are all integrated within a common privacy framework - and it is all decentralised.
|
||||
|
||||
**Hubzilla** is the server software which makes this possible. It is a sophisticated and unique combination of an open source content management system and a decentralised identity, communications, and permissions framework and protocol suite, built using common webserver technology (PHP/MySQL/Apache, although Mariadb or Postgres and Nginx could also be used - we're pretty easy). The end result is a level of systems integration, privacy control, and communications features that you wouldn't think are possible in either a content management system or a decentralised communications network. It also brings a new level of cooperation and privacy to the web and introduces the concept of personally owned "single sign-on" to web services across the entire internet.
|
||||
Everything you publish or share can be restricted to those channels and people you wish to share them with; and these permissions work completely invisibly - even with channels on different servers or other communications services.
|
||||
|
||||
Hubzilla hubs are
|
||||
Migration and live backups of your connections, settings, and everything you publish are built-in, so you never need worry about server failure.
|
||||
|
||||
* decentralised
|
||||
* inherently social
|
||||
* optionally inter-networked with other hubs
|
||||
* privacy-enabled (privacy exclusions work across the entire internet to any registered identity on any compatible hubs)
|
||||
Hubzilla is completely decentralised and open source, for you modify or adapt to your needs and desires. Plugins, themes, and numerous configuration options extend the overall capabilities to do anything you can imagine.
|
||||
|
||||
Possible website applications include
|
||||
|
||||
* decentralised social networking nodes
|
||||
* personal cloud storage
|
||||
* file dropboxes
|
||||
* managing organisational communications and activities
|
||||
* collaboration and community decision-making
|
||||
* small business websites
|
||||
* public and private media/file libraries
|
||||
* blogs
|
||||
* event promotion
|
||||
* feed aggregation and republishing
|
||||
* forums
|
||||
* dating websites
|
||||
* pretty much anything you can do on a traditional blog or community website, but that you could do better if you could easily connect it with other websites or privately share things across website boundaries.
|
||||
**Who Are We?**
|
||||
|
||||
The Hubzilla community consists of passionate volunteers creating an open source commons of decentralised services which are highly integrated and can rival the feature set of large centralised providers. We do our best to provide ethical software which places you in control of your online communications and privacy expectations.
|
||||
|
||||
|
||||
[](https://travis-ci.org/redmatrix/hubzilla)
|
||||
|
||||
92
Zotlabs/Access/AccessList.php
Normal file
92
Zotlabs/Access/AccessList.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Access;
|
||||
|
||||
|
||||
class AccessList {
|
||||
|
||||
private $allow_cid;
|
||||
private $allow_gid;
|
||||
private $deny_cid;
|
||||
private $deny_gid;
|
||||
|
||||
/* indicates if we are using the default constructor values or values that have been set explicitly. */
|
||||
|
||||
private $explicit;
|
||||
|
||||
function __construct($channel) {
|
||||
|
||||
if($channel) {
|
||||
$this->allow_cid = $channel['channel_allow_cid'];
|
||||
$this->allow_gid = $channel['channel_allow_gid'];
|
||||
$this->deny_cid = $channel['channel_deny_cid'];
|
||||
$this->deny_gid = $channel['channel_deny_gid'];
|
||||
}
|
||||
else {
|
||||
$this->allow_cid = '';
|
||||
$this->allow_gid = '';
|
||||
$this->deny_cid = '';
|
||||
$this->deny_gid = '';
|
||||
}
|
||||
|
||||
$this->explicit = false;
|
||||
}
|
||||
|
||||
function get_explicit() {
|
||||
return $this->explicit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set AccessList from strings such as those in already
|
||||
* existing stored data items
|
||||
*/
|
||||
|
||||
function set($arr,$explicit = true) {
|
||||
$this->allow_cid = $arr['allow_cid'];
|
||||
$this->allow_gid = $arr['allow_gid'];
|
||||
$this->deny_cid = $arr['deny_cid'];
|
||||
$this->deny_gid = $arr['deny_gid'];
|
||||
|
||||
$this->explicit = $explicit;
|
||||
}
|
||||
|
||||
/**
|
||||
* return an array consisting of the current
|
||||
* access list components where the elements
|
||||
* are directly storable.
|
||||
*/
|
||||
|
||||
function get() {
|
||||
return array(
|
||||
'allow_cid' => $this->allow_cid,
|
||||
'allow_gid' => $this->allow_gid,
|
||||
'deny_cid' => $this->deny_cid,
|
||||
'deny_gid' => $this->deny_gid,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set AccessList from arrays, such as those provided by
|
||||
* acl_selector(). For convenience, a string (or non-array) input is
|
||||
* assumed to be a comma-separated list and auto-converted into an array.
|
||||
*/
|
||||
|
||||
function set_from_array($arr,$explicit = true) {
|
||||
$this->allow_cid = perms2str((is_array($arr['contact_allow']))
|
||||
? $arr['contact_allow'] : explode(',',$arr['contact_allow']));
|
||||
$this->allow_gid = perms2str((is_array($arr['group_allow']))
|
||||
? $arr['group_allow'] : explode(',',$arr['group_allow']));
|
||||
$this->deny_cid = perms2str((is_array($arr['contact_deny']))
|
||||
? $arr['contact_deny'] : explode(',',$arr['contact_deny']));
|
||||
$this->deny_gid = perms2str((is_array($arr['group_deny']))
|
||||
? $arr['group_deny'] : explode(',',$arr['group_deny']));
|
||||
|
||||
$this->explicit = $explicit;
|
||||
}
|
||||
|
||||
function is_private() {
|
||||
return (($this->allow_cid || $this->allow_gid || $this->deny_cid || $this->deny_gid) ? true : false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
36
Zotlabs/Access/PermissionLimits.php
Normal file
36
Zotlabs/Access/PermissionLimits.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Access;
|
||||
|
||||
use \Zotlabs\Lib as ZLib;
|
||||
|
||||
class PermissionLimits {
|
||||
|
||||
static public function Std_Limits() {
|
||||
$perms = Permissions::Perms();
|
||||
$limits = array();
|
||||
foreach($perms as $k => $v) {
|
||||
if(strstr($k,'view'))
|
||||
$limits[$k] = PERMS_PUBLIC;
|
||||
else
|
||||
$limits[$k] = PERMS_SPECIFIC;
|
||||
}
|
||||
return $limits;
|
||||
}
|
||||
|
||||
static public function Set($channel_id,$perm,$perm_limit) {
|
||||
ZLib\PConfig::Set($channel_id,'perm_limits',$perm,$perm_limit);
|
||||
}
|
||||
|
||||
static public function Get($channel_id,$perm = '') {
|
||||
if($perm) {
|
||||
return Zlib\PConfig::Get($channel_id,'perm_limits',$perm);
|
||||
}
|
||||
else {
|
||||
Zlib\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'];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
279
Zotlabs/Access/PermissionRoles.php
Normal file
279
Zotlabs/Access/PermissionRoles.php
Normal file
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Zotlabs\Access;
|
||||
|
||||
use Zotlabs\Lib as Zlib;
|
||||
|
||||
class PermissionRoles {
|
||||
|
||||
static public function version() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
static function role_perms($role) {
|
||||
|
||||
$ret = array();
|
||||
|
||||
$ret['role'] = $role;
|
||||
|
||||
switch($role) {
|
||||
case 'social':
|
||||
$ret['perms_auto'] = false;
|
||||
$ret['default_collection'] = false;
|
||||
$ret['directory_publish'] = true;
|
||||
$ret['online'] = true;
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
|
||||
'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
|
||||
'post_mail', 'chat', 'post_like', 'republish' ];
|
||||
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
break;
|
||||
|
||||
case 'social_restricted':
|
||||
$ret['perms_auto'] = false;
|
||||
$ret['default_collection'] = true;
|
||||
$ret['directory_publish'] = true;
|
||||
$ret['online'] = true;
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
|
||||
'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
|
||||
'post_mail', 'chat', 'post_like' ];
|
||||
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
|
||||
break;
|
||||
|
||||
case 'social_private':
|
||||
$ret['perms_auto'] = false;
|
||||
$ret['default_collection'] = true;
|
||||
$ret['directory_publish'] = false;
|
||||
$ret['online'] = false;
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
|
||||
'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
|
||||
'post_mail', 'post_like' ];
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
$ret['limits']['view_contacts'] = PERMS_SPECIFIC;
|
||||
$ret['limits']['view_storage'] = PERMS_SPECIFIC;
|
||||
|
||||
break;
|
||||
|
||||
case 'forum':
|
||||
$ret['perms_auto'] = true;
|
||||
$ret['default_collection'] = false;
|
||||
$ret['directory_publish'] = true;
|
||||
$ret['online'] = false;
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
|
||||
'view_pages', 'view_wiki', 'post_wall', 'post_comments', 'tag_deliver',
|
||||
'post_mail', 'post_like' , 'republish', 'chat' ];
|
||||
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
break;
|
||||
|
||||
case 'forum_restricted':
|
||||
$ret['perms_auto'] = false;
|
||||
$ret['default_collection'] = true;
|
||||
$ret['directory_publish'] = true;
|
||||
$ret['online'] = false;
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
|
||||
'view_pages', 'view_wiki', 'post_wall', 'post_comments', 'tag_deliver',
|
||||
'post_mail', 'post_like' , 'chat' ];
|
||||
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
|
||||
break;
|
||||
|
||||
case 'forum_private':
|
||||
$ret['perms_auto'] = false;
|
||||
$ret['default_collection'] = true;
|
||||
$ret['directory_publish'] = false;
|
||||
$ret['online'] = false;
|
||||
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
|
||||
'view_pages', 'view_wiki', 'post_wall', 'post_comments',
|
||||
'post_mail', 'post_like' , 'chat' ];
|
||||
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
$ret['limits']['view_profile'] = PERMS_SPECIFIC;
|
||||
$ret['limits']['view_contacts'] = PERMS_SPECIFIC;
|
||||
$ret['limits']['view_storage'] = PERMS_SPECIFIC;
|
||||
$ret['limits']['view_pages'] = PERMS_SPECIFIC;
|
||||
$ret['limits']['view_wiki'] = PERMS_SPECIFIC;
|
||||
|
||||
break;
|
||||
|
||||
case 'feed':
|
||||
$ret['perms_auto'] = true;
|
||||
$ret['default_collection'] = false;
|
||||
$ret['directory_publish'] = true;
|
||||
$ret['online'] = false;
|
||||
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
|
||||
'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
|
||||
'post_mail', 'post_like' , 'republish' ];
|
||||
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
|
||||
break;
|
||||
|
||||
case 'feed_restricted':
|
||||
$ret['perms_auto'] = false;
|
||||
$ret['default_collection'] = true;
|
||||
$ret['directory_publish'] = false;
|
||||
$ret['online'] = false;
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
|
||||
'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
|
||||
'post_mail', 'post_like' , 'republish' ];
|
||||
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
|
||||
break;
|
||||
|
||||
case 'soapbox':
|
||||
$ret['perms_auto'] = true;
|
||||
$ret['default_collection'] = false;
|
||||
$ret['directory_publish'] = true;
|
||||
$ret['online'] = false;
|
||||
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
|
||||
'view_pages', 'view_wiki', 'post_like' , 'republish' ];
|
||||
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
|
||||
break;
|
||||
|
||||
case 'repository':
|
||||
$ret['perms_auto'] = true;
|
||||
$ret['default_collection'] = false;
|
||||
$ret['directory_publish'] = true;
|
||||
$ret['online'] = false;
|
||||
|
||||
$ret['perms_connect'] = [
|
||||
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
|
||||
'view_pages', 'view_wiki', 'write_storage', 'write_pages', 'post_wall', 'post_comments', 'tag_deliver',
|
||||
'post_mail', 'post_like' , 'republish', 'chat', 'write_wiki' ];
|
||||
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
break;
|
||||
|
||||
case 'custom':
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$x = get_config('system','role_perms');
|
||||
// let system settings over-ride any or all
|
||||
if($x && is_array($x) && array_key_exists($role,$x))
|
||||
$ret = array_merge($ret,$x[$role]);
|
||||
|
||||
call_hooks('get_role_perms',$ret);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static public function new_custom_perms($uid,$perm,$abooks) {
|
||||
|
||||
// set permissionlimits for this permission here, for example:
|
||||
|
||||
// if($perm === 'mynewperm')
|
||||
// \Zotlabs\Access\PermissionLimits::Set($uid,$perm,1);
|
||||
|
||||
if($perm === 'view_wiki')
|
||||
\Zotlabs\Access\PermissionLimits::Set($uid,$perm,PERMS_PUBLIC);
|
||||
|
||||
if($perm === 'write_wiki')
|
||||
\Zotlabs\Access\PermissionLimits::Set($uid,$perm,PERMS_SPECIFIC);
|
||||
|
||||
|
||||
// set autoperms here if applicable
|
||||
// choices are to set to 0, 1, or the value of an existing perm
|
||||
|
||||
if(get_pconfig($uid,'system','autoperms')) {
|
||||
|
||||
$c = channelx_by_n($uid);
|
||||
$value = 0;
|
||||
|
||||
// if($perm === 'mynewperm')
|
||||
// $value = get_abconfig($uid,$c['channel_hash'],'autoperms','someexistingperm');
|
||||
|
||||
if($perm === 'view_wiki')
|
||||
$value = get_abconfig($uid,$c['channel_hash'],'autoperms','view_pages');
|
||||
|
||||
if($perm === 'write_wiki')
|
||||
$value = get_abconfig($uid,$c['channel_hash'],'autoperms','write_pages');
|
||||
|
||||
if($c) {
|
||||
set_abconfig($uid,$c['channel_hash'],'autoperms',$perm,$value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// now set something for all existing connections.
|
||||
|
||||
if($abooks) {
|
||||
foreach($abooks as $ab) {
|
||||
switch($perm) {
|
||||
// case 'mynewperm':
|
||||
// choices are to set to 1, set to 0, or clone an existing perm
|
||||
// set_abconfig($uid,$ab['abook_xchan'],'my_perms',$perm,
|
||||
// intval(get_abconfig($uid,$ab['abook_xchan'],'my_perms','someexistingperm')));
|
||||
|
||||
case 'view_wiki':
|
||||
set_abconfig($uid,$ab['abook_xchan'],'my_perms',$perm,
|
||||
intval(get_abconfig($uid,$ab['abook_xchan'],'my_perms','view_pages')));
|
||||
|
||||
case 'write_wiki':
|
||||
set_abconfig($uid,$ab['abook_xchan'],'my_perms',$perm,
|
||||
intval(get_abconfig($uid,$ab['abook_xchan'],'my_perms','write_pages')));
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static public function roles() {
|
||||
$roles = [
|
||||
t('Social Networking') => [
|
||||
'social' => t('Social - Mostly Public'),
|
||||
'social_restricted' => t('Social - Restricted'),
|
||||
'social_private' => t('Social - Private')
|
||||
],
|
||||
|
||||
t('Community Forum') => [
|
||||
'forum' => t('Forum - Mostly Public'),
|
||||
'forum_restricted' => t('Forum - Restricted'),
|
||||
'forum_private' => t('Forum - Private')
|
||||
],
|
||||
|
||||
t('Feed Republish') => [
|
||||
'feed' => t('Feed - Mostly Public'),
|
||||
'feed_restricted' => t('Feed - Restricted')
|
||||
],
|
||||
|
||||
t('Special Purpose') => [
|
||||
'soapbox' => t('Special - Celebrity/Soapbox'),
|
||||
'repository' => t('Special - Group Repository')
|
||||
],
|
||||
|
||||
t('Other') => [
|
||||
'custom' => t('Custom/Expert Mode')
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
return $roles;
|
||||
}
|
||||
|
||||
}
|
||||
221
Zotlabs/Access/Permissions.php
Normal file
221
Zotlabs/Access/Permissions.php
Normal file
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Zotlabs\Access;
|
||||
|
||||
use Zotlabs\Lib as Zlib;
|
||||
|
||||
class Permissions {
|
||||
|
||||
/**
|
||||
* Extensible permissions.
|
||||
* To add new permissions, add to the list of $perms below, with a simple description.
|
||||
*
|
||||
* Also visit PermissionRoles.php and add to the $ret['perms_connect'] property for any role
|
||||
* if this permission should be granted to new connections.
|
||||
*
|
||||
* Next look at PermissionRoles::new_custom_perms() and provide a handler for updating custom
|
||||
* permission roles. You will want to set a default PermissionLimit for each channel and also
|
||||
* provide a sane default for any existing connections. You may or may not wish to provide a
|
||||
* default auto permission. If in doubt, leave this alone as custom permissions by definition
|
||||
* are the responsibility of the channel owner to manage. You just don't want to create any
|
||||
* suprises or break things so you have an opportunity to provide sane settings.
|
||||
*
|
||||
* Update the version here and in PermissionRoles
|
||||
*
|
||||
*
|
||||
* Permissions with 'view' in the name are considered read permissions. Anything
|
||||
* else requires authentication. Read permission limits are PERMS_PUBLIC and anything else
|
||||
* is given PERMS_SPECIFIC.
|
||||
*
|
||||
* PermissionLimits::Std_limits() retrieves the standard limits. A permission role
|
||||
* MAY alter an individual setting after retrieving the Std_limits if you require
|
||||
* something different for a specific permission within the given role.
|
||||
*
|
||||
*/
|
||||
|
||||
static public function version() {
|
||||
// This must match the version in PermissionRoles.php before permission updates can run.
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
static public function Perms($filter = '') {
|
||||
|
||||
$perms = [
|
||||
'view_stream' => t('Can view my channel stream and posts'),
|
||||
'send_stream' => t('Can send me their channel stream and posts'),
|
||||
'view_profile' => t('Can view my default channel profile'),
|
||||
'view_contacts' => t('Can view my connections'),
|
||||
'view_storage' => t('Can view my file storage and photos'),
|
||||
'write_storage' => t('Can upload/modify my file storage and photos'),
|
||||
'view_pages' => t('Can view my channel webpages'),
|
||||
'view_wiki' => t('Can view my wiki pages'),
|
||||
'write_pages' => t('Can create/edit my channel webpages'),
|
||||
'write_wiki' => t('Can write to my wiki pages'),
|
||||
'post_wall' => t('Can post on my channel (wall) page'),
|
||||
'post_comments' => t('Can comment on or like my posts'),
|
||||
'post_mail' => t('Can send me private mail messages'),
|
||||
'post_like' => t('Can like/dislike profiles and profile things'),
|
||||
'tag_deliver' => t('Can forward to all my channel connections via @+ mentions in posts'),
|
||||
'chat' => t('Can chat with me'),
|
||||
'republish' => t('Can source my public posts in derived channels'),
|
||||
'delegate' => t('Can administer my channel')
|
||||
];
|
||||
|
||||
$x = array('permissions' => $perms, 'filter' => $filter);
|
||||
call_hooks('permissions_list',$x);
|
||||
return($x['permissions']);
|
||||
|
||||
}
|
||||
|
||||
static public function BlockedAnonPerms() {
|
||||
|
||||
// Perms from the above list that are blocked from anonymous observers.
|
||||
// e.g. you must be authenticated.
|
||||
|
||||
$res = array();
|
||||
$perms = PermissionLimits::Std_limits();
|
||||
foreach($perms as $perm => $limit) {
|
||||
if($limit != PERMS_PUBLIC) {
|
||||
$res[] = $perm;
|
||||
}
|
||||
}
|
||||
|
||||
$x = array('permissions' => $res);
|
||||
call_hooks('write_perms',$x);
|
||||
return($x['permissions']);
|
||||
|
||||
}
|
||||
|
||||
// converts [ 0 => 'view_stream', ... ]
|
||||
// to [ 'view_stream' => 1 ]
|
||||
// for any permissions in $arr;
|
||||
// Undeclared permissions are set to 0
|
||||
|
||||
static public function FilledPerms($arr) {
|
||||
if(is_null($arr)) {
|
||||
btlogger('FilledPerms: null');
|
||||
}
|
||||
|
||||
$everything = self::Perms();
|
||||
$ret = [];
|
||||
foreach($everything as $k => $v) {
|
||||
if(in_array($k,$arr))
|
||||
$ret[$k] = 1;
|
||||
else
|
||||
$ret[$k] = 0;
|
||||
}
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
static public function OPerms($arr) {
|
||||
$ret = [];
|
||||
if($arr) {
|
||||
foreach($arr as $k => $v) {
|
||||
$ret[] = [ 'name' => $k, 'value' => $v ];
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
static public function FilledAutoperms($channel_id) {
|
||||
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) {
|
||||
$arr[$rr['k']] = intval($rr['v']);
|
||||
}
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
static public function PermsCompare($p1,$p2) {
|
||||
foreach($p1 as $k => $v) {
|
||||
if(! array_key_exists($k,$p2))
|
||||
return false;
|
||||
if($p1[$k] != $p2[$k])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static public function connect_perms($channel_id) {
|
||||
|
||||
$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);
|
||||
$permcat = $pcp->fetch($pc);
|
||||
if($permcat && $permcat['perms']) {
|
||||
foreach($permcat['perms'] as $p) {
|
||||
$my_perms[$p['name']] = $p['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// look up the permission role to see if it specified auto-connect
|
||||
// and if there was no permcat or a default permcat, set the perms
|
||||
// from the role
|
||||
|
||||
$role = get_pconfig($channel_id,'system','permissions_role');
|
||||
if($role) {
|
||||
$xx = PermissionRoles::role_perms($role);
|
||||
if($xx['perms_auto'])
|
||||
$automatic = 1;
|
||||
|
||||
if((! $my_perms) && ($xx['perms_connect'])) {
|
||||
$default_perms = $xx['perms_connect'];
|
||||
$my_perms = Permissions::FilledPerms($default_perms);
|
||||
}
|
||||
}
|
||||
|
||||
// If we reached this point without having any permission information,
|
||||
// it is likely a custom permissions role. First see if there are any
|
||||
// automatic permissions.
|
||||
|
||||
if(! $my_perms) {
|
||||
$m = Permissions::FilledAutoperms($channel_id);
|
||||
if($m) {
|
||||
$automatic = 1;
|
||||
$my_perms = $m;
|
||||
}
|
||||
}
|
||||
|
||||
// If we reached this point with no permissions, the channel is using
|
||||
// custom perms but they are not automatic. They will be stored in abconfig with
|
||||
// the channel's channel_hash (the 'self' connection).
|
||||
|
||||
if(! $my_perms) {
|
||||
$r = q("select channel_hash from channel where channel_id = %d",
|
||||
intval($channel_id)
|
||||
);
|
||||
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) {
|
||||
$my_perms[$xv['k']] = intval($xv['v']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ( [ 'perms' => $my_perms, 'automatic' => $automatic ] );
|
||||
}
|
||||
|
||||
}
|
||||
14
Zotlabs/Daemon/Addon.php
Normal file
14
Zotlabs/Daemon/Addon.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
|
||||
class Addon {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
call_hooks('daemon_addon',$argv);
|
||||
|
||||
}
|
||||
}
|
||||
54
Zotlabs/Daemon/Checksites.php
Normal file
54
Zotlabs/Daemon/Checksites.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
|
||||
|
||||
class Checksites {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
logger('checksites: start');
|
||||
|
||||
if(($argc > 1) && ($argv[1]))
|
||||
$site_id = $argv[1];
|
||||
|
||||
if($site_id)
|
||||
$sql_options = " and site_url = '" . dbesc($argv[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'),
|
||||
intval(SITE_TYPE_ZOT)
|
||||
);
|
||||
|
||||
if(! $r)
|
||||
return;
|
||||
|
||||
foreach($r as $rr) {
|
||||
if(! strcasecmp($rr['site_url'],z_root()))
|
||||
continue;
|
||||
|
||||
$x = ping_site($rr['site_url']);
|
||||
if($x['success']) {
|
||||
logger('checksites: ' . $rr['site_url']);
|
||||
q("update site set site_update = '%s' where site_url = '%s' ",
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($rr['site_url'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
logger('marking dead site: ' . $x['message']);
|
||||
q("update site set site_dead = 1 where site_url = '%s' ",
|
||||
dbesc($rr['site_url'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
14
Zotlabs/Daemon/Cli_suggest.php
Normal file
14
Zotlabs/Daemon/Cli_suggest.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/socgraph.php');
|
||||
|
||||
class Cli_suggest {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
update_suggestions();
|
||||
|
||||
}
|
||||
}
|
||||
204
Zotlabs/Daemon/Cron.php
Normal file
204
Zotlabs/Daemon/Cron.php
Normal file
@@ -0,0 +1,204 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
class Cron {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
$maxsysload = intval(get_config('system','maxloadavg'));
|
||||
if($maxsysload < 1)
|
||||
$maxsysload = 50;
|
||||
if(function_exists('sys_getloadavg')) {
|
||||
$load = sys_getloadavg();
|
||||
if(intval($load[0]) > $maxsysload) {
|
||||
logger('system: load ' . $load . ' too high. Cron deferred to next scheduled run.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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'))) {
|
||||
logger("cron: Already running");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a lockfile. Needs two vars, but $x doesn't need to contain anything.
|
||||
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
|
||||
|
||||
require_once('include/sharedwithme.php');
|
||||
apply_updates();
|
||||
|
||||
// expire any expired mail
|
||||
|
||||
q("delete from mail where expires > '%s' and expires < %s ",
|
||||
dbesc(NULL_DATE),
|
||||
db_utcnow()
|
||||
);
|
||||
|
||||
// expire any expired items
|
||||
|
||||
$r = q("select id from item where expires > '2001-01-01 00:00:00' and expires < %s
|
||||
and item_deleted = 0 ",
|
||||
db_utcnow()
|
||||
);
|
||||
if($r) {
|
||||
require_once('include/items.php');
|
||||
foreach($r as $rr)
|
||||
drop_item($rr['id'],false);
|
||||
}
|
||||
|
||||
|
||||
// delete expired access tokens
|
||||
|
||||
$r = q("select atoken_id from atoken where atoken_expires > '%s' and atoken_expires < %s",
|
||||
dbesc(NULL_DATE),
|
||||
db_utcnow()
|
||||
);
|
||||
if($r) {
|
||||
require_once('include/security.php');
|
||||
foreach($r as $rr) {
|
||||
atoken_delete($rr['atoken_id']);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that every channel pings a directory server once a month. This way we can discover
|
||||
// channels and sites that quietly vanished and prevent the directory from accumulating stale
|
||||
// or dead entries.
|
||||
|
||||
$r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s",
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
$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'))
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$x = q("update item set item_delayed = 0 where id = %d",
|
||||
intval($rr['id'])
|
||||
);
|
||||
if($x) {
|
||||
$z = q("select * from item where id = %d",
|
||||
intval($message_id)
|
||||
);
|
||||
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) ]
|
||||
]
|
||||
);
|
||||
}
|
||||
Master::Summon(array('Notifier','wall-new',$rr['id']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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'));
|
||||
|
||||
// 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'));
|
||||
|
||||
|
||||
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 = ''
|
||||
and xchan_photo_date < %s - INTERVAL %s",
|
||||
db_utcnow(),
|
||||
db_quoteinterval('1 DAY')
|
||||
);
|
||||
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'
|
||||
where xchan_hash = '%s'",
|
||||
dbesc($photos[0]),
|
||||
dbesc($photos[1]),
|
||||
dbesc($photos[2]),
|
||||
dbesc($photos[3]),
|
||||
dbesc($rr['xchan_hash'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// pull in some public posts
|
||||
|
||||
if(! get_config('system','disable_discover_tab'))
|
||||
Master::Summon(array('Externals'));
|
||||
|
||||
$generation = 0;
|
||||
|
||||
$restart = false;
|
||||
|
||||
if(($argc > 1) && ($argv[1] == 'restart')) {
|
||||
$restart = true;
|
||||
$generation = intval($argv[2]);
|
||||
if(! $generation)
|
||||
killme();
|
||||
}
|
||||
|
||||
reload_plugins();
|
||||
|
||||
$d = datetime_convert();
|
||||
|
||||
// TODO check to see if there are any cronhooks before wasting a process
|
||||
|
||||
if(! $restart)
|
||||
Master::Summon(array('Cronhooks'));
|
||||
|
||||
set_config('system','lastcron',datetime_convert());
|
||||
|
||||
//All done - clear the lockfile
|
||||
@unlink($lockfile);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
89
Zotlabs/Daemon/Cron_daily.php
Normal file
89
Zotlabs/Daemon/Cron_daily.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
class Cron_daily {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
logger('cron_daily: start');
|
||||
|
||||
/**
|
||||
* Cron Daily
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
require_once('include/dir_fns.php');
|
||||
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) {
|
||||
Master::Summon(array('Cron_weekly'));
|
||||
}
|
||||
|
||||
// once daily run birthday_updates and then expire in background
|
||||
|
||||
// FIXME: add birthday updates, both locally and for xprof for use
|
||||
// by directory servers
|
||||
|
||||
update_birthdays();
|
||||
|
||||
// expire any read notifications over a month old
|
||||
|
||||
q("delete from notify where seen = 1 and created < %s - INTERVAL %s",
|
||||
db_utcnow(), db_quoteinterval('30 DAY')
|
||||
);
|
||||
|
||||
//update statistics in config
|
||||
require_once('include/statistics_fns.php');
|
||||
update_channels_total_stat();
|
||||
update_channels_active_halfyear_stat();
|
||||
update_channels_active_monthly_stat();
|
||||
update_local_posts_stat();
|
||||
|
||||
|
||||
// expire old delivery reports
|
||||
|
||||
$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",
|
||||
db_utcnow(),
|
||||
db_quoteinterval($keep_reports . ' DAY')
|
||||
);
|
||||
|
||||
// expire any expired accounts
|
||||
downgrade_accounts();
|
||||
|
||||
// If this is a directory server, request a sync with an upstream
|
||||
// directory at least once a day, up to once every poll interval.
|
||||
// Pull remote changes and push local changes.
|
||||
// potential issue: how do we keep from creating an endless update loop?
|
||||
|
||||
$dirmode = get_config('system','directory_mode');
|
||||
|
||||
if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
|
||||
require_once('include/dir_fns.php');
|
||||
sync_directories($dirmode);
|
||||
}
|
||||
|
||||
|
||||
Master::Summon(array('Expire'));
|
||||
Master::Summon(array('Cli_suggest'));
|
||||
|
||||
remove_obsolete_hublocs();
|
||||
|
||||
call_hooks('cron_daily',datetime_convert());
|
||||
|
||||
set_config('system','last_expire_day',$d2);
|
||||
|
||||
/**
|
||||
* End Cron Daily
|
||||
*/
|
||||
}
|
||||
}
|
||||
47
Zotlabs/Daemon/Cron_weekly.php
Normal file
47
Zotlabs/Daemon/Cron_weekly.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
class Cron_weekly {
|
||||
|
||||
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());
|
||||
|
||||
z_check_cert();
|
||||
|
||||
prune_hub_reinstalls();
|
||||
|
||||
mark_orphan_hubsxchans();
|
||||
|
||||
|
||||
// get rid of really old poco records
|
||||
|
||||
q("delete from xlink where xlink_updated < %s - INTERVAL %s and xlink_static = 0 ",
|
||||
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));
|
||||
}
|
||||
|
||||
// Check for dead sites
|
||||
Master::Summon(array('Checksites'));
|
||||
|
||||
// update searchable doc indexes
|
||||
Master::Summon(array('Importdoc'));
|
||||
|
||||
/**
|
||||
* End Cron Weekly
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
17
Zotlabs/Daemon/Cronhooks.php
Normal file
17
Zotlabs/Daemon/Cronhooks.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
class Cronhooks {
|
||||
|
||||
static public function run($argc,$argv){
|
||||
|
||||
logger('cronhooks: start');
|
||||
|
||||
$d = datetime_convert();
|
||||
|
||||
call_hooks('cron', $d);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
55
Zotlabs/Daemon/CurlAuth.php
Normal file
55
Zotlabs/Daemon/CurlAuth.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
// 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.
|
||||
|
||||
// Handles expiration of stale cookies currently by deleting them and rewriting the file.
|
||||
|
||||
class CurlAuth {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
if($argc != 2)
|
||||
killme();
|
||||
|
||||
\App::$session->start();
|
||||
|
||||
$_SESSION['authenticated'] = 1;
|
||||
$_SESSION['uid'] = $argv[1];
|
||||
|
||||
$x = session_id();
|
||||
|
||||
$f = 'store/[data]/cookie_' . $argv[1];
|
||||
$c = 'store/[data]/cookien_' . $argv[1];
|
||||
|
||||
$e = file_exists($f);
|
||||
|
||||
$output = '';
|
||||
|
||||
if($e) {
|
||||
$lines = file($f);
|
||||
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()) {
|
||||
$output .= $line . "\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
$output .= $line;
|
||||
}
|
||||
}
|
||||
}
|
||||
$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($c,$x);
|
||||
|
||||
killme();
|
||||
}
|
||||
}
|
||||
85
Zotlabs/Daemon/Deliver.php
Normal file
85
Zotlabs/Daemon/Deliver.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
require_once('include/queue_fn.php');
|
||||
|
||||
|
||||
class Deliver {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
if($argc < 2)
|
||||
return;
|
||||
|
||||
logger('deliver: invoked: ' . print_r($argv,true), LOGGER_DATA);
|
||||
|
||||
for($x = 1; $x < $argc; $x ++) {
|
||||
|
||||
if(! $argv[$x])
|
||||
continue;
|
||||
|
||||
$dresult = null;
|
||||
$r = q("select * from outq where outq_hash = '%s' limit 1",
|
||||
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)) {
|
||||
foreach($dresult as $xx) {
|
||||
if(is_array($xx) && array_key_exists('message_id',$xx)) {
|
||||
if(delivery_report_is_storable($xx)) {
|
||||
q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s','%s','%s','%s','%s' ) ",
|
||||
dbesc($xx['message_id']),
|
||||
dbesc($xx['location']),
|
||||
dbesc($xx['recipient']),
|
||||
dbesc($xx['status']),
|
||||
dbesc(datetime_convert($xx['date'])),
|
||||
dbesc($xx['sender'])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
q("delete from dreport where dreport_queue = '%s'",
|
||||
dbesc($argv[$x])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise it's a remote delivery - call queue_deliver() with the $immediate flag
|
||||
|
||||
queue_deliver($r[0],true);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Zotlabs/Daemon/Deliver_hooks.php
Normal file
24
Zotlabs/Daemon/Deliver_hooks.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
|
||||
class Deliver_hooks {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
if($argc < 2)
|
||||
return;
|
||||
|
||||
|
||||
$r = q("select * from item where id = '%d'",
|
||||
intval($argv[1])
|
||||
);
|
||||
if($r)
|
||||
call_hooks('notifier_normal',$r[0]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
100
Zotlabs/Daemon/Directory.php
Normal file
100
Zotlabs/Daemon/Directory.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
require_once('include/dir_fns.php');
|
||||
require_once('include/queue_fn.php');
|
||||
|
||||
|
||||
class Directory {
|
||||
|
||||
static public function run($argc,$argv){
|
||||
|
||||
if($argc < 2)
|
||||
return;
|
||||
|
||||
$force = false;
|
||||
$pushall = true;
|
||||
|
||||
if($argc > 2) {
|
||||
if($argv[2] === 'force')
|
||||
$force = true;
|
||||
if($argv[2] === 'nopush')
|
||||
$pushall = false;
|
||||
}
|
||||
|
||||
logger('directory update', LOGGER_DEBUG);
|
||||
|
||||
$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)
|
||||
return;
|
||||
|
||||
$channel = $x[0];
|
||||
|
||||
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);
|
||||
|
||||
q("update channel set channel_dirdate = '%s' where channel_id = %d",
|
||||
dbesc(datetime_convert()),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
|
||||
// Now update all the connections
|
||||
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';
|
||||
|
||||
// ensure the upstream directory is updated
|
||||
|
||||
$packet = zot_build_packet($channel,(($force) ? 'force_refresh' : 'refresh'));
|
||||
$z = zot_zot($url,$packet);
|
||||
|
||||
// re-queue if unsuccessful
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
$hash = random_string();
|
||||
|
||||
queue_insert(array(
|
||||
'hash' => $hash,
|
||||
'account_id' => $channel['channel_account_id'],
|
||||
'channel_id' => $channel['channel_id'],
|
||||
'posturl' => $url,
|
||||
'notify' => $packet,
|
||||
));
|
||||
|
||||
}
|
||||
else {
|
||||
q("update channel set channel_dirdate = '%s' where channel_id = %d",
|
||||
dbesc(datetime_convert()),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
}
|
||||
|
||||
// Now update all the connections
|
||||
if($pushall)
|
||||
Master::Summon(array('Notifier','refresh_all',$channel['channel_id']));
|
||||
|
||||
}
|
||||
}
|
||||
93
Zotlabs/Daemon/Expire.php
Normal file
93
Zotlabs/Daemon/Expire.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
|
||||
class Expire {
|
||||
|
||||
static public function run($argc,$argv){
|
||||
|
||||
cli_startup();
|
||||
|
||||
// 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')
|
||||
);
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
drop_item($rr['id'], false, DROPITEM_PHASE2);
|
||||
}
|
||||
}
|
||||
|
||||
// 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')
|
||||
);
|
||||
|
||||
/** @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);
|
||||
|
||||
$site_expire = get_config('system', 'default_expire_days');
|
||||
|
||||
logger('site_expire: ' . $site_expire);
|
||||
|
||||
$r = q("SELECT channel_id, channel_system, channel_address, channel_expire_days from channel where true");
|
||||
|
||||
if ($r) {
|
||||
foreach ($r as $rr) {
|
||||
|
||||
// expire the sys channel separately
|
||||
if (intval($rr['channel_system']))
|
||||
continue;
|
||||
|
||||
// service class default (if non-zero) over-rides the site default
|
||||
|
||||
$service_class_expire = service_class_fetch($rr['channel_id'], 'expire_days');
|
||||
if (intval($service_class_expire))
|
||||
$channel_expire = $service_class_expire;
|
||||
else
|
||||
$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 {
|
||||
$expire_days = $rr['channel_expire_days'];
|
||||
}
|
||||
|
||||
// if the site or service class expiration is non-zero and less than person expiration, use that
|
||||
logger('Expire: ' . $rr['channel_address'] . ' interval: ' . $expire_days, LOGGER_DEBUG);
|
||||
item_expire($rr['channel_id'], $expire_days);
|
||||
}
|
||||
}
|
||||
|
||||
$x = get_sys_channel();
|
||||
if ($x) {
|
||||
|
||||
// this should probably just fetch the channel_expire_days from the sys channel,
|
||||
// but there's no convenient way to set it.
|
||||
|
||||
$expire_days = get_config('system', 'sys_expire_days');
|
||||
if ($expire_days === false)
|
||||
$expire_days = 30;
|
||||
|
||||
if (intval($site_expire) && (intval($site_expire) < intval($expire_days))) {
|
||||
$expire_days = $site_expire;
|
||||
}
|
||||
|
||||
logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG);
|
||||
|
||||
if ($expire_days)
|
||||
item_expire($x['channel_id'], $expire_days);
|
||||
|
||||
logger('Expire: sys: done', LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
98
Zotlabs/Daemon/Externals.php
Normal file
98
Zotlabs/Daemon/Externals.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
require_once('include/channel.php');
|
||||
|
||||
|
||||
class Externals {
|
||||
|
||||
static public function run($argc,$argv){
|
||||
|
||||
$total = 0;
|
||||
$attempts = 0;
|
||||
|
||||
logger('externals: startup', LOGGER_DEBUG);
|
||||
|
||||
// pull in some public posts
|
||||
|
||||
|
||||
while($total == 0 && $attempts < 3) {
|
||||
$arr = array('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'] = '';
|
||||
$results = process_delivery(array('hash' => 'undefined'), get_item_elements($message),
|
||||
array(array('hash' => $sys['xchan_hash'])), false, true);
|
||||
$total ++;
|
||||
}
|
||||
logger('externals: import_public_posts: ' . $total . ' messages imported', LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
Zotlabs/Daemon/Gprobe.php
Normal file
33
Zotlabs/Daemon/Gprobe.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
|
||||
// performs zot_finger on $argv[1], which is a hex_encoded webbie/reddress
|
||||
|
||||
class Gprobe {
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
if($argc != 2)
|
||||
return;
|
||||
|
||||
$url = hex2bin($argv[1]);
|
||||
|
||||
if(! strpos($url,'@'))
|
||||
return;
|
||||
|
||||
$r = q("select * from xchan where xchan_addr = '%s' limit 1",
|
||||
dbesc($url)
|
||||
);
|
||||
|
||||
if(! $r) {
|
||||
$j = \Zotlabs\Zot\Finger::run($url,null);
|
||||
if($j['success']) {
|
||||
$y = import_xchan($j);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
35
Zotlabs/Daemon/Importdoc.php
Executable file
35
Zotlabs/Daemon/Importdoc.php
Executable file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
|
||||
class Importdoc {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
require_once('include/help.php');
|
||||
|
||||
self::update_docs_dir('doc/*');
|
||||
|
||||
}
|
||||
|
||||
static public function update_docs_dir($s) {
|
||||
$f = basename($s);
|
||||
$d = dirname($s);
|
||||
if($s === 'doc/html')
|
||||
return;
|
||||
$files = glob("$d/$f");
|
||||
if($files) {
|
||||
foreach($files as $fi) {
|
||||
if($fi === 'doc/html')
|
||||
continue;
|
||||
if(is_dir($fi))
|
||||
self::update_docs_dir("$fi/*");
|
||||
else
|
||||
store_doc_file($fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
30
Zotlabs/Daemon/Master.php
Normal file
30
Zotlabs/Daemon/Master.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Master {
|
||||
|
||||
static public function Summon($arr) {
|
||||
proc_run('php','Zotlabs/Daemon/Master.php',$arr);
|
||||
}
|
||||
|
||||
static public function Release($argc,$argv) {
|
||||
cli_startup();
|
||||
logger('Master: release: ' . print_r($argv,true), LOGGER_ALL,LOG_DEBUG);
|
||||
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
|
||||
$cls::run($argc,$argv);
|
||||
}
|
||||
}
|
||||
667
Zotlabs/Daemon/Notifier.php
Normal file
667
Zotlabs/Daemon/Notifier.php
Normal file
@@ -0,0 +1,667 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/queue_fn.php');
|
||||
require_once('include/html2plain.php');
|
||||
require_once('include/conversation.php');
|
||||
|
||||
/*
|
||||
* This file was at one time responsible for doing all deliveries, but this caused
|
||||
* big problems on shared hosting systems, where the process might get killed by the
|
||||
* hosting provider and nothing would get delivered.
|
||||
* It now only delivers one message under certain cases, and invokes a queued
|
||||
* delivery mechanism (include/deliver.php) to deliver individual contacts at
|
||||
* controlled intervals.
|
||||
* This has a much better chance of surviving random processes getting killed
|
||||
* by the hosting provider.
|
||||
*
|
||||
* The basic flow is:
|
||||
* Identify the type of message
|
||||
* Collect any information that needs to be sent
|
||||
* Convert it into a suitable generic format for sending
|
||||
* Figure out who the recipients are and if we need to relay
|
||||
* through a conversation owner
|
||||
* Once we know what recipients are involved, collect a list of
|
||||
* destination sites
|
||||
* Build and store a queue item for each unique site and invoke
|
||||
* a delivery process for each site or a small number of sites (1-3)
|
||||
* and add a slight delay between each delivery invocation if desired (usually)
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The notifier is typically called with:
|
||||
*
|
||||
* Zotlabs\Daemon\Master::Summon(array('Notifier', COMMAND, ITEM_ID));
|
||||
*
|
||||
* where COMMAND is one of the following:
|
||||
*
|
||||
* activity (in diaspora.php, dfrn_confirm.php, profiles.php)
|
||||
* comment-import (in diaspora.php, items.php)
|
||||
* comment-new (in item.php)
|
||||
* drop (in diaspora.php, items.php, photos.php)
|
||||
* edit_post (in item.php)
|
||||
* event (in events.php)
|
||||
* expire (in items.php)
|
||||
* like (in like.php, poke.php)
|
||||
* mail (in message.php)
|
||||
* tag (in photos.php, poke.php, tagger.php)
|
||||
* tgroup (in items.php)
|
||||
* wall-new (in photos.php, item.php)
|
||||
*
|
||||
* and ITEM_ID is the id of the item in the database that needs to be sent to others.
|
||||
*
|
||||
* ZOT
|
||||
* permission_create abook_id
|
||||
* permission_update abook_id
|
||||
* refresh_all channel_id
|
||||
* purge_all channel_id
|
||||
* expire channel_id
|
||||
* relay item_id (item was relayed to owner, we will deliver it as owner)
|
||||
* single_activity item_id (deliver to a singleton network from the appropriate clone)
|
||||
* single_mail mail_id (deliver to a singleton network from the appropriate clone)
|
||||
* location channel_id
|
||||
* request channel_id xchan_hash message_id
|
||||
* rating xlink_id
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
require_once('include/zot.php');
|
||||
require_once('include/queue_fn.php');
|
||||
require_once('include/datetime.php');
|
||||
require_once('include/items.php');
|
||||
require_once('include/bbcode.php');
|
||||
require_once('include/channel.php');
|
||||
|
||||
|
||||
class Notifier {
|
||||
|
||||
static public function run($argc,$argv){
|
||||
|
||||
if($argc < 3)
|
||||
return;
|
||||
|
||||
logger('notifier: invoked: ' . print_r($argv,true), LOGGER_DEBUG);
|
||||
|
||||
$cmd = $argv[1];
|
||||
|
||||
$item_id = $argv[2];
|
||||
|
||||
$extra = (($argc > 3) ? $argv[3] : null);
|
||||
|
||||
if(! $item_id)
|
||||
return;
|
||||
|
||||
$sys = get_sys_channel();
|
||||
|
||||
$deliveries = array();
|
||||
|
||||
$dead_hubs = array();
|
||||
|
||||
$dh = q("select site_url from site where site_dead = 1");
|
||||
if($dh) {
|
||||
foreach($dh as $dead) {
|
||||
$dead_hubs[] = $dead['site_url'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$request = false;
|
||||
$mail = false;
|
||||
$top_level = false;
|
||||
$location = false;
|
||||
$recipients = array();
|
||||
$url_recipients = array();
|
||||
$normal_mode = true;
|
||||
$packet_type = 'undefined';
|
||||
|
||||
if($cmd === 'mail' || $cmd === 'single_mail') {
|
||||
$normal_mode = false;
|
||||
$mail = true;
|
||||
$private = true;
|
||||
$message = q("SELECT * FROM mail WHERE id = %d LIMIT 1",
|
||||
intval($item_id)
|
||||
);
|
||||
if(! $message) {
|
||||
return;
|
||||
}
|
||||
xchan_mail_query($message[0]);
|
||||
$uid = $message[0]['channel_id'];
|
||||
$recipients[] = $message[0]['from_xchan']; // include clones
|
||||
$recipients[] = $message[0]['to_xchan'];
|
||||
$item = $message[0];
|
||||
|
||||
$encoded_item = encode_mail($item);
|
||||
|
||||
$s = q("select * from channel where channel_id = %d limit 1",
|
||||
intval($item['channel_id'])
|
||||
);
|
||||
if($s)
|
||||
$channel = $s[0];
|
||||
|
||||
}
|
||||
elseif($cmd === 'request') {
|
||||
$channel_id = $item_id;
|
||||
$xchan = $argv[3];
|
||||
$request_message_id = $argv[4];
|
||||
|
||||
$s = q("select * from channel where channel_id = %d limit 1",
|
||||
intval($channel_id)
|
||||
);
|
||||
if($s)
|
||||
$channel = $s[0];
|
||||
|
||||
$private = true;
|
||||
$recipients[] = $xchan;
|
||||
$packet_type = 'request';
|
||||
$normal_mode = false;
|
||||
}
|
||||
elseif($cmd == 'permission_update' || $cmd == 'permission_create') {
|
||||
// Get the (single) recipient
|
||||
$r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_self = 0",
|
||||
intval($item_id)
|
||||
);
|
||||
if($r) {
|
||||
$uid = $r[0]['abook_channel'];
|
||||
// Get the sender
|
||||
$channel = channelx_by_n($uid);
|
||||
if($channel) {
|
||||
$perm_update = array('sender' => $channel, 'recipient' => $r[0], 'success' => false, 'deliveries' => '');
|
||||
|
||||
if($cmd == 'permission_create')
|
||||
call_hooks('permissions_create',$perm_update);
|
||||
else
|
||||
call_hooks('permissions_update',$perm_update);
|
||||
|
||||
if($perm_update['success']) {
|
||||
if($perm_update['deliveries']) {
|
||||
$deliveries[] = $perm_update['deliveries'];
|
||||
do_delivery($deliveries);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
$recipients[] = $r[0]['abook_xchan'];
|
||||
$private = false;
|
||||
$packet_type = 'refresh';
|
||||
$packet_recips = array(array('guid' => $r[0]['xchan_guid'],'guid_sig' => $r[0]['xchan_guid_sig'],'hash' => $r[0]['xchan_hash']));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif($cmd === 'refresh_all') {
|
||||
logger('notifier: refresh_all: ' . $item_id);
|
||||
$uid = $item_id;
|
||||
$channel = channelx_by_n($item_id);
|
||||
$r = q("select abook_xchan from abook where abook_channel = %d",
|
||||
intval($item_id)
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$recipients[] = $rr['abook_xchan'];
|
||||
}
|
||||
}
|
||||
$private = false;
|
||||
$packet_type = 'refresh';
|
||||
}
|
||||
elseif($cmd === 'location') {
|
||||
logger('notifier: location: ' . $item_id);
|
||||
$s = q("select * from channel where channel_id = %d limit 1",
|
||||
intval($item_id)
|
||||
);
|
||||
if($s)
|
||||
$channel = $s[0];
|
||||
$uid = $item_id;
|
||||
$recipients = array();
|
||||
$r = q("select abook_xchan from abook where abook_channel = %d",
|
||||
intval($item_id)
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$recipients[] = $rr['abook_xchan'];
|
||||
}
|
||||
}
|
||||
|
||||
$encoded_item = array('locations' => zot_encode_locations($channel),'type' => 'location', 'encoding' => 'zot');
|
||||
$target_item = array('aid' => $channel['channel_account_id'],'uid' => $channel['channel_id']);
|
||||
$private = false;
|
||||
$packet_type = 'location';
|
||||
$location = true;
|
||||
}
|
||||
elseif($cmd === 'purge_all') {
|
||||
logger('notifier: purge_all: ' . $item_id);
|
||||
$s = q("select * from channel where channel_id = %d limit 1",
|
||||
intval($item_id)
|
||||
);
|
||||
if($s)
|
||||
$channel = $s[0];
|
||||
$uid = $item_id;
|
||||
$recipients = array();
|
||||
$r = q("select abook_xchan from abook where abook_channel = %d and abook_self = 0",
|
||||
intval($item_id)
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$recipients[] = $rr['abook_xchan'];
|
||||
}
|
||||
}
|
||||
$private = false;
|
||||
$packet_type = 'purge';
|
||||
}
|
||||
else {
|
||||
|
||||
// Normal items
|
||||
|
||||
// Fetch the target item
|
||||
|
||||
$r = q("SELECT * FROM item WHERE id = %d and parent != 0 LIMIT 1",
|
||||
intval($item_id)
|
||||
);
|
||||
|
||||
if(! $r)
|
||||
return;
|
||||
|
||||
xchan_query($r);
|
||||
|
||||
$r = fetch_post_tags($r);
|
||||
|
||||
$target_item = $r[0];
|
||||
$deleted_item = false;
|
||||
|
||||
if(intval($target_item['item_deleted'])) {
|
||||
logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG);
|
||||
$deleted_item = true;
|
||||
}
|
||||
|
||||
if(intval($target_item['item_type']) != ITEM_TYPE_POST) {
|
||||
logger('notifier: target item not forwardable: type ' . $target_item['item_type'], LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for non published items, but allow an exclusion for transmitting hidden file activities
|
||||
|
||||
if(intval($target_item['item_unpublished']) || intval($target_item['item_delayed']) ||
|
||||
( intval($target_item['item_hidden']) && ($target_item['obj_type'] !== ACTIVITY_OBJ_FILE))) {
|
||||
logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
if(strpos($target_item['postopts'],'nodeliver') !== false) {
|
||||
logger('notifier: target item is undeliverable', LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
$s = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1",
|
||||
intval($target_item['uid'])
|
||||
);
|
||||
if($s)
|
||||
$channel = $s[0];
|
||||
|
||||
if($channel['channel_hash'] !== $target_item['author_xchan'] && $channel['channel_hash'] !== $target_item['owner_xchan']) {
|
||||
logger("notifier: Sending channel {$channel['channel_hash']} is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}", LOGGER_NORMAL, LOG_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if($target_item['id'] == $target_item['parent']) {
|
||||
$parent_item = $target_item;
|
||||
$top_level_post = true;
|
||||
}
|
||||
else {
|
||||
// fetch the parent item
|
||||
$r = q("SELECT * from item where id = %d order by id asc",
|
||||
intval($target_item['parent'])
|
||||
);
|
||||
|
||||
if(! $r)
|
||||
return;
|
||||
|
||||
if(strpos($r[0]['postopts'],'nodeliver') !== false) {
|
||||
logger('notifier: target item is undeliverable', LOGGER_DEBUG, LOG_NOTICE);
|
||||
return;
|
||||
}
|
||||
|
||||
xchan_query($r);
|
||||
$r = fetch_post_tags($r);
|
||||
|
||||
$parent_item = $r[0];
|
||||
$top_level_post = false;
|
||||
}
|
||||
|
||||
// avoid looping of discover items 12/4/2014
|
||||
|
||||
if($sys && $parent_item['uid'] == $sys['channel_id'])
|
||||
return;
|
||||
|
||||
$encoded_item = encode_item($target_item);
|
||||
|
||||
// Send comments to the owner to re-deliver to everybody in the conversation
|
||||
// We only do this if the item in question originated on this site. This prevents looping.
|
||||
// To clarify, a site accepting a new comment is responsible for sending it to the owner for relay.
|
||||
// Relaying should never be initiated on a post that arrived from elsewhere.
|
||||
|
||||
// We should normally be able to rely on ITEM_ORIGIN, but start_delivery_chain() incorrectly set this
|
||||
// flag on comments for an extended period. So we'll also call comment_local_origin() which looks at
|
||||
// the hostname in the message_id and provides a second (fallback) opinion.
|
||||
|
||||
$relay_to_owner = (((! $top_level_post) && (intval($target_item['item_origin'])) && comment_local_origin($target_item)) ? true : false);
|
||||
|
||||
|
||||
|
||||
$uplink = false;
|
||||
|
||||
// $cmd === 'relay' indicates the owner is sending it to the original recipients
|
||||
// don't allow the item in the relay command to relay to owner under any circumstances, it will loop
|
||||
|
||||
logger('notifier: relay_to_owner: ' . (($relay_to_owner) ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG);
|
||||
logger('notifier: top_level_post: ' . (($top_level_post) ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG);
|
||||
|
||||
// tag_deliver'd post which needs to be sent back to the original author
|
||||
|
||||
if(($cmd === 'uplink') && intval($parent_item['item_uplink']) && (! $top_level_post)) {
|
||||
logger('notifier: uplink');
|
||||
$uplink = true;
|
||||
}
|
||||
|
||||
if(($relay_to_owner || $uplink) && ($cmd !== 'relay')) {
|
||||
logger('notifier: followup relay', LOGGER_DEBUG);
|
||||
$recipients = array(($uplink) ? $parent_item['source_xchan'] : $parent_item['owner_xchan']);
|
||||
$private = true;
|
||||
if(! $encoded_item['flags'])
|
||||
$encoded_item['flags'] = array();
|
||||
$encoded_item['flags'][] = 'relay';
|
||||
$upstream = true;
|
||||
}
|
||||
else {
|
||||
logger('notifier: normal distribution', LOGGER_DEBUG);
|
||||
if($cmd === 'relay')
|
||||
logger('notifier: owner relay');
|
||||
$upstream = false;
|
||||
// if our parent is a tag_delivery recipient, uplink to the original author causing
|
||||
// a delivery fork.
|
||||
|
||||
if(($parent_item) && intval($parent_item['item_uplink']) && (! $top_level_post) && ($cmd !== 'uplink')) {
|
||||
// don't uplink a relayed post to the relay owner
|
||||
if($parent_item['source_xchan'] !== $parent_item['owner_xchan']) {
|
||||
logger('notifier: uplinking this item');
|
||||
Master::Summon(array('Notifier','uplink',$item_id));
|
||||
}
|
||||
}
|
||||
|
||||
$private = false;
|
||||
$recipients = collect_recipients($parent_item,$private);
|
||||
|
||||
// FIXME add any additional recipients such as mentions, etc.
|
||||
|
||||
// don't send deletions onward for other people's stuff
|
||||
// TODO verify this is needed - copied logic from same place in old code
|
||||
|
||||
if(intval($target_item['item_deleted']) && (! intval($target_item['item_wall']))) {
|
||||
logger('notifier: ignoring delete notification for non-wall item', LOGGER_NORMAL, LOG_NOTICE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$walltowall = (($top_level_post && $channel['xchan_hash'] === $target_item['author_xchan']) ? true : false);
|
||||
|
||||
// Generic delivery section, we have an encoded item and recipients
|
||||
// Now start the delivery process
|
||||
|
||||
$x = $encoded_item;
|
||||
$x['title'] = 'private';
|
||||
$x['body'] = 'private';
|
||||
logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG);
|
||||
|
||||
stringify_array_elms($recipients);
|
||||
if(! $recipients)
|
||||
return;
|
||||
|
||||
// logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG);
|
||||
|
||||
$env_recips = (($private) ? array() : null);
|
||||
|
||||
$details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',',$recipients) . ")");
|
||||
|
||||
|
||||
$recip_list = array();
|
||||
|
||||
if($details) {
|
||||
foreach($details as $d) {
|
||||
|
||||
$recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')';
|
||||
if($private)
|
||||
$env_recips[] = array('guid' => $d['xchan_guid'],'guid_sig' => $d['xchan_guid_sig'],'hash' => $d['xchan_hash']);
|
||||
|
||||
if($d['xchan_network'] === 'mail' && $normal_mode) {
|
||||
$delivery_options = get_xconfig($d['xchan_hash'],'system','delivery_mode');
|
||||
if(! $delivery_options)
|
||||
format_and_send_email($channel,$d,$target_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$narr = array(
|
||||
'channel' => $channel,
|
||||
'upstream' => $upstream,
|
||||
'env_recips' => $env_recips,
|
||||
'packet_recips' => $packet_recips,
|
||||
'recipients' => $recipients,
|
||||
'item' => $item,
|
||||
'target_item' => $target_item,
|
||||
'top_level_post' => $top_level_post,
|
||||
'private' => $private,
|
||||
'relay_to_owner' => $relay_to_owner,
|
||||
'uplink' => $uplink,
|
||||
'cmd' => $cmd,
|
||||
'mail' => $mail,
|
||||
'single' => (($cmd === 'single_mail' || $cmd === 'single_activity') ? true : false),
|
||||
'location' => $location,
|
||||
'request' => $request,
|
||||
'normal_mode' => $normal_mode,
|
||||
'packet_type' => $packet_type,
|
||||
'walltowall' => $walltowall,
|
||||
'queued' => array()
|
||||
);
|
||||
|
||||
call_hooks('notifier_process', $narr);
|
||||
if($narr['queued']) {
|
||||
foreach($narr['queued'] as $pq)
|
||||
$deliveries[] = $pq;
|
||||
}
|
||||
|
||||
// notifier_process can alter the recipient list
|
||||
|
||||
$recipients = $narr['recipients'];
|
||||
$env_recips = $narr['env_recips'];
|
||||
$packet_recips = $narr['packet_recips'];
|
||||
|
||||
if(($private) && (! $env_recips)) {
|
||||
// shouldn't happen
|
||||
logger('notifier: private message with no envelope recipients.' . print_r($argv,true), LOGGER_NORMAL, LOG_NOTICE);
|
||||
}
|
||||
|
||||
logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list,true), LOGGER_DEBUG);
|
||||
|
||||
|
||||
// Now we have collected recipients (except for external mentions, FIXME)
|
||||
// Let's reduce this to a set of hubs.
|
||||
|
||||
$r = q("select hubloc.*, site.site_crypto from hubloc left join site on site_url = hubloc_url where hubloc_hash in (" . implode(',',$recipients) . ")
|
||||
and hubloc_error = 0 and hubloc_deleted = 0"
|
||||
);
|
||||
|
||||
|
||||
if(! $r) {
|
||||
logger('notifier: no hubs', LOGGER_NORMAL, LOG_NOTICE);
|
||||
return;
|
||||
}
|
||||
|
||||
$hubs = $r;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, since it may have been
|
||||
* a re-install which has not yet been detected and pruned.
|
||||
* For other networks which don't have or require sitekeys, we'll have to use the URL
|
||||
*/
|
||||
|
||||
|
||||
$hublist = array(); // this provides an easily printable list for the logs
|
||||
$dhubs = array(); // delivery hubs where we store our resulting unique array
|
||||
$keys = array(); // array of keys to check uniquness for zot hubs
|
||||
$urls = array(); // array of urls to check uniqueness of hubs from other networks
|
||||
|
||||
|
||||
foreach($hubs as $hub) {
|
||||
if(in_array($hub['hubloc_url'],$dead_hubs)) {
|
||||
logger('skipping dead hub: ' . $hub['hubloc_url'], LOGGER_DEBUG, LOG_INFO);
|
||||
continue;
|
||||
}
|
||||
|
||||
if($hub['hubloc_network'] == 'zot') {
|
||||
if(! in_array($hub['hubloc_sitekey'],$keys)) {
|
||||
$hublist[] = $hub['hubloc_host'];
|
||||
$dhubs[] = $hub;
|
||||
$keys[] = $hub['hubloc_sitekey'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(! in_array($hub['hubloc_url'],$urls)) {
|
||||
$hublist[] = $hub['hubloc_host'];
|
||||
$dhubs[] = $hub;
|
||||
$urls[] = $hub['hubloc_url'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist,true), LOGGER_DEBUG, LOG_DEBUG);
|
||||
|
||||
|
||||
foreach($dhubs as $hub) {
|
||||
|
||||
if($hub['hubloc_network'] !== 'zot') {
|
||||
|
||||
$narr = array(
|
||||
'channel' => $channel,
|
||||
'upstream' => $upstream,
|
||||
'env_recips' => $env_recips,
|
||||
'packet_recips' => $packet_recips,
|
||||
'recipients' => $recipients,
|
||||
'item' => $item,
|
||||
'target_item' => $target_item,
|
||||
'hub' => $hub,
|
||||
'top_level_post' => $top_level_post,
|
||||
'private' => $private,
|
||||
'relay_to_owner' => $relay_to_owner,
|
||||
'uplink' => $uplink,
|
||||
'cmd' => $cmd,
|
||||
'mail' => $mail,
|
||||
'single' => (($cmd === 'single_mail' || $cmd === 'single_activity') ? true : false),
|
||||
'location' => $location,
|
||||
'request' => $request,
|
||||
'normal_mode' => $normal_mode,
|
||||
'packet_type' => $packet_type,
|
||||
'walltowall' => $walltowall,
|
||||
'queued' => array()
|
||||
);
|
||||
|
||||
|
||||
call_hooks('notifier_hub',$narr);
|
||||
if($narr['queued']) {
|
||||
foreach($narr['queued'] as $pq)
|
||||
$deliveries[] = $pq;
|
||||
}
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
// singleton deliveries by definition 'not got zot'.
|
||||
// Single deliveries are other federated networks (plugins) and we're essentially
|
||||
// delivering only to those that have this site url in their abook_instance
|
||||
// and only from within a sync operation. This means if you post from a clone,
|
||||
// and a connection is connected to one of your other clones; assuming that hub
|
||||
// is running it will receive a sync packet. On receipt of this sync packet it
|
||||
// will invoke a delivery to those connections which are connected to just that
|
||||
// hub instance.
|
||||
|
||||
if($cmd === 'single_mail' || $cmd === 'single_activity') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// default: zot protocol
|
||||
|
||||
$hash = random_string();
|
||||
$packet = null;
|
||||
|
||||
if($packet_type === 'refresh' || $packet_type === 'purge') {
|
||||
$packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null));
|
||||
}
|
||||
elseif($packet_type === 'request') {
|
||||
$packet = zot_build_packet($channel,$packet_type,$env_recips,$hub['hubloc_sitekey'],$hub['site_crypto'],
|
||||
$hash, array('message_id' => $request_message_id)
|
||||
);
|
||||
}
|
||||
|
||||
if($packet) {
|
||||
queue_insert(array(
|
||||
'hash' => $hash,
|
||||
'account_id' => $channel['channel_account_id'],
|
||||
'channel_id' => $channel['channel_id'],
|
||||
'posturl' => $hub['hubloc_callback'],
|
||||
'notify' => $packet
|
||||
));
|
||||
}
|
||||
else {
|
||||
$packet = zot_build_packet($channel,'notify',$env_recips,(($private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto'],$hash);
|
||||
queue_insert(array(
|
||||
'hash' => $hash,
|
||||
'account_id' => $target_item['aid'],
|
||||
'channel_id' => $target_item['uid'],
|
||||
'posturl' => $hub['hubloc_callback'],
|
||||
'notify' => $packet,
|
||||
'msg' => json_encode($encoded_item)
|
||||
));
|
||||
|
||||
// only create delivery reports for normal undeleted items
|
||||
if(is_array($target_item) && array_key_exists('postopts',$target_item) && (! $target_item['item_deleted']) && (! get_config('system','disable_dreport'))) {
|
||||
q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan, dreport_queue ) values ( '%s','%s','%s','%s','%s','%s','%s' ) ",
|
||||
dbesc($target_item['mid']),
|
||||
dbesc($hub['hubloc_host']),
|
||||
dbesc($hub['hubloc_host']),
|
||||
dbesc('queued'),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($channel['channel_hash']),
|
||||
dbesc($hash)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$deliveries[] = $hash;
|
||||
}
|
||||
|
||||
if($normal_mode) {
|
||||
$x = q("select * from hook where hook = 'notifier_normal'");
|
||||
if($x)
|
||||
Master::Summon(array('Deliver_hooks',$target_item['id']));
|
||||
|
||||
}
|
||||
|
||||
if($deliveries)
|
||||
do_delivery($deliveries);
|
||||
|
||||
logger('notifier: basic loop complete.', LOGGER_DEBUG);
|
||||
|
||||
call_hooks('notifier_end',$target_item);
|
||||
|
||||
logger('notifer: complete.');
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
76
Zotlabs/Daemon/Onedirsync.php
Normal file
76
Zotlabs/Daemon/Onedirsync.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
require_once('include/dir_fns.php');
|
||||
|
||||
|
||||
class Onedirsync {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
logger('onedirsync: start ' . intval($argv[1]));
|
||||
|
||||
if(($argc > 1) && (intval($argv[1])))
|
||||
$update_id = intval($argv[1]);
|
||||
|
||||
if(! $update_id) {
|
||||
logger('onedirsync: no update');
|
||||
return;
|
||||
}
|
||||
|
||||
$r = q("select * from updates where ud_id = %d limit 1",
|
||||
intval($update_id)
|
||||
);
|
||||
|
||||
if(! $r)
|
||||
return;
|
||||
if(($r[0]['ud_flags'] & UPDATE_FLAGS_UPDATED) || (! $r[0]['ud_addr']))
|
||||
return;
|
||||
|
||||
// Have we probed this channel more recently than the other directory server
|
||||
// (where we received this update from) ?
|
||||
// If we have, we don't need to do anything except mark any older entries updated
|
||||
|
||||
$x = q("select * from updates where ud_addr = '%s' and ud_date > '%s' and ( ud_flags & %d )>0 order by ud_date desc limit 1",
|
||||
dbesc($r[0]['ud_addr']),
|
||||
dbesc($r[0]['ud_date']),
|
||||
intval(UPDATE_FLAGS_UPDATED)
|
||||
);
|
||||
if($x) {
|
||||
$y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 and ud_date != '%s'",
|
||||
intval(UPDATE_FLAGS_UPDATED),
|
||||
dbesc($r[0]['ud_addr']),
|
||||
intval(UPDATE_FLAGS_UPDATED),
|
||||
dbesc($x[0]['ud_date'])
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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",
|
||||
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 ",
|
||||
intval(UPDATE_FLAGS_UPDATED),
|
||||
dbesc($r[0]['ud_addr']),
|
||||
intval(UPDATE_FLAGS_UPDATED)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// we might have to pull this out some day, but for now update_directory_entry()
|
||||
// runs zot_finger() and is kind of zot specific
|
||||
|
||||
if($h && $h[0]['hubloc_network'] !== 'zot')
|
||||
return;
|
||||
|
||||
update_directory_entry($r[0]);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
163
Zotlabs/Daemon/Onepoll.php
Normal file
163
Zotlabs/Daemon/Onepoll.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
require_once('include/socgraph.php');
|
||||
|
||||
|
||||
class Onepoll {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
logger('onepoll: start');
|
||||
|
||||
if(($argc > 1) && (intval($argv[1])))
|
||||
$contact_id = intval($argv[1]);
|
||||
|
||||
if(! $contact_id) {
|
||||
logger('onepoll: no contact');
|
||||
return;
|
||||
}
|
||||
|
||||
$d = datetime_convert();
|
||||
|
||||
$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
|
||||
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) {
|
||||
logger('onepoll: abook_id not found: ' . $contact_id);
|
||||
return;
|
||||
}
|
||||
|
||||
$contact = $contacts[0];
|
||||
|
||||
$t = $contact['abook_updated'];
|
||||
|
||||
$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)
|
||||
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')
|
||||
);
|
||||
|
||||
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'])
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if($contact['xchan_network'] !== 'zot')
|
||||
return;
|
||||
|
||||
// update permissions
|
||||
|
||||
$x = zot_refresh($contact,$importer);
|
||||
|
||||
$responded = false;
|
||||
$updated = datetime_convert();
|
||||
$connected = datetime_convert();
|
||||
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),
|
||||
intval($contact['abook_id'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
q("update abook set abook_updated = '%s', abook_connected = '%s' where abook_id = %d",
|
||||
dbesc($updated),
|
||||
dbesc($connected),
|
||||
intval($contact['abook_id'])
|
||||
);
|
||||
$responded = true;
|
||||
}
|
||||
|
||||
if(! $responded)
|
||||
return;
|
||||
|
||||
if($contact['xchan_connurl']) {
|
||||
$fetch_feed = true;
|
||||
$x = null;
|
||||
|
||||
// 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'));
|
||||
|
||||
if(! $can_view_stream)
|
||||
$fetch_feed = false;
|
||||
|
||||
// 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;
|
||||
|
||||
if($fetch_feed) {
|
||||
|
||||
$feedurl = str_replace('/poco/','/zotfeed/',$contact['xchan_connurl']);
|
||||
$feedurl .= '?f=&mindate=' . urlencode($last_update);
|
||||
|
||||
$x = z_fetch_url($feedurl);
|
||||
|
||||
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 ++;
|
||||
}
|
||||
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']);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
202
Zotlabs/Daemon/Poller.php
Normal file
202
Zotlabs/Daemon/Poller.php
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
class Poller {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
$maxsysload = intval(get_config('system','maxloadavg'));
|
||||
if($maxsysload < 1)
|
||||
$maxsysload = 50;
|
||||
if(function_exists('sys_getloadavg')) {
|
||||
$load = sys_getloadavg();
|
||||
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')));
|
||||
|
||||
// Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it.
|
||||
$lockfile = 'store/[data]/poller';
|
||||
if((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600))
|
||||
&& (! get_config('system','override_poll_lockfile'))) {
|
||||
logger("poller: Already running");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a lockfile. Needs two vars, but $x doesn't need to contain anything.
|
||||
file_put_contents($lockfile, $x);
|
||||
|
||||
logger('poller: start');
|
||||
|
||||
$manual_id = 0;
|
||||
$generation = 0;
|
||||
|
||||
$force = false;
|
||||
$restart = false;
|
||||
|
||||
if(($argc > 1) && ($argv[1] == 'force'))
|
||||
$force = true;
|
||||
|
||||
if(($argc > 1) && ($argv[1] == 'restart')) {
|
||||
$restart = true;
|
||||
$generation = intval($argv[2]);
|
||||
if(! $generation)
|
||||
killme();
|
||||
}
|
||||
|
||||
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'))
|
||||
: ''
|
||||
);
|
||||
|
||||
$randfunc = db_getfunc('RAND');
|
||||
|
||||
$contacts = q("SELECT * FROM abook LEFT JOIN xchan on abook_xchan = xchan_hash
|
||||
LEFT JOIN account on abook_account = account_id
|
||||
where abook_self = 0
|
||||
$sql_extra
|
||||
AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY $randfunc",
|
||||
intval(ACCOUNT_OK),
|
||||
intval(ACCOUNT_UNVERIFIED) // FIXME
|
||||
|
||||
);
|
||||
|
||||
if($contacts) {
|
||||
|
||||
foreach($contacts as $contact) {
|
||||
|
||||
$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)
|
||||
$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);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if($contact['xchan_network'] !== 'zot')
|
||||
continue;
|
||||
|
||||
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",
|
||||
dbesc(datetime_convert()),
|
||||
intval($contact['abook_id'])
|
||||
);
|
||||
$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",
|
||||
intval($contact['abook_id'])
|
||||
);
|
||||
$update = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(intval($contact['abook_archived'])) {
|
||||
$update = false;
|
||||
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))
|
||||
$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) {
|
||||
$update = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(intval($contact['abook_pending']) || intval($contact['abook_archived']) || intval($contact['abook_ignored']) || intval($contact['abook_blocked']))
|
||||
continue;
|
||||
|
||||
if((! $update) && (! $force))
|
||||
continue;
|
||||
|
||||
Master::Summon(array('Onepoll',$contact['abook_id']));
|
||||
if($interval)
|
||||
@time_sleep_until(microtime(true) + (float) $interval);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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 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'))
|
||||
continue;
|
||||
Master::Summon(array('Onedirsync',$rr['ud_id']));
|
||||
if($interval)
|
||||
@time_sleep_until(microtime(true) + (float) $interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_config('system','lastpoll',datetime_convert());
|
||||
|
||||
//All done - clear the lockfile
|
||||
@unlink($lockfile);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
75
Zotlabs/Daemon/Queue.php
Normal file
75
Zotlabs/Daemon/Queue.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/queue_fn.php');
|
||||
require_once('include/zot.php');
|
||||
|
||||
class Queue {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
require_once('include/items.php');
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
if(argc() > 1)
|
||||
$queue_id = argv(1);
|
||||
else
|
||||
$queue_id = 0;
|
||||
|
||||
logger('queue: start');
|
||||
|
||||
// delete all queue items more than 3 days old
|
||||
// but first mark these sites dead if we haven't heard from them in a month
|
||||
|
||||
$r = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s",
|
||||
db_utcnow(), db_quoteinterval('3 DAY')
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$site_url = '';
|
||||
$h = parse_url($rr['outq_posturl']);
|
||||
$desturl = $h['scheme'] . '://' . $h['host'] . (($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')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$r = q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s",
|
||||
db_utcnow(), db_quoteinterval('3 DAY')
|
||||
);
|
||||
|
||||
if($queue_id) {
|
||||
$r = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1",
|
||||
dbesc($queue_id)
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
// For the first 12 hours we'll try to deliver every 15 minutes
|
||||
// After that, we'll only attempt delivery once per hour.
|
||||
// This currently only handles the default queue drivers ('zot' or '') which we will group by posturl
|
||||
// so that we don't start off a thousand deliveries for a couple of dead hubs.
|
||||
// The zot driver will deliver everything destined for a single hub once contact is made (*if* contact is made).
|
||||
// Other drivers will have to do something different here and may need their own query.
|
||||
|
||||
// Note: this requires some tweaking as new posts to long dead hubs once a day will keep them in the
|
||||
// "every 15 minutes" category. We probably need to prioritise them when inserted into the queue
|
||||
// or just prior to this query based on recent and long-term delivery history. If we have good reason to believe
|
||||
// the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once
|
||||
// or twice a day.
|
||||
|
||||
$r = q("SELECT * FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s ",
|
||||
db_utcnow()
|
||||
);
|
||||
}
|
||||
if(! $r)
|
||||
return;
|
||||
|
||||
foreach($r as $rv) {
|
||||
queue_deliver($rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Zotlabs/Daemon/README.md
Normal file
43
Zotlabs/Daemon/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
Daemon (background) Processes
|
||||
=============================
|
||||
|
||||
|
||||
This directory provides background tasks which are executed by a
|
||||
command-line process and detached from normal web processing.
|
||||
|
||||
Background tasks are invoked by calling
|
||||
|
||||
|
||||
Zotlabs\Daemon\Master::Summon([ $cmd, $arg1, $argn... ]);
|
||||
|
||||
The Master class loads the desired command file and passes the arguments.
|
||||
|
||||
|
||||
To create a background task 'Foo' use the following template.
|
||||
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
class Foo {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
// do something
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
The Master class "summons" the command by creating an executable script
|
||||
from the provided arguments, then it invokes "Release" to execute the script
|
||||
detached from web processing. This process calls the static::run() function
|
||||
with any command line arguments using the traditional argc, argv format.
|
||||
|
||||
Please note: These are *real* $argc, $argv variables passed from the command
|
||||
line, and not the parsed argc() and argv() functions/variables which were
|
||||
obtained from parsing path components of the request URL by web processes.
|
||||
|
||||
Background processes do not emit displayable output except through logs. They
|
||||
should also not make any assumptions about their HTML and web environment
|
||||
(as they do not have a web environment), particularly with respect to global
|
||||
variables such as $_SERVER, $_REQUEST, $_GET, $_POST, $_COOKIES, and $_SESSION.
|
||||
|
||||
113
Zotlabs/Daemon/Ratenotif.php
Normal file
113
Zotlabs/Daemon/Ratenotif.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
require_once('include/zot.php');
|
||||
require_once('include/queue_fn.php');
|
||||
|
||||
|
||||
class Ratenotif {
|
||||
|
||||
static public function run($argc,$argv) {
|
||||
|
||||
require_once("datetime.php");
|
||||
require_once('include/items.php');
|
||||
|
||||
if($argc < 3)
|
||||
return;
|
||||
|
||||
|
||||
logger('ratenotif: invoked: ' . print_r($argv,true), LOGGER_DEBUG);
|
||||
|
||||
$cmd = $argv[1];
|
||||
|
||||
$item_id = $argv[2];
|
||||
|
||||
|
||||
if($cmd === 'rating') {
|
||||
$r = q("select * from xlink where xlink_id = %d and xlink_static = 1 limit 1",
|
||||
intval($item_id)
|
||||
);
|
||||
if(! $r) {
|
||||
logger('rating not found');
|
||||
return;
|
||||
}
|
||||
|
||||
$encoded_item = array(
|
||||
'type' => 'rating',
|
||||
'encoding' => 'zot',
|
||||
'target' => $r[0]['xlink_link'],
|
||||
'rating' => intval($r[0]['xlink_rating']),
|
||||
'rating_text' => $r[0]['xlink_rating_text'],
|
||||
'signature' => $r[0]['xlink_sig'],
|
||||
'edited' => $r[0]['xlink_updated']
|
||||
);
|
||||
}
|
||||
|
||||
$channel = channelx_by_hash($r[0]['xlink_xchan']);
|
||||
if(! $channel) {
|
||||
logger('no channel');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$primary = get_directory_primary();
|
||||
|
||||
if(! $primary)
|
||||
return;
|
||||
|
||||
|
||||
$interval = ((get_config('system','delivery_interval') !== false)
|
||||
? intval(get_config('system','delivery_interval')) : 2 );
|
||||
|
||||
$deliveries_per_process = intval(get_config('system','delivery_batch_count'));
|
||||
|
||||
if($deliveries_per_process <= 0)
|
||||
$deliveries_per_process = 1;
|
||||
|
||||
$deliver = array();
|
||||
|
||||
$x = z_fetch_url($primary . '/regdir');
|
||||
if($x['success']) {
|
||||
$j = json_decode($x['body'],true);
|
||||
if($j && $j['success'] && is_array($j['directories'])) {
|
||||
|
||||
foreach($j['directories'] as $h) {
|
||||
if($h == z_root())
|
||||
continue;
|
||||
|
||||
$hash = random_string();
|
||||
$n = zot_build_packet($channel,'notify',null,null,'',$hash);
|
||||
|
||||
queue_insert(array(
|
||||
'hash' => $hash,
|
||||
'account_id' => $channel['channel_account_id'],
|
||||
'channel_id' => $channel['channel_id'],
|
||||
'posturl' => $h . '/post',
|
||||
'notify' => $n,
|
||||
'msg' => json_encode($encoded_item)
|
||||
));
|
||||
|
||||
$deliver[] = $hash;
|
||||
|
||||
if(count($deliver) >= $deliveries_per_process) {
|
||||
Master::Summon(array('Deliver',$deliver));
|
||||
$deliver = array();
|
||||
if($interval)
|
||||
@time_sleep_until(microtime(true) + (float) $interval);
|
||||
}
|
||||
}
|
||||
|
||||
// catch any stragglers
|
||||
|
||||
if(count($deliver)) {
|
||||
Master::Summon(array('Deliver',$deliver));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger('ratenotif: complete.');
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
107
Zotlabs/Extend/Hook.php
Normal file
107
Zotlabs/Extend/Hook.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Extend;
|
||||
|
||||
|
||||
class Hook {
|
||||
|
||||
static public function register($hook,$file,$function,$version = 1,$priority = 0) {
|
||||
if(is_array($function)) {
|
||||
$function = serialize($function);
|
||||
}
|
||||
|
||||
$r = q("SELECT * FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s' and priority = %d and hook_version = %d LIMIT 1",
|
||||
dbesc($hook),
|
||||
dbesc($file),
|
||||
dbesc($function),
|
||||
intval($priority),
|
||||
intval($version)
|
||||
);
|
||||
if($r)
|
||||
return true;
|
||||
|
||||
// To aid in upgrade and transition, remove old settings for any registered hooks that match in all respects except
|
||||
// for priority or hook_version
|
||||
|
||||
$r = q("DELETE FROM hook where hook = '%s' and file = '%s' and fn = '%s'",
|
||||
dbesc($hook),
|
||||
dbesc($file),
|
||||
dbesc($function)
|
||||
);
|
||||
|
||||
$r = q("INSERT INTO hook (hook, file, fn, priority, hook_version) VALUES ( '%s', '%s', '%s', %d, %d )",
|
||||
dbesc($hook),
|
||||
dbesc($file),
|
||||
dbesc($function),
|
||||
intval($priority),
|
||||
intval($version)
|
||||
);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
static public function unregister($hook,$file,$function,$version = 1,$priority = 0) {
|
||||
if(is_array($function)) {
|
||||
$function = serialize($function);
|
||||
}
|
||||
$r = q("DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s' and priority = %d and hook_version = %d",
|
||||
dbesc($hook),
|
||||
dbesc($file),
|
||||
dbesc($function),
|
||||
intval($priority),
|
||||
intval($version)
|
||||
);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
// unregister all hooks with this file component.
|
||||
// Useful for addon upgrades where you want to clean out old interfaces.
|
||||
|
||||
static public function unregister_by_file($file) {
|
||||
|
||||
$r = q("DELETE FROM hook WHERE file = '%s' ",
|
||||
dbesc($file)
|
||||
);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Inserts a hook into a page request.
|
||||
*
|
||||
* Insert a short-lived hook into the running page request.
|
||||
* Hooks are normally persistent so that they can be called
|
||||
* across asynchronous processes such as delivery and poll
|
||||
* processes.
|
||||
*
|
||||
* insert_hook lets you attach a hook callback immediately
|
||||
* which will not persist beyond the life of this page request
|
||||
* or the current process.
|
||||
*
|
||||
* @param string $hook
|
||||
* name of hook to attach callback
|
||||
* @param string $fn
|
||||
* function name of callback handler
|
||||
* @param int $version
|
||||
* hook interface version, 0 uses two callback params, 1 uses one callback param
|
||||
* @param int $priority
|
||||
* currently not implemented in this function, would require the hook array to be resorted
|
||||
*/
|
||||
|
||||
static public function insert($hook, $fn, $version = 0, $priority = 0) {
|
||||
if(is_array($fn)) {
|
||||
$fn = serialize($fn);
|
||||
}
|
||||
|
||||
if(! is_array(App::$hooks))
|
||||
App::$hooks = array();
|
||||
|
||||
if(! array_key_exists($hook, App::$hooks))
|
||||
App::$hooks[$hook] = array();
|
||||
|
||||
App::$hooks[$hook][] = array('', $fn, $priority, $version);
|
||||
}
|
||||
|
||||
}
|
||||
18
Zotlabs/Identity/BasicId.php
Normal file
18
Zotlabs/Identity/BasicId.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Identity\BasicId;
|
||||
|
||||
class BasicId {
|
||||
|
||||
private $name;
|
||||
private $profile_photo;
|
||||
private $profile_url;
|
||||
private $address;
|
||||
private $protocol;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
16
Zotlabs/Identity/ProfilePhoto.php
Normal file
16
Zotlabs/Identity/ProfilePhoto.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace Zotlabs\Identity\ProfilePhoto;
|
||||
|
||||
class ProfilePhoto {
|
||||
|
||||
private $photo_large_url;
|
||||
private $photo_medium_url;
|
||||
private $photo_small_url;
|
||||
private $photo_mimetype;
|
||||
private $photo_updated;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
25
Zotlabs/Lib/AConfig.php
Normal file
25
Zotlabs/Lib/AConfig.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
// account configuration storage is built on top of the under-utilised xconfig
|
||||
|
||||
class AConfig {
|
||||
|
||||
static public function Load($account_id) {
|
||||
return XConfig::Load('a_' . $account_id);
|
||||
}
|
||||
|
||||
static public function Get($account_id,$family,$key,$default = false) {
|
||||
return XConfig::Get('a_' . $account_id,$family,$key, $default);
|
||||
}
|
||||
|
||||
static public function Set($account_id,$family,$key,$value) {
|
||||
return XConfig::Set('a_' . $account_id,$family,$key,$value);
|
||||
}
|
||||
|
||||
static public function Delete($account_id,$family,$key) {
|
||||
return XConfig::Delete('a_' . $account_id,$family,$key);
|
||||
}
|
||||
|
||||
}
|
||||
75
Zotlabs/Lib/AbConfig.php
Normal file
75
Zotlabs/Lib/AbConfig.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
|
||||
class AbConfig {
|
||||
|
||||
static public function Load($chan,$xhash,$family = '') {
|
||||
if($family)
|
||||
$where = sprintf(" and cat = '%s' ",dbesc($family));
|
||||
$r = q("select * from abconfig where chan = %d and xchan = '%s' $where",
|
||||
intval($chan),
|
||||
dbesc($xhash)
|
||||
);
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
||||
static public function Get($chan,$xhash,$family,$key, $default = false) {
|
||||
$r = q("select * from abconfig where chan = %d and xchan = '%s' and cat = '%s' and k = '%s' limit 1",
|
||||
intval($chan),
|
||||
dbesc($xhash),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
if($r) {
|
||||
return ((preg_match('|^a:[0-9]+:{.*}$|s', $r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']);
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
|
||||
static public function Set($chan,$xhash,$family,$key,$value) {
|
||||
|
||||
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
|
||||
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
|
||||
|
||||
if(self::Get($chan,$xhash,$family,$key) === false) {
|
||||
$r = q("insert into abconfig ( chan, xchan, cat, k, v ) values ( %d, '%s', '%s', '%s', '%s' ) ",
|
||||
intval($chan),
|
||||
dbesc($xhash),
|
||||
dbesc($family),
|
||||
dbesc($key),
|
||||
dbesc($dbvalue)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$r = q("update abconfig set v = '%s' where chan = %d and xchan = '%s' and cat = '%s' and k = '%s' ",
|
||||
dbesc($dbvalue),
|
||||
dbesc($chan),
|
||||
dbesc($xhash),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
}
|
||||
|
||||
if($r)
|
||||
return $value;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static public function Delete($chan,$xhash,$family,$key) {
|
||||
|
||||
$r = q("delete from abconfig where chan = %d and xchan = '%s' and cat = '%s' and k = '%s' ",
|
||||
intval($chan),
|
||||
dbesc($xhash),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
}
|
||||
24
Zotlabs/Lib/Api_router.php
Normal file
24
Zotlabs/Lib/Api_router.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
|
||||
class Api_router {
|
||||
|
||||
static private $routes = array();
|
||||
|
||||
static function register($path,$fn,$auth_required) {
|
||||
self::$routes[$path] = [ 'func' => $fn, 'auth' => $auth_required ];
|
||||
}
|
||||
|
||||
static function find($path) {
|
||||
if(array_key_exists($path,self::$routes))
|
||||
return self::$routes[$path];
|
||||
return null;
|
||||
}
|
||||
|
||||
static function dbg() {
|
||||
return self::$routes;
|
||||
}
|
||||
|
||||
}
|
||||
793
Zotlabs/Lib/Apps.php
Normal file
793
Zotlabs/Lib/Apps.php
Normal file
@@ -0,0 +1,793 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/**
|
||||
* Apps
|
||||
*
|
||||
*/
|
||||
|
||||
require_once('include/plugin.php');
|
||||
require_once('include/channel.php');
|
||||
|
||||
|
||||
class Apps {
|
||||
|
||||
static public $installed_system_apps = null;
|
||||
|
||||
static public function get_system_apps($translate = true) {
|
||||
|
||||
$ret = array();
|
||||
if(is_dir('apps'))
|
||||
$files = glob('apps/*.apd');
|
||||
else
|
||||
$files = glob('app/*.apd');
|
||||
if($files) {
|
||||
foreach($files as $f) {
|
||||
$x = self::parse_app_description($f,$translate);
|
||||
if($x) {
|
||||
$ret[] = $x;
|
||||
}
|
||||
}
|
||||
}
|
||||
$files = glob('addon/*/*.apd');
|
||||
if($files) {
|
||||
foreach($files as $f) {
|
||||
$path = explode('/',$f);
|
||||
$plugin = $path[1];
|
||||
if(plugin_is_installed($plugin)) {
|
||||
$x = self::parse_app_description($f,$translate);
|
||||
if($x) {
|
||||
$x['plugin'] = $plugin;
|
||||
$ret[] = $x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function import_system_apps() {
|
||||
if(! local_channel())
|
||||
return;
|
||||
$apps = self::get_system_apps(false);
|
||||
|
||||
self::$installed_system_apps = q("select * from app where app_system = 1 and app_channel = %d",
|
||||
intval(local_channel())
|
||||
);
|
||||
|
||||
if($apps) {
|
||||
foreach($apps as $app) {
|
||||
$id = self::check_install_system_app($app);
|
||||
// $id will be boolean true or false to install an app, or an integer id to update an existing app
|
||||
if($id === false)
|
||||
continue;
|
||||
if($id !== true) {
|
||||
// if we already installed this app, but it changed, preserve any categories we created
|
||||
$s = '';
|
||||
$r = q("select * from term where otype = %d and oid = %d",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($id)
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $t) {
|
||||
if($s)
|
||||
$s .= ',';
|
||||
$s .= $t['term'];
|
||||
}
|
||||
$app['categories'] = $s;
|
||||
}
|
||||
}
|
||||
$app['uid'] = local_channel();
|
||||
$app['guid'] = hash('whirlpool',$app['name']);
|
||||
$app['system'] = 1;
|
||||
self::app_install(local_channel(),$app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install the system app if no system apps have been installed, or if a new system app
|
||||
* is discovered, or if the version of a system app changes.
|
||||
*/
|
||||
|
||||
static public function check_install_system_app($app) {
|
||||
if((! is_array(self::$installed_system_apps)) || (! count(self::$installed_system_apps))) {
|
||||
return true;
|
||||
}
|
||||
$notfound = true;
|
||||
foreach(self::$installed_system_apps as $iapp) {
|
||||
if($iapp['app_id'] == hash('whirlpool',$app['name'])) {
|
||||
$notfound = false;
|
||||
if(($iapp['app_version'] != $app['version'])
|
||||
|| ($app['plugin'] && (! $iapp['app_plugin']))) {
|
||||
return intval($iapp['app_id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $notfound;
|
||||
}
|
||||
|
||||
|
||||
static public function app_name_compare($a,$b) {
|
||||
return strcasecmp($a['name'],$b['name']);
|
||||
}
|
||||
|
||||
|
||||
static public function parse_app_description($f,$translate = true) {
|
||||
$ret = array();
|
||||
|
||||
$baseurl = z_root();
|
||||
$channel = \App::get_channel();
|
||||
$address = (($channel) ? $channel['channel_address'] : '');
|
||||
|
||||
//future expansion
|
||||
|
||||
$observer = \App::get_observer();
|
||||
|
||||
|
||||
$lines = @file($f);
|
||||
if($lines) {
|
||||
foreach($lines as $x) {
|
||||
if(preg_match('/^([a-zA-Z].*?):(.*?)$/ism',$x,$matches)) {
|
||||
$ret[$matches[1]] = trim(str_replace(array('$baseurl','$nick'),array($baseurl,$address),$matches[2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(! $ret['photo'])
|
||||
$ret['photo'] = $baseurl . '/' . get_default_profile_photo(80);
|
||||
|
||||
$ret['type'] = 'system';
|
||||
|
||||
foreach($ret as $k => $v) {
|
||||
if(strpos($v,'http') === 0) {
|
||||
if(! (local_channel() && strpos($v,z_root()) === 0)) {
|
||||
$ret[$k] = zid($v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(array_key_exists('desc',$ret))
|
||||
$ret['desc'] = str_replace(array('\'','"'),array(''','&dquot;'),$ret['desc']);
|
||||
|
||||
if(array_key_exists('target',$ret))
|
||||
$ret['target'] = str_replace(array('\'','"'),array(''','&dquot;'),$ret['target']);
|
||||
|
||||
if(array_key_exists('version',$ret))
|
||||
$ret['version'] = str_replace(array('\'','"'),array(''','&dquot;'),$ret['version']);
|
||||
|
||||
if(array_key_exists('categories',$ret))
|
||||
$ret['categories'] = str_replace(array('\'','"'),array(''','&dquot;'),$ret['categories']);
|
||||
|
||||
if(array_key_exists('requires',$ret)) {
|
||||
$requires = explode(',',$ret['requires']);
|
||||
foreach($requires as $require) {
|
||||
$require = trim(strtolower($require));
|
||||
switch($require) {
|
||||
case 'nologin':
|
||||
if(local_channel())
|
||||
unset($ret);
|
||||
break;
|
||||
case 'admin':
|
||||
if(! is_site_admin())
|
||||
unset($ret);
|
||||
break;
|
||||
case 'local_channel':
|
||||
if(! local_channel())
|
||||
unset($ret);
|
||||
break;
|
||||
case 'public_profile':
|
||||
if(! is_public_profile())
|
||||
unset($ret);
|
||||
break;
|
||||
case 'observer':
|
||||
if(! $observer)
|
||||
unset($ret);
|
||||
break;
|
||||
default:
|
||||
if(! (local_channel() && feature_enabled(local_channel(),$require)))
|
||||
unset($ret);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if($ret) {
|
||||
if($translate)
|
||||
self::translate_system_apps($ret);
|
||||
return $ret;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static public function translate_system_apps(&$arr) {
|
||||
$apps = array(
|
||||
'Site Admin' => t('Site Admin'),
|
||||
'Report Bug' => t('Report Bug'),
|
||||
'View Bookmarks' => t('View Bookmarks'),
|
||||
'My Chatrooms' => t('My Chatrooms'),
|
||||
'Connections' => t('Connections'),
|
||||
'Firefox Share' => t('Firefox Share'),
|
||||
'Remote Diagnostics' => t('Remote Diagnostics'),
|
||||
'Suggest Channels' => t('Suggest Channels'),
|
||||
'Login' => t('Login'),
|
||||
'Channel Manager' => t('Channel Manager'),
|
||||
'Grid' => t('Grid'),
|
||||
'Settings' => t('Settings'),
|
||||
'Files' => t('Files'),
|
||||
'Webpages' => t('Webpages'),
|
||||
'Wiki' => t('Wiki'),
|
||||
'Channel Home' => t('Channel Home'),
|
||||
'View Profile' => t('View Profile'),
|
||||
'Photos' => t('Photos'),
|
||||
'Events' => t('Events'),
|
||||
'Directory' => t('Directory'),
|
||||
'Help' => t('Help'),
|
||||
'Mail' => t('Mail'),
|
||||
'Mood' => t('Mood'),
|
||||
'Poke' => t('Poke'),
|
||||
'Chat' => t('Chat'),
|
||||
'Search' => t('Search'),
|
||||
'Probe' => t('Probe'),
|
||||
'Suggest' => t('Suggest'),
|
||||
'Random Channel' => t('Random Channel'),
|
||||
'Invite' => t('Invite'),
|
||||
'Features' => t('Features'),
|
||||
'Language' => t('Language'),
|
||||
'Post' => t('Post'),
|
||||
'Profile Photo' => t('Profile Photo')
|
||||
);
|
||||
|
||||
if(array_key_exists($arr['name'],$apps)) {
|
||||
$arr['name'] = $apps[$arr['name']];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// papp is a portable app
|
||||
|
||||
static public function app_render($papp,$mode = 'view') {
|
||||
|
||||
/**
|
||||
* modes:
|
||||
* view: normal mode for viewing an app via bbcode from a conversation or page
|
||||
* provides install/update button if you're logged in locally
|
||||
* list: normal mode for viewing an app on the app page
|
||||
* no buttons are shown
|
||||
* edit: viewing the app page in editing mode provides a delete button
|
||||
* nav: render apps for app-bin
|
||||
*/
|
||||
|
||||
$installed = false;
|
||||
|
||||
if(! $papp)
|
||||
return;
|
||||
|
||||
if(! $papp['photo'])
|
||||
$papp['photo'] = z_root() . '/' . get_default_profile_photo(80);
|
||||
|
||||
self::translate_system_apps($papp);
|
||||
|
||||
if(($papp['plugin']) && (! plugin_is_installed($papp['plugin'])))
|
||||
return '';
|
||||
|
||||
$papp['papp'] = self::papp_encode($papp);
|
||||
|
||||
if(! strstr($papp['url'],'://'))
|
||||
$papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url'];
|
||||
|
||||
foreach($papp as $k => $v) {
|
||||
if(strpos($v,'http') === 0 && $k != 'papp') {
|
||||
if(! (local_channel() && strpos($v,z_root()) === 0)) {
|
||||
$papp[$k] = zid($v);
|
||||
}
|
||||
}
|
||||
if($k === 'desc')
|
||||
$papp['desc'] = str_replace(array('\'','"'),array(''','&dquot;'),$papp['desc']);
|
||||
|
||||
if($k === 'requires') {
|
||||
$requires = explode(',',$v);
|
||||
foreach($requires as $require) {
|
||||
$require = trim(strtolower($require));
|
||||
switch($require) {
|
||||
case 'nologin':
|
||||
if(local_channel())
|
||||
return '';
|
||||
break;
|
||||
case 'admin':
|
||||
if(! is_site_admin())
|
||||
return '';
|
||||
break;
|
||||
case 'local_channel':
|
||||
if(! local_channel())
|
||||
return '';
|
||||
break;
|
||||
case 'public_profile':
|
||||
if(! is_public_profile())
|
||||
return '';
|
||||
break;
|
||||
case 'observer':
|
||||
$observer = \App::get_observer();
|
||||
if(! $observer)
|
||||
return '';
|
||||
break;
|
||||
default:
|
||||
if(! (local_channel() && feature_enabled(local_channel(),$require)))
|
||||
return '';
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$hosturl = '';
|
||||
|
||||
if(local_channel()) {
|
||||
$installed = self::app_installed(local_channel(),$papp);
|
||||
$hosturl = z_root() . '/';
|
||||
}
|
||||
elseif(remote_channel()) {
|
||||
$observer = \App::get_observer();
|
||||
if($observer && $observer['xchan_network'] === 'zot') {
|
||||
// some folks might have xchan_url redirected offsite, use the connurl
|
||||
$x = parse_url($observer['xchan_connurl']);
|
||||
if($x) {
|
||||
$hosturl = $x['scheme'] . '://' . $x['host'] . '/';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$install_action = (($installed) ? t('Update') : t('Install'));
|
||||
$icon = ((strpos($papp['photo'],'icon:') === 0) ? substr($papp['photo'],5) : '');
|
||||
|
||||
return replace_macros(get_markup_template('app.tpl'),array(
|
||||
'$app' => $papp,
|
||||
'$icon' => $icon,
|
||||
'$hosturl' => $hosturl,
|
||||
'$purchase' => (($papp['page'] && (! $installed)) ? t('Purchase') : ''),
|
||||
'$install' => (($hosturl && $mode == 'view') ? $install_action : ''),
|
||||
'$edit' => ((local_channel() && $installed && $mode == 'edit') ? t('Edit') : ''),
|
||||
'$delete' => ((local_channel() && $installed && $mode == 'edit') ? t('Delete') : ''),
|
||||
'$undelete' => ((local_channel() && $installed && $mode == 'edit') ? t('Undelete') : ''),
|
||||
'$deleted' => $papp['deleted'],
|
||||
'$feature' => (($papp['embed']) ? false : true),
|
||||
'$featured' => ((strpos($papp['categories'], 'nav_featured_app') === false) ? false : true),
|
||||
'$navapps' => (($mode == 'nav') ? true : false),
|
||||
'$add' => t('Add to app-tray'),
|
||||
'$remove' => t('Remove from app-tray')
|
||||
));
|
||||
}
|
||||
|
||||
static public function app_install($uid,$app) {
|
||||
$app['uid'] = $uid;
|
||||
|
||||
if(self::app_installed($uid,$app))
|
||||
$x = self::app_update($app);
|
||||
else
|
||||
$x = self::app_store($app);
|
||||
|
||||
if($x['success']) {
|
||||
$r = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc($x['app_id']),
|
||||
intval($uid)
|
||||
);
|
||||
if($r) {
|
||||
if(! $r[0]['app_system']) {
|
||||
if($app['categories'] && (! $app['term'])) {
|
||||
$r[0]['term'] = q("select * from term where otype = %d and oid = %d",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($r[0]['id'])
|
||||
);
|
||||
build_sync_packet($uid,array('app' => $r[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $x['app_id'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static public function app_destroy($uid,$app) {
|
||||
|
||||
|
||||
if($uid && $app['guid']) {
|
||||
|
||||
$x = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc($app['guid']),
|
||||
intval($uid)
|
||||
);
|
||||
if($x) {
|
||||
if(! intval($x[0]['app_deleted'])) {
|
||||
$x[0]['app_deleted'] = 1;
|
||||
q("delete from term where otype = %d and oid = %d",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($x[0]['id'])
|
||||
);
|
||||
if($x[0]['app_system']) {
|
||||
$r = q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d",
|
||||
dbesc($app['guid']),
|
||||
intval($uid)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$r = q("delete from app where app_id = '%s' and app_channel = %d",
|
||||
dbesc($app['guid']),
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
// we don't sync system apps - they may be completely different on the other system
|
||||
build_sync_packet($uid,array('app' => $x));
|
||||
}
|
||||
}
|
||||
else {
|
||||
self::app_undestroy($uid,$app);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public function app_undestroy($uid,$app) {
|
||||
|
||||
// undelete a system app
|
||||
|
||||
if($uid && $app['guid']) {
|
||||
|
||||
$x = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc($app['guid']),
|
||||
intval($uid)
|
||||
);
|
||||
if($x) {
|
||||
if($x[0]['app_system']) {
|
||||
$r = q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d",
|
||||
dbesc($app['guid']),
|
||||
intval($uid)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public function app_feature($uid,$app) {
|
||||
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc($app['guid']),
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
$x = q("select * from term where otype = %d and oid = %d and term = 'nav_featured_app' limit 1",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($r[0]['id'])
|
||||
);
|
||||
|
||||
if($x) {
|
||||
q("delete from term where otype = %d and oid = %d and term = 'nav_featured_app'",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($x[0]['oid'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
store_item_tag($uid,$r[0]['id'],TERM_OBJ_APP,TERM_CATEGORY,'nav_featured_app',escape_tags(z_root() . '/apps/?f=&cat=nav_featured_app'));
|
||||
}
|
||||
}
|
||||
|
||||
static public function app_installed($uid,$app) {
|
||||
|
||||
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc((array_key_exists('guid',$app)) ? $app['guid'] : ''),
|
||||
intval($uid)
|
||||
);
|
||||
return(($r) ? true : false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function app_list($uid, $deleted = false, $cat = '') {
|
||||
if($deleted)
|
||||
$sql_extra = "";
|
||||
else
|
||||
$sql_extra = " and app_deleted = 0 ";
|
||||
|
||||
if($cat) {
|
||||
$r = q("select oid from term where otype = %d and term = '%s'",
|
||||
intval(TERM_OBJ_APP),
|
||||
dbesc($cat)
|
||||
);
|
||||
if(! $r)
|
||||
return $r;
|
||||
$sql_extra .= " and app.id in ( ";
|
||||
$s = '';
|
||||
foreach($r as $rr) {
|
||||
if($s)
|
||||
$s .= ',';
|
||||
$s .= intval($rr['oid']);
|
||||
}
|
||||
$sql_extra .= $s . ') ';
|
||||
}
|
||||
|
||||
$r = q("select * from app where app_channel = %d $sql_extra order by app_name asc",
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
for($x = 0; $x < count($r); $x ++) {
|
||||
if(! $r[$x]['app_system'])
|
||||
$r[$x]['type'] = 'personal';
|
||||
$r[$x]['term'] = q("select * from term where otype = %d and oid = %d",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($r[$x]['id'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return($r);
|
||||
}
|
||||
|
||||
|
||||
static public function app_decode($s) {
|
||||
$x = base64_decode(str_replace(array('<br />',"\r","\n",' '),array('','','',''),$s));
|
||||
return json_decode($x,true);
|
||||
}
|
||||
|
||||
|
||||
static public function app_store($arr) {
|
||||
|
||||
//logger('app_store: ' . print_r($arr,true));
|
||||
|
||||
$darray = array();
|
||||
$ret = array('success' => false);
|
||||
|
||||
$darray['app_url'] = ((x($arr,'url')) ? $arr['url'] : '');
|
||||
$darray['app_channel'] = ((x($arr,'uid')) ? $arr['uid'] : 0);
|
||||
|
||||
if((! $darray['app_url']) || (! $darray['app_channel']))
|
||||
return $ret;
|
||||
|
||||
if($arr['photo'] && (strpos($arr['photo'],'icon:') !== 0) && (! strstr($arr['photo'],z_root()))) {
|
||||
$x = import_xchan_photo($arr['photo'],get_observer_hash(),true);
|
||||
$arr['photo'] = $x[1];
|
||||
}
|
||||
|
||||
|
||||
$darray['app_id'] = ((x($arr,'guid')) ? $arr['guid'] : random_string(). '.' . \App::get_hostname());
|
||||
$darray['app_sig'] = ((x($arr,'sig')) ? $arr['sig'] : '');
|
||||
$darray['app_author'] = ((x($arr,'author')) ? $arr['author'] : get_observer_hash());
|
||||
$darray['app_name'] = ((x($arr,'name')) ? escape_tags($arr['name']) : t('Unknown'));
|
||||
$darray['app_desc'] = ((x($arr,'desc')) ? escape_tags($arr['desc']) : '');
|
||||
$darray['app_photo'] = ((x($arr,'photo')) ? $arr['photo'] : z_root() . '/' . get_default_profile_photo(80));
|
||||
$darray['app_version'] = ((x($arr,'version')) ? escape_tags($arr['version']) : '');
|
||||
$darray['app_addr'] = ((x($arr,'addr')) ? escape_tags($arr['addr']) : '');
|
||||
$darray['app_price'] = ((x($arr,'price')) ? escape_tags($arr['price']) : '');
|
||||
$darray['app_page'] = ((x($arr,'page')) ? escape_tags($arr['page']) : '');
|
||||
$darray['app_plugin'] = ((x($arr,'plugin')) ? escape_tags($arr['plugin']) : '');
|
||||
$darray['app_requires'] = ((x($arr,'requires')) ? escape_tags($arr['requires']) : '');
|
||||
$darray['app_system'] = ((x($arr,'system')) ? intval($arr['system']) : 0);
|
||||
$darray['app_deleted'] = ((x($arr,'deleted')) ? intval($arr['deleted']) : 0);
|
||||
|
||||
$created = datetime_convert();
|
||||
|
||||
$r = q("insert into app ( app_id, app_sig, app_author, app_name, app_desc, app_url, app_photo, app_version, app_channel, app_addr, app_price, app_page, app_requires, app_created, app_edited, app_system, app_plugin, app_deleted ) values ( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', %d )",
|
||||
dbesc($darray['app_id']),
|
||||
dbesc($darray['app_sig']),
|
||||
dbesc($darray['app_author']),
|
||||
dbesc($darray['app_name']),
|
||||
dbesc($darray['app_desc']),
|
||||
dbesc($darray['app_url']),
|
||||
dbesc($darray['app_photo']),
|
||||
dbesc($darray['app_version']),
|
||||
intval($darray['app_channel']),
|
||||
dbesc($darray['app_addr']),
|
||||
dbesc($darray['app_price']),
|
||||
dbesc($darray['app_page']),
|
||||
dbesc($darray['app_requires']),
|
||||
dbesc($created),
|
||||
dbesc($created),
|
||||
intval($darray['app_system']),
|
||||
dbesc($darray['app_plugin']),
|
||||
intval($darray['app_deleted'])
|
||||
);
|
||||
if($r) {
|
||||
$ret['success'] = true;
|
||||
$ret['app_id'] = $darray['app_id'];
|
||||
}
|
||||
if($arr['categories']) {
|
||||
$x = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc($darray['app_id']),
|
||||
intval($darray['app_channel'])
|
||||
);
|
||||
$y = explode(',',$arr['categories']);
|
||||
if($y) {
|
||||
foreach($y as $t) {
|
||||
$t = trim($t);
|
||||
if($t) {
|
||||
store_item_tag($darray['app_channel'],$x[0]['id'],TERM_OBJ_APP,TERM_CATEGORY,escape_tags($t),escape_tags(z_root() . '/apps/?f=&cat=' . escape_tags($t)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
static public function app_update($arr) {
|
||||
|
||||
//logger('app_update: ' . print_r($arr,true));
|
||||
$darray = array();
|
||||
$ret = array('success' => false);
|
||||
|
||||
$darray['app_url'] = ((x($arr,'url')) ? $arr['url'] : '');
|
||||
$darray['app_channel'] = ((x($arr,'uid')) ? $arr['uid'] : 0);
|
||||
$darray['app_id'] = ((x($arr,'guid')) ? $arr['guid'] : 0);
|
||||
|
||||
if((! $darray['app_url']) || (! $darray['app_channel']) || (! $darray['app_id']))
|
||||
return $ret;
|
||||
|
||||
if($arr['photo'] && (strpos($arr['photo'],'icon:') !== 0) && (! strstr($arr['photo'],z_root()))) {
|
||||
$x = import_xchan_photo($arr['photo'],get_observer_hash(),true);
|
||||
$arr['photo'] = $x[1];
|
||||
}
|
||||
|
||||
$darray['app_sig'] = ((x($arr,'sig')) ? $arr['sig'] : '');
|
||||
$darray['app_author'] = ((x($arr,'author')) ? $arr['author'] : get_observer_hash());
|
||||
$darray['app_name'] = ((x($arr,'name')) ? escape_tags($arr['name']) : t('Unknown'));
|
||||
$darray['app_desc'] = ((x($arr,'desc')) ? escape_tags($arr['desc']) : '');
|
||||
$darray['app_photo'] = ((x($arr,'photo')) ? $arr['photo'] : z_root() . '/' . get_default_profile_photo(80));
|
||||
$darray['app_version'] = ((x($arr,'version')) ? escape_tags($arr['version']) : '');
|
||||
$darray['app_addr'] = ((x($arr,'addr')) ? escape_tags($arr['addr']) : '');
|
||||
$darray['app_price'] = ((x($arr,'price')) ? escape_tags($arr['price']) : '');
|
||||
$darray['app_page'] = ((x($arr,'page')) ? escape_tags($arr['page']) : '');
|
||||
$darray['app_plugin'] = ((x($arr,'plugin')) ? escape_tags($arr['plugin']) : '');
|
||||
$darray['app_requires'] = ((x($arr,'requires')) ? escape_tags($arr['requires']) : '');
|
||||
$darray['app_system'] = ((x($arr,'system')) ? intval($arr['system']) : 0);
|
||||
$darray['app_deleted'] = ((x($arr,'deleted')) ? intval($arr['deleted']) : 0);
|
||||
|
||||
$edited = datetime_convert();
|
||||
|
||||
$r = q("update app set app_sig = '%s', app_author = '%s', app_name = '%s', app_desc = '%s', app_url = '%s', app_photo = '%s', app_version = '%s', app_addr = '%s', app_price = '%s', app_page = '%s', app_requires = '%s', app_edited = '%s', app_system = %d, app_plugin = '%s', app_deleted = %d where app_id = '%s' and app_channel = %d",
|
||||
dbesc($darray['app_sig']),
|
||||
dbesc($darray['app_author']),
|
||||
dbesc($darray['app_name']),
|
||||
dbesc($darray['app_desc']),
|
||||
dbesc($darray['app_url']),
|
||||
dbesc($darray['app_photo']),
|
||||
dbesc($darray['app_version']),
|
||||
dbesc($darray['app_addr']),
|
||||
dbesc($darray['app_price']),
|
||||
dbesc($darray['app_page']),
|
||||
dbesc($darray['app_requires']),
|
||||
dbesc($edited),
|
||||
intval($darray['app_system']),
|
||||
dbesc($darray['app_plugin']),
|
||||
intval($darray['app_deleted']),
|
||||
dbesc($darray['app_id']),
|
||||
intval($darray['app_channel'])
|
||||
);
|
||||
if($r) {
|
||||
$ret['success'] = true;
|
||||
$ret['app_id'] = $darray['app_id'];
|
||||
}
|
||||
|
||||
$x = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc($darray['app_id']),
|
||||
intval($darray['app_channel'])
|
||||
);
|
||||
if($x) {
|
||||
q("delete from term where otype = %d and oid = %d",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($x[0]['id'])
|
||||
);
|
||||
if($arr['categories']) {
|
||||
$y = explode(',',$arr['categories']);
|
||||
if($y) {
|
||||
foreach($y as $t) {
|
||||
$t = trim($t);
|
||||
if($t) {
|
||||
store_item_tag($darray['app_channel'],$x[0]['id'],TERM_OBJ_APP,TERM_CATEGORY,escape_tags($t),escape_tags(z_root() . '/apps/?f=&cat=' . escape_tags($t)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function app_encode($app,$embed = false) {
|
||||
|
||||
$ret = array();
|
||||
|
||||
$ret['type'] = 'personal';
|
||||
|
||||
if($app['app_id'])
|
||||
$ret['guid'] = $app['app_id'];
|
||||
|
||||
if($app['app_id'])
|
||||
$ret['guid'] = $app['app_id'];
|
||||
|
||||
if($app['app_sig'])
|
||||
$ret['sig'] = $app['app_sig'];
|
||||
|
||||
if($app['app_author'])
|
||||
$ret['author'] = $app['app_author'];
|
||||
|
||||
if($app['app_name'])
|
||||
$ret['name'] = $app['app_name'];
|
||||
|
||||
if($app['app_desc'])
|
||||
$ret['desc'] = $app['app_desc'];
|
||||
|
||||
if($app['app_url'])
|
||||
$ret['url'] = $app['app_url'];
|
||||
|
||||
if($app['app_photo'])
|
||||
$ret['photo'] = $app['app_photo'];
|
||||
|
||||
if($app['app_icon'])
|
||||
$ret['icon'] = $app['app_icon'];
|
||||
|
||||
if($app['app_version'])
|
||||
$ret['version'] = $app['app_version'];
|
||||
|
||||
if($app['app_addr'])
|
||||
$ret['addr'] = $app['app_addr'];
|
||||
|
||||
if($app['app_price'])
|
||||
$ret['price'] = $app['app_price'];
|
||||
|
||||
if($app['app_page'])
|
||||
$ret['page'] = $app['app_page'];
|
||||
|
||||
if($app['app_requires'])
|
||||
$ret['requires'] = $app['app_requires'];
|
||||
|
||||
if($app['app_system'])
|
||||
$ret['system'] = $app['app_system'];
|
||||
|
||||
if($app['app_plugin'])
|
||||
$ret['plugin'] = $app['app_plugin'];
|
||||
|
||||
if($app['app_deleted'])
|
||||
$ret['deleted'] = $app['app_deleted'];
|
||||
|
||||
if($app['term']) {
|
||||
$s = '';
|
||||
foreach($app['term'] as $t) {
|
||||
if($s)
|
||||
$s .= ',';
|
||||
$s .= $t['term'];
|
||||
}
|
||||
$ret['categories'] = $s;
|
||||
}
|
||||
|
||||
|
||||
if(! $embed)
|
||||
return $ret;
|
||||
|
||||
$ret['embed'] = true;
|
||||
|
||||
if(array_key_exists('categories',$ret))
|
||||
unset($ret['categories']);
|
||||
|
||||
$j = json_encode($ret);
|
||||
return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]';
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function papp_encode($papp) {
|
||||
return chunk_split(base64_encode(json_encode($papp)),72,"\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
51
Zotlabs/Lib/Cache.php
Normal file
51
Zotlabs/Lib/Cache.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/**
|
||||
* cache api
|
||||
*/
|
||||
|
||||
class Cache {
|
||||
public static function get($key) {
|
||||
|
||||
$key = substr($key,0,254);
|
||||
|
||||
$r = q("SELECT v FROM cache WHERE k = '%s' limit 1",
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
if ($r)
|
||||
return $r[0]['v'];
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function set($key,$value) {
|
||||
|
||||
$key = substr($key,0,254);
|
||||
|
||||
$r = q("SELECT * FROM cache WHERE k = '%s' limit 1",
|
||||
dbesc($key)
|
||||
);
|
||||
if($r) {
|
||||
q("UPDATE cache SET v = '%s', updated = '%s' WHERE k = '%s'",
|
||||
dbesc($value),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($key));
|
||||
}
|
||||
else {
|
||||
q("INSERT INTO cache ( k, v, updated) VALUES ('%s','%s','%s')",
|
||||
dbesc($key),
|
||||
dbesc($value),
|
||||
dbesc(datetime_convert()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function clear() {
|
||||
q("DELETE FROM cache WHERE updated < '%s'",
|
||||
dbesc(datetime_convert('UTC','UTC',"now - 30 days")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
267
Zotlabs/Lib/Chatroom.php
Normal file
267
Zotlabs/Lib/Chatroom.php
Normal file
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/**
|
||||
* @brief Chat related functions.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
class Chatroom {
|
||||
/**
|
||||
* @brief Creates a chatroom.
|
||||
*
|
||||
* @param array $channel
|
||||
* @param array $arr
|
||||
* @return An associative array containing:
|
||||
* - success: A boolean
|
||||
* - message: (optional) A string
|
||||
*/
|
||||
|
||||
static public function create($channel, $arr) {
|
||||
|
||||
$ret = array('success' => false);
|
||||
|
||||
$name = trim($arr['name']);
|
||||
if(! $name) {
|
||||
$ret['message'] = t('Missing room name');
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$r = q("select cr_id from chatroom where cr_uid = %d and cr_name = '%s' limit 1",
|
||||
intval($channel['channel_id']),
|
||||
dbesc($name)
|
||||
);
|
||||
if($r) {
|
||||
$ret['message'] = t('Duplicate room name');
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$r = q("select count(cr_id) as total from chatroom where cr_aid = %d",
|
||||
intval($channel['channel_account_id'])
|
||||
);
|
||||
if($r)
|
||||
$limit = service_class_fetch($channel['channel_id'], 'chatrooms');
|
||||
|
||||
if(($r) && ($limit !== false) && ($r[0]['total'] >= $limit)) {
|
||||
$ret['message'] = upgrade_message();
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if(! array_key_exists('expire', $arr))
|
||||
$arr['expire'] = 120; // minutes, e.g. 2 hours
|
||||
|
||||
$created = datetime_convert();
|
||||
|
||||
$x = q("insert into chatroom ( cr_aid, cr_uid, cr_name, cr_created, cr_edited, cr_expire, allow_cid, allow_gid, deny_cid, deny_gid )
|
||||
values ( %d, %d , '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s' ) ",
|
||||
intval($channel['channel_account_id']),
|
||||
intval($channel['channel_id']),
|
||||
dbesc($name),
|
||||
dbesc($created),
|
||||
dbesc($created),
|
||||
intval($arr['expire']),
|
||||
dbesc($arr['allow_cid']),
|
||||
dbesc($arr['allow_gid']),
|
||||
dbesc($arr['deny_cid']),
|
||||
dbesc($arr['deny_gid'])
|
||||
);
|
||||
|
||||
if($x)
|
||||
$ret['success'] = true;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
static public function destroy($channel,$arr) {
|
||||
|
||||
$ret = array('success' => false);
|
||||
|
||||
if(intval($arr['cr_id']))
|
||||
$sql_extra = " and cr_id = " . intval($arr['cr_id']) . " ";
|
||||
elseif(trim($arr['cr_name']))
|
||||
$sql_extra = " and cr_name = '" . protect_sprintf(dbesc(trim($arr['cr_name']))) . "' ";
|
||||
else {
|
||||
$ret['message'] = t('Invalid room specifier.');
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$r = q("select * from chatroom where cr_uid = %d $sql_extra limit 1",
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
if(! $r) {
|
||||
$ret['message'] = t('Invalid room specifier.');
|
||||
return $ret;
|
||||
}
|
||||
|
||||
build_sync_packet($channel['channel_id'],array('chatroom' => $r));
|
||||
|
||||
q("delete from chatroom where cr_id = %d",
|
||||
intval($r[0]['cr_id'])
|
||||
);
|
||||
if($r[0]['cr_id']) {
|
||||
q("delete from chatpresence where cp_room = %d",
|
||||
intval($r[0]['cr_id'])
|
||||
);
|
||||
q("delete from chat where chat_room = %d",
|
||||
intval($r[0]['cr_id'])
|
||||
);
|
||||
}
|
||||
|
||||
$ret['success'] = true;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
static public function enter($observer_xchan, $room_id, $status, $client) {
|
||||
|
||||
if(! $room_id || ! $observer_xchan)
|
||||
return;
|
||||
|
||||
$r = q("select * from chatroom where cr_id = %d limit 1",
|
||||
intval($room_id)
|
||||
);
|
||||
if(! $r) {
|
||||
notice( t('Room not found.') . EOL);
|
||||
return false;
|
||||
}
|
||||
require_once('include/security.php');
|
||||
$sql_extra = permissions_sql($r[0]['cr_uid']);
|
||||
|
||||
$x = q("select * from chatroom where cr_id = %d and cr_uid = %d $sql_extra limit 1",
|
||||
intval($room_id),
|
||||
intval($r[0]['cr_uid'])
|
||||
);
|
||||
if(! $x) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return false;
|
||||
}
|
||||
|
||||
$limit = service_class_fetch($r[0]['cr_uid'], 'chatters_inroom');
|
||||
if($limit !== false) {
|
||||
$y = q("select count(*) as total from chatpresence where cp_room = %d",
|
||||
intval($room_id)
|
||||
);
|
||||
if($y && $y[0]['total'] > $limit) {
|
||||
notice( t('Room is full') . EOL);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(intval($x[0]['cr_expire'])) {
|
||||
$r = q("delete from chat where created < %s - INTERVAL %s and chat_room = %d",
|
||||
db_utcnow(),
|
||||
db_quoteinterval( intval($x[0]['cr_expire']) . ' MINUTE' ),
|
||||
intval($x[0]['cr_id'])
|
||||
);
|
||||
}
|
||||
|
||||
$r = q("select * from chatpresence where cp_xchan = '%s' and cp_room = %d limit 1",
|
||||
dbesc($observer_xchan),
|
||||
intval($room_id)
|
||||
);
|
||||
if($r) {
|
||||
q("update chatpresence set cp_last = '%s' where cp_id = %d and cp_client = '%s'",
|
||||
dbesc(datetime_convert()),
|
||||
intval($r[0]['cp_id']),
|
||||
dbesc($client)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
$r = q("insert into chatpresence ( cp_room, cp_xchan, cp_last, cp_status, cp_client )
|
||||
values ( %d, '%s', '%s', '%s', '%s' )",
|
||||
intval($room_id),
|
||||
dbesc($observer_xchan),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($status),
|
||||
dbesc($client)
|
||||
);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
||||
function leave($observer_xchan, $room_id, $client) {
|
||||
if(! $room_id || ! $observer_xchan)
|
||||
return;
|
||||
|
||||
$r = q("select * from chatpresence where cp_xchan = '%s' and cp_room = %d and cp_client = '%s' limit 1",
|
||||
dbesc($observer_xchan),
|
||||
intval($room_id),
|
||||
dbesc($client)
|
||||
);
|
||||
if($r) {
|
||||
q("delete from chatpresence where cp_id = %d",
|
||||
intval($r[0]['cp_id'])
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static public function roomlist($uid) {
|
||||
require_once('include/security.php');
|
||||
$sql_extra = permissions_sql($uid);
|
||||
|
||||
$r = q("select allow_cid, allow_gid, deny_cid, deny_gid, cr_name, cr_expire, cr_id, count(cp_id) as cr_inroom from chatroom left join chatpresence on cr_id = cp_room where cr_uid = %d $sql_extra group by cr_name, cr_id order by cr_name",
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
static public function list_count($uid) {
|
||||
require_once('include/security.php');
|
||||
$sql_extra = permissions_sql($uid);
|
||||
|
||||
$r = q("select count(*) as total from chatroom where cr_uid = %d $sql_extra",
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
return $r[0]['total'];
|
||||
}
|
||||
|
||||
/**
|
||||
* create a chat message via API.
|
||||
* It is the caller's responsibility to enter the room.
|
||||
*/
|
||||
|
||||
static public function message($uid, $room_id, $xchan, $text) {
|
||||
|
||||
$ret = array('success' => false);
|
||||
|
||||
if(! $text)
|
||||
return;
|
||||
|
||||
$sql_extra = permissions_sql($uid);
|
||||
|
||||
$r = q("select * from chatroom where cr_uid = %d and cr_id = %d $sql_extra",
|
||||
intval($uid),
|
||||
intval($room_id)
|
||||
);
|
||||
if(! $r)
|
||||
return $ret;
|
||||
|
||||
$arr = array(
|
||||
'chat_room' => $room_id,
|
||||
'chat_xchan' => $xchan,
|
||||
'chat_text' => $text
|
||||
);
|
||||
|
||||
call_hooks('chat_message', $arr);
|
||||
|
||||
$x = q("insert into chat ( chat_room, chat_xchan, created, chat_text )
|
||||
values( %d, '%s', '%s', '%s' )",
|
||||
intval($room_id),
|
||||
dbesc($xchan),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($arr['chat_text'])
|
||||
);
|
||||
|
||||
$ret['success'] = true;
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
166
Zotlabs/Lib/Config.php
Normal file
166
Zotlabs/Lib/Config.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
|
||||
class Config {
|
||||
|
||||
/**
|
||||
* @brief Loads the hub's configuration from database to a cached storage.
|
||||
*
|
||||
* Retrieve a category ($family) of config variables from database to a cached
|
||||
* storage in the global App::$config[$family].
|
||||
*
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
*/
|
||||
|
||||
static public function Load($family) {
|
||||
if(! array_key_exists($family, \App::$config))
|
||||
\App::$config[$family] = array();
|
||||
|
||||
if(! array_key_exists('config_loaded', \App::$config[$family])) {
|
||||
$r = q("SELECT * FROM config WHERE cat = '%s'", dbesc($family));
|
||||
if($r !== false) {
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$k = $rr['k'];
|
||||
\App::$config[$family][$k] = $rr['v'];
|
||||
}
|
||||
}
|
||||
\App::$config[$family]['config_loaded'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a configuration value for the hub.
|
||||
*
|
||||
* Stores a config value ($value) in the category ($family) under the key ($key).
|
||||
*
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
* @param string $key
|
||||
* The configuration key to set
|
||||
* @param mixed $value
|
||||
* The value to store in the configuration
|
||||
* @return mixed
|
||||
* Return the set value, or false if the database update failed
|
||||
*/
|
||||
|
||||
static public function Set($family,$key,$value) {
|
||||
// manage array value
|
||||
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
|
||||
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
|
||||
|
||||
if(get_config($family, $key) === false || (! self::get_from_storage($family, $key))) {
|
||||
$ret = q("INSERT INTO config ( cat, k, v ) VALUES ( '%s', '%s', '%s' ) ",
|
||||
dbesc($family),
|
||||
dbesc($key),
|
||||
dbesc($dbvalue)
|
||||
);
|
||||
if($ret) {
|
||||
\App::$config[$family][$key] = $value;
|
||||
$ret = $value;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$ret = q("UPDATE config SET v = '%s' WHERE cat = '%s' AND k = '%s'",
|
||||
dbesc($dbvalue),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
if($ret) {
|
||||
\App::$config[$family][$key] = $value;
|
||||
$ret = $value;
|
||||
}
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a particular config variable given the category name ($family)
|
||||
* and a key.
|
||||
*
|
||||
* Get a particular config variable from the given category ($family) and the
|
||||
* $key from a cached storage in App::$config[$family]. If a key is found in the
|
||||
* DB but does not exist in local config cache, pull it into the cache so we
|
||||
* do not have to hit the DB again for this item.
|
||||
*
|
||||
* Returns false if not set.
|
||||
*
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
* @param string $key
|
||||
* The configuration key to query
|
||||
* @return mixed Return value or false on error or if not set
|
||||
*/
|
||||
|
||||
static public function Get($family,$key,$default = false) {
|
||||
if((! array_key_exists($family, \App::$config)) || (! array_key_exists('config_loaded', \App::$config[$family])))
|
||||
self::Load($family);
|
||||
|
||||
if(array_key_exists('config_loaded', \App::$config[$family])) {
|
||||
if(! array_key_exists($key, \App::$config[$family])) {
|
||||
return $default;
|
||||
}
|
||||
return ((! is_array(\App::$config[$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$family][$key]))
|
||||
? unserialize(\App::$config[$family][$key])
|
||||
: \App::$config[$family][$key]
|
||||
);
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deletes the given key from the hub's configuration database.
|
||||
*
|
||||
* Removes the configured value from the stored cache in App::$config[$family]
|
||||
* and removes it from the database.
|
||||
*
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
* @param string $key
|
||||
* The configuration key to delete
|
||||
* @return mixed
|
||||
*/
|
||||
|
||||
static public function Delete($family,$key) {
|
||||
|
||||
$ret = false;
|
||||
|
||||
if(array_key_exists($family, \App::$config) && array_key_exists($key, \App::$config[$family]))
|
||||
unset(\App::$config[$family][$key]);
|
||||
$ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s'",
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns a value directly from the database configuration storage.
|
||||
*
|
||||
* This function queries directly the database and bypasses the chached storage
|
||||
* from get_config($family, $key).
|
||||
*
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
* @param string $key
|
||||
* The configuration key to query
|
||||
* @return mixed
|
||||
*/
|
||||
|
||||
static private function get_from_storage($family,$key) {
|
||||
$ret = q("SELECT * FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1",
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
797
Zotlabs/Lib/Enotify.php
Normal file
797
Zotlabs/Lib/Enotify.php
Normal file
@@ -0,0 +1,797 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/**
|
||||
* @brief File with functions and a class for generating system and email notifications.
|
||||
*/
|
||||
|
||||
|
||||
class Enotify {
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param array $params an assoziative array with:
|
||||
* * \e string \b from_xchan sender xchan hash
|
||||
* * \e string \b to_xchan recipient xchan hash
|
||||
* * \e array \b item an assoziative array
|
||||
* * \e int \b type one of the NOTIFY_* constants from boot.php
|
||||
* * \e string \b link
|
||||
* * \e string \b parent_mid
|
||||
* * \e string \b otype
|
||||
* * \e string \b verb
|
||||
* * \e string \b activity
|
||||
*/
|
||||
|
||||
|
||||
static public function submit($params) {
|
||||
|
||||
logger('notification: entry', LOGGER_DEBUG);
|
||||
|
||||
// throw a small amount of entropy into the system to breakup duplicates arriving at the same precise instant.
|
||||
usleep(mt_rand(0, 10000));
|
||||
|
||||
if ($params['from_xchan']) {
|
||||
$x = q("select * from xchan where xchan_hash = '%s' limit 1",
|
||||
dbesc($params['from_xchan'])
|
||||
);
|
||||
}
|
||||
if ($params['to_xchan']) {
|
||||
$y = q("select channel.*, account.* from channel left join account on channel_account_id = account_id
|
||||
where channel_hash = '%s' and channel_removed = 0 limit 1",
|
||||
dbesc($params['to_xchan'])
|
||||
);
|
||||
}
|
||||
if ($x & $y) {
|
||||
$sender = $x[0];
|
||||
$recip = $y[0];
|
||||
} else {
|
||||
logger('notification: no sender or recipient.');
|
||||
logger('sender: ' . $params['from_xchan']);
|
||||
logger('recip: ' . $params['to_xchan']);
|
||||
return;
|
||||
}
|
||||
|
||||
// from here on everything is in the recipients language
|
||||
|
||||
push_lang($recip['account_language']); // should probably have a channel language
|
||||
|
||||
$banner = t('$Projectname Notification');
|
||||
$product = t('$projectname'); // PLATFORM_NAME;
|
||||
$siteurl = z_root();
|
||||
$thanks = t('Thank You,');
|
||||
$sitename = get_config('system','sitename');
|
||||
$site_admin = sprintf( t('%s Administrator'), $sitename);
|
||||
|
||||
$sender_name = $product;
|
||||
$hostname = \App::get_hostname();
|
||||
if(strpos($hostname,':'))
|
||||
$hostname = substr($hostname,0,strpos($hostname,':'));
|
||||
|
||||
// Do not translate 'noreply' as it must be a legal 7-bit email address
|
||||
|
||||
$reply_email = get_config('system','reply_address');
|
||||
if(! $reply_email)
|
||||
$reply_email = 'noreply' . '@' . $hostname;
|
||||
|
||||
$sender_email = get_config('system','from_email');
|
||||
if(! $sender_email)
|
||||
$sender_email = 'Administrator' . '@' . \App::get_hostname();
|
||||
|
||||
$sender_name = get_config('system','from_email_name');
|
||||
if(! $sender_name)
|
||||
$sender_name = \Zotlabs\Lib\System::get_site_name();
|
||||
|
||||
|
||||
$additional_mail_header = "";
|
||||
|
||||
if(array_key_exists('item', $params)) {
|
||||
require_once('include/conversation.php');
|
||||
// if it's a normal item...
|
||||
if (array_key_exists('verb', $params['item'])) {
|
||||
// localize_item() alters the original item so make a copy first
|
||||
$i = $params['item'];
|
||||
logger('calling localize');
|
||||
localize_item($i);
|
||||
$title = $i['title'];
|
||||
$body = $i['body'];
|
||||
$private = (($i['item_private']) || intval($i['item_obscured']));
|
||||
}
|
||||
else {
|
||||
$title = $params['item']['title'];
|
||||
$body = $params['item']['body'];
|
||||
}
|
||||
if($params['item']['created'] < datetime_convert('UTC','UTC','now - 1 month')) {
|
||||
logger('notification invoked for an old item which may have been refetched.',LOGGER_DEBUG,LOG_INFO);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$title = $body = '';
|
||||
}
|
||||
|
||||
|
||||
// e.g. "your post", "David's photo", etc.
|
||||
$possess_desc = t('%s <!item_type!>');
|
||||
|
||||
if ($params['type'] == NOTIFY_MAIL) {
|
||||
logger('notification: mail');
|
||||
$subject = sprintf( t('[$Projectname:Notify] New mail received at %s'),$sitename);
|
||||
|
||||
$preamble = sprintf( t('%1$s, %2$s sent you a new private message at %3$s.'),$recip['channel_name'], $sender['xchan_name'],$sitename);
|
||||
$epreamble = sprintf( t('%1$s sent you %2$s.'),'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]');
|
||||
$sitelink = t('Please visit %s to view and/or reply to your private messages.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl . '/mail/' . $params['item']['id'] );
|
||||
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/mail/' . $params['item']['id'] . '">' . $sitename . '</a>');
|
||||
$itemlink = $siteurl . '/mail/' . $params['item']['id'];
|
||||
}
|
||||
|
||||
if ($params['type'] == NOTIFY_COMMENT) {
|
||||
// logger("notification: params = " . print_r($params, true), LOGGER_DEBUG);
|
||||
|
||||
$itemlink = $params['link'];
|
||||
|
||||
// ignore like/unlike activity on posts - they probably require a separate notification preference
|
||||
|
||||
if (array_key_exists('item',$params) && (! visible_activity($params['item']))) {
|
||||
logger('notification: not a visible activity. Ignoring.');
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
$parent_mid = $params['parent_mid'];
|
||||
|
||||
// Check to see if there was already a notify for this post.
|
||||
// If so don't create a second notification
|
||||
|
||||
$p = null;
|
||||
$p = q("select id from notify where link = '%s' and uid = %d limit 1",
|
||||
dbesc($params['link']),
|
||||
intval($recip['channel_id'])
|
||||
);
|
||||
if ($p) {
|
||||
logger('notification: comment already notified');
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// if it's a post figure out who's post it is.
|
||||
|
||||
$p = null;
|
||||
|
||||
if($params['otype'] === 'item' && $parent_mid) {
|
||||
$p = q("select * from item where mid = '%s' and uid = %d limit 1",
|
||||
dbesc($parent_mid),
|
||||
intval($recip['channel_id'])
|
||||
);
|
||||
}
|
||||
|
||||
xchan_query($p);
|
||||
|
||||
|
||||
$item_post_type = item_post_type($p[0]);
|
||||
// $private = $p[0]['item_private'];
|
||||
$parent_id = $p[0]['id'];
|
||||
|
||||
$parent_item = $p[0];
|
||||
|
||||
//$possess_desc = str_replace('<!item_type!>',$possess_desc);
|
||||
|
||||
// "a post"
|
||||
$dest_str = sprintf(t('%1$s, %2$s commented on [zrl=%3$s]a %4$s[/zrl]'),
|
||||
$recip['channel_name'],
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$itemlink,
|
||||
$item_post_type);
|
||||
|
||||
// "George Bull's post"
|
||||
if($p)
|
||||
$dest_str = sprintf(t('%1$s, %2$s commented on [zrl=%3$s]%4$s\'s %5$s[/zrl]'),
|
||||
$recip['channel_name'],
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$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 commented on [zrl=%3$s]your %4$s[/zrl]'),
|
||||
$recip['channel_name'],
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$itemlink,
|
||||
$item_post_type);
|
||||
|
||||
// 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
|
||||
// differents subjects for messages on the same thread.
|
||||
|
||||
$subject = sprintf( t('[$Projectname:Notify] Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
|
||||
$preamble = sprintf( t('%1$s, %2$s commented on an item/conversation you have been following.'), $recip['channel_name'], $sender['xchan_name']);
|
||||
$epreamble = $dest_str;
|
||||
|
||||
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
|
||||
}
|
||||
|
||||
if ($params['type'] == NOTIFY_LIKE) {
|
||||
// logger("notification: params = " . print_r($params, true), LOGGER_DEBUG);
|
||||
|
||||
$itemlink = $params['link'];
|
||||
|
||||
// ignore like/unlike activity on posts - they probably require a separate notification preference
|
||||
|
||||
if (array_key_exists('item',$params) && (! activity_match($params['item']['verb'],ACTIVITY_LIKE))) {
|
||||
logger('notification: not a like activity. Ignoring.');
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
$parent_mid = $params['parent_mid'];
|
||||
|
||||
// Check to see if there was already a notify for this post.
|
||||
// If so don't create a second notification
|
||||
|
||||
$p = null;
|
||||
$p = q("select id from notify where link = '%s' and uid = %d limit 1",
|
||||
dbesc($params['link']),
|
||||
intval($recip['channel_id'])
|
||||
);
|
||||
if ($p) {
|
||||
logger('notification: like already notified');
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// if it's a post figure out who's post it is.
|
||||
|
||||
$p = null;
|
||||
|
||||
if($params['otype'] === 'item' && $parent_mid) {
|
||||
$p = q("select * from item where mid = '%s' and uid = %d limit 1",
|
||||
dbesc($parent_mid),
|
||||
intval($recip['channel_id'])
|
||||
);
|
||||
}
|
||||
|
||||
xchan_query($p);
|
||||
|
||||
|
||||
$item_post_type = item_post_type($p[0]);
|
||||
// $private = $p[0]['item_private'];
|
||||
$parent_id = $p[0]['id'];
|
||||
|
||||
$parent_item = $p[0];
|
||||
|
||||
|
||||
// "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 liked [zrl=%3$s]your %4$s[/zrl]'),
|
||||
$recip['channel_name'],
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$itemlink,
|
||||
$item_post_type);
|
||||
else {
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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, %2$s liked an item/conversation you created.'), $recip['channel_name'], $sender['xchan_name']);
|
||||
$epreamble = $dest_str;
|
||||
|
||||
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
|
||||
}
|
||||
|
||||
|
||||
|
||||
if($params['type'] == NOTIFY_WALL) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] %s posted to your profile wall') , $sender['xchan_name']);
|
||||
|
||||
$preamble = sprintf( t('%1$s, %2$s posted to your profile wall at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename);
|
||||
|
||||
$epreamble = sprintf( t('%1$s, %2$s posted to [zrl=%3$s]your wall[/zrl]') ,
|
||||
$recip['channel_name'],
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$params['link']);
|
||||
|
||||
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
if ($params['type'] == NOTIFY_TAGSELF) {
|
||||
|
||||
$p = null;
|
||||
$p = q("select id from notify where link = '%s' and uid = %d limit 1",
|
||||
dbesc($params['link']),
|
||||
intval($recip['channel_id'])
|
||||
);
|
||||
if ($p) {
|
||||
logger('enotify: tag: already notified about this post');
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
$subject = sprintf( t('[$Projectname:Notify] %s tagged you') , $sender['xchan_name']);
|
||||
$preamble = sprintf( t('%1$s, %2$s tagged you at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('%1$s, %2$s [zrl=%3$s]tagged you[/zrl].') ,
|
||||
$recip['channel_name'],
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$params['link']);
|
||||
|
||||
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
if ($params['type'] == NOTIFY_POKE) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] %1$s poked you') , $sender['xchan_name']);
|
||||
$preamble = sprintf( t('%1$s, %2$s poked you at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('%1$s, %2$s [zrl=%2$s]poked you[/zrl].') ,
|
||||
$recip['channel_name'],
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$params['link']);
|
||||
|
||||
$subject = str_replace('poked', t($params['activity']), $subject);
|
||||
$preamble = str_replace('poked', t($params['activity']), $preamble);
|
||||
$epreamble = str_replace('poked', t($params['activity']), $epreamble);
|
||||
|
||||
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
if ($params['type'] == NOTIFY_TAGSHARE) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] %s tagged your post') , $sender['xchan_name']);
|
||||
$preamble = sprintf( t('%1$s, %2$s tagged your post at %3$s') , $recip['channel_name'],$sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('%1$s, %2$s tagged [zrl=%3$s]your post[/zrl]') ,
|
||||
$recip['channel_name'],
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
|
||||
$itemlink);
|
||||
|
||||
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
if ($params['type'] == NOTIFY_INTRO) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] Introduction received'));
|
||||
$preamble = sprintf( t('%1$s, you\'ve received an new connection request from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('%1$s, you\'ve received [zrl=%2$s]a new connection request[/zrl] from %3$s.'),
|
||||
$recip['channel_name'],
|
||||
$siteurl . '/connections/ifpending',
|
||||
'[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.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl . '/connections/ifpending');
|
||||
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/connections/ifpending">' . $sitename . '</a>');
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
if ($params['type'] == NOTIFY_SUGGEST) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] Friend suggestion received'));
|
||||
$preamble = sprintf( t('%1$s, you\'ve received a friend suggestion from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('%1$s, you\'ve received [zrl=%2$s]a friend suggestion[/zrl] for %3$s from %4$s.'),
|
||||
$recip['channel_name'],
|
||||
$itemlink,
|
||||
'[zrl=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/zrl]',
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
|
||||
|
||||
$body = t('Name:') . ' ' . $params['item']['name'] . "\n";
|
||||
$body .= t('Photo:') . ' ' . $params['item']['photo'] . "\n";
|
||||
$body .= sprintf( t('You may visit their profile at %s'),$params['item']['url']);
|
||||
|
||||
$sitelink = t('Please visit %s to approve or reject the suggestion.');
|
||||
$tsitelink = sprintf( $sitelink, $siteurl );
|
||||
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
|
||||
$itemlink = $params['link'];
|
||||
}
|
||||
|
||||
if ($params['type'] == NOTIFY_CONFIRM) {
|
||||
// ?
|
||||
}
|
||||
|
||||
if ($params['type'] == NOTIFY_SYSTEM) {
|
||||
// ?
|
||||
}
|
||||
|
||||
$h = array(
|
||||
'params' => $params,
|
||||
'subject' => $subject,
|
||||
'preamble' => $preamble,
|
||||
'epreamble' => $epreamble,
|
||||
'body' => $body,
|
||||
'sitelink' => $sitelink,
|
||||
'sitename' => $sitename,
|
||||
'tsitelink' => $tsitelink,
|
||||
'hsitelink' => $hsitelink,
|
||||
'itemlink' => $itemlink,
|
||||
'sender' => $sender,
|
||||
'recipient' => $recip
|
||||
);
|
||||
|
||||
call_hooks('enotify', $h);
|
||||
|
||||
$subject = $h['subject'];
|
||||
$preamble = $h['preamble'];
|
||||
$epreamble = $h['epreamble'];
|
||||
$body = $h['body'];
|
||||
$sitelink = $h['sitelink'];
|
||||
$tsitelink = $h['tsitelink'];
|
||||
$hsitelink = $h['hsitelink'];
|
||||
$itemlink = $h['itemlink'];
|
||||
|
||||
|
||||
require_once('include/html2bbcode.php');
|
||||
|
||||
do {
|
||||
$dups = false;
|
||||
$hash = random_string();
|
||||
$r = q("SELECT id FROM notify WHERE hash = '%s' LIMIT 1",
|
||||
dbesc($hash));
|
||||
if ($r)
|
||||
$dups = true;
|
||||
} while ($dups === true);
|
||||
|
||||
|
||||
$datarray = array();
|
||||
$datarray['hash'] = $hash;
|
||||
$datarray['sender_hash'] = $sender['xchan_hash'];
|
||||
$datarray['xname'] = $sender['xchan_name'];
|
||||
$datarray['url'] = $sender['xchan_url'];
|
||||
$datarray['photo'] = $sender['xchan_photo_s'];
|
||||
$datarray['created'] = datetime_convert();
|
||||
$datarray['aid'] = $recip['channel_account_id'];
|
||||
$datarray['uid'] = $recip['channel_id'];
|
||||
$datarray['link'] = $itemlink;
|
||||
$datarray['parent'] = $parent_mid;
|
||||
$datarray['parent_item'] = $parent_item;
|
||||
$datarray['ntype'] = $params['type'];
|
||||
$datarray['verb'] = $params['verb'];
|
||||
$datarray['otype'] = $params['otype'];
|
||||
$datarray['abort'] = false;
|
||||
|
||||
$datarray['item'] = $params['item'];
|
||||
|
||||
call_hooks('enotify_store', $datarray);
|
||||
|
||||
if ($datarray['abort']) {
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// create notification entry in DB
|
||||
$seen = 0;
|
||||
|
||||
// Mark some notifications as seen right away
|
||||
// Note! The notification have to be created, because they are used to send emails
|
||||
// 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
|
||||
// (probably would be better that way)
|
||||
|
||||
$always_show_in_notices = get_pconfig($recip['channel_id'],'system','always_show_in_notices');
|
||||
|
||||
if (!$always_show_in_notices) {
|
||||
if (($params['type'] == NOTIFY_WALL) || ($params['type'] == NOTIFY_MAIL) || ($params['type'] == NOTIFY_INTRO)) {
|
||||
$seen = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$r = q("insert into notify (hash,xname,url,photo,created,aid,uid,link,parent,seen,ntype,verb,otype)
|
||||
values('%s','%s','%s','%s','%s',%d,%d,'%s','%s',%d,%d,'%s','%s')",
|
||||
dbesc($datarray['hash']),
|
||||
dbesc($datarray['xname']),
|
||||
dbesc($datarray['url']),
|
||||
dbesc($datarray['photo']),
|
||||
dbesc($datarray['created']),
|
||||
intval($datarray['aid']),
|
||||
intval($datarray['uid']),
|
||||
dbesc($datarray['link']),
|
||||
dbesc($datarray['parent']),
|
||||
intval($seen),
|
||||
intval($datarray['ntype']),
|
||||
dbesc($datarray['verb']),
|
||||
dbesc($datarray['otype'])
|
||||
);
|
||||
|
||||
$r = q("select id from notify where hash = '%s' and uid = %d limit 1",
|
||||
dbesc($hash),
|
||||
intval($recip['channel_id'])
|
||||
);
|
||||
if ($r) {
|
||||
$notify_id = $r[0]['id'];
|
||||
} else {
|
||||
logger('notification not found.');
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
$itemlink = z_root() . '/notify/view/' . $notify_id;
|
||||
$msg = str_replace('$itemlink',$itemlink,$epreamble);
|
||||
|
||||
// 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);
|
||||
|
||||
$r = q("update notify set msg = '%s' where id = %d and uid = %d",
|
||||
dbesc($msg),
|
||||
intval($notify_id),
|
||||
intval($datarray['uid'])
|
||||
);
|
||||
|
||||
// send email notification if notification preferences permit
|
||||
|
||||
require_once('bbcode.php');
|
||||
if ((intval($recip['channel_notifyflags']) & intval($params['type'])) || $params['type'] == NOTIFY_SYSTEM) {
|
||||
|
||||
logger('notification: sending notification email');
|
||||
|
||||
$hn = get_pconfig($recip['channel_id'],'system','email_notify_host');
|
||||
if($hn && (! stristr(\App::get_hostname(),$hn))) {
|
||||
// this isn't the email notification host
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
$textversion = strip_tags(html_entity_decode(bbcode(stripslashes(str_replace(array("\\r", "\\n"), array( "", "\n"), $body))),ENT_QUOTES,'UTF-8'));
|
||||
|
||||
$htmlversion = bbcode(stripslashes(str_replace(array("\\r","\\n"), array("","<br />\n"),$body)));
|
||||
|
||||
|
||||
// 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);
|
||||
|
||||
// unset when done to revert to normal behaviour
|
||||
|
||||
unset($_SESSION['zid_override']);
|
||||
unset($_SESSION['zrl_override']);
|
||||
|
||||
$datarray = array();
|
||||
$datarray['banner'] = $banner;
|
||||
$datarray['product'] = $product;
|
||||
$datarray['preamble'] = $preamble;
|
||||
$datarray['sitename'] = $sitename;
|
||||
$datarray['siteurl'] = $siteurl;
|
||||
$datarray['type'] = $params['type'];
|
||||
$datarray['parent'] = $params['parent_mid'];
|
||||
$datarray['source_name'] = $sender['xchan_name'];
|
||||
$datarray['source_link'] = $sender['xchan_url'];
|
||||
$datarray['source_photo'] = $sender['xchan_photo_s'];
|
||||
$datarray['uid'] = $recip['channel_id'];
|
||||
$datarray['username'] = $recip['channel_name'];
|
||||
$datarray['hsitelink'] = $hsitelink;
|
||||
$datarray['tsitelink'] = $tsitelink;
|
||||
$datarray['hitemlink'] = '<a href="' . $itemlink . '">' . $itemlink . '</a>';
|
||||
$datarray['titemlink'] = $itemlink;
|
||||
$datarray['thanks'] = $thanks;
|
||||
$datarray['site_admin'] = $site_admin;
|
||||
$datarray['title'] = stripslashes($title);
|
||||
$datarray['htmlversion'] = $htmlversion;
|
||||
$datarray['textversion'] = $textversion;
|
||||
$datarray['subject'] = $subject;
|
||||
$datarray['headers'] = $additional_mail_header;
|
||||
$datarray['email_secure'] = false;
|
||||
|
||||
call_hooks('enotify_mail', $datarray);
|
||||
|
||||
// Default to private - don't disclose message contents over insecure channels (such as email)
|
||||
// Might be interesting to use GPG,PGP,S/MIME encryption instead
|
||||
// but we'll save that for a clever plugin developer to implement
|
||||
|
||||
$private_activity = false;
|
||||
|
||||
if (! $datarray['email_secure']) {
|
||||
switch ($params['type']) {
|
||||
case NOTIFY_WALL:
|
||||
case NOTIFY_TAGSELF:
|
||||
case NOTIFY_POKE:
|
||||
case NOTIFY_COMMENT:
|
||||
if (! $private)
|
||||
break;
|
||||
$private_activity = true;
|
||||
case NOTIFY_MAIL:
|
||||
$datarray['textversion'] = $datarray['htmlversion'] = $datarray['title'] = '';
|
||||
$datarray['subject'] = preg_replace('/' . preg_quote(t('[$Projectname:Notify]')) . '/','$0*',$datarray['subject']);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($private_activity
|
||||
&& intval(get_pconfig($datarray['uid'], 'system', 'ignore_private_notifications'))) {
|
||||
|
||||
pop_lang();
|
||||
return;
|
||||
}
|
||||
|
||||
// load the template for private message notifications
|
||||
$tpl = get_markup_template('email_notify_html.tpl');
|
||||
$email_html_body = replace_macros($tpl,array(
|
||||
'$banner' => $datarray['banner'],
|
||||
'$notify_icon' => \Zotlabs\Lib\System::get_notify_icon(),
|
||||
'$product' => $datarray['product'],
|
||||
'$preamble' => $datarray['preamble'],
|
||||
'$sitename' => $datarray['sitename'],
|
||||
'$siteurl' => $datarray['siteurl'],
|
||||
'$source_name' => $datarray['source_name'],
|
||||
'$source_link' => $datarray['source_link'],
|
||||
'$source_photo' => $datarray['source_photo'],
|
||||
'$username' => $datarray['to_name'],
|
||||
'$hsitelink' => $datarray['hsitelink'],
|
||||
'$hitemlink' => $datarray['hitemlink'],
|
||||
'$thanks' => $datarray['thanks'],
|
||||
'$site_admin' => $datarray['site_admin'],
|
||||
'$title' => $datarray['title'],
|
||||
'$htmlversion' => $datarray['htmlversion'],
|
||||
));
|
||||
|
||||
// load the template for private message notifications
|
||||
$tpl = get_markup_template('email_notify_text.tpl');
|
||||
$email_text_body = replace_macros($tpl, array(
|
||||
'$banner' => $datarray['banner'],
|
||||
'$product' => $datarray['product'],
|
||||
'$preamble' => $datarray['preamble'],
|
||||
'$sitename' => $datarray['sitename'],
|
||||
'$siteurl' => $datarray['siteurl'],
|
||||
'$source_name' => $datarray['source_name'],
|
||||
'$source_link' => $datarray['source_link'],
|
||||
'$source_photo' => $datarray['source_photo'],
|
||||
'$username' => $datarray['to_name'],
|
||||
'$tsitelink' => $datarray['tsitelink'],
|
||||
'$titemlink' => $datarray['titemlink'],
|
||||
'$thanks' => $datarray['thanks'],
|
||||
'$site_admin' => $datarray['site_admin'],
|
||||
'$title' => $datarray['title'],
|
||||
'$textversion' => $datarray['textversion'],
|
||||
));
|
||||
|
||||
// logger('text: ' . $email_text_body);
|
||||
|
||||
// use the EmailNotification library to send the message
|
||||
|
||||
self::send(array(
|
||||
'fromName' => $sender_name,
|
||||
'fromEmail' => $sender_email,
|
||||
'replyTo' => $reply_email,
|
||||
'toEmail' => $recip['account_email'],
|
||||
'messageSubject' => $datarray['subject'],
|
||||
'htmlVersion' => $email_html_body,
|
||||
'textVersion' => $email_text_body,
|
||||
'additionalMailHeader' => $datarray['headers'],
|
||||
));
|
||||
}
|
||||
|
||||
pop_lang();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a multipart/alternative message with Text and HTML versions.
|
||||
*
|
||||
* @param array $params an assoziative array with:
|
||||
* * \e string \b fromName name of the sender
|
||||
* * \e string \b fromEmail email of the sender
|
||||
* * \e string \b replyTo replyTo address to direct responses
|
||||
* * \e string \b toEmail destination email address
|
||||
* * \e string \b messageSubject subject of the message
|
||||
* * \e string \b htmlVersion html version of the message
|
||||
* * \e string \b textVersion text only version of the message
|
||||
* * \e string \b additionalMailHeader additions to the smtp mail header
|
||||
*/
|
||||
static public function send($params) {
|
||||
|
||||
$params['sent'] = false;
|
||||
$params['result'] = false;
|
||||
|
||||
call_hooks('email_send', $params);
|
||||
|
||||
if($params['sent']) {
|
||||
logger("notification: enotify::send (addon) returns " . (($params['result']) ? 'success' : 'failure'), LOGGER_DEBUG);
|
||||
return $params['result'];
|
||||
}
|
||||
|
||||
$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
|
||||
$mimeBoundary = rand(0, 9) . "-"
|
||||
.rand(10000000000, 9999999999) . "-"
|
||||
.rand(10000000000, 9999999999) . "=:"
|
||||
.rand(10000, 99999);
|
||||
|
||||
// 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" .
|
||||
"Content-Type: multipart/alternative; boundary=\"{$mimeBoundary}\"";
|
||||
|
||||
// assemble the final multipart message body with the text and html types included
|
||||
$textBody = chunk_split(base64_encode($params['textVersion']));
|
||||
$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
|
||||
|
||||
// send the message
|
||||
$res = mail(
|
||||
$params['toEmail'], // send to address
|
||||
$messageSubject, // subject
|
||||
$multipartMessageBody, // message body
|
||||
$messageHeader // message headers
|
||||
);
|
||||
logger("notification: enotify::send returns " . (($res) ? 'success' : 'failure'), LOGGER_DEBUG);
|
||||
return $res;
|
||||
}
|
||||
|
||||
static public function format($item) {
|
||||
|
||||
$ret = '';
|
||||
|
||||
require_once('include/conversation.php');
|
||||
|
||||
// Call localize_item to get a one line status for activities.
|
||||
// This should set $item['localized'] to indicate we have a brief summary.
|
||||
|
||||
localize_item($item);
|
||||
|
||||
if($item['localize']) {
|
||||
$itemem_text = $item['localize'];
|
||||
}
|
||||
else {
|
||||
$itemem_text = (($item['item_thread_top'])
|
||||
? t('created a new post')
|
||||
: sprintf( t('commented on %s\'s post'), $item['owner']['xchan_name']));
|
||||
}
|
||||
|
||||
// convert this logic into a json array just like the system notifications
|
||||
|
||||
return array(
|
||||
'notify_link' => $item['llink'],
|
||||
'name' => $item['author']['xchan_name'],
|
||||
'url' => $item['author']['xchan_url'],
|
||||
'photo' => $item['author']['xchan_photo_s'],
|
||||
'when' => relative_date($item['created']),
|
||||
'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'),
|
||||
'message' => strip_tags(bbcode($itemem_text))
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
57
Zotlabs/Lib/ExtendedZip.php
Normal file
57
Zotlabs/Lib/ExtendedZip.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/**
|
||||
* Description of ExtendedZip
|
||||
*
|
||||
* @author andrew
|
||||
*/
|
||||
class ExtendedZip extends \ZipArchive {
|
||||
|
||||
// Member function to add a whole file system subtree to the archive
|
||||
public function addTree($dirname, $localname = '') {
|
||||
if ($localname)
|
||||
$this->addEmptyDir($localname);
|
||||
$this->_addTree($dirname, $localname);
|
||||
}
|
||||
|
||||
// Internal function, to recurse
|
||||
protected function _addTree($dirname, $localname) {
|
||||
$dir = opendir($dirname);
|
||||
while ($filename = readdir($dir)) {
|
||||
// Discard . and ..
|
||||
if ($filename == '.' || $filename == '..')
|
||||
continue;
|
||||
|
||||
// Proceed according to type
|
||||
$path = $dirname . '/' . $filename;
|
||||
$localpath = $localname ? ($localname . '/' . $filename) : $filename;
|
||||
if (is_dir($path)) {
|
||||
// Directory: add & recurse
|
||||
$this->addEmptyDir($localpath);
|
||||
$this->_addTree($path, $localpath);
|
||||
}
|
||||
else if (is_file($path)) {
|
||||
// File: just add
|
||||
$this->addFile($path, $localpath);
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
}
|
||||
|
||||
// Helper function
|
||||
public static function zipTree($dirname, $zipFilename, $flags = 0, $localname = '') {
|
||||
$zip = new self();
|
||||
$zip->open($zipFilename, $flags);
|
||||
$zip->addTree($dirname, $localname);
|
||||
$zip->close();
|
||||
}
|
||||
|
||||
}
|
||||
165
Zotlabs/Lib/IConfig.php
Normal file
165
Zotlabs/Lib/IConfig.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
|
||||
|
||||
class IConfig {
|
||||
|
||||
static public function Load(&$item) {
|
||||
return;
|
||||
}
|
||||
|
||||
static public function Get(&$item, $family, $key, $default = false) {
|
||||
|
||||
$is_item = false;
|
||||
|
||||
if(is_array($item)) {
|
||||
$is_item = true;
|
||||
if((! array_key_exists('iconfig',$item)) || (! is_array($item['iconfig'])))
|
||||
$item['iconfig'] = array();
|
||||
|
||||
if(array_key_exists('item_id',$item))
|
||||
$iid = $item['item_id'];
|
||||
else
|
||||
$iid = $item['id'];
|
||||
}
|
||||
elseif(intval($item))
|
||||
$iid = $item;
|
||||
|
||||
if(! $iid)
|
||||
return $default;
|
||||
|
||||
if(is_array($item) && array_key_exists('iconfig',$item) && is_array($item['iconfig'])) {
|
||||
foreach($item['iconfig'] as $c) {
|
||||
if($c['iid'] == $iid && $c['cat'] == $family && $c['k'] == $key)
|
||||
return $c['v'];
|
||||
}
|
||||
}
|
||||
|
||||
$r = q("select * from iconfig where iid = %d and cat = '%s' and k = '%s' limit 1",
|
||||
intval($iid),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
if($r) {
|
||||
$r[0]['v'] = ((preg_match('|^a:[0-9]+:{.*}$|s',$r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']);
|
||||
if($is_item)
|
||||
$item['iconfig'][] = $r[0];
|
||||
return $r[0]['v'];
|
||||
}
|
||||
return $default;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* IConfig::Set(&$item, $family, $key, $value, $sharing = false);
|
||||
*
|
||||
* $item - item array or item id. If passed an array the iconfig meta information is
|
||||
* added to the item structure (which will need to be saved with item_store eventually).
|
||||
* If passed an id, the DB is updated, but may not be federated and/or cloned.
|
||||
* $family - namespace of meta variable
|
||||
* $key - key of meta variable
|
||||
* $value - value of meta variable
|
||||
* $sharing - boolean (default false); if true the meta information is propagated with the item
|
||||
* to other sites/channels, mostly useful when $item is an array and has not yet been stored/delivered.
|
||||
* If the meta information is added after delivery and you wish it to be shared, it may be necessary to
|
||||
* alter the item edited timestamp and invoke the delivery process on the updated item. The edited
|
||||
* timestamp needs to be altered in order to trigger an item_store_update() at the receiving end.
|
||||
*/
|
||||
|
||||
|
||||
static public function Set(&$item, $family, $key, $value, $sharing = false) {
|
||||
|
||||
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
|
||||
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
|
||||
|
||||
$is_item = false;
|
||||
$idx = null;
|
||||
|
||||
if(is_array($item)) {
|
||||
$is_item = true;
|
||||
if((! array_key_exists('iconfig',$item)) || (! is_array($item['iconfig'])))
|
||||
$item['iconfig'] = array();
|
||||
elseif($item['iconfig']) {
|
||||
for($x = 0; $x < count($item['iconfig']); $x ++) {
|
||||
if($item['iconfig'][$x]['cat'] == $family && $item['iconfig'][$x]['k'] == $key) {
|
||||
$idx = $x;
|
||||
}
|
||||
}
|
||||
}
|
||||
$entry = array('cat' => $family, 'k' => $key, 'v' => $value, 'sharing' => $sharing);
|
||||
|
||||
if(is_null($idx))
|
||||
$item['iconfig'][] = $entry;
|
||||
else
|
||||
$item['iconfig'][$idx] = $entry;
|
||||
return $value;
|
||||
}
|
||||
|
||||
if(intval($item))
|
||||
$iid = intval($item);
|
||||
|
||||
if(! $iid)
|
||||
return false;
|
||||
|
||||
if(self::Get($item, $family, $key) === false) {
|
||||
$r = q("insert into iconfig( iid, cat, k, v, sharing ) values ( %d, '%s', '%s', '%s', %d ) ",
|
||||
intval($iid),
|
||||
dbesc($family),
|
||||
dbesc($key),
|
||||
dbesc($dbvalue),
|
||||
intval($sharing)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$r = q("update iconfig set v = '%s', sharing = %d where iid = %d and cat = '%s' and k = '%s' ",
|
||||
dbesc($dbvalue),
|
||||
intval($sharing),
|
||||
intval($iid),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
}
|
||||
|
||||
if(! $r)
|
||||
return false;
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static public function Delete(&$item, $family, $key) {
|
||||
|
||||
|
||||
$is_item = false;
|
||||
$idx = null;
|
||||
|
||||
if(is_array($item)) {
|
||||
$is_item = true;
|
||||
if(is_array($item['iconfig'])) {
|
||||
for($x = 0; $x < count($item['iconfig']); $x ++) {
|
||||
if($item['iconfig'][$x]['cat'] == $family && $item['iconfig'][$x]['k'] == $key) {
|
||||
unset($item['iconfig'][$x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(intval($item))
|
||||
$iid = intval($item);
|
||||
|
||||
if(! $iid)
|
||||
return false;
|
||||
|
||||
return q("delete from iconfig where iid = %d and cat = '%s' and k = '%s' ",
|
||||
intval($iid),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
210
Zotlabs/Lib/NativeWiki.php
Normal file
210
Zotlabs/Lib/NativeWiki.php
Normal file
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
define ( 'NWIKI_ITEM_RESOURCE_TYPE', 'nwiki' );
|
||||
|
||||
class NativeWiki {
|
||||
|
||||
|
||||
static public function listwikis($channel, $observer_hash) {
|
||||
|
||||
$sql_extra = item_permissions_sql($channel['channel_id'], $observer_hash);
|
||||
$wikis = q("SELECT * FROM item
|
||||
WHERE resource_type = '%s' AND mid = parent_mid AND uid = %d AND item_deleted = 0 $sql_extra",
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
|
||||
if($wikis) {
|
||||
foreach($wikis as &$w) {
|
||||
$w['rawName'] = get_iconfig($w, 'wiki', 'rawName');
|
||||
$w['htmlName'] = escape_tags($w['rawName']);
|
||||
$w['urlName'] = urlencode(urlencode($w['rawName']));
|
||||
$w['mimeType'] = get_iconfig($w, 'wiki', 'mimeType');
|
||||
$w['lock'] = (($w['item_private'] || $w['allow_cid'] || $w['allow_gid'] || $w['deny_cid'] || $w['deny_gid']) ? true : false);
|
||||
}
|
||||
}
|
||||
// TODO: query db for wikis the observer can access. Return with two lists, for read and write access
|
||||
return array('wikis' => $wikis);
|
||||
}
|
||||
|
||||
|
||||
function create_wiki($channel, $observer_hash, $wiki, $acl) {
|
||||
|
||||
// Generate unique resource_id using the same method as item_message_id()
|
||||
do {
|
||||
$dups = false;
|
||||
$resource_id = random_string();
|
||||
$r = q("SELECT mid FROM item WHERE resource_id = '%s' AND resource_type = '%s' AND uid = %d LIMIT 1",
|
||||
dbesc($resource_id),
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
if($r)
|
||||
$dups = true;
|
||||
} while($dups == true);
|
||||
|
||||
$ac = $acl->get();
|
||||
$mid = item_message_id();
|
||||
|
||||
$arr = array(); // Initialize the array of parameters for the post
|
||||
$item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0);
|
||||
$wiki_url = z_root() . '/wiki/' . $channel['channel_address'] . '/' . $wiki['urlName'];
|
||||
$arr['aid'] = $channel['channel_account_id'];
|
||||
$arr['uid'] = $channel['channel_id'];
|
||||
$arr['mid'] = $mid;
|
||||
$arr['parent_mid'] = $mid;
|
||||
$arr['item_hidden'] = $item_hidden;
|
||||
$arr['resource_type'] = NWIKI_ITEM_RESOURCE_TYPE;
|
||||
$arr['resource_id'] = $resource_id;
|
||||
$arr['owner_xchan'] = $channel['channel_hash'];
|
||||
$arr['author_xchan'] = $observer_hash;
|
||||
$arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']);
|
||||
$arr['llink'] = $arr['plink'];
|
||||
$arr['title'] = $wiki['htmlName']; // name of new wiki;
|
||||
$arr['allow_cid'] = $ac['allow_cid'];
|
||||
$arr['allow_gid'] = $ac['allow_gid'];
|
||||
$arr['deny_cid'] = $ac['deny_cid'];
|
||||
$arr['deny_gid'] = $ac['deny_gid'];
|
||||
$arr['item_wall'] = 1;
|
||||
$arr['item_origin'] = 1;
|
||||
$arr['item_thread_top'] = 1;
|
||||
$arr['item_private'] = intval($acl->is_private());
|
||||
$arr['verb'] = ACTIVITY_CREATE;
|
||||
$arr['obj_type'] = ACTIVITY_OBJ_WIKI;
|
||||
$arr['body'] = '[table][tr][td][h1]New Wiki[/h1][/td][/tr][tr][td][zrl=' . $wiki_url . ']' . $wiki['htmlName'] . '[/zrl][/td][/tr][/table]';
|
||||
|
||||
$arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_wiki'),true);
|
||||
|
||||
// Save the wiki name information using iconfig. This is shareable.
|
||||
if(! set_iconfig($arr, 'wiki', 'rawName', $wiki['rawName'], true)) {
|
||||
return array('item' => null, 'success' => false);
|
||||
}
|
||||
if(! set_iconfig($arr, 'wiki', 'mimeType', $wiki['mimeType'], true)) {
|
||||
return array('item' => null, 'success' => false);
|
||||
}
|
||||
|
||||
$post = item_store($arr);
|
||||
|
||||
$item_id = $post['item_id'];
|
||||
|
||||
if($item_id) {
|
||||
\Zotlabs\Daemon\Master::Summon(array('Notifier', 'activity', $item_id));
|
||||
return array('item' => $post['item'], 'item_id' => $item_id, 'success' => true);
|
||||
}
|
||||
else {
|
||||
return array('item' => null, 'success' => false);
|
||||
}
|
||||
}
|
||||
|
||||
static public 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 = %d )) ",
|
||||
intval($uid),
|
||||
intval($id),
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
intval($resource_id)
|
||||
);
|
||||
if($r) {
|
||||
xchan_query($r);
|
||||
$sync_item = fetch_post_tags($r);
|
||||
build_sync_packet($uid,array('wiki' => array(encode_item($sync_item[0],true))));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
info( t('Wiki files deleted successfully'));
|
||||
|
||||
return array('item' => $item, 'item_id' => $item['id'], 'success' => (($drop === 1) ? true : false));
|
||||
}
|
||||
|
||||
|
||||
static public 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",
|
||||
intval($channel_id),
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
dbesc($resource_id)
|
||||
);
|
||||
if(! $item) {
|
||||
return array('wiki' => null);
|
||||
}
|
||||
else {
|
||||
|
||||
$w = $item[0]; // wiki item table record
|
||||
// Get wiki metadata
|
||||
$rawName = get_iconfig($w, 'wiki', 'rawName');
|
||||
$mimeType = get_iconfig($w, 'wiki', 'mimeType');
|
||||
|
||||
return array(
|
||||
'wiki' => $w,
|
||||
'rawName' => $rawName,
|
||||
'htmlName' => escape_tags($rawName),
|
||||
'urlName' => urlencode(urlencode($rawName)),
|
||||
'mimeType' => $mimeType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static public function exists_by_name($uid, $urlName) {
|
||||
|
||||
$sql_extra = item_permissions_sql($uid);
|
||||
|
||||
$item = q("SELECT item.id, resource_id FROM item left join iconfig on iconfig.iid = item.id
|
||||
WHERE resource_type = '%s' AND iconfig.v = '%s' AND uid = %d
|
||||
AND item_deleted = 0 $sql_extra limit 1",
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
dbesc(urldecode($urlName)),
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
if($item) {
|
||||
return array('id' => $item[0]['id'], 'resource_id' => $item[0]['resource_id']);
|
||||
}
|
||||
else {
|
||||
return array('id' => null, 'resource_id' => null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static public function get_permissions($resource_id, $owner_id, $observer_hash) {
|
||||
// TODO: For now, only the owner can edit
|
||||
$sql_extra = item_permissions_sql($owner_id, $observer_hash);
|
||||
|
||||
if(local_channel() && local_channel() == $owner_id) {
|
||||
return [ 'read' => true, 'write' => true, 'success' => true ];
|
||||
}
|
||||
|
||||
$r = q("SELECT * FROM item WHERE uid = %d and resource_type = '%s' AND resource_id = '%s' $sql_extra LIMIT 1",
|
||||
intval($owner_id),
|
||||
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
|
||||
dbesc($resource_id)
|
||||
);
|
||||
|
||||
if(! $r) {
|
||||
return array('read' => false, 'write' => false, 'success' => true);
|
||||
}
|
||||
else {
|
||||
// TODO: Create a new permission setting for wiki analogous to webpages. Until
|
||||
// then, use webpage permissions
|
||||
$write = perm_is_allowed($owner_id, $observer_hash,'write_wiki');
|
||||
return array('read' => true, 'write' => $write, 'success' => true);
|
||||
}
|
||||
}
|
||||
}
|
||||
664
Zotlabs/Lib/NativeWikiPage.php
Normal file
664
Zotlabs/Lib/NativeWikiPage.php
Normal file
@@ -0,0 +1,664 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use \Zotlabs\Lib as Zlib;
|
||||
|
||||
class NativeWikiPage {
|
||||
|
||||
static public function page_list($channel_id,$observer_hash, $resource_id) {
|
||||
|
||||
// TODO: Create item table records for pages so that metadata like title can be applied
|
||||
$w = Zlib\NativeWiki::get_wiki($channel_id,$observer_hash,$resource_id);
|
||||
|
||||
$pages[] = [
|
||||
'resource_id' => '',
|
||||
'title' => 'Home',
|
||||
'url' => 'Home',
|
||||
'link_id' => 'id_wiki_home_0'
|
||||
];
|
||||
|
||||
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
|
||||
|
||||
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and item_deleted = 0
|
||||
$sql_extra group by mid",
|
||||
dbesc($resource_id),
|
||||
intval($channel_id)
|
||||
);
|
||||
if($r) {
|
||||
$items = fetch_post_tags($r,true);
|
||||
foreach($items as $page_item) {
|
||||
$title = get_iconfig($page_item['id'],'nwikipage','pagetitle',t('(No Title)'));
|
||||
if(urldecode($title) !== 'Home') {
|
||||
$pages[] = [
|
||||
'resource_id' => $resource_id,
|
||||
'title' => escape_tags($title),
|
||||
'url' => urlencode(urlencode($title)),
|
||||
'link_id' => 'id_' . substr($resource_id, 0, 10) . '_' . $page_item['id']
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array('pages' => $pages, 'wiki' => $w);
|
||||
}
|
||||
|
||||
|
||||
static public function create_page($channel_id, $observer_hash, $name, $resource_id) {
|
||||
|
||||
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
|
||||
if (! $w['wiki']) {
|
||||
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
|
||||
}
|
||||
|
||||
// create an empty activity
|
||||
|
||||
$arr = [];
|
||||
$arr['uid'] = $channel_id;
|
||||
$arr['author_xchan'] = $observer_hash;
|
||||
$arr['resource_type'] = 'nwikipage';
|
||||
$arr['resource_id'] = $resource_id;
|
||||
$arr['allow_cid'] = $w['wiki']['allow_cid'];
|
||||
$arr['allow_gid'] = $w['wiki']['allow_gid'];
|
||||
$arr['deny_cid'] = $w['wiki']['deny_cid'];
|
||||
$arr['deny_gid'] = $w['wiki']['deny_gid'];
|
||||
|
||||
$arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel_id,'view_wiki'),true);
|
||||
|
||||
// We may wish to change this some day.
|
||||
$arr['item_unpublished'] = 1;
|
||||
|
||||
set_iconfig($arr,'nwikipage','pagetitle',(($name) ? $name : t('(No Title)')),true);
|
||||
|
||||
$p = post_activity_item($arr, false, false);
|
||||
|
||||
if($p['item_id']) {
|
||||
$page = [
|
||||
'rawName' => $name,
|
||||
'htmlName' => escape_tags($name),
|
||||
'urlName' => urlencode($name),
|
||||
|
||||
];
|
||||
|
||||
return array('page' => $page, 'item_id' => $p['item_id'], 'item' => $p['activity'], 'wiki' => $w, 'message' => '', 'success' => true);
|
||||
}
|
||||
return [ 'success' => false, 'message' => t('Wiki page create failed.') ];
|
||||
}
|
||||
|
||||
static public function rename_page($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
|
||||
$pageNewName = ((array_key_exists('pageNewName',$arr)) ? $arr['pageNewName'] : '');
|
||||
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
|
||||
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
|
||||
|
||||
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if(! $w['wiki']) {
|
||||
return array('message' => t('Wiki not found.'), 'success' => false);
|
||||
}
|
||||
|
||||
|
||||
$ic = q("select * from iconfig left join item on iconfig.iid = item.id
|
||||
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($pageNewName)
|
||||
);
|
||||
|
||||
if($ic) {
|
||||
return [ 'success' => false, 'message' => t('Destination name already exists') ];
|
||||
}
|
||||
|
||||
|
||||
$ids = [];
|
||||
|
||||
$ic = q("select *, item.id as item_id from iconfig left join item on iconfig.iid = item.id
|
||||
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($pageUrlName)
|
||||
);
|
||||
|
||||
if($ic) {
|
||||
foreach($ic as $c) {
|
||||
set_iconfig($c['item_id'],'nwikipage','pagetitle',$pageNewName);
|
||||
}
|
||||
|
||||
$page = [
|
||||
'rawName' => $pageNewName,
|
||||
'htmlName' => escape_tags($pageNewName),
|
||||
'urlName' => urlencode(escape_tags($pageNewName))
|
||||
];
|
||||
|
||||
return [ 'success' => true, 'page' => $page ];
|
||||
}
|
||||
|
||||
return [ 'success' => false, 'item_id' => $c['item_id'], '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'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id',$arr)) ? intval($arr['channel_id']) : 0);
|
||||
$revision = ((array_key_exists('revision',$arr)) ? intval($arr['revision']) : (-1));
|
||||
|
||||
|
||||
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if (! $w['wiki']) {
|
||||
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
|
||||
}
|
||||
|
||||
$item = self::load_page($arr);
|
||||
|
||||
if($item) {
|
||||
$content = $item['body'];
|
||||
|
||||
return [
|
||||
'content' => json_encode($content),
|
||||
'mimeType' => $w['mimeType'],
|
||||
'message' => '',
|
||||
'success' => true
|
||||
];
|
||||
}
|
||||
|
||||
return array('content' => null, 'message' => t('Error reading page content'), 'success' => false);
|
||||
|
||||
}
|
||||
|
||||
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'] : '');
|
||||
$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 array('history' => null, 'message' => 'Error reading wiki', 'success' => false);
|
||||
}
|
||||
|
||||
$items = self::load_page_history($arr);
|
||||
|
||||
$history = [];
|
||||
|
||||
if($items) {
|
||||
$processed = 0;
|
||||
foreach($items as $item) {
|
||||
if($processed > 1000)
|
||||
break;
|
||||
$processed ++;
|
||||
$history[] = [
|
||||
'revision' => $item['revision'],
|
||||
'date' => datetime_convert('UTC',date_default_timezone_get(),$item['edited']),
|
||||
'name' => $item['author']['xchan_name'],
|
||||
'title' => get_iconfig($item,'nwikipage','commit_msg')
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return [ 'success' => true, 'history' => $history ];
|
||||
}
|
||||
|
||||
return [ 'success' => false ];
|
||||
|
||||
}
|
||||
|
||||
|
||||
static public function load_page($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
|
||||
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
|
||||
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
|
||||
$revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : (-1));
|
||||
|
||||
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
|
||||
if (! $w['wiki']) {
|
||||
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
|
||||
}
|
||||
|
||||
$ids = '';
|
||||
|
||||
$ic = q("select * from iconfig left join item on iconfig.iid = item.id where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($pageUrlName)
|
||||
);
|
||||
|
||||
if($ic) {
|
||||
foreach($ic as $c) {
|
||||
if($ids)
|
||||
$ids .= ',';
|
||||
$ids .= intval($c['iid']);
|
||||
}
|
||||
}
|
||||
|
||||
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
|
||||
|
||||
if($revision == (-1))
|
||||
$sql_extra .= " order by revision desc ";
|
||||
elseif($revision)
|
||||
$sql_extra .= " and revision = " . intval($revision) . " ";
|
||||
|
||||
$r = null;
|
||||
|
||||
|
||||
if($ids) {
|
||||
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) $sql_extra limit 1",
|
||||
dbesc($resource_id),
|
||||
intval($channel_id)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
$items = fetch_post_tags($r,true);
|
||||
return $items[0];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static public function load_page_history($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
|
||||
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
|
||||
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
|
||||
$revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : (-1));
|
||||
|
||||
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if (! $w['wiki']) {
|
||||
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
|
||||
}
|
||||
|
||||
$ids = '';
|
||||
|
||||
$ic = q("select * from iconfig left join item on iconfig.iid = item.id where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($pageUrlName)
|
||||
);
|
||||
|
||||
if($ic) {
|
||||
foreach($ic as $c) {
|
||||
if($ids)
|
||||
$ids .= ',';
|
||||
$ids .= intval($c['iid']);
|
||||
}
|
||||
}
|
||||
|
||||
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
|
||||
|
||||
$sql_extra .= " order by revision desc ";
|
||||
|
||||
$r = null;
|
||||
if($ids) {
|
||||
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) and item_deleted = 0 $sql_extra",
|
||||
dbesc($resource_id),
|
||||
intval($channel_id)
|
||||
);
|
||||
if($r) {
|
||||
xchan_query($r);
|
||||
$items = fetch_post_tags($r,true);
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static public function prepare_content($s) {
|
||||
|
||||
$text = preg_replace_callback('{
|
||||
(?:\n\n|\A\n?)
|
||||
( # $1 = the code block -- one or more lines, starting with a space/tab
|
||||
(?>
|
||||
[ ]{'.'4'.'} # Lines must start with a tab or a tab-width of spaces
|
||||
.*\n+
|
||||
)+
|
||||
)
|
||||
((?=^[ ]{0,'.'4'.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
|
||||
}xm',
|
||||
'self::nwiki_prepare_content_callback', $s);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
static public function nwiki_prepare_content_callback($matches) {
|
||||
$codeblock = $matches[1];
|
||||
|
||||
$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES, UTF8, false);
|
||||
return "\n\n" . $codeblock ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static public function save_page($arr) {
|
||||
|
||||
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
|
||||
$content = ((array_key_exists('content',$arr)) ? purify_html(Zlib\NativeWikiPage::prepare_content($arr['content'])) : '');
|
||||
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
|
||||
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
|
||||
$revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : 0);
|
||||
|
||||
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
|
||||
if (!$w['wiki']) {
|
||||
return array('message' => t('Error reading wiki'), 'success' => false);
|
||||
}
|
||||
|
||||
// fetch the most recently saved revision.
|
||||
|
||||
$item = self::load_page($arr);
|
||||
if(! $item) {
|
||||
return array('message' => t('Page not found'), 'success' => false);
|
||||
}
|
||||
|
||||
// change just the fields we need to change to create a revision;
|
||||
|
||||
unset($item['id']);
|
||||
unset($item['author']);
|
||||
|
||||
$item['parent'] = 0;
|
||||
$item['body'] = $content;
|
||||
$item['author_xchan'] = $observer_hash;
|
||||
$item['revision'] = (($arr['revision']) ? intval($arr['revision']) + 1 : intval($item['revision']) + 1);
|
||||
$item['edited'] = datetime_convert();
|
||||
|
||||
if($item['iconfig'] && is_array($item['iconfig']) && count($item['iconfig'])) {
|
||||
for($x = 0; $x < count($item['iconfig']); $x ++) {
|
||||
unset($item['iconfig'][$x]['id']);
|
||||
unset($item['iconfig'][$x]['iid']);
|
||||
}
|
||||
}
|
||||
|
||||
$ret = item_store($item, false, false);
|
||||
|
||||
if($ret['item_id'])
|
||||
return array('message' => '', 'item_id' => $ret['item_id'], 'filename' => $filename, '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);
|
||||
|
||||
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
|
||||
if(! $w['wiki']) {
|
||||
return [ 'success' => false, 'message' => t('Error reading wiki') ];
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
$ic = q("select * from iconfig left join item on iconfig.iid = item.id
|
||||
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
|
||||
intval($channel_id),
|
||||
dbesc($pageUrlName)
|
||||
);
|
||||
|
||||
if($ic) {
|
||||
foreach($ic as $c) {
|
||||
$ids[] = intval($c['iid']);
|
||||
}
|
||||
}
|
||||
|
||||
if($ids) {
|
||||
drop_items($ids);
|
||||
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);
|
||||
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
|
||||
|
||||
if (! $commitHash) {
|
||||
return array('content' => $content, '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);
|
||||
}
|
||||
|
||||
$x = $arr;
|
||||
|
||||
if(intval($commitHash) > 0) {
|
||||
unset($x['commitHash']);
|
||||
$x['revision'] = intval($commitHash) - 1;
|
||||
$loaded = self::load_page($x);
|
||||
|
||||
if($loaded) {
|
||||
$content = $loaded['body'];
|
||||
return [ 'content' => $content, 'success' => true ];
|
||||
}
|
||||
return [ 'content' => $content, '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));
|
||||
$compareCommit = ((array_key_exists('compareCommit',$arr)) ? $arr['compareCommit'] : 0);
|
||||
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
|
||||
|
||||
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
|
||||
if (!$w['wiki']) {
|
||||
return array('message' => t('Error reading wiki'), 'success' => false);
|
||||
}
|
||||
|
||||
$x = $arr;
|
||||
$x['revision'] = (-1);
|
||||
|
||||
$currpage = self::load_page($x);
|
||||
if($currpage)
|
||||
$currentContent = $currpage['body'];
|
||||
|
||||
$x['revision'] = $compareCommit;
|
||||
$comppage = self::load_page($x);
|
||||
if($comppage)
|
||||
$compareContent = $comppage['body'];
|
||||
|
||||
if($currpage && $comppage) {
|
||||
require_once('library/class.Diff.php');
|
||||
$diff = \Diff::toTable(\Diff::compare($currentContent, $compareContent));
|
||||
|
||||
return [ 'success' => true, 'diff' => $diff ];
|
||||
}
|
||||
return [ 'success' => false, 'message' => t('Compare: object not found.') ];
|
||||
|
||||
}
|
||||
|
||||
static public function commit($arr) {
|
||||
|
||||
$commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : t('Page updated'));
|
||||
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
|
||||
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
|
||||
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : t('Untitled'));
|
||||
|
||||
if(array_key_exists('resource_id', $arr)) {
|
||||
$resource_id = $arr['resource_id'];
|
||||
}
|
||||
else {
|
||||
return array('message' => t('Wiki resource_id required for git commit'), 'success' => false);
|
||||
}
|
||||
|
||||
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
|
||||
if (! $w['wiki']) {
|
||||
return array('message' => t('Error reading wiki'), 'success' => false);
|
||||
}
|
||||
|
||||
|
||||
$page = self::load_page($arr);
|
||||
|
||||
if($page) {
|
||||
set_iconfig($page['id'],'nwikipage','commit_msg',escape_tags($commit_msg),true);
|
||||
return [ 'success' => true, 'item_id' => $page['id'], 'page' => $page ];
|
||||
}
|
||||
|
||||
return [ 'success' => false, 'message' => t('Page not found.') ];
|
||||
|
||||
}
|
||||
|
||||
static public function convert_links($s, $wikiURL) {
|
||||
|
||||
if (strpos($s,'[[') !== false) {
|
||||
preg_match_all("/\[\[(.*?)\]\]/", $s, $match);
|
||||
$pages = $pageURLs = array();
|
||||
foreach ($match[1] as $m) {
|
||||
// TODO: Why do we need to double urlencode for this to work?
|
||||
$pageURLs[] = urlencode(urlencode(escape_tags($m)));
|
||||
$pages[] = $m;
|
||||
}
|
||||
$idx = 0;
|
||||
while(strpos($s,'[[') !== false) {
|
||||
$replace = '<a href="'.$wikiURL.'/'.$pageURLs[$idx].'">'.$pages[$idx].'</a>';
|
||||
$s = preg_replace("/\[\[(.*?)\]\]/", $replace, $s, 1);
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the instances of the string [toc] with a list element that will be populated by
|
||||
* a table of contents by the JavaScript library
|
||||
* @param string $s
|
||||
* @return string
|
||||
*/
|
||||
static public function generate_toc($s) {
|
||||
if (strpos($s,'[toc]') !== false) {
|
||||
//$toc_md = wiki_toc($s); // Generate Markdown-formatted list prior to HTML render
|
||||
$toc_md = '<ul id="wiki-toc"></ul>'; // use the available jQuery plugin http://ndabas.github.io/toc/
|
||||
$s = preg_replace("/\[toc\]/", $toc_md, $s, -1);
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a select set of bbcode tags. Much of the code is copied from include/bbcode.php
|
||||
* @param string $s
|
||||
* @return string
|
||||
*/
|
||||
static public function bbcode($s) {
|
||||
|
||||
$s = str_replace(array('[baseurl]', '[sitename]'), array(z_root(), get_config('system', 'sitename')), $s);
|
||||
|
||||
$s = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_callback', $s);
|
||||
|
||||
$s = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_necallback', $s);
|
||||
|
||||
|
||||
$observer = \App::get_observer();
|
||||
if ($observer) {
|
||||
$s1 = '<span class="bb_observer" title="' . t('Different viewers will see this text differently') . '">';
|
||||
$s2 = '</span>';
|
||||
$obsBaseURL = $observer['xchan_connurl'];
|
||||
$obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL);
|
||||
$s = str_replace('[observer.baseurl]', $obsBaseURL, $s);
|
||||
$s = str_replace('[observer.url]', $observer['xchan_url'], $s);
|
||||
$s = str_replace('[observer.name]', $s1 . $observer['xchan_name'] . $s2, $s);
|
||||
$s = str_replace('[observer.address]', $s1 . $observer['xchan_addr'] . $s2, $s);
|
||||
$s = str_replace('[observer.webname]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $s);
|
||||
$s = str_replace('[observer.photo]', '', $s);
|
||||
}
|
||||
else {
|
||||
$s = str_replace('[observer.baseurl]', '', $s);
|
||||
$s = str_replace('[observer.url]', '', $s);
|
||||
$s = str_replace('[observer.name]', '', $s);
|
||||
$s = str_replace('[observer.address]', '', $s);
|
||||
$s = str_replace('[observer.webname]', '', $s);
|
||||
$s = str_replace('[observer.photo]', '', $s);
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
static public function get_file_ext($arr) {
|
||||
if($arr['mimeType'] == 'text/bbcode')
|
||||
return '.bb';
|
||||
else
|
||||
return '.md';
|
||||
}
|
||||
|
||||
// This function is derived from
|
||||
// http://stackoverflow.com/questions/32068537/generate-table-of-contents-from-markdown-in-php
|
||||
static public function toc($content) {
|
||||
// ensure using only "\n" as line-break
|
||||
$source = str_replace(["\r\n", "\r"], "\n", $content);
|
||||
|
||||
// look for markdown TOC items
|
||||
preg_match_all(
|
||||
'/^(?:=|-|#).*$/m',
|
||||
$source,
|
||||
$matches,
|
||||
PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE
|
||||
);
|
||||
|
||||
// preprocess: iterate matched lines to create an array of items
|
||||
// where each item is an array(level, text)
|
||||
$file_size = strlen($source);
|
||||
foreach ($matches[0] as $item) {
|
||||
$found_mark = substr($item[0], 0, 1);
|
||||
if ($found_mark == '#') {
|
||||
// text is the found item
|
||||
$item_text = $item[0];
|
||||
$item_level = strrpos($item_text, '#') + 1;
|
||||
$item_text = substr($item_text, $item_level);
|
||||
} else {
|
||||
// text is the previous line (empty if <hr>)
|
||||
$item_offset = $item[1];
|
||||
$prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2));
|
||||
$item_text =
|
||||
substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1);
|
||||
$item_text = trim($item_text);
|
||||
$item_level = $found_mark == '=' ? 1 : 2;
|
||||
}
|
||||
if (!trim($item_text) OR strpos($item_text, '|') !== FALSE) {
|
||||
// item is an horizontal separator or a table header, don't mind
|
||||
continue;
|
||||
}
|
||||
$raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)];
|
||||
}
|
||||
$o = '';
|
||||
foreach($raw_toc as $t) {
|
||||
$level = intval($t['level']);
|
||||
$text = $t['text'];
|
||||
switch ($level) {
|
||||
case 1:
|
||||
$li = '* ';
|
||||
break;
|
||||
case 2:
|
||||
$li = ' * ';
|
||||
break;
|
||||
case 3:
|
||||
$li = ' * ';
|
||||
break;
|
||||
case 4:
|
||||
$li = ' * ';
|
||||
break;
|
||||
default:
|
||||
$li = '* ';
|
||||
break;
|
||||
}
|
||||
$o .= $li . $text . "\n";
|
||||
}
|
||||
return $o;
|
||||
}
|
||||
|
||||
}
|
||||
204
Zotlabs/Lib/PConfig.php
Normal file
204
Zotlabs/Lib/PConfig.php
Normal file
@@ -0,0 +1,204 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
|
||||
class PConfig {
|
||||
|
||||
/**
|
||||
* @brief Loads all configuration values of a channel into a cached storage.
|
||||
*
|
||||
* All configuration values of the given channel are stored in global cache
|
||||
* which is available under the global variable App::$config[$uid].
|
||||
*
|
||||
* @param string $uid
|
||||
* The channel_id
|
||||
* @return void|false Nothing or false if $uid is false
|
||||
*/
|
||||
|
||||
static public function Load($uid) {
|
||||
if(is_null($uid) || $uid === false)
|
||||
return false;
|
||||
|
||||
if(! array_key_exists($uid, \App::$config))
|
||||
\App::$config[$uid] = array();
|
||||
|
||||
if(! is_array(\App::$config)) {
|
||||
btlogger('App::$config not an array: ' . $uid);
|
||||
}
|
||||
|
||||
if(! is_array(\App::$config[$uid])) {
|
||||
btlogger('App::$config[$uid] not an array: ' . $uid);
|
||||
}
|
||||
|
||||
$r = q("SELECT * FROM pconfig WHERE uid = %d",
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$k = $rr['k'];
|
||||
$c = $rr['cat'];
|
||||
if(! array_key_exists($c, \App::$config[$uid])) {
|
||||
\App::$config[$uid][$c] = array();
|
||||
\App::$config[$uid][$c]['config_loaded'] = true;
|
||||
}
|
||||
\App::$config[$uid][$c][$k] = $rr['v'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a particular channel's config variable given the category name
|
||||
* ($family) and a key.
|
||||
*
|
||||
* Get a particular channel's config value from the given category ($family)
|
||||
* and the $key from a cached storage in App::$config[$uid].
|
||||
*
|
||||
* Returns false if not set.
|
||||
*
|
||||
* @param string $uid
|
||||
* The channel_id
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
* @param string $key
|
||||
* The configuration key to query
|
||||
* @param boolean $instore (deprecated, without function)
|
||||
* @return mixed Stored value or false if it does not exist
|
||||
*/
|
||||
|
||||
static public function Get($uid,$family,$key,$default = false) {
|
||||
|
||||
if(is_null($uid) || $uid === false)
|
||||
return $default;
|
||||
|
||||
if(! array_key_exists($uid, \App::$config))
|
||||
self::Load($uid);
|
||||
|
||||
if((! array_key_exists($family, \App::$config[$uid])) || (! array_key_exists($key, \App::$config[$uid][$family])))
|
||||
return $default;
|
||||
|
||||
return ((! is_array(\App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$uid][$family][$key]))
|
||||
? unserialize(\App::$config[$uid][$family][$key])
|
||||
: \App::$config[$uid][$family][$key]
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a configuration value for a channel.
|
||||
*
|
||||
* Stores a config value ($value) in the category ($family) under the key ($key)
|
||||
* for the channel_id $uid.
|
||||
*
|
||||
* @param string $uid
|
||||
* The channel_id
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
* @param string $key
|
||||
* The configuration key to set
|
||||
* @param string $value
|
||||
* The value to store
|
||||
* @return mixed Stored $value or false
|
||||
*/
|
||||
|
||||
static public function Set($uid, $family, $key, $value) {
|
||||
|
||||
// this catches subtle errors where this function has been called
|
||||
// with local_channel() when not logged in (which returns false)
|
||||
// and throws an error in array_key_exists below.
|
||||
// we provide a function backtrace in the logs so that we can find
|
||||
// and fix the calling function.
|
||||
|
||||
if(is_null($uid) || $uid === false) {
|
||||
btlogger('UID is FALSE!', LOGGER_NORMAL, LOG_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// manage array value
|
||||
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
|
||||
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
|
||||
|
||||
if(get_pconfig($uid, $family, $key) === false) {
|
||||
if(! array_key_exists($uid, \App::$config))
|
||||
\App::$config[$uid] = array();
|
||||
if(! array_key_exists($family, \App::$config[$uid]))
|
||||
\App::$config[$uid][$family] = array();
|
||||
|
||||
$ret = q("INSERT INTO pconfig ( uid, cat, k, v ) VALUES ( %d, '%s', '%s', '%s' ) ",
|
||||
intval($uid),
|
||||
dbesc($family),
|
||||
dbesc($key),
|
||||
dbesc($dbvalue)
|
||||
);
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
$ret = q("UPDATE pconfig SET v = '%s' WHERE uid = %d and cat = '%s' AND k = '%s'",
|
||||
dbesc($dbvalue),
|
||||
intval($uid),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// keep a separate copy for all variables which were
|
||||
// set in the life of this page. We need this to
|
||||
// synchronise channel clones.
|
||||
|
||||
if(! array_key_exists('transient', \App::$config[$uid]))
|
||||
\App::$config[$uid]['transient'] = array();
|
||||
if(! array_key_exists($family, \App::$config[$uid]['transient']))
|
||||
\App::$config[$uid]['transient'][$family] = array();
|
||||
|
||||
\App::$config[$uid][$family][$key] = $value;
|
||||
\App::$config[$uid]['transient'][$family][$key] = $value;
|
||||
|
||||
if($ret)
|
||||
return $value;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deletes the given key from the channel's configuration.
|
||||
*
|
||||
* Removes the configured value from the stored cache in App::$config[$uid]
|
||||
* and removes it from the database.
|
||||
*
|
||||
* @param string $uid
|
||||
* The channel_id
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
* @param string $key
|
||||
* The configuration key to delete
|
||||
* @return mixed
|
||||
*/
|
||||
|
||||
static public function Delete($uid, $family, $key) {
|
||||
|
||||
if(is_null($uid) || $uid === false)
|
||||
return false;
|
||||
|
||||
$ret = false;
|
||||
|
||||
if(array_key_exists($uid,\App::$config)
|
||||
&& is_array(\App::$config['uid'])
|
||||
&& array_key_exists($family,\App::$config['uid'])
|
||||
&& array_key_exists($key, \App::$config[$uid][$family]))
|
||||
unset(\App::$config[$uid][$family][$key]);
|
||||
|
||||
$ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'",
|
||||
intval($uid),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
146
Zotlabs/Lib/Permcat.php
Normal file
146
Zotlabs/Lib/Permcat.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use \Zotlabs\Access as Zaccess;
|
||||
|
||||
class Permcat {
|
||||
|
||||
private $permcats = [];
|
||||
|
||||
public function __construct($channel_id) {
|
||||
|
||||
$perms = [];
|
||||
|
||||
// first check role perms for a perms_connect setting
|
||||
|
||||
$role = get_pconfig($channel_id,'system','permissions_role');
|
||||
if($role) {
|
||||
$x = Zaccess\PermissionRoles::role_perms($role);
|
||||
if($x['perms_connect']) {
|
||||
$perms = Zaccess\Permissions::FilledPerms($x['perms_connect']);
|
||||
}
|
||||
}
|
||||
|
||||
// if no role perms it may be a custom role, see if there any autoperms
|
||||
|
||||
if(! $perms) {
|
||||
$perms = Zaccess\Permissions::FilledAutoPerms($channel_id);
|
||||
}
|
||||
|
||||
// if no autoperms it may be a custom role with manual perms
|
||||
|
||||
if(! $perms) {
|
||||
$r = q("select channel_hash from channel where channel_id = %d",
|
||||
intval($channel_id)
|
||||
);
|
||||
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) {
|
||||
$perms[$xv['k']] = intval($xv['v']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nothing was found - create a filled permission array where all permissions are 0
|
||||
|
||||
if(! $perms) {
|
||||
$perms = Zaccess\Permissions::FilledPerms([]);
|
||||
}
|
||||
|
||||
$this->permcats[] = [
|
||||
'name' => 'default',
|
||||
'localname' => t('default','permcat'),
|
||||
'perms' => Zaccess\Permissions::Operms($perms),
|
||||
'system' => 1
|
||||
];
|
||||
|
||||
|
||||
$p = $this->load_permcats($channel_id);
|
||||
if($p) {
|
||||
for($x = 0; $x < count($p); $x++) {
|
||||
$this->permcats[] = [
|
||||
'name' => $p[$x][0],
|
||||
'localname' => $p[$x][1],
|
||||
'perms' => Zaccess\Permissions::Operms(Zaccess\Permissions::FilledPerms($p[$x][2])),
|
||||
'system' => intval($p[$x][3])
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function listing() {
|
||||
return $this->permcats;
|
||||
}
|
||||
|
||||
public function fetch($name) {
|
||||
if($name && $this->permcats) {
|
||||
foreach($this->permcats as $permcat) {
|
||||
if(strcasecmp($permcat['name'],$name) === 0) {
|
||||
return $permcat;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ['error' => true];
|
||||
}
|
||||
|
||||
public function load_permcats($uid) {
|
||||
|
||||
$permcats = [
|
||||
[ 'follower', t('follower','permcat'),
|
||||
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki',
|
||||
'post_like' ], 1
|
||||
],
|
||||
[ 'contributor', t('contributor','permcat'),
|
||||
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki',
|
||||
'post_wall','post_comments','write_wiki','post_like','tag_deliver','chat' ], 1
|
||||
],
|
||||
[ 'publisher', t('publisher','permcat'),
|
||||
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages',
|
||||
'write_storage','post_wall','write_pages','write_wiki','post_comments','post_like','tag_deliver',
|
||||
'chat', 'republish' ], 1
|
||||
]
|
||||
];
|
||||
|
||||
if($uid) {
|
||||
$x = q("select * from pconfig where uid = %d and cat = 'permcat'",
|
||||
intval($uid)
|
||||
);
|
||||
if($x) {
|
||||
foreach($x as $xv) {
|
||||
$value = ((preg_match('|^a:[0-9]+:{.*}$|s', $xv['v'])) ? unserialize($xv['v']) : $xv['v']);
|
||||
$permcats[] = [ $xv['k'], $xv['k'], $value, 0 ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call_hooks('permcats',$permcats);
|
||||
|
||||
return $permcats;
|
||||
|
||||
}
|
||||
|
||||
static public function find_permcat($arr,$name) {
|
||||
if((! $arr) || (! $name))
|
||||
return false;
|
||||
foreach($arr as $p)
|
||||
if($p['name'] == $name)
|
||||
return $p['value'];
|
||||
}
|
||||
|
||||
static public function update($channel_id, $name,$permarr) {
|
||||
PConfig::Set($channel_id,'permcat',$name,$permarr);
|
||||
}
|
||||
|
||||
static public function delete($channel_id,$name) {
|
||||
PConfig::Delete($channel_id,'permcat',$name);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
159
Zotlabs/Lib/PermissionDescription.php
Normal file
159
Zotlabs/Lib/PermissionDescription.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
require_once("include/permissions.php");
|
||||
require_once("include/language.php");
|
||||
require_once("include/text.php");
|
||||
|
||||
|
||||
/**
|
||||
* Encapsulates information the ACL dialog requires to describe
|
||||
* permission settings for an item with an empty ACL.
|
||||
* i.e the caption, icon, and tooltip for the no-ACL option in the ACL dialog.
|
||||
*/
|
||||
class PermissionDescription {
|
||||
|
||||
private $global_perm;
|
||||
private $channel_perm;
|
||||
private $fallback_description;
|
||||
|
||||
/**
|
||||
* Constructor is private.
|
||||
* Use static methods fromGlobalPermission(), fromStandalonePermission(),
|
||||
* or fromDescription() to create instances.
|
||||
*
|
||||
* @internal
|
||||
* @param int $global_perm
|
||||
* @param int $channel_perm
|
||||
* @param string $description (optional) default empty
|
||||
*/
|
||||
private function __construct($global_perm, $channel_perm, $description = '') {
|
||||
$this->global_perm = $global_perm;
|
||||
$this->channel_perm = $channel_perm;
|
||||
$this->fallback_description = ($description == '') ? t('Visible to your default audience') : $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the interpretation of an empty ACL can't be summarised with a global default permission
|
||||
* or a specific permission setting then use this method and describe what it means instead.
|
||||
* Remember to localize the description first.
|
||||
*
|
||||
* @param string $description - the localized caption for the no-ACL option in the ACL dialog.
|
||||
* @return a new instance of PermissionDescription
|
||||
*/
|
||||
public static function fromDescription($description) {
|
||||
return new PermissionDescription('', 0x80000, $description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method only if the interpretation of an empty ACL doesn't fall back to a global
|
||||
* default permission. You should pass one of the constants from boot.php - PERMS_PUBLIC,
|
||||
* PERMS_NETWORK etc.
|
||||
*
|
||||
* @param integer $perm - a single enumerated constant permission - PERMS_PUBLIC, PERMS_NETWORK etc.
|
||||
* @return a new instance of PermissionDescription
|
||||
*/
|
||||
public static function fromStandalonePermission($perm) {
|
||||
|
||||
$result = new PermissionDescription('', $perm);
|
||||
|
||||
$checkPerm = $result->get_permission_description();
|
||||
if($checkPerm == $result->fallback_description) {
|
||||
$result = null;
|
||||
logger('null PermissionDescription from unknown standalone permission: ' . $perm, LOGGER_DEBUG, LOG_ERR);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the preferred way to create a PermissionDescription, as it provides the most details.
|
||||
* Use this method if you know an empty ACL will result in one of the global default permissions
|
||||
* being used, such as channel_r_stream (for which you would pass 'view_stream').
|
||||
*
|
||||
* @param string $permname - a key for the global perms array from get_perms() in permissions.php,
|
||||
* e.g. 'view_stream', 'view_profile', etc.
|
||||
* @return a new instance of PermissionDescription
|
||||
*/
|
||||
public static function fromGlobalPermission($permname) {
|
||||
|
||||
$result = null;
|
||||
|
||||
$global_perms = \Zotlabs\Access\Permissions::Perms();
|
||||
|
||||
if(array_key_exists($permname, $global_perms)) {
|
||||
|
||||
$channelPerm = \Zotlabs\Access\PermissionLimits::Get(\App::$channel['channel_id'], $permname);
|
||||
|
||||
$result = new PermissionDescription('', $channelPerm);
|
||||
} else {
|
||||
// The acl dialog can handle null arguments, but it shouldn't happen
|
||||
logger('null PermissionDescription from unknown global permission: ' . $permname, LOGGER_DEBUG, LOG_ERR);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a localized description of the permission, or a generic message if the permission
|
||||
* is unknown.
|
||||
*
|
||||
* @return string description
|
||||
*/
|
||||
public function get_permission_description() {
|
||||
|
||||
switch($this->channel_perm) {
|
||||
case 0: return t('Only me');
|
||||
case PERMS_PUBLIC: return t('Public');
|
||||
case PERMS_NETWORK: return t('Anybody in the $Projectname network');
|
||||
case PERMS_SITE: return sprintf(t('Any account on %s'), \App::get_hostname());
|
||||
case PERMS_CONTACTS: return t('Any of my connections');
|
||||
case PERMS_SPECIFIC: return t('Only connections I specifically allow');
|
||||
case PERMS_AUTHED: return t('Anybody authenticated (could include visitors from other networks)');
|
||||
case PERMS_PENDING: return t('Any connections including those who haven\'t yet been approved');
|
||||
default: return $this->fallback_description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an icon css class name if an appropriate one is available, e.g. "fa-globe" for Public,
|
||||
* otherwise returns empty string.
|
||||
*
|
||||
* @return string icon css class name (often FontAwesome)
|
||||
*/
|
||||
public function get_permission_icon() {
|
||||
|
||||
switch($this->channel_perm) {
|
||||
case 0:/* only me */ return 'fa-eye-slash';
|
||||
case PERMS_PUBLIC: return 'fa-globe';
|
||||
case PERMS_NETWORK: return 'fa-share-alt-square'; // fa-share-alt-square is very similiar to the hubzilla logo, but we should create our own logo class to use
|
||||
case PERMS_SITE: return 'fa-sitemap';
|
||||
case PERMS_CONTACTS: return 'fa-group';
|
||||
case PERMS_SPECIFIC: return 'fa-list';
|
||||
case PERMS_AUTHED: return '';
|
||||
case PERMS_PENDING: return '';
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localized description of where the permission came from, if this is known.
|
||||
* If it's not know, or if the permission is standalone and didn't come from a default
|
||||
* permission setting, then empty string is returned.
|
||||
*
|
||||
* @return string description or empty string
|
||||
*/
|
||||
public function get_permission_origin_description() {
|
||||
|
||||
switch($this->global_perm) {
|
||||
case PERMS_R_STREAM: return t('This is your default setting for the audience of your normal stream, and posts.');
|
||||
case PERMS_R_PROFILE: return t('This is your default setting for who can view your default channel profile');
|
||||
case PERMS_R_ABOOK: return t('This is your default setting for who can view your connections');
|
||||
case PERMS_R_STORAGE: return t('This is your default setting for who can view your file storage and photos');
|
||||
case PERMS_R_PAGES: return t('This is your default setting for the audience of your webpages');
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
19
Zotlabs/Lib/ProtoDriver.php
Normal file
19
Zotlabs/Lib/ProtoDriver.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/*
|
||||
* Abstraction class for dealing with alternate networks (which of course do not exist, hence the abstraction)
|
||||
*/
|
||||
|
||||
|
||||
abstract class ProtoDriver {
|
||||
abstract protected function discover($channel,$location);
|
||||
abstract protected function deliver($item,$channel,$recipients);
|
||||
abstract protected function collect($channel,$connection);
|
||||
abstract protected function change_permissions($permissions,$channel,$recipient);
|
||||
abstract protected function acknowledge_permissions($permissions,$channel,$recipient);
|
||||
abstract protected function deliver_private($item,$channel,$recipients);
|
||||
abstract protected function collect_private($channel,$connection);
|
||||
|
||||
}
|
||||
127
Zotlabs/Lib/SuperCurl.php
Normal file
127
Zotlabs/Lib/SuperCurl.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/**
|
||||
* @brief wrapper for z_fetch_url() which can be instantiated with several built-in parameters and
|
||||
* these can be modified and re-used. Useful for CalDAV and other processes which need to authenticate
|
||||
* and set lots of CURL options (many of which stay the same from one call to the next).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
class SuperCurl {
|
||||
|
||||
|
||||
private $auth;
|
||||
private $url;
|
||||
|
||||
private $curlopt = array();
|
||||
|
||||
private $headers = null;
|
||||
public $filepos = 0;
|
||||
public $filehandle = 0;
|
||||
public $request_data = '';
|
||||
|
||||
private $request_method = 'GET';
|
||||
private $upload = false;
|
||||
private $cookies = false;
|
||||
|
||||
|
||||
private function set_data($s) {
|
||||
$this->request_data = $s;
|
||||
$this->filepos = 0;
|
||||
}
|
||||
|
||||
public function curl_read($ch,$fh,$size) {
|
||||
|
||||
if($this->filepos < 0) {
|
||||
unset($fh);
|
||||
return '';
|
||||
}
|
||||
|
||||
$s = substr($this->request_data,$this->filepos,$size);
|
||||
|
||||
if(strlen($s) < $size)
|
||||
$this->filepos = (-1);
|
||||
else
|
||||
$this->filepos = $this->filepos + $size;
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
public function __construct($opts = array()) {
|
||||
$this->set($opts);
|
||||
}
|
||||
|
||||
private function set($opts = array()) {
|
||||
if($opts) {
|
||||
foreach($opts as $k => $v) {
|
||||
switch($k) {
|
||||
case 'http_auth':
|
||||
$this->auth = $v;
|
||||
break;
|
||||
case 'magicauth':
|
||||
// currently experimental
|
||||
$this->magicauth = $v;
|
||||
\Zotlabs\Daemon\Master::Summon([ 'CurlAuth', $v ]);
|
||||
break;
|
||||
case 'custom':
|
||||
$this->request_method = $v;
|
||||
break;
|
||||
case 'url':
|
||||
$this->url = $v;
|
||||
break;
|
||||
case 'data':
|
||||
$this->set_data($v);
|
||||
if($v) {
|
||||
$this->upload = true;
|
||||
}
|
||||
else {
|
||||
$this->upload = false;
|
||||
}
|
||||
break;
|
||||
case 'headers':
|
||||
$this->headers = $v;
|
||||
break;
|
||||
default:
|
||||
$this->curlopts[$k] = $v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function exec() {
|
||||
$opts = $this->curlopts;
|
||||
$url = $this->url;
|
||||
if($this->auth)
|
||||
$opts['http_auth'] = $this->auth;
|
||||
if($this->magicauth) {
|
||||
$opts['cookiejar'] = 'store/[data]/cookie_' . $this->magicauth;
|
||||
$opts['cookiefile'] = 'store/[data]/cookie_' . $this->magicauth;
|
||||
$opts['cookie'] = 'PHPSESSID=' . trim(file_get_contents('store/[data]/cookien_' . $this->magicauth));
|
||||
$c = channelx_by_n($this->magicauth);
|
||||
if($c)
|
||||
$url = zid($this->url,channel_reddress($c));
|
||||
}
|
||||
if($this->custom)
|
||||
$opts['custom'] = $this->custom;
|
||||
if($this->headers)
|
||||
$opts['headers'] = $this->headers;
|
||||
if($this->upload) {
|
||||
$opts['upload'] = true;
|
||||
$opts['infile'] = $this->filehandle;
|
||||
$opts['infilesize'] = strlen($this->request_data);
|
||||
$opts['readfunc'] = [ $this, 'curl_read' ] ;
|
||||
}
|
||||
|
||||
$recurse = 0;
|
||||
return z_fetch_url($this->url,true,$recurse,(($opts) ? $opts : null));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
82
Zotlabs/Lib/System.php
Normal file
82
Zotlabs/Lib/System.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
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 public function get_site_name() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['sitename'])
|
||||
return \App::$config['system']['sitename'];
|
||||
return '';
|
||||
}
|
||||
|
||||
static public function get_project_version() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['hide_version'])
|
||||
return '';
|
||||
return self::get_std_version();
|
||||
}
|
||||
|
||||
static public function get_update_version() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['hide_version'])
|
||||
return '';
|
||||
return DB_UPDATE_VERSION;
|
||||
}
|
||||
|
||||
|
||||
static public function get_notify_icon() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['email_notify_icon_url'])
|
||||
return \App::$config['system']['email_notify_icon_url'];
|
||||
return z_root() . DEFAULT_NOTIFY_ICON;
|
||||
}
|
||||
|
||||
static public function get_site_icon() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['site_icon_url'])
|
||||
return \App::$config['system']['site_icon_url'];
|
||||
return z_root() . DEFAULT_PLATFORM_ICON ;
|
||||
}
|
||||
|
||||
|
||||
static public function get_project_link() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['project_link'])
|
||||
return \App::$config['system']['project_link'];
|
||||
return 'https://hubzilla.org';
|
||||
}
|
||||
|
||||
static public function get_project_srclink() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['project_srclink'])
|
||||
return \App::$config['system']['project_srclink'];
|
||||
return 'https://github.com/redmatrix/hubzilla';
|
||||
}
|
||||
|
||||
|
||||
|
||||
static public function get_server_role() {
|
||||
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['server_role'])
|
||||
return \App::$config['system']['server_role'];
|
||||
return 'standard';
|
||||
}
|
||||
|
||||
static public function get_std_version() {
|
||||
if(defined('STD_VERSION'))
|
||||
return STD_VERSION;
|
||||
return '0.0.0';
|
||||
}
|
||||
|
||||
static public function compatible_project($p) {
|
||||
|
||||
if(get_directory_realm() != DIRECTORY_REALM)
|
||||
return true;
|
||||
|
||||
foreach(['hubzilla','zap'] as $t) {
|
||||
if(stristr($p,$t))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
21
Zotlabs/Lib/Techlevels.php
Normal file
21
Zotlabs/Lib/Techlevels.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
|
||||
class Techlevels {
|
||||
|
||||
static public function levels() {
|
||||
$techlevels = [
|
||||
'0' => t('Beginner/Basic'),
|
||||
'1' => t('Novice - not skilled but willing to learn'),
|
||||
'2' => t('Intermediate - somewhat comfortable'),
|
||||
'3' => t('Advanced - very comfortable'),
|
||||
'4' => t('Expert - I can write computer code'),
|
||||
'5' => t('Wizard - I probably know more than you do')
|
||||
];
|
||||
return $techlevels;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
801
Zotlabs/Lib/ThreadItem.php
Normal file
801
Zotlabs/Lib/ThreadItem.php
Normal file
@@ -0,0 +1,801 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
require_once('include/text.php');
|
||||
|
||||
/**
|
||||
* A thread item
|
||||
*/
|
||||
|
||||
class ThreadItem {
|
||||
|
||||
public $data = array();
|
||||
private $template = 'conv_item.tpl';
|
||||
private $comment_box_template = 'comment_item.tpl';
|
||||
private $commentable = false;
|
||||
// list of supported reaction emojis - a site can over-ride this via config system.reactions
|
||||
private $reactions = ['1f60a','1f44f','1f37e','1f48b','1f61e','2665','1f606','1f62e','1f634','1f61c','1f607','1f608'];
|
||||
private $toplevel = false;
|
||||
private $children = array();
|
||||
private $parent = null;
|
||||
private $conversation = null;
|
||||
private $redirect_url = null;
|
||||
private $owner_url = '';
|
||||
private $owner_photo = '';
|
||||
private $owner_name = '';
|
||||
private $wall_to_wall = false;
|
||||
private $threaded = false;
|
||||
private $visiting = false;
|
||||
private $channel = null;
|
||||
private $display_mode = 'normal';
|
||||
|
||||
|
||||
public function __construct($data) {
|
||||
|
||||
$this->data = $data;
|
||||
$this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
|
||||
|
||||
// Prepare the children
|
||||
if(count($data['children'])) {
|
||||
foreach($data['children'] as $item) {
|
||||
|
||||
/*
|
||||
* Only add those that will be displayed
|
||||
*/
|
||||
|
||||
if((! visible_activity($item)) || array_key_exists('blocked',$item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$child = new ThreadItem($item);
|
||||
$this->add_child($child);
|
||||
}
|
||||
}
|
||||
|
||||
// allow a site to configure the order and content of the reaction emoji list
|
||||
if($this->toplevel) {
|
||||
$x = get_config('system','reactions');
|
||||
if($x && is_array($x) && count($x)) {
|
||||
$this->reactions = $x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data in a form usable by a conversation template
|
||||
*
|
||||
* Returns:
|
||||
* _ The data requested on success
|
||||
* _ false on failure
|
||||
*/
|
||||
|
||||
public function get_template_data($conv_responses, $thread_level=1) {
|
||||
|
||||
$result = array();
|
||||
|
||||
$item = $this->get_data();
|
||||
|
||||
$commentww = '';
|
||||
$sparkle = '';
|
||||
$buttons = '';
|
||||
$dropping = false;
|
||||
$star = false;
|
||||
$isstarred = "unstarred fa-star-o";
|
||||
$indent = '';
|
||||
$osparkle = '';
|
||||
$total_children = $this->count_descendants();
|
||||
$unseen_comments = (($item['real_uid']) ? 0 : $this->count_unseen_descendants());
|
||||
|
||||
$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'])
|
||||
|| strlen($item['deny_cid']) || strlen($item['deny_gid']))))
|
||||
? t('Private Message')
|
||||
: false);
|
||||
$shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false);
|
||||
|
||||
// allow an exemption for sharing stuff from your private feeds
|
||||
if($item['author']['xchan_network'] === 'rss')
|
||||
$shareable = true;
|
||||
|
||||
$mode = $conv->get_mode();
|
||||
|
||||
if(local_channel() && $observer['xchan_hash'] === $item['author_xchan'])
|
||||
$edpost = array(z_root()."/editpost/".$item['id'], t("Edit"));
|
||||
else
|
||||
$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())
|
||||
$dropping = true;
|
||||
|
||||
|
||||
if(array_key_exists('real_uid',$item)) {
|
||||
$edpost = false;
|
||||
$dropping = false;
|
||||
}
|
||||
|
||||
|
||||
if($dropping) {
|
||||
$drop = array(
|
||||
'dropping' => $dropping,
|
||||
'delete' => t('Delete'),
|
||||
);
|
||||
}
|
||||
// FIXME
|
||||
if($observer_is_pageowner) {
|
||||
$multidrop = array(
|
||||
'select' => t('Select'),
|
||||
);
|
||||
}
|
||||
|
||||
$filer = ((($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) ? t("Save to Folder") : false);
|
||||
|
||||
$profile_avatar = $item['author']['xchan_photo_m'];
|
||||
$profile_link = chanlink_hash($item['author_xchan']);
|
||||
$profile_name = $item['author']['xchan_name'];
|
||||
|
||||
$location = format_location($item);
|
||||
$isevent = false;
|
||||
$attend = null;
|
||||
$canvote = false;
|
||||
|
||||
// process action responses - e.g. like/dislike/attend/agree/whatever
|
||||
$response_verbs = array('like');
|
||||
if(feature_enabled($conv->get_profile_owner(),'dislike'))
|
||||
$response_verbs[] = 'dislike';
|
||||
if($item['obj_type'] === ACTIVITY_OBJ_EVENT) {
|
||||
$response_verbs[] = 'attendyes';
|
||||
$response_verbs[] = 'attendno';
|
||||
$response_verbs[] = 'attendmaybe';
|
||||
if($this->is_commentable()) {
|
||||
$isevent = true;
|
||||
$attend = array( t('I will attend'), t('I will not attend'), t('I might attend'));
|
||||
}
|
||||
}
|
||||
|
||||
$consensus = (intval($item['item_consensus']) ? true : false);
|
||||
if($consensus) {
|
||||
$response_verbs[] = 'agree';
|
||||
$response_verbs[] = 'disagree';
|
||||
$response_verbs[] = 'abstain';
|
||||
if($this->is_commentable()) {
|
||||
$conlabels = array( t('I agree'), t('I disagree'), t('I abstain'));
|
||||
$canvote = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(! feature_enabled($conv->get_profile_owner(),'dislike'))
|
||||
unset($conv_responses['dislike']);
|
||||
|
||||
$responses = get_responses($conv_responses,$response_verbs,$this,$item);
|
||||
|
||||
$my_responses = [];
|
||||
foreach($response_verbs as $v) {
|
||||
$my_responses[$v] = (($conv_responses[$v][$item['mid'] . '-m']) ? 1 : 0);
|
||||
}
|
||||
|
||||
$like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : '');
|
||||
$like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : '');
|
||||
if (count($like_list) > MAX_LIKERS) {
|
||||
$like_list_part = array_slice($like_list, 0, MAX_LIKERS);
|
||||
array_push($like_list_part, '<a href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
|
||||
} else {
|
||||
$like_list_part = '';
|
||||
}
|
||||
$like_button_label = tt('Like','Likes',$like_count,'noun');
|
||||
|
||||
if (feature_enabled($conv->get_profile_owner(),'dislike')) {
|
||||
$dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : '');
|
||||
$dislike_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : '');
|
||||
$dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun');
|
||||
if (count($dislike_list) > MAX_LIKERS) {
|
||||
$dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS);
|
||||
array_push($dislike_list_part, '<a href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
|
||||
} else {
|
||||
$dislike_list_part = '';
|
||||
}
|
||||
}
|
||||
|
||||
$showlike = ((x($conv_responses['like'],$item['mid'])) ? format_like($conv_responses['like'][$item['mid']],$conv_responses['like'][$item['mid'] . '-l'],'like',$item['mid']) : '');
|
||||
$showdislike = ((x($conv_responses['dislike'],$item['mid']) && feature_enabled($conv->get_profile_owner(),'dislike'))
|
||||
? format_like($conv_responses['dislike'][$item['mid']],$conv_responses['dislike'][$item['mid'] . '-l'],'dislike',$item['mid']) : '');
|
||||
|
||||
/*
|
||||
* We should avoid doing this all the time, but it depends on the conversation mode
|
||||
* And the conv mode may change when we change the conv, or it changes its mode
|
||||
* Maybe we should establish a way to be notified about conversation changes
|
||||
*/
|
||||
|
||||
$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))) {
|
||||
|
||||
// FIXME we don't need all this stuff, some can be done in the template
|
||||
|
||||
$star = array(
|
||||
'do' => t("Add Star"),
|
||||
'undo' => t("Remove Star"),
|
||||
'toggle' => t("Toggle Star Status"),
|
||||
'classdo' => (intval($item['item_starred']) ? "hidden" : ""),
|
||||
'classundo' => (intval($item['item_starred']) ? "" : "hidden"),
|
||||
'isstarred' => (intval($item['item_starred']) ? "starred fa-star" : "unstarred fa-star-o"),
|
||||
'starred' => t('starred'),
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
$indent = 'comment';
|
||||
}
|
||||
|
||||
|
||||
$verified = (intval($item['item_verified']) ? t('Message signature validated') : '');
|
||||
$forged = ((($item['sig']) && (! intval($item['item_verified']))) ? t('Message signature incorrect') : '');
|
||||
$unverified = '' ; // (($this->is_wall_to_wall() && (! intval($item['item_verified']))) ? t('Message cannot be verified') : '');
|
||||
|
||||
|
||||
|
||||
// FIXME - check this permission
|
||||
if($conv->get_profile_owner() == local_channel()) {
|
||||
$tagger = array(
|
||||
'tagit' => t("Add Tag"),
|
||||
'classtagger' => "",
|
||||
);
|
||||
}
|
||||
|
||||
$server_role = get_config('system','server_role');
|
||||
|
||||
$has_bookmarks = false;
|
||||
if(is_array($item['term'])) {
|
||||
foreach($item['term'] as $t) {
|
||||
if((get_account_techlevel() > 0) && ($t['ttype'] == TERM_BOOKMARK))
|
||||
$has_bookmarks = true;
|
||||
}
|
||||
}
|
||||
|
||||
$has_event = false;
|
||||
if(($item['obj_type'] === ACTIVITY_OBJ_EVENT) && $conv->get_profile_owner() == local_channel())
|
||||
$has_event = true;
|
||||
|
||||
if($this->is_commentable()) {
|
||||
$like = array( t("I like this \x28toggle\x29"), t("like"));
|
||||
$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
|
||||
}
|
||||
|
||||
if ($shareable)
|
||||
$share = array( t('Share This'), t('share'));
|
||||
|
||||
$dreport = '';
|
||||
|
||||
$keep_reports = intval(get_config('system','expire_delivery_reports'));
|
||||
if($keep_reports === 0)
|
||||
$keep_reports = 30;
|
||||
|
||||
if((! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0)
|
||||
$dreport = t('Delivery Report');
|
||||
|
||||
if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0)
|
||||
$indent .= ' shiny';
|
||||
|
||||
|
||||
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($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) : '');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$children = $this->get_children();
|
||||
|
||||
$has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false);
|
||||
|
||||
$tmp_item = array(
|
||||
'template' => $this->get_template(),
|
||||
'mode' => $mode,
|
||||
'type' => implode("",array_slice(explode("/",$item['verb']),-1)),
|
||||
'body' => $body['html'],
|
||||
'tags' => $body['tags'],
|
||||
'categories' => $body['categories'],
|
||||
'mentions' => $body['mentions'],
|
||||
'attachments' => $body['attachments'],
|
||||
'folders' => $body['folders'],
|
||||
'text' => strip_tags($body['html']),
|
||||
'id' => $this->get_id(),
|
||||
'mid' => $item['mid'],
|
||||
'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']),
|
||||
'llink' => $item['llink'],
|
||||
'viewthread' => $viewthread,
|
||||
'to' => t('to'),
|
||||
'via' => t('via'),
|
||||
'wall' => t('Wall-to-Wall'),
|
||||
'vwall' => t('via Wall-To-Wall:'),
|
||||
'profile_url' => $profile_link,
|
||||
'thread_action_menu' => thread_action_menu($item,$conv->get_mode()),
|
||||
'thread_author_menu' => thread_author_menu($item,$conv->get_mode()),
|
||||
'item_photo_menu' => item_photo_menu($item),
|
||||
'dreport' => $dreport,
|
||||
'name' => $profile_name,
|
||||
'thumb' => $profile_avatar,
|
||||
'osparkle' => $osparkle,
|
||||
'sparkle' => $sparkle,
|
||||
'title' => $item['title'],
|
||||
'title_tosource' => get_pconfig($conv->get_profile_owner(),'system','title_tosource'),
|
||||
'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'),
|
||||
'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'),
|
||||
'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,
|
||||
'verified' => $verified,
|
||||
'unverified' => $unverified,
|
||||
'forged' => $forged,
|
||||
'location' => $location,
|
||||
'attend_label' => t('Attend'),
|
||||
'attend_title' => t('Attendance Options'),
|
||||
'vote_label' => t('Vote'),
|
||||
'vote_title' => t('Voting Options'),
|
||||
'indent' => $indent,
|
||||
'owner_url' => $this->get_owner_url(),
|
||||
'owner_photo' => $this->get_owner_photo(),
|
||||
'owner_name' => $this->get_owner_name(),
|
||||
'photo' => $body['photo'],
|
||||
'event' => $body['event'],
|
||||
'has_tags' => $has_tags,
|
||||
'reactions' => $this->reactions,
|
||||
// Item toolbar buttons
|
||||
'emojis' => (($this->is_toplevel() && $this->is_commentable() && feature_enabled($conv->get_profile_owner(),'emojis')) ? '1' : ''),
|
||||
'like' => $like,
|
||||
'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
|
||||
'share' => $share,
|
||||
'rawmid' => $item['mid'],
|
||||
'plink' => get_plink($item),
|
||||
'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''),
|
||||
'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts')) ? $star : ''),
|
||||
'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''),
|
||||
'filer' => ((feature_enabled($conv->get_profile_owner(),'filing')) ? $filer : ''),
|
||||
'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 : ''),
|
||||
// end toolbar buttons
|
||||
|
||||
'unseen_comments' => $unseen_comments,
|
||||
'comment_count' => $total_children,
|
||||
'comment_count_txt' => $comment_count_txt,
|
||||
'list_unseen_txt' => $list_unseen_txt,
|
||||
'markseen' => t('Mark all seen'),
|
||||
'responses' => $responses,
|
||||
'my_responses' => $my_responses,
|
||||
'like_count' => $like_count,
|
||||
'like_list' => $like_list,
|
||||
'like_list_part' => $like_list_part,
|
||||
'like_button_label' => $like_button_label,
|
||||
'like_modal_title' => t('Likes','noun'),
|
||||
'dislike_modal_title' => t('Dislikes','noun'),
|
||||
'dislike_count' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_count : ''),
|
||||
'dislike_list' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list : ''),
|
||||
'dislike_list_part' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list_part : ''),
|
||||
'dislike_button_label' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_button_label : ''),
|
||||
'modal_dismiss' => t('Close'),
|
||||
'showlike' => $showlike,
|
||||
'showdislike' => $showdislike,
|
||||
'comment' => $this->get_comment_box($indent),
|
||||
'previewing' => ($conv->is_preview() ? ' preview ' : ''),
|
||||
'wait' => t('Please wait'),
|
||||
'submid' => str_replace(['+','='], ['',''], base64_encode(substr($item['mid'],0,32))),
|
||||
'thread_level' => $thread_level
|
||||
);
|
||||
|
||||
$arr = array('item' => $item, 'output' => $tmp_item);
|
||||
call_hooks('display_item', $arr);
|
||||
|
||||
$result = $arr['output'];
|
||||
|
||||
$result['children'] = array();
|
||||
$nb_children = count($children);
|
||||
|
||||
$visible_comments = get_config('system','expanded_comments');
|
||||
if($visible_comments === false)
|
||||
$visible_comments = 3;
|
||||
|
||||
// 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']))
|
||||
// $visible_comments = 99999;
|
||||
|
||||
if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
|
||||
foreach($children as $child) {
|
||||
$result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1);
|
||||
}
|
||||
// Collapse
|
||||
if(($nb_children > $visible_comments) || ($thread_level > 1)) {
|
||||
$result['children'][0]['comment_firstcollapsed'] = true;
|
||||
$result['children'][0]['num_comments'] = $comment_count_txt;
|
||||
$result['children'][0]['hide_text'] = sprintf( t('%s show all'), '<i class="fa fa-chevron-down"></i>');
|
||||
if($thread_level > 1) {
|
||||
$result['children'][$nb_children - 1]['comment_lastcollapsed'] = true;
|
||||
}
|
||||
else {
|
||||
$result['children'][$nb_children - ($visible_comments + 1)]['comment_lastcollapsed'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result['private'] = $item['item_private'];
|
||||
$result['toplevel'] = ($this->is_toplevel() ? 'toplevel_item' : '');
|
||||
|
||||
if($this->is_threaded()) {
|
||||
$result['flatten'] = false;
|
||||
$result['threaded'] = true;
|
||||
}
|
||||
else {
|
||||
$result['flatten'] = true;
|
||||
$result['threaded'] = false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return $this->get_data_value('id');
|
||||
}
|
||||
|
||||
public function get_display_mode() {
|
||||
return $this->display_mode;
|
||||
}
|
||||
|
||||
public function set_display_mode($mode) {
|
||||
$this->display_mode = $mode;
|
||||
}
|
||||
|
||||
public function is_threaded() {
|
||||
return $this->threaded;
|
||||
}
|
||||
|
||||
public function set_commentable($val) {
|
||||
$this->commentable = $val;
|
||||
foreach($this->get_children() as $child)
|
||||
$child->set_commentable($val);
|
||||
}
|
||||
|
||||
public function is_commentable() {
|
||||
return $this->commentable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child item
|
||||
*/
|
||||
public function add_child($item) {
|
||||
$item_id = $item->get_id();
|
||||
if(!$item_id) {
|
||||
logger('[ERROR] Item::add_child : Item has no ID!!', LOGGER_DEBUG);
|
||||
return false;
|
||||
}
|
||||
if($this->get_child($item->get_id())) {
|
||||
logger('[WARN] Item::add_child : Item already exists ('. $item->get_id() .').', LOGGER_DEBUG);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Only add what will be displayed
|
||||
*/
|
||||
|
||||
if(activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$item->set_parent($this);
|
||||
$this->children[] = $item;
|
||||
return end($this->children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a child by its ID
|
||||
*/
|
||||
public function get_child($id) {
|
||||
foreach($this->get_children() as $child) {
|
||||
if($child->get_id() == $id)
|
||||
return $child;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all our children
|
||||
*/
|
||||
public function get_children() {
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set our parent
|
||||
*/
|
||||
protected function set_parent($item) {
|
||||
$parent = $this->get_parent();
|
||||
if($parent) {
|
||||
$parent->remove_child($this);
|
||||
}
|
||||
$this->parent = $item;
|
||||
$this->set_conversation($item->get_conversation());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove our parent
|
||||
*/
|
||||
protected function remove_parent() {
|
||||
$this->parent = null;
|
||||
$this->conversation = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a child
|
||||
*/
|
||||
public function remove_child($item) {
|
||||
$id = $item->get_id();
|
||||
foreach($this->get_children() as $key => $child) {
|
||||
if($child->get_id() == $id) {
|
||||
$child->remove_parent();
|
||||
unset($this->children[$key]);
|
||||
// Reindex the array, in order to make sure there won't be any trouble on loops using count()
|
||||
$this->children = array_values($this->children);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
logger('[WARN] Item::remove_child : Item is not a child ('. $id .').', LOGGER_DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent item
|
||||
*/
|
||||
protected function get_parent() {
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* set conversation
|
||||
*/
|
||||
public function set_conversation($conv) {
|
||||
$previous_mode = ($this->conversation ? $this->conversation->get_mode() : '');
|
||||
|
||||
$this->conversation = $conv;
|
||||
|
||||
// Set it on our children too
|
||||
foreach($this->get_children() as $child)
|
||||
$child->set_conversation($conv);
|
||||
}
|
||||
|
||||
/**
|
||||
* get conversation
|
||||
*/
|
||||
public function get_conversation() {
|
||||
return $this->conversation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw data
|
||||
*
|
||||
* We shouldn't need this
|
||||
*/
|
||||
public function get_data() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a data value
|
||||
*
|
||||
* Returns:
|
||||
* _ value on success
|
||||
* _ false on failure
|
||||
*/
|
||||
public function get_data_value($name) {
|
||||
if(!isset($this->data[$name])) {
|
||||
// logger('[ERROR] Item::get_data_value : Item has no value name "'. $name .'".', LOGGER_DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->data[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template
|
||||
*/
|
||||
public function get_template() {
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
|
||||
public function set_template($t) {
|
||||
$this->template = $t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a toplevel post
|
||||
*/
|
||||
private function is_toplevel() {
|
||||
return $this->toplevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the total of our descendants
|
||||
*/
|
||||
private function count_descendants() {
|
||||
$children = $this->get_children();
|
||||
$total = count($children);
|
||||
if($total > 0) {
|
||||
foreach($children as $child) {
|
||||
$total += $child->count_descendants();
|
||||
}
|
||||
}
|
||||
return $total;
|
||||
}
|
||||
|
||||
private function count_unseen_descendants() {
|
||||
$children = $this->get_children();
|
||||
$total = count($children);
|
||||
if($total > 0) {
|
||||
$total = 0;
|
||||
foreach($children as $child) {
|
||||
if((! visible_activity($child->data)) || array_key_exists('author_blocked',$child->data)) {
|
||||
continue;
|
||||
}
|
||||
if(intval($child->data['item_unseen']))
|
||||
$total ++;
|
||||
}
|
||||
}
|
||||
return $total;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the template for the comment box
|
||||
*/
|
||||
private function get_comment_box_template() {
|
||||
return $this->comment_box_template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the comment box
|
||||
*
|
||||
* Returns:
|
||||
* _ The comment box string (empty if no comment box)
|
||||
* _ false on failure
|
||||
*/
|
||||
private function get_comment_box($indent) {
|
||||
|
||||
if(!$this->is_toplevel() && !get_config('system','thread_allow')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$comment_box = '';
|
||||
$conv = $this->get_conversation();
|
||||
|
||||
// logger('Commentable conv: ' . $conv->is_commentable());
|
||||
|
||||
if(! $this->is_commentable())
|
||||
return;
|
||||
|
||||
$template = get_markup_template($this->get_comment_box_template());
|
||||
|
||||
$observer = $conv->get_observer();
|
||||
|
||||
$qc = ((local_channel()) ? get_pconfig(local_channel(),'system','qcomment') : null);
|
||||
$qcomment = (($qc) ? explode("\n",$qc) : null);
|
||||
|
||||
$arr = array('comment_buttons' => '','id' => $this->get_id());
|
||||
call_hooks('comment_buttons',$arr);
|
||||
$comment_buttons = $arr['comment_buttons'];
|
||||
|
||||
|
||||
$comment_box = replace_macros($template,array(
|
||||
'$return_path' => '',
|
||||
'$threaded' => $this->is_threaded(),
|
||||
'$jsreload' => '', //(($conv->get_mode() === 'display') ? $_SESSION['return_url'] : ''),
|
||||
'$type' => (($conv->get_mode() === 'channel') ? 'wall-comment' : 'net-comment'),
|
||||
'$id' => $this->get_id(),
|
||||
'$parent' => $this->get_id(),
|
||||
'$qcomment' => $qcomment,
|
||||
'$comment_buttons' => $comment_buttons,
|
||||
'$profile_uid' => $conv->get_profile_owner(),
|
||||
'$mylink' => $observer['xchan_url'],
|
||||
'$mytitle' => t('This is you'),
|
||||
'$myphoto' => $observer['xchan_photo_s'],
|
||||
'$comment' => t('Comment'),
|
||||
'$submit' => t('Submit'),
|
||||
'$edbold' => t('Bold'),
|
||||
'$editalic' => t('Italic'),
|
||||
'$eduline' => t('Underline'),
|
||||
'$edquote' => t('Quote'),
|
||||
'$edcode' => t('Code'),
|
||||
'$edimg' => t('Image'),
|
||||
'$edurl' => t('Insert Link'),
|
||||
'$edvideo' => t('Video'),
|
||||
'$preview' => t('Preview'), // ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''),
|
||||
'$indent' => $indent,
|
||||
'$feature_encrypt' => ((feature_enabled($conv->get_profile_owner(),'content_encrypt')) ? true : false),
|
||||
'$encrypt' => t('Encrypt text'),
|
||||
'$cipher' => $conv->get_cipher(),
|
||||
'$sourceapp' => \App::$sourcename
|
||||
|
||||
));
|
||||
|
||||
return $comment_box;
|
||||
}
|
||||
|
||||
private function get_redirect_url() {
|
||||
return $this->redirect_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are a wall to wall item and set the relevant properties
|
||||
*/
|
||||
protected function check_wall_to_wall() {
|
||||
$conv = $this->get_conversation();
|
||||
$this->wall_to_wall = false;
|
||||
$this->owner_url = '';
|
||||
$this->owner_photo = '';
|
||||
$this->owner_name = '';
|
||||
|
||||
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'];
|
||||
$this->owner_name = $this->data['owner']['xchan_name'];
|
||||
$this->wall_to_wall = true;
|
||||
}
|
||||
}
|
||||
|
||||
private function is_wall_to_wall() {
|
||||
return $this->wall_to_wall;
|
||||
}
|
||||
|
||||
private function get_owner_url() {
|
||||
return $this->owner_url;
|
||||
}
|
||||
|
||||
private function get_owner_photo() {
|
||||
return $this->owner_photo;
|
||||
}
|
||||
|
||||
private function get_owner_name() {
|
||||
return $this->owner_name;
|
||||
}
|
||||
|
||||
private function is_visiting() {
|
||||
return $this->visiting;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
220
Zotlabs/Lib/ThreadStream.php
Normal file
220
Zotlabs/Lib/ThreadStream.php
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
require_once('boot.php');
|
||||
require_once('include/text.php');
|
||||
require_once('include/items.php');
|
||||
|
||||
/**
|
||||
* A list of threads
|
||||
*
|
||||
*/
|
||||
|
||||
class ThreadStream {
|
||||
|
||||
private $threads = array();
|
||||
private $mode = null;
|
||||
private $observer = null;
|
||||
private $writable = false;
|
||||
private $commentable = false;
|
||||
private $profile_owner = 0;
|
||||
private $preview = false;
|
||||
private $prepared_item = '';
|
||||
private $cipher = 'aes256';
|
||||
|
||||
// $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
|
||||
// a normal "post" item
|
||||
|
||||
public function __construct($mode, $preview, $prepared_item = '') {
|
||||
$this->set_mode($mode);
|
||||
$this->preview = $preview;
|
||||
$this->prepared_item = $prepared_item;
|
||||
$c = ((local_channel()) ? get_pconfig(local_channel(),'system','default_cipher') : '');
|
||||
if($c)
|
||||
$this->cipher = $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode we'll be displayed on
|
||||
*/
|
||||
private function set_mode($mode) {
|
||||
if($this->get_mode() == $mode)
|
||||
return;
|
||||
|
||||
$this->observer = \App::get_observer();
|
||||
$ob_hash = (($this->observer) ? $this->observer['xchan_hash'] : '');
|
||||
|
||||
switch($mode) {
|
||||
case 'network':
|
||||
$this->profile_owner = local_channel();
|
||||
$this->writable = true;
|
||||
break;
|
||||
case 'channel':
|
||||
$this->profile_owner = \App::$profile['profile_uid'];
|
||||
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
|
||||
break;
|
||||
case 'display':
|
||||
// in this mode we set profile_owner after initialisation (from conversation()) and then
|
||||
// pull some trickery which allows us to re-invoke this function afterward
|
||||
// it's an ugly hack so @FIXME
|
||||
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
|
||||
break;
|
||||
case 'page':
|
||||
$this->profile_owner = \App::$profile['uid'];
|
||||
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
|
||||
break;
|
||||
default:
|
||||
logger('[ERROR] Conversation::set_mode : Unhandled mode ('. $mode .').', LOGGER_DEBUG);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
$this->mode = $mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mode
|
||||
*/
|
||||
public function get_mode() {
|
||||
return $this->mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if page is writable
|
||||
*/
|
||||
public function is_writable() {
|
||||
return $this->writable;
|
||||
}
|
||||
|
||||
public function is_commentable() {
|
||||
return $this->commentable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if page is a preview
|
||||
*/
|
||||
public function is_preview() {
|
||||
return $this->preview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get profile owner
|
||||
*/
|
||||
public function get_profile_owner() {
|
||||
return $this->profile_owner;
|
||||
}
|
||||
|
||||
public function set_profile_owner($uid) {
|
||||
$this->profile_owner = $uid;
|
||||
$mode = $this->get_mode();
|
||||
$this->mode = null;
|
||||
$this->set_mode($mode);
|
||||
}
|
||||
|
||||
public function get_observer() {
|
||||
return $this->observer;
|
||||
}
|
||||
|
||||
public function get_cipher() {
|
||||
return $this->cipher;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a thread to the conversation
|
||||
*
|
||||
* Returns:
|
||||
* _ The inserted item on success
|
||||
* _ false on failure
|
||||
*/
|
||||
public function add_thread($item) {
|
||||
$item_id = $item->get_id();
|
||||
if(!$item_id) {
|
||||
logger('Item has no ID!!', LOGGER_DEBUG, LOG_ERR);
|
||||
return false;
|
||||
}
|
||||
if($this->get_thread($item->get_id())) {
|
||||
logger('Thread already exists ('. $item->get_id() .').', LOGGER_DEBUG, LOG_WARNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only add things that will be displayed
|
||||
*/
|
||||
|
||||
|
||||
if(($item->get_data_value('id') != $item->get_data_value('parent')) && (activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$item->set_commentable(false);
|
||||
$ob_hash = (($this->observer) ? $this->observer['xchan_hash'] : '');
|
||||
|
||||
if(! comments_are_now_closed($item->get_data())) {
|
||||
if(($item->get_data_value('author_xchan') === $ob_hash) || ($item->get_data_value('owner_xchan') === $ob_hash))
|
||||
$item->set_commentable(true);
|
||||
|
||||
if(intval($item->get_data_value('item_nocomment'))) {
|
||||
$item->set_commentable(false);
|
||||
}
|
||||
elseif(($this->observer) && (! $item->is_commentable())) {
|
||||
if((array_key_exists('owner',$item->data)) && intval($item->data['owner']['abook_self']))
|
||||
$item->set_commentable(perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'));
|
||||
else
|
||||
$item->set_commentable(can_comment_on_post($ob_hash,$item->data));
|
||||
}
|
||||
}
|
||||
require_once('include/channel.php');
|
||||
|
||||
$item->set_conversation($this);
|
||||
$this->threads[] = $item;
|
||||
return end($this->threads);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data in a form usable by a conversation template
|
||||
*
|
||||
* We should find a way to avoid using those arguments (at least most of them)
|
||||
*
|
||||
* Returns:
|
||||
* _ The data requested on success
|
||||
* _ false on failure
|
||||
*/
|
||||
public function get_template_data($conv_responses) {
|
||||
$result = array();
|
||||
|
||||
foreach($this->threads as $item) {
|
||||
|
||||
if(($item->get_data_value('id') == $item->get_data_value('parent')) && $this->prepared_item) {
|
||||
$item_data = $this->prepared_item;
|
||||
}
|
||||
else {
|
||||
$item_data = $item->get_template_data($conv_responses);
|
||||
}
|
||||
if(!$item_data) {
|
||||
logger('Failed to get item template data ('. $item->get_id() .').', LOGGER_DEBUG, LOG_ERR);
|
||||
return false;
|
||||
}
|
||||
$result[] = $item_data;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a thread based on its item id
|
||||
*
|
||||
* Returns:
|
||||
* _ The found item on success
|
||||
* _ false on failure
|
||||
*/
|
||||
private function get_thread($id) {
|
||||
foreach($this->threads as $item) {
|
||||
if($item->get_id() == $id)
|
||||
return $item;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
160
Zotlabs/Lib/XConfig.php
Normal file
160
Zotlabs/Lib/XConfig.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
|
||||
class XConfig {
|
||||
|
||||
/**
|
||||
* @brief Loads a full xchan's configuration into a cached storage.
|
||||
*
|
||||
* All configuration values of the given observer hash are stored in global
|
||||
* cache which is available under the global variable App::$config[$xchan].
|
||||
*
|
||||
* @param string $xchan
|
||||
* The observer's hash
|
||||
* @return void|false Returns false if xchan is not set
|
||||
*/
|
||||
|
||||
static public function Load($xchan) {
|
||||
|
||||
if(! $xchan)
|
||||
return false;
|
||||
|
||||
if(! array_key_exists($xchan, \App::$config))
|
||||
\App::$config[$xchan] = array();
|
||||
|
||||
$r = q("SELECT * FROM xconfig WHERE xchan = '%s'",
|
||||
dbesc($xchan)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
$k = $rr['k'];
|
||||
$c = $rr['cat'];
|
||||
if(! array_key_exists($c, \App::$config[$xchan])) {
|
||||
\App::$config[$xchan][$c] = array();
|
||||
\App::$config[$xchan][$c]['config_loaded'] = true;
|
||||
}
|
||||
\App::$config[$xchan][$c][$k] = $rr['v'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a particular observer's config variable given the category
|
||||
* name ($family) and a key.
|
||||
*
|
||||
* Get a particular observer's config value from the given category ($family)
|
||||
* and the $key from a cached storage in App::$config[$xchan].
|
||||
*
|
||||
* Returns false if not set.
|
||||
*
|
||||
* @param string $xchan
|
||||
* The observer's hash
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
* @param string $key
|
||||
* The configuration key to query
|
||||
* @return mixed Stored $value or false if it does not exist
|
||||
*/
|
||||
|
||||
static public function Get($xchan, $family, $key, $default = false) {
|
||||
|
||||
if(! $xchan)
|
||||
return $default;
|
||||
|
||||
if(! array_key_exists($xchan, \App::$config))
|
||||
load_xconfig($xchan);
|
||||
|
||||
if((! array_key_exists($family, \App::$config[$xchan])) || (! array_key_exists($key, \App::$config[$xchan][$family])))
|
||||
return $default;
|
||||
|
||||
return ((! is_array(\App::$config[$xchan][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$xchan][$family][$key]))
|
||||
? unserialize(\App::$config[$xchan][$family][$key])
|
||||
: \App::$config[$xchan][$family][$key]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a configuration value for an observer.
|
||||
*
|
||||
* Stores a config value ($value) in the category ($family) under the key ($key)
|
||||
* for the observer's $xchan hash.
|
||||
*
|
||||
*
|
||||
* @param string $xchan
|
||||
* The observer's hash
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
* @param string $key
|
||||
* The configuration key to set
|
||||
* @param string $value
|
||||
* The value to store
|
||||
* @return mixed Stored $value or false
|
||||
*/
|
||||
|
||||
static public function Set($xchan, $family, $key, $value) {
|
||||
|
||||
// manage array value
|
||||
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
|
||||
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
|
||||
|
||||
if(self::Get($xchan, $family, $key) === false) {
|
||||
if(! array_key_exists($xchan, \App::$config))
|
||||
\App::$config[$xchan] = array();
|
||||
if(! array_key_exists($family, \App::$config[$xchan]))
|
||||
\App::$config[$xchan][$family] = array();
|
||||
|
||||
$ret = q("INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' ) ",
|
||||
dbesc($xchan),
|
||||
dbesc($family),
|
||||
dbesc($key),
|
||||
dbesc($dbvalue)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$ret = q("UPDATE xconfig SET v = '%s' WHERE xchan = '%s' and cat = '%s' AND k = '%s'",
|
||||
dbesc($dbvalue),
|
||||
dbesc($xchan),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
}
|
||||
|
||||
\App::$config[$xchan][$family][$key] = $value;
|
||||
|
||||
if($ret)
|
||||
return $value;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deletes the given key from the observer's config.
|
||||
*
|
||||
* Removes the configured value from the stored cache in App::$config[$xchan]
|
||||
* and removes it from the database.
|
||||
*
|
||||
* @param string $xchan
|
||||
* The observer's hash
|
||||
* @param string $family
|
||||
* The category of the configuration value
|
||||
* @param string $key
|
||||
* The configuration key to delete
|
||||
* @return mixed
|
||||
*/
|
||||
|
||||
static public function Delete($xchan, $family, $key) {
|
||||
|
||||
if(x(\App::$config[$xchan][$family], $key))
|
||||
unset(\App::$config[$xchan][$family][$key]);
|
||||
$ret = q("DELETE FROM xconfig WHERE xchan = '%s' AND cat = '%s' AND k = '%s'",
|
||||
dbesc($xchan),
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
30
Zotlabs/Lib/ZotDriver.php
Normal file
30
Zotlabs/Lib/ZotDriver.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
|
||||
class ZotDriver extends ProtoDriver {
|
||||
|
||||
protected function discover($channel,$location) {
|
||||
|
||||
}
|
||||
protected function deliver($item,$channel,$recipients) {
|
||||
|
||||
}
|
||||
protected function collect($channel,$connection) {
|
||||
|
||||
}
|
||||
protected function change_permissions($permissions,$channel,$recipient) {
|
||||
|
||||
}
|
||||
protected function acknowledge_permissions($permissions,$channel,$recipient) {
|
||||
|
||||
}
|
||||
protected function deliver_private($item,$channel,$recipients) {
|
||||
|
||||
}
|
||||
protected function collect_private($channel,$connection) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
93
Zotlabs/Module/Achievements.php
Normal file
93
Zotlabs/Module/Achievements.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
|
||||
class Achievements extends \Zotlabs\Web\Controller {
|
||||
|
||||
function get() {
|
||||
// This doesn't work, so
|
||||
if (! is_developer())
|
||||
return;
|
||||
|
||||
if(argc() > 1)
|
||||
$which = argv(1);
|
||||
else {
|
||||
notice( t('Requested profile is not available.') . EOL );
|
||||
return;
|
||||
}
|
||||
|
||||
$profile = 0;
|
||||
$profile = argv(1);
|
||||
profile_load($which,$profile);
|
||||
|
||||
$r = q("select channel_id from channel where channel_address = '%s'",
|
||||
dbesc($which)
|
||||
);
|
||||
if($r) {
|
||||
$owner = intval($r[0]['channel_id']);
|
||||
}
|
||||
|
||||
$observer = \App::get_observer();
|
||||
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
$perms = get_all_perms($owner,$ob_hash);
|
||||
if(! $perms['view_profile']) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$newmembertext = t('Some blurb about what to do when you\'re new here');
|
||||
|
||||
|
||||
// By default, all badges are false
|
||||
$contactbadge = false;
|
||||
$profilebadge = false;
|
||||
$keywordsbadge = false;
|
||||
|
||||
// Check number of contacts. Award a badge if over 10
|
||||
// We'll figure these out on each page load instead of
|
||||
// writing them to the DB because that will mean one needs
|
||||
// to retain their achievements - eg, you can't add
|
||||
// a bunch of channels just to get your badge, and then
|
||||
// delete them all again. If these become popular or
|
||||
// used in profiles or something, we may need to reconsider
|
||||
// and add a table for this - because this won't scale.
|
||||
|
||||
$r = q("select * from abook where abook_channel = %d",
|
||||
intval($owner)
|
||||
);
|
||||
|
||||
if (count($r))
|
||||
$contacts = count($r);
|
||||
// We're checking for 11 to adjust for the abook record for self
|
||||
if ($contacts >= 11)
|
||||
$contactbadge = true;
|
||||
|
||||
// Check if an about field in the profile has been created.
|
||||
|
||||
$r = q("select * from profile where uid = %d and about <> ''",
|
||||
intval($owner)
|
||||
);
|
||||
|
||||
if ($r)
|
||||
$profilebadge = 1;
|
||||
|
||||
// Check if keywords have been set
|
||||
|
||||
$r = q("select * from profile where uid = %d and keywords <> ''",
|
||||
intval($owner)
|
||||
);
|
||||
|
||||
if($r)
|
||||
$keywordsbadge = 1;
|
||||
|
||||
return replace_macros(get_markup_template("achievements.tpl"), array(
|
||||
'$newmembertext' => $newmembertext,
|
||||
'$profilebadge' => $profilebadge,
|
||||
'$contactbadge' => $contactbadge,
|
||||
'$keywordsbadge' => $keywordsbadge,
|
||||
'$channelsbadge' => $channelsbadge
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
433
Zotlabs/Module/Acl.php
Normal file
433
Zotlabs/Module/Acl.php
Normal file
@@ -0,0 +1,433 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
/*
|
||||
* ACL selector json backend
|
||||
* This module provides JSON lists of connections and local/remote channels
|
||||
* (xchans) to populate various tools such as the ACL (AccessControlList) popup
|
||||
* and various auto-complete functions (such as email recipients, search, and
|
||||
* mention targets.
|
||||
* There are two primary output structural formats. One for the ACL widget and
|
||||
* the other for auto-completion.
|
||||
* Many of the behaviour variations are triggered on the use of single character keys
|
||||
* however this functionality has grown in an ad-hoc manner and has gotten quite messy over time.
|
||||
*/
|
||||
|
||||
require_once("include/acl_selectors.php");
|
||||
require_once("include/group.php");
|
||||
|
||||
|
||||
class Acl extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
// logger('mod_acl: ' . print_r($_REQUEST,true));
|
||||
|
||||
$start = (x($_REQUEST,'start') ? $_REQUEST['start'] : 0);
|
||||
$count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 500);
|
||||
$search = (x($_REQUEST,'search') ? $_REQUEST['search'] : '');
|
||||
$type = (x($_REQUEST,'type') ? $_REQUEST['type'] : '');
|
||||
$noforums = (x($_REQUEST,'n') ? $_REQUEST['n'] : false);
|
||||
|
||||
|
||||
// $type =
|
||||
// '' => standard ACL request
|
||||
// 'g' => Groups only ACL request
|
||||
// 'c' => Connections only ACL request or editor (textarea) mention request
|
||||
// $_REQUEST['search'] contains ACL search text.
|
||||
|
||||
|
||||
// $type =
|
||||
// 'm' => autocomplete private mail recipient (checks post_mail permission)
|
||||
// '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,
|
||||
// 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.
|
||||
|
||||
if(array_key_exists('query',$_REQUEST)) {
|
||||
$search = $_REQUEST['query'];
|
||||
}
|
||||
|
||||
if( (! local_channel()) && (! ($type == 'x' || $type == 'c')))
|
||||
killme();
|
||||
|
||||
$permitted = [];
|
||||
|
||||
if(in_array($type, [ 'm', 'a', 'c' ])) {
|
||||
|
||||
// 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.
|
||||
|
||||
$x = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = '%s' and v = '1'",
|
||||
intval(local_channel()),
|
||||
dbesc(($type === 'm') ? 'post_mail' : 'tag_deliver')
|
||||
);
|
||||
|
||||
$permitted = ids_to_array($x,'xchan');
|
||||
|
||||
}
|
||||
|
||||
|
||||
if($search) {
|
||||
$sql_extra = " AND groups.gname LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ";
|
||||
$sql_extra2 = "AND ( xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'%" . dbesc($search) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") ";
|
||||
|
||||
// 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
|
||||
// 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))
|
||||
. "' IN xchan_name) else position('" . protect_sprintf(dbesc($search)) . "' IN xchan_addr) end, ";
|
||||
|
||||
$col = ((strpos($search,'@') !== false) ? 'xchan_addr' : 'xchan_name' );
|
||||
$sql_extra3 = "AND $col 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(
|
||||
"type" => "g",
|
||||
"photo" => "images/twopeople.png",
|
||||
"name" => t('Profile','acl') . ' ' . $rv['profile_name'],
|
||||
"id" => 'vp' . $rv['id'],
|
||||
"xid" => 'vp.' . $rv['profile_guid'],
|
||||
"uids" => group_get_profile_members_xchan(local_channel(), $rv['id']),
|
||||
"link" => ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Normal privacy groups
|
||||
|
||||
$r = q("SELECT groups.id, groups.hash, groups.gname
|
||||
FROM groups, group_member
|
||||
WHERE groups.deleted = 0 AND groups.uid = %d
|
||||
AND group_member.gid = groups.id
|
||||
$sql_extra
|
||||
GROUP BY groups.id
|
||||
ORDER BY groups.gname
|
||||
LIMIT %d OFFSET %d",
|
||||
intval(local_channel()),
|
||||
intval($count),
|
||||
intval($start)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
foreach($r as $g){
|
||||
// logger('acl: group: ' . $g['gname'] . ' members: ' . group_get_members_xchan($g['id']));
|
||||
$groups[] = array(
|
||||
"type" => "g",
|
||||
"photo" => "images/twopeople.png",
|
||||
"name" => $g['gname'],
|
||||
"id" => $g['id'],
|
||||
"xid" => $g['hash'],
|
||||
"uids" => group_get_members_xchan($g['id']),
|
||||
"link" => ''
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($type == '' || $type == 'c') {
|
||||
|
||||
$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)
|
||||
$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 != '')
|
||||
$extra_channels_sql = " OR (abook_channel IN ($extra_channels_sql)) and abook_hidden = 0 ";
|
||||
|
||||
|
||||
// Add atokens belonging to the local channel @TODO restrict by search
|
||||
|
||||
$r2 = null;
|
||||
|
||||
$r1 = q("select * from atoken where atoken_uid = %d",
|
||||
intval(local_channel())
|
||||
);
|
||||
|
||||
if($r1) {
|
||||
require_once('include/security.php');
|
||||
$r2 = array();
|
||||
foreach($r1 as $rr) {
|
||||
$x = atoken_xchan($rr);
|
||||
$r2[] = [
|
||||
'id' => 'a' . $rr['atoken_id'] ,
|
||||
'hash' => $x['xchan_hash'],
|
||||
'name' => $x['xchan_name'],
|
||||
'micro' => $x['xchan_photo_m'],
|
||||
'url' => z_root(),
|
||||
'nick' => $x['xchan_addr'],
|
||||
'abook_their_perms' => 0,
|
||||
'abook_flags' => 0,
|
||||
'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
|
||||
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())
|
||||
);
|
||||
if($r2)
|
||||
$r = array_merge($r2,$r);
|
||||
|
||||
}
|
||||
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
|
||||
FROM xchan left join xlink on xlink_link = xchan_hash
|
||||
WHERE xlink_xchan = '%s' AND xchan_deleted = 0 $sql_extra2 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)
|
||||
$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
|
||||
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);
|
||||
$nickpos = strpos($x['nick'],$search);
|
||||
// Use a large position if not found
|
||||
return min($namepos === false ? 9999 : $namepos, $nickpos === false ? 9999 : $nickpos);
|
||||
};
|
||||
// This could be made simpler if PHP supported stable sorting
|
||||
usort($r,function($a,$b) use($matchpos) {
|
||||
$pos1 = $matchpos($a);
|
||||
$pos2 = $matchpos($b);
|
||||
if($pos1 == $pos2) { // Order alphabetically if match position is the same
|
||||
if($a['name'] == $b['name'])
|
||||
return 0;
|
||||
else
|
||||
return ($a['name'] < $b['name']) ? -1 : 1;
|
||||
}
|
||||
return ($pos1 < $pos2) ? -1 : 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
if(intval(get_config('system','taganyone')) || intval(get_pconfig(local_channel(),'system','taganyone'))) {
|
||||
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
|
||||
WHERE xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc"
|
||||
);
|
||||
if($r2)
|
||||
$r = array_merge($r,$r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
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())
|
||||
);
|
||||
if($z) {
|
||||
foreach($z as $zz) {
|
||||
if(in_array($zz['hash'],$permitted)) {
|
||||
$r[] = $zz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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
|
||||
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);
|
||||
$contacts = array();
|
||||
if($r) {
|
||||
foreach($r as $g) {
|
||||
$contacts[] = array(
|
||||
"photo" => $g['photo'],
|
||||
"name" => $g['name'],
|
||||
"nick" => $g['address'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$o = array(
|
||||
'start' => $start,
|
||||
'count' => $count,
|
||||
'items' => $contacts,
|
||||
);
|
||||
echo json_encode($o);
|
||||
killme();
|
||||
}
|
||||
else
|
||||
$r = array();
|
||||
|
||||
if($r) {
|
||||
foreach($r as $g){
|
||||
|
||||
// remove RSS feeds from ACLs - they are inaccessible
|
||||
if(strpos($g['hash'],'/') && $type != 'a')
|
||||
continue;
|
||||
|
||||
if(in_array($g['hash'],$permitted) && $type == 'c' && (! $noforums)) {
|
||||
$contacts[] = array(
|
||||
"type" => "c",
|
||||
"photo" => "images/twopeople.png",
|
||||
"name" => $g['name'] . '+',
|
||||
"id" => $g['id'] . '+',
|
||||
"xid" => $g['hash'],
|
||||
"link" => $g['nick'],
|
||||
"nick" => substr($g['nick'],0,strpos($g['nick'],'@')),
|
||||
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
|
||||
"taggable" => 'taggable',
|
||||
"label" => t('network')
|
||||
);
|
||||
}
|
||||
$contacts[] = array(
|
||||
"type" => "c",
|
||||
"photo" => $g['micro'],
|
||||
"name" => $g['name'],
|
||||
"id" => $g['id'],
|
||||
"xid" => $g['hash'],
|
||||
"link" => $g['nick'],
|
||||
"nick" => (($g['nick']) ? substr($g['nick'],0,strpos($g['nick'],'@')) : t('RSS')),
|
||||
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
|
||||
"taggable" => '',
|
||||
"label" => '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$items = array_merge($groups, $contacts);
|
||||
|
||||
$o = array(
|
||||
'start' => $start,
|
||||
'count' => $count,
|
||||
'items' => $items,
|
||||
);
|
||||
|
||||
|
||||
|
||||
echo json_encode($o);
|
||||
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
$url = $directory['url'] . '/dirsearch';
|
||||
}
|
||||
|
||||
$count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 100);
|
||||
if($url) {
|
||||
$query = $url . '?f=' ;
|
||||
$query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode($search) : '');
|
||||
|
||||
$x = z_fetch_url($query);
|
||||
if($x['success']) {
|
||||
$t = 0;
|
||||
$j = json_decode($x['body'],true);
|
||||
if($j && $j['results']) {
|
||||
return $j['results'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
||||
153
Zotlabs/Module/Admin.php
Normal file
153
Zotlabs/Module/Admin.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/**
|
||||
* @file Zotlabs/Module/Admin.php
|
||||
* @brief Hubzilla's admin controller.
|
||||
*
|
||||
* Controller for the /admin/ area.
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/queue_fn.php');
|
||||
require_once('include/account.php');
|
||||
|
||||
/**
|
||||
* @brief Admin area.
|
||||
*
|
||||
*/
|
||||
class Admin extends \Zotlabs\Web\Controller {
|
||||
|
||||
private $sm = null;
|
||||
|
||||
function __construct() {
|
||||
$this->sm = new \Zotlabs\Web\SubModule();
|
||||
}
|
||||
|
||||
function post(){
|
||||
logger('admin_post', LOGGER_DEBUG);
|
||||
|
||||
if(! is_site_admin()) {
|
||||
return;
|
||||
}
|
||||
if (argc() > 1) {
|
||||
$this->sm->call('post');
|
||||
}
|
||||
|
||||
goaway(z_root() . '/admin' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
||||
function get() {
|
||||
|
||||
logger('admin_content', LOGGER_DEBUG);
|
||||
|
||||
if(! is_site_admin()) {
|
||||
return login(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Page content
|
||||
*/
|
||||
|
||||
$o = '';
|
||||
|
||||
if(argc() > 1) {
|
||||
$o = $this->sm->call('get');
|
||||
if($o === false) {
|
||||
notice( t('Item not found.') );
|
||||
}
|
||||
}
|
||||
else {
|
||||
$o = $this->admin_page_summary();
|
||||
}
|
||||
|
||||
if(is_ajax()) {
|
||||
echo $o;
|
||||
killme();
|
||||
return '';
|
||||
}
|
||||
else {
|
||||
return $o;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns content for Admin Summary Page.
|
||||
*
|
||||
* @return string HTML from parsed admin_summary.tpl
|
||||
*/
|
||||
function admin_page_summary() {
|
||||
|
||||
// list total user accounts, expirations etc.
|
||||
$accounts = array();
|
||||
$r = q("SELECT COUNT(*) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account",
|
||||
db_utcnow(),
|
||||
db_utcnow(),
|
||||
dbesc(NULL_DATE),
|
||||
intval(ACCOUNT_BLOCKED)
|
||||
);
|
||||
if ($r) {
|
||||
$accounts['total'] = array('label' => t('# Accounts'), 'val' => $r[0]['total']);
|
||||
$accounts['blocked'] = array('label' => t('# blocked accounts'), 'val' => $r[0]['blocked']);
|
||||
$accounts['expired'] = array('label' => t('# expired accounts'), 'val' => $r[0]['expired']);
|
||||
$accounts['expiring'] = array('label' => t('# expiring accounts'), 'val' => $r[0]['expiring']);
|
||||
}
|
||||
|
||||
// pending registrations
|
||||
$r = q("SELECT COUNT(id) AS rtotal FROM register WHERE uid != '0'");
|
||||
$pending = $r[0]['rtotal'];
|
||||
|
||||
// available channels, primary and clones
|
||||
$channels = array();
|
||||
$r = q("SELECT COUNT(*) AS total, COUNT(CASE WHEN channel_primary = 1 THEN 1 ELSE NULL END) AS main, COUNT(CASE WHEN channel_primary = 0 THEN 1 ELSE NULL END) AS clones FROM channel WHERE channel_removed = 0");
|
||||
if ($r) {
|
||||
$channels['total'] = array('label' => t('# Channels'), 'val' => $r[0]['total']);
|
||||
$channels['main'] = array('label' => t('# primary'), 'val' => $r[0]['main']);
|
||||
$channels['clones'] = array('label' => t('# clones'), 'val' => $r[0]['clones']);
|
||||
}
|
||||
|
||||
// We can do better, but this is a quick queue status
|
||||
$r = q("SELECT COUNT(outq_delivered) AS total FROM outq WHERE outq_delivered = 0");
|
||||
$queue = (($r) ? $r[0]['total'] : 0);
|
||||
$queues = array( 'label' => t('Message queues'), 'queue' => $queue );
|
||||
|
||||
// If no plugins active return 0, otherwise list of plugin names
|
||||
$plugins = (count(\App::$plugins) == 0) ? count(\App::$plugins) : \App::$plugins;
|
||||
|
||||
// Could be extended to provide also other alerts to the admin
|
||||
$alertmsg = '';
|
||||
// annoy admin about upcoming unsupported PHP version
|
||||
if (version_compare(PHP_VERSION, '5.4', '<')) {
|
||||
$alertmsg = 'Your PHP version ' . PHP_VERSION . ' will not be supported with the next major release of $Projectname. You are strongly urged to upgrade to a current version.'
|
||||
. '<br>PHP 5.3 has reached its <a href="http://php.net/eol.php" class="alert-link">End of Life (EOL)</a> in August 2014.'
|
||||
. ' A list about current PHP versions can be found <a href="http://php.net/supported-versions.php" class="alert-link">here</a>.';
|
||||
}
|
||||
|
||||
$vmaster = get_repository_version('master');
|
||||
$vdev = get_repository_version('dev');
|
||||
|
||||
$upgrade = ((version_compare(STD_VERSION,$vmaster) < 0) ? t('Your software should be updated') : '');
|
||||
|
||||
$t = get_markup_template('admin_summary.tpl');
|
||||
return replace_macros($t, array(
|
||||
'$title' => t('Administration'),
|
||||
'$page' => t('Summary'),
|
||||
'$adminalertmsg' => $alertmsg,
|
||||
'$queues' => $queues,
|
||||
'$accounts' => array( t('Registered accounts'), $accounts),
|
||||
'$pending' => array( t('Pending registrations'), $pending),
|
||||
'$channels' => array( t('Registered channels'), $channels),
|
||||
'$plugins' => array( t('Active plugins'), $plugins ),
|
||||
'$version' => array( t('Version'), STD_VERSION),
|
||||
'$vmaster' => array( t('Repository version (master)'), $vmaster),
|
||||
'$vdev' => array( t('Repository version (dev)'), $vdev),
|
||||
'$upgrade' => $upgrade,
|
||||
'$build' => get_config('system', 'db_version')
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
84
Zotlabs/Module/Admin/Account_edit.php
Normal file
84
Zotlabs/Module/Admin/Account_edit.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
|
||||
class Account_edit {
|
||||
|
||||
function post() {
|
||||
|
||||
$account_id = $_REQUEST['aid'];
|
||||
|
||||
if(! $account_id)
|
||||
return;
|
||||
|
||||
$pass1 = trim($_REQUEST['pass1']);
|
||||
$pass2 = trim($_REQUEST['pass2']);
|
||||
if($pass1 && $pass2 && ($pass1 === $pass2)) {
|
||||
$salt = random_string(32);
|
||||
$password_encoded = hash('whirlpool', $salt . $pass1);
|
||||
$r = q("update account set account_salt = '%s', account_password = '%s',
|
||||
account_password_changed = '%s' where account_id = %d",
|
||||
dbesc($salt),
|
||||
dbesc($password_encoded),
|
||||
dbesc(datetime_convert()),
|
||||
intval($account_id)
|
||||
);
|
||||
if($r)
|
||||
info( sprintf( t('Password changed for account %d.'), $account_id). EOL);
|
||||
|
||||
}
|
||||
|
||||
$service_class = trim($_REQUEST['service_class']);
|
||||
$account_level = intval(trim($_REQUEST['account_level']));
|
||||
$account_language = trim($_REQUEST['account_language']);
|
||||
|
||||
$r = q("update account set account_service_class = '%s', account_level = %d, account_language = '%s'
|
||||
where account_id = %d",
|
||||
dbesc($service_class),
|
||||
intval($account_level),
|
||||
dbesc($account_language),
|
||||
intval($account_id)
|
||||
);
|
||||
|
||||
if($r)
|
||||
info( t('Account settings updated.') . EOL);
|
||||
|
||||
goaway(z_root() . '/admin/accounts');
|
||||
}
|
||||
|
||||
|
||||
function get() {
|
||||
if(argc() > 2)
|
||||
$account_id = argv(2);
|
||||
|
||||
$x = q("select * from account where account_id = %d limit 1",
|
||||
intval($account_id)
|
||||
);
|
||||
|
||||
if(! $x) {
|
||||
notice ( t('Account not found.') . EOL);
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
$a = replace_macros(get_markup_template('admin_account_edit.tpl'), [
|
||||
'$account' => $x[0],
|
||||
'$title' => t('Account Edit'),
|
||||
'$pass1' => [ 'pass1', t('New Password'), ' ','' ],
|
||||
'$pass2' => [ 'pass2', t('New Password again'), ' ','' ],
|
||||
'$account_level' => [ 'account_level', t('Technical skill level'), $x[0]['account_level'], '', \Zotlabs\Lib\Techlevels::levels() ],
|
||||
'$account_language' => [ 'account_language' , t('Account language (for emails)'), $x[0]['account_language'], '', language_list() ],
|
||||
'$service_class' => [ 'service_class', t('Service class'), $x[0]['account_service_class'], '' ],
|
||||
'$submit' => t('Submit'),
|
||||
]
|
||||
);
|
||||
|
||||
return $a;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
205
Zotlabs/Module/Admin/Accounts.php
Normal file
205
Zotlabs/Module/Admin/Accounts.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
|
||||
class Accounts {
|
||||
|
||||
/**
|
||||
* @brief Handle POST actions on accounts admin page.
|
||||
*
|
||||
* This function is called when on the admin user/account page the form was
|
||||
* submitted to handle multiple operations at once. If one of the icons next
|
||||
* to an entry are pressed the function admin_page_accounts() will handle this.
|
||||
*
|
||||
*/
|
||||
|
||||
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');
|
||||
|
||||
// change to switch structure?
|
||||
// account block/unblock button was submitted
|
||||
if (x($_POST, 'page_users_block')) {
|
||||
for ($i = 0; $i < count($users); $i++) {
|
||||
// if account is blocked remove blocked bit-flag, otherwise add blocked bit-flag
|
||||
$op = ($blocked[$i]) ? '& ~' : '| ';
|
||||
q("UPDATE account SET account_flags = (account_flags $op%d) WHERE account_id = %d",
|
||||
intval(ACCOUNT_BLOCKED),
|
||||
intval($users[$i])
|
||||
);
|
||||
}
|
||||
notice( sprintf( tt("%s account blocked/unblocked", "%s account blocked/unblocked", count($users)), count($users)) );
|
||||
}
|
||||
// account delete button was submitted
|
||||
if (x($_POST, 'page_accounts_delete')) {
|
||||
foreach ($users as $uid){
|
||||
account_remove($uid, true, false);
|
||||
}
|
||||
notice( sprintf( tt("%s account deleted", "%s accounts deleted", count($users)), count($users)) );
|
||||
}
|
||||
// registration approved button was submitted
|
||||
if (x($_POST, 'page_users_approve')) {
|
||||
foreach ($pending as $hash) {
|
||||
account_allow($hash);
|
||||
}
|
||||
}
|
||||
// registration deny button was submitted
|
||||
if (x($_POST, 'page_users_deny')) {
|
||||
foreach ($pending as $hash) {
|
||||
account_deny($hash);
|
||||
}
|
||||
}
|
||||
|
||||
goaway(z_root() . '/admin/accounts' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate accounts admin page and handle single item operations.
|
||||
*
|
||||
* This function generates the accounts/account admin page and handles the actions
|
||||
* if an icon next to an entry was clicked. If several items were selected and
|
||||
* the form was submitted it is handled by the function admin_page_accounts_post().
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
function get(){
|
||||
if (argc() > 2) {
|
||||
$uid = argv(3);
|
||||
$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');
|
||||
|
||||
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':
|
||||
q("UPDATE account SET account_flags = ( account_flags | %d ) WHERE account_id = %d",
|
||||
intval(ACCOUNT_BLOCKED),
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
notice( sprintf( t("Account '%s' blocked") , $account[0]['account_email']) . EOL);
|
||||
break;
|
||||
case 'unblock':
|
||||
q("UPDATE account SET account_flags = ( account_flags & ~%d ) WHERE account_id = %d",
|
||||
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)
|
||||
);
|
||||
|
||||
/* 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');
|
||||
$dir = 'asc';
|
||||
if(array_key_exists('dir',$_REQUEST))
|
||||
$dir = ((intval($_REQUEST['dir'])) ? 'asc' : 'desc');
|
||||
|
||||
$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
|
||||
where true $serviceclass order by $key $dir limit %d offset %d ",
|
||||
intval(ACCOUNT_BLOCKED),
|
||||
db_concat('ch.channel_address', ' '),
|
||||
intval(\App::$pager['itemspage']),
|
||||
intval(\App::$pager['start'])
|
||||
);
|
||||
|
||||
// function _setup_users($e){
|
||||
// $accounts = Array(
|
||||
// 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']);
|
||||
// $e['lastitem_date'] = relative_date($e['lastitem_date']);
|
||||
// return $e;
|
||||
// }
|
||||
// $users = array_map("_setup_users", $users);
|
||||
|
||||
$t = get_markup_template('admin_accounts.tpl');
|
||||
$o = replace_macros($t, array(
|
||||
// strings //
|
||||
'$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.'),
|
||||
'$approve' => t('Approve'),
|
||||
'$deny' => t('Deny'),
|
||||
'$delete' => t('Delete'),
|
||||
'$block' => t('Block'),
|
||||
'$unblock' => t('Unblock'),
|
||||
'$odir' => $odir,
|
||||
'$base' => $base,
|
||||
'$h_users' => t('Accounts'),
|
||||
'$th_users' => array(
|
||||
[ t('ID'), 'account_id' ],
|
||||
[ t('Email'), 'account_email' ],
|
||||
[ 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?'),
|
||||
|
||||
'$form_security_token' => get_form_security_token("admin_accounts"),
|
||||
|
||||
// values //
|
||||
'$baseurl' => z_root(),
|
||||
|
||||
'$pending' => $pending,
|
||||
'$users' => $users,
|
||||
));
|
||||
$o .= paginate($a);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
176
Zotlabs/Module/Admin/Channels.php
Normal file
176
Zotlabs/Module/Admin/Channels.php
Normal file
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
/**
|
||||
* @brief Admin Module for Channels.
|
||||
*
|
||||
*/
|
||||
class Channels {
|
||||
|
||||
/**
|
||||
* @brief Handle POST actions on channels admin page.
|
||||
*
|
||||
*/
|
||||
function post() {
|
||||
|
||||
$channels = ( x($_POST, 'channel') ? $_POST['channel'] : Array() );
|
||||
|
||||
check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels');
|
||||
|
||||
$xor = db_getfunc('^');
|
||||
|
||||
if(x($_POST, 'page_channels_block')) {
|
||||
foreach($channels as $uid) {
|
||||
q("UPDATE channel SET channel_pageflags = ( channel_pageflags $xor %d ) where channel_id = %d",
|
||||
intval(PAGE_CENSORED),
|
||||
intval( $uid )
|
||||
);
|
||||
\Zotlabs\Daemon\Master::Summon(array('Directory', $uid, 'nopush'));
|
||||
}
|
||||
notice( sprintf( tt("%s channel censored/uncensored", "%s channels censored/uncensored", count($channels)), count($channels)) );
|
||||
}
|
||||
if(x($_POST, 'page_channels_code')) {
|
||||
foreach($channels as $uid) {
|
||||
q("UPDATE channel SET channel_pageflags = ( channel_pageflags $xor %d ) where channel_id = %d",
|
||||
intval(PAGE_ALLOWCODE),
|
||||
intval( $uid )
|
||||
);
|
||||
}
|
||||
notice( sprintf( tt("%s channel code allowed/disallowed", "%s channels code allowed/disallowed", count($channels)), count($channels)) );
|
||||
}
|
||||
if(x($_POST, 'page_channels_delete')) {
|
||||
foreach($channels as $uid) {
|
||||
channel_remove($uid, true);
|
||||
}
|
||||
notice( sprintf( tt("%s channel deleted", "%s channels deleted", count($channels)), count($channels)) );
|
||||
}
|
||||
|
||||
goaway(z_root() . '/admin/channels' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate channels admin page and handle single item operations.
|
||||
*
|
||||
* @return string with parsed HTML
|
||||
*/
|
||||
function get() {
|
||||
if(argc() > 2) {
|
||||
$uid = argv(3);
|
||||
$channel = q("SELECT * FROM channel WHERE channel_id = %d",
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
if(! $channel) {
|
||||
notice( t('Channel not found') . EOL);
|
||||
goaway(z_root() . '/admin/channels' );
|
||||
}
|
||||
|
||||
switch(argv(2)) {
|
||||
case "delete":{
|
||||
check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't');
|
||||
// delete channel
|
||||
channel_remove($uid,true);
|
||||
|
||||
notice( sprintf(t("Channel '%s' deleted"), $channel[0]['channel_name']) . EOL);
|
||||
}; break;
|
||||
|
||||
case "block":{
|
||||
check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't');
|
||||
$pflags = $channel[0]['channel_pageflags'] ^ PAGE_CENSORED;
|
||||
q("UPDATE channel SET channel_pageflags = %d where channel_id = %d",
|
||||
intval($pflags),
|
||||
intval( $uid )
|
||||
);
|
||||
\Zotlabs\Daemon\Master::Summon(array('Directory',$uid,'nopush'));
|
||||
|
||||
notice( sprintf( (($pflags & PAGE_CENSORED) ? t("Channel '%s' censored"): t("Channel '%s' uncensored")) , $channel[0]['channel_name'] . ' (' . $channel[0]['channel_address'] . ')' ) . EOL);
|
||||
}; break;
|
||||
|
||||
case "code":{
|
||||
check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't');
|
||||
$pflags = $channel[0]['channel_pageflags'] ^ PAGE_ALLOWCODE;
|
||||
q("UPDATE channel SET channel_pageflags = %d where channel_id = %d",
|
||||
intval($pflags),
|
||||
intval( $uid )
|
||||
);
|
||||
|
||||
notice( sprintf( (($pflags & PAGE_ALLOWCODE) ? t("Channel '%s' code allowed"): t("Channel '%s' code disallowed")) , $channel[0]['channel_name'] . ' (' . $channel[0]['channel_address'] . ')' ) . EOL);
|
||||
}; break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
goaway(z_root() . '/admin/channels' );
|
||||
}
|
||||
|
||||
$key = (($_REQUEST['key']) ? dbesc($_REQUEST['key']) : 'channel_id');
|
||||
$dir = 'asc';
|
||||
if(array_key_exists('dir',$_REQUEST))
|
||||
$dir = ((intval($_REQUEST['dir'])) ? 'asc' : 'desc');
|
||||
|
||||
$base = z_root() . '/admin/channels?f=';
|
||||
$odir = (($dir === 'asc') ? '0' : '1');
|
||||
|
||||
/* get channels */
|
||||
|
||||
$total = q("SELECT count(*) as total FROM channel where channel_removed = 0 and channel_system = 0");
|
||||
if($total) {
|
||||
\App::set_pager_total($total[0]['total']);
|
||||
\App::set_pager_itemspage(100);
|
||||
}
|
||||
|
||||
$channels = q("SELECT * from channel where channel_removed = 0 and channel_system = 0 order by $key $dir limit %d offset %d ",
|
||||
intval(\App::$pager['itemspage']),
|
||||
intval(\App::$pager['start'])
|
||||
);
|
||||
|
||||
if($channels) {
|
||||
for($x = 0; $x < count($channels); $x ++) {
|
||||
if($channels[$x]['channel_pageflags'] & PAGE_CENSORED)
|
||||
$channels[$x]['blocked'] = true;
|
||||
else
|
||||
$channels[$x]['blocked'] = false;
|
||||
|
||||
if($channels[$x]['channel_pageflags'] & PAGE_ALLOWCODE)
|
||||
$channels[$x]['allowcode'] = true;
|
||||
else
|
||||
$channels[$x]['allowcode'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
$t = get_markup_template('admin_channels.tpl');
|
||||
$o = replace_macros($t, array(
|
||||
// strings //
|
||||
'$title' => t('Administration'),
|
||||
'$page' => t('Channels'),
|
||||
'$submit' => t('Submit'),
|
||||
'$select_all' => t('select all'),
|
||||
'$delete' => t('Delete'),
|
||||
'$block' => t('Censor'),
|
||||
'$unblock' => t('Uncensor'),
|
||||
'$code' => t('Allow Code'),
|
||||
'$uncode' => t('Disallow Code'),
|
||||
'$h_channels' => t('Channel'),
|
||||
'$base' => $base,
|
||||
'$odir' => $odir,
|
||||
'$th_channels' => array(
|
||||
[ t('UID'), 'channel_id' ],
|
||||
[ t('Name'), 'channel_name' ],
|
||||
[ t('Address'), 'channel_address' ]),
|
||||
|
||||
'$confirm_delete_multi' => t('Selected channels will be deleted!\n\nEverything that was posted in these channels on this site will be permanently deleted!\n\nAre you sure?'),
|
||||
'$confirm_delete' => t('The channel {0} will be deleted!\n\nEverything that was posted in this channel on this site will be permanently deleted!\n\nAre you sure?'),
|
||||
|
||||
'$form_security_token' => get_form_security_token('admin_channels'),
|
||||
|
||||
// values //
|
||||
'$baseurl' => z_root(),
|
||||
'$channels' => $channels,
|
||||
));
|
||||
$o .= paginate($a);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
}
|
||||
68
Zotlabs/Module/Admin/Dbsync.php
Normal file
68
Zotlabs/Module/Admin/Dbsync.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
|
||||
class Dbsync {
|
||||
|
||||
|
||||
|
||||
|
||||
function get() {
|
||||
$o = '';
|
||||
|
||||
if(argc() > 3 && intval(argv(3)) && argv(2) === 'mark') {
|
||||
set_config('database', 'update_r' . intval(argv(3)), 'success');
|
||||
if(intval(get_config('system','db_version')) <= intval(argv(3)))
|
||||
set_config('system','db_version',intval(argv(3)) + 1);
|
||||
info( t('Update has been marked successful') . EOL);
|
||||
goaway(z_root() . '/admin/dbsync');
|
||||
}
|
||||
|
||||
if(argc() > 2 && intval(argv(2))) {
|
||||
require_once('install/update.php');
|
||||
$func = 'update_r' . intval(argv(2));
|
||||
if(function_exists($func)) {
|
||||
$retval = $func();
|
||||
if($retval === UPDATE_FAILED) {
|
||||
$o .= sprintf( t('Executing %s failed. Check system logs.'), $func);
|
||||
}
|
||||
elseif($retval === UPDATE_SUCCESS) {
|
||||
$o .= sprintf( t('Update %s was successfully applied.'), $func);
|
||||
set_config('database',$func, 'success');
|
||||
}
|
||||
else
|
||||
$o .= sprintf( t('Update %s did not return a status. Unknown if it succeeded.'), $func);
|
||||
}
|
||||
else
|
||||
$o .= sprintf( t('Update function %s could not be found.'), $func);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
$failed = array();
|
||||
$r = q("select * from config where cat = 'database' ");
|
||||
if(count($r)) {
|
||||
foreach($r as $rr) {
|
||||
$upd = intval(substr($rr['k'],8));
|
||||
if($rr['v'] === 'success')
|
||||
continue;
|
||||
$failed[] = $upd;
|
||||
}
|
||||
}
|
||||
if(! count($failed))
|
||||
return '<div class="generic-content-wrapper-styled"><h3>' . t('No failed updates.') . '</h3></div>';
|
||||
|
||||
$o = replace_macros(get_markup_template('failed_updates.tpl'),array(
|
||||
'$base' => z_root(),
|
||||
'$banner' => t('Failed Updates'),
|
||||
'$desc' => '',
|
||||
'$mark' => t('Mark success (if update was manually applied)'),
|
||||
'$apply' => t('Attempt to execute this update step automatically'),
|
||||
'$failed' => $failed
|
||||
));
|
||||
|
||||
return $o;
|
||||
}
|
||||
}
|
||||
74
Zotlabs/Module/Admin/Features.php
Normal file
74
Zotlabs/Module/Admin/Features.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
|
||||
class Features {
|
||||
|
||||
|
||||
function post() {
|
||||
|
||||
check_form_security_token_redirectOnErr('/admin/features', 'admin_manage_features');
|
||||
|
||||
logger('postvars: ' . print_r($_POST,true));
|
||||
|
||||
$arr = array();
|
||||
$features = get_features(false);
|
||||
|
||||
foreach($features as $fname => $fdata) {
|
||||
foreach(array_slice($fdata,1) as $f) {
|
||||
$feature = $f[0];
|
||||
|
||||
if(array_key_exists('feature_' . $feature,$_POST))
|
||||
$val = intval($_POST['feature_' . $feature]);
|
||||
else
|
||||
$val = 0;
|
||||
set_config('feature',$feature,$val);
|
||||
|
||||
if(array_key_exists('featurelock_' . $feature,$_POST))
|
||||
set_config('feature_lock',$feature,$val);
|
||||
else
|
||||
del_config('feature_lock',$feature);
|
||||
}
|
||||
}
|
||||
|
||||
goaway(z_root() . '/admin/features' );
|
||||
|
||||
}
|
||||
|
||||
function get() {
|
||||
|
||||
if((argc() > 1) && (argv(1) === 'features')) {
|
||||
$arr = array();
|
||||
$features = get_features(false);
|
||||
|
||||
foreach($features as $fname => $fdata) {
|
||||
$arr[$fname] = array();
|
||||
$arr[$fname][0] = $fdata[0];
|
||||
foreach(array_slice($fdata,1) as $f) {
|
||||
|
||||
$set = get_config('feature',$f[0]);
|
||||
if($set === false)
|
||||
$set = $f[3];
|
||||
$arr[$fname][1][] = array(
|
||||
array('feature_' .$f[0],$f[1],$set,$f[2],array(t('Off'),t('On'))),
|
||||
array('featurelock_' .$f[0],sprintf( t('Lock feature %s'),$f[1]),(($f[4] !== false) ? 1 : 0),'',array(t('Off'),t('On')))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$tpl = get_markup_template("admin_settings_features.tpl");
|
||||
$o .= replace_macros($tpl, array(
|
||||
'$form_security_token' => get_form_security_token("admin_manage_features"),
|
||||
'$title' => t('Manage Additional Features'),
|
||||
'$features' => $arr,
|
||||
'$submit' => t('Submit'),
|
||||
));
|
||||
|
||||
return $o;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
101
Zotlabs/Module/Admin/Logs.php
Normal file
101
Zotlabs/Module/Admin/Logs.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
class Logs {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief POST handler for logs admin page.
|
||||
*
|
||||
*/
|
||||
|
||||
function post() {
|
||||
if (x($_POST, 'page_logs')) {
|
||||
check_form_security_token_redirectOnErr('/admin/logs', 'admin_logs');
|
||||
|
||||
$logfile = ((x($_POST,'logfile')) ? notags(trim($_POST['logfile'])) : '');
|
||||
$debugging = ((x($_POST,'debugging')) ? true : false);
|
||||
$loglevel = ((x($_POST,'loglevel')) ? intval(trim($_POST['loglevel'])) : 0);
|
||||
|
||||
set_config('system','logfile', $logfile);
|
||||
set_config('system','debugging', $debugging);
|
||||
set_config('system','loglevel', $loglevel);
|
||||
}
|
||||
|
||||
info( t('Log settings updated.') );
|
||||
goaway(z_root() . '/admin/logs' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Logs admin page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
function get() {
|
||||
|
||||
$log_choices = Array(
|
||||
LOGGER_NORMAL => 'Normal',
|
||||
LOGGER_TRACE => 'Trace',
|
||||
LOGGER_DEBUG => 'Debug',
|
||||
LOGGER_DATA => 'Data',
|
||||
LOGGER_ALL => 'All'
|
||||
);
|
||||
|
||||
$t = get_markup_template('admin_logs.tpl');
|
||||
|
||||
$f = get_config('system', 'logfile');
|
||||
|
||||
$data = '';
|
||||
|
||||
if(!file_exists($f)) {
|
||||
$data = t("Error trying to open <strong>$f</strong> log file.\r\n<br/>Check to see if file $f exist and is
|
||||
readable.");
|
||||
}
|
||||
else {
|
||||
$fp = fopen($f, 'r');
|
||||
if(!$fp) {
|
||||
$data = t("Couldn't open <strong>$f</strong> log file.\r\n<br/>Check to see if file $f is readable.");
|
||||
}
|
||||
else {
|
||||
$fstat = fstat($fp);
|
||||
$size = $fstat['size'];
|
||||
if($size != 0)
|
||||
{
|
||||
if($size > 5000000 || $size < 0)
|
||||
$size = 5000000;
|
||||
$seek = fseek($fp,0-$size,SEEK_END);
|
||||
if($seek === 0) {
|
||||
$data = escape_tags(fread($fp,$size));
|
||||
while(! feof($fp))
|
||||
$data .= escape_tags(fread($fp,4096));
|
||||
}
|
||||
}
|
||||
fclose($fp);
|
||||
}
|
||||
}
|
||||
|
||||
return replace_macros($t, array(
|
||||
'$title' => t('Administration'),
|
||||
'$page' => t('Logs'),
|
||||
'$submit' => t('Submit'),
|
||||
'$clear' => t('Clear'),
|
||||
'$data' => $data,
|
||||
'$baseurl' => z_root(),
|
||||
'$logname' => get_config('system','logfile'),
|
||||
|
||||
// name, label, value, help string, extra data...
|
||||
'$debugging' => array('debugging', t("Debugging"),get_config('system','debugging'), ""),
|
||||
'$logfile' => array('logfile', t("Log file"), get_config('system','logfile'), t("Must be writable by web server. Relative to your top-level webserver directory.")),
|
||||
'$loglevel' => array('loglevel', t("Log level"), get_config('system','loglevel'), "", $log_choices),
|
||||
|
||||
'$form_security_token' => get_form_security_token('admin_logs'),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
475
Zotlabs/Module/Admin/Plugins.php
Normal file
475
Zotlabs/Module/Admin/Plugins.php
Normal file
@@ -0,0 +1,475 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
use \Zotlabs\Storage\GitRepo as GitRepo;
|
||||
|
||||
class Plugins {
|
||||
|
||||
|
||||
function post() {
|
||||
|
||||
if(argc() > 2 && is_file("addon/" . argv(2) . "/" . argv(2) . ".php")) {
|
||||
@include_once("addon/" . argv(2) . "/" . argv(2) . ".php");
|
||||
if(function_exists(argv(2).'_plugin_admin_post')) {
|
||||
$func = argv(2) . '_plugin_admin_post';
|
||||
$func($a);
|
||||
}
|
||||
|
||||
goaway(z_root() . '/admin/plugins/' . argv(2) );
|
||||
|
||||
}
|
||||
elseif(argc() > 2) {
|
||||
switch(argv(2)) {
|
||||
case 'updaterepo':
|
||||
if (array_key_exists('repoName', $_REQUEST)) {
|
||||
$repoName = $_REQUEST['repoName'];
|
||||
}
|
||||
else {
|
||||
json_return_and_die(array('message' => 'No repo name provided.', 'success' => false));
|
||||
}
|
||||
$extendDir = 'store/[data]/git/sys/extend';
|
||||
$addonDir = $extendDir . '/addon';
|
||||
if (!file_exists($extendDir)) {
|
||||
if (!mkdir($extendDir, 0770, true)) {
|
||||
logger('Error creating extend folder: ' . $extendDir);
|
||||
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
|
||||
}
|
||||
else {
|
||||
if (!symlink(realpath('extend/addon'), $addonDir)) {
|
||||
logger('Error creating symlink to addon folder: ' . $addonDir);
|
||||
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
|
||||
}
|
||||
}
|
||||
}
|
||||
$repoDir = 'store/[data]/git/sys/extend/addon/' . $repoName;
|
||||
if (!is_dir($repoDir)) {
|
||||
logger('Repo directory does not exist: ' . $repoDir);
|
||||
json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false));
|
||||
}
|
||||
if (!is_writable($repoDir)) {
|
||||
logger('Repo directory not writable to web server: ' . $repoDir);
|
||||
json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false));
|
||||
}
|
||||
$git = new GitRepo('sys', null, false, $repoName, $repoDir);
|
||||
try {
|
||||
if ($git->pull()) {
|
||||
$files = array_diff(scandir($repoDir), array('.', '..'));
|
||||
foreach ($files as $file) {
|
||||
if (is_dir($repoDir . '/' . $file) && $file !== '.git') {
|
||||
$source = '../extend/addon/' . $repoName . '/' . $file;
|
||||
$target = realpath('addon/') . '/' . $file;
|
||||
unlink($target);
|
||||
if (!symlink($source, $target)) {
|
||||
logger('Error linking addons to /addon');
|
||||
json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false));
|
||||
}
|
||||
}
|
||||
}
|
||||
json_return_and_die(array('message' => 'Repo updated.', 'success' => true));
|
||||
} else {
|
||||
json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false));
|
||||
}
|
||||
} catch (\PHPGit\Exception\GitException $e) {
|
||||
json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false));
|
||||
}
|
||||
case 'removerepo':
|
||||
if (array_key_exists('repoName', $_REQUEST)) {
|
||||
$repoName = $_REQUEST['repoName'];
|
||||
} else {
|
||||
json_return_and_die(array('message' => 'No repo name provided.', 'success' => false));
|
||||
}
|
||||
$extendDir = 'store/[data]/git/sys/extend';
|
||||
$addonDir = $extendDir . '/addon';
|
||||
if (!file_exists($extendDir)) {
|
||||
if (!mkdir($extendDir, 0770, true)) {
|
||||
logger('Error creating extend folder: ' . $extendDir);
|
||||
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
|
||||
} else {
|
||||
if (!symlink(realpath('extend/addon'), $addonDir)) {
|
||||
logger('Error creating symlink to addon folder: ' . $addonDir);
|
||||
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
|
||||
}
|
||||
}
|
||||
}
|
||||
$repoDir = 'store/[data]/git/sys/extend/addon/' . $repoName;
|
||||
if (!is_dir($repoDir)) {
|
||||
logger('Repo directory does not exist: ' . $repoDir);
|
||||
json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false));
|
||||
}
|
||||
if (!is_writable($repoDir)) {
|
||||
logger('Repo directory not writable to web server: ' . $repoDir);
|
||||
json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false));
|
||||
}
|
||||
// TODO: remove directory and unlink /addon/files
|
||||
if (rrmdir($repoDir)) {
|
||||
json_return_and_die(array('message' => 'Repo deleted.', 'success' => true));
|
||||
} else {
|
||||
json_return_and_die(array('message' => 'Error deleting addon repo.', 'success' => false));
|
||||
}
|
||||
case 'installrepo':
|
||||
require_once('library/markdown.php');
|
||||
if (array_key_exists('repoURL', $_REQUEST)) {
|
||||
require_once('library/PHPGit.autoload.php'); // Load PHPGit dependencies
|
||||
$repoURL = $_REQUEST['repoURL'];
|
||||
$extendDir = 'store/[data]/git/sys/extend';
|
||||
$addonDir = $extendDir . '/addon';
|
||||
if (!file_exists($extendDir)) {
|
||||
if (!mkdir($extendDir, 0770, true)) {
|
||||
logger('Error creating extend folder: ' . $extendDir);
|
||||
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
|
||||
} else {
|
||||
if (!symlink(realpath('extend/addon'), $addonDir)) {
|
||||
logger('Error creating symlink to addon folder: ' . $addonDir);
|
||||
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_writable($extendDir)) {
|
||||
logger('Directory not writable to web server: ' . $extendDir);
|
||||
json_return_and_die(array('message' => 'Directory not writable to web server.', 'success' => false));
|
||||
}
|
||||
$repoName = null;
|
||||
if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') {
|
||||
$repoName = $_REQUEST['repoName'];
|
||||
} else {
|
||||
$repoName = GitRepo::getRepoNameFromURL($repoURL);
|
||||
}
|
||||
if (!$repoName) {
|
||||
logger('Invalid git repo');
|
||||
json_return_and_die(array('message' => 'Invalid git repo', 'success' => false));
|
||||
}
|
||||
$repoDir = $addonDir . '/' . $repoName;
|
||||
$tempRepoBaseDir = 'store/[data]/git/sys/temp/';
|
||||
$tempAddonDir = $tempRepoBaseDir . $repoName;
|
||||
|
||||
if (!is_writable($addonDir) || !is_writable($tempAddonDir)) {
|
||||
logger('Temp repo directory or /extend/addon not writable to web server: ' . $tempAddonDir);
|
||||
json_return_and_die(array('message' => 'Temp repo directory not writable to web server.', 'success' => false));
|
||||
}
|
||||
rename($tempAddonDir, $repoDir);
|
||||
|
||||
if (!is_writable(realpath('addon/'))) {
|
||||
logger('/addon directory not writable to web server: ' . $tempAddonDir);
|
||||
json_return_and_die(array('message' => '/addon directory not writable to web server.', 'success' => false));
|
||||
}
|
||||
$files = array_diff(scandir($repoDir), array('.', '..'));
|
||||
foreach ($files as $file) {
|
||||
if (is_dir($repoDir . '/' . $file) && $file !== '.git') {
|
||||
$source = '../extend/addon/' . $repoName . '/' . $file;
|
||||
$target = realpath('addon/') . '/' . $file;
|
||||
unlink($target);
|
||||
if (!symlink($source, $target)) {
|
||||
logger('Error linking addons to /addon');
|
||||
json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false));
|
||||
}
|
||||
}
|
||||
}
|
||||
$git = new GitRepo('sys', $repoURL, false, $repoName, $repoDir);
|
||||
$repo = $git->probeRepo();
|
||||
json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true));
|
||||
}
|
||||
case 'addrepo':
|
||||
require_once('library/markdown.php');
|
||||
if (array_key_exists('repoURL', $_REQUEST)) {
|
||||
require_once('library/PHPGit.autoload.php'); // Load PHPGit dependencies
|
||||
$repoURL = $_REQUEST['repoURL'];
|
||||
$extendDir = 'store/[data]/git/sys/extend';
|
||||
$addonDir = $extendDir . '/addon';
|
||||
$tempAddonDir = realpath('store/[data]') . '/git/sys/temp';
|
||||
if (!file_exists($extendDir)) {
|
||||
if (!mkdir($extendDir, 0770, true)) {
|
||||
logger('Error creating extend folder: ' . $extendDir);
|
||||
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
|
||||
} else {
|
||||
if (!symlink(realpath('extend/addon'), $addonDir)) {
|
||||
logger('Error creating symlink to addon folder: ' . $addonDir);
|
||||
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_dir($tempAddonDir)) {
|
||||
if (!mkdir($tempAddonDir, 0770, true)) {
|
||||
logger('Error creating temp plugin repo folder: ' . $tempAddonDir);
|
||||
json_return_and_die(array('message' => 'Error creating temp plugin repo folder: ' . $tempAddonDir, 'success' => false));
|
||||
}
|
||||
}
|
||||
$repoName = null;
|
||||
if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') {
|
||||
$repoName = $_REQUEST['repoName'];
|
||||
} else {
|
||||
$repoName = GitRepo::getRepoNameFromURL($repoURL);
|
||||
}
|
||||
if (!$repoName) {
|
||||
logger('Invalid git repo');
|
||||
json_return_and_die(array('message' => 'Invalid git repo: ' . $repoName, 'success' => false));
|
||||
}
|
||||
$repoDir = $tempAddonDir . '/' . $repoName;
|
||||
if (!is_writable($tempAddonDir)) {
|
||||
logger('Temporary directory for new addon repo is not writable to web server: ' . $tempAddonDir);
|
||||
json_return_and_die(array('message' => 'Temporary directory for new addon repo is not writable to web server.', 'success' => false));
|
||||
}
|
||||
// clone the repo if new automatically
|
||||
$git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir);
|
||||
|
||||
$remotes = $git->git->remote();
|
||||
$fetchURL = $remotes['origin']['fetch'];
|
||||
if ($fetchURL !== $git->url) {
|
||||
if (rrmdir($repoDir)) {
|
||||
$git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir);
|
||||
} else {
|
||||
json_return_and_die(array('message' => 'Error deleting existing addon repo.', 'success' => false));
|
||||
}
|
||||
}
|
||||
$repo = $git->probeRepo();
|
||||
$repo['readme'] = $repo['manifest'] = null;
|
||||
foreach ($git->git->tree('master') as $object) {
|
||||
if ($object['type'] == 'blob' && (strtolower($object['file']) === 'readme.md' || strtolower($object['file']) === 'readme')) {
|
||||
$repo['readme'] = Markdown($git->git->cat->blob($object['hash']));
|
||||
} else if ($object['type'] == 'blob' && strtolower($object['file']) === 'manifest.json') {
|
||||
$repo['manifest'] = $git->git->cat->blob($object['hash']);
|
||||
}
|
||||
}
|
||||
json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true));
|
||||
} else {
|
||||
json_return_and_die(array('message' => 'No repo URL provided', 'success' => false));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
/*
|
||||
* Single plugin
|
||||
*/
|
||||
|
||||
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);
|
||||
$info = get_plugin_info($plugin);
|
||||
$x = check_plugin_versions($info);
|
||||
|
||||
// disable plugins which are installed but incompatible versions
|
||||
|
||||
if($enabled && ! $x) {
|
||||
$enabled = false;
|
||||
$idz = array_search($plugin, \App::$plugins);
|
||||
if ($idz !== false) {
|
||||
unset(\App::$plugins[$idz]);
|
||||
uninstall_plugin($plugin);
|
||||
set_config("system","addon", implode(", ",\App::$plugins));
|
||||
}
|
||||
}
|
||||
$info['disabled'] = 1-intval($x);
|
||||
|
||||
if (x($_GET,"a") && $_GET['a']=="t"){
|
||||
check_form_security_token_redirectOnErr('/admin/plugins', 'admin_plugins', 't');
|
||||
$pinstalled = false;
|
||||
// Toggle plugin status
|
||||
$idx = array_search($plugin, \App::$plugins);
|
||||
if ($idx !== false){
|
||||
unset(\App::$plugins[$idx]);
|
||||
uninstall_plugin($plugin);
|
||||
$pinstalled = false;
|
||||
info( sprintf( t("Plugin %s disabled."), $plugin ) );
|
||||
} else {
|
||||
\App::$plugins[] = $plugin;
|
||||
install_plugin($plugin);
|
||||
$pinstalled = true;
|
||||
info( sprintf( t("Plugin %s enabled."), $plugin ) );
|
||||
}
|
||||
set_config("system","addon", implode(", ",\App::$plugins));
|
||||
|
||||
if($pinstalled) {
|
||||
@require_once("addon/$plugin/$plugin.php");
|
||||
if(function_exists($plugin.'_plugin_admin'))
|
||||
goaway(z_root() . '/admin/plugins/' . $plugin);
|
||||
}
|
||||
goaway(z_root() . '/admin/plugins' );
|
||||
}
|
||||
// display plugin details
|
||||
require_once('library/markdown.php');
|
||||
|
||||
if (in_array($plugin, \App::$plugins)){
|
||||
$status = 'on';
|
||||
$action = t('Disable');
|
||||
} else {
|
||||
$status = 'off';
|
||||
$action = t('Enable');
|
||||
}
|
||||
|
||||
$readme = null;
|
||||
if (is_file("addon/$plugin/README.md")){
|
||||
$readme = file_get_contents("addon/$plugin/README.md");
|
||||
$readme = Markdown($readme);
|
||||
} else if (is_file("addon/$plugin/README")){
|
||||
$readme = "<pre>". file_get_contents("addon/$plugin/README") ."</pre>";
|
||||
}
|
||||
|
||||
$admin_form = '';
|
||||
|
||||
$r = q("select * from addon where plugin_admin = 1 and aname = '%s' limit 1",
|
||||
dbesc($plugin)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
@require_once("addon/$plugin/$plugin.php");
|
||||
if(function_exists($plugin.'_plugin_admin')) {
|
||||
$func = $plugin.'_plugin_admin';
|
||||
$func($a, $admin_form);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$t = get_markup_template('admin_plugins_details.tpl');
|
||||
return replace_macros($t, array(
|
||||
'$title' => t('Administration'),
|
||||
'$page' => t('Plugins'),
|
||||
'$toggle' => t('Toggle'),
|
||||
'$settings' => t('Settings'),
|
||||
'$baseurl' => z_root(),
|
||||
|
||||
'$plugin' => $plugin,
|
||||
'$status' => $status,
|
||||
'$action' => $action,
|
||||
'$info' => $info,
|
||||
'$str_author' => t('Author: '),
|
||||
'$str_maintainer' => t('Maintainer: '),
|
||||
'$str_minversion' => t('Minimum project version: '),
|
||||
'$str_maxversion' => t('Maximum project version: '),
|
||||
'$str_minphpversion' => t('Minimum PHP version: '),
|
||||
'$str_serverroles' => t('Compatible Server Roles: '),
|
||||
'$str_requires' => t('Requires: '),
|
||||
'$disabled' => t('Disabled - version incompatibility'),
|
||||
|
||||
'$admin_form' => $admin_form,
|
||||
'$function' => 'plugins',
|
||||
'$screenshot' => '',
|
||||
'$readme' => $readme,
|
||||
|
||||
'$form_security_token' => get_form_security_token('admin_plugins'),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* List plugins
|
||||
*/
|
||||
$plugins = array();
|
||||
$files = glob('addon/*/');
|
||||
if($files) {
|
||||
foreach($files as $file) {
|
||||
if (is_dir($file)){
|
||||
list($tmp, $id) = array_map('trim', explode('/', $file));
|
||||
$info = get_plugin_info($id);
|
||||
$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);
|
||||
if ($idz !== false) {
|
||||
unset(\App::$plugins[$idz]);
|
||||
uninstall_plugin($id);
|
||||
set_config("system","addon", implode(", ",\App::$plugins));
|
||||
}
|
||||
}
|
||||
$info['disabled'] = 1-intval($x);
|
||||
|
||||
$plugins[] = array( $id, (($enabled)?"on":"off") , $info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usort($plugins,'self::plugin_sort');
|
||||
|
||||
$allowManageRepos = false;
|
||||
if(is_writable('extend/addon') && is_writable('store/[data]')) {
|
||||
$allowManageRepos = true;
|
||||
}
|
||||
|
||||
$admin_plugins_add_repo_form= replace_macros(
|
||||
get_markup_template('admin_plugins_addrepo.tpl'), array(
|
||||
'$post' => 'admin/plugins/addrepo',
|
||||
'$desc' => t('Enter the public git repository URL of the plugin repo.'),
|
||||
'$repoURL' => array('repoURL', t('Plugin repo git URL'), '', ''),
|
||||
'$repoName' => array('repoName', t('Custom repo name'), '', '', t('(optional)')),
|
||||
'$submit' => t('Download Plugin Repo')
|
||||
)
|
||||
);
|
||||
$newRepoModalID = random_string(3);
|
||||
$newRepoModal = replace_macros(
|
||||
get_markup_template('generic_modal.tpl'), array(
|
||||
'$id' => $newRepoModalID,
|
||||
'$title' => t('Install new repo'),
|
||||
'$ok' => t('Install'),
|
||||
'$cancel' => t('Cancel')
|
||||
)
|
||||
);
|
||||
|
||||
$reponames = $this->listAddonRepos();
|
||||
$addonrepos = [];
|
||||
foreach($reponames as $repo) {
|
||||
$addonrepos[] = array('name' => $repo, 'description' => '');
|
||||
// TODO: Parse repo info to provide more information about repos
|
||||
}
|
||||
|
||||
$t = get_markup_template('admin_plugins.tpl');
|
||||
return replace_macros($t, array(
|
||||
'$title' => t('Administration'),
|
||||
'$page' => t('Plugins'),
|
||||
'$submit' => t('Submit'),
|
||||
'$baseurl' => z_root(),
|
||||
'$function' => 'plugins',
|
||||
'$plugins' => $plugins,
|
||||
'$disabled' => t('Disabled - version incompatibility'),
|
||||
'$form_security_token' => get_form_security_token('admin_plugins'),
|
||||
'$allowManageRepos' => $allowManageRepos,
|
||||
'$managerepos' => t('Manage Repos'),
|
||||
'$installedtitle' => t('Installed Plugin Repositories'),
|
||||
'$addnewrepotitle' => t('Install a New Plugin Repository'),
|
||||
'$expandform' => false,
|
||||
'$form' => $admin_plugins_add_repo_form,
|
||||
'$newRepoModal' => $newRepoModal,
|
||||
'$newRepoModalID' => $newRepoModalID,
|
||||
'$addonrepos' => $addonrepos,
|
||||
'$repoUpdateButton' => t('Update'),
|
||||
'$repoBranchButton' => t('Switch branch'),
|
||||
'$repoRemoveButton' => t('Remove')
|
||||
));
|
||||
}
|
||||
|
||||
function listAddonRepos() {
|
||||
$addonrepos = [];
|
||||
$addonDir = 'extend/addon/';
|
||||
if(is_dir($addonDir)) {
|
||||
if ($handle = opendir($addonDir)) {
|
||||
while (false !== ($entry = readdir($handle))) {
|
||||
if ($entry != "." && $entry != "..") {
|
||||
$addonrepos[] = $entry;
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
}
|
||||
}
|
||||
return $addonrepos;
|
||||
}
|
||||
|
||||
static public function plugin_sort($a,$b) {
|
||||
return(strcmp(strtolower($a[2]['name']),strtolower($b[2]['name'])));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
169
Zotlabs/Module/Admin/Profs.php
Normal file
169
Zotlabs/Module/Admin/Profs.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
class Profs {
|
||||
|
||||
function post() {
|
||||
|
||||
if(array_key_exists('basic',$_REQUEST)) {
|
||||
$arr = explode(',',$_REQUEST['basic']);
|
||||
for($x = 0; $x < count($arr); $x ++)
|
||||
if(trim($arr[$x]))
|
||||
$arr[$x] = trim($arr[$x]);
|
||||
set_config('system','profile_fields_basic',$arr);
|
||||
|
||||
if(array_key_exists('advanced',$_REQUEST)) {
|
||||
$arr = explode(',',$_REQUEST['advanced']);
|
||||
for($x = 0; $x < count($arr); $x ++)
|
||||
if(trim($arr[$x]))
|
||||
$arr[$x] = trim($arr[$x]);
|
||||
set_config('system','profile_fields_advanced',$arr);
|
||||
}
|
||||
goaway(z_root() . '/admin/profs');
|
||||
}
|
||||
|
||||
|
||||
if(array_key_exists('field_name',$_REQUEST)) {
|
||||
if($_REQUEST['id']) {
|
||||
$r = q("update profdef set field_name = '%s', field_type = '%s', field_desc = '%s' field_help = '%s', field_inputs = '%s' where id = %d",
|
||||
dbesc($_REQUEST['field_name']),
|
||||
dbesc($_REQUEST['field_type']),
|
||||
dbesc($_REQUEST['field_desc']),
|
||||
dbesc($_REQUEST['field_help']),
|
||||
dbesc($_REQUEST['field_inputs']),
|
||||
intval($_REQUEST['id'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
$r = q("insert into profdef ( field_name, field_type, field_desc, field_help, field_inputs ) values ( '%s' , '%s', '%s', '%s', '%s' )",
|
||||
dbesc($_REQUEST['field_name']),
|
||||
dbesc($_REQUEST['field_type']),
|
||||
dbesc($_REQUEST['field_desc']),
|
||||
dbesc($_REQUEST['field_help']),
|
||||
dbesc($_REQUEST['field_inputs'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// add to chosen array basic or advanced
|
||||
|
||||
goaway(z_root() . '/admin/profs');
|
||||
}
|
||||
|
||||
function get() {
|
||||
|
||||
if((argc() > 3) && argv(2) == 'drop' && intval(argv(3))) {
|
||||
$r = q("delete from profdef where id = %d",
|
||||
intval(argv(3))
|
||||
);
|
||||
// remove from allowed fields
|
||||
|
||||
goaway(z_root() . '/admin/profs');
|
||||
}
|
||||
|
||||
if((argc() > 2) && argv(2) === 'new') {
|
||||
return replace_macros(get_markup_template('profdef_edit.tpl'),array(
|
||||
'$header' => t('New Profile Field'),
|
||||
'$field_name' => array('field_name',t('Field nickname'),$_REQUEST['field_name'],t('System name of field')),
|
||||
'$field_type' => array('field_type',t('Input type'),(($_REQUEST['field_type']) ? $_REQUEST['field_type'] : 'text'),''),
|
||||
'$field_desc' => array('field_desc',t('Field Name'),$_REQUEST['field_desc'],t('Label on profile pages')),
|
||||
'$field_help' => array('field_help',t('Help text'),$_REQUEST['field_help'],t('Additional info (optional)')),
|
||||
'$submit' => t('Save')
|
||||
));
|
||||
}
|
||||
|
||||
if((argc() > 2) && intval(argv(2))) {
|
||||
$r = q("select * from profdef where id = %d limit 1",
|
||||
intval(argv(2))
|
||||
);
|
||||
if(! $r) {
|
||||
notice( t('Field definition not found') . EOL);
|
||||
goaway(z_root() . '/admin/profs');
|
||||
}
|
||||
|
||||
return replace_macros(get_markup_template('profdef_edit.tpl'),array(
|
||||
'$id' => intval($r[0]['id']),
|
||||
'$header' => t('Edit Profile Field'),
|
||||
'$field_name' => array('field_name',t('Field nickname'),$r[0]['field_name'],t('System name of field')),
|
||||
'$field_type' => array('field_type',t('Input type'),$r[0]['field_type'],''),
|
||||
'$field_desc' => array('field_desc',t('Field Name'),$r[0]['field_desc'],t('Label on profile pages')),
|
||||
'$field_help' => array('field_help',t('Help text'),$r[0]['field_help'],t('Additional info (optional)')),
|
||||
'$submit' => t('Save')
|
||||
));
|
||||
}
|
||||
|
||||
$basic = '';
|
||||
$barr = array();
|
||||
$fields = get_profile_fields_basic();
|
||||
if(! $fields)
|
||||
$fields = get_profile_fields_basic(1);
|
||||
if($fields) {
|
||||
foreach($fields as $k => $v) {
|
||||
if($basic)
|
||||
$basic .= ', ';
|
||||
$basic .= trim($k);
|
||||
$barr[] = trim($k);
|
||||
}
|
||||
}
|
||||
|
||||
$advanced = '';
|
||||
$fields = get_profile_fields_advanced();
|
||||
if(! $fields)
|
||||
$fields = get_profile_fields_advanced(1);
|
||||
if($fields) {
|
||||
foreach($fields as $k => $v) {
|
||||
if(in_array(trim($k),$barr))
|
||||
continue;
|
||||
if($advanced)
|
||||
$advanced .= ', ';
|
||||
$advanced .= trim($k);
|
||||
}
|
||||
}
|
||||
|
||||
$all = '';
|
||||
$fields = get_profile_fields_advanced(1);
|
||||
if($fields) {
|
||||
foreach($fields as $k => $v) {
|
||||
if($all)
|
||||
$all .= ', ';
|
||||
$all .= trim($k);
|
||||
}
|
||||
}
|
||||
|
||||
$r = q("select * from profdef where true");
|
||||
if($r) {
|
||||
foreach($r as $rr) {
|
||||
if($all)
|
||||
$all .= ', ';
|
||||
$all .= $rr['field_name'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$o = replace_macros(get_markup_template('admin_profiles.tpl'),array(
|
||||
'$title' => t('Profile Fields'),
|
||||
'$basic' => array('basic',t('Basic Profile Fields'),$basic,''),
|
||||
'$advanced' => array('advanced',t('Advanced Profile Fields'),$advanced,t('(In addition to basic fields)')),
|
||||
'$all' => $all,
|
||||
'$all_desc' => t('All available fields'),
|
||||
'$cust_field_desc' => t('Custom Fields'),
|
||||
'$cust_fields' => $r,
|
||||
'$edit' => t('Edit'),
|
||||
'$drop' => t('Delete'),
|
||||
'$new' => t('Create Custom Field'),
|
||||
'$submit' => t('Submit')
|
||||
));
|
||||
|
||||
return $o;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
53
Zotlabs/Module/Admin/Queue.php
Normal file
53
Zotlabs/Module/Admin/Queue.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
|
||||
class Queue {
|
||||
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
$o = '';
|
||||
|
||||
$expert = ((array_key_exists('expert',$_REQUEST)) ? intval($_REQUEST['expert']) : 0);
|
||||
|
||||
if($_REQUEST['drophub']) {
|
||||
hubloc_mark_as_down($_REQUEST['drophub']);
|
||||
remove_queue_by_posturl($_REQUEST['drophub']);
|
||||
}
|
||||
|
||||
if($_REQUEST['emptyhub']) {
|
||||
remove_queue_by_posturl($_REQUEST['emptyhub']);
|
||||
}
|
||||
|
||||
$r = q("select count(outq_posturl) as total, max(outq_priority) as priority, outq_posturl from outq
|
||||
where outq_delivered = 0 group by outq_posturl order by total desc");
|
||||
|
||||
for($x = 0; $x < count($r); $x ++) {
|
||||
$r[$x]['eurl'] = urlencode($r[$x]['outq_posturl']);
|
||||
$r[$x]['connected'] = datetime_convert('UTC',date_default_timezone_get(),$r[$x]['connected'],'Y-m-d');
|
||||
}
|
||||
|
||||
$o = replace_macros(get_markup_template('admin_queue.tpl'), array(
|
||||
'$banner' => t('Queue Statistics'),
|
||||
'$numentries' => t('Total Entries'),
|
||||
'$priority' => t('Priority'),
|
||||
'$desturl' => t('Destination URL'),
|
||||
'$nukehub' => t('Mark hub permanently offline'),
|
||||
'$empty' => t('Empty queue for this hub'),
|
||||
'$lastconn' => t('Last known contact'),
|
||||
'$hasentries' => ((count($r)) ? true : false),
|
||||
'$entries' => $r,
|
||||
'$expert' => $expert
|
||||
));
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
123
Zotlabs/Module/Admin/Security.php
Normal file
123
Zotlabs/Module/Admin/Security.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
class Security {
|
||||
|
||||
function post() {
|
||||
check_form_security_token_redirectOnErr('/admin/security', 'admin_security');
|
||||
|
||||
$allowed_email = ((x($_POST,'allowed_email')) ? notags(trim($_POST['allowed_email'])) : '');
|
||||
$not_allowed_email = ((x($_POST,'not_allowed_email')) ? notags(trim($_POST['not_allowed_email'])) : '');
|
||||
|
||||
set_config('system','allowed_email', $allowed_email);
|
||||
set_config('system','not_allowed_email', $not_allowed_email);
|
||||
|
||||
$block_public = ((x($_POST,'block_public')) ? True : False);
|
||||
set_config('system','block_public',$block_public);
|
||||
|
||||
$ws = $this->trim_array_elems(explode("\n",$_POST['whitelisted_sites']));
|
||||
set_config('system','whitelisted_sites',$ws);
|
||||
|
||||
$bs = $this->trim_array_elems(explode("\n",$_POST['blacklisted_sites']));
|
||||
set_config('system','blacklisted_sites',$bs);
|
||||
|
||||
$wc = $this->trim_array_elems(explode("\n",$_POST['whitelisted_channels']));
|
||||
set_config('system','whitelisted_channels',$wc);
|
||||
|
||||
$bc = $this->trim_array_elems(explode("\n",$_POST['blacklisted_channels']));
|
||||
set_config('system','blacklisted_channels',$bc);
|
||||
|
||||
$embed_sslonly = ((x($_POST,'embed_sslonly')) ? True : False);
|
||||
set_config('system','embed_sslonly',$embed_sslonly);
|
||||
|
||||
$we = $this->trim_array_elems(explode("\n",$_POST['embed_allow']));
|
||||
set_config('system','embed_allow',$we);
|
||||
|
||||
$be = $this->trim_array_elems(explode("\n",$_POST['embed_deny']));
|
||||
set_config('system','embed_deny',$be);
|
||||
|
||||
$ts = ((x($_POST,'transport_security')) ? True : False);
|
||||
set_config('system','transport_security_header',$ts);
|
||||
|
||||
$cs = ((x($_POST,'content_security')) ? True : False);
|
||||
set_config('system','content_security_policy',$cs);
|
||||
|
||||
goaway(z_root() . '/admin/security');
|
||||
}
|
||||
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
$whitesites = get_config('system','whitelisted_sites');
|
||||
$whitesites_str = ((is_array($whitesites)) ? implode($whitesites,"\n") : '');
|
||||
|
||||
$blacksites = get_config('system','blacklisted_sites');
|
||||
$blacksites_str = ((is_array($blacksites)) ? implode($blacksites,"\n") : '');
|
||||
|
||||
|
||||
$whitechannels = get_config('system','whitelisted_channels');
|
||||
$whitechannels_str = ((is_array($whitechannels)) ? implode($whitechannels,"\n") : '');
|
||||
|
||||
$blackchannels = get_config('system','blacklisted_channels');
|
||||
$blackchannels_str = ((is_array($blackchannels)) ? implode($blackchannels,"\n") : '');
|
||||
|
||||
|
||||
$whiteembeds = get_config('system','embed_allow');
|
||||
$whiteembeds_str = ((is_array($whiteembeds)) ? implode($whiteembeds,"\n") : '');
|
||||
|
||||
$blackembeds = get_config('system','embed_deny');
|
||||
$blackembeds_str = ((is_array($blackembeds)) ? implode($blackembeds,"\n") : '');
|
||||
|
||||
$embed_coop = intval(get_config('system','embed_coop'));
|
||||
|
||||
if((! $whiteembeds) && (! $blackembeds)) {
|
||||
$embedhelp1 = t("By default, unfiltered HTML is allowed in embedded media. This is inherently insecure.");
|
||||
}
|
||||
|
||||
$embedhelp2 = t("The recommended setting is to only allow unfiltered HTML from the following sites:");
|
||||
$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'),
|
||||
'$page' => t('Security'),
|
||||
'$form_security_token' => get_form_security_token('admin_security'),
|
||||
'$block_public' => array('block_public', t("Block public"), get_config('system','block_public'), t("Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated.")),
|
||||
'$transport_security' => array('transport_security', t('Set "Transport Security" HTTP header'),intval(get_config('system','transport_security_header')),''),
|
||||
'$content_security' => array('content_security', t('Set "Content Security Policy" HTTP header'),intval(get_config('system','content_security_policy')),''),
|
||||
'$allowed_email' => array('allowed_email', t("Allowed email domains"), get_config('system','allowed_email'), t("Comma separated list of domains which are allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains")),
|
||||
'$not_allowed_email' => array('not_allowed_email', t("Not allowed email domains"), get_config('system','not_allowed_email'), t("Comma separated list of domains which are not allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains, unless allowed domains have been defined.")),
|
||||
'$whitelisted_sites' => array('whitelisted_sites', t('Allow communications only from these sites'), $whitesites_str, t('One site per line. Leave empty to allow communication from anywhere by default')),
|
||||
'$blacklisted_sites' => array('blacklisted_sites', t('Block communications from these sites'), $blacksites_str, ''),
|
||||
'$whitelisted_channels' => array('whitelisted_channels', t('Allow communications only from these channels'), $whitechannels_str, t('One channel (hash) per line. Leave empty to allow from any channel by default')),
|
||||
'$blacklisted_channels' => array('blacklisted_channels', t('Block communications from these channels'), $blackchannels_str, ''),
|
||||
'$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, ''),
|
||||
|
||||
// '$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')
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
function trim_array_elems($arr) {
|
||||
$narr = array();
|
||||
|
||||
if($arr && is_array($arr)) {
|
||||
for($x = 0; $x < count($arr); $x ++) {
|
||||
$y = trim($arr[$x]);
|
||||
if($y)
|
||||
$narr[] = $y;
|
||||
}
|
||||
}
|
||||
return $narr;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
321
Zotlabs/Module/Admin/Site.php
Normal file
321
Zotlabs/Module/Admin/Site.php
Normal file
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
class Site {
|
||||
|
||||
/**
|
||||
* @brief POST handler for Admin Site Page.
|
||||
*
|
||||
*/
|
||||
function post(){
|
||||
if (!x($_POST, 'page_site')) {
|
||||
return;
|
||||
}
|
||||
|
||||
check_form_security_token_redirectOnErr('/admin/site', 'admin_site');
|
||||
|
||||
$sitename = ((x($_POST,'sitename')) ? notags(trim($_POST['sitename'])) : '');
|
||||
$server_role = ((x($_POST,'server_role')) ? notags(trim($_POST['server_role'])) : 'standard');
|
||||
|
||||
$banner = ((x($_POST,'banner')) ? trim($_POST['banner']) : false);
|
||||
|
||||
$admininfo = ((x($_POST,'admininfo')) ? trim($_POST['admininfo']) : false);
|
||||
$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'])) : '');
|
||||
$maximagesize = ((x($_POST,'maximagesize')) ? intval(trim($_POST['maximagesize'])) : 0);
|
||||
|
||||
$register_policy = ((x($_POST,'register_policy')) ? intval(trim($_POST['register_policy'])) : 0);
|
||||
|
||||
$access_policy = ((x($_POST,'access_policy')) ? intval(trim($_POST['access_policy'])) : 0);
|
||||
$invite_only = ((x($_POST,'invite_only')) ? 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'])) : '');
|
||||
$frontpage = ((x($_POST,'frontpage')) ? notags(trim($_POST['frontpage'])) : '');
|
||||
$mirror_frontpage = ((x($_POST,'mirror_frontpage')) ? intval(trim($_POST['mirror_frontpage'])) : 0);
|
||||
$directory_server = ((x($_POST,'directory_server')) ? trim($_POST['directory_server']) : '');
|
||||
$allowed_sites = ((x($_POST,'allowed_sites')) ? notags(trim($_POST['allowed_sites'])) : '');
|
||||
$force_publish = ((x($_POST,'publish_all')) ? True : False);
|
||||
$disable_discover_tab = ((x($_POST,'disable_discover_tab')) ? False : True);
|
||||
$login_on_homepage = ((x($_POST,'login_on_homepage')) ? True : False);
|
||||
$enable_context_help = ((x($_POST,'enable_context_help')) ? True : False);
|
||||
$global_directory = ((x($_POST,'directory_submit_url')) ? notags(trim($_POST['directory_submit_url'])) : '');
|
||||
$no_community_page = !((x($_POST,'no_community_page')) ? True : False);
|
||||
$default_expire_days = ((array_key_exists('default_expire_days',$_POST)) ? intval($_POST['default_expire_days']) : 0);
|
||||
|
||||
$verifyssl = ((x($_POST,'verifyssl')) ? True : False);
|
||||
$proxyuser = ((x($_POST,'proxyuser')) ? notags(trim($_POST['proxyuser'])) : '');
|
||||
$proxy = ((x($_POST,'proxy')) ? notags(trim($_POST['proxy'])) : '');
|
||||
$timeout = ((x($_POST,'timeout')) ? intval(trim($_POST['timeout'])) : 60);
|
||||
$delivery_interval = ((x($_POST,'delivery_interval'))? intval(trim($_POST['delivery_interval'])) : 0);
|
||||
$delivery_batch_count = ((x($_POST,'delivery_batch_count') && $_POST['delivery_batch_count'] > 0)? intval(trim($_POST['delivery_batch_count'])) : 1);
|
||||
$poll_interval = ((x($_POST,'poll_interval')) ? intval(trim($_POST['poll_interval'])) : 0);
|
||||
$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);
|
||||
$techlevel_lock = ((x($_POST,'techlock')) ? intval($_POST['techlock']) : 0);
|
||||
|
||||
$techlevel = null;
|
||||
if(array_key_exists('techlevel', $_POST))
|
||||
$techlevel = intval($_POST['techlevel']);
|
||||
|
||||
set_config('system', 'server_role', $server_role);
|
||||
set_config('system', 'feed_contacts', $feed_contacts);
|
||||
set_config('system', 'delivery_interval', $delivery_interval);
|
||||
set_config('system', 'delivery_batch_count', $delivery_batch_count);
|
||||
set_config('system', 'poll_interval', $poll_interval);
|
||||
set_config('system', 'maxloadavg', $maxloadavg);
|
||||
set_config('system', 'frontpage', $frontpage);
|
||||
set_config('system', 'mirror_frontpage', $mirror_frontpage);
|
||||
set_config('system', 'sitename', $sitename);
|
||||
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', 'default_expire_days', $default_expire_days);
|
||||
set_config('system', 'techlevel_lock', $techlevel_lock);
|
||||
|
||||
if(! is_null($techlevel))
|
||||
set_config('system', 'techlevel', $techlevel);
|
||||
|
||||
if($directory_server)
|
||||
set_config('system','directory_server',$directory_server);
|
||||
|
||||
if ($banner == '') {
|
||||
del_config('system', 'banner');
|
||||
} else {
|
||||
set_config('system', 'banner', $banner);
|
||||
}
|
||||
|
||||
if ($admininfo == ''){
|
||||
del_config('system', 'admininfo');
|
||||
} else {
|
||||
require_once('include/text.php');
|
||||
linkify_tags($a, $admininfo, local_channel());
|
||||
set_config('system', 'admininfo', $admininfo);
|
||||
}
|
||||
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);
|
||||
set_config('system','maximagesize', $maximagesize);
|
||||
|
||||
set_config('system','register_policy', $register_policy);
|
||||
set_config('system','invitation_only', $invite_only);
|
||||
set_config('system','access_policy', $access_policy);
|
||||
set_config('system','account_abandon_days', $abandon_days);
|
||||
set_config('system','register_text', $register_text);
|
||||
set_config('system','allowed_sites', $allowed_sites);
|
||||
set_config('system','publish_all', $force_publish);
|
||||
set_config('system','disable_discover_tab', $disable_discover_tab);
|
||||
if ($global_directory == '') {
|
||||
del_config('system', 'directory_submit_url');
|
||||
} else {
|
||||
set_config('system', 'directory_submit_url', $global_directory);
|
||||
}
|
||||
|
||||
set_config('system','no_community_page', $no_community_page);
|
||||
set_config('system','no_utf', $no_utf);
|
||||
set_config('system','verifyssl', $verifyssl);
|
||||
set_config('system','proxyuser', $proxyuser);
|
||||
set_config('system','proxy', $proxy);
|
||||
set_config('system','curl_timeout', $timeout);
|
||||
|
||||
info( t('Site settings updated.') . EOL);
|
||||
goaway(z_root() . '/admin/site' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Admin page site.
|
||||
*
|
||||
* @return string with HTML
|
||||
*/
|
||||
function get() {
|
||||
|
||||
/* Installed langs */
|
||||
$lang_choices = array();
|
||||
$langs = glob('view/*/hstrings.php');
|
||||
|
||||
if(is_array($langs) && count($langs)) {
|
||||
if(! in_array('view/en/hstrings.php',$langs))
|
||||
$langs[] = 'view/en/';
|
||||
asort($langs);
|
||||
foreach($langs as $l) {
|
||||
$t = explode("/",$l);
|
||||
$lang_choices[$t[1]] = $t[1];
|
||||
}
|
||||
}
|
||||
|
||||
/* Installed themes */
|
||||
$theme_choices_mobile["---"] = t("Default");
|
||||
$theme_choices = array();
|
||||
$files = glob('view/theme/*');
|
||||
if($files) {
|
||||
foreach($files as $file) {
|
||||
$vars = '';
|
||||
$f = basename($file);
|
||||
|
||||
$info = get_theme_info($f);
|
||||
$compatible = check_plugin_versions($info);
|
||||
if(!$compatible) {
|
||||
$theme_choices[$f] = $theme_choices_mobile[$f] = sprintf(t('%s - (Incompatible)'), $f);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file_exists($file . '/library'))
|
||||
continue;
|
||||
if (file_exists($file . '/mobile'))
|
||||
$vars = t('mobile');
|
||||
if (file_exists($file . '/experimental'))
|
||||
$vars .= t('experimental');
|
||||
if (file_exists($file . '/unsupported'))
|
||||
$vars .= t('unsupported');
|
||||
if ($vars) {
|
||||
$theme_choices[$f] = $f . ' (' . $vars . ')';
|
||||
$theme_choices_mobile[$f] = $f . ' (' . $vars . ')';
|
||||
}
|
||||
else {
|
||||
$theme_choices[$f] = $f;
|
||||
$theme_choices_mobile[$f] = $f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$dir_choices = null;
|
||||
$dirmode = get_config('system','directory_mode');
|
||||
$realm = get_directory_realm();
|
||||
|
||||
// directory server should not be set or settable unless we are a directory client
|
||||
|
||||
if($dirmode == DIRECTORY_MODE_NORMAL) {
|
||||
$x = q("select site_url from site where site_flags in (%d,%d) and site_realm = '%s'",
|
||||
intval(DIRECTORY_MODE_SECONDARY),
|
||||
intval(DIRECTORY_MODE_PRIMARY),
|
||||
dbesc($realm)
|
||||
);
|
||||
if($x) {
|
||||
$dir_choices = array();
|
||||
foreach($x as $xx) {
|
||||
$dir_choices[$xx['site_url']] = $xx['site_url'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Banner */
|
||||
|
||||
$banner = get_config('system', 'banner');
|
||||
if($banner === false)
|
||||
$banner = get_config('system','sitename');
|
||||
|
||||
$banner = htmlspecialchars($banner);
|
||||
|
||||
/* Admin Info */
|
||||
$admininfo = get_config('system', 'admininfo');
|
||||
|
||||
/* Register policy */
|
||||
$register_choices = Array(
|
||||
REGISTER_CLOSED => t("No"),
|
||||
REGISTER_APPROVE => t("Yes - with approval"),
|
||||
REGISTER_OPEN => t("Yes")
|
||||
);
|
||||
|
||||
/* Acess policy */
|
||||
$access_choices = Array(
|
||||
ACCESS_PRIVATE => t("My site is not a public server"),
|
||||
ACCESS_PAID => t("My site has paid access only"),
|
||||
ACCESS_FREE => t("My site has free access only"),
|
||||
ACCESS_TIERED => t("My site offers free accounts with optional paid upgrades")
|
||||
);
|
||||
|
||||
$discover_tab = get_config('system','disable_discover_tab');
|
||||
// $disable public streams by default
|
||||
if($discover_tab === false)
|
||||
$discover_tab = 1;
|
||||
// now invert the logic for the setting.
|
||||
$discover_tab = (1 - $discover_tab);
|
||||
|
||||
$server_roles = [
|
||||
'basic' => t('Basic/Minimal Social Networking'),
|
||||
'standard' => t('Standard Configuration (default)'),
|
||||
'pro' => t('Professional')
|
||||
];
|
||||
|
||||
$techlevels = [
|
||||
'0' => t('Beginner/Basic'),
|
||||
'1' => t('Novice - not skilled but willing to learn'),
|
||||
'2' => t('Intermediate - somewhat comfortable'),
|
||||
'3' => t('Advanced - very comfortable'),
|
||||
'4' => t('Expert - I can write computer code'),
|
||||
'5' => t('Wizard - I probably know more than you do')
|
||||
];
|
||||
|
||||
$homelogin = get_config('system','login_on_homepage');
|
||||
$enable_context_help = get_config('system','enable_context_help');
|
||||
|
||||
$t = get_markup_template("admin_site.tpl");
|
||||
return replace_macros($t, array(
|
||||
'$title' => t('Administration'),
|
||||
'$page' => t('Site'),
|
||||
'$submit' => t('Submit'),
|
||||
'$registration' => t('Registration'),
|
||||
'$upload' => t('File upload'),
|
||||
'$corporate' => t('Policies'),
|
||||
'$advanced' => t('Advanced'),
|
||||
|
||||
'$baseurl' => z_root(),
|
||||
// name, label, value, help string, extra data...
|
||||
'$sitename' => array('sitename', t("Site name"), htmlspecialchars(get_config('system','sitename'), ENT_QUOTES, 'UTF-8'),''),
|
||||
|
||||
'$server_role' => array('server_role', t("Server Configuration/Role"), get_config('system','server_role'),'',$server_roles),
|
||||
|
||||
'$techlevel' => [ 'techlevel', t('Site default technical skill level'), get_config('system','techlevel'), t('Used to provide a member experience matched to technical comfort level'), $techlevels ],
|
||||
|
||||
'$techlock' => [ 'techlock', t('Lock the technical skill level setting'), get_config('system','techlevel_lock'), t('Members can set their own technical comfort level by default') ],
|
||||
|
||||
'$banner' => array('banner', t("Banner/Logo"), $banner, ""),
|
||||
'$admininfo' => array('admininfo', t("Administrator Information"), $admininfo, t("Contact information for site administrators. Displayed on siteinfo page. BBCode can be used here")),
|
||||
'$siteinfo' => array('siteinfo', t('Site Information'), get_config('system','siteinfo'), t("Publicly visible description of this site. Displayed on siteinfo page. BBCode can be used here")),
|
||||
'$language' => array('language', t("System language"), get_config('system','language'), "", $lang_choices),
|
||||
'$theme' => array('theme', t("System theme"), get_config('system','theme'), t("Default system theme - may be over-ridden by user profiles - <a href='#' id='cnftheme'>change theme settings</a>"), $theme_choices),
|
||||
'$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile),
|
||||
// '$site_channel' => array('site_channel', t("Channel to use for this website's static pages"), get_config('system','site_channel'), t("Site Channel")),
|
||||
'$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.")),
|
||||
'$access_policy' => array('access_policy', t("Which best describes the types of account offered by this hub?"), get_config('system','access_policy'), "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.")),
|
||||
'$frontpage' => array('frontpage', t("Site homepage to show visitors (default: login box)"), get_config('system','frontpage'), t("example: 'public' 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.')),
|
||||
'$login_on_homepage' => array('login_on_homepage', t("Login on Homepage"),((intval($homelogin) || $homelogin === false) ? 1 : '') , t("Present a login box to visitors on the home page if no other content has been configured.")),
|
||||
'$enable_context_help' => array('enable_context_help', t("Enable context help"),((intval($enable_context_help) === 1 || $enable_context_help === false) ? 1 : 0) , t("Display contextual help for the current page when the help button is pressed.")),
|
||||
|
||||
'$directory_server' => (($dir_choices) ? array('directory_server', t("Directory Server URL"), get_config('system','directory_server'), t("Default directory server"), $dir_choices) : null),
|
||||
|
||||
'$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).")),
|
||||
'$delivery_interval' => array('delivery_interval', t("Delivery interval"), (x(get_config('system','delivery_interval'))?get_config('system','delivery_interval'):2), t("Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.")),
|
||||
'$delivery_batch_count' => array('delivery_batch_count', t('Deliveries per process'),(x(get_config('system','delivery_batch_count'))?get_config('system','delivery_batch_count'):1), t("Number of deliveries to attempt in a single operating system process. Adjust if necessary to tune system performance. Recommend: 1-5.")),
|
||||
'$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.")),
|
||||
'$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')),
|
||||
'$form_security_token' => get_form_security_token("admin_site"),
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
233
Zotlabs/Module/Admin/Themes.php
Normal file
233
Zotlabs/Module/Admin/Themes.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module\Admin;
|
||||
|
||||
|
||||
class Themes {
|
||||
|
||||
function post() {
|
||||
|
||||
$theme = argv(2);
|
||||
if (is_file("view/theme/$theme/php/config.php")){
|
||||
require_once("view/theme/$theme/php/config.php");
|
||||
// fixme add parent theme if derived
|
||||
if (function_exists("theme_admin_post")){
|
||||
theme_admin_post($a);
|
||||
}
|
||||
}
|
||||
info(t('Theme settings updated.'));
|
||||
if(is_ajax())
|
||||
return;
|
||||
|
||||
goaway(z_root() . '/admin/themes/' . $theme );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Themes admin page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
function get(){
|
||||
|
||||
$allowed_themes_str = get_config('system', 'allowed_themes');
|
||||
$allowed_themes_raw = explode(',', $allowed_themes_str);
|
||||
$allowed_themes = array();
|
||||
if(count($allowed_themes_raw))
|
||||
foreach($allowed_themes_raw as $x)
|
||||
if(strlen(trim($x)))
|
||||
$allowed_themes[] = trim($x);
|
||||
|
||||
$themes = array();
|
||||
$files = glob('view/theme/*');
|
||||
if($files) {
|
||||
foreach($files as $file) {
|
||||
$f = basename($file);
|
||||
$is_experimental = intval(file_exists($file . '/.experimental'));
|
||||
$is_supported = 1-(intval(file_exists($file . '/.unsupported'))); // Is not used yet
|
||||
$is_allowed = intval(in_array($f,$allowed_themes));
|
||||
$themes[] = array('name' => $f, 'experimental' => $is_experimental, 'supported' => $is_supported, 'allowed' => $is_allowed);
|
||||
}
|
||||
}
|
||||
|
||||
if(! count($themes)) {
|
||||
notice( t('No themes found.'));
|
||||
return '';
|
||||
}
|
||||
|
||||
/*
|
||||
* Single theme
|
||||
*/
|
||||
|
||||
if (\App::$argc == 3){
|
||||
$theme = \App::$argv[2];
|
||||
if(! is_dir("view/theme/$theme")){
|
||||
notice( t("Item not found.") );
|
||||
return '';
|
||||
}
|
||||
|
||||
if (x($_GET,"a") && $_GET['a']=="t"){
|
||||
check_form_security_token_redirectOnErr('/admin/themes', 'admin_themes', 't');
|
||||
|
||||
// Toggle theme status
|
||||
|
||||
$this->toggle_theme($themes, $theme, $result);
|
||||
$s = $this->rebuild_theme_table($themes);
|
||||
if($result)
|
||||
info( sprintf('Theme %s enabled.', $theme));
|
||||
else
|
||||
info( sprintf('Theme %s disabled.', $theme));
|
||||
|
||||
set_config('system', 'allowed_themes', $s);
|
||||
goaway(z_root() . '/admin/themes' );
|
||||
}
|
||||
|
||||
// display theme details
|
||||
require_once('library/markdown.php');
|
||||
|
||||
if ($this->theme_status($themes,$theme)) {
|
||||
$status="on"; $action= t("Disable");
|
||||
} else {
|
||||
$status="off"; $action= t("Enable");
|
||||
}
|
||||
|
||||
$readme=Null;
|
||||
if (is_file("view/theme/$theme/README.md")){
|
||||
$readme = file_get_contents("view/theme/$theme/README.md");
|
||||
$readme = Markdown($readme);
|
||||
} else if (is_file("view/theme/$theme/README")){
|
||||
$readme = "<pre>". file_get_contents("view/theme/$theme/README") ."</pre>";
|
||||
}
|
||||
|
||||
$admin_form = '';
|
||||
if (is_file("view/theme/$theme/php/config.php")){
|
||||
require_once("view/theme/$theme/php/config.php");
|
||||
if(function_exists("theme_admin")){
|
||||
$admin_form = theme_admin($a);
|
||||
}
|
||||
}
|
||||
|
||||
$screenshot = array( get_theme_screenshot($theme), t('Screenshot'));
|
||||
if(! stristr($screenshot[0],$theme))
|
||||
$screenshot = null;
|
||||
|
||||
$t = get_markup_template('admin_plugins_details.tpl');
|
||||
return replace_macros($t, array(
|
||||
'$title' => t('Administration'),
|
||||
'$page' => t('Themes'),
|
||||
'$toggle' => t('Toggle'),
|
||||
'$settings' => t('Settings'),
|
||||
'$baseurl' => z_root(),
|
||||
|
||||
'$plugin' => $theme,
|
||||
'$status' => $status,
|
||||
'$action' => $action,
|
||||
'$info' => get_theme_info($theme),
|
||||
'$function' => 'themes',
|
||||
'$admin_form' => $admin_form,
|
||||
'$str_author' => t('Author: '),
|
||||
'$str_maintainer' => t('Maintainer: '),
|
||||
'$screenshot' => $screenshot,
|
||||
'$readme' => $readme,
|
||||
|
||||
'$form_security_token' => get_form_security_token('admin_themes'),
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
* List themes
|
||||
*/
|
||||
|
||||
$xthemes = array();
|
||||
if($themes) {
|
||||
foreach($themes as $th) {
|
||||
$xthemes[] = array($th['name'],(($th['allowed']) ? "on" : "off"), get_theme_info($th['name']));
|
||||
}
|
||||
}
|
||||
|
||||
$t = get_markup_template('admin_plugins.tpl');
|
||||
return replace_macros($t, array(
|
||||
'$title' => t('Administration'),
|
||||
'$page' => t('Themes'),
|
||||
'$submit' => t('Submit'),
|
||||
'$baseurl' => z_root(),
|
||||
'$function' => 'themes',
|
||||
'$plugins' => $xthemes,
|
||||
'$experimental' => t('[Experimental]'),
|
||||
'$unsupported' => t('[Unsupported]'),
|
||||
'$form_security_token' => get_form_security_token('admin_themes'),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param array $themes
|
||||
* @param string $th
|
||||
* @param int $result
|
||||
*/
|
||||
function toggle_theme(&$themes, $th, &$result) {
|
||||
for($x = 0; $x < count($themes); $x ++) {
|
||||
if($themes[$x]['name'] === $th) {
|
||||
if($themes[$x]['allowed']) {
|
||||
$themes[$x]['allowed'] = 0;
|
||||
$result = 0;
|
||||
}
|
||||
else {
|
||||
$themes[$x]['allowed'] = 1;
|
||||
$result = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $themes
|
||||
* @param string $th
|
||||
* @return int
|
||||
*/
|
||||
function theme_status($themes, $th) {
|
||||
for($x = 0; $x < count($themes); $x ++) {
|
||||
if($themes[$x]['name'] === $th) {
|
||||
if($themes[$x]['allowed']) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $themes
|
||||
* @return string
|
||||
*/
|
||||
function rebuild_theme_table($themes) {
|
||||
$o = '';
|
||||
if(count($themes)) {
|
||||
foreach($themes as $th) {
|
||||
if($th['allowed']) {
|
||||
if(strlen($o))
|
||||
$o .= ',';
|
||||
$o .= $th['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
125
Zotlabs/Module/Api.php
Normal file
125
Zotlabs/Module/Api.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/api.php');
|
||||
|
||||
class Api extends \Zotlabs\Web\Controller {
|
||||
|
||||
|
||||
function init() {
|
||||
zot_api_init();
|
||||
|
||||
api_register_func('api/client/register', 'api_client_register', false);
|
||||
api_register_func('api/oauth/request_token', 'api_oauth_request_token', false);
|
||||
api_register_func('api/oauth/access_token', 'api_oauth_access_token', false);
|
||||
|
||||
$args = [];
|
||||
call_hooks('api_register',$args);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function post() {
|
||||
if(! local_channel()) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function get() {
|
||||
|
||||
if(\App::$cmd === 'api/oauth/authorize'){
|
||||
|
||||
/*
|
||||
* api/oauth/authorize interact with the user. return a standard page
|
||||
*/
|
||||
|
||||
\App::$page['template'] = 'minimal';
|
||||
|
||||
// get consumer/client from request token
|
||||
try {
|
||||
$request = OAuth1Request::from_request();
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
echo "<pre>"; var_dump($e); killme();
|
||||
}
|
||||
|
||||
|
||||
if(x($_POST,'oauth_yes')){
|
||||
|
||||
$app = $this->oauth_get_client($request);
|
||||
if (is_null($app))
|
||||
return "Invalid request. Unknown token.";
|
||||
|
||||
$consumer = new OAuth1Consumer($app['client_id'], $app['pw'], $app['redirect_uri']);
|
||||
|
||||
$verifier = md5($app['secret'] . local_channel());
|
||||
set_config('oauth', $verifier, local_channel());
|
||||
|
||||
|
||||
if($consumer->callback_url != null) {
|
||||
$params = $request->get_parameters();
|
||||
$glue = '?';
|
||||
if(strstr($consumer->callback_url,$glue))
|
||||
$glue = '?';
|
||||
goaway($consumer->callback_url . $glue . "oauth_token=" . OAuth1Util::urlencode_rfc3986($params['oauth_token']) . "&oauth_verifier=" . OAuth1Util::urlencode_rfc3986($verifier));
|
||||
killme();
|
||||
}
|
||||
|
||||
$tpl = get_markup_template("oauth_authorize_done.tpl");
|
||||
$o = replace_macros($tpl, array(
|
||||
'$title' => t('Authorize application connection'),
|
||||
'$info' => t('Return to your app and insert this Security Code:'),
|
||||
'$code' => $verifier,
|
||||
));
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
if(! local_channel()) {
|
||||
//TODO: we need login form to redirect to this page
|
||||
notice( t('Please login to continue.') . EOL );
|
||||
return login(false,'api-login',$request->get_parameters());
|
||||
}
|
||||
|
||||
$app = $this->oauth_get_client($request);
|
||||
if (is_null($app))
|
||||
return "Invalid request. Unknown token.";
|
||||
|
||||
$tpl = get_markup_template('oauth_authorize.tpl');
|
||||
$o = replace_macros($tpl, array(
|
||||
'$title' => t('Authorize application connection'),
|
||||
'$app' => $app,
|
||||
'$authorize' => t('Do you want to authorize this application to access your posts and contacts, and/or create new posts for you?'),
|
||||
'$yes' => t('Yes'),
|
||||
'$no' => t('No'),
|
||||
));
|
||||
|
||||
//echo "<pre>"; var_dump($app); killme();
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
echo api_call();
|
||||
killme();
|
||||
}
|
||||
|
||||
function oauth_get_client($request){
|
||||
|
||||
$params = $request->get_parameters();
|
||||
$token = $params['oauth_token'];
|
||||
|
||||
$r = q("SELECT clients.* FROM clients, tokens WHERE clients.client_id = tokens.client_id
|
||||
AND tokens.id = '%s' AND tokens.auth_scope = 'request' ",
|
||||
dbesc($token)
|
||||
);
|
||||
if($r)
|
||||
return $r[0];
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
139
Zotlabs/Module/Appman.php
Normal file
139
Zotlabs/Module/Appman.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php /** @file */
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
//require_once('include/apps.php');
|
||||
|
||||
use \Zotlabs\Lib as Zlib;
|
||||
|
||||
class Appman extends \Zotlabs\Web\Controller {
|
||||
|
||||
function post() {
|
||||
|
||||
if(! local_channel())
|
||||
return;
|
||||
|
||||
if($_POST['url']) {
|
||||
$arr = array(
|
||||
'uid' => intval($_REQUEST['uid']),
|
||||
'url' => escape_tags($_REQUEST['url']),
|
||||
'guid' => escape_tags($_REQUEST['guid']),
|
||||
'author' => escape_tags($_REQUEST['author']),
|
||||
'addr' => escape_tags($_REQUEST['addr']),
|
||||
'name' => escape_tags($_REQUEST['name']),
|
||||
'desc' => escape_tags($_REQUEST['desc']),
|
||||
'photo' => escape_tags($_REQUEST['photo']),
|
||||
'version' => escape_tags($_REQUEST['version']),
|
||||
'price' => escape_tags($_REQUEST['price']),
|
||||
'requires' => escape_tags($_REQUEST['requires']),
|
||||
'system' => intval($_REQUEST['system']),
|
||||
'plugin' => escape_tags($_REQUEST['plugin']),
|
||||
'sig' => escape_tags($_REQUEST['sig']),
|
||||
'categories' => escape_tags($_REQUEST['categories'])
|
||||
);
|
||||
|
||||
$_REQUEST['appid'] = Zlib\Apps::app_install(local_channel(),$arr);
|
||||
|
||||
if(Zlib\Apps::app_installed(local_channel(),$arr))
|
||||
info( t('App installed.') . EOL);
|
||||
|
||||
goaway(z_root() . '/apps');
|
||||
return; //not reached
|
||||
}
|
||||
|
||||
|
||||
$papp = Zlib\Apps::app_decode($_POST['papp']);
|
||||
|
||||
if(! is_array($papp)) {
|
||||
notice( t('Malformed app.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
if($_POST['install']) {
|
||||
Zlib\Apps::app_install(local_channel(),$papp);
|
||||
if(Zlib\Apps::app_installed(local_channel(),$papp))
|
||||
info( t('App installed.') . EOL);
|
||||
}
|
||||
|
||||
if($_POST['delete']) {
|
||||
Zlib\Apps::app_destroy(local_channel(),$papp);
|
||||
}
|
||||
|
||||
if($_POST['edit']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if($_POST['feature']) {
|
||||
Zlib\Apps::app_feature(local_channel(),$papp);
|
||||
}
|
||||
|
||||
if($_SESSION['return_url'])
|
||||
goaway(z_root() . '/' . $_SESSION['return_url']);
|
||||
|
||||
goaway(z_root() . '/apps');
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
if(! local_channel()) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$channel = \App::get_channel();
|
||||
$app = null;
|
||||
$embed = null;
|
||||
if($_REQUEST['appid']) {
|
||||
$r = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc($_REQUEST['appid']),
|
||||
dbesc(local_channel())
|
||||
);
|
||||
if($r) {
|
||||
$app = $r[0];
|
||||
|
||||
$term = q("select * from term where otype = %d and oid = %d",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($r[0]['id'])
|
||||
);
|
||||
if($term) {
|
||||
$app['categories'] = '';
|
||||
foreach($term as $t) {
|
||||
if($app['categories'])
|
||||
$app['categories'] .= ',';
|
||||
$app['categories'] .= $t['term'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$embed = array('embed', t('Embed code'), Zlib\Apps::app_encode($app,true),'', 'onclick="this.select();"');
|
||||
|
||||
}
|
||||
|
||||
return replace_macros(get_markup_template('app_create.tpl'), array(
|
||||
|
||||
'$banner' => (($app) ? t('Edit App') : t('Create App')),
|
||||
'$app' => $app,
|
||||
'$guid' => (($app) ? $app['app_id'] : ''),
|
||||
'$author' => (($app) ? $app['app_author'] : $channel['channel_hash']),
|
||||
'$addr' => (($app) ? $app['app_addr'] : $channel['xchan_addr']),
|
||||
'$name' => array('name', t('Name of app'),(($app) ? $app['app_name'] : ''), t('Required')),
|
||||
'$url' => array('url', t('Location (URL) of app'),(($app) ? $app['app_url'] : ''), t('Required')),
|
||||
'$desc' => array('desc', t('Description'),(($app) ? $app['app_desc'] : ''), ''),
|
||||
'$photo' => array('photo', t('Photo icon URL'),(($app) ? $app['app_photo'] : ''), t('80 x 80 pixels - optional')),
|
||||
'$categories' => array('categories',t('Categories (optional, comma separated list)'),(($app) ? $app['categories'] : ''),''),
|
||||
'$version' => array('version', t('Version ID'),(($app) ? $app['app_version'] : ''), ''),
|
||||
'$price' => array('price', t('Price of app'),(($app) ? $app['app_price'] : ''), ''),
|
||||
'$page' => array('page', t('Location (URL) to purchase app'),(($app) ? $app['app_page'] : ''), ''),
|
||||
'$system' => (($app) ? intval($app['app_system']) : 0),
|
||||
'$plugin' => (($app) ? $app['app_plugin'] : ''),
|
||||
'$requires' => (($app) ? $app['app_requires'] : ''),
|
||||
'$embed' => $embed,
|
||||
'$submit' => t('Submit')
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
54
Zotlabs/Module/Apps.php
Normal file
54
Zotlabs/Module/Apps.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
|
||||
use \Zotlabs\Lib as Zlib;
|
||||
|
||||
class Apps extends \Zotlabs\Web\Controller {
|
||||
|
||||
function get() {
|
||||
|
||||
if(argc() == 2 && argv(1) == 'edit')
|
||||
$mode = 'edit';
|
||||
else
|
||||
$mode = 'list';
|
||||
|
||||
$_SESSION['return_url'] = \App::$query_string;
|
||||
|
||||
$apps = array();
|
||||
|
||||
if(local_channel()) {
|
||||
Zlib\Apps::import_system_apps();
|
||||
$syslist = array();
|
||||
$list = Zlib\Apps::app_list(local_channel(), (($mode == 'edit') ? true : false), $_GET['cat']);
|
||||
if($list) {
|
||||
foreach($list as $x) {
|
||||
$syslist[] = Zlib\Apps::app_encode($x);
|
||||
}
|
||||
}
|
||||
Zlib\Apps::translate_system_apps($syslist);
|
||||
}
|
||||
else
|
||||
$syslist = Zlib\Apps::get_system_apps(true);
|
||||
|
||||
usort($syslist,'Zotlabs\\Lib\\Apps::app_name_compare');
|
||||
|
||||
// logger('apps: ' . print_r($syslist,true));
|
||||
|
||||
foreach($syslist as $app) {
|
||||
$apps[] = Zlib\Apps::app_render($app,$mode);
|
||||
}
|
||||
|
||||
return replace_macros(get_markup_template('myapps.tpl'), array(
|
||||
'$sitename' => get_config('system','sitename'),
|
||||
'$cat' => ((array_key_exists('cat',$_GET) && $_GET['cat']) ? escape_tags($_GET['cat']) : ''),
|
||||
'$title' => t('Apps'),
|
||||
'$apps' => $apps,
|
||||
'$authed' => ((local_channel()) ? true : false),
|
||||
'$manage' => t('Manage apps'),
|
||||
'$create' => (($mode == 'edit') ? t('Create new app') : '')
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
61
Zotlabs/Module/Attach.php
Normal file
61
Zotlabs/Module/Attach.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/security.php');
|
||||
require_once('include/attach.php');
|
||||
|
||||
|
||||
class Attach extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
if(argc() < 2) {
|
||||
notice( t('Item not available.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$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)
|
||||
return;
|
||||
|
||||
|
||||
$unsafe_types = array('text/html','text/css','application/javascript');
|
||||
|
||||
if(in_array($r['data']['filetype'],$unsafe_types)) {
|
||||
header('Content-type: text/plain');
|
||||
}
|
||||
else {
|
||||
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');
|
||||
else
|
||||
$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']);
|
||||
killme();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
61
Zotlabs/Module/Authtest.php
Normal file
61
Zotlabs/Module/Authtest.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/zot.php');
|
||||
|
||||
|
||||
class Authtest extends \Zotlabs\Web\Controller {
|
||||
|
||||
function get() {
|
||||
|
||||
|
||||
$auth_success = false;
|
||||
$o .= '<h3>Magic-Auth Diagnostic</h3>';
|
||||
|
||||
if(! local_channel()) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return $o;
|
||||
}
|
||||
|
||||
$o .= '<form action="authtest" method="get">';
|
||||
$o .= 'Target URL: <input type="text" style="width: 250px;" name="dest" value="' . $_GET['dest'] .'" />';
|
||||
$o .= '<input type="submit" name="submit" value="Submit" /></form>';
|
||||
|
||||
$o .= '<br /><br />';
|
||||
|
||||
if(x($_GET,'dest')) {
|
||||
if(strpos($_GET['dest'],'@')) {
|
||||
$_GET['dest'] = $_REQUEST['dest'] = 'https://' . substr($_GET['dest'],strpos($_GET['dest'],'@')+1) . '/channel/' . substr($_GET['dest'],0,strpos($_GET['dest'],'@'));
|
||||
}
|
||||
|
||||
$_REQUEST['test'] = 1;
|
||||
$mod = new Magic();
|
||||
$x = $mod->init($a);
|
||||
|
||||
$o .= 'Local Setup returns: ' . print_r($x,true);
|
||||
|
||||
|
||||
|
||||
if($x['url']) {
|
||||
$z = z_fetch_url($x['url'] . '&test=1');
|
||||
if($z['success']) {
|
||||
$j = json_decode($z['body'],true);
|
||||
if(! $j)
|
||||
$o .= 'json_decode failure from remote site. ' . print_r($z['body'],true);
|
||||
$o .= 'Remote site responded: ' . print_r($j,true);
|
||||
if($j['success'] && strpos($j['message'],'Authentication Success'))
|
||||
$auth_success = true;
|
||||
}
|
||||
else {
|
||||
$o .= 'fetch url failure.' . print_r($z,true);
|
||||
}
|
||||
}
|
||||
|
||||
if(! $auth_success)
|
||||
$o .= 'Authentication Failed!' . EOL;
|
||||
}
|
||||
|
||||
return str_replace("\n",'<br />',$o);
|
||||
}
|
||||
|
||||
}
|
||||
92
Zotlabs/Module/Block.php
Normal file
92
Zotlabs/Module/Block.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/items.php');
|
||||
require_once('include/conversation.php');
|
||||
require_once('include/page_widgets.php');
|
||||
|
||||
|
||||
class Block extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
$which = argv(1);
|
||||
$profile = 0;
|
||||
profile_load($which,$profile);
|
||||
|
||||
if(\App::$profile['profile_uid'])
|
||||
head_set_icon(\App::$profile['thumb']);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
if(! perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),'view_pages')) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
if(argc() < 3) {
|
||||
notice( t('Invalid item.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$channel_address = argv(1);
|
||||
$page_id = argv(2);
|
||||
|
||||
$u = q("select channel_id from channel where channel_address = '%s' limit 1",
|
||||
dbesc($channel_address)
|
||||
);
|
||||
|
||||
if(! $u) {
|
||||
notice( t('Channel not found.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
if($_REQUEST['rev'])
|
||||
$revision = " and revision = " . intval($_REQUEST['rev']) . " ";
|
||||
else
|
||||
$revision = " order by revision desc ";
|
||||
|
||||
require_once('include/security.php');
|
||||
$sql_options = item_permissions_sql($u[0]['channel_id']);
|
||||
|
||||
$r = q("select item.* from item left join iconfig on item.id = iconfig.iid
|
||||
where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' and iconfig.k = 'BUILDBLOCK' and
|
||||
item_type = %d $sql_options $revision limit 1",
|
||||
intval($u[0]['channel_id']),
|
||||
dbesc($page_id),
|
||||
intval(ITEM_TYPE_BLOCK)
|
||||
);
|
||||
|
||||
if(! $r) {
|
||||
|
||||
// Check again with no permissions clause to see if it is a permissions issue
|
||||
|
||||
$x = q("select item.* from item left join iconfig on item.id = iconfig.iid
|
||||
where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' and iconfig.k = 'BUILDBLOCK' and
|
||||
item_type = %d $revision limit 1",
|
||||
intval($u[0]['channel_id']),
|
||||
dbesc($page_id),
|
||||
intval(ITEM_TYPE_BLOCK)
|
||||
);
|
||||
if($x) {
|
||||
// Yes, it's there. You just aren't allowed to see it.
|
||||
notice( t('Permission denied.') . EOL);
|
||||
}
|
||||
else {
|
||||
notice( t('Page not found.') . EOL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
xchan_query($r);
|
||||
$r = fetch_post_tags($r,true);
|
||||
|
||||
$o .= prepare_page($r[0]);
|
||||
return $o;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
173
Zotlabs/Module/Blocks.php
Normal file
173
Zotlabs/Module/Blocks.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/channel.php');
|
||||
require_once('include/conversation.php');
|
||||
require_once('include/acl_selectors.php');
|
||||
|
||||
|
||||
class Blocks extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
if(argc() > 1 && argv(1) === 'sys' && is_site_admin()) {
|
||||
$sys = get_sys_channel();
|
||||
if($sys && intval($sys['channel_id'])) {
|
||||
\App::$is_sys = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(argc() > 1)
|
||||
$which = argv(1);
|
||||
else
|
||||
return;
|
||||
|
||||
profile_load($which);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
if(! \App::$profile) {
|
||||
notice( t('Requested profile is not available.') . EOL );
|
||||
\App::$error = 404;
|
||||
return;
|
||||
}
|
||||
|
||||
$which = argv(1);
|
||||
|
||||
$_SESSION['return_url'] = \App::$query_string;
|
||||
|
||||
$uid = local_channel();
|
||||
$owner = 0;
|
||||
$channel = null;
|
||||
$observer = \App::get_observer();
|
||||
|
||||
$channel = \App::get_channel();
|
||||
|
||||
if(\App::$is_sys && is_site_admin()) {
|
||||
$sys = get_sys_channel();
|
||||
if($sys && intval($sys['channel_id'])) {
|
||||
$uid = $owner = intval($sys['channel_id']);
|
||||
$channel = $sys;
|
||||
$observer = $sys;
|
||||
}
|
||||
}
|
||||
|
||||
if(! $owner) {
|
||||
// Figure out who the page owner is.
|
||||
$r = q("select channel_id from channel where channel_address = '%s'",
|
||||
dbesc($which)
|
||||
);
|
||||
if($r) {
|
||||
$owner = intval($r[0]['channel_id']);
|
||||
}
|
||||
}
|
||||
|
||||
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
|
||||
$perms = get_all_perms($owner,$ob_hash);
|
||||
|
||||
if(! $perms['write_pages']) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Block design features from visitors
|
||||
|
||||
if((! $uid) || ($uid != $owner)) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$mimetype = (($_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner,'system','page_mimetype'));
|
||||
|
||||
$x = array(
|
||||
'webpage' => ITEM_TYPE_BLOCK,
|
||||
'is_owner' => true,
|
||||
'nickname' => \App::$profile['channel_address'],
|
||||
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
|
||||
'bang' => '',
|
||||
'showacl' => false,
|
||||
'visitor' => true,
|
||||
'mimetype' => $mimetype,
|
||||
'mimeselect' => true,
|
||||
'hide_location' => true,
|
||||
'ptlabel' => t('Block Name'),
|
||||
'profile_uid' => intval($owner),
|
||||
'expanded' => true,
|
||||
'novoting' => true,
|
||||
'bbco_autocomplete' => 'bbcode',
|
||||
'bbcode' => true
|
||||
);
|
||||
|
||||
if($_REQUEST['title'])
|
||||
$x['title'] = $_REQUEST['title'];
|
||||
if($_REQUEST['body'])
|
||||
$x['body'] = $_REQUEST['body'];
|
||||
if($_REQUEST['pagetitle'])
|
||||
$x['pagetitle'] = $_REQUEST['pagetitle'];
|
||||
|
||||
$editor = status_editor($a,$x);
|
||||
|
||||
|
||||
$r = q("select iconfig.iid, iconfig.k, iconfig.v, mid, title, body, mimetype, created, edited from iconfig
|
||||
left join item on iconfig.iid = item.id
|
||||
where uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK'
|
||||
and item_type = %d order by item.created desc",
|
||||
intval($owner),
|
||||
intval(ITEM_TYPE_BLOCK)
|
||||
);
|
||||
|
||||
$pages = null;
|
||||
|
||||
if($r) {
|
||||
$pages = array();
|
||||
foreach($r as $rr) {
|
||||
$element_arr = array(
|
||||
'type' => 'block',
|
||||
'title' => $rr['title'],
|
||||
'body' => $rr['body'],
|
||||
'created' => $rr['created'],
|
||||
'edited' => $rr['edited'],
|
||||
'mimetype' => $rr['mimetype'],
|
||||
'pagetitle' => $rr['v'],
|
||||
'mid' => $rr['mid']
|
||||
);
|
||||
$pages[$rr['iid']][] = array(
|
||||
'url' => $rr['iid'],
|
||||
'name' => $rr['v'],
|
||||
'title' => $rr['title'],
|
||||
'created' => $rr['created'],
|
||||
'edited' => $rr['edited'],
|
||||
'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//Build the base URL for edit links
|
||||
$url = z_root() . '/editblock/' . $which;
|
||||
|
||||
$o .= replace_macros(get_markup_template('blocklist.tpl'), array(
|
||||
'$baseurl' => $url,
|
||||
'$title' => t('Blocks'),
|
||||
'$name' => t('Block Name'),
|
||||
'$blocktitle' => t('Block Title'),
|
||||
'$created' => t('Created'),
|
||||
'$edited' => t('Edited'),
|
||||
'$create' => t('Create'),
|
||||
'$edit' => t('Edit'),
|
||||
'$share' => t('Share'),
|
||||
'$delete' => t('Delete'),
|
||||
'$editor' => $editor,
|
||||
'$pages' => $pages,
|
||||
'$channel' => $which,
|
||||
'$view' => t('View'),
|
||||
'$preview' => '1',
|
||||
));
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
}
|
||||
105
Zotlabs/Module/Bookmarks.php
Normal file
105
Zotlabs/Module/Bookmarks.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
|
||||
class Bookmarks extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
if(! local_channel())
|
||||
return;
|
||||
$item_id = intval($_REQUEST['item']);
|
||||
$burl = trim($_REQUEST['burl']);
|
||||
|
||||
if(! $item_id)
|
||||
return;
|
||||
|
||||
$u = \App::get_channel();
|
||||
|
||||
$item_normal = item_normal();
|
||||
|
||||
$i = q("select * from item where id = %d and uid = %d $item_normal limit 1",
|
||||
intval($item_id),
|
||||
intval(local_channel())
|
||||
);
|
||||
|
||||
if(! $i)
|
||||
return;
|
||||
|
||||
$i = fetch_post_tags($i);
|
||||
|
||||
$item = $i[0];
|
||||
|
||||
$terms = get_terms_oftype($item['term'],TERM_BOOKMARK);
|
||||
|
||||
if($terms) {
|
||||
require_once('include/bookmarks.php');
|
||||
|
||||
$s = q("select * from xchan where xchan_hash = '%s' limit 1",
|
||||
dbesc($item['author_xchan'])
|
||||
);
|
||||
if(! $s) {
|
||||
logger('mod_bookmarks: author lookup failed.');
|
||||
killme();
|
||||
}
|
||||
foreach($terms as $t) {
|
||||
if($burl) {
|
||||
if($burl == $t['url']) {
|
||||
bookmark_add($u,$s[0],$t,$item['item_private']);
|
||||
}
|
||||
}
|
||||
else
|
||||
bookmark_add($u,$s[0],$t,$item['item_private']);
|
||||
|
||||
info( t('Bookmark added') . EOL);
|
||||
}
|
||||
}
|
||||
killme();
|
||||
}
|
||||
|
||||
function get() {
|
||||
if(! local_channel()) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
require_once('include/menu.php');
|
||||
require_once('include/conversation.php');
|
||||
|
||||
$channel = \App::get_channel();
|
||||
|
||||
$o = profile_tabs($a,true,$channel['channel_address']);
|
||||
|
||||
$o .= '<div class="generic-content-wrapper-styled">';
|
||||
|
||||
$o .= '<h3>' . t('My Bookmarks') . '</h3>';
|
||||
|
||||
$x = menu_list(local_channel(),'',MENU_BOOKMARK);
|
||||
|
||||
if($x) {
|
||||
foreach($x as $xx) {
|
||||
$y = menu_fetch($xx['menu_name'],local_channel(),get_observer_hash());
|
||||
$o .= menu_render($y,'',true);
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '<h3>' . t('My Connections Bookmarks') . '</h3>';
|
||||
|
||||
|
||||
$x = menu_list(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK);
|
||||
|
||||
if($x) {
|
||||
foreach($x as $xx) {
|
||||
$y = menu_fetch($xx['menu_name'],local_channel(),get_observer_hash());
|
||||
$o .= menu_render($y,'',true);
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '</div>';
|
||||
|
||||
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