mirror of
https://framagit.org/hubzilla/core.git
synced 2026-06-22 01:17:41 -04:00
Compare commits
504 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
deb4c54ebf | ||
|
|
281f518705 | ||
|
|
a8d9747b12 | ||
|
|
4b15b07b8b | ||
|
|
607a5488d6 | ||
|
|
519b52ccdc | ||
|
|
99fd2e1cb9 | ||
|
|
a69c460ee6 | ||
|
|
71ba6406e3 | ||
|
|
4922acb18a | ||
|
|
a0ea19e51c | ||
|
|
8c4457db8b | ||
|
|
89a1af1794 | ||
|
|
d5e0c24e13 | ||
|
|
8fe73d73ce | ||
|
|
7559d6bb5c | ||
|
|
6c40576f94 | ||
|
|
1c9c0dc70e | ||
|
|
bf4227bfef | ||
|
|
c296d4bbed | ||
|
|
68452fb07b | ||
|
|
7f98cd606b | ||
|
|
d0ae7a0493 | ||
|
|
81e79eec04 | ||
|
|
7d70f2f0f2 | ||
|
|
016a11ad27 | ||
|
|
1637e61681 | ||
|
|
1ec0e91405 | ||
|
|
3ab52a060b | ||
|
|
866cd52073 | ||
|
|
1774140307 | ||
|
|
698dce044d | ||
|
|
bf0d73515e | ||
|
|
8152da1275 | ||
|
|
01de48b994 | ||
|
|
eb10820195 | ||
|
|
9d33456c89 | ||
|
|
56c7f76230 | ||
|
|
3dd9559d9f | ||
|
|
51ac502d97 | ||
|
|
955ee217e3 | ||
|
|
6d8bfe58ef | ||
|
|
f98f540256 | ||
|
|
54c7319075 | ||
|
|
72c930f964 | ||
|
|
ce24b86841 | ||
|
|
f63ba541d7 | ||
|
|
a9f54473db | ||
|
|
eb5ea6536b | ||
|
|
2e9b64347d | ||
|
|
4fcff43fb5 | ||
|
|
260b61ba3b | ||
|
|
d0b7e4ea79 | ||
|
|
33c95c810a | ||
|
|
81105ff9de | ||
|
|
ced3113516 | ||
|
|
cbd208eea3 | ||
|
|
ad85825cab | ||
|
|
2fb816139a | ||
|
|
c9166b26c5 | ||
|
|
86d58065b3 | ||
|
|
1f265cc6d5 | ||
|
|
4474fdd4f9 | ||
|
|
f71eeab5be | ||
|
|
b3526415f9 | ||
|
|
471ded3efa | ||
|
|
e954d8c55e | ||
|
|
70f82c3967 | ||
|
|
707e07bbbc | ||
|
|
e89eb04427 | ||
|
|
ae0e82ee3a | ||
|
|
9ea5d20a3d | ||
|
|
fd69008484 | ||
|
|
3256aa8be9 | ||
|
|
add26a5b5f | ||
|
|
dc03263bef | ||
|
|
1897cd0b1b | ||
|
|
ba24958b37 | ||
|
|
52a2a0d89a | ||
|
|
91944da69e | ||
|
|
78e30a4d32 | ||
|
|
ccd6d1a38c | ||
|
|
f14c1be963 | ||
|
|
db5e92b72d | ||
|
|
3d3580b23f | ||
|
|
04d44c9965 | ||
|
|
82bd91d9d7 | ||
|
|
492533729d | ||
|
|
283b606c09 | ||
|
|
b0b5523f2b | ||
|
|
738797467d | ||
|
|
a2ee5705f4 | ||
|
|
cc1713b69a | ||
|
|
6f5e4c5c2e | ||
|
|
3e0b9c01b6 | ||
|
|
25218fea43 | ||
|
|
c907e569f1 | ||
|
|
b6bec6f7b7 | ||
|
|
31b5cfe7ef | ||
|
|
0efb3c5c95 | ||
|
|
d88f3169c8 | ||
|
|
3772e910df | ||
|
|
e995d45b53 | ||
|
|
669136bce7 | ||
|
|
2448e6df27 | ||
|
|
4c0b37db66 | ||
|
|
85d42cebe2 | ||
|
|
fcafaef190 | ||
|
|
6fa8deddb5 | ||
|
|
0ea3f3d36d | ||
|
|
1ef7fcda0a | ||
|
|
cb7dc2059a | ||
|
|
e988bc9fae | ||
|
|
bbd60c1a31 | ||
|
|
951187e540 | ||
|
|
cb23a9e235 | ||
|
|
8d9623674d | ||
|
|
0c835c3403 | ||
|
|
6d181ee69e | ||
|
|
0b491a6626 | ||
|
|
1a0684cc55 | ||
|
|
2d97f8fa25 | ||
|
|
98a3c97820 | ||
|
|
081a61a276 | ||
|
|
89e1328ed0 | ||
|
|
3b516f2ded | ||
|
|
8c386191fa | ||
|
|
5cb4db0353 | ||
|
|
dc43cd9a85 | ||
|
|
5a6a7386a8 | ||
|
|
4f545e31dd | ||
|
|
437c0a8913 | ||
|
|
268fccdb30 | ||
|
|
bb7689be93 | ||
|
|
bd63af69b2 | ||
|
|
e5d4358d61 | ||
|
|
0fa4962620 | ||
|
|
fe4d6229a4 | ||
|
|
10c5de4f6e | ||
|
|
99a1569d07 | ||
|
|
9fb5cd12be | ||
|
|
d85c737db7 | ||
|
|
9657fc50e9 | ||
|
|
de468a29b0 | ||
|
|
794b456b8a | ||
|
|
a0cb5fcb3f | ||
|
|
0d382634ec | ||
|
|
225c83dfbe | ||
|
|
083c4c95d1 | ||
|
|
e736945f1d | ||
|
|
4a2e5add36 | ||
|
|
067a79a778 | ||
|
|
cb0102b971 | ||
|
|
8b46767d30 | ||
|
|
26b5dabe72 | ||
|
|
3f39d0d249 | ||
|
|
2b4d2baa7b | ||
|
|
3130a94a4c | ||
|
|
d44c004bd0 | ||
|
|
c655046e1f | ||
|
|
b32c1c1e22 | ||
|
|
38f040f9b5 | ||
|
|
7dcaebf281 | ||
|
|
fad5f98405 | ||
|
|
ea07bd1693 | ||
|
|
4a7b2e92a5 | ||
|
|
deaab14c5f | ||
|
|
8258b8b088 | ||
|
|
db5c217a21 | ||
|
|
724ee7dbab | ||
|
|
fb9fe0d3c4 | ||
|
|
9019636449 | ||
|
|
933b4fbcfe | ||
|
|
7675ed0145 | ||
|
|
73e7f0aad2 | ||
|
|
f25211d7ff | ||
|
|
30cf6d827c | ||
|
|
1323ea8e18 | ||
|
|
9f98f6bbd5 | ||
|
|
cedc6c4230 | ||
|
|
b6a58fbf6e | ||
|
|
e295765bef | ||
|
|
141dc2fdb7 | ||
|
|
c051c4d0aa | ||
|
|
e289078f82 | ||
|
|
87f79381d9 | ||
|
|
98840ae1d0 | ||
|
|
28746891c8 | ||
|
|
e5d0ef79ef | ||
|
|
2eb51233f6 | ||
|
|
5432819788 | ||
|
|
c03f543b54 | ||
|
|
de6506eb57 | ||
|
|
fb48bbe2c1 | ||
|
|
65132c8fdc | ||
|
|
2c0936187a | ||
|
|
48dbba2f4d | ||
|
|
16068af0bb | ||
|
|
d09b01245f | ||
|
|
bdd0b0a6fb | ||
|
|
48030617ab | ||
|
|
bb49a51be3 | ||
|
|
da266f5739 | ||
|
|
f6a4997d6f | ||
|
|
7a99089204 | ||
|
|
2e55973da2 | ||
|
|
80f4eea9e9 | ||
|
|
be5c8aa2a3 | ||
|
|
643d25b34b | ||
|
|
c29d79854e | ||
|
|
2834544451 | ||
|
|
60eb8aa42b | ||
|
|
aff39521cd | ||
|
|
f19ad4b087 | ||
|
|
fc243196d9 | ||
|
|
42e78d9666 | ||
|
|
26d1653bfb | ||
|
|
f39ccab6c1 | ||
|
|
a0e6dcbb77 | ||
|
|
320a0c1b62 | ||
|
|
84d52a9ad1 | ||
|
|
dc298ce6c1 | ||
|
|
b464cd6181 | ||
|
|
4eb7e29bab | ||
|
|
63243808b8 | ||
|
|
f271448767 | ||
|
|
3915164bb4 | ||
|
|
94e5aa172c | ||
|
|
ff68bee174 | ||
|
|
25aa7d5738 | ||
|
|
07e3a218cb | ||
|
|
e6ad00dabc | ||
|
|
23d781e348 | ||
|
|
b53cacd54c | ||
|
|
ade3da4d89 | ||
|
|
1e5f614a5e | ||
|
|
159c8dbd5f | ||
|
|
5faa285f6f | ||
|
|
c9af19cdb2 | ||
|
|
3e74e4784c | ||
|
|
bcbd7f4f6d | ||
|
|
7ead6086dc | ||
|
|
4685568e0d | ||
|
|
ec57f8293a | ||
|
|
9e5344d624 | ||
|
|
4c122fd3b3 | ||
|
|
d318a1b807 | ||
|
|
7959dd9f57 | ||
|
|
fde03c7ae0 | ||
|
|
7fcc770fbf | ||
|
|
dcb09a8b39 | ||
|
|
57c22f4d0f | ||
|
|
ed1cfa5c7b | ||
|
|
3878dbd6bd | ||
|
|
e80de634d3 | ||
|
|
536f80e50b | ||
|
|
554577fad7 | ||
|
|
3eeb173efd | ||
|
|
3ac99479fe | ||
|
|
7cadb43029 | ||
|
|
f25ac63f18 | ||
|
|
8a79d8d06f | ||
|
|
ddf7fad82f | ||
|
|
6a9d569d5a | ||
|
|
ad017baaa6 | ||
|
|
b5d673c102 | ||
|
|
d7fa707f00 | ||
|
|
b4b7ec1693 | ||
|
|
6f8b7f177d | ||
|
|
7a2a621309 | ||
|
|
fe25bab3a7 | ||
|
|
3abd764512 | ||
|
|
dc255a4ecf | ||
|
|
fc201ae067 | ||
|
|
de5455b401 | ||
|
|
1243207b8f | ||
|
|
39f4a56348 | ||
|
|
d48c9ce562 | ||
|
|
6ced694b53 | ||
|
|
bc128c604b | ||
|
|
a2168870b9 | ||
|
|
c0197b698e | ||
|
|
f2ae99f64e | ||
|
|
14f6667687 | ||
|
|
6f9348540e | ||
|
|
51b667020e | ||
|
|
c6d54d03de | ||
|
|
8e35ef2faa | ||
|
|
b74a2dff9e | ||
|
|
6400bcc81f | ||
|
|
4abe0703d7 | ||
|
|
4b389ddba9 | ||
|
|
1e92aeb7f9 | ||
|
|
b6153edf6b | ||
|
|
93c333a9bc | ||
|
|
43142dde9f | ||
|
|
63bc905061 | ||
|
|
573c6f3df3 | ||
|
|
28692b867c | ||
|
|
74b8f1f240 | ||
|
|
0be74299ca | ||
|
|
e90fc6d714 | ||
|
|
866d88de53 | ||
|
|
69acee497d | ||
|
|
6c74672d40 | ||
|
|
6320506c27 | ||
|
|
1ee0f3ce1d | ||
|
|
0730d31c22 | ||
|
|
a2a8b2e9fb | ||
|
|
8821fa9a0e | ||
|
|
363100a612 | ||
|
|
1f7ac5e787 | ||
|
|
2233a0317e | ||
|
|
498ef78e6f | ||
|
|
8a606365c8 | ||
|
|
9c27f94709 | ||
|
|
a99e067b91 | ||
|
|
831f4324ae | ||
|
|
ad205abd90 | ||
|
|
231f4a28eb | ||
|
|
95b52b6aa9 | ||
|
|
385c23a2b6 | ||
|
|
3a81edbcb0 | ||
|
|
2f377089e6 | ||
|
|
6427e84053 | ||
|
|
f1def0c03a | ||
|
|
b8a5ebe0f7 | ||
|
|
24a9ad6260 | ||
|
|
88577e1e97 | ||
|
|
38225aabc9 | ||
|
|
3653ae1af0 | ||
|
|
f2aa42f18a | ||
|
|
aa5bd9bbfc | ||
|
|
73b931ed14 | ||
|
|
42f75842c6 | ||
|
|
73fafd9f2b | ||
|
|
13c51e519d | ||
|
|
ef05cecaeb | ||
|
|
5b6d70fd60 | ||
|
|
cfb8bf3ab9 | ||
|
|
3cf7e09609 | ||
|
|
a127f76bad | ||
|
|
d11b05de71 | ||
|
|
a03b440139 | ||
|
|
2865dc45eb | ||
|
|
923e80d143 | ||
|
|
ba96b2ce85 | ||
|
|
d9f467f215 | ||
|
|
23a8c2f0a8 | ||
|
|
76856188e1 | ||
|
|
f3d8838256 | ||
|
|
2871ef2897 | ||
|
|
aa7358c837 | ||
|
|
57b553a79f | ||
|
|
f08498440a | ||
|
|
e9c07c554a | ||
|
|
a570ef4691 | ||
|
|
7cf7aa397e | ||
|
|
8ba378bc6f | ||
|
|
03af9d415f | ||
|
|
e5acf404ab | ||
|
|
2e89cfe5ed | ||
|
|
21951ec5ee | ||
|
|
10786336c5 | ||
|
|
9e33fa8ee2 | ||
|
|
1658244b09 | ||
|
|
3c60e33d74 | ||
|
|
5fb28cb51c | ||
|
|
1677c664f0 | ||
|
|
d35a79515f | ||
|
|
872fced729 | ||
|
|
4ae65a63c6 | ||
|
|
acc43bbbad | ||
|
|
947d827431 | ||
|
|
339f787b92 | ||
|
|
c71a6dbbd2 | ||
|
|
cd5815dcdc | ||
|
|
19c478421d | ||
|
|
a5f311665c | ||
|
|
d8c14eec58 | ||
|
|
4798fa79c3 | ||
|
|
b50aa43542 | ||
|
|
6bf1b15666 | ||
|
|
9d4f2c7817 | ||
|
|
040b52a1d5 | ||
|
|
c8c9e4a901 | ||
|
|
b4f6241357 | ||
|
|
4ba414fb8d | ||
|
|
33718ff16f | ||
|
|
19437f45ef | ||
|
|
7a7ff97e63 | ||
|
|
ef7cedca2d | ||
|
|
cbb2cabd74 | ||
|
|
f7cc249b50 | ||
|
|
9530b5b313 | ||
|
|
f91409a9ec | ||
|
|
4330e0bb94 | ||
|
|
6ad561dc4c | ||
|
|
70c7817a5c | ||
|
|
cc9237c7ce | ||
|
|
e75e8ed8f1 | ||
|
|
2e85852a9c | ||
|
|
c49a646bef | ||
|
|
c130c3ee45 | ||
|
|
c96fe501ef | ||
|
|
2f0f7b9df6 | ||
|
|
9fec396fa2 | ||
|
|
bf774caf0e | ||
|
|
f08b0edad2 | ||
|
|
63e41c233f | ||
|
|
48433c9479 | ||
|
|
4691bd37c9 | ||
|
|
d24a3a2713 | ||
|
|
57af09ad47 | ||
|
|
c5fc4827c8 | ||
|
|
3c89c7d4f4 | ||
|
|
2a3dc6cc50 | ||
|
|
c748d3f0f8 | ||
|
|
9b2ea44000 | ||
|
|
83f6846143 | ||
|
|
3b2311f356 | ||
|
|
ea0229dcfc | ||
|
|
3cdfa94c29 | ||
|
|
2fd5762125 | ||
|
|
52ecdb007f | ||
|
|
9b8fed738b | ||
|
|
ee3fafc9fb | ||
|
|
47867a0993 | ||
|
|
a2ba81afe1 | ||
|
|
a15254a1e2 | ||
|
|
1a68dbccf1 | ||
|
|
fc87ef282e | ||
|
|
ab0e5e3eee | ||
|
|
356b6127d3 | ||
|
|
87c62db41e | ||
|
|
0d3855cd63 | ||
|
|
ffce0a705b | ||
|
|
49e65902d7 | ||
|
|
2e1fd79c7a | ||
|
|
0c577b5fd0 | ||
|
|
3178a96a89 | ||
|
|
c1cec623bb | ||
|
|
d3cc098070 | ||
|
|
69e7812646 | ||
|
|
d39320475c | ||
|
|
0faf47b3ca | ||
|
|
660ff3da7b | ||
|
|
7ec8a2267a | ||
|
|
4a35764e13 | ||
|
|
3a50b185ce | ||
|
|
57e2904cc0 | ||
|
|
77866625cc | ||
|
|
b49c844a23 | ||
|
|
f6275fdb79 | ||
|
|
2c55ee5a73 | ||
|
|
548b0c4931 | ||
|
|
8c61f89a14 | ||
|
|
7cc0cb78a0 | ||
|
|
6aa945cde1 | ||
|
|
375b3f3f67 | ||
|
|
b68b5a60c5 | ||
|
|
f4c29d13f4 | ||
|
|
121fb74c6c | ||
|
|
4a098cbeaf | ||
|
|
6123fd2be8 | ||
|
|
176a7cb9b5 | ||
|
|
a7e734b700 | ||
|
|
b31123ae2f | ||
|
|
330c8ca890 | ||
|
|
e645ea3bf0 | ||
|
|
50f439ae8e | ||
|
|
3a102d9dc3 | ||
|
|
bb4c08caf0 | ||
|
|
f8795288a1 | ||
|
|
107b8e67d2 | ||
|
|
5a36a37b9f | ||
|
|
0910ae0e52 | ||
|
|
c6c5a8b5e0 | ||
|
|
da2c57c432 | ||
|
|
e7523e7fc5 | ||
|
|
e52d3639de | ||
|
|
1a27edf6dd | ||
|
|
9750543157 | ||
|
|
95dab5c764 | ||
|
|
619df9ca22 | ||
|
|
5442bac707 | ||
|
|
99688cdde5 | ||
|
|
6b7478c5a5 | ||
|
|
644e3b790c | ||
|
|
c064cfde91 | ||
|
|
2826d030f4 | ||
|
|
1ab1da5816 | ||
|
|
05800a5ff3 | ||
|
|
120292bd6c | ||
|
|
aaa863cda7 | ||
|
|
3c722f9aa7 | ||
|
|
14f28907bc | ||
|
|
185f53d298 | ||
|
|
3ae42d471a | ||
|
|
0a817f952e | ||
|
|
bc7f8b15a8 | ||
|
|
02111ad649 | ||
|
|
6f00964d47 | ||
|
|
eafa1d56c3 |
@@ -1,4 +1,5 @@
|
||||
stages:
|
||||
- pretest
|
||||
- test
|
||||
- deploy
|
||||
|
||||
@@ -25,23 +26,6 @@ variables:
|
||||
POSTGRES_USER: ci-user
|
||||
POSTGRES_PASSWORD: ci-pass
|
||||
|
||||
|
||||
before_script:
|
||||
# Install & enable Xdebug for code coverage reports
|
||||
- apt-get update
|
||||
- apt-get install -yqq libicu-dev libjpeg-dev libpng-dev libpq-dev libyaml-dev libzip-dev mariadb-client postgresql-client unzip zip
|
||||
- pecl install xdebug yaml
|
||||
- docker-php-ext-enable xdebug yaml
|
||||
- docker-php-ext-configure gd --with-jpeg=/usr/include/
|
||||
- docker-php-ext-install gd bcmath intl pdo_mysql pdo_pgsql zip exif
|
||||
|
||||
# Install composer
|
||||
- curl -sS https://getcomposer.org/installer | php
|
||||
# Install dev libraries from composer
|
||||
- php ./composer.phar install --no-progress
|
||||
# php.ini settings
|
||||
- echo 'xdebug.mode=coverage' >> /usr/local/etc/php/php.ini
|
||||
|
||||
# hidden job definition with template for MySQL/MariaDB
|
||||
.job_template_mysql: &job_definition_mysql
|
||||
stage: test
|
||||
@@ -53,10 +37,10 @@ before_script:
|
||||
HZ_TEST_DB_DATABASE: $MYSQL_DATABASE
|
||||
script:
|
||||
# Import hubzilla's DB schema
|
||||
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
|
||||
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" --skip-ssl "$MYSQL_DATABASE"
|
||||
# Show databases and relations/tables of hubzilla's database
|
||||
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
|
||||
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
|
||||
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" --skip-ssl "$MYSQL_DATABASE"
|
||||
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" --skip-ssl "$MYSQL_DATABASE"
|
||||
# Run the actual tests
|
||||
- touch dbfail.out
|
||||
- vendor/bin/phpunit -d memory_limit=256M --configuration tests/phpunit.xml --no-progress --stop-on-error --coverage-text --colors=never --log-junit tests/results/junit.xml || exit_code=$?
|
||||
@@ -100,29 +84,55 @@ before_script:
|
||||
paths:
|
||||
- tests/results/
|
||||
|
||||
default:
|
||||
image: php:8.2
|
||||
before_script:
|
||||
# Install & enable Xdebug for code coverage reports
|
||||
- apt-get update
|
||||
- apt-get install -yqq libicu-dev libjpeg-dev libpng-dev libpq-dev libyaml-dev libgmp-dev libzip-dev mariadb-client postgresql-client unzip zip
|
||||
- pecl install xdebug yaml
|
||||
- docker-php-ext-enable xdebug yaml
|
||||
- docker-php-ext-configure gd --with-jpeg=/usr/include/
|
||||
- docker-php-ext-install gd gmp intl pdo_mysql pdo_pgsql zip exif
|
||||
|
||||
# PHP8.1 with MySQL 8.0
|
||||
php8.1_mysql8.0.22:
|
||||
image: php:8.1
|
||||
# Install composer
|
||||
- curl -sS https://getcomposer.org/installer | php
|
||||
# Install dev libraries from composer
|
||||
- php ./composer.phar install --no-progress
|
||||
# php.ini settings
|
||||
- echo 'xdebug.mode=coverage' >> /usr/local/etc/php/php.ini
|
||||
|
||||
check_templates:
|
||||
stage: pretest
|
||||
script:
|
||||
- touch .htconfig.php
|
||||
- php util/precompile_smarty3.php
|
||||
|
||||
phpstan:
|
||||
stage: pretest
|
||||
script:
|
||||
- touch .htconfig.php
|
||||
- vendor/bin/phpstan --memory-limit=512M
|
||||
|
||||
# PHP8.2 with MySQL 8.0
|
||||
php8.2_mysql8.0.22:
|
||||
services:
|
||||
- mysql:8.0
|
||||
<<: *job_definition_mysql
|
||||
<<: *artifacts_template
|
||||
|
||||
# PHP8.1 with MariaDB 10.6
|
||||
php8.1_mariadb10.6:
|
||||
image: php:8.1
|
||||
# PHP8.2 with MariaDB 10.6
|
||||
php8.2_mariadb10.6:
|
||||
services:
|
||||
- name: mariadb:10.6
|
||||
alias: mysql
|
||||
<<: *job_definition_mysql
|
||||
<<: *artifacts_template
|
||||
|
||||
# PHP8.1 with PostgreSQL 12
|
||||
php8.1_postgres12:
|
||||
image: php:8.1
|
||||
# PHP8.2 with PostgreSQL 14
|
||||
php8.2_postgres14:
|
||||
services:
|
||||
- postgres:12-alpine
|
||||
- postgres:14-alpine
|
||||
<<: *job_definition_postgres
|
||||
<<: *artifacts_template
|
||||
|
||||
|
||||
232
CHANGELOG
232
CHANGELOG
@@ -1,3 +1,235 @@
|
||||
Hubzilla 11.2.1 (2026-05-20)
|
||||
- Fix channel creation failing in some situations
|
||||
- Drop payloads with unsafe json-ld keys in LDSignatures::verify() and add tests
|
||||
- Fix App::$profile set for removed channels
|
||||
- Fix MessageFilter breaking when expecting string but array is given
|
||||
- Superblock: fix blocking failed in some setups (sponsored by NLnet NGI0 Commons Fund/Superblock)
|
||||
- Superblock: fix missing import of attribute (sponsored by NLnet NGI0 Commons Fund/Superblock)
|
||||
- Superblock: improved detection of reshares (sponsored by NLnet NGI0 Commons Fund/Superblock)
|
||||
- Cards: fix PHP warning
|
||||
- Gallery: fix PHP warnings and only implement observer related javascript if there actually is an observer
|
||||
- Diaspora: fix missing local namespace for thr_parent
|
||||
|
||||
|
||||
Hubzilla 11.2 (2026-03-26)
|
||||
Features
|
||||
- Introduce parse_webbie() for preparing webbies and URLs for webfinger usage
|
||||
- Allow to override cUrl useragent
|
||||
- New HQ system status widget for admins (sponsored by NLnet NGI0 Commons Fund/Performance Profiling)
|
||||
- Refactor drop_query_params() to deal with array params and add test
|
||||
|
||||
Maintenance
|
||||
- Remove private members from API docs
|
||||
- Remove custom CA certs
|
||||
- Add type annotations in Extend\Route
|
||||
- Set method visibility in Extend\Route
|
||||
- Add API docs and licence info to Extend\Route
|
||||
- Add a short sleep interval to Activity::init_background_fetch() when adding new work items in a loop
|
||||
- Add a short sleep interval to the convo daemon loop to spread the load for large collections
|
||||
- Use PHP matching rules in util/run_xgettext
|
||||
- Store translation templates as .pot instead of .po
|
||||
- Deprecate NULL_DATE constant in favor of DBA::get_null_date()
|
||||
- Composer require guzzlehttp/psr7
|
||||
- Update composer libs
|
||||
- Move HQ channel notifications widget HTML to template
|
||||
- Deprecate tags and attachment in activities
|
||||
- Update the nginx config example to meet the more modern approach
|
||||
|
||||
Bugfixes
|
||||
- Fix route and widget register() not deduplicating entries
|
||||
- Fix issue in route and widget unregister() where we unregistered even if only one of the two arguments did not match
|
||||
- Fix issue in Storage/Directory where we returned a partial path instead of throwing exception if a directory of a path could not be found
|
||||
- Fix possible endless loop in externals daemon
|
||||
- Fix fatal error in italian translation file
|
||||
- Fix mod network not displaying direct messages when filters active - issue #1973
|
||||
- Fix ghost notifications with reshared items - issue #1970
|
||||
- Fix issue with double typed objects in Lib/Activity
|
||||
- Fix events displaying event timezone instead of adjusted to timezone
|
||||
- Fix duplicated terms in activity object
|
||||
- Fix last modified timestamp not updating in attach_store()
|
||||
|
||||
Addons
|
||||
- Wopi: fix headers already set warning when serving the file to the client
|
||||
- Superblock: complete rewrite with extended functionality and added tests for version 3.0 (sponsored by NLnet NGI0 Commons Fund/Superblock)
|
||||
- Diaspora: use Diaspora/2 useragent when fetching hcards to prevent being redirected to some shady bot guard
|
||||
- Add composer config and autoload files for addons
|
||||
- Wopi: return early in construct_page hook and
|
||||
- Wopi: fix wrong hook name in uninstall function
|
||||
|
||||
|
||||
Hubzilla 11.0 (2026-01-30)
|
||||
Features
|
||||
- Rewrite Lib/MessageFilter (ported from forte) and add more tests
|
||||
- Rewrite editor encryption feature to implement libsodium and PBDKF2 for password hashing
|
||||
- Change default feed behaviour to return only toplevels - issue #1953
|
||||
- Add active themes list to siteinfo
|
||||
- Show viewsource link for pubstream items
|
||||
- Implement singleton object cache
|
||||
- Restructure cross protocol message payload
|
||||
- View source will now display the raw object instead of just the object body
|
||||
- Implement OWA2
|
||||
|
||||
Maintenance
|
||||
- Remove capability to update xchan entries via api
|
||||
- Move mod search HTML to template
|
||||
- Move channel activities HTML to template
|
||||
- Update util/run_xgettext.sh to ignore not relevant directories
|
||||
- Update composer libs
|
||||
- Move mod bookmarks HTML to template
|
||||
- Update italian translation
|
||||
- Use finfo class to determine mime type in attach_store()
|
||||
- Bump attach.filetype field length to match photo.mimetype
|
||||
- Move share container HTML to template
|
||||
- Update cloud directory template to provid necessary data for the WOPI addon
|
||||
- Remove appearances of curl_close() - deprected and noop since PHP version 8.0
|
||||
- Remove appearances of GD imagedestroy() - deprected and noop since PHP version 8.0
|
||||
- Improve replies id detection
|
||||
- Remove deprecated ofeed and ochannel modules
|
||||
- Remove support for deprecated AS1 verbs and objects in the network stream filters
|
||||
- Improve detection of allday events
|
||||
- Improve handling of events with no endTime
|
||||
- Provide the event hash and timezone in the event object
|
||||
- Improve test isolation
|
||||
- Add quoteUri field to activities
|
||||
- Update API docs for hooks
|
||||
- Improve share to quote conversion
|
||||
- Rename (un)serialise() -> json_(un)serialize()
|
||||
- CI: Replace use of create_identity in MagicTest
|
||||
- CI: Add test db fixtures for hubloc table
|
||||
- CI: Stub crypto calls from CreateIdentityTest (performance)
|
||||
- CI: Add tests for Profiles module
|
||||
- CI: Add tests for Zotfinger module
|
||||
- Use json serialisation for iconfig
|
||||
- CI: Add test db fixtures for channel table
|
||||
- CI: Reload test fixtures from db
|
||||
- CI: Set logged in channel for delete account test
|
||||
- CI: Use root passwd to set up MySQL test db
|
||||
- Improve dba_pdo::insert function
|
||||
- Update install document
|
||||
- Remove unused sprintf.js
|
||||
- Move twitteroauth lib to addon_common
|
||||
- Move slinky lib to addon_common
|
||||
- Move openid lib to openid addon
|
||||
- Move XRI lib to wppost addon
|
||||
- Move diff lib to wiki addon
|
||||
- Remove unused bootbox lib
|
||||
- Remove unused images
|
||||
- Move language specific folders into its own subfolder in /view
|
||||
- Remove deprecated sjcl lib and references
|
||||
- Deprecate outbound JSalmon signatures
|
||||
|
||||
Bugfixes
|
||||
- Fix reply-to button not diplayed for anonymous visitors allthough permission is granted
|
||||
- Fix wrong icon class in mod cloud
|
||||
- Fix bulk deleting files
|
||||
- Fix cloud root folder shows unknown error
|
||||
- Fix edited timestamp in attach_store() in case of update
|
||||
- Fix grammar in Lib/Enotify::submit()
|
||||
- Fix content-type and length header in Module\TestCase
|
||||
- Fix args in xchan_fetch() not escaped
|
||||
- Fix notification for events linking to wrong message id - issue #1954
|
||||
- Fix post_mail permission not working independend from the send_stream/post_comment permissions - issue #1951
|
||||
- Fix item relayed again in case it comes back from a channel that sources our channel
|
||||
- Fix activity signer not set
|
||||
- Fix category link not update at clone - issue #1932
|
||||
- Fix tag delivery attempted item type is not post - fix issue #1941
|
||||
- Fix block/unblock account - issue #1947
|
||||
- Fix warnings in profile_edit template
|
||||
- Fix warnings for optional args in field templates
|
||||
- Fix undefined vars in mod profiles
|
||||
- Fix fullscreen button class
|
||||
- Fix delivery report when syncing cloned channels
|
||||
|
||||
Addon
|
||||
- New addon implementing basic WOPI protocol - integrates collabora with the Files app
|
||||
- Redphotos: addon removed - was used for migration from redmatrix to hubzilla
|
||||
- Redfiles: addon removed - was used for migration from redmatrix to hubzilla
|
||||
- Nsfw: rewrite to implement new MessageFilter
|
||||
- Wiki: fix long loading time due to oembed attempts
|
||||
- Wiki: improved SQL query
|
||||
|
||||
|
||||
Hubzilla 10.6.1 (2025-11-21)
|
||||
- Fix insufficient target attribution for forums
|
||||
- Fix reshare regression in forum logic
|
||||
|
||||
|
||||
Hubzilla 10.6 (2025-11-04)
|
||||
Features
|
||||
- Improved background fetching of replies collection
|
||||
- Refactor ASCache and implement ASCache::isCacheable()
|
||||
- Implement ASCache in ASCollection to improve performance
|
||||
- Add json support for help index widget and add a new template
|
||||
- Implement hidden pconfig system.notifications_count_limit
|
||||
- Implement hidden pconfig system.invert_notifications_order
|
||||
- Implement mark seen button for unseen pubstream notifications
|
||||
- Iplemented per forum unseen items notifications
|
||||
- Intoduce Activity::pasteQuote() to paste the quote into the body at the right place if applicable and add test
|
||||
- Implement ASCache in Activity::get_quote()
|
||||
- Display webfinger address instead of channel URL in HQ widget notifications tab for consistency with other tabs
|
||||
- Implement FEP-e232 object links
|
||||
|
||||
Maintenance
|
||||
- Use a better supported json canonicalization library
|
||||
- CI: up PHP images to version 8.2
|
||||
- Enable gmp PHP extension in gitlab-ci
|
||||
- Update composer libs which now require gmp PHP extension
|
||||
- HTTPSig: return early if we got no key info
|
||||
- Removed deprecated conv_list template
|
||||
- Refactor PhotoGd::imageString() to reduce complexity
|
||||
- Add ImageQuality class to hold quality values
|
||||
- CI: introduce a quick test to check that smarty templates compile in the pretest stage
|
||||
- CI: upgrade to postgres 13
|
||||
- CI: add pretest step and run PHPStan
|
||||
- Updated Spanish strings
|
||||
- Remove redundant f arg in tagcloud widget
|
||||
- Remove dead code in handle_tag()
|
||||
- Enable unit tests for extensions
|
||||
- Remove obsolete phpunit configurations
|
||||
- Code cleanup
|
||||
|
||||
Bugfixes
|
||||
- Fix encoding for webpage, layout and block title and body when editing - issue #1946
|
||||
- Fix issues which prevented files and photos to be updated correctly after rename via DAV
|
||||
- Fix contact edit modal logic hijacking URL fragment on pages other than /connections
|
||||
- Fix deprecation notice in MessagesWidgetTest
|
||||
- Fix whole URL punified in mod follow
|
||||
- Fix whole URL punified in mod search
|
||||
- Fix setup check for zip PHP extension
|
||||
- Fix issue where remote channels could post to wall if they had write_storage permission - issue #1940
|
||||
- Fix not all notification icons updated
|
||||
- Fix notification icon not updated on medium screen size
|
||||
- Fix image/gif not handled in PhotoGd::imageString()
|
||||
- Fix issue where not all seen items where removed from the unseen notifications
|
||||
- Fix item_by_item_id() not returning Announce items - issue #1936
|
||||
- Fix archive widget not returning results if in single item mode - issue #1911
|
||||
- Fix db query in verify_email_address()
|
||||
- Fix search in mod channel only returning single post items - issue #1929
|
||||
- Fix commented out register_account hook breaking notify admin addon
|
||||
|
||||
Addon
|
||||
- Superblock: add tests
|
||||
- Wiki: fix photo embed buton for markdown
|
||||
- Pubcrawl: return early if we could not find an AS actor id and add some logging
|
||||
- Cart: adapt logic to fetch tpl from theme folder first
|
||||
- Cavatar: remove redundant arg from cavatar_init()
|
||||
|
||||
|
||||
Hubzilla 10.4.4 (2025-10-06)
|
||||
- Fix issue when confirming pending registrations
|
||||
- Fix TOS headings
|
||||
- Fix TOS paths
|
||||
- Add english TermsOfService.md
|
||||
- CI: skip ssl check
|
||||
|
||||
|
||||
Hubzilla 10.4.3 (2025-08-15)
|
||||
- Refactor module vote to prohibit double votes at the sender side
|
||||
- Fix vote answers counted as comments
|
||||
- Start transition of deprecated AS1 item.verb vocabulary to AS2 on demand in mod channel, articles and cards
|
||||
- Fix regression in retrieving channel address in wtagblock() and whitespace fixes
|
||||
|
||||
|
||||
Hubzilla 10.4.2 (2025-08-08)
|
||||
- Implement item_custom_display hook in mod HQ
|
||||
- Refactor item fetching functions to reflect item_normal() changes
|
||||
|
||||
@@ -38,7 +38,6 @@ class PermissionRoles {
|
||||
];
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
$ret['limits']['post_comments'] = PERMS_AUTHED;
|
||||
$ret['limits']['post_mail'] = PERMS_AUTHED;
|
||||
$ret['limits']['post_like'] = PERMS_AUTHED;
|
||||
$ret['limits']['chat'] = PERMS_AUTHED;
|
||||
break;
|
||||
|
||||
@@ -212,6 +212,7 @@ class Permissions {
|
||||
* @return array Associative array with
|
||||
* * \e array \b perms Permission array
|
||||
* * \e int \b automatic 0 or 1
|
||||
* * \e srtring \b role
|
||||
*/
|
||||
static public function connect_perms($channel_id) {
|
||||
|
||||
@@ -230,70 +231,6 @@ class Permissions {
|
||||
}
|
||||
}
|
||||
|
||||
// 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, 'role' => $pc]);
|
||||
}
|
||||
/*
|
||||
static public function serialise($p) {
|
||||
$n = [];
|
||||
if ($p) {
|
||||
foreach ($p as $k => $v) {
|
||||
if (intval($v)) {
|
||||
$n[] = $k;
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode(',', $n);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace Zotlabs\Daemon;
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\ASCollection;
|
||||
use Zotlabs\Lib\ASCache;
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
class Convo {
|
||||
|
||||
@@ -12,52 +14,77 @@ class Convo {
|
||||
|
||||
logger('convo invoked: ' . print_r($argv, true));
|
||||
|
||||
if ($argc != 4) {
|
||||
if ($argc < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = $argv[1];
|
||||
$channel_id = intval($argv[2]);
|
||||
$contact_hash = $argv[3];
|
||||
|
||||
$channel = channelx_by_n($channel_id);
|
||||
if (!$channel) {
|
||||
$channels = explode(',', $argv[1]);
|
||||
if (!$channels) {
|
||||
return;
|
||||
}
|
||||
|
||||
$r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1",
|
||||
intval($channel_id),
|
||||
dbesc($contact_hash)
|
||||
);
|
||||
|
||||
if (!$r) {
|
||||
$observer_hash = $argv[2];
|
||||
if (!$observer_hash) {
|
||||
return;
|
||||
}
|
||||
|
||||
$contact = array_shift($r);
|
||||
|
||||
$obj = new ASCollection($id, $channel);
|
||||
|
||||
$messages = $obj->get();
|
||||
|
||||
if (!$messages) {
|
||||
$mid = $argv[3];
|
||||
if (!$mid) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($messages as $message) {
|
||||
if (is_string($message)) {
|
||||
$message = Activity::fetch($message, $channel);
|
||||
$force = $argv[4] ?? false;
|
||||
$interval = Config::Get('queueworker', 'queue_interval', 500000);
|
||||
|
||||
foreach ($channels as $channel_id) {
|
||||
$channel = channelx_by_n($channel_id);
|
||||
|
||||
$obj = new ASCollection($mid, $channel);
|
||||
|
||||
$messages = $obj->get();
|
||||
|
||||
if (!$messages) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// set client flag because comments will probably just be objects and not full blown activities
|
||||
// and that lets us use implied_create
|
||||
$AS = new ActivityStreams($message);
|
||||
if ($AS->is_valid() && is_array($AS->obj)) {
|
||||
$item = Activity::decode_note($AS);
|
||||
$item['item_fetched'] = true;
|
||||
Activity::store($channel, $contact['abook_xchan'], $AS, $item);
|
||||
foreach ($messages as $message) {
|
||||
$network_fetch = false;
|
||||
|
||||
if (is_string($message)) {
|
||||
$cached = ASCache::Get($message);
|
||||
if ($cached) {
|
||||
// logger('convo_cached: ' . $message);
|
||||
$data = $cached;
|
||||
}
|
||||
else {
|
||||
// logger('convo_fetching: ' . $message);
|
||||
$network_fetch = true;
|
||||
|
||||
$data = Activity::fetch($message, $channel);
|
||||
if ($data) {
|
||||
ASCache::Set($message, $data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
$data = $message;
|
||||
}
|
||||
|
||||
if (!$network_fetch) {
|
||||
// Add some delay so that the DB will not be overwhelmed
|
||||
// Fetched from network will already have a slight delay
|
||||
usleep($interval);
|
||||
}
|
||||
|
||||
$AS = new ActivityStreams($data);
|
||||
if ($AS->is_valid() && is_array($AS->obj)) {
|
||||
$item = Activity::decode_note($AS);
|
||||
$item['item_fetched'] = true;
|
||||
Activity::store($channel, $observer_hash, $AS, $item, false, $force);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\ObjCache;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
use Zotlabs\Lib\Libzotdir;
|
||||
|
||||
@@ -92,7 +94,7 @@ class Cron {
|
||||
// delete expired access tokens
|
||||
|
||||
$r = q("select atoken_id from atoken where atoken_expires > '%s' and atoken_expires < %s",
|
||||
dbesc(NULL_DATE),
|
||||
dbesc(DBA::$dba->get_null_date()),
|
||||
db_utcnow()
|
||||
);
|
||||
if ($r) {
|
||||
@@ -236,6 +238,73 @@ class Cron {
|
||||
if (!$restart)
|
||||
Master::Summon(array('Cronhooks'));
|
||||
|
||||
|
||||
// move as obj cache to fs
|
||||
if (!Config::Get('system', 'as_objects_moved')) {
|
||||
$results = dbq("select iconfig.*, item.mid from iconfig left join item on iid = item.id where cat = 'activitypub' and k = 'rawmsg' limit 300");
|
||||
if ($results) {
|
||||
foreach ($results as $result) {
|
||||
if (is_string($result['v'])) {
|
||||
if (str_starts_with($result['v'], '{')) {
|
||||
$result['v'] = json_decode($result['v'], true);
|
||||
}
|
||||
|
||||
elseif (str_starts_with($result['v'], 'json:')) {
|
||||
$result['v'] = json_unserialize($result['v']);
|
||||
}
|
||||
|
||||
elseif (preg_match('|^a:[0-9]+:{.*}$|s', $result['v'])) {
|
||||
$result['v'] = unserialize($result['v'], ['allowed_classes' => false]);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($result['v'])) {
|
||||
ObjCache::Set($result['mid'], $result['v']);
|
||||
}
|
||||
|
||||
q("delete from iconfig where id = %d",
|
||||
intval($result['id'])
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Config::Set('system', 'as_objects_moved', 1);
|
||||
}
|
||||
}
|
||||
|
||||
// move diaspora obj cache to fs
|
||||
if (!Config::Get('system', 'diaspora_objects_moved')) {
|
||||
$results = dbq("select iconfig.*, item.mid from iconfig left join item on iid = item.id where cat = 'diaspora' and k = 'fields' limit 300");
|
||||
if ($results) {
|
||||
foreach ($results as $result) {
|
||||
if (is_string($result['v'])) {
|
||||
if (str_starts_with($result['v'], '{')) {
|
||||
$result['v'] = json_decode($result['v'], true);
|
||||
}
|
||||
|
||||
elseif (str_starts_with($result['v'], 'json:')) {
|
||||
$result['v'] = json_unserialize($result['v']);
|
||||
}
|
||||
|
||||
elseif (preg_match('|^a:[0-9]+:{.*}$|s', $result['v'])) {
|
||||
$result['v'] = unserialize($result['v'], ['allowed_classes' => false]);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($result['v'])) {
|
||||
ObjCache::Set($result['mid'], $result['v'], 'diaspora');
|
||||
}
|
||||
|
||||
q("delete from iconfig where id = %d",
|
||||
intval($result['id'])
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Config::Set('system', 'diaspora_objects_moved', 1);
|
||||
}
|
||||
}
|
||||
|
||||
Config::Set('system', 'lastcron', datetime_convert());
|
||||
|
||||
//All done - clear the lockfile
|
||||
|
||||
@@ -74,6 +74,8 @@ class Externals {
|
||||
}
|
||||
}
|
||||
|
||||
$attempts++;
|
||||
|
||||
if (!$url) {
|
||||
continue;
|
||||
}
|
||||
@@ -85,7 +87,6 @@ class Externals {
|
||||
$blacklisted = true;
|
||||
}
|
||||
|
||||
$attempts++;
|
||||
|
||||
// make sure we can eventually break out if somebody blacklists all known sites
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ class Fetchparents {
|
||||
Activity::fetch_and_store_parents($channel, $observer_hash, $mid, null, $force);
|
||||
}
|
||||
|
||||
Activity::init_background_fetch($observer_hash);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\IConfig;
|
||||
use Zotlabs\Lib\ObjCache;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Queue;
|
||||
|
||||
@@ -264,7 +266,6 @@ class Notifier {
|
||||
}
|
||||
|
||||
if (!item_forwardable($target_item)) {
|
||||
//hz_syslog(print_r($target_item,true));
|
||||
logger('notifier: target item not forwardable', LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
@@ -317,10 +318,15 @@ class Notifier {
|
||||
return;
|
||||
}
|
||||
|
||||
$m = get_iconfig($target_item, 'activitypub', 'signed_data');
|
||||
$m = ObjCache::Get($target_item['mid']);
|
||||
|
||||
if (!$m) {
|
||||
$m = IConfig::Get($target_item, 'activitypub', 'rawmsg');
|
||||
}
|
||||
|
||||
// Re-use existing signature unless the activity type changed to a Tombstone, which won't verify.
|
||||
if ($m && (!intval($target_item['item_deleted']))) {
|
||||
self::$encoded_item = json_decode($m, true);
|
||||
self::$encoded_item = $m;
|
||||
}
|
||||
else {
|
||||
$activity = Activity::encode_activity($target_item);
|
||||
|
||||
@@ -9,10 +9,14 @@ class Onedirsync {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if ($argc < 2 || is_int($argv[1]) === false) {
|
||||
logger('onedirsync: no update id');
|
||||
return;
|
||||
}
|
||||
|
||||
logger('onedirsync: start ' . intval($argv[1]));
|
||||
|
||||
if (($argc > 1) && (intval($argv[1])))
|
||||
$update_id = intval($argv[1]);
|
||||
$update_id = intval($argv[1]);
|
||||
|
||||
if (!$update_id) {
|
||||
logger('onedirsync: no update id');
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\ASCollection;
|
||||
@@ -15,10 +16,14 @@ class Onepoll {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if ($argc < 2 || is_int($argv[1]) === false) {
|
||||
logger('onepoll: no contact');
|
||||
return;
|
||||
}
|
||||
|
||||
logger('onepoll: start');
|
||||
|
||||
if (($argc > 1) && (intval($argv[1])))
|
||||
$contact_id = intval($argv[1]);
|
||||
$contact_id = intval($argv[1]);
|
||||
|
||||
if (!$contact_id) {
|
||||
logger('onepoll: no contact');
|
||||
@@ -34,7 +39,7 @@ class Onepoll {
|
||||
$contacts = q("SELECT abook.*, xchan.* FROM abook
|
||||
LEFT JOIN xchan ON xchan_hash = abook_xchan
|
||||
WHERE abook_id = %d",
|
||||
intval($contact_id)
|
||||
$contact_id
|
||||
);
|
||||
|
||||
if (!$contacts) {
|
||||
@@ -53,7 +58,7 @@ class Onepoll {
|
||||
|
||||
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))
|
||||
$last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= DBA::$dba->get_null_date()))
|
||||
? datetime_convert('UTC', 'UTC', 'now - 7 days')
|
||||
: datetime_convert('UTC', 'UTC', $contact['abook_updated'] . ' - 2 days')
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
class Poller {
|
||||
@@ -117,7 +118,7 @@ class Poller {
|
||||
|
||||
// if we've never connected with them, start the mark for death countdown from now
|
||||
|
||||
if ($c <= NULL_DATE) {
|
||||
if ($c <= DBA::$dba->get_null_date()) {
|
||||
q("update abook set abook_connected = '%s' where abook_id = %d",
|
||||
dbesc(datetime_convert()),
|
||||
intval($contact['abook_id'])
|
||||
@@ -173,7 +174,7 @@ class Poller {
|
||||
|
||||
if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
|
||||
$r = q("SELECT * FROM updates WHERE ud_update = 1 AND (ud_last = '%s' OR ud_last > %s - INTERVAL %s)",
|
||||
dbesc(NULL_DATE),
|
||||
dbesc(DBA::$dba->get_null_date()),
|
||||
db_utcnow(),
|
||||
db_quoteinterval('7 DAY')
|
||||
);
|
||||
@@ -184,7 +185,7 @@ class Poller {
|
||||
// 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'] > DBA::$dba->get_null_date())
|
||||
if ($rr['ud_last'] > datetime_convert('UTC', 'UTC', 'now - 1 day'))
|
||||
continue;
|
||||
|
||||
|
||||
@@ -1,15 +1,57 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Hubzilla Community
|
||||
* SPDX-FileContributor: redmatrix
|
||||
* SPDX-FileContributor: Klaus Weidenbach
|
||||
* SPDX-FileContributor: zotlabs
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Extend;
|
||||
|
||||
use App;
|
||||
|
||||
/**
|
||||
* @brief Hook class.
|
||||
* A class for hooking into Hubzilla.
|
||||
*
|
||||
* Hooks are functions that Hubzilla will invoke at certain points in the code
|
||||
* during execution. An addon can register a callback handler that will be
|
||||
* called whenever the specified hook is invoked. A callback handler is a
|
||||
* function that takes a reference to an array containing the callback
|
||||
* arguments as it's only argument.
|
||||
*
|
||||
* @see call_hooks
|
||||
* @see load_hooks
|
||||
*/
|
||||
class Hook {
|
||||
|
||||
/**
|
||||
* Register a callback handler for a hook.
|
||||
*
|
||||
* A callback handler is a function that takes a reference to an array
|
||||
* containing the callback arguments as it's only argument.
|
||||
*
|
||||
* The contents and meaning of the array depends on the hook invoked. By
|
||||
* modifying the contents of the array the hook can pass data back to the
|
||||
* caller.
|
||||
*
|
||||
* Once the `Hook::register` function has been called, the callback may be
|
||||
* invoked.
|
||||
*
|
||||
* @param string $hook The name of the hook to register a handler for.
|
||||
* @param string $file The source file of the callback handler.
|
||||
* @param string|array $function
|
||||
* The function name of the callback handler, as a
|
||||
* string or an array.
|
||||
* @param int $version Hook interface version, allways 1.
|
||||
* @param int $priority The priority of the callback handler, higher
|
||||
* numbers takes precedence.
|
||||
*
|
||||
* @return true if the handler was already registered, otherwise the result
|
||||
* from inserting the hook in the database.
|
||||
*/
|
||||
static public function register($hook,$file,$function,$version = 1,$priority = 0) {
|
||||
if(is_array($function)) {
|
||||
$function = serialize($function);
|
||||
@@ -45,6 +87,14 @@ class Hook {
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an array of hook callback handlers.
|
||||
*
|
||||
* All of the handlers must be in the same source file.
|
||||
*
|
||||
* @param string $file The source file of the callback handlers.
|
||||
* @param array $arr An array of `hookname => functionname` pairs.
|
||||
*/
|
||||
static public function register_array($file,$arr) {
|
||||
if($arr) {
|
||||
foreach($arr as $k => $v) {
|
||||
@@ -54,6 +104,20 @@ class Hook {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unregister a hook callback handler.
|
||||
*
|
||||
* @param string $hook The name of the hook to register a callback handler for.
|
||||
* @param string $file The source file of the hook callback handler.
|
||||
* @param string|array $function
|
||||
* The function name of the callback handler, as a
|
||||
* string or an array.
|
||||
* @param int $version Hook interface version, allways 1.
|
||||
* @param int $priority The priority of the callback handler, higher
|
||||
* numbers takes precedence.
|
||||
*
|
||||
* @return The result of the database delete operation.
|
||||
*/
|
||||
static public function unregister($hook,$file,$function,$version = 1,$priority = 0) {
|
||||
if(is_array($function)) {
|
||||
$function = serialize($function);
|
||||
@@ -70,11 +134,13 @@ class Hook {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister all hooks with this file component.
|
||||
* Unregister all hooks handlers from a given source file.
|
||||
*
|
||||
* Useful for addon upgrades where you want to clean out old interfaces.
|
||||
*
|
||||
* @param string $file
|
||||
* @param string $file The source file where the hook handlers were defined.
|
||||
*
|
||||
* @return The result from the database delete operation.
|
||||
*/
|
||||
static public function unregister_by_file($file) {
|
||||
$r = q("DELETE FROM hook WHERE file = '%s' ",
|
||||
@@ -85,25 +151,20 @@ class Hook {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts a hook into a page request.
|
||||
* 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 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.
|
||||
* This function 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
|
||||
* @param string $hook Name of hook to attach callback.
|
||||
* @param string|array $fn Name of callback handler as a string or array.
|
||||
* @param int $version Hook interface version, allways 1.
|
||||
* @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)) {
|
||||
@@ -119,4 +180,4 @@ class Hook {
|
||||
App::$hooks[$hook][] = array('', $fn, $priority, $version);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,106 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2018 The Hubzilla Community
|
||||
* SPDX-FileContributor: Zotlabs
|
||||
* SPDX-FileContributor: Mario <mario@mariovavti.com>
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Extend;
|
||||
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
/**
|
||||
* Class for managing routes.
|
||||
*
|
||||
* Routes connect a URL path to a module that will handle requests to that
|
||||
* path.
|
||||
*
|
||||
* For example by registering a route like this:
|
||||
*
|
||||
* ```php
|
||||
* Route::register(
|
||||
* __DIR__ . '/Mod_Myroute.php',
|
||||
* 'myroute'
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* Hubzilla will direct requests to the '/myroute' URL path to the 'Myroute'
|
||||
* controller located in the '/Mod_Myroute.php' file in the same directory as
|
||||
* the file this code was called from.
|
||||
*
|
||||
* Routes are stored persistently, so this function will typically be called from
|
||||
* the `<addon>_load()` function if called from an addon. Accordingly, the route must
|
||||
* be unregistered when no longer needed, like this:
|
||||
*
|
||||
* ```php
|
||||
* Route::unregister(
|
||||
* __DIR__ . '/Mod_Myroute.php',
|
||||
* 'myroute'
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* This will typically be called from the `<addon>_unload()` function in an addon.
|
||||
*/
|
||||
class Route {
|
||||
|
||||
static function register($file,$modname) {
|
||||
/**
|
||||
* Register a new route.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* Route::register(
|
||||
* __DIR__ . '/Mod_Myroute.php',
|
||||
* 'myroute'
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* The route is stored persistently, and must be unregistered when no longer needed.
|
||||
*
|
||||
* @param string $file The file containing the controller for handling requests to this route.
|
||||
* @param string $modname The name of the module (URL path).
|
||||
*
|
||||
* @see {@link Zotlabs::Extend::Route.unregister() unregister()}
|
||||
* @see {@link Zotlabs::Extend::Route.unregister_by_file() unregister_by_file()}
|
||||
*/
|
||||
public static function register(string $file, string $modname): void {
|
||||
$rt = self::get();
|
||||
|
||||
foreach ($rt as $r) {
|
||||
if ($r[0] === $file && $r[1] === $modname) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$rt[] = [ $file, $modname ];
|
||||
self::set($rt);
|
||||
}
|
||||
|
||||
static function unregister($file,$modname) {
|
||||
/**
|
||||
* Unregister a route.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* Route::unregister(
|
||||
* __DIR__ . '/Mod_Myroute.php',
|
||||
* 'myroute'
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* @param string $file The file containing the controller for handling requests to this route.
|
||||
* @param string $modname The name of the module (URL path).
|
||||
*
|
||||
* @see {@link Zotlabs::Extend::Route.register() register()}
|
||||
* @see {@link Zotlabs::Extend::Route.unregister_by_file() unregister_by_file()}
|
||||
*/
|
||||
public static function unregister(string $file, string $modname): void {
|
||||
$rt = self::get();
|
||||
if($rt) {
|
||||
$n = [];
|
||||
foreach($rt as $r) {
|
||||
if($r[0] !== $file && $r[1] !== $modname) {
|
||||
if(!($r[0] === $file && $r[1] === $modname)) {
|
||||
$n[] = $r;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +108,23 @@ class Route {
|
||||
}
|
||||
}
|
||||
|
||||
static function unregister_by_file($file) {
|
||||
/**
|
||||
* Unregister all routes by source file.
|
||||
*
|
||||
* Removes all persistently stored routes with hanclers in the
|
||||
* given source file.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* Route::unregister_by_file(__DIR__ . '/Mod_Myroute.php');
|
||||
* ```
|
||||
*
|
||||
* @param string $file The file containing the controllers to remove.
|
||||
*
|
||||
* @see {@link Zotlabs::Extend::Route.register() register()}
|
||||
* @see {@link Zotlabs::Extend::Route.unregister() unregister()}
|
||||
*/
|
||||
public static function unregister_by_file(string $file): void {
|
||||
$rt = self::get();
|
||||
if($rt) {
|
||||
$n = [];
|
||||
@@ -38,11 +137,18 @@ class Route {
|
||||
}
|
||||
}
|
||||
|
||||
static function get() {
|
||||
/**
|
||||
* Get an array of all defined routes.
|
||||
*
|
||||
* @return An array of routes, where each entry is an array
|
||||
* containing two elements, the file, and the module
|
||||
* name.
|
||||
*/
|
||||
public static function get(): array {
|
||||
return Config::Get('system','routes',[]);
|
||||
}
|
||||
|
||||
static function set($r) {
|
||||
private static function set(array $r): mixed {
|
||||
return Config::Set('system','routes',$r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,13 @@ class Widget {
|
||||
|
||||
static function register($file,$widget) {
|
||||
$rt = self::get();
|
||||
|
||||
foreach ($rt as $r) {
|
||||
if ($r[0] === $file && $r[1] === $widget) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$rt[] = [ $file, $widget ];
|
||||
self::set($rt);
|
||||
}
|
||||
@@ -17,7 +24,7 @@ class Widget {
|
||||
if($rt) {
|
||||
$n = [];
|
||||
foreach($rt as $r) {
|
||||
if($r[0] !== $file && $r[1] !== $widget) {
|
||||
if(!($r[0] === $file && $r[1] === $widget)) {
|
||||
$n[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,27 +7,63 @@ namespace Zotlabs\Lib;
|
||||
*/
|
||||
|
||||
class ASCache {
|
||||
public static function isEnabled() {
|
||||
public static function isEnabled()
|
||||
{
|
||||
return Config::Get('system', 'as_object_cache_enabled', true);
|
||||
}
|
||||
|
||||
public static function getAge() {
|
||||
public static function getAge(): string
|
||||
{
|
||||
return Config::Get('system', 'as_object_cache_time', '10 MINUTE');
|
||||
}
|
||||
|
||||
public static function Get($key) {
|
||||
public static function Get(string $key): array
|
||||
{
|
||||
if (!self::isEnabled()) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
return Cache::get($key, self::getAge());
|
||||
$ret = Cache::get($key, self::getAge());
|
||||
|
||||
if ($ret) {
|
||||
return json_unserialize($ret);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public static function Set($key, $value) {
|
||||
public static function Set(string $key, array $obj): void
|
||||
{
|
||||
if (!self::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Cache::set($key, $value);
|
||||
if (!self::isCacheable($obj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Cache::set($key, json_serialize($obj));
|
||||
}
|
||||
|
||||
public static function isCacheable(array $obj): bool
|
||||
{
|
||||
$to = [];
|
||||
$cc = [];
|
||||
|
||||
if (isset($obj['to'])) {
|
||||
$to = is_array($obj['to']) ? $obj['to'] : [$obj['to']];
|
||||
}
|
||||
|
||||
if (isset($obj['cc'])) {
|
||||
$cc = is_array($obj['cc']) ? $obj['cc'] : [$obj['cc']];
|
||||
}
|
||||
|
||||
$receivers = array_merge($to, $cc);
|
||||
|
||||
if ($receivers && !in_array(ACTIVITY_PUBLIC_INBOX, $receivers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,19 @@ class ASCollection {
|
||||
}
|
||||
|
||||
if (is_string($obj)) {
|
||||
$data = Activity::fetch($obj, $channel);
|
||||
$cached = ASCache::Get($obj);
|
||||
if ($cached) {
|
||||
// logger('cached: ' . $obj);
|
||||
$data = $cached;
|
||||
}
|
||||
else {
|
||||
// logger('fetching: ' . $obj);
|
||||
$data = Activity::fetch($obj, $channel);
|
||||
if ($data) {
|
||||
ASCache::Set($obj, $data);
|
||||
}
|
||||
}
|
||||
|
||||
$this->history[] = $obj;
|
||||
}
|
||||
|
||||
@@ -83,6 +95,8 @@ class ASCollection {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = null;
|
||||
|
||||
if (is_array($this->nextpage)) {
|
||||
$data = $this->nextpage;
|
||||
}
|
||||
@@ -92,7 +106,20 @@ class ASCollection {
|
||||
// recursion detected
|
||||
return false;
|
||||
}
|
||||
$data = Activity::fetch($this->nextpage, $this->channel);
|
||||
|
||||
$cached = ASCache::Get($this->nextpage);
|
||||
if ($cached) {
|
||||
// logger('cached: ' . $this->nextpage);
|
||||
$data = $cached;
|
||||
}
|
||||
else {
|
||||
$data = Activity::fetch($this->nextpage, $this->channel);
|
||||
if ($data) {
|
||||
// logger('fetching: ' . $this->nextpage);
|
||||
ASCache::Set($this->nextpage, $data);
|
||||
}
|
||||
}
|
||||
|
||||
$this->history[] = $this->nextpage;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Access\PermissionLimits;
|
||||
use Zotlabs\Access\PermissionRoles;
|
||||
use Zotlabs\Access\Permissions;
|
||||
@@ -91,6 +92,8 @@ class Activity {
|
||||
|
||||
logger('fetch: ' . $url, LOGGER_DEBUG);
|
||||
|
||||
$start_timestamp = microtime(true);
|
||||
|
||||
if (strpos($url, 'x-zot:') === 0) {
|
||||
$x = ZotURL::fetch($url, $channel);
|
||||
}
|
||||
@@ -129,7 +132,6 @@ class Activity {
|
||||
}
|
||||
|
||||
$h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), false);
|
||||
$start_timestamp = microtime(true);
|
||||
$x = z_fetch_url($url, true, $redirects, ['headers' => $h]);
|
||||
}
|
||||
|
||||
@@ -382,7 +384,12 @@ class Activity {
|
||||
if ($items) {
|
||||
$x = [];
|
||||
foreach ($items as $i) {
|
||||
$m = IConfig::Get($i['id'], 'activitypub', 'rawmsg');
|
||||
$m = ObjCache::Get($i['mid']);
|
||||
|
||||
if (!$m) {
|
||||
$m = IConfig::Get($i['id'], 'activitypub', 'rawmsg');
|
||||
}
|
||||
|
||||
if ($m) {
|
||||
if (is_string($m))
|
||||
$t = json_decode($m, true);
|
||||
@@ -501,8 +508,8 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
$ret['id'] = ((strpos($i['mid'], 'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid']));
|
||||
$ret['diaspora:guid'] = $i['uuid'];
|
||||
$ret['id'] = ((strpos($i['mid'], 'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid']));
|
||||
$ret['uuid'] = $i['uuid'];
|
||||
|
||||
$images = [];
|
||||
$audios = [];
|
||||
@@ -562,7 +569,7 @@ class Activity {
|
||||
$ret['published'] = datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME);
|
||||
if ($i['created'] !== $i['edited'])
|
||||
$ret['updated'] = datetime_convert('UTC', 'UTC', $i['edited'], ATOM_TIME);
|
||||
if ($i['expires'] > NULL_DATE) {
|
||||
if ($i['expires'] > DBA::$dba->get_null_date()) {
|
||||
$ret['expires'] = datetime_convert('UTC', 'UTC', $i['expires'], ATOM_TIME);
|
||||
}
|
||||
|
||||
@@ -587,7 +594,7 @@ class Activity {
|
||||
|
||||
$ret['commentPolicy'] = (($i['item_wall']) ? map_scope(PermissionLimits::Get($i['uid'], 'post_comments')) : '');
|
||||
|
||||
if (array_key_exists('comments_closed', $i) && $i['comments_closed'] !== EMPTY_STR && $i['comments_closed'] > NULL_DATE) {
|
||||
if (array_key_exists('comments_closed', $i) && $i['comments_closed'] !== EMPTY_STR && $i['comments_closed'] > DBA::$dba->get_null_date()) {
|
||||
if ($ret['commentPolicy']) {
|
||||
$ret['commentPolicy'] .= ' ';
|
||||
}
|
||||
@@ -620,15 +627,6 @@ class Activity {
|
||||
$ret['context'] = $cnv;
|
||||
}
|
||||
|
||||
if ($i['mimetype'] === 'text/bbcode') {
|
||||
if ($i['title'])
|
||||
$ret['name'] = unescape_tags($i['title']);
|
||||
if ($i['summary'])
|
||||
$ret['summary'] = unescape_tags($i['summary']);
|
||||
$ret['content'] = bbcode(unescape_tags($i['body']), ['cache' => true]);
|
||||
$ret['source'] = ['content' => unescape_tags($i['body']), 'mediaType' => 'text/bbcode'];
|
||||
}
|
||||
|
||||
$actor = self::encode_person($i['author'], false);
|
||||
if ($actor)
|
||||
$ret['actor'] = $actor;
|
||||
@@ -646,6 +644,57 @@ class Activity {
|
||||
$ret['tag'] = $t;
|
||||
}
|
||||
|
||||
// TODO: Do not replace the if the owner is a forum.
|
||||
// Receivers will not be able to fetch the original in that case.
|
||||
if (str_contains($i['body'], '[/share]')) {
|
||||
preg_match_all('/\[share(.*?)\](.*?)\[\/share\]/ism', $i['body'], $all_shares, PREG_SET_ORDER);
|
||||
|
||||
$quote_urls = [];
|
||||
$obj_links = [];
|
||||
|
||||
foreach ($all_shares as $share) {
|
||||
// Extract the link attribute from each [share] block if slated for quote
|
||||
if (str_contains($share[1], "quote='true'") && preg_match("/link='(.*?)'/ism", $share[1], $match)) {
|
||||
$url = $match[1];
|
||||
$quote_urls[] = $url;
|
||||
|
||||
$quote_name = 'RE: ' . $url;
|
||||
|
||||
// Replace this share block with a formatted URL reference
|
||||
$i['body'] = str_replace($share[0], $quote_name, $i['body']);
|
||||
|
||||
$obj_links[] = [
|
||||
'type' => 'Link',
|
||||
'mediaType' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
||||
'href' => $url,
|
||||
'name' => $quote_name
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($quote_urls) {
|
||||
$ret['quoteUrl'] = $quote_urls[0];
|
||||
$ret['quoteUri'] = $quote_urls[0];
|
||||
|
||||
if (empty($ret['tag'])) {
|
||||
$ret['tag'] = $obj_links;
|
||||
}
|
||||
else {
|
||||
$ret['tag'] = array_merge($ret['tag'], $obj_links);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($i['mimetype'] === 'text/bbcode') {
|
||||
if ($i['title'])
|
||||
$ret['name'] = unescape_tags($i['title']);
|
||||
if ($i['summary'])
|
||||
$ret['summary'] = unescape_tags($i['summary']);
|
||||
$ret['content'] = bbcode(unescape_tags($i['body']));
|
||||
$ret['source'] = ['content' => unescape_tags($i['body']), 'mediaType' => 'text/bbcode'];
|
||||
}
|
||||
|
||||
$a = self::encode_attachment($i);
|
||||
if ($a) {
|
||||
$ret['attachment'] = $a;
|
||||
@@ -789,8 +838,7 @@ class Activity {
|
||||
if ($iconfig && array_key_exists('iconfig', $item) && is_array($item['iconfig'])) {
|
||||
foreach ($item['iconfig'] as $att) {
|
||||
if ($att['sharing']) {
|
||||
$value = ((is_string($att['v']) && preg_match('|^a:[0-9]+:{.*}$|s', $att['v'])) ? unserialize($att['v']) : $att['v']);
|
||||
$ret[] = ['type' => 'PropertyValue', 'name' => 'zot.' . $att['cat'] . '.' . $att['k'], 'value' => $value];
|
||||
$ret[] = ['type' => 'PropertyValue', 'name' => 'zot.' . $att['cat'] . '.' . $att['k'], 'value' => $att['v']];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -811,6 +859,10 @@ class Activity {
|
||||
$entry = [];
|
||||
if (isset($att['type']) && $att['type'] === 'PropertyValue') {
|
||||
if (isset($att['name'])) {
|
||||
if (in_array($att['name'], ['zot.activitypub.rawmsg', 'zot.diaspora.fields'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = explode('.', $att['name']);
|
||||
if (count($key) === 3 && $key[0] === 'zot') {
|
||||
$entry['cat'] = $key[1];
|
||||
@@ -957,7 +1009,7 @@ class Activity {
|
||||
$ret['id'] = ((strpos($i['mid'], 'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid']));
|
||||
}
|
||||
|
||||
$ret['diaspora:guid'] = $i['uuid'];
|
||||
$ret['uuid'] = $i['uuid'];
|
||||
|
||||
if (!empty($i['title']))
|
||||
$ret['name'] = html2plain(bbcode($i['title']));
|
||||
@@ -1074,6 +1126,7 @@ class Activity {
|
||||
return [];
|
||||
}
|
||||
|
||||
/* Those should not be required in activities anymore after version 11
|
||||
$t = self::encode_taxonomy($i);
|
||||
if ($t) {
|
||||
$ret['tag'] = $t;
|
||||
@@ -1083,6 +1136,7 @@ class Activity {
|
||||
if ($a) {
|
||||
$ret['attachment'] = $a;
|
||||
}
|
||||
*/
|
||||
|
||||
if (intval($i['item_private']) === 0) {
|
||||
$ret['to'] = [ACTIVITY_PUBLIC_INBOX];
|
||||
@@ -1511,7 +1565,7 @@ class Activity {
|
||||
'abook_created' => datetime_convert(),
|
||||
'abook_updated' => datetime_convert(),
|
||||
'abook_connected' => datetime_convert(),
|
||||
'abook_dob' => NULL_DATE,
|
||||
'abook_dob' => DBA::$dba->get_null_date(),
|
||||
'abook_pending' => intval(($automatic) ? 0 : 1),
|
||||
'abook_instance' => z_root()
|
||||
]
|
||||
@@ -1963,6 +2017,8 @@ class Activity {
|
||||
$multi = true;
|
||||
}
|
||||
|
||||
$answer_found = false;
|
||||
|
||||
if ($response) {
|
||||
$mid = $response['mid'];
|
||||
$content = trim($response['title']);
|
||||
@@ -1992,7 +2048,6 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
$answer_found = false;
|
||||
$foundPrevious = false;
|
||||
if ($multi) {
|
||||
for ($c = 0; $c < count($o['anyOf']); $c++) {
|
||||
@@ -2034,7 +2089,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($pollItem['comments_closed'] > NULL_DATE) {
|
||||
if ($pollItem['comments_closed'] > DBA::$dba->get_null_date()) {
|
||||
if ($pollItem['comments_closed'] > datetime_convert()) {
|
||||
$o['closed'] = datetime_convert('UTC', 'UTC', $pollItem['comments_closed'], ATOM_TIME);
|
||||
// set this to force an update
|
||||
@@ -2086,15 +2141,16 @@ class Activity {
|
||||
}
|
||||
|
||||
static function decode_note($act) {
|
||||
|
||||
$response_activity = false;
|
||||
$s = [];
|
||||
|
||||
$obj_type = is_array($act->objprop('type')) ? $act->objprop('type')[0] : $act->objprop('type');
|
||||
|
||||
// These activities should have been handled separately in the Inbox module and should not be turned into posts
|
||||
|
||||
if (
|
||||
in_array($act->type, ['Follow', 'Accept', 'Reject', 'Create', 'Update']) &&
|
||||
($act->objprop('type') === 'Follow' || ActivityStreams::is_an_actor($act->objprop('type')))
|
||||
($obj_type === 'Follow' || ActivityStreams::is_an_actor($obj_type))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -2167,7 +2223,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($act->type, ['Invite', 'Create']) && $act->objprop('type') === 'Event') {
|
||||
if (in_array($act->type, ['Invite', 'Create']) && $obj_type === 'Event') {
|
||||
$s['mid'] = $s['parent_mid'] = $act->id;
|
||||
}
|
||||
|
||||
@@ -2199,25 +2255,25 @@ class Activity {
|
||||
$mention = self::get_actor_bbmention($obj_actor['id']);
|
||||
|
||||
if ($act->type === 'Like') {
|
||||
$content['content'] = sprintf(t('Likes %1$s\'s %2$s'), $mention, $act->obj['type']) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('Likes %1$s\'s %2$s'), $mention, $act->obj['type']);
|
||||
}
|
||||
if ($act->type === 'Dislike') {
|
||||
$content['content'] = sprintf(t('Doesn\'t like %1$s\'s %2$s'), $mention, $act->obj['type']) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('Doesn\'t like %1$s\'s %2$s'), $mention, $act->obj['type']);
|
||||
}
|
||||
|
||||
// handle event RSVPs
|
||||
if (($act->objprop('type') === 'Event') || ($act->objprop('type') === 'Invite' && array_path_exists('object/type', $act->obj) && $act->obj['object']['type'] === 'Event')) {
|
||||
if (in_array($obj_type, ['Event', 'Invite'])) {
|
||||
if ($act->type === 'Accept') {
|
||||
$content['content'] = sprintf(t('Will attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('Will attend %s\'s event'), $mention);
|
||||
}
|
||||
if ($act->type === 'Reject') {
|
||||
$content['content'] = sprintf(t('Will not attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('Will not attend %s\'s event'), $mention);
|
||||
}
|
||||
if ($act->type === 'TentativeAccept') {
|
||||
$content['content'] = sprintf(t('May attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('May attend %s\'s event'), $mention);
|
||||
}
|
||||
if ($act->type === 'TentativeReject') {
|
||||
$content['content'] = sprintf(t('May not attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('May not attend %s\'s event'), $mention);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2251,7 +2307,7 @@ class Activity {
|
||||
if ($s['mid'] === $s['parent_mid']) {
|
||||
$s['item_thread_top'] = 1;
|
||||
$s['item_nocomment'] = 0;
|
||||
$s['comments_closed'] = NULL_DATE;
|
||||
$s['comments_closed'] = DBA::$dba->get_null_date();
|
||||
|
||||
// it is a parent node - decode the comment policy info if present
|
||||
if ($act->objprop('commentPolicy')) {
|
||||
@@ -2285,20 +2341,49 @@ class Activity {
|
||||
$s['body'] = markdown_to_bb($act->objprop('content'));
|
||||
}
|
||||
|
||||
if ($act->objprop('quoteUrl')) {
|
||||
$quote_bbcode = self::get_quote_bbcode($act->obj['quoteUrl']);
|
||||
$quote_urls = [];
|
||||
|
||||
if ($s['body']) {
|
||||
$s['body'] .= "\r\n\r\n";
|
||||
if (isset($act->obj['tag'])) {
|
||||
foreach($act->obj['tag'] as $t) {
|
||||
if (is_array($t) && $t['type'] === 'Link' && $t['mediaType'] === 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"') {
|
||||
$quote_urls[] = $t['href'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$s['body'] .= $quote_bbcode;
|
||||
if (!$quote_urls) {
|
||||
$quote_url = $act->obj['quoteUrl'] ?? $act->obj['quoteUri'] ?? $act->obj['_misskey_quote'] ?? $act->obj['quote'] ?? null;
|
||||
|
||||
if ($quote_url) {
|
||||
$quote_urls = [$quote_url];
|
||||
}
|
||||
}
|
||||
|
||||
// Backwards compatibility: only process quote items if there is no share tag in them.
|
||||
// Otherwise they will appear doubled.
|
||||
if ($quote_urls && !str_contains($s['body'], '[/share]')) {
|
||||
foreach($quote_urls as $quote_url) {
|
||||
if (!is_string($quote_url)) {
|
||||
// FIXME: requires investigation
|
||||
logger('Not a string: ' . print_r($quote_url,true));
|
||||
continue;
|
||||
}
|
||||
|
||||
$quote = self::get_quote($quote_url);
|
||||
|
||||
if (!$quote) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$s['body'] = self::pasteQuote($s['body'] ?? EMPTY_STR, $quote);
|
||||
$s['term'] = $quote['term'];
|
||||
}
|
||||
}
|
||||
|
||||
$s['verb'] = self::activity_mapper($act->type);
|
||||
|
||||
// Mastodon does not provide update timestamps when updating poll tallies which means race conditions may occur here.
|
||||
if ($act->type === 'Update' && $act->objprop('type') === 'Question' && $s['edited'] === $s['created']) {
|
||||
if ($act->type === 'Update' && $obj_type === 'Question' && $s['edited'] === $s['created']) {
|
||||
$s['edited'] = datetime_convert();
|
||||
}
|
||||
|
||||
@@ -2306,8 +2391,8 @@ class Activity {
|
||||
$s['item_deleted'] = 1;
|
||||
}
|
||||
|
||||
if ($act->objprop('type')) {
|
||||
$s['obj_type'] = self::activity_obj_mapper($act->obj['type']);
|
||||
if ($obj_type) {
|
||||
$s['obj_type'] = self::activity_obj_mapper($obj_type);
|
||||
}
|
||||
|
||||
$s['obj'] = $act->obj;
|
||||
@@ -2340,7 +2425,13 @@ class Activity {
|
||||
$a = self::decode_taxonomy($act->obj);
|
||||
|
||||
if ($a) {
|
||||
$s['term'] = $a;
|
||||
if (isset($s['term'])) {
|
||||
// term might contain content from a quote post
|
||||
$s['term'] = array_merge($s['term'], $a);
|
||||
}
|
||||
else {
|
||||
$s['term'] = $a;
|
||||
}
|
||||
}
|
||||
|
||||
$a = self::decode_attachment($act->obj);
|
||||
@@ -2362,7 +2453,7 @@ class Activity {
|
||||
$s = self::bb_attach($s);
|
||||
}
|
||||
|
||||
if ($act->objprop('type') === 'Question' && in_array($act->type, ['Create', 'Update'])) {
|
||||
if ($obj_type === 'Question' && in_array($act->type, ['Create', 'Update'])) {
|
||||
if ($act->objprop('endTime')) {
|
||||
$s['comments_closed'] = datetime_convert('UTC', 'UTC', $act->obj['endTime']);
|
||||
}
|
||||
@@ -2374,7 +2465,7 @@ class Activity {
|
||||
|
||||
if (!$response_activity) {
|
||||
|
||||
if ($act->objprop('type') === 'Profile') {
|
||||
if ($obj_type === 'Profile') {
|
||||
$s['parent_mid'] = $s['mid'];
|
||||
$s['item_thread_top'] = 1;
|
||||
}
|
||||
@@ -2384,7 +2475,7 @@ class Activity {
|
||||
// right now just link to the largest mp4 we find that will fit in our
|
||||
// standard content region
|
||||
|
||||
if ($act->objprop('type') === 'Video') {
|
||||
if ($obj_type === 'Video') {
|
||||
|
||||
$vtypes = [
|
||||
'video/mp4',
|
||||
@@ -2466,7 +2557,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if ($act->objprop('type') === 'Audio') {
|
||||
if ($obj_type === 'Audio') {
|
||||
|
||||
$atypes = [
|
||||
'audio/mpeg',
|
||||
@@ -2498,7 +2589,7 @@ class Activity {
|
||||
|
||||
}
|
||||
|
||||
if ($act->objprop('type') === 'Image' && strpos($s['body'], 'zrl=') === false) {
|
||||
if ($obj_type === 'Image' && strpos($s['body'], 'zrl=') === false) {
|
||||
|
||||
$ptr = null;
|
||||
|
||||
@@ -2513,10 +2604,10 @@ class Activity {
|
||||
foreach ($ptr as $vurl) {
|
||||
if (strpos($s['body'], $vurl['href']) === false) {
|
||||
$bb_imgs = '[zmg]' . $vurl['href'] . '[/zmg]' . "\r\n";
|
||||
$s['body'] = $bb_imgs . $s['body'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$s['body'] = $bb_imgs . $s['body'];
|
||||
}
|
||||
elseif (is_string($act->obj['url'])) {
|
||||
if (strpos($s['body'], $act->obj['url']) === false) {
|
||||
@@ -2526,7 +2617,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if ($act->objprop('type') === 'Page' && !$s['body']) {
|
||||
if ($obj_type === 'Page' && !$s['body']) {
|
||||
|
||||
$ptr = null;
|
||||
$purl = EMPTY_STR;
|
||||
@@ -2566,7 +2657,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($act->objprop('type'), ['Note', 'Article', 'Page'])) {
|
||||
if (in_array($obj_type, ['Note', 'Article', 'Page', 'Question'])) {
|
||||
$ptr = null;
|
||||
|
||||
if (array_key_exists('url', $act->obj)) {
|
||||
@@ -2605,55 +2696,11 @@ class Activity {
|
||||
$s['item_private'] = 2;
|
||||
}
|
||||
|
||||
$ap_rawmsg = '';
|
||||
$diaspora_rawmsg = '';
|
||||
$raw_arr = [];
|
||||
|
||||
$raw_arr = json_decode($act->raw, true);
|
||||
|
||||
// This is a zot6 packet and the raw activitypub or diaspora message json
|
||||
// is possibly available in the attachement.
|
||||
if (array_key_exists('signed', $raw_arr) && isset($act->data['attachment']) && is_array($act->data['attachment'])) {
|
||||
foreach($act->data['attachment'] as $a) {
|
||||
if (
|
||||
isset($a['type']) && $a['type'] === 'PropertyValue' &&
|
||||
isset($a['name']) && $a['name'] === 'zot.activitypub.rawmsg' &&
|
||||
isset($a['value'])
|
||||
) {
|
||||
$ap_rawmsg = $a['value'];
|
||||
}
|
||||
if (
|
||||
isset($a['type']) && $a['type'] === 'PropertyValue' &&
|
||||
isset($a['name']) && $a['name'] === 'zot.diaspora.fields' &&
|
||||
isset($a['value'])
|
||||
) {
|
||||
$diaspora_rawmsg = $a['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!$ap_rawmsg && array_key_exists('signed', $raw_arr)) {
|
||||
// zap
|
||||
$ap_rawmsg = json_encode($act->data, JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
if ($ap_rawmsg) {
|
||||
IConfig::Set($s, 'activitypub', 'rawmsg', $ap_rawmsg, 1);
|
||||
}
|
||||
elseif (!array_key_exists('signed', $raw_arr)) {
|
||||
IConfig::Set($s, 'activitypub', 'rawmsg', $act->raw, 1);
|
||||
}
|
||||
|
||||
if ($diaspora_rawmsg) {
|
||||
IConfig::Set($s, 'diaspora', 'fields', $diaspora_rawmsg, 1);
|
||||
}
|
||||
|
||||
if ($act->raw_recips) {
|
||||
IConfig::Set($s, 'activitypub', 'recips', $act->raw_recips);
|
||||
}
|
||||
|
||||
if ($act->objprop('type') === 'Event' && $act->objprop('timezone')) {
|
||||
if ($obj_type === 'Event' && $act->objprop('timezone')) {
|
||||
IConfig::Set($s, 'event', 'timezone', $act->objprop('timezone'), true);
|
||||
}
|
||||
|
||||
@@ -2666,8 +2713,9 @@ class Activity {
|
||||
return $hookinfo['s'];
|
||||
|
||||
}
|
||||
|
||||
static function store($channel, $observer_hash, $act, $item, $fetch_parents = true, $force = false, $is_collection_operation = false) {
|
||||
$is_sys_channel = is_sys_channel($channel['channel_id']);
|
||||
$is_sys_channel = $channel['channel_system'];
|
||||
$is_child_node = false;
|
||||
$parent = null;
|
||||
|
||||
@@ -2714,7 +2762,9 @@ class Activity {
|
||||
$force = true;
|
||||
}
|
||||
|
||||
if ($fetch_parents) {
|
||||
$attempt_parents_fetch = $fetch_parents && !in_array($channel['channel_id'], App::$cache['as_fetch_objects'][$item['mid']]['channels'] ?? []);
|
||||
|
||||
if ($attempt_parents_fetch) {
|
||||
App::$cache['as_fetch_objects'][$item['mid']]['channels'][] = $channel['channel_id'];
|
||||
App::$cache['as_fetch_objects'][$item['mid']]['force'] = intval($force);
|
||||
return;
|
||||
@@ -2816,15 +2866,11 @@ class Activity {
|
||||
}
|
||||
|
||||
if (tgroup_check($channel['channel_id'], $item) && (!$is_child_node)) {
|
||||
// for forum deliveries, make sure we keep a copy of the signed original
|
||||
IConfig::Set($item, 'activitypub', 'rawmsg', $act->raw, 1);
|
||||
$allowed = true;
|
||||
}
|
||||
|
||||
if (intval($item['item_private']) === 2) {
|
||||
if (perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail')) {
|
||||
$allowed = true;
|
||||
}
|
||||
$allowed = perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail');
|
||||
}
|
||||
|
||||
if ($is_sys_channel) {
|
||||
@@ -2890,13 +2936,19 @@ class Activity {
|
||||
if (!$item['author_xchan'] || !$item['owner_xchan'])
|
||||
return;
|
||||
|
||||
if ($channel['channel_system']) {
|
||||
$incl = Config::Get('system','pubstream_incl');
|
||||
$excl = Config::Get('system','pubstream_excl');
|
||||
if ($is_sys_channel) {
|
||||
$incl = Config::Get('system', 'pubstream_incl', '');
|
||||
$excl = Config::Get('system', 'pubstream_excl', '');
|
||||
|
||||
if(($incl || $excl) && !MessageFilter::evaluate($item, $incl, $excl)) {
|
||||
logger('post is filtered');
|
||||
return;
|
||||
if ($incl || $excl) {
|
||||
$plaintext = prepare_text($item['body'], ((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
|
||||
$plaintext = html2plain((isset($item['summary']) && $item['summary']) ? $item['summary'] . ' ' . $plaintext : $plaintext);
|
||||
$plaintext = html2plain((isset($item['title']) && $item['title']) ? $item['title'] . ' ' . $plaintext : $plaintext);
|
||||
|
||||
if (!(new MessageFilter($item, html_entity_decode($incl), html_entity_decode($excl), ['plaintext' => $plaintext]))->evaluate()) {
|
||||
logger('post is filtered');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2995,13 +3047,22 @@ class Activity {
|
||||
// TODO: not implemented
|
||||
// self::rewrite_mentions($item);
|
||||
|
||||
$r = q("select id, created, edited from item where mid = '%s' and uid = %d limit 1",
|
||||
if (!ObjCache::Get($item['mid'])) {
|
||||
ObjCache::Set($item['mid'], $act->data);
|
||||
}
|
||||
|
||||
$r = q("select id, created, edited, owner_xchan, author_xchan from item where mid = '%s' and uid = %d limit 1",
|
||||
dbesc($item['mid']),
|
||||
intval($item['uid'])
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
if ($item['edited'] > $r[0]['edited']) {
|
||||
// Only update the object cache if there is no owner/author mismatch.
|
||||
if ($r[0]['owner_xchan'] === $item['owner_xchan'] && $r[0]['author_xchan'] === $item['author_xchan']) {
|
||||
ObjCache::Set($item['mid'], $act->data);
|
||||
}
|
||||
|
||||
$item['id'] = $r[0]['id'];
|
||||
$x = item_store_update($item, deliver: false);
|
||||
}
|
||||
@@ -3035,30 +3096,21 @@ class Activity {
|
||||
send_status_notifications($x['item_id'], $x['item']);
|
||||
|
||||
sync_an_item($channel['channel_id'], $x['item_id']);
|
||||
}
|
||||
|
||||
if ($fetch_parents && $parent && !intval($parent[0]['item_private'])) {
|
||||
logger('topfetch', LOGGER_DEBUG);
|
||||
// if the thread owner is a connnection, we will already receive any additional comments to their posts
|
||||
// but if they are not we can try to fetch others in the background
|
||||
$connected = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1",
|
||||
intval($channel['channel_id']),
|
||||
dbesc($parent[0]['owner_xchan'])
|
||||
);
|
||||
if (!$connected) {
|
||||
// determine if the top-level post provides a replies collection
|
||||
if ($parent[0]['obj']) {
|
||||
$parent[0]['obj'] = json_decode($parent[0]['obj'], true);
|
||||
}
|
||||
logger('topfetch: ' . print_r($parent[0], true), LOGGER_ALL);
|
||||
$id = ((array_path_exists('obj/replies/id', $parent[0])) ? $parent[0]['obj']['replies']['id'] : false);
|
||||
if (!$id) {
|
||||
$id = ((array_path_exists('obj/replies', $parent[0]) && is_string($parent[0]['obj']['replies'])) ? $parent[0]['obj']['replies'] : false);
|
||||
}
|
||||
if ($id) {
|
||||
Master::Summon(['Convo', $id, $channel['channel_id'], $observer_hash]);
|
||||
}
|
||||
$replies_id = null;
|
||||
|
||||
if (isset($act->obj['replies'])) {
|
||||
$replies_id = is_array($act->obj['replies']) ? $act->obj['replies']['id'] : $act->obj['replies'];
|
||||
}
|
||||
|
||||
// Only store replies collection for background fetching if the item has been fetched.
|
||||
// A message that has just been posted usually will not have any replies yet.
|
||||
// Also dismiss duplicates.
|
||||
|
||||
$attempt_replies_fetch = $replies_id && !empty($item['item_fetched']) && !in_array($channel['channel_id'], App::$cache['as_fetch_collection'][$replies_id]['channels'] ?? []);
|
||||
if ($attempt_replies_fetch) {
|
||||
App::$cache['as_fetch_collection'][$replies_id]['channels'][] = $channel['channel_id'];
|
||||
App::$cache['as_fetch_collection'][$replies_id]['force'] = intval($force);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3107,7 +3159,7 @@ class Activity {
|
||||
$cached = ASCache::Get($current_item['parent_mid']);
|
||||
if ($cached) {
|
||||
// logger('cached: ' . $current_item['parent_mid']);
|
||||
$n = unserialise($cached);
|
||||
$n = $cached;
|
||||
}
|
||||
else {
|
||||
// logger('fetching: ' . $current_item['parent_mid']);
|
||||
@@ -3115,7 +3167,7 @@ class Activity {
|
||||
if (!$n) {
|
||||
break;
|
||||
}
|
||||
ASCache::Set($current_item['parent_mid'], serialise($n));
|
||||
ASCache::Set($current_item['parent_mid'], $n);
|
||||
}
|
||||
|
||||
$a = new ActivityStreams($n);
|
||||
@@ -3278,6 +3330,12 @@ class Activity {
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: it appears sometimes $s is an array (needs invetigation)
|
||||
if (!is_string($s)) {
|
||||
btlogger('Not a string: ' . print_r($s, true));
|
||||
return true;
|
||||
}
|
||||
|
||||
$s_alt = htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
if (
|
||||
@@ -3573,27 +3631,46 @@ class Activity {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static function get_quote_bbcode($url) {
|
||||
static function get_quote($url) {
|
||||
$ret = [];
|
||||
$a = null;
|
||||
|
||||
$ret = '';
|
||||
$cached = ASCache::Get($url);
|
||||
if ($cached) {
|
||||
// logger('cached: ' . $url);
|
||||
$a = $cached;
|
||||
}
|
||||
else {
|
||||
// logger('fetching: ' . $url);
|
||||
$a = self::fetch($url);
|
||||
if ($a) {
|
||||
ASCache::Set($url, $a);
|
||||
}
|
||||
}
|
||||
|
||||
$a = self::fetch($url);
|
||||
if ($a) {
|
||||
$act = new ActivityStreams($a);
|
||||
|
||||
if ($act->is_valid()) {
|
||||
$content = self::get_content($act->obj);
|
||||
$decoded = self::decode_note($act);
|
||||
|
||||
$ret .= "[share author='" . urlencode($act->actor['name'] ?? $act->actor['preferredUsername']) .
|
||||
$bbcode = "[share author='" . urlencode($act->actor['name'] ?? $act->actor['preferredUsername']) .
|
||||
"' profile='" . $act->actor['id'] .
|
||||
"' avatar='" . ($act->actor['icon']['url'] ?? z_root() . '/' . get_default_profile_photo(80)) .
|
||||
"' link='" . $act->obj['id'] .
|
||||
"' link='" . $url .
|
||||
"' auth='" . ((is_matrix_url($act->actor['id'])) ? 'true' : 'false') .
|
||||
"' posted='" . $act->obj['published'] .
|
||||
"' message_id='" . $act->obj['id'] .
|
||||
"']";
|
||||
$ret .= self::bb_content($content, 'content');
|
||||
$ret .= '[/share]';
|
||||
|
||||
$bbcode .= $decoded['body'];
|
||||
$bbcode .= '[/share]';
|
||||
|
||||
$ret['bbcode'] = $bbcode;
|
||||
$ret['url'] = $decoded['plink'];
|
||||
$ret['mid'] = $decoded['mid'];
|
||||
$ret['term'] = $decoded['term'] ?? [];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3670,10 +3747,11 @@ class Activity {
|
||||
|
||||
'conversation' => 'ostatus:conversation',
|
||||
|
||||
'guid' => 'diaspora:guid',
|
||||
|
||||
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
|
||||
'Hashtag' => 'as:Hashtag'
|
||||
'Hashtag' => 'as:Hashtag',
|
||||
|
||||
'quoteUrl' => 'as:quoteUrl',
|
||||
'quoteUri' => 'http://fedibird.com/ns#quoteUri'
|
||||
];
|
||||
|
||||
}
|
||||
@@ -3712,11 +3790,13 @@ class Activity {
|
||||
*/
|
||||
|
||||
public static function init_background_fetch(string $observer_hash = '') {
|
||||
if (isset(App::$cache['zot_fetch_objects'])) {
|
||||
$channels_str = '';
|
||||
|
||||
$interval = Config::Get('queueworker', 'queue_interval', 500000);
|
||||
|
||||
if (isset(App::$cache['zot_fetch_objects'])) {
|
||||
foreach (App::$cache['zot_fetch_objects'] as $mid => $info) {
|
||||
$force = $info['force'];
|
||||
$channels_str = '';
|
||||
|
||||
foreach ($info['channels'] as $c) {
|
||||
if ($channels_str) {
|
||||
@@ -3726,19 +3806,22 @@ class Activity {
|
||||
}
|
||||
|
||||
Master::Summon(['Zotconvo', $channels_str, $mid, $force]);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$observer_hash) {
|
||||
logger('Attempt to initiate Fetchparents or Convo daemon without observer');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset(App::$cache['as_fetch_objects'])) {
|
||||
if (!$observer_hash) {
|
||||
logger('Attempt to initiate Fetchparents daemon without observer');
|
||||
return;
|
||||
}
|
||||
|
||||
$channels_str = '';
|
||||
|
||||
foreach (App::$cache['as_fetch_objects'] as $mid => $info) {
|
||||
$force = $info['force'];
|
||||
$channels_str = '';
|
||||
|
||||
foreach ($info['channels'] as $c) {
|
||||
if ($channels_str) {
|
||||
@@ -3748,8 +3831,33 @@ class Activity {
|
||||
}
|
||||
|
||||
Master::Summon(['Fetchparents', $channels_str, $observer_hash, $mid, $force]);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset(App::$cache['as_fetch_collection'])) {
|
||||
foreach (App::$cache['as_fetch_collection'] as $mid => $info) {
|
||||
$force = $info['force'];
|
||||
$channels_str = '';
|
||||
|
||||
foreach ($info['channels'] as $c) {
|
||||
if ($channels_str) {
|
||||
$channels_str .= ',';
|
||||
}
|
||||
$channels_str .= $c;
|
||||
}
|
||||
|
||||
Master::Summon(['Convo', $channels_str, $observer_hash, $mid, $force]);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function addToCollection($channel, $object, $target, $sourceItem = null, $deliver = true) {
|
||||
@@ -3859,4 +3967,34 @@ class Activity {
|
||||
?? '';
|
||||
}
|
||||
|
||||
public static function pasteQuote(string $body, array $quote): string
|
||||
{
|
||||
// Escape URLs for regex safety
|
||||
$urls = array_map('preg_quote', [$quote['url'], $quote['mid']], array_fill(0, 2, '/'));
|
||||
|
||||
$patterns = [];
|
||||
foreach ($urls as $url) {
|
||||
// Match both plain and BBCode-style references, with optional line breaks or spaces
|
||||
$patterns[] = '/RE:\s*(?:\[url=' . $url . '\]' . $url . '\[\/url\]|' . $url . ')[\s\r\n]?/i';
|
||||
}
|
||||
|
||||
$found = false;
|
||||
foreach ($patterns as $pattern) {
|
||||
if (preg_match($pattern, $body)) {
|
||||
$found = true;
|
||||
$body = preg_replace($pattern, $quote['bbcode'], $body);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
if (!empty($body)) {
|
||||
$body .= "\r\n\r\n";
|
||||
}
|
||||
$body .= $quote['bbcode'];
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ class ActivityStreams {
|
||||
}
|
||||
|
||||
// cache for future use
|
||||
ASCache::Set($this->id, 'json:' . $this->raw);
|
||||
ASCache::Set($this->id, $this->data);
|
||||
|
||||
$this->type = $this->get_primary_type();
|
||||
$this->actor = $this->get_actor('actor', '', '');
|
||||
@@ -413,13 +413,13 @@ class ActivityStreams {
|
||||
$cached = ASCache::Get($x);
|
||||
if ($cached) {
|
||||
// logger('AS cached: ' . $x);
|
||||
$y = unserialise($cached);
|
||||
$y = $cached;
|
||||
}
|
||||
else {
|
||||
// logger('AS fetching: ' . $x);
|
||||
$y = $this->fetch_property($x);
|
||||
if ($y) {
|
||||
ASCache::Set($x, serialise($y));
|
||||
ASCache::Set($x, $y);
|
||||
}
|
||||
}
|
||||
if (is_array($y)) {
|
||||
@@ -553,6 +553,7 @@ class ActivityStreams {
|
||||
}
|
||||
|
||||
$url = unparse_url($parseUrl);
|
||||
$this->signer = ['id' => $url];
|
||||
|
||||
$hublocs = Activity::get_actor_hublocs($url);
|
||||
|
||||
|
||||
@@ -1028,12 +1028,7 @@ class Apps {
|
||||
if(! $syslist)
|
||||
return;
|
||||
|
||||
foreach($syslist as $k => $li) {
|
||||
if($li['guid'] === $guid) {
|
||||
$position = $k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$position = array_find_key($syslist, fn ($v) => $v['guid'] === $guid);
|
||||
if(! $position)
|
||||
return;
|
||||
|
||||
@@ -1082,12 +1077,7 @@ class Apps {
|
||||
if(! $syslist)
|
||||
return;
|
||||
|
||||
foreach($syslist as $k => $li) {
|
||||
if($li['guid'] === $guid) {
|
||||
$position = $k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$position = array_find_key($syslist, fn ($v) => $v['guid'] === $guid);
|
||||
if($position >= count($syslist) - 1)
|
||||
return;
|
||||
|
||||
|
||||
@@ -132,8 +132,8 @@ class Config {
|
||||
$value = App::$config[$family][$key];
|
||||
|
||||
if (! is_array($value)) {
|
||||
if (substr($value, 0, 5) == 'json:') {
|
||||
return json_decode(substr($value, 5), true);
|
||||
if (str_starts_with($value, 'json:')) {
|
||||
return json_unserialize($value);
|
||||
} else if (preg_match('|^a:[0-9]+:{.*}$|s', $value)) {
|
||||
// Unserialize in inherently unsafe. Try to mitigate by not
|
||||
// allowing unserializing objects. Only kept for backwards
|
||||
|
||||
43
Zotlabs/Lib/DbStats.php
Normal file
43
Zotlabs/Lib/DbStats.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use DBA;
|
||||
|
||||
/**
|
||||
* Abstract class to obtain statistics from the database.
|
||||
*
|
||||
* This class should not be instantiated on it's own, but you can get
|
||||
* a concrete class for the configured database type of this site by
|
||||
* calling the `DbStats::getStats()` function.
|
||||
*/
|
||||
abstract class DbStats {
|
||||
|
||||
/**
|
||||
* Get an object for getting statistics from the database.
|
||||
*
|
||||
* @return DbStats The concrete class for obtaining the statistics from
|
||||
* this instances database.
|
||||
*/
|
||||
public static function getStats(): DbStats {
|
||||
return DBA::$dba->is_postgres()
|
||||
? new PostgresDbStats()
|
||||
: new MySQLDbStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of queries recorded by the database.
|
||||
*
|
||||
* @return int Number of queries.
|
||||
*/
|
||||
public abstract function getQueries(): int;
|
||||
|
||||
// Prevent instantiation of this class
|
||||
private function __construct() {}
|
||||
}
|
||||
@@ -431,7 +431,7 @@ class Enotify {
|
||||
|
||||
elseif (isset($params['type']) && $params['type'] === NOTIFY_INTRO) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] Introduction received'));
|
||||
$preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
|
||||
$preamble = sprintf( t('You\'ve received a new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('You\'ve received [zrl=%1$s]a new connection request[/zrl] from %2$s.'),
|
||||
$siteurl . '/connections/ifpending',
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
|
||||
@@ -511,7 +511,7 @@ class Enotify {
|
||||
*/
|
||||
|
||||
|
||||
$hash = ((in_array($params['verb'], ['Create', 'Update'])) ? $params['item']['uuid'] : $params['item']['thr_parent_uuid']);
|
||||
$hash = ((in_array($params['verb'], ['Create', 'Update', 'Invite'])) ? $params['item']['uuid'] : $params['item']['thr_parent_uuid']);
|
||||
|
||||
if (!$hash) {
|
||||
$hash = new_uuid();
|
||||
|
||||
@@ -34,8 +34,20 @@ class IConfig {
|
||||
|
||||
if(is_array($item) && array_key_exists('iconfig',$item) && is_array($item['iconfig'])) {
|
||||
foreach($item['iconfig'] as $c) {
|
||||
if (isset($c['iid']) && $c['iid'] == $iid && isset($c['cat']) && $c['cat'] == $family && isset($c['k']) && $c['k'] == $key)
|
||||
if (isset($c['iid']) && $c['iid'] == $iid && isset($c['cat']) && $c['cat'] == $family && isset($c['k']) && $c['k'] == $key) {
|
||||
if (is_string($c['v'])) {
|
||||
if (str_starts_with($c['v'], 'json:')) {
|
||||
$c['v'] = json_unserialize($c['v']);
|
||||
} else if (preg_match('|^a:[0-9]+:{.*}$|s', $c['v'])) {
|
||||
// Unserialize in inherently unsafe. Try to mitigate by not
|
||||
// allowing unserializing objects. Only kept for backwards
|
||||
// compatibility. JSON serialization should be prefered.
|
||||
$c['v'] = unserialize($c['v'], ['allowed_classes' => false]);
|
||||
}
|
||||
}
|
||||
|
||||
return $c['v'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +56,24 @@ class IConfig {
|
||||
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)
|
||||
if (str_starts_with($r[0]['v'], 'json:')) {
|
||||
$r[0]['v'] = json_unserialize($r[0]['v']);
|
||||
} else if (preg_match('|^a:[0-9]+:{.*}$|s', $r[0]['v'])) {
|
||||
// Unserialize in inherently unsafe. Try to mitigate by not
|
||||
// allowing unserializing objects. Only kept for backwards
|
||||
// compatibility. JSON serialization should be prefered.
|
||||
$r[0]['v'] = unserialize($r[0]['v'], ['allowed_classes' => false]);
|
||||
}
|
||||
|
||||
if ($is_item) {
|
||||
$item['iconfig'][] = $r[0];
|
||||
}
|
||||
|
||||
return $r[0]['v'];
|
||||
}
|
||||
|
||||
return $default;
|
||||
|
||||
}
|
||||
@@ -73,7 +97,7 @@ class IConfig {
|
||||
|
||||
static public function Set(&$item, $family, $key, $value, $sharing = false) {
|
||||
|
||||
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
|
||||
$dbvalue = ((is_array($value)) ? json_serialize($value) : $value);
|
||||
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
|
||||
|
||||
$is_item = false;
|
||||
|
||||
@@ -25,18 +25,18 @@ class Img_filesize {
|
||||
|
||||
|
||||
static function getLocalFileSize($url) {
|
||||
|
||||
|
||||
$fname = basename($url);
|
||||
$resolution = 0;
|
||||
|
||||
|
||||
if(strpos($fname,'.') !== false)
|
||||
$fname = substr($fname,0,strpos($fname,'.'));
|
||||
|
||||
|
||||
if(substr($fname,-2,1) == '-') {
|
||||
$resolution = intval(substr($fname,-1,1));
|
||||
$fname = substr($fname,0,-2);
|
||||
}
|
||||
|
||||
|
||||
$r = q("SELECT filesize FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
|
||||
dbesc($fname),
|
||||
intval($resolution)
|
||||
@@ -116,7 +116,6 @@ function getRemoteFileSize($url)
|
||||
|
||||
curl_exec($ch);
|
||||
curl_getinfo($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return $size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Mmccook\JsonCanonicalizator\JsonCanonicalizatorFactory;
|
||||
use Root23\JsonCanonicalizer\JsonCanonicalizer;
|
||||
use StephenHill\Base58;
|
||||
|
||||
class JcsEddsa2022 {
|
||||
@@ -102,8 +102,8 @@ class JcsEddsa2022 {
|
||||
}
|
||||
|
||||
public function canonicalize($data) {
|
||||
$canonicalization = JsonCanonicalizatorFactory::getInstance();
|
||||
return $canonicalization->canonicalize($data);
|
||||
$canonicalizer = new JsonCanonicalizer();
|
||||
return $canonicalizer->canonicalize($data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ class LDSignatures {
|
||||
|
||||
|
||||
static function verify($data,$pubkey) {
|
||||
$expand_and_check_unsafe = true;
|
||||
|
||||
$ohash = self::hash(self::signable_options($data['signature']));
|
||||
$dhash = self::hash(self::signable_data($data));
|
||||
$ohash = self::hash(self::signable_options($data['signature']), $expand_and_check_unsafe);
|
||||
$dhash = self::hash(self::signable_data($data), $expand_and_check_unsafe);
|
||||
|
||||
$x = Crypto::verify($ohash . $dhash,base64_decode($data['signature']['signatureValue']), $pubkey);
|
||||
logger('LD-verify: ' . intval($x));
|
||||
@@ -74,11 +75,11 @@ class LDSignatures {
|
||||
return json_encode($newopts,JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
static function hash($obj) {
|
||||
return hash('sha256', self::normalise($obj));
|
||||
static function hash($obj, $expand_and_check_unsafe = false) {
|
||||
return hash('sha256', self::normalise($obj, $expand_and_check_unsafe));
|
||||
}
|
||||
|
||||
static function normalise($data) {
|
||||
static function normalise($data, $expand_and_check_unsafe) {
|
||||
$ret = '';
|
||||
|
||||
if(is_string($data)) {
|
||||
@@ -90,6 +91,15 @@ class LDSignatures {
|
||||
|
||||
jsonld_set_document_loader('jsonld_document_loader');
|
||||
|
||||
if ($expand_and_check_unsafe) {
|
||||
$expanded = jsonld_expand($data);
|
||||
|
||||
if (self::contains_unsafe_keys($expanded)) {
|
||||
logger('contains_unsafe_keys: ' . print_r($data,true));
|
||||
throw new \Exception('json-ld graph modification operation detected');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$ret = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]);
|
||||
}
|
||||
@@ -132,6 +142,41 @@ class LDSignatures {
|
||||
|
||||
}
|
||||
|
||||
static function contains_unsafe_keys(array|object $data, int $depth = 0): bool
|
||||
{
|
||||
if ($depth > 64) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$unsafe_keys = ['@graph', '@included', '@reverse'];
|
||||
|
||||
if (is_object($data)) {
|
||||
$data = (array) $data;
|
||||
}
|
||||
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => $value) {
|
||||
//
|
||||
// We can't use `in_array` since the keys may contain more than
|
||||
// just the keyword after expansion, typically "_:@included"
|
||||
// for an unnamed node with the "@included" key.
|
||||
//
|
||||
// So we use `array_filter` with a callback instead:
|
||||
$matches = array_filter($unsafe_keys, fn ($k) => strpos($key, $k) !== false);
|
||||
|
||||
if (!empty($matches)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_array($value) || is_object($value)) {
|
||||
if (self::contains_unsafe_keys($value, $depth + 1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -199,8 +199,7 @@ class Libsync {
|
||||
dbesc($sender)
|
||||
);
|
||||
|
||||
$mid = 'sync';
|
||||
|
||||
$mid = $arr['item'][0]['message_id'] ?? 'sync';
|
||||
|
||||
$DR = new DReport(z_root(), $sender, $d, $mid);
|
||||
|
||||
@@ -306,15 +305,8 @@ class Libsync {
|
||||
|
||||
if (array_key_exists('item', $arr) && $arr['item']) {
|
||||
sync_items($channel, $arr['item'], ((array_key_exists('relocate', $arr)) ? $arr['relocate'] : null));
|
||||
$mid = $arr['item'][0]['message_id'] . '#sync';
|
||||
}
|
||||
|
||||
// deprecated, maintaining for a few months for upward compatibility
|
||||
// this should sync webpages, but the logic is a bit subtle
|
||||
|
||||
//if (array_key_exists('item_id', $arr) && $arr['item_id'])
|
||||
// sync_items($channel, $arr['item_id']);
|
||||
|
||||
if (array_key_exists('menu', $arr) && $arr['menu'])
|
||||
sync_menus($channel, $arr['menu']);
|
||||
|
||||
@@ -757,12 +749,11 @@ class Libsync {
|
||||
*/
|
||||
call_hooks('process_channel_sync_delivery', $addon);
|
||||
|
||||
$DR = new DReport(z_root(), $d, $d, $mid, 'channel sync processed');
|
||||
|
||||
$DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>');
|
||||
$DR->update('channel sync processed');
|
||||
|
||||
$result[] = $DR->get();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Access\PermissionLimits;
|
||||
use Zotlabs\Access\Permissions;
|
||||
use Zotlabs\Daemon\Master;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Web\HTTPSig;
|
||||
|
||||
require_once('include/crypto.php');
|
||||
@@ -116,10 +116,6 @@ class Libzot {
|
||||
}
|
||||
|
||||
if ($msg) {
|
||||
$actors = get_hubloc_id_urls_by_x($channel['channel_hash']);
|
||||
if ($encoding === 'activitystreams' && array_key_exists('actor', $msg) && is_string($msg['actor']) && in_array($msg['actor'], $actors)) {
|
||||
$msg = JSalmon::sign($msg, $actors[0], $channel['channel_prvkey']);
|
||||
}
|
||||
$data['data'] = $msg;
|
||||
}
|
||||
else {
|
||||
@@ -353,7 +349,7 @@ class Libzot {
|
||||
$next_birthday = datetime_convert('UTC', 'UTC', $record['data']['profile']['next_birthday']);
|
||||
}
|
||||
else {
|
||||
$next_birthday = NULL_DATE;
|
||||
$next_birthday = DBA::$dba->get_null_date();
|
||||
}
|
||||
|
||||
$profile_assign = get_pconfig($channel['channel_id'], 'system', 'profile_assign', '');
|
||||
@@ -1302,8 +1298,17 @@ class Libzot {
|
||||
$item['comment_policy'] = 'authenticated';
|
||||
}
|
||||
|
||||
if (isset($AS->meta['signed_data']) && $AS->meta['signed_data']) {
|
||||
IConfig::Set($item, 'activitypub', 'signed_data', $AS->meta['signed_data'], false);
|
||||
if (!ObjCache::Get($item['mid'])) {
|
||||
ObjCache::Set($item['mid'], $AS->data);
|
||||
}
|
||||
else {
|
||||
$existing = q("SELECT owner_xchan, author_xchan FROM item WHERE mid = '%s' LIMIT 1",
|
||||
dbesc($item['mid'])
|
||||
);
|
||||
|
||||
if ($existing && $existing[0]['owner_xchan'] === $item['owner_xchan'] && $existing[0]['author_xchan'] === $item['author_xchan']) {
|
||||
ObjCache::Set($item['mid'], $AS->data);
|
||||
}
|
||||
}
|
||||
|
||||
logger('Activity received: ' . print_r($item, true), LOGGER_DATA, LOG_DEBUG);
|
||||
@@ -1645,12 +1650,19 @@ class Libzot {
|
||||
if (intval($channel['channel_system']) && (!$arr['item_private']) && (!$relay)) {
|
||||
$local_public = true;
|
||||
|
||||
$incl = Config::Get('system','pubstream_incl');
|
||||
$excl = Config::Get('system','pubstream_excl');
|
||||
$incl = Config::Get('system','pubstream_incl', '');
|
||||
$excl = Config::Get('system','pubstream_excl', '');
|
||||
|
||||
if(($incl || $excl) && !MessageFilter::evaluate($arr, $incl, $excl)) {
|
||||
$local_public = false;
|
||||
continue;
|
||||
if ($incl || $excl) {
|
||||
$plaintext = prepare_text($arr['body'], ((isset($arr['mimetype'])) ? $arr['mimetype'] : 'text/bbcode'));
|
||||
$plaintext = html2plain((isset($arr['summary']) && $arr['summary']) ? $arr['summary'] . ' ' . $plaintext : $plaintext);
|
||||
$plaintext = html2plain((isset($arr['title']) && $arr['title']) ? $arr['title'] . ' ' . $plaintext : $plaintext);
|
||||
|
||||
if (!(new MessageFilter($arr, html_entity_decode($incl), html_entity_decode($excl), ['plaintext' => $plaintext]))->evaluate()) {
|
||||
logger('post is filtered');
|
||||
$local_public = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$r = q("select xchan_selfcensored, xchan_censored from xchan where xchan_hash = '%s'",
|
||||
@@ -1659,6 +1671,7 @@ class Libzot {
|
||||
|
||||
// don't import sys channel posts from selfcensored or censored authors
|
||||
if ($r && ($r[0]['xchan_selfcensored'] || $r[0]['xchan_censored'])) {
|
||||
logger('author is censored');
|
||||
$local_public = false;
|
||||
continue;
|
||||
}
|
||||
@@ -1829,9 +1842,7 @@ class Libzot {
|
||||
}
|
||||
|
||||
if (intval($arr['item_private']) === 2) {
|
||||
if (!perm_is_allowed($channel['channel_id'], $sender, 'post_mail')) {
|
||||
$allowed = false;
|
||||
}
|
||||
$allowed = perm_is_allowed($channel['channel_id'], $sender, 'post_mail');
|
||||
}
|
||||
|
||||
if (!$allowed) {
|
||||
@@ -1893,11 +1904,13 @@ class Libzot {
|
||||
else {
|
||||
$DR->update('update ignored');
|
||||
$result[] = $DR->get();
|
||||
|
||||
// We need this line to ensure wall-to-wall comments and add/remove activities are relayed (by falling through to the relay bit),
|
||||
// and at the same time not relay any other relayable posts more than once, because to do so is very wasteful.
|
||||
if (!intval($r[0]['item_origin']))
|
||||
|
||||
// The second part should prevent possible items that come back to us from channels that source our channel from being relayed again (sender != owner or author).
|
||||
if (!intval($r[0]['item_origin']) || (intval($r[0]['item_origin']) && !in_array($sender, [$r[0]['owner_xchan'], $r[0]['author_xchan']]))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2148,10 +2161,9 @@ class Libzot {
|
||||
}
|
||||
|
||||
if (isset($AS->meta['signed_data'])) {
|
||||
IConfig::Set($arr, 'activitypub', 'signed_data', $AS->meta['signed_data'], false);
|
||||
$j = json_decode($AS->meta['signed_data'], true);
|
||||
if ($j) {
|
||||
IConfig::Set($arr, 'activitypub', 'rawmsg', json_encode(JSalmon::unpack($j['data'])), true);
|
||||
ObjCache::Set($arr['mid'], json_encode(JSalmon::unpack($j['data'])));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Zotfinger;
|
||||
@@ -216,7 +217,7 @@ class Libzotdir {
|
||||
[
|
||||
'site_url' => DIRECTORY_FALLBACK_MASTER,
|
||||
'site_flags' => DIRECTORY_MODE_PRIMARY,
|
||||
'site_update' => NULL_DATE,
|
||||
'site_update' => DBA::$dba->get_null_date(),
|
||||
'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch',
|
||||
'site_realm' => DIRECTORY_REALM,
|
||||
'site_valid' => 1,
|
||||
@@ -247,7 +248,7 @@ class Libzotdir {
|
||||
|
||||
$token = Config::Get('system','realm_token');
|
||||
|
||||
$syncdate = (($rr['site_sync'] <= NULL_DATE) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
|
||||
$syncdate = (($rr['site_sync'] <= DBA::$dba->get_null_date()) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
|
||||
$x = z_fetch_url($rr['site_directory'] . '?f=&sync=' . urlencode($syncdate) . (($token) ? '&t=' . $token : ''));
|
||||
|
||||
if (! $x['success'])
|
||||
@@ -724,7 +725,7 @@ class Libzotdir {
|
||||
|
||||
if ($u) {
|
||||
$x = q("UPDATE updates SET $date_sql $flag_sql ud_last = '%s', ud_host = '%s', ud_addr = '%s', ud_update = 0 WHERE ud_id = %d",
|
||||
dbesc(NULL_DATE),
|
||||
dbesc(DBA::$dba->get_null_date()),
|
||||
dbesc(z_root()),
|
||||
dbesc($addr),
|
||||
intval($u[0]['ud_id'])
|
||||
|
||||
@@ -4,260 +4,417 @@ namespace Zotlabs\Lib;
|
||||
|
||||
require_once('include/html2plain.php');
|
||||
|
||||
class MessageFilter {
|
||||
class MessageFilter
|
||||
{
|
||||
protected $lastMatch = '';
|
||||
protected $item = null;
|
||||
protected $include = '';
|
||||
protected $exclude = '';
|
||||
protected $options = [];
|
||||
protected $tags = null;
|
||||
protected $language = '';
|
||||
protected $text = '';
|
||||
protected $excludeRules = [];
|
||||
protected $includeRules = [];
|
||||
|
||||
public static function evaluate($item, $incl, $excl) {
|
||||
public function __construct($item, $include = '', $exclude = '', $options = [])
|
||||
{
|
||||
$this->item = $item;
|
||||
$this->include = $include;
|
||||
$this->exclude = $exclude;
|
||||
$this->options = $options;
|
||||
$this->setup();
|
||||
}
|
||||
|
||||
$text = prepare_text($item['body'], ((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
|
||||
$text = html2plain((!empty($item['title'])) ? $item['title'] . ' ' . $text : $text);
|
||||
protected function setup()
|
||||
{
|
||||
// Option: plaintext
|
||||
// Improve language detection by providing a plaintext version of $item['body'] which has no markup constructs/tags.
|
||||
|
||||
$lang = null;
|
||||
if ((strpos($incl, 'lang=') !== false) || (strpos($excl, 'lang=') !== false) || (strpos($incl, 'lang!=') !== false) || (strpos($excl, 'lang!=') !== false)) {
|
||||
$lang = detect_language($text);
|
||||
}
|
||||
if (array_key_exists('plaintext', $this->options)) {
|
||||
$this->text = $this->options['plaintext'];
|
||||
} else {
|
||||
$this->text = $this->item['body'];
|
||||
}
|
||||
|
||||
$tags = ((isset($item['term']) && is_array($item['term']) && count($item['term'])) ? $item['term'] : false);
|
||||
$this->language = '';
|
||||
|
||||
$until = null;
|
||||
// Language matching is a bit tricky, because the language can be ambiguous (detect_language() returns '').
|
||||
// If the language is ambiguous, the message will pass (be accepted) regardless of language rules.
|
||||
|
||||
// exclude always has priority
|
||||
if (str_contains($this->include, 'lang=')
|
||||
|| str_contains($this->exclude, 'lang=')
|
||||
|| str_contains($this->include, 'lang!=')
|
||||
|| str_contains($this->exclude, 'lang!=')) {
|
||||
$this->language = detect_language($this->text);
|
||||
}
|
||||
|
||||
$exclude = (($excl) ? explode("\n", $excl) : null);
|
||||
$this->tags = ((isset($this->item['term']) && is_array($this->item['term'])
|
||||
&& count($this->item['term'])) ? $this->item['term'] : null);
|
||||
|
||||
if ($exclude) {
|
||||
foreach ($exclude as $word) {
|
||||
$word = html_entity_decode(trim($word));
|
||||
if (! $word) {
|
||||
continue;
|
||||
}
|
||||
if (isset($lang) && ((strpos($word, 'lang=') === 0) || (strpos($word, 'lang!=') === 0))) {
|
||||
if (!strlen($lang)) {
|
||||
// Result is ambiguous. As we are matching deny rules only at this time, continue tests.
|
||||
// Any matching deny rule concludes testing.
|
||||
continue;
|
||||
}
|
||||
if (strpos($word, 'lang=') === 0 && strcasecmp($lang, trim(substr($word, 5))) == 0) {
|
||||
return false;
|
||||
} elseif (strpos($word, 'lang!=') === 0 && strcasecmp($lang, trim(substr($word, 6))) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
elseif (str_starts_with($word, 'until=')) {
|
||||
$until = strtotime(trim(substr($word, 6)));
|
||||
if ($until > strtotime($item['created'] . ' UTC')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
elseif (substr($word, 0, 1) === '#' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '$' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 2) === '?+') {
|
||||
if (self::test_condition(substr($word, 2), $item['obj'])) {
|
||||
return false;
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '?') {
|
||||
if (self::test_condition(substr($word, 1), $item)) {
|
||||
return false;
|
||||
}
|
||||
} elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) {
|
||||
return false;
|
||||
} elseif (stristr($text, $word) !== false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->excludeRules = $this->parse($this->exclude);
|
||||
$this->includeRules = $this->parse($this->include);
|
||||
|
||||
$include = (($incl) ? explode("\n", $incl) : null);
|
||||
}
|
||||
|
||||
if ($include) {
|
||||
foreach ($include as $word) {
|
||||
$word = html_entity_decode(trim($word));
|
||||
if (! $word) {
|
||||
continue;
|
||||
}
|
||||
if (isset($lang) && ((strpos($word, 'lang=') === 0) || (strpos($word, 'lang!=') === 0))) {
|
||||
if (!strlen($lang)) {
|
||||
// Result is ambiguous. However we are checking allow rules
|
||||
// and an ambiguous language is always permitted.
|
||||
return true;
|
||||
}
|
||||
if (strpos($word, 'lang=') === 0 && strcasecmp($lang, trim(substr($word, 5))) == 0) {
|
||||
return true;
|
||||
} elseif (strpos($word, 'lang!=') === 0 && strcasecmp($lang, trim(substr($word, 6))) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
elseif (str_starts_with($word, 'until=')) {
|
||||
$until = strtotime(trim(substr($word, 6)));
|
||||
if ($until > strtotime($item['created'] . ' UTC')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
elseif (substr($word, 0, 1) === '#' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '$' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 2) === '?+') {
|
||||
if (self::test_condition(substr($word, 2), $item['obj'])) {
|
||||
return true;
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '?') {
|
||||
if (self::test_condition(substr($word, 1), $item)) {
|
||||
return true;
|
||||
}
|
||||
} elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) {
|
||||
return true;
|
||||
} elseif (stristr($text, $word) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
protected function parse($string): array
|
||||
{
|
||||
$rules = [];
|
||||
if (! strlen($string)) {
|
||||
return $rules;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
$phrases = preg_split("/(\s\|\|\s|\s&&\s|\n)/", $string, flags: PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
if (!$phrases) {
|
||||
return $rules;
|
||||
}
|
||||
for ($index = 0; $index < count($phrases); $index ++) {
|
||||
// Even indices are rules and odd indices are operations, linefeed is an implict OR.
|
||||
if (!($index & 1)) {
|
||||
$currentRule = ['operation' => '', 'rule' => $phrases[$index]];
|
||||
if ($index && isset($phrases[$index - 1])) {
|
||||
$currentRule['operation'] = $phrases[$index - 1];
|
||||
if ($currentRule['operation'] === "\n") {
|
||||
$currentRule['operation'] = ' || ';
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
$rules[] = $currentRule;
|
||||
}
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate a conditional expression with support for AND (&&) and OR (||) operators.
|
||||
*
|
||||
* - ?foo ~= baz which will check if item.foo contains the string 'baz';
|
||||
* - ?foo == baz which will check if item.foo is the string 'baz';
|
||||
* - ?foo != baz which will check if item.foo is not the string 'baz';
|
||||
* - ?foo >= 3 which will check if item.foo is greater than or equal to 3;
|
||||
* - ?foo > 3 which will check if item.foo is greater than 3;
|
||||
* - ?foo <= 3 which will check if item.foo is less than or equal to 3;
|
||||
* - ?foo < 3 which will check if item.foo is less than 3;
|
||||
*
|
||||
* - ?foo {} baz which will check if 'baz' is an array element in item.foo
|
||||
* - ?foo {*} baz which will check if 'baz' is an array key in item.foo
|
||||
* - ?foo which will check for a return of a true condition for item.foo;
|
||||
|
||||
public function evaluate(): bool
|
||||
{
|
||||
|
||||
$previousResult = $newResult = null;
|
||||
|
||||
// exclude always has priority
|
||||
|
||||
$exclude = $this->excludeRules;
|
||||
$include = $this->includeRules;
|
||||
|
||||
if ($exclude) {
|
||||
foreach ($exclude as $rule) {
|
||||
if (!strlen(trim($rule['rule']))) {
|
||||
continue;
|
||||
}
|
||||
if (!strlen($this->language) && ((str_starts_with($rule['rule'], 'lang=')) || (str_starts_with($rule['rule'], 'lang!=')))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $this->evaluateRule($rule['rule']);
|
||||
|
||||
switch ($rule['operation']) {
|
||||
case '':
|
||||
$previousResult = $newResult = $result;
|
||||
break;
|
||||
case ' || ':
|
||||
$newResult = $previousResult || $result;
|
||||
break;
|
||||
case ' && ':
|
||||
$newResult = $previousResult && $result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($newResult) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$previousResult = $newResult = null;
|
||||
|
||||
if ($include) {
|
||||
foreach ($include as $rule) {
|
||||
if (!strlen(trim($rule['rule']))) {
|
||||
continue;
|
||||
}
|
||||
if (!strlen($this->language) && ((str_starts_with($rule['rule'], 'lang=')) || (str_starts_with($rule['rule'], 'lang!=')))) {
|
||||
continue;
|
||||
}
|
||||
$result = $this->evaluateRule($rule['rule']);
|
||||
|
||||
switch ($rule['operation']) {
|
||||
case '':
|
||||
$previousResult = $newResult = $result;
|
||||
break;
|
||||
case ' || ':
|
||||
$newResult = $previousResult || $result;
|
||||
break;
|
||||
case ' && ':
|
||||
$newResult = $previousResult && $result;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return $newResult ?? true;
|
||||
}
|
||||
|
||||
protected function evaluateRule($ruleText): bool
|
||||
{
|
||||
$ruleText = trim($ruleText);
|
||||
|
||||
if (($this->language) && ((str_starts_with($ruleText, 'lang=')) || (str_starts_with($ruleText, 'lang!=')))) {
|
||||
if (str_starts_with($ruleText, 'lang=') && strcasecmp($this->language, trim(substr($ruleText, 5))) == 0) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
} elseif (str_starts_with($ruleText, 'lang!=') && strcasecmp($this->language, trim(substr($ruleText, 6))) != 0) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, 'until=')) {
|
||||
$until = strtotime(trim(substr($ruleText, 6)));
|
||||
|
||||
if ($until > strtotime($this->item['created'] . ' UTC')) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, '#') && $this->tags) {
|
||||
// #hashtag match
|
||||
foreach ($this->tags as $t) {
|
||||
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (!strcasecmp($t['term'], substr($ruleText, 1)) || (substr($ruleText, 1) === '*'))) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// hashtag count match
|
||||
if (substr($ruleText, 1, 1) === '>') {
|
||||
$hashtagLimit = (int)substr($ruleText, 2);
|
||||
$hashtagCount = 0;
|
||||
foreach ($this->tags as $t) {
|
||||
if ($t['ttype'] == TERM_HASHTAG || $t['ttype'] == TERM_COMMUNITYTAG) {
|
||||
$hashtagCount++;
|
||||
}
|
||||
}
|
||||
if ($hashtagLimit && $hashtagCount > $hashtagLimit) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, '@') && $this->tags) {
|
||||
// @mention match
|
||||
foreach ($this->tags as $t) {
|
||||
if ((($t['ttype'] == TERM_MENTION && (!strcasecmp($t['term'], substr($ruleText, 1)))) || (substr($ruleText, 1) === '*'))) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// mention count match
|
||||
if (substr($ruleText, 1, 1) === '>') {
|
||||
$mentionLimit = (int)substr($ruleText, 2);
|
||||
$mentionCount = 0;
|
||||
foreach ($this->tags as $t) {
|
||||
if ($t['ttype'] == TERM_MENTION) {
|
||||
$mentionCount++;
|
||||
}
|
||||
}
|
||||
if ($mentionLimit && $mentionCount > $mentionLimit) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, '$') && $this->tags) {
|
||||
foreach ($this->tags as $t) {
|
||||
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($ruleText, 1)) || (substr($ruleText, 1) === '*'))) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, '?+') && is_array($this->item['obj'])) {
|
||||
if ($this->test_condition(substr($ruleText, 2), $this->item['obj'])) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, '?')) {
|
||||
$this->item['ua'] = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
if (empty($this->item['app'])) {
|
||||
$author_app = $this->item['author']['site_project'] ?? '';
|
||||
if (!$author_app && isset($this->item['author'])) {
|
||||
if (str_contains($this->item['author']['xchan_hash'], 'threads.net') || str_contains($this->item['author']['xchan_hash'], 'threads.com')) {
|
||||
$author_app = 'threads';
|
||||
}
|
||||
}
|
||||
$this->item['app'] = $author_app;
|
||||
}
|
||||
if ($this->test_condition(substr($ruleText, 1), $this->item)) {
|
||||
unset($this->item['ua']);
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
unset($this->item['ua']);
|
||||
} elseif ((str_starts_with($ruleText, '/')) && preg_match($ruleText, $this->item['body'])) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
} elseif (stristr($this->item['body'], $ruleText) !== false) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function getLastMatch(): string
|
||||
{
|
||||
return $this->lastMatch;
|
||||
}
|
||||
public function setLastMatch($string): MessageFilter
|
||||
{
|
||||
$this->lastMatch = $string;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @brief Test for Conditional Execution conditions. Shamelessly ripped off from src/Render/Comanche
|
||||
*
|
||||
* This is extensible. The first version of variable testing supports tests of the forms:
|
||||
*
|
||||
* - ?foo ~= baz will check if item.foo contains the string 'baz';
|
||||
* - ?foo == baz will check if item.foo is the string 'baz';
|
||||
* - ?foo != baz will check if item.foo is not the string 'baz';
|
||||
* - ?foo // baz will check if item.foo matches the regular expression 'baz';
|
||||
* - ?foo >= 3 will check if item.foo is greater than or equal to 3;
|
||||
* - ?foo > 3 will check if item.foo is greater than 3;
|
||||
* - ?foo <= 3 will check if item.foo is less than or equal to 3;
|
||||
* - ?foo < 3 will check if item.foo is less than 3;
|
||||
* - ?foo & 2 will check if item.foo has the second bit set.
|
||||
* - ?foo !& 2 will check if item.foo does not have the second bit set.
|
||||
*
|
||||
* - ?foo {} baz which will check if 'baz' is an array element in item.foo
|
||||
* - ?foo {*} baz which will check if 'baz' is an array key in item.foo
|
||||
* - ?foo which will check for a return of a true condition for item.foo;
|
||||
* - ?!foo which will check for a return of a false condition for item.foo;
|
||||
*
|
||||
* The values 0, '', an empty array, and an unset value will all evaluate to false.
|
||||
*
|
||||
* @param string $s The condition string to evaluate.
|
||||
* @param array $item The associative array providing variable values.
|
||||
* @return bool True if the condition is met, false otherwise.
|
||||
*/
|
||||
* The values 0, '', an empty array, and an unset value will all evaluate to false.
|
||||
*
|
||||
* @param string $s
|
||||
* @param array $item
|
||||
* @return bool
|
||||
*/
|
||||
protected function test_condition($s,$item)
|
||||
{
|
||||
$s = trim($s);
|
||||
|
||||
public static function test_condition($s, $item) {
|
||||
$s = trim($s);
|
||||
if (preg_match('/(.*?)\s&\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x & (int) trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle OR (||)
|
||||
// Split on '||' not inside quotes
|
||||
$or_parts = preg_split('/\s*\|\|\s*/', $s);
|
||||
if (count($or_parts) > 1) {
|
||||
foreach ($or_parts as $part) {
|
||||
if (self::test_condition(ltrim($part, '?+'), $item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (preg_match('/(.*?)\s!&\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (!($x & (int) trim($matches[2]))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle AND (&&)
|
||||
// Split on '&&' not inside quotes
|
||||
$and_parts = preg_split('/\s*\&\&\s*/', $s);
|
||||
if (count($and_parts) > 1) {
|
||||
foreach ($and_parts as $part) {
|
||||
if (!self::test_condition(ltrim($part, '?+'), $item)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/(.*?)\s~=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (is_string($x) && stripos($x, trim($matches[2])) !== false) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Basic checks
|
||||
if (preg_match('/(.*?)\s==\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x == trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Contains substring (case-insensitive)
|
||||
if (preg_match('/(.*?)\s\~\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return (stripos($x, trim($matches[2])) !== false);
|
||||
}
|
||||
if (preg_match('/(.*?)\s!=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x != trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Equality
|
||||
if (preg_match('/(.*?)\s\=\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x == trim($matches[2]));
|
||||
}
|
||||
if (preg_match('/(.*?)\s\/\/\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (substr(trim($matches[2]),0,1) !== substr(trim($matches[2]),-1)) {
|
||||
$matches[2] = '/' . trim($matches[2]) . '/';
|
||||
}
|
||||
if (preg_match(trim($matches[2]), $x)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Inequality
|
||||
if (preg_match('/(.*?)\s\!\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x != trim($matches[2]));
|
||||
}
|
||||
if (preg_match('/(.*?)\s>=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x >= trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Greater than or equal
|
||||
if (preg_match('/(.*?)\s\>\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x >= trim($matches[2]));
|
||||
}
|
||||
if (preg_match('/(.*?)\s<=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x <= trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Less than or equal
|
||||
if (preg_match('/(.*?)\s\<\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x <= trim($matches[2]));
|
||||
}
|
||||
if (preg_match('/(.*?)\s>\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x > trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Greater than
|
||||
if (preg_match('/(.*?)\s\>\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x > trim($matches[2]));
|
||||
}
|
||||
if (preg_match('/(.*?)\s<\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x < trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Less than
|
||||
if (preg_match('/(.*?)\s\<\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x < trim($matches[2]));
|
||||
}
|
||||
// Array contains value
|
||||
if (preg_match('/(.*?)\s\{\}\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (is_array($x) && in_array(trim($matches[2]), $x)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Array contains value
|
||||
if (preg_match('/(.*?)\s\{\}\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return (is_array($x) && in_array(trim($matches[2]), $x));
|
||||
}
|
||||
|
||||
// Array contains key
|
||||
if (preg_match('/(.*?)\s\{\*\}\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return (is_array($x) && array_key_exists(trim($matches[2]), $x));
|
||||
}
|
||||
|
||||
// Ordering of this check (for falsiness) with relation to the following one (check for truthiness) is important.
|
||||
// Falsy check
|
||||
if (preg_match('/\!(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return !$x;
|
||||
}
|
||||
|
||||
// Truthy check (default)
|
||||
if (preg_match('/(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return (bool)$x;
|
||||
}
|
||||
|
||||
// If no conditions matched, return false
|
||||
return false;
|
||||
}
|
||||
// Array contains key
|
||||
if (preg_match('/(.*?)\s\{\*}\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (is_array($x) && array_key_exists(trim($matches[2]), $x)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ordering of this check (for falseness) with relation to the following one (check for truthiness) is important.
|
||||
if (preg_match('/!(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (!$x) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
37
Zotlabs/Lib/MySQLDbStats.php
Normal file
37
Zotlabs/Lib/MySQLDbStats.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use DBA;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Concrete implementation for getting stats from MySQL and MariaDB databases.
|
||||
*/
|
||||
class MySQLDbStats extends DbStats {
|
||||
|
||||
public function getQueries(): int {
|
||||
//
|
||||
// We can't use the regular Hubzilla db helper function here, as
|
||||
// it will only return information from a `SELECT` statement.
|
||||
//
|
||||
// Use the basic PDO access instead.
|
||||
//
|
||||
$query = DBA::$dba->db->prepare('SHOW STATUS LIKE "Queries"');
|
||||
$query->execute();
|
||||
|
||||
$result = $query->fetch(PDO::FETCH_ASSOC);
|
||||
logger(print_r($result, true));
|
||||
if (!empty($result)) {
|
||||
return $result['Value'] ?? -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
40
Zotlabs/Lib/ObjCache.php
Normal file
40
Zotlabs/Lib/ObjCache.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
class ObjCache
|
||||
{
|
||||
public static function Get($path, $type = 'as')
|
||||
{
|
||||
if (!$path) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$localpath = Hashpath::path($path, 'store/[data]/[obj]/' . $type, 2, alg: 'sha256');
|
||||
if (file_exists($localpath)) {
|
||||
return json_unserialize(file_get_contents($localpath));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public static function Set($path, $content, $type = 'as') {
|
||||
if (!$path) {
|
||||
return;
|
||||
}
|
||||
|
||||
$localpath = Hashpath::path($path, 'store/[data]/[obj]/' . $type, 2, alg: 'sha256');
|
||||
file_put_contents($localpath, json_serialize($content));
|
||||
}
|
||||
|
||||
public static function Delete($path, $type = 'as') {
|
||||
if (!$path) {
|
||||
return;
|
||||
}
|
||||
|
||||
$localpath = Hashpath::path($path, 'store/[data]/[obj]/' . $type, 2, alg: 'sha256');
|
||||
if (file_exists($localpath)) {
|
||||
unlink($localpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Zotlabs/Lib/PostgresDbStats.php
Normal file
32
Zotlabs/Lib/PostgresDbStats.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use DBA;
|
||||
|
||||
/**
|
||||
* Concrete implementation for getting stats from PostgreSQL databases.
|
||||
*/
|
||||
class PostgresDbStats extends DbStats {
|
||||
|
||||
public function getQueries(): int {
|
||||
$sqlGetQps = <<<'SQL'
|
||||
select (xact_commit + xact_rollback) as queries
|
||||
from pg_stat_database
|
||||
where datname='%s'
|
||||
SQL;
|
||||
|
||||
$result = q($sqlGetQps, DBA::$dba->dbname);
|
||||
if (!empty($result)) {
|
||||
return $result[0]['queries'] ?? -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,20 @@ use Zotlabs\Zot6\Zot6Handler;
|
||||
|
||||
class Queue {
|
||||
|
||||
/**
|
||||
* Get number of entries in the out queue.
|
||||
*
|
||||
* When delivery is successful, the item is removed from the out queue, so
|
||||
* the number of items in the queue reflects the number of pending delivery
|
||||
* attempts.
|
||||
*
|
||||
* @return int Number of items in the out queue.
|
||||
*/
|
||||
static function count(): int {
|
||||
$r = dbq('select count(*) as total from outq');
|
||||
return $r[0]['total'] ?? 0;
|
||||
}
|
||||
|
||||
static function update($id, $add_priority = 0) {
|
||||
|
||||
logger('queue: requeue item ' . $id,LOGGER_DEBUG);
|
||||
|
||||
28
Zotlabs/Lib/QueueWorkerStats.php
Normal file
28
Zotlabs/Lib/QueueWorkerStats.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
class QueueWorkerStats
|
||||
{
|
||||
public readonly int $size;
|
||||
public readonly int $active;
|
||||
|
||||
public function __construct() {
|
||||
$query = <<<'SQL'
|
||||
select count(*) as total from workerq
|
||||
union (select count(*) as qworkers from workerq where workerq_reservationid is not null)
|
||||
SQL;
|
||||
|
||||
$result = dbq('select count(*) as total from workerq');
|
||||
$this->size = !empty($result) ? $result[0]['total'] : -1;
|
||||
|
||||
$result = dbq('select count(*) as qworkers from workerq where workerq_reservationid is not null');
|
||||
$this->active = !empty($result) ? $result[0]['qworkers'] : -1;
|
||||
}
|
||||
}
|
||||
@@ -118,26 +118,28 @@ class Share {
|
||||
$photo_bb = $object['body'];
|
||||
}
|
||||
|
||||
if (strpos($this->item['body'], "[/share]") !== false) {
|
||||
$pos = strpos($this->item['body'], "[share");
|
||||
$bb = substr($this->item['body'], $pos);
|
||||
} else {
|
||||
$bb = "[share author='".urlencode($this->item['author']['xchan_name']).
|
||||
"' profile='" . $this->item['author']['xchan_url'] .
|
||||
"' avatar='" . $this->item['author']['xchan_photo_s'] .
|
||||
"' link='" . $this->item['plink'] .
|
||||
"' auth='" . (($this->item['author']['xchan_network'] === 'zot6') ? 'true' : 'false') .
|
||||
"' posted='" . $this->item['created'] .
|
||||
"' message_id='" . $this->item['mid'] .
|
||||
"']";
|
||||
if($this->item['title'])
|
||||
if (!str_contains($this->item['body'], '[/share]')) {
|
||||
$quote = in_array($this->item['author']['xchan_network'], ['zot6', 'activitypub']) ? "quote='true'" : '';
|
||||
|
||||
$bb .= "[share author='" . urlencode($this->item['author']['xchan_name']) . "'
|
||||
profile='" . $this->item['author']['xchan_url'] . "'
|
||||
avatar='" . $this->item['author']['xchan_photo_s'] . "'
|
||||
link='" . $this->item['plink'] . "'
|
||||
auth='" . (($this->item['author']['xchan_network'] === 'zot6') ? 'true' : 'false') . "'
|
||||
posted='" . $this->item['created'] . "'
|
||||
message_id='" . $this->item['mid'] . "'
|
||||
$quote
|
||||
]";
|
||||
|
||||
if ($this->item['title']) {
|
||||
$bb .= '[h3][b]'.$this->item['title'].'[/b][/h3]'."\r\n";
|
||||
}
|
||||
|
||||
$bb .= (($is_photo) ? $photo_bb . "\r\n" . $this->item['body'] : $this->item['body']);
|
||||
$bb .= "[/share]";
|
||||
}
|
||||
|
||||
return $bb;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Access\AccessList;
|
||||
|
||||
require_once('include/text.php');
|
||||
@@ -120,10 +121,10 @@ class ThreadItem {
|
||||
$locktype = 0;
|
||||
}
|
||||
|
||||
$shareable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && (intval($item['item_private']) === 0));
|
||||
$shareable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && (intval($item['item_private']) === 0) && !str_contains($item['body'], '[/share]'));
|
||||
|
||||
// allow an exemption for sharing stuff from your private feeds
|
||||
if($item['author']['xchan_network'] === 'rss')
|
||||
if ($item['author']['xchan_network'] === 'rss')
|
||||
$shareable = true;
|
||||
|
||||
$repeatable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && intval($item['item_private']) === 0 && in_array($item['author']['xchan_network'], ['zot6', 'activitypub']));
|
||||
@@ -283,9 +284,12 @@ class ThreadItem {
|
||||
$reply_to = [];
|
||||
$reactions_allowed = false;
|
||||
|
||||
if($this->is_commentable() && $observer) {
|
||||
if($this->is_commentable()) {
|
||||
$reply_to = array( t("Reply to this message"), t("reply"), t("Reply to"));
|
||||
$reactions_allowed = true;
|
||||
|
||||
if ($observer) {
|
||||
$reactions_allowed = true;
|
||||
}
|
||||
}
|
||||
|
||||
$share = [];
|
||||
@@ -412,7 +416,7 @@ class ThreadItem {
|
||||
'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'),
|
||||
'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created']),
|
||||
'editedtime' => (($item['edited'] != $item['created']) ? sprintf(t('Last edited %s'), relative_time($item['edited'])) : ''),
|
||||
'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf(t('Expires %s'), relative_time($item['expires'])) : ''),
|
||||
'expiretime' => (($item['expires'] > DBA::$dba->get_null_date()) ? sprintf(t('Expires %s'), relative_time($item['expires'])) : ''),
|
||||
'lock' => $lock,
|
||||
'locktype' => $locktype,
|
||||
'delayed' => (($item['item_delayed']) ? sprintf(t('Published %s'), relative_time($item['created'])) : ''),
|
||||
|
||||
@@ -9,7 +9,7 @@ trait HelpHelperTrait {
|
||||
// PHP versions before 8.2 does not support trait constants,
|
||||
// Leave this commented out until we drop support for PHP 8.1.
|
||||
//
|
||||
// const VALID_FILE_EXT = ['md', 'bb', 'html'];
|
||||
// const VALID_FILE_EXT = ['md', 'bb', 'html', 'json'];
|
||||
|
||||
private string $file_name = '';
|
||||
private string $file_type = '';
|
||||
@@ -58,7 +58,7 @@ trait HelpHelperTrait {
|
||||
private function find_help_file(string $base_path, string $lang): void {
|
||||
|
||||
// Use local variable until we can use trait constants.
|
||||
$valid_file_ext = ['md', 'bb', 'html'];
|
||||
$valid_file_ext = ['md', 'bb', 'html', 'json'];
|
||||
|
||||
$base_path_with_lang = "doc/{$lang}/${base_path}";
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
require_once('include/account.php');
|
||||
@@ -90,7 +91,7 @@ class Admin extends \Zotlabs\Web\Controller {
|
||||
$r = q("SELECT COUNT(CASE WHEN account_id > 0 THEN 1 ELSE NULL END) 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),
|
||||
dbesc(DBA::$dba->get_null_date()),
|
||||
intval(ACCOUNT_BLOCKED)
|
||||
);
|
||||
if ($r) {
|
||||
|
||||
@@ -341,29 +341,27 @@ class Accounts {
|
||||
* @SuppressWarnings(PHPMD.ShortVariable)
|
||||
*/
|
||||
private function block_unblock_accounts(): void {
|
||||
if (!isset($_POST['user']) || !isset($_POST['blocked'])) {
|
||||
if (!isset($_POST['user'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$users = $_POST['user'];
|
||||
$blocked = $_POST['blocked'];
|
||||
|
||||
if (!is_array($users) || !is_array($blocked)) {
|
||||
if (!is_array($users)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($users as $i => $id) {
|
||||
// if account is blocked remove blocked bit-flag, otherwise add blocked bit-flag
|
||||
$op = $blocked[$i] ? '& ~' : '| ';
|
||||
$xor = db_getfunc('^');
|
||||
|
||||
q("UPDATE account SET account_flags = (account_flags $op%d) WHERE account_id = %d",
|
||||
foreach($users as $id) {
|
||||
q("UPDATE account SET account_flags = (account_flags $xor %d) WHERE account_id = %d",
|
||||
intval(ACCOUNT_BLOCKED),
|
||||
intval($id)
|
||||
);
|
||||
}
|
||||
|
||||
$count = count($users);
|
||||
$fmt = tt("%s account blocked/unblocked", "%s account blocked/unblocked", $count);
|
||||
$fmt = tt("%s account blocked/unblocked", "%s accounts blocked/unblocked", $count);
|
||||
notice(sprintf($fmt, $count));
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class Attach_edit extends Controller {
|
||||
$admin_delete = false;
|
||||
|
||||
$is_creator = (($creator == $observer_hash) ? true : false);
|
||||
$move = ((! $copy && ($folder !== $newfolder || (($single) ? $filename !== $newfilename : false))) ? true : false);
|
||||
$move = ((!$delete && !$copy && ($folder !== $newfolder || (($single) ? $filename !== $newfilename : false))) ? true : false);
|
||||
|
||||
$perms = get_all_perms($channel_id, $observer_hash);
|
||||
|
||||
|
||||
@@ -83,37 +83,31 @@ class Bookmarks extends \Zotlabs\Web\Controller {
|
||||
|
||||
$channel = \App::get_channel();
|
||||
|
||||
$o = '';
|
||||
|
||||
$o .= '<div class="generic-content-wrapper-styled">';
|
||||
|
||||
$o .= '<h3>' . t('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);
|
||||
$bookmarks = [];
|
||||
$x = menu_list(local_channel(), '', MENU_BOOKMARK);
|
||||
if ($x) {
|
||||
foreach ($x as $xx) {
|
||||
$y = menu_fetch($xx['menu_name'], local_channel(), get_observer_hash());
|
||||
$bookmarks[] = 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);
|
||||
$conn_bookmarks = [];
|
||||
$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());
|
||||
$conn_bookmarks[] = menu_render($y, '', true);
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '</div>';
|
||||
|
||||
return $o;
|
||||
|
||||
return replace_macros(get_markup_template('bookmarks.tpl'), [
|
||||
'$title1' => t('Bookmarks'),
|
||||
'$title2' => t('My Connections Bookmarks'),
|
||||
'$bookmarks' => $bookmarks,
|
||||
'$conn_bookmarks' => $conn_bookmarks,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
|
||||
class Changeaddr extends \Zotlabs\Web\Controller {
|
||||
|
||||
@@ -29,7 +30,7 @@ class Changeaddr extends \Zotlabs\Web\Controller {
|
||||
if(! ($x && $x['account']))
|
||||
return;
|
||||
|
||||
if($account['account_password_changed'] > NULL_DATE) {
|
||||
if($account['account_password_changed'] > DBA::$dba->get_null_date()) {
|
||||
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
|
||||
if($account['account_password_changed'] > $d1) {
|
||||
notice( t('Channel name changes are not allowed within 48 hours of changing the account password.') . EOL);
|
||||
|
||||
@@ -148,14 +148,14 @@ class Channel extends Controller {
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Posts and comments'),
|
||||
'href' => z_root() . '/feed/' . $which
|
||||
'href' => z_root() . '/feed/' . $which . '?top=0'
|
||||
]);
|
||||
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Only posts'),
|
||||
'href' => z_root() . '/feed/' . $which . '?f=&top=1'
|
||||
'href' => z_root() . '/feed/' . $which . '?top=1'
|
||||
]);
|
||||
|
||||
|
||||
@@ -243,7 +243,7 @@ class Channel extends Controller {
|
||||
// search terms header
|
||||
if ($search) {
|
||||
$o .= replace_macros(get_markup_template("section_title.tpl"), [
|
||||
'$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8')
|
||||
'$title' => t('Searching for:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8')
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -386,8 +386,8 @@ class Channel extends Controller {
|
||||
|
||||
if ($noscript_content || $load) {
|
||||
if ($mid) {
|
||||
$r = q("SELECT *, parent AS item_id from item where $identifier = '%s' and uid = %d $item_normal
|
||||
AND item_wall = 1 $permission_sql $sql_extra limit 1",
|
||||
$r = q("SELECT item.parent AS item_id, item.verb from item where $identifier = '%s' and item.uid = %d $item_normal
|
||||
AND item.item_wall = 1 $permission_sql $sql_extra limit 1",
|
||||
dbesc($mid),
|
||||
intval(App::$profile['profile_uid'])
|
||||
);
|
||||
@@ -396,7 +396,7 @@ class Channel extends Controller {
|
||||
}
|
||||
}
|
||||
else {
|
||||
$r = q("SELECT parent AS item_id, $ordering FROM item
|
||||
$r = q("SELECT item.parent AS item_id, item.verb, $ordering FROM item
|
||||
LEFT JOIN abook ON (item.author_xchan = abook.abook_xchan $abook_uids)
|
||||
WHERE item.uid = %d
|
||||
AND item.id = item.parent
|
||||
@@ -417,6 +417,11 @@ class Channel extends Controller {
|
||||
}
|
||||
}
|
||||
if ($r) {
|
||||
|
||||
// 11.08.2025 start transition deprecated AS1 item.verb vocabulary to AS2 on demand.
|
||||
// Keep this until we officially deprecate AS1 data.
|
||||
AS1_to_AS2_verbs($r);
|
||||
|
||||
$thr_parents = null;
|
||||
if ($mid) {
|
||||
$thr_parents = get_recursive_thr_parents($r[0]);
|
||||
@@ -439,7 +444,7 @@ class Channel extends Controller {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
$mode = (($search) ? 'search' : 'channel');
|
||||
$mode = 'channel';
|
||||
|
||||
if ((!$update) && (!$load)) {
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
use Zotlabs\Access\AccessList;
|
||||
@@ -300,7 +301,7 @@ class Channel_calendar extends Controller {
|
||||
from event left join item on item.resource_id = event.event_hash
|
||||
where event.uid = %d and event.dtstart > '%s' and event.dtend > event.dtstart",
|
||||
intval(local_channel()),
|
||||
dbesc(NULL_DATE)
|
||||
dbesc(DBA::$dba->get_null_date())
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
@@ -232,7 +233,7 @@ class Dirsearch extends Controller {
|
||||
$spkt = array('transactions' => array());
|
||||
|
||||
$r = q("SELECT * FROM updates WHERE ud_update = 0 AND ud_last = '%s' AND ud_date >= '%s' ORDER BY ud_date DESC",
|
||||
dbesc(NULL_DATE),
|
||||
dbesc(DBA::$dba->get_null_date()),
|
||||
dbesc($sync)
|
||||
);
|
||||
|
||||
|
||||
@@ -61,12 +61,10 @@ class Dreport extends \Zotlabs\Web\Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
$r = q("select * from dreport where dreport_xchan = '%s' and (dreport_mid = '%s' or dreport_mid = '%s' or dreport_mid = '%s' or dreport_mid = '%s')",
|
||||
$r = q("select * from dreport where dreport_xchan = '%s' and (dreport_mid = '%s' or dreport_mid = '%s')",
|
||||
dbesc($channel['channel_hash']),
|
||||
dbesc($mid),
|
||||
dbesc($mid . '#sync'),
|
||||
dbesc(str_replace('/item/', '/activity/', $mid)),
|
||||
dbesc(str_replace('/item/', '/activity/', $mid) . '#sync')
|
||||
dbesc(str_replace('/item/', '/activity/', $mid))
|
||||
);
|
||||
|
||||
if(! $r) {
|
||||
|
||||
@@ -122,10 +122,10 @@ class Editblock extends \Zotlabs\Web\Controller {
|
||||
'ptyp' => $itm[0]['type'],
|
||||
'mimeselect' => true,
|
||||
'mimetype' => $itm[0]['mimetype'],
|
||||
'body' => undo_post_tagging($content),
|
||||
'body' => htmlspecialchars_decode(undo_post_tagging($content), ENT_COMPAT),
|
||||
'post_id' => $post_id,
|
||||
'visitor' => true,
|
||||
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
|
||||
'title' => htmlspecialchars_decode($itm[0]['title'], ENT_COMPAT),
|
||||
'placeholdertitle' => t('Title (optional)'),
|
||||
'pagetitle' => $block_title,
|
||||
'profile_uid' => (intval($channel['channel_id'])),
|
||||
|
||||
@@ -121,9 +121,9 @@ class Editlayout extends \Zotlabs\Web\Controller {
|
||||
'hide_preview' => true,
|
||||
'disable_comments' => true,
|
||||
'ptyp' => $itm[0]['obj_type'],
|
||||
'body' => undo_post_tagging($itm[0]['body']),
|
||||
'body' => htmlspecialchars_decode(undo_post_tagging($itm[0]['body']), ENT_COMPAT),
|
||||
'post_id' => $post_id,
|
||||
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
|
||||
'title' => htmlspecialchars_decode($itm[0]['title'], ENT_COMPAT),
|
||||
'pagetitle' => $layout_title,
|
||||
'ptlabel' => t('Layout Name'),
|
||||
'placeholdertitle' => t('Layout Description (Optional)'),
|
||||
|
||||
@@ -144,7 +144,7 @@ class Editwebpage extends \Zotlabs\Web\Controller {
|
||||
'hide_location' => true,
|
||||
'hide_voting' => true,
|
||||
'ptyp' => $itm[0]['type'],
|
||||
'body' => undo_post_tagging($content),
|
||||
'body' => htmlspecialchars_decode(undo_post_tagging($content), ENT_COMPAT),
|
||||
'post_id' => $post_id,
|
||||
'visitor' => ($is_owner) ? true : false,
|
||||
'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')),
|
||||
@@ -154,7 +154,7 @@ class Editwebpage extends \Zotlabs\Web\Controller {
|
||||
'mimeselect' => true,
|
||||
'layout' => $layout,
|
||||
'layoutselect' => true,
|
||||
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
|
||||
'title' => htmlspecialchars_decode($itm[0]['title'], ENT_COMPAT),
|
||||
'lockstate' => (((strlen($itm[0]['allow_cid'])) || (strlen($itm[0]['allow_gid'])) || (strlen($itm[0]['deny_cid'])) || (strlen($itm[0]['deny_gid']))) ? 'lock' : 'unlock'),
|
||||
'profile_uid' => (intval($owner)),
|
||||
'bbcode' => (($mimetype == 'text/bbcode') ? true : false)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/security.php');
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
|
||||
class Embed extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
$post_id = ((argc() > 1) ? intval(argv(1)) : 0);
|
||||
|
||||
if(! $post_id)
|
||||
killme();
|
||||
|
||||
echo '[share=' . $post_id . '][/share]';
|
||||
killme();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,49 +2,47 @@
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\PConfig;
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
require_once('include/items.php');
|
||||
|
||||
class Feed extends \Zotlabs\Web\Controller {
|
||||
class Feed extends Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
$params = [];
|
||||
|
||||
$params['begin'] = ((x($_REQUEST,'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE);
|
||||
$params['end'] = ((x($_REQUEST,'date_end')) ? $_REQUEST['date_end'] : '');
|
||||
$params['type'] = ((stristr(argv(0),'json')) ? 'json' : 'xml');
|
||||
$params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0);
|
||||
$params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0);
|
||||
$params['start'] = ((x($_REQUEST,'start')) ? intval($_REQUEST['start']) : 0);
|
||||
$params['records'] = ((x($_REQUEST,'records')) ? intval($_REQUEST['records']) : 10);
|
||||
$params['direction'] = ((x($_REQUEST,'direction')) ? dbesc($_REQUEST['direction']) : 'desc');
|
||||
$params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : '');
|
||||
$params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 0);
|
||||
if (argc() < 2) {
|
||||
killme();
|
||||
}
|
||||
|
||||
if(! in_array($params['direction'],['asc','desc'])) {
|
||||
if (observer_prohibited(true)) {
|
||||
killme();
|
||||
}
|
||||
|
||||
$channel = channelx_by_nick(argv(1));
|
||||
if (!$channel) {
|
||||
killme();
|
||||
}
|
||||
|
||||
$params['begin'] = $_REQUEST['date_begin'] ?? DBA::$dba->get_null_date();
|
||||
$params['end'] = $_REQUEST['date_end'] ?? '';
|
||||
$params['type'] = 'xml';
|
||||
$params['pages'] = ((!empty($_REQUEST['pages'])) ? intval($_REQUEST['pages']) : 0);
|
||||
$params['top'] = ((array_key_exists('top', $_REQUEST)) ? intval($_REQUEST['top']) : PConfig::Get($channel['channel_id'], 'system', 'channel_simple_feed', 1));
|
||||
$params['start'] = ((!empty($_REQUEST['start'])) ? intval($_REQUEST['start']) : 0);
|
||||
$params['records'] = ((!empty($_REQUEST['records'])) ? intval($_REQUEST['records']) : 10);
|
||||
$params['cat'] = ((!empty($_REQUEST['cat'])) ? escape_tags($_REQUEST['cat']) : '');
|
||||
$params['compat'] = ((!empty($_REQUEST['compat'])) ? intval($_REQUEST['compat']) : 0);
|
||||
$params['direction'] = ((!empty($_REQUEST['direction'])) ? dbesc($_REQUEST['direction']) : 'desc');
|
||||
|
||||
if (!in_array($params['direction'], ['asc', 'desc'])) {
|
||||
$params['direction'] = 'desc';
|
||||
}
|
||||
|
||||
if(argc() > 1) {
|
||||
logger('public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $channel['channel_address']);
|
||||
|
||||
if(observer_prohibited(true)) {
|
||||
killme();
|
||||
}
|
||||
echo get_public_feed($channel, $params);
|
||||
|
||||
killme();
|
||||
|
||||
$channel = channelx_by_nick(argv(1));
|
||||
if(! $channel) {
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
logger('public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $channel['channel_address']);
|
||||
|
||||
echo get_public_feed($channel,$params);
|
||||
|
||||
killme();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,14 @@ class Follow extends Controller {
|
||||
}
|
||||
|
||||
$uid = local_channel();
|
||||
$url = notags(punify(trim($_REQUEST['url'])));
|
||||
$url = notags(trim($_REQUEST['url']));
|
||||
|
||||
$parsed = parse_url($url);
|
||||
if (isset($parsed['host'])) {
|
||||
$parsed['host'] = punify($parsed['host']);
|
||||
$url = unparse_url($parsed);
|
||||
}
|
||||
|
||||
$return_url = $_SESSION['return_url'];
|
||||
$interactive = $_REQUEST['interactive'] ?? 1;
|
||||
$channel = App::get_channel();
|
||||
|
||||
@@ -7,6 +7,7 @@ require_once('include/import.php');
|
||||
require_once('include/perm_upgrade.php');
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use URLify;
|
||||
use Zotlabs\Daemon\Master;
|
||||
use Zotlabs\Lib\Config;
|
||||
@@ -331,7 +332,7 @@ class Import extends Controller {
|
||||
else {
|
||||
$photos = import_xchan_photo($xchan['xchan_photo_l'], $xchan['xchan_hash']);
|
||||
if ($photos[4])
|
||||
$photodate = NULL_DATE;
|
||||
$photodate = DBA::$dba->get_null_date();
|
||||
else
|
||||
$photodate = $xchan['xchan_photo_date'];
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use URLify;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\IConfig;
|
||||
@@ -17,6 +18,7 @@ use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
use Zotlabs\Lib\ThreadListener;
|
||||
use Zotlabs\Access\PermissionRoles;
|
||||
use Zotlabs\Lib\ObjCache;
|
||||
|
||||
require_once('include/crypto.php');
|
||||
require_once('include/items.php');
|
||||
@@ -185,7 +187,8 @@ class Item extends Controller {
|
||||
$obj_type = ((!empty($_POST['obj_type'])) ? escape_tags($_POST['obj_type']) : 'Note');
|
||||
|
||||
// allow API to bulk load a bunch of imported items with sending out a bunch of posts.
|
||||
$nopush = ((!empty($_POST['nopush'])) ? intval($_POST['nopush']) : 0);
|
||||
$nopush = ((!empty($_POST['nopush'])) ? intval($_POST['nopush']) : $item_type !== ITEM_TYPE_POST);
|
||||
|
||||
|
||||
/*
|
||||
* Check service class limits
|
||||
@@ -207,7 +210,7 @@ class Item extends Controller {
|
||||
}
|
||||
|
||||
|
||||
$expires = NULL_DATE;
|
||||
$expires = DBA::$dba->get_null_date();
|
||||
|
||||
$route = '';
|
||||
$parent_item = null;
|
||||
@@ -557,7 +560,7 @@ class Item extends Controller {
|
||||
if (!empty($_POST['expire'])) {
|
||||
$expires = datetime_convert(date_default_timezone_get(), 'UTC', $_POST['expire']);
|
||||
if ($expires <= datetime_convert())
|
||||
$expires = NULL_DATE;
|
||||
$expires = DBA::$dba->get_null_date();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -755,7 +758,7 @@ class Item extends Controller {
|
||||
$cats = explode(',', $categories);
|
||||
foreach ($cats as $cat) {
|
||||
|
||||
$catlink = $owner_xchan['xchan_url'] . '?f=&cat=' . urlencode(trim($cat));
|
||||
$catlink = channel_url($channel) . '?cat=' . urlencode(trim($cat));
|
||||
|
||||
$post_tags[] = [
|
||||
'uid' => $profile_uid,
|
||||
@@ -799,7 +802,7 @@ class Item extends Controller {
|
||||
$item_origin = (($origin) ? 1 : 0);
|
||||
$item_consensus = (($consensus) ? 1 : 0);
|
||||
$item_nocomment = (($nocomment) ? 1 : 0);
|
||||
$comments_closed = (($nocomment) ? $comments_closed : NULL_DATE);
|
||||
$comments_closed = (($nocomment) ? $comments_closed : DBA::$dba->get_null_date());
|
||||
|
||||
// determine if this is a wall post
|
||||
|
||||
@@ -873,7 +876,7 @@ class Item extends Controller {
|
||||
|
||||
if ($obj['endTime']) {
|
||||
$d = datetime_convert('UTC','UTC', $obj['endTime']);
|
||||
if ($d > NULL_DATE) {
|
||||
if ($d > DBA::$dba->get_null_date()) {
|
||||
$comments_closed = $d;
|
||||
}
|
||||
}
|
||||
@@ -1041,23 +1044,16 @@ class Item extends Controller {
|
||||
|
||||
$x = item_store_update($datarray, $execflag);
|
||||
|
||||
if ($x['success']) {
|
||||
if ($x['success'] && intval($item_type) === ITEM_TYPE_POST) {
|
||||
$item = [$x['item']];
|
||||
xchan_query($item);
|
||||
$item = fetch_post_tags($item);
|
||||
$encoded_item = Activity::build_packet(Activity::encode_activity($item[0]), $channel, false);
|
||||
ObjCache::Set($item[0]['mid'], $encoded_item);
|
||||
|
||||
$this->add_listeners($datarray);
|
||||
}
|
||||
|
||||
/* sync this is done in item_store_update()
|
||||
if (!$parent) {
|
||||
$r = q("select * from item where id = %d",
|
||||
intval($post_id)
|
||||
);
|
||||
if ($r) {
|
||||
xchan_query($r);
|
||||
$sync_item = fetch_post_tags($r);
|
||||
Libsync::build_sync_packet($profile_uid, ['item' => [encode_item($sync_item[0], true)]]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (!$nopush) {
|
||||
Master::Summon(['Notifier', 'edit_post', $post_id]);
|
||||
if (intval($x['approval_id'])) {
|
||||
@@ -1080,9 +1076,17 @@ class Item extends Controller {
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
$post = item_store($datarray, $execflag);
|
||||
|
||||
if ($post['success']) {
|
||||
if ($post['success'] && intval($item_type) === ITEM_TYPE_POST) {
|
||||
$item = [$post['item']];
|
||||
xchan_query($item);
|
||||
// TODO: fetch_post_tags() will add term and iconfig twice if called twice and it looks like they are already added here
|
||||
//$item = fetch_post_tags($item);
|
||||
$encoded_item = Activity::build_packet(Activity::encode_activity($item[0]), $channel, false);
|
||||
ObjCache::Set($item[0]['mid'], $encoded_item);
|
||||
|
||||
$this->add_listeners($datarray);
|
||||
}
|
||||
|
||||
@@ -1162,19 +1166,6 @@ class Item extends Controller {
|
||||
killme();
|
||||
}
|
||||
|
||||
/* sync this is done in item_store_update()
|
||||
if ($parent || $datarray['item_private'] == 1) {
|
||||
$r = q("select * from item where id = %d",
|
||||
intval($post_id)
|
||||
);
|
||||
if ($r) {
|
||||
xchan_query($r);
|
||||
$sync_item = fetch_post_tags($r);
|
||||
Libsync::build_sync_packet($profile_uid, ['item' => [encode_item($sync_item[0], true)]]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
$datarray['id'] = $post_id;
|
||||
$datarray['llink'] = z_root() . '/display/' . $datarray['uuid'];
|
||||
|
||||
@@ -1213,11 +1204,6 @@ class Item extends Controller {
|
||||
if ($mode === 'channel')
|
||||
profile_load($channel['channel_address']);
|
||||
|
||||
$item[] = $datarray;
|
||||
$item[0]['owner'] = $owner_xchan;
|
||||
$item[0]['author'] = $observer;
|
||||
$item[0]['attach'] = $datarray['attach'];
|
||||
|
||||
$json = [
|
||||
'success' => 1,
|
||||
'id' => $post_id,
|
||||
|
||||
@@ -433,7 +433,7 @@ class Like extends Controller {
|
||||
$arr['item_wall'] = 1;
|
||||
}
|
||||
else {
|
||||
switch ($item['object_type']) {
|
||||
switch ($item['obj_type']) {
|
||||
case 'Image':
|
||||
$post_type = t('image');
|
||||
break;
|
||||
|
||||
@@ -110,7 +110,7 @@ class Lockview extends Controller {
|
||||
// as unknown specific recipients. The sender will have the visibility list and will fall through to the
|
||||
// next section.
|
||||
|
||||
echo '<div class="dropdown-item-text">' . translate_scope((!$item['public_policy']) ? 'specific' : $item['public_policy']) . '</div>';
|
||||
echo '<div class="dropdown-item-text">' . escape_tags(translate_scope((!$item['public_policy']) ? 'specific' : $item['public_policy'])) . '</div>';
|
||||
killme();
|
||||
}
|
||||
|
||||
@@ -232,25 +232,17 @@ class Lockview extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
$access_list_header = '<div class="dropdown-header text-uppercase h6">' . t('Access') . '</div>';
|
||||
$guest_access_list_header = '<div class="dropdown-header text-uppercase h6">' . t('Guest access') . '</div>';
|
||||
$ocap_access_list_header = '<div class="dropdown-header text-uppercase h6">' . t('OCAP access') . '</div>';
|
||||
$divider = '<div class="dropdown-divider"></div>';
|
||||
$str = '';
|
||||
$tpl = get_markup_template('access_dropdown.tpl');
|
||||
|
||||
if ($access_list) {
|
||||
$str .= $access_list_header . implode($access_list);
|
||||
}
|
||||
echo replace_macros($tpl, [
|
||||
'$access_header' => t('Access'),
|
||||
'$guest_access_header' => t('Guest access'),
|
||||
'$ocap_access_header' => t('OCAP access'),
|
||||
|
||||
if ($guest_access_list) {
|
||||
$str .= $divider . $guest_access_list_header . implode($guest_access_list);
|
||||
}
|
||||
|
||||
if ($ocap_access_list) {
|
||||
$str .= $divider . $ocap_access_list_header . implode($ocap_access_list);
|
||||
}
|
||||
|
||||
echo $str;
|
||||
'$access_list' => $access_list ? implode($access_list) : '',
|
||||
'$guest_access_list' => $guest_access_list ? implode($guest_access_list) : '',
|
||||
'$ocap_access_list' => $ocap_access_list ? implode($ocap_access_list) : '',
|
||||
]);
|
||||
killme();
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Web\HTTPSig;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\SConfig;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use HttpSignature\HttpMessageSigner;
|
||||
|
||||
class Magic extends Controller {
|
||||
|
||||
@@ -101,26 +103,65 @@ class Magic extends Controller {
|
||||
$dest = strip_zids($dest);
|
||||
$dest = strip_query_param($dest,'f');
|
||||
|
||||
// We now post to the OWA endpoint. This improves security by providing a signed digest
|
||||
// try RFC9421 first
|
||||
|
||||
$data = json_encode([ 'OpenWebAuth' => random_string() ]);
|
||||
$request = new Request(
|
||||
'GET',
|
||||
$owapath,
|
||||
[
|
||||
'Host' => $parsed['host'],
|
||||
'Date' => gmdate('D, d M Y H:i:s T'),
|
||||
'Accept' => 'application/x-zot+json',
|
||||
'X-Open-Web-Auth' => random_string(),
|
||||
],
|
||||
);
|
||||
|
||||
$headers = [];
|
||||
$headers['Accept'] = 'application/x-zot+json' ;
|
||||
$headers['Content-Type'] = 'application/x-zot+json' ;
|
||||
$headers['X-Open-Web-Auth'] = random_string();
|
||||
$headers['Host'] = $parsed['host'];
|
||||
$headers['(request-target)'] = 'get /owa';
|
||||
$signer = new HttpMessageSigner();
|
||||
|
||||
$signer->setPrivateKey($channel['channel_prvkey']);
|
||||
$signer->setAlgorithm('rsa-v1_5-sha256');
|
||||
$signer->setKeyId(channel_url($channel));
|
||||
$signer->setCreated(time());
|
||||
$signer->setExpires(time() + 3600);
|
||||
|
||||
$coveredFields = '("@method" "@target-uri" "host" "date" "accept" "x-open-web-auth")';
|
||||
$request = $signer->signRequest($coveredFields, $request);
|
||||
$signedHeaders = $signer->getHeaders($request);
|
||||
|
||||
$curlHeaders = [];
|
||||
foreach ($signedHeaders as $key => $value) {
|
||||
$curlHeaders[] = $key . ': ' . $value;
|
||||
}
|
||||
|
||||
$headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512');
|
||||
$redirects = 0;
|
||||
$x = z_fetch_url($owapath, false, $redirects, ['headers' => $curlHeaders]);
|
||||
logger('owa RFC9421 fetch returned: ' . print_r($x,true),LOGGER_DATA);
|
||||
|
||||
$x = z_fetch_url($owapath, false, $redirects, ['headers' => $headers]);
|
||||
$rfc9421 = false;
|
||||
|
||||
logger('owa fetch returned: ' . print_r($x,true),LOGGER_DATA);
|
||||
if ($x['success']) {
|
||||
$rfc9421_result = json_decode($x['body'], true);
|
||||
$rfc9421 = $rfc9421_result['success'];
|
||||
}
|
||||
|
||||
if (!$rfc9421 || ($x['return_code'] >= 400 && $x['return_code'] != 404)) {
|
||||
$headers = [];
|
||||
$headers['Accept'] = 'application/x-zot+json' ;
|
||||
$headers['Content-Type'] = 'application/x-zot+json' ;
|
||||
$headers['X-Open-Web-Auth'] = random_string();
|
||||
$headers['Host'] = $parsed['host'];
|
||||
$headers['(request-target)'] = 'get /owa';
|
||||
|
||||
$headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512');
|
||||
$redirects = 0;
|
||||
|
||||
$x = z_fetch_url($owapath, false, $redirects, ['headers' => $headers]);
|
||||
logger('owa fetch returned: ' . print_r($x,true),LOGGER_DATA);
|
||||
}
|
||||
|
||||
if ($x['success']) {
|
||||
$j = json_decode($x['body'],true);
|
||||
|
||||
if ($j['success'] && $j['encrypted_token']) {
|
||||
// decrypt the token using our private key
|
||||
$token = '';
|
||||
@@ -139,7 +180,6 @@ class Magic extends Controller {
|
||||
|
||||
echo $o;
|
||||
killme();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,6 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
|
||||
$status_editor = '';
|
||||
|
||||
|
||||
if (Apps::system_app_installed(local_channel(), 'Affinity Tool')) {
|
||||
$affinity_locked = intval(get_pconfig(local_channel(), 'affinity', 'lock', 1));
|
||||
if ($affinity_locked) {
|
||||
@@ -144,8 +143,11 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
}
|
||||
|
||||
if(x($_GET, 'search') || $file || (!$pf && $cid) || $hashtags || $verb || $category || $conv || $unseen)
|
||||
if($search || $file || (!$pf && $cid) || $hashtags || $verb || $category || $conv || $unseen) {
|
||||
$nouveau = true;
|
||||
}
|
||||
|
||||
$dismiss_privacy_filter = array_intersect(['cid', 'star', 'conv', 'file', 'verb', 'cat', 'search'], array_keys($_GET));
|
||||
|
||||
$cid_r = [];
|
||||
|
||||
@@ -273,18 +275,6 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
// This is for nouveau view cid queries (not a public forum)
|
||||
$sql_extra = " AND author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' ";
|
||||
}
|
||||
elseif($pf && $unseen && $nouveau) {
|
||||
|
||||
$vnotify = get_pconfig(local_channel(), 'system', 'vnotify');
|
||||
$likes_sql = '';
|
||||
if (!($vnotify & VNOTIFY_LIKE)) {
|
||||
$likes_sql = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
|
||||
}
|
||||
|
||||
// This is for nouveau view public forum cid queries (if a forum notification is clicked)
|
||||
$sql_extra = " AND item.parent IN (SELECT DISTINCT parent FROM item WHERE uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' OR owner_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' ) $item_normal) AND item_unseen = 1 AND verb != 'Announce' $likes_sql ";
|
||||
|
||||
}
|
||||
else {
|
||||
// This is for threaded view cid queries (e.g. if a forum is selected from the forum filter)
|
||||
$sql_extra = " AND item.parent IN (SELECT DISTINCT parent FROM item WHERE uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' OR owner_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' ) $item_normal) ";
|
||||
@@ -359,15 +349,15 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
// The name 'verb' is a holdover from the earlier XML
|
||||
// ActivityStreams specification.
|
||||
|
||||
if (substr($verb, 0, 1) === '.') {
|
||||
if (str_starts_with($verb, '.')) {
|
||||
$sql_verb = substr($verb, 1);
|
||||
$sql_extra .= sprintf(" AND item.obj_type like '%s' ",
|
||||
dbesc(protect_sprintf('%' . $sql_verb . '%'))
|
||||
$sql_extra .= sprintf(" AND item.obj_type = '%s' AND item.verb IN ('Create', 'Update', 'Invite') ",
|
||||
dbesc(protect_sprintf($sql_verb))
|
||||
);
|
||||
}
|
||||
else {
|
||||
$sql_extra .= sprintf(" AND item.verb like '%s' ",
|
||||
dbesc(protect_sprintf('%' . $verb . '%'))
|
||||
$sql_extra .= sprintf(" AND item.verb = '%s' ",
|
||||
dbesc(protect_sprintf($verb))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -376,30 +366,23 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
$sql_extra .= term_query('item', $file, TERM_FILE);
|
||||
}
|
||||
|
||||
if ($dm) {
|
||||
$sql_extra .= ' AND item.item_private = 2 ';
|
||||
if (!$dismiss_privacy_filter) {
|
||||
if ($dm) {
|
||||
$sql_extra .= ' AND item.item_private = 2 ';
|
||||
}
|
||||
else {
|
||||
$sql_extra .= ' AND item.item_private IN (0, 1) ';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$sql_extra .= ' AND item.item_private IN (0, 1) ';
|
||||
}
|
||||
|
||||
|
||||
if($conv) {
|
||||
$item_thread_top = '';
|
||||
$sql_extra .= " AND ( author_xchan = '" . dbesc($channel['channel_hash']) . "' OR item_mentionsme = 1 ) ";
|
||||
}
|
||||
|
||||
if($update && ! $load) {
|
||||
|
||||
// only setup pagination on initial page view
|
||||
$pager_sql = '';
|
||||
|
||||
}
|
||||
else {
|
||||
$itemspage = get_pconfig(local_channel(), 'system', 'itemspage');
|
||||
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10));
|
||||
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start']));
|
||||
}
|
||||
$itemspage = get_pconfig(local_channel(), 'system', 'itemspage');
|
||||
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10));
|
||||
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start']));
|
||||
|
||||
// cmin and cmax are both -1 when the affinity tool is disabled
|
||||
|
||||
@@ -436,8 +419,6 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
$page_mode = 'list';
|
||||
}
|
||||
|
||||
$parents_str = '';
|
||||
|
||||
// This fixes a very subtle bug so I'd better explain it. You wake up in the morning or return after a day
|
||||
// or three and look at your matrix page - after opening up your browser. The first page loads just as it
|
||||
// should. All of a sudden a few seconds later, page 2 will get inserted at the beginning of the page
|
||||
@@ -469,10 +450,6 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
ORDER BY item.created DESC $pager_sql "
|
||||
);
|
||||
|
||||
$parents_str = ids_to_querystr($items, 'item_id');
|
||||
|
||||
require_once('include/items.php');
|
||||
|
||||
xchan_query($items);
|
||||
|
||||
$items = fetch_post_tags($items, true);
|
||||
|
||||
@@ -33,7 +33,7 @@ class New_channel extends \Zotlabs\Web\Controller {
|
||||
// first name
|
||||
if(strpos($x,' '))
|
||||
$test[] = legal_webbie(substr($x,0,strpos($x,' ')));
|
||||
if($test[0]) {
|
||||
if (!empty($test[0])) {
|
||||
// first name plus first initial of last
|
||||
$test[] = ((strpos($x,' ')) ? $test[0] . legal_webbie(trim(substr($x,strpos($x,' '),2))) : '');
|
||||
// first name plus random number
|
||||
@@ -69,7 +69,7 @@ class New_channel extends \Zotlabs\Web\Controller {
|
||||
// first name
|
||||
if(strpos($x,' '))
|
||||
$test[] = legal_webbie(substr($x,0,strpos($x,' ')));
|
||||
if($test[0]) {
|
||||
if (!empty($test[0])) {
|
||||
// first name plus first initial of last
|
||||
$test[] = ((strpos($x,' ')) ? $test[0] . legal_webbie(trim(substr($x,strpos($x,' '),2))) : '');
|
||||
// first name plus random number
|
||||
|
||||
@@ -12,51 +12,72 @@ class Notifications extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
|
||||
// ajax mark all unseen items read
|
||||
if(x($_REQUEST, 'markRead')) {
|
||||
if(isset($_REQUEST['markRead'])) {
|
||||
if (str_starts_with($_REQUEST['markRead'], 'forum_')) {
|
||||
$forum_id = substr($_REQUEST['markRead'], 6);
|
||||
|
||||
$abook = q("SELECT abook_xchan FROM abook WHERE abook_channel = %d AND abook_id = %d",
|
||||
intval(local_channel()),
|
||||
intval($forum_id)
|
||||
);
|
||||
|
||||
if ($abook) {
|
||||
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND owner_xchan = '%s' AND item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1)",
|
||||
intval(local_channel()),
|
||||
dbesc($abook[0]['abook_xchan'])
|
||||
);
|
||||
}
|
||||
|
||||
killme();
|
||||
}
|
||||
|
||||
switch($_REQUEST['markRead']) {
|
||||
case 'dm':
|
||||
$r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_private = 2",
|
||||
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_private = 2",
|
||||
intval(local_channel())
|
||||
);
|
||||
break;
|
||||
case 'network':
|
||||
$r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_private IN (0, 1)",
|
||||
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1)",
|
||||
intval(local_channel())
|
||||
);
|
||||
break;
|
||||
case 'home':
|
||||
$r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1)",
|
||||
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1)",
|
||||
intval(local_channel())
|
||||
);
|
||||
break;
|
||||
case 'all_events':
|
||||
$evdays = intval(get_pconfig(local_channel(), 'system', 'evdays', 3));
|
||||
$r = q("UPDATE event SET dismissed = 1 WHERE uid = %d AND dismissed = 0 AND dtstart < '%s' AND dtstart > '%s' ",
|
||||
q("UPDATE event SET dismissed = 1 WHERE uid = %d AND dismissed = 0 AND dtstart < '%s' AND dtstart > '%s' ",
|
||||
intval(local_channel()),
|
||||
dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')),
|
||||
dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days'))
|
||||
);
|
||||
break;
|
||||
case 'notify':
|
||||
$r = q("UPDATE notify SET seen = 1 WHERE seen = 0 AND uid = %d",
|
||||
q("UPDATE notify SET seen = 1 WHERE seen = 0 AND uid = %d",
|
||||
intval(local_channel())
|
||||
);
|
||||
break;
|
||||
case 'pubs':
|
||||
unset($_SESSION['static_loadtime']);
|
||||
$_SESSION['sse_loadtime'] = datetime_convert();
|
||||
$_SESSION['static_loadtime'] = datetime_convert();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
killme();
|
||||
}
|
||||
|
||||
// ajax mark all comments of a parent item read
|
||||
if(x($_REQUEST, 'markItemRead') && local_channel()) {
|
||||
if(isset($_REQUEST['markItemRead']) && local_channel()) {
|
||||
$r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND parent = %d",
|
||||
intval(local_channel()),
|
||||
intval($_REQUEST['markItemRead'])
|
||||
);
|
||||
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/contact_widgets.php');
|
||||
require_once('include/items.php');
|
||||
require_once("include/bbcode.php");
|
||||
require_once('include/security.php');
|
||||
require_once('include/conversation.php');
|
||||
require_once('include/acl_selectors.php');
|
||||
require_once('include/permissions.php');
|
||||
|
||||
/**
|
||||
* @brief Channel Controller for broken OStatus implementations
|
||||
*
|
||||
*/
|
||||
class Ochannel extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
$which = null;
|
||||
if(argc() > 1)
|
||||
$which = argv(1);
|
||||
if(! $which) {
|
||||
if(local_channel()) {
|
||||
$channel = \App::get_channel();
|
||||
if($channel && $channel['channel_address'])
|
||||
$which = $channel['channel_address'];
|
||||
}
|
||||
}
|
||||
if(! $which) {
|
||||
notice( t('You must be logged in to see this page.') . EOL );
|
||||
return;
|
||||
}
|
||||
|
||||
$profile = 0;
|
||||
$channel = \App::get_channel();
|
||||
|
||||
if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) {
|
||||
$which = $channel['channel_address'];
|
||||
$profile = argv(1);
|
||||
}
|
||||
|
||||
head_add_link( [
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'href' => z_root() . '/ofeed/' . $which
|
||||
]);
|
||||
|
||||
|
||||
// Run profile_load() here to make sure the theme is set before
|
||||
// we start loading content
|
||||
|
||||
profile_load($which,$profile);
|
||||
}
|
||||
|
||||
function get($update = 0, $load = false) {
|
||||
|
||||
if(argc() < 2)
|
||||
return;
|
||||
|
||||
if($load)
|
||||
$_SESSION['loadtime'] = datetime_convert();
|
||||
|
||||
return '<script>window.location.href = "' . z_root() . '/' . str_replace('ochannel/','channel/',\App::$query_string) . '";</script>';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
/* Ofeed: Broken feed for software which requires broken feeds */
|
||||
|
||||
require_once('include/items.php');
|
||||
|
||||
class Ofeed extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
$params = [];
|
||||
|
||||
$params['begin'] = ((x($_REQUEST,'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE);
|
||||
$params['end'] = ((x($_REQUEST,'date_end')) ? $_REQUEST['date_end'] : '');
|
||||
$params['type'] = ((stristr(argv(0),'json')) ? 'json' : 'xml');
|
||||
$params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0);
|
||||
$params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0);
|
||||
$params['start'] = ((x($_REQUEST,'start')) ? intval($_REQUEST['start']) : 0);
|
||||
$params['records'] = ((x($_REQUEST,'records')) ? intval($_REQUEST['records']) : 10);
|
||||
$params['direction'] = ((x($_REQUEST,'direction')) ? dbesc($_REQUEST['direction']) : 'desc');
|
||||
$params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : '');
|
||||
$params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 1);
|
||||
|
||||
if(! in_array($params['direction'],['asc','desc'])) {
|
||||
$params['direction'] = 'desc';
|
||||
}
|
||||
|
||||
if(argc() > 1) {
|
||||
|
||||
if(observer_prohibited(true)) {
|
||||
killme();
|
||||
}
|
||||
|
||||
$channel = channelx_by_nick(argv(1));
|
||||
if(! $channel) {
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
logger('public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $channel['channel_address']);
|
||||
|
||||
echo get_public_feed($channel,$params);
|
||||
|
||||
killme();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\Config;
|
||||
@@ -47,7 +48,7 @@ class Outbox extends Controller {
|
||||
|
||||
$params = [];
|
||||
|
||||
$params['begin'] = ((x($_REQUEST, 'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE);
|
||||
$params['begin'] = ((x($_REQUEST, 'date_begin')) ? $_REQUEST['date_begin'] : DBA::$dba->get_null_date());
|
||||
$params['end'] = ((x($_REQUEST, 'date_end')) ? $_REQUEST['date_end'] : '');
|
||||
$params['type'] = 'json';
|
||||
$params['pages'] = ((x($_REQUEST, 'pages')) ? intval($_REQUEST['pages']) : 0);
|
||||
|
||||
@@ -19,96 +19,128 @@ use Zotlabs\Web\Controller;
|
||||
class Owa extends Controller {
|
||||
|
||||
public function init(): void
|
||||
{
|
||||
{
|
||||
|
||||
$ret = [ 'success' => false ];
|
||||
|
||||
if (!$this->validateAuthorizationHeader()) {
|
||||
$this->error('Missing or invalid authorization header.');
|
||||
// try OpenWebAuth over RFC9421
|
||||
|
||||
$sigdata = HTTPSig::verify(EMPTY_STR);
|
||||
|
||||
if ($sigdata && $sigdata['portable_id'] && $sigdata['header_valid']) {
|
||||
$portable_id = $sigdata['portable_id'];
|
||||
|
||||
if (!check_channelallowed($portable_id)) {
|
||||
json_return_and_die($ret, 'application/x-zot+json');
|
||||
}
|
||||
|
||||
if (!check_siteallowed($sigdata['signer'])) {
|
||||
json_return_and_die($ret, 'application/x-zot+json');
|
||||
}
|
||||
|
||||
$hubs = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE hubloc_hash = '%s' ORDER BY hubloc_id DESC",
|
||||
dbesc($portable_id)
|
||||
);
|
||||
|
||||
if ($hubs) {
|
||||
logger('OWA RFC9421 success: ' . $hubs[0]['hubloc_id_url'], LOGGER_DATA);
|
||||
$ret['success'] = true;
|
||||
$token = random_string(32);
|
||||
Verify::create('owt', 0, $token, $hubs[0]['hubloc_id_url']);
|
||||
$result = '';
|
||||
openssl_public_encrypt($token, $result, $hubs[0]['xchan_pubkey']);
|
||||
$ret['encrypted_token'] = base64url_encode($result);
|
||||
}
|
||||
}
|
||||
|
||||
$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_REMOTE_USER'];
|
||||
|
||||
$sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']);
|
||||
if ($sigblock) {
|
||||
$keyId = $sigblock['keyId'];
|
||||
$parsed = parse_url($keyId);
|
||||
if (str_starts_with($parsed['scheme'],'http')) {
|
||||
unset($parsed['fragment']);
|
||||
unset($parsed['query']);
|
||||
$keyId = unparse_url($parsed);
|
||||
else {
|
||||
if (!$this->validateAuthorizationHeader()) {
|
||||
$this->error('Missing or invalid authorization header.');
|
||||
}
|
||||
else {
|
||||
$keyId = str_replace('acct:', '', $keyId);
|
||||
}
|
||||
if ($keyId) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s')
|
||||
AND hubloc_deleted = 0 AND xchan_pubkey != ''
|
||||
ORDER BY hubloc_id DESC",
|
||||
dbesc($keyId),
|
||||
dbesc($keyId),
|
||||
dbesc($keyId)
|
||||
);
|
||||
if (! $r) {
|
||||
$found = discover_by_webbie($keyId);
|
||||
logger('found = ' . print_r($found, true));
|
||||
if ($found) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') AND hubloc_deleted = 0 AND xchan_pubkey != '' ORDER BY hubloc_id DESC ",
|
||||
dbesc($keyId),
|
||||
dbesc($keyId),
|
||||
dbesc($keyId)
|
||||
);
|
||||
}
|
||||
|
||||
$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_REMOTE_USER'];
|
||||
|
||||
$sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']);
|
||||
if ($sigblock) {
|
||||
$keyId = $sigblock['keyId'];
|
||||
$parsed = parse_url($keyId);
|
||||
if (str_starts_with($parsed['scheme'],'http')) {
|
||||
unset($parsed['fragment']);
|
||||
unset($parsed['query']);
|
||||
$keyId = unparse_url($parsed);
|
||||
}
|
||||
|
||||
if ($r) {
|
||||
foreach ($r as $hubloc) {
|
||||
$verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']);
|
||||
if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) {
|
||||
logger('OWA header: ' . print_r($verified,true),LOGGER_DATA);
|
||||
logger('OWA success: ' . $hubloc['hubloc_id_url'],LOGGER_DATA);
|
||||
$ret['success'] = true;
|
||||
$token = random_string(32);
|
||||
Verify::create('owt',0,$token,$hubloc['hubloc_id_url']);
|
||||
$result = '';
|
||||
openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']);
|
||||
$ret['encrypted_token'] = base64url_encode($result);
|
||||
break;
|
||||
} else {
|
||||
logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']);
|
||||
else {
|
||||
$keyId = str_replace('acct:', '', $keyId);
|
||||
}
|
||||
if ($keyId) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s')
|
||||
AND hubloc_deleted = 0 AND xchan_pubkey != ''
|
||||
ORDER BY hubloc_id DESC",
|
||||
dbesc($keyId),
|
||||
dbesc($keyId),
|
||||
dbesc($keyId)
|
||||
);
|
||||
if (! $r) {
|
||||
$found = discover_by_webbie($keyId);
|
||||
logger('found = ' . print_r($found, true));
|
||||
if ($found) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') AND hubloc_deleted = 0 AND xchan_pubkey != '' ORDER BY hubloc_id DESC ",
|
||||
dbesc($keyId),
|
||||
dbesc($keyId),
|
||||
dbesc($keyId)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$ret['success']) {
|
||||
if ($r) {
|
||||
foreach ($r as $hubloc) {
|
||||
$verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']);
|
||||
if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) {
|
||||
logger('OWA header: ' . print_r($verified,true),LOGGER_DATA);
|
||||
logger('OWA success: ' . $hubloc['hubloc_id_url'],LOGGER_DATA);
|
||||
$ret['success'] = true;
|
||||
$token = random_string(32);
|
||||
Verify::create('owt',0,$token,$hubloc['hubloc_id_url']);
|
||||
$result = '';
|
||||
openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']);
|
||||
$ret['encrypted_token'] = base64url_encode($result);
|
||||
break;
|
||||
} else {
|
||||
logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']);
|
||||
}
|
||||
}
|
||||
|
||||
// Possible a reinstall?
|
||||
// In this case we probably already have an old hubloc
|
||||
// but not the new one yet.
|
||||
if (!$ret['success']) {
|
||||
|
||||
$found = discover_by_webbie($keyId);
|
||||
// Possible a reinstall?
|
||||
// In this case we probably already have an old hubloc
|
||||
// but not the new one yet.
|
||||
|
||||
if ($found) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s') AND hubloc_deleted = 0 ORDER BY hubloc_id DESC LIMIT 1",
|
||||
dbesc(str_replace('acct:', '', $keyId)),
|
||||
dbesc($keyId)
|
||||
);
|
||||
$found = discover_by_webbie($keyId);
|
||||
|
||||
if ($r) {
|
||||
$verified = HTTPSig::verify(file_get_contents('php://input'), $r[0]['xchan_pubkey']);
|
||||
if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) {
|
||||
logger('OWA header: ' . print_r($verified,true), LOGGER_DATA);
|
||||
logger('OWA success: ' . $r[0]['hubloc_id_url'], LOGGER_DATA);
|
||||
$ret['success'] = true;
|
||||
$token = random_string(32);
|
||||
Verify::create('owt', 0, $token, $r[0]['hubloc_id_url']);
|
||||
$result = '';
|
||||
openssl_public_encrypt($token, $result, $r[0]['xchan_pubkey']);
|
||||
$ret['encrypted_token'] = base64url_encode($result);
|
||||
} else {
|
||||
logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']);
|
||||
if ($found) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s') AND hubloc_deleted = 0 ORDER BY hubloc_id DESC LIMIT 1",
|
||||
dbesc(str_replace('acct:', '', $keyId)),
|
||||
dbesc($keyId)
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
$verified = HTTPSig::verify(file_get_contents('php://input'), $r[0]['xchan_pubkey']);
|
||||
if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) {
|
||||
logger('OWA header: ' . print_r($verified,true), LOGGER_DATA);
|
||||
logger('OWA success: ' . $r[0]['hubloc_id_url'], LOGGER_DATA);
|
||||
$ret['success'] = true;
|
||||
$token = random_string(32);
|
||||
Verify::create('owt', 0, $token, $r[0]['hubloc_id_url']);
|
||||
$result = '';
|
||||
openssl_public_encrypt($token, $result, $r[0]['xchan_pubkey']);
|
||||
$ret['encrypted_token'] = base64url_encode($result);
|
||||
} else {
|
||||
logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
79
Zotlabs/Module/Perfstats.php
Normal file
79
Zotlabs/Module/Perfstats.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/* Handler for perfstats requests.
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\DbStats;
|
||||
use Zotlabs\Lib\Queue;
|
||||
use Zotlabs\Lib\QueueWorkerStats;
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
/**
|
||||
* Controller for the `/perfstats` module.
|
||||
*
|
||||
* Collects various performance stats for the site, and reponds with the stats
|
||||
* as a json array.
|
||||
*/
|
||||
class Perfstats extends Controller
|
||||
{
|
||||
public function init(): void {
|
||||
//
|
||||
// We only accept GET requests
|
||||
//
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
http_status_exit(400, 'Unsupported method');
|
||||
}
|
||||
|
||||
//
|
||||
// We only accept json requests
|
||||
//
|
||||
if (getBestSupportedMimeType(['application/json']) === null) {
|
||||
http_status_exit(400, 'No supported format');
|
||||
}
|
||||
|
||||
//
|
||||
// Only admins should be given access
|
||||
//
|
||||
if (!is_site_admin()) {
|
||||
http_status(401, 'Access denied');
|
||||
json_return_and_die(['error' => 'access denied']);
|
||||
}
|
||||
|
||||
$data = $this->getStats();
|
||||
json_return_and_die($data);
|
||||
}
|
||||
|
||||
private function getStats(): array {
|
||||
$stats = [];
|
||||
|
||||
if (function_exists('sys_getloadavg')) {
|
||||
$stats['loadavg'] = sys_getloadavg();
|
||||
}
|
||||
|
||||
$stats['dbqueries'] = $this->getNumQueries();
|
||||
$stats['outqueue'] = Queue::count();
|
||||
|
||||
$qwstats = new QueueWorkerStats();
|
||||
$stats['queueworkers'] = $qwstats->active;
|
||||
$stats['workqsz'] = $qwstats->size;
|
||||
|
||||
// Return a timestamp, so that it is possible to infer
|
||||
// changes of the stats over time. A resolution of
|
||||
// seconds should be good enough for our purposes.
|
||||
$stats['ts'] = time();
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
private function getNumQueries(): int {
|
||||
$stats = DbStats::getStats();
|
||||
return $stats->getQueries();
|
||||
}
|
||||
}
|
||||
@@ -60,26 +60,16 @@ class Profile extends Controller {
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Posts and comments'),
|
||||
'href' => z_root() . '/feed/' . $which
|
||||
'href' => z_root() . '/feed/' . $which . '?top=0'
|
||||
]);
|
||||
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Only posts'),
|
||||
'href' => z_root() . '/feed/' . $which . '?f=&top=1'
|
||||
'href' => z_root() . '/feed/' . $which . '?top=1'
|
||||
]);
|
||||
|
||||
|
||||
if (!$profile) {
|
||||
$x = q("select channel_id as profile_uid from channel where channel_address = '%s' limit 1",
|
||||
dbesc(argv(1))
|
||||
);
|
||||
if ($x) {
|
||||
App::$profile = $x[0];
|
||||
}
|
||||
}
|
||||
|
||||
profile_load($which, $profile);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
@@ -340,7 +341,7 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
$with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : '');
|
||||
|
||||
if(! strlen($howlong))
|
||||
$howlong = NULL_DATE;
|
||||
$howlong = DBA::$dba->get_null_date();
|
||||
else
|
||||
$howlong = datetime_convert(date_default_timezone_get(),'UTC',$howlong);
|
||||
|
||||
@@ -696,10 +697,9 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
$show_presence = ['show_presence', t('Reveal my online status'), $show_presence_val, '', [t('No'), t('Yes')]];
|
||||
}
|
||||
|
||||
$extra_fields = array();
|
||||
$q = q("select * from profdef where true");
|
||||
if($q) {
|
||||
$extra_fields = array();
|
||||
|
||||
foreach($q as $qq) {
|
||||
$mine = q("select v from profext where k = '%s' and hash = '%s' and channel_id = %d limit 1",
|
||||
dbesc($qq['field_name']),
|
||||
@@ -759,7 +759,7 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
'$profile_id' => $r[0]['id'],
|
||||
'$profile_name' => array('profile_name', t('Profile name'), $r[0]['profile_name'], t('Required'), '*'),
|
||||
'$is_default' => $is_default,
|
||||
'$default' => t('This is your default profile.') . EOL . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))),
|
||||
'$default' => t('This is your default profile.') . ' ' . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))),
|
||||
'$advanced' => $advanced,
|
||||
'$name' => array('name', t('Your full name'), $r[0]['fullname'], t('Required'), '*'),
|
||||
'$pdesc' => array('pdesc', t('Short title/description'), $r[0]['pdesc'], t('Maximal 190 characters'), '', 'maxlength="190"'),
|
||||
@@ -775,7 +775,7 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
'$marital' => marital_selector($r[0]['marital']),
|
||||
'$marital_min' => marital_selector_min($r[0]['marital']),
|
||||
'$with' => array('with', t("Who (if applicable)"), $r[0]['partner'], t('Examples: cathy123, Cathy Williams, cathy@example.com')),
|
||||
'$howlong' => array('howlong', t('Since (date)'), ($r[0]['howlong'] <= NULL_DATE ? '' : datetime_convert('UTC',date_default_timezone_get(),$r[0]['howlong']))),
|
||||
'$howlong' => array('howlong', t('Since (date)'), ($r[0]['howlong'] <= DBA::$dba->get_null_date() ? '' : datetime_convert('UTC',date_default_timezone_get(),$r[0]['howlong']))),
|
||||
'$sexual' => sexpref_selector($r[0]['sexual']),
|
||||
'$sexual_min' => sexpref_selector_min($r[0]['sexual']),
|
||||
'$about' => array('about', t('Tell us about yourself'), $r[0]['about']),
|
||||
@@ -833,6 +833,8 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
);
|
||||
if($r) {
|
||||
|
||||
$profiles = '';
|
||||
|
||||
$tpl = get_markup_template('profile_entry.tpl');
|
||||
foreach($r as $rr) {
|
||||
$profiles .= replace_macros($tpl, array(
|
||||
@@ -841,7 +843,7 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
'$alt' => t('Profile Image'),
|
||||
'$profile_name' => $rr['profile_name'],
|
||||
'$visible' => (($rr['is_default'])
|
||||
? '<strong>' . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))) . '</strong>'
|
||||
? '<strong>' . escape_tags(translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile')))) . '</strong>'
|
||||
: '<a href="' . z_root() . '/profperm/' . $rr['id'] . '" />' . t('Edit visibility') . '</a>')
|
||||
));
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ class Profperm extends \Zotlabs\Web\Controller {
|
||||
if($switchtotext === false)
|
||||
$switchtotext = 400;
|
||||
|
||||
$change = 0;
|
||||
|
||||
if((argc() > 2) && intval(argv(1)) && intval(argv(2))) {
|
||||
$r = q("SELECT abook_id FROM abook WHERE abook_id = %d and abook_channel = %d limit 1",
|
||||
@@ -74,10 +75,11 @@ class Profperm extends \Zotlabs\Web\Controller {
|
||||
dbesc($profile['profile_guid'])
|
||||
);
|
||||
|
||||
$ingroup = array();
|
||||
if($r)
|
||||
$ingroup = [];
|
||||
if($r) {
|
||||
foreach($r as $member)
|
||||
$ingroup[] = $member['abook_id'];
|
||||
}
|
||||
|
||||
$members = $r;
|
||||
|
||||
@@ -104,68 +106,57 @@ class Profperm extends \Zotlabs\Web\Controller {
|
||||
);
|
||||
|
||||
$members = $r;
|
||||
|
||||
$ingroup = array();
|
||||
if(count($r))
|
||||
$ingroup = [];
|
||||
if($r) {
|
||||
foreach($r as $member)
|
||||
$ingroup[] = $member['abook_id'];
|
||||
}
|
||||
|
||||
$o .= '<h2>' . t('Profile Visibility Editor') . '</h2>';
|
||||
|
||||
$o .= '<h3>' . t('Profile') . ' \'' . $profile['profile_name'] . '\'</h3>';
|
||||
|
||||
$o .= '<div id="prof-edit-desc">' . t('Click on a contact to add or remove.') . '</div>';
|
||||
|
||||
}
|
||||
|
||||
$o .= '<div id="prof-update-wrapper">';
|
||||
if($change)
|
||||
$o = '';
|
||||
|
||||
$o .= '<div id="prof-members-title">';
|
||||
$o .= '<h3>' . t('Visible To') . '</h3>';
|
||||
$o .= '</div>';
|
||||
$o .= '<div id="prof-members">';
|
||||
|
||||
$textmode = (($switchtotext && (count($members) > $switchtotext)) ? true : false);
|
||||
|
||||
foreach($members as $member) {
|
||||
if($member['xchan_url']) {
|
||||
$member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;';
|
||||
$o .= micropro($member,true,'mpprof', $textmode);
|
||||
}
|
||||
}
|
||||
$o .= '</div><div id="prof-members-end"></div>';
|
||||
$o .= '<hr id="prof-separator" />';
|
||||
|
||||
$o .= '<div id="prof-all-contcts-title">';
|
||||
$o .= '<h3>' . t("All Connections") . '</h3>';
|
||||
$o .= '</div>';
|
||||
$o .= '<div id="prof-all-contacts">';
|
||||
|
||||
$r = abook_connections(local_channel());
|
||||
|
||||
if($r) {
|
||||
$textmode = (($switchtotext && (count($r) > $switchtotext)) ? true : false);
|
||||
foreach($r as $member) {
|
||||
if(! in_array($member['abook_id'],$ingroup)) {
|
||||
$member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;';
|
||||
$o .= micropro($member,true,'mpprof',$textmode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '</div><div id="prof-all-contacts-end"></div>';
|
||||
$o .= '<h2>' . t('Profile Visibility Editor') . '</h2>';
|
||||
$o .= '<h3>' . t('Profile') . ' \'' . $profile['profile_name'] . '\'</h3>';
|
||||
$o .= '<div id="prof-edit-desc">' . t('Click on a contact to add or remove.') . '</div>';
|
||||
}
|
||||
|
||||
// Build template data
|
||||
$members_tpl = [];
|
||||
$textmode = (($switchtotext && (count($members) > $switchtotext)) ? true : false);
|
||||
if($members) {
|
||||
foreach($members as $member) {
|
||||
if($member['xchan_url']) {
|
||||
$member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;';
|
||||
$members_tpl[] = [ 'micro' => micropro($member, true, 'mpprof', $textmode) ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$all_members_tpl = [];
|
||||
$r = abook_connections(local_channel());
|
||||
if($r) {
|
||||
$textmode = (($switchtotext && (count($r) > $switchtotext)) ? true : false);
|
||||
foreach($r as $member) {
|
||||
if(! in_array($member['abook_id'], $ingroup)) {
|
||||
$member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;';
|
||||
$all_members_tpl[] = [ 'micro' => micropro($member, true, 'mpprof', $textmode) ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use tpl for the inner part
|
||||
$inner_html = replace_macros(get_markup_template('profile_members.tpl'), [
|
||||
'$visible_to' => t('Visible To'),
|
||||
'$all_connections' => t('All Connections'),
|
||||
'$members' => $members_tpl,
|
||||
'$all_members' => $all_members_tpl,
|
||||
]);
|
||||
|
||||
if($change) {
|
||||
echo $o;
|
||||
echo $inner_html;
|
||||
killme();
|
||||
}
|
||||
$o .= '</div>';
|
||||
|
||||
$o .= $inner_html;
|
||||
return $o;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -247,11 +247,7 @@ class Pubstream extends \Zotlabs\Web\Controller {
|
||||
|
||||
if($r) {
|
||||
$items = items_by_parent_ids($r, blog_mode: $blog_mode);
|
||||
|
||||
// use effective_uid param of xchan_query to help sort out comment permission
|
||||
// for sys_channel owned items.
|
||||
|
||||
xchan_query($items, true, local_channel());
|
||||
xchan_query($items);
|
||||
$items = fetch_post_tags($items,true);
|
||||
$items = conv_sort($items, $ordering);
|
||||
}
|
||||
|
||||
@@ -142,7 +142,15 @@ class Regate extends \Zotlabs\Web\Controller {
|
||||
|
||||
if (($flags & ACCOUNT_PENDING ) == ACCOUNT_PENDING) {
|
||||
$nextpage = 'regate/' . bin2hex($did2) . $didx;
|
||||
q("COMMIT");
|
||||
$approve = send_reg_approval_email_from_register($r['reg_id']);
|
||||
if ($approve['success']) {
|
||||
q("COMMIT");
|
||||
} else {
|
||||
q("ROLLBACK");
|
||||
$msg_code = 'ZAR1237E';
|
||||
$msg = t('Account verification notify error');
|
||||
zar_log($msg_code . ' ' . $msg . ': ' . print_r($approve, true));
|
||||
}
|
||||
}
|
||||
elseif (($flags ^ REGISTER_AGREED) == 0) {
|
||||
|
||||
@@ -386,7 +394,7 @@ class Regate extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
else {
|
||||
zar_log('ZAR1135E not awaited url parameter received');
|
||||
goaway(z_root);
|
||||
goaway(z_root());
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -323,7 +323,6 @@ class Register extends Controller {
|
||||
$did2 = $email;
|
||||
$didx = 'e';
|
||||
|
||||
push_lang(($reg['lang']) ? $reg['lang'] : App::$language);
|
||||
$reonar['from'] = Config::Get('system', 'from_email');
|
||||
$reonar['to'] = $email;
|
||||
$reonar['subject'] = sprintf( t('Registration confirmation for %s'), Config::Get('system','sitename'));
|
||||
@@ -338,7 +337,6 @@ class Register extends Controller {
|
||||
'$hash' => $empin
|
||||
]
|
||||
);
|
||||
pop_lang();
|
||||
zar_reg_mail($reonar);
|
||||
|
||||
} else {
|
||||
@@ -445,7 +443,7 @@ class Register extends Controller {
|
||||
if(intval(Config::Get('system','register_policy')) == REGISTER_APPROVE) {
|
||||
$register_msg = ['register_msg', t('Why do you want to join this hub?'), ((x($_REQUEST,'register_msg')) ? $_REQUEST['register_msg'] : ''), t('This will help to review your registration')];
|
||||
$registration_is = t('Registration on this hub is by approval only.');
|
||||
$other_sites = '<a href="pubsites">' . t('Register at another affiliated hub in case when prefered') . '</a>';
|
||||
$other_sites = '<a href="pubsites">' . t('Register at another affiliated hub if preferred') . '</a>';
|
||||
}
|
||||
|
||||
$duty = zar_register_dutystate();
|
||||
|
||||
@@ -1,37 +1,38 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
|
||||
class Removeaccount extends \Zotlabs\Web\Controller {
|
||||
|
||||
function post() {
|
||||
|
||||
|
||||
if(! local_channel())
|
||||
return;
|
||||
|
||||
|
||||
if($_SESSION['delegate'])
|
||||
return;
|
||||
|
||||
|
||||
if((! x($_POST,'qxz_password')) || (! strlen(trim($_POST['qxz_password']))))
|
||||
return;
|
||||
|
||||
|
||||
if((! x($_POST,'verify')) || (! strlen(trim($_POST['verify']))))
|
||||
return;
|
||||
|
||||
|
||||
if($_POST['verify'] !== $_SESSION['remove_account_verify'])
|
||||
return;
|
||||
|
||||
|
||||
|
||||
|
||||
$account = \App::get_account();
|
||||
$account_id = get_account_id();
|
||||
|
||||
|
||||
$x = account_verify_password($account['account_email'],$_POST['qxz_password']);
|
||||
if(! ($x && $x['account']))
|
||||
return;
|
||||
|
||||
if($account['account_password_changed'] > NULL_DATE) {
|
||||
|
||||
if($account['account_password_changed'] > DBA::$dba->get_null_date()) {
|
||||
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
|
||||
if($account['account_password_changed'] > d1) {
|
||||
if($account['account_password_changed'] > $d1) {
|
||||
notice( t('Account removals are not allowed within 48 hours of changing the account password.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
|
||||
class Removeme extends \Zotlabs\Web\Controller {
|
||||
|
||||
@@ -29,7 +30,7 @@ class Removeme extends \Zotlabs\Web\Controller {
|
||||
if(! ($x && $x['account']))
|
||||
return;
|
||||
|
||||
if($account['account_password_changed'] > NULL_DATE) {
|
||||
if($account['account_password_changed'] > DBA::$dba->get_null_date()) {
|
||||
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
|
||||
if($account['account_password_changed'] > $d1) {
|
||||
notice( t('Channel removals are not allowed within 48 hours of changing the account password.') . EOL);
|
||||
|
||||
@@ -25,7 +25,7 @@ class Request extends Controller
|
||||
}
|
||||
|
||||
|
||||
private function processSubthreadRequest() : string
|
||||
private function processSubthreadRequest(): void
|
||||
{
|
||||
$mid = $_GET['mid'];
|
||||
$parent = intval($_GET['parent']);
|
||||
@@ -52,11 +52,11 @@ class Request extends Controller
|
||||
json_return_and_die($ret);
|
||||
}
|
||||
|
||||
public function get() : string
|
||||
public function init() : void
|
||||
{
|
||||
|
||||
if (in_array($_GET['verb'], ['comment', 'load'])) {
|
||||
return self::processSubthreadRequest();
|
||||
self::processSubthreadRequest();
|
||||
}
|
||||
|
||||
$verb = self::mapVerb($_GET['verb']);
|
||||
|
||||
@@ -42,9 +42,7 @@ class Search extends Controller {
|
||||
$observer = App::get_observer();
|
||||
$observer_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
|
||||
$o = '<div class="generic-content-wrapper-styled">' . "\r\n";
|
||||
|
||||
$o .= '<h2>' . t('Search') . '</h2>';
|
||||
$title = t('Search');
|
||||
|
||||
if (x(App::$data, 'search'))
|
||||
$search = trim(App::$data['search']);
|
||||
@@ -57,7 +55,7 @@ class Search extends Controller {
|
||||
$search = ((x($_GET, 'tag')) ? trim(escape_tags(rawurldecode($_GET['tag']))) : '');
|
||||
}
|
||||
|
||||
$o .= search($search, 'search-box', '/search', ((local_channel()) ? true : false));
|
||||
$searchbox = search($search, 'search-box', '/search', ((local_channel()) ? true : false));
|
||||
|
||||
if (local_channel() && str_starts_with($search, 'https://') && !$update && !$load) {
|
||||
|
||||
@@ -71,7 +69,13 @@ class Search extends Controller {
|
||||
$url = unpack_link_id(basename($url));
|
||||
}
|
||||
|
||||
$f = Libzot::fetch_conversation(App::get_channel(), punify($url), true);
|
||||
$parsed = parse_url($url);
|
||||
if (isset($parsed['host'])) {
|
||||
$parsed['host'] = punify($parsed['host']);
|
||||
$url = unparse_url($parsed);
|
||||
}
|
||||
|
||||
$f = Libzot::fetch_conversation(App::get_channel(), $url, true);
|
||||
|
||||
if ($f) {
|
||||
$uuid = $f[0]['message_uuid'];
|
||||
@@ -87,7 +91,7 @@ class Search extends Controller {
|
||||
else {
|
||||
// try other fetch providers (e.g. diaspora, pubcrawl)
|
||||
$hookdata = [
|
||||
'url' => punify($url)
|
||||
'url' => $url
|
||||
];
|
||||
call_hooks('fetch_provider', $hookdata);
|
||||
}
|
||||
@@ -115,8 +119,17 @@ class Search extends Controller {
|
||||
goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search);
|
||||
}
|
||||
|
||||
if (!$search)
|
||||
return $o;
|
||||
if (!$search) {
|
||||
$tpl = get_markup_template('search.tpl');
|
||||
|
||||
return replace_macros($tpl, [
|
||||
'$title' => $title,
|
||||
'$searchbox' => $searchbox,
|
||||
'$livesearch' => '',
|
||||
'$results_header' => '',
|
||||
'$conversation' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
if ($tag) {
|
||||
$wildtag = str_replace('*', '%', $search);
|
||||
@@ -137,14 +150,15 @@ class Search extends Controller {
|
||||
// OR your own posts if you are a logged in member
|
||||
// No items will be shown if the member has a blocked profile wall.
|
||||
|
||||
$livesearch = '';
|
||||
|
||||
if ((!$update) && (!$load)) {
|
||||
|
||||
// This is ugly, but we can't pass the profile_uid through the session to the ajax updater,
|
||||
// because browser prefetching might change it on us. We have to deliver it with the page.
|
||||
|
||||
$o .= '<div id="live-search"></div>' . "\r\n";
|
||||
$o .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1))
|
||||
$livesearch .= '<div id="live-search"></div>' . "\r\n";
|
||||
$livesearch .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1))
|
||||
. "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . "; </script>\r\n";
|
||||
|
||||
App::$page['htmlhead'] = replace_macros(get_markup_template("build_query.tpl"), [
|
||||
@@ -248,15 +262,21 @@ class Search extends Controller {
|
||||
}
|
||||
|
||||
if ($tag)
|
||||
$o .= '<h2>' . sprintf(t('Items tagged with: %s'), $search) . '</h2>';
|
||||
$results_header = sprintf(t('Items tagged with: %s'), $search);
|
||||
else
|
||||
$o .= '<h2>' . sprintf(t('Search results for: %s'), $search) . '</h2>';
|
||||
$results_header = sprintf(t('Search results for: %s'), $search);
|
||||
|
||||
$o .= conversation($items, 'search', $update, 'client');
|
||||
$conversation = conversation($items, 'search', $update, 'client');
|
||||
|
||||
$o .= '</div>';
|
||||
$tpl = get_markup_template('search.tpl');
|
||||
|
||||
return $o;
|
||||
return replace_macros($tpl, [
|
||||
'$title' => $title,
|
||||
'$searchbox' => $searchbox,
|
||||
'$livesearch' => $livesearch,
|
||||
'$results_header' => $results_header ?? '',
|
||||
'$conversation' => $conversation,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -153,14 +153,6 @@ class Channel {
|
||||
Master::Summon(['Directory', local_channel()]);
|
||||
Libsync::build_sync_packet();
|
||||
|
||||
$email_changed = false;
|
||||
if ($email_changed && App::$config['system']['register_policy'] == REGISTER_VERIFY) {
|
||||
|
||||
// FIXME - set to un-verified, blocked and redirect to logout
|
||||
// Q: Why? Are we verifying people or email addresses?
|
||||
// A: the policy is to verify email addresses
|
||||
}
|
||||
|
||||
goaway(z_root() . '/settings');
|
||||
return; // NOTREACHED
|
||||
}
|
||||
|
||||
@@ -504,12 +504,6 @@ class Setup extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
$this->check_add($checks, t('Generate ed25519 encryption keys'), $res, true, $help);
|
||||
|
||||
$res1 = extension_loaded('bcmath');
|
||||
$res2 = extension_loaded('gmp');
|
||||
if (! ($res1 || $res2)) {
|
||||
$help = t('Error: one of "bcmath" or "gmp" (bigmath library) extensions are required.') . EOL;
|
||||
}
|
||||
$this->check_add($checks, t('Bigmath library (either bcmath or gmp)'), $res1||$res2, $help);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -534,6 +528,7 @@ class Setup extends \Zotlabs\Web\Controller {
|
||||
$this->check_add($ck_funcs, t('xml PHP module'), true, true);
|
||||
$this->check_add($ck_funcs, t('zip PHP module'), true, true);
|
||||
$this->check_add($ck_funcs, t('intl PHP module'), true, true);
|
||||
$this->check_add($ck_funcs, t('gmp PHP module'), true, true);
|
||||
|
||||
if(function_exists('apache_get_modules')){
|
||||
if(! in_array('mod_rewrite', apache_get_modules())) {
|
||||
@@ -590,9 +585,15 @@ class Setup extends \Zotlabs\Web\Controller {
|
||||
$ck_funcs[6]['status'] = false;
|
||||
$ck_funcs[6]['help'] = t('Error: zip PHP module required but not installed.');
|
||||
}
|
||||
if(! extension_loaded('intl')) {
|
||||
$ck_funcs[6]['status'] = false;
|
||||
$ck_funcs[6]['help'] = t('Error: intl PHP module required but not installed.');
|
||||
|
||||
if (!extension_loaded('intl')) {
|
||||
$ck_funcs[7]['status'] = false;
|
||||
$ck_funcs[7]['help'] = t('Error: intl PHP module required but not installed.');
|
||||
}
|
||||
|
||||
if (!extension_loaded('gmp')) {
|
||||
$ck_funcs[8]['status'] = false;
|
||||
$ck_funcs[8]['help'] = t('Error: gmp PHP module required but not installed.');
|
||||
}
|
||||
|
||||
$checks = array_merge($checks, $ck_funcs);
|
||||
|
||||
@@ -17,6 +17,9 @@ class Siteinfo extends \Zotlabs\Web\Controller {
|
||||
$federated = [];
|
||||
call_hooks('federated_transports',$federated);
|
||||
|
||||
$themes = Config::Get('system', 'allowed_themes');
|
||||
$themes = array_map('trim', explode(',', $themes));
|
||||
|
||||
$siteinfo = replace_macros(get_markup_template('siteinfo.tpl'),
|
||||
[
|
||||
'$title' => t('About this site'),
|
||||
@@ -40,6 +43,7 @@ class Siteinfo extends \Zotlabs\Web\Controller {
|
||||
'$prj_link' => \Zotlabs\Lib\System::get_project_link(),
|
||||
'$prj_src' => \Zotlabs\Lib\System::get_project_srclink(),
|
||||
'$addons' => array( t('Active addons'), \App::$plugins ),
|
||||
'$themes' => array( t('Active themes'), $themes ),
|
||||
'$blocked_sites' => array( t('Blocked sites'), \Zotlabs\Lib\Config::Get('system', 'blacklisted_sites') )
|
||||
]
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Web\Controller;
|
||||
@@ -136,7 +137,7 @@ class Sse extends Controller {
|
||||
|
||||
session_reset();
|
||||
|
||||
XConfig::Set(self::$ob_hash, 'sse', 'timestamp', NULL_DATE);
|
||||
XConfig::Set(self::$ob_hash, 'sse', 'timestamp', DBA::$dba->get_null_date());
|
||||
XConfig::Set(self::$ob_hash, 'sse', 'notifications', []);
|
||||
|
||||
if (ob_get_length() > 0) {
|
||||
|
||||
@@ -20,6 +20,8 @@ class Sse_bs extends Controller {
|
||||
public static $limit;
|
||||
public static $offset;
|
||||
public static $xchans;
|
||||
public static $direction;
|
||||
public static $count_limit;
|
||||
|
||||
function init() {
|
||||
self::$uid = local_channel();
|
||||
@@ -41,6 +43,8 @@ class Sse_bs extends Controller {
|
||||
self::$limit = 30;
|
||||
self::$offset = 0;
|
||||
self::$xchans = '';
|
||||
self::$direction = get_pconfig(self::$uid, 'system', 'invert_notifications_order', false) ? 'ASC' : 'DESC';
|
||||
self::$count_limit = get_pconfig(self::$uid, 'system', 'notifications_count_limit', 100);
|
||||
|
||||
if (!empty($_REQUEST['sse_rmids'])) {
|
||||
self::mark_read(explode(',', $_REQUEST['sse_rmids']));
|
||||
@@ -64,7 +68,6 @@ class Sse_bs extends Controller {
|
||||
$_SESSION['sse_loadtime'] = datetime_convert();
|
||||
}
|
||||
|
||||
|
||||
$network = false;
|
||||
$dm = false;
|
||||
$home = false;
|
||||
@@ -91,8 +94,14 @@ class Sse_bs extends Controller {
|
||||
default:
|
||||
}
|
||||
|
||||
$selected_forum_id = null;
|
||||
if (str_starts_with(argv(1), 'forum_')) {
|
||||
$selected_forum_id = argv(1);
|
||||
$f = 'bs_forums';
|
||||
}
|
||||
|
||||
if(self::$offset && $f) {
|
||||
$result = self::$f(true);
|
||||
$result = self::$f($selected_forum_id ?? true);
|
||||
json_return_and_die($result);
|
||||
}
|
||||
|
||||
@@ -102,7 +111,7 @@ class Sse_bs extends Controller {
|
||||
self::bs_home($home),
|
||||
self::bs_notify(),
|
||||
self::bs_intros(),
|
||||
self::bs_forums(),
|
||||
self::bs_forums($selected_forum_id),
|
||||
self::bs_pubs($pubs),
|
||||
self::bs_files(),
|
||||
self::bs_all_events(),
|
||||
@@ -122,7 +131,7 @@ class Sse_bs extends Controller {
|
||||
$str = '';
|
||||
$slice = 0;
|
||||
|
||||
$mids_all = isset($_SESSION['sse_mids_all']) ? unserialise($_SESSION['sse_mids_all']) : [];
|
||||
$mids_all = isset($_SESSION['sse_mids_all']) ? json_unserialize($_SESSION['sse_mids_all']) : [];
|
||||
|
||||
if (count($mids_all) > 3000) {
|
||||
$slice = count($mids_all) - 3000;
|
||||
@@ -168,7 +177,7 @@ class Sse_bs extends Controller {
|
||||
$mids_all = array_merge($mids_all, $activities_arr);
|
||||
}
|
||||
|
||||
$_SESSION['sse_mids_all'] = serialise(array_unique($mids_all));
|
||||
$_SESSION['sse_mids_all'] = json_serialize(array_unique($mids_all));
|
||||
|
||||
if(! self::$uid) {
|
||||
return;
|
||||
@@ -205,6 +214,8 @@ class Sse_bs extends Controller {
|
||||
|
||||
$limit = intval(self::$limit);
|
||||
$offset = self::$offset;
|
||||
$direction = self::$direction;
|
||||
$count_limit = intval(self::$count_limit);
|
||||
|
||||
$sql_extra = '';
|
||||
if (!(self::$vnotify & VNOTIFY_LIKE)) {
|
||||
@@ -235,7 +246,7 @@ class Sse_bs extends Controller {
|
||||
$item_normal
|
||||
$sql_extra
|
||||
$sql_extra2
|
||||
ORDER BY item.created DESC LIMIT $limit OFFSET $offset",
|
||||
ORDER BY item.created $direction LIMIT $limit OFFSET $offset",
|
||||
intval(self::$uid),
|
||||
dbescdate($_SESSION['sse_loadtime']),
|
||||
dbesc(self::$ob_hash)
|
||||
@@ -262,7 +273,7 @@ class Sse_bs extends Controller {
|
||||
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
|
||||
AND author_xchan != '%s'
|
||||
$item_normal
|
||||
$sql_extra LIMIT 100",
|
||||
$sql_extra LIMIT $count_limit",
|
||||
intval(self::$uid),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
@@ -290,6 +301,8 @@ class Sse_bs extends Controller {
|
||||
|
||||
$limit = intval(self::$limit);
|
||||
$offset = self::$offset;
|
||||
$direction = self::$direction;
|
||||
$count_limit = intval(self::$count_limit);
|
||||
|
||||
$sql_extra = '';
|
||||
if (!(self::$vnotify & VNOTIFY_LIKE)) {
|
||||
@@ -320,7 +333,7 @@ class Sse_bs extends Controller {
|
||||
$item_normal
|
||||
$sql_extra
|
||||
$sql_extra2
|
||||
ORDER BY created DESC LIMIT $limit OFFSET $offset",
|
||||
ORDER BY created $direction LIMIT $limit OFFSET $offset",
|
||||
intval(self::$uid),
|
||||
dbescdate($_SESSION['sse_loadtime']),
|
||||
dbesc(self::$ob_hash)
|
||||
@@ -346,7 +359,7 @@ class Sse_bs extends Controller {
|
||||
WHERE uid = %d and item_unseen = 1 AND item_private = 2
|
||||
$item_normal
|
||||
$sql_extra
|
||||
AND author_xchan != '%s' LIMIT 100",
|
||||
AND author_xchan != '%s' LIMIT $count_limit",
|
||||
intval(self::$uid),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
@@ -374,6 +387,8 @@ class Sse_bs extends Controller {
|
||||
|
||||
$limit = intval(self::$limit);
|
||||
$offset = self::$offset;
|
||||
$direction = self::$direction;
|
||||
$count_limit = intval(self::$count_limit);
|
||||
|
||||
$sql_extra = '';
|
||||
if (!(self::$vnotify & VNOTIFY_LIKE)) {
|
||||
@@ -405,7 +420,7 @@ class Sse_bs extends Controller {
|
||||
$item_normal
|
||||
$sql_extra
|
||||
$sql_extra2
|
||||
ORDER BY item.created DESC LIMIT $limit OFFSET $offset",
|
||||
ORDER BY item.created $direction LIMIT $limit OFFSET $offset",
|
||||
intval(self::$uid),
|
||||
dbescdate($_SESSION['sse_loadtime']),
|
||||
dbesc(self::$ob_hash)
|
||||
@@ -431,7 +446,7 @@ class Sse_bs extends Controller {
|
||||
WHERE uid = %d and item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1)
|
||||
$item_normal
|
||||
$sql_extra
|
||||
AND author_xchan != '%s' LIMIT 100",
|
||||
AND author_xchan != '%s' LIMIT $count_limit",
|
||||
intval(self::$uid),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
@@ -470,6 +485,8 @@ class Sse_bs extends Controller {
|
||||
|
||||
$limit = intval(self::$limit);
|
||||
$offset = self::$offset;
|
||||
$direction = self::$direction;
|
||||
$count_limit = intval(self::$count_limit);
|
||||
|
||||
$sys = get_sys_channel();
|
||||
$sql_extra = '';
|
||||
@@ -485,7 +502,7 @@ class Sse_bs extends Controller {
|
||||
$sql_extra2 = " AND CASE WHEN item.verb = '" . ACTIVITY_SHARE . "' THEN item.owner_xchan ELSE item.author_xchan END IN (" . self::$xchans . ") ";
|
||||
|
||||
$sql_extra3 = '';
|
||||
$sse_mids_all = isset($_SESSION['sse_mids_all']) ? unserialise($_SESSION['sse_mids_all']) : [];
|
||||
$sse_mids_all = isset($_SESSION['sse_mids_all']) ? json_unserialize($_SESSION['sse_mids_all']) : [];
|
||||
if ($sse_mids_all) {
|
||||
$sql_extra3 = " AND item.uuid NOT IN (" . protect_sprintf(implode(',', $sse_mids_all)) . ") ";
|
||||
}
|
||||
@@ -515,9 +532,9 @@ class Sse_bs extends Controller {
|
||||
$sql_extra
|
||||
$sql_extra2
|
||||
$sql_extra3
|
||||
ORDER BY item.created DESC LIMIT $limit OFFSET $offset",
|
||||
ORDER BY item.created $direction LIMIT $limit OFFSET $offset",
|
||||
dbescdate($_SESSION['sse_loadtime']),
|
||||
dbescdate($_SESSION['last_login_date'] ?? $_SESSION['static_loadtime']),
|
||||
dbescdate($_SESSION['static_loadtime']),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
|
||||
@@ -543,9 +560,9 @@ class Sse_bs extends Controller {
|
||||
$item_normal
|
||||
$sql_extra
|
||||
$sql_extra3
|
||||
AND author_xchan != '%s' LIMIT 100",
|
||||
AND author_xchan != '%s' LIMIT $count_limit",
|
||||
dbescdate($_SESSION['sse_loadtime']),
|
||||
dbescdate($_SESSION['last_login_date'] ?? $_SESSION['static_loadtime']),
|
||||
dbescdate($_SESSION['static_loadtime']),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
|
||||
@@ -568,7 +585,9 @@ class Sse_bs extends Controller {
|
||||
if(! (self::$vnotify & VNOTIFY_SYSTEM))
|
||||
return $result;
|
||||
|
||||
$r = q("SELECT * FROM notify WHERE uid = %d AND seen = 0 ORDER BY created DESC",
|
||||
$direction = self::$direction;
|
||||
|
||||
$r = q("SELECT * FROM notify WHERE uid = %d AND seen = 0 ORDER BY created $direction",
|
||||
intval(self::$uid)
|
||||
);
|
||||
|
||||
@@ -595,7 +614,9 @@ class Sse_bs extends Controller {
|
||||
if(! (self::$vnotify & VNOTIFY_INTRO))
|
||||
return $result;
|
||||
|
||||
$r = q("SELECT * FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ORDER BY abook_created DESC LIMIT 50",
|
||||
$direction = self::$direction;
|
||||
|
||||
$r = q("SELECT * FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ORDER BY abook_created $direction LIMIT 50",
|
||||
intval(self::$uid)
|
||||
);
|
||||
|
||||
@@ -610,69 +631,95 @@ class Sse_bs extends Controller {
|
||||
|
||||
}
|
||||
|
||||
function bs_forums() {
|
||||
|
||||
$result['forums']['notifications'] = [];
|
||||
$result['forums']['count'] = 0;
|
||||
$result['forums']['offset'] = -1;
|
||||
|
||||
if(! self::$uid)
|
||||
return $result;
|
||||
|
||||
if(! (self::$vnotify & VNOTIFY_FORUMS))
|
||||
return $result;
|
||||
|
||||
function bs_forums($selected_forum_id) {
|
||||
$forums = get_forum_channels(self::$uid);
|
||||
|
||||
if(!self::$uid || !(self::$vnotify & VNOTIFY_FORUMS) || !$forums) {
|
||||
$result['forum']['notifications'] = [];
|
||||
$result['forum']['count'] = 0;
|
||||
$result['forum']['offset'] = -1;
|
||||
return $result;
|
||||
}
|
||||
|
||||
if($forums) {
|
||||
$item_normal = item_normal();
|
||||
$p_sql = '';
|
||||
|
||||
$sql_extra = '';
|
||||
if(! (self::$vnotify & VNOTIFY_LIKE))
|
||||
$sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
|
||||
|
||||
$fcount = count($forums);
|
||||
$i = 0;
|
||||
|
||||
for($x = 0; $x < $fcount; $x ++) {
|
||||
|
||||
$r = q("select count(*) as total from item
|
||||
where uid = %d and (owner_xchan = '%s' or author_xchan = '%s') and author_xchan != '%s' and verb != 'Announce' and item_unseen = 1 $sql_extra $item_normal",
|
||||
$forum_id = 'forum_' . $forums[$x]['abook_id'];
|
||||
|
||||
$result[$forum_id]['notifications'] = [];
|
||||
$result[$forum_id]['count'] = 0;
|
||||
|
||||
$limit = intval(self::$limit);
|
||||
$offset = self::$offset;
|
||||
$direction = self::$direction;
|
||||
$count_limit = intval(self::$count_limit);
|
||||
|
||||
$sql_extra = '';
|
||||
if (!(self::$vnotify & VNOTIFY_LIKE)) {
|
||||
$sql_extra = " AND item.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
|
||||
}
|
||||
elseif (!feature_enabled(self::$uid, 'dislike')) {
|
||||
$sql_extra = " AND item.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
|
||||
}
|
||||
|
||||
$item_normal = item_normal();
|
||||
|
||||
// Filter internal follow activities and strerams add/remove activities
|
||||
$item_normal .= " AND item.verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
|
||||
|
||||
if ($forum_id === $selected_forum_id) {
|
||||
$items = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item
|
||||
LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid
|
||||
WHERE item.uid = %d
|
||||
AND item.created <= '%s'
|
||||
AND item.owner_xchan = '%s'
|
||||
AND item.item_unseen = 1 AND item.item_wall = 0 AND item.item_private IN (0, 1)
|
||||
AND item.obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
|
||||
AND NOT (item.verb = 'Announce' AND item.item_thread_top = 1) -- only show the announce activity and not the resulting item
|
||||
AND NOT item.author_xchan = '%s'
|
||||
$item_normal
|
||||
$sql_extra
|
||||
ORDER BY item.created $direction LIMIT $limit OFFSET $offset",
|
||||
intval(self::$uid),
|
||||
dbescdate($_SESSION['sse_loadtime']),
|
||||
dbescdate($forums[$x]['xchan_hash']),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
|
||||
if ($items) {
|
||||
$result[$forum_id]['offset'] = ((count($items) == $limit) ? intval($offset + $limit) : -1);
|
||||
xchan_query($items);
|
||||
foreach($items as $item) {
|
||||
$parsed = Enotify::format($item);
|
||||
if($parsed) {
|
||||
$result[$forum_id]['notifications'][] = $parsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$result[$forum_id]['offset'] = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$r = q("SELECT id FROM item
|
||||
WHERE uid = %d and item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1)
|
||||
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
|
||||
AND author_xchan != '%s'
|
||||
AND item.owner_xchan = '%s'
|
||||
$item_normal
|
||||
$sql_extra LIMIT $count_limit",
|
||||
intval(self::$uid),
|
||||
dbesc($forums[$x]['xchan_hash']),
|
||||
dbesc($forums[$x]['xchan_hash']),
|
||||
dbesc(self::$ob_hash)
|
||||
dbesc(self::$ob_hash),
|
||||
dbesc($forums[$x]['xchan_hash'])
|
||||
);
|
||||
|
||||
if($r[0]['total']) {
|
||||
|
||||
$forums[$x]['notify_link'] = z_root() . '/network/?f=&pf=1&unseen=1&cid=' . $forums[$x]['abook_id'];
|
||||
$forums[$x]['name'] = $forums[$x]['xchan_name'];
|
||||
$forums[$x]['addr'] = $forums[$x]['xchan_addr'] ?? $forums[$x]['xchan_url'];
|
||||
$forums[$x]['url'] = $forums[$x]['xchan_url'];
|
||||
$forums[$x]['photo'] = $forums[$x]['xchan_photo_s'];
|
||||
$forums[$x]['unseen'] = $r[0]['total'];
|
||||
$forums[$x]['private_forum'] = ((isset($forums[$x]['private_forum']) && $forums[$x]['private_forum']) ? 'lock' : '');
|
||||
$forums[$x]['message'] = ((isset($forums[$x]['private_forum']) && $forums[$x]['private_forum']) ? t('Private forum') : t('Public forum'));
|
||||
|
||||
unset($forums[$x]['abook_id']);
|
||||
unset($forums[$x]['xchan_hash']);
|
||||
unset($forums[$x]['xchan_name']);
|
||||
unset($forums[$x]['xchan_url']);
|
||||
unset($forums[$x]['xchan_photo_s']);
|
||||
|
||||
$i = $i + $r[0]['total'];
|
||||
|
||||
}
|
||||
else {
|
||||
unset($forums[$x]);
|
||||
if ($r) {
|
||||
$result[$forum_id]['count'] = count($r);
|
||||
}
|
||||
}
|
||||
|
||||
$result['forums']['count'] = $i;
|
||||
$result['forums']['notifications'] = array_values($forums);
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -691,6 +738,7 @@ class Sse_bs extends Controller {
|
||||
if(! (self::$vnotify & VNOTIFY_FILES))
|
||||
return $result;
|
||||
|
||||
$direction = self::$direction;
|
||||
$item_normal = item_normal();
|
||||
|
||||
// Filter internal follow activities and strerams add/remove activities
|
||||
@@ -703,7 +751,7 @@ class Sse_bs extends Controller {
|
||||
AND author_xchan != '%s'
|
||||
AND item_unseen = 1
|
||||
$item_normal
|
||||
ORDER BY created DESC",
|
||||
ORDER BY created $direction",
|
||||
dbesc(ACTIVITY_POST),
|
||||
intval(self::$uid),
|
||||
dbesc(self::$ob_hash)
|
||||
@@ -735,10 +783,12 @@ class Sse_bs extends Controller {
|
||||
if(! (self::$vnotify & VNOTIFY_EVENT))
|
||||
return $result;
|
||||
|
||||
$direction = self::$direction;
|
||||
|
||||
$r = q("SELECT * FROM event left join xchan on event_xchan = xchan_hash
|
||||
WHERE event.uid = %d AND dtstart < '%s' AND dtstart > '%s' and dismissed = 0
|
||||
and etype in ( 'event', 'birthday' )
|
||||
ORDER BY dtstart DESC",
|
||||
ORDER BY dtstart $direction",
|
||||
intval(self::$uid),
|
||||
dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval(self::$evdays) . ' days')),
|
||||
dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days'))
|
||||
|
||||
@@ -3,21 +3,21 @@ namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/event.php');
|
||||
|
||||
|
||||
use DBA;
|
||||
|
||||
class Tasks extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
|
||||
|
||||
|
||||
// logger('request: ' . print_r($_REQUEST,true));
|
||||
|
||||
|
||||
$arr = array();
|
||||
|
||||
if(argc() > 1 && argv(1) === 'fetch') {
|
||||
|
||||
if(argc() > 1 && argv(1) === 'fetch') {
|
||||
if(argc() > 2 && argv(2) === 'all')
|
||||
$arr['all'] = 1;
|
||||
|
||||
|
||||
$x = tasks_fetch($arr);
|
||||
$x['html'] = '';
|
||||
if($x['tasks']) {
|
||||
@@ -53,7 +53,7 @@ class Tasks extends \Zotlabs\Web\Controller {
|
||||
$event = $r[0];
|
||||
if($event['event_status'] === 'COMPLETED') {
|
||||
$event['event_status'] = 'IN-PROCESS';
|
||||
$event['event_status_date'] = NULL_DATE;
|
||||
$event['event_status_date'] = DBA::$dba->get_null_date();
|
||||
$event['event_percent'] = 0;
|
||||
$event['event_sequence'] = $event['event_sequence'] + 1;
|
||||
$event['edited'] = datetime_convert();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use Zotlabs\Lib\AccessList;
|
||||
@@ -84,7 +85,7 @@ class Tokens extends Controller {
|
||||
if(trim($_POST['expires']))
|
||||
$expires = datetime_convert(date_default_timezone_get(),'UTC',$_POST['expires']);
|
||||
else
|
||||
$expires = NULL_DATE;
|
||||
$expires = DBA::$dba->get_null_date();
|
||||
$max_atokens = service_class_fetch($channel['channel_id'],'access_tokens');
|
||||
if($max_atokens) {
|
||||
$r = q("select count(atoken_id) as total where atoken_uid = %d",
|
||||
@@ -290,7 +291,7 @@ class Tokens extends Controller {
|
||||
'$atoken' => $atoken,
|
||||
'$name' => array('name', t('Login Name') . ' <span class="required">*</span>', $atoken['atoken_name'] ?? '',''),
|
||||
'$token'=> array('token', t('Login Password') . ' <span class="required">*</span>', $atoken['atoken_token'] ?? new_token(), ''),
|
||||
'$expires'=> array('expires', t('Expires (yyyy-mm-dd)'), ((isset($atoken['atoken_expires']) && $atoken['atoken_expires'] > NULL_DATE) ? datetime_convert('UTC',date_default_timezone_get(),$atoken['atoken_expires']) : ''), ''),
|
||||
'$expires'=> array('expires', t('Expires (yyyy-mm-dd)'), ((isset($atoken['atoken_expires']) && $atoken['atoken_expires'] > DBA::$dba->get_null_date()) ? datetime_convert('UTC',date_default_timezone_get(),$atoken['atoken_expires']) : ''), ''),
|
||||
'$submit' => t('Submit'),
|
||||
'$delete' => t('Delete')
|
||||
));
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\IConfig;
|
||||
use Zotlabs\Lib\ObjCache;
|
||||
|
||||
class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
|
||||
@@ -28,13 +30,16 @@ class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
$item_normal = item_normal_search();
|
||||
|
||||
if(local_channel() && $item_id) {
|
||||
$r = q("select id, mid, uuid, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1",
|
||||
$r = q("select * from item where uid in (%d , %d) and id = %d $item_normal limit 1",
|
||||
intval(local_channel()),
|
||||
intval($sys['channel_id']),
|
||||
intval($item_id)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
xchan_query($r, true);
|
||||
$r = fetch_post_tags($r);
|
||||
|
||||
if(intval($r[0]['item_obscured']))
|
||||
$dload = true;
|
||||
|
||||
@@ -45,15 +50,41 @@ class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
killme();
|
||||
}
|
||||
|
||||
$cached = true;
|
||||
|
||||
$content = escape_tags($r[0]['body']);
|
||||
$o = (($json) ? json_encode($content) : $content);
|
||||
$obj = ObjCache::Get($r[0]['mid']);
|
||||
|
||||
if (!$obj) {
|
||||
$obj = IConfig::Get($r[0], 'activitypub', 'rawmsg');
|
||||
}
|
||||
|
||||
if (in_array($r[0]['owner']['xchan_network'], ['diaspora'])) {
|
||||
$obj = ObjCache::Get($r[0]['mid'], 'diaspora');
|
||||
|
||||
if (!$obj) {
|
||||
$obj = IConfig::Get($r[0], 'diaspora', 'fields');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$obj) {
|
||||
$cached = false;
|
||||
$obj = Activity::encode_activity($r[0]);
|
||||
}
|
||||
|
||||
if ($obj) {
|
||||
$content = (($cached) ? 'Cached: ' : '') . '<pre>' . escape_tags(json_encode($obj, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) . '</pre>';
|
||||
}
|
||||
else {
|
||||
$content = escape_tags($r[0]['body']);
|
||||
}
|
||||
|
||||
$o = (($json) ? json_encode($content) : str_replace("\n", '<br>', $content));
|
||||
}
|
||||
}
|
||||
|
||||
if(is_ajax()) {
|
||||
echo '<div class="p-1">';
|
||||
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a><br>mid: ' . $r[0]['mid'] . '<br>uuid: ' . $r[0]['uuid'] . '</div>';
|
||||
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a><br>mid: ' . $r[0]['mid'] . '<br>hashpath: ' . hash('sha256', $r[0]['mid']) . '<br>uuid: ' . $r[0]['uuid'] . '</div>';
|
||||
echo '<hr>';
|
||||
echo '<pre class="p-1">' . $o . '</pre>';
|
||||
echo '</div>';
|
||||
|
||||
@@ -20,27 +20,35 @@ class Vote extends Controller {
|
||||
json_return_and_die($ret);
|
||||
}
|
||||
|
||||
|
||||
$fetch = null;
|
||||
$id = argv(1);
|
||||
$response = $_REQUEST['answer'];
|
||||
|
||||
if ($id) {
|
||||
$fetch = q("select * from item where id = %d limit 1",
|
||||
intval($id)
|
||||
);
|
||||
if (!$id) {
|
||||
$ret['message'] = t('Missing poll id.');
|
||||
json_return_and_die($ret);
|
||||
}
|
||||
|
||||
$answer = q("select * from item where parent = %d and uid = %d and obj_type = 'Answer' limit 1",
|
||||
intval($id),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
|
||||
if ($fetch && $fetch[0]['obj_type'] === 'Question') {
|
||||
$obj = json_decode($fetch[0]['obj'],true);
|
||||
|
||||
if ($answer) {
|
||||
$ret['message'] = t('You have already submitted your vote for this poll.');
|
||||
json_return_and_die($ret);
|
||||
}
|
||||
else {
|
||||
|
||||
$poll = q("select * from item where id = %d limit 1",
|
||||
intval($id)
|
||||
);
|
||||
|
||||
if (!$poll && $poll[0]['obj_type'] !== 'Question') {
|
||||
$ret['message'] = t('Poll not found.');
|
||||
json_return_and_die($ret);
|
||||
}
|
||||
|
||||
$response = $_REQUEST['answer'];
|
||||
$obj = json_decode($poll[0]['obj'],true);
|
||||
|
||||
$valid = false;
|
||||
|
||||
if ($obj['oneOf']) {
|
||||
@@ -81,17 +89,18 @@ class Vote extends Controller {
|
||||
$item['aid'] = $channel['channel_account_id'];
|
||||
$item['uid'] = $channel['channel_id'];
|
||||
$item['item_origin'] = 1;
|
||||
$item['parent'] = $fetch[0]['id'];
|
||||
$item['parent_mid'] = $fetch[0]['mid'];
|
||||
$item['thr_parent'] = $fetch[0]['mid'];
|
||||
$item['parent'] = $poll[0]['id'];
|
||||
$item['parent_mid'] = $poll[0]['mid'];
|
||||
$item['thr_parent'] = $poll[0]['mid'];
|
||||
$item['uuid'] = new_uuid();
|
||||
$item['mid'] = z_root() . '/item/' . $item['uuid'];
|
||||
$item['verb'] = 'Create';
|
||||
$item['title'] = $res;
|
||||
$item['author_xchan'] = $channel['channel_hash'];
|
||||
$item['owner_xchan'] = $fetch[0]['author_xchan'];
|
||||
$item['allow_cid'] = '<' . $fetch[0]['author_xchan'] . '>';
|
||||
$item['owner_xchan'] = $poll[0]['author_xchan'];
|
||||
$item['allow_cid'] = '<' . $poll[0]['author_xchan'] . '>';
|
||||
$item['item_private'] = 1;
|
||||
$item['item_unseen'] = 0;
|
||||
$item['obj_type'] = 'Note';
|
||||
$item['author'] = channelx_by_n($channel['channel_id']);
|
||||
$item['obj'] = Activity::encode_item($item);
|
||||
@@ -104,7 +113,7 @@ class Vote extends Controller {
|
||||
|
||||
$x = item_store($item);
|
||||
|
||||
retain_item($fetch[0]['id']);
|
||||
retain_item($poll[0]['id']);
|
||||
|
||||
if($x['success']) {
|
||||
Master::Summon(['Notifier', 'like', $x['item_id']]);
|
||||
@@ -125,7 +134,7 @@ class Vote extends Controller {
|
||||
}
|
||||
|
||||
$ret['success'] = true;
|
||||
$ret['message'] = t('Response submitted. Updates may not appear instantly.');
|
||||
$ret['message'] = t('Your vote has been submitted. Updates may not appear instantly.');
|
||||
json_return_and_die($ret);
|
||||
}
|
||||
}
|
||||
|
||||
49
Zotlabs/Photo/ImageQuality.php
Normal file
49
Zotlabs/Photo/ImageQuality.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Hubzilla Community
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Photo;
|
||||
|
||||
/**
|
||||
* A class to represent image quality values.
|
||||
*/
|
||||
class ImageQuality
|
||||
{
|
||||
readonly string $mimeType;
|
||||
readonly int $value;
|
||||
|
||||
private const DEFAULT_VALUE = [
|
||||
'image/jpeg' => JPEG_QUALITY,
|
||||
'image/png' => PNG_QUALITY,
|
||||
'image/avif' => AVIF_QUALITY,
|
||||
'image/webp' => WEBP_QUALITY,
|
||||
];
|
||||
|
||||
public function __construct(string $mimeType, int $value) {
|
||||
[ $min, $max ] = $this->validRange($mimeType);
|
||||
|
||||
if ($value < $min || $value > $max) {
|
||||
$value = self::DEFAULT_VALUE[$mimeType];
|
||||
}
|
||||
|
||||
$this->value = $value;
|
||||
$this->mimeType = $mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the valid quality setting range for the given mime type.
|
||||
*
|
||||
* @param string $mimeType The mime type whose range to return.
|
||||
*
|
||||
* @return The valid range as an array with two elements as
|
||||
* [ $min, $max ].
|
||||
*/
|
||||
public static function validRange(string $mimeType): array {
|
||||
// Max quality for PNG is 10, for all others it's 100.
|
||||
return $mimeType === 'image/png' ? [ 0, 10 ] : [ 1, 100 ];
|
||||
}
|
||||
}
|
||||
@@ -115,9 +115,10 @@ abstract class PhotoDriver {
|
||||
*/
|
||||
public function __construct($data, $type = '') {
|
||||
$this->types = $this->supportedTypes();
|
||||
if(! array_key_exists($type, $this->types)) {
|
||||
if(!$type || !array_key_exists($type, $this->types)) {
|
||||
$type = 'image/jpeg';
|
||||
}
|
||||
|
||||
$this->type = $type;
|
||||
$this->valid = false;
|
||||
$this->load($data, $type);
|
||||
|
||||
@@ -12,6 +12,7 @@ class PhotoGd extends PhotoDriver {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see \Zotlabs\Photo\PhotoDriver::supportedTypes()
|
||||
*/
|
||||
public function supportedTypes() {
|
||||
@@ -31,12 +32,15 @@ class PhotoGd extends PhotoDriver {
|
||||
return $t;
|
||||
}
|
||||
|
||||
/**
|
||||
* phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter
|
||||
*/
|
||||
protected function load($data, $type) {
|
||||
$this->valid = false;
|
||||
if(! $data)
|
||||
return;
|
||||
|
||||
$this->image = @imagecreatefromstring($data);
|
||||
$this->image = imagecreatefromstring($data);
|
||||
if($this->image !== false) {
|
||||
$this->valid = true;
|
||||
$this->setDimensions();
|
||||
@@ -59,10 +63,13 @@ class PhotoGd extends PhotoDriver {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief GD imagedestroy() is deprected and noop since PHP version 8.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function destroy() {
|
||||
if($this->is_valid()) {
|
||||
imagedestroy($this->image);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,8 +98,6 @@ class PhotoGd extends PhotoDriver {
|
||||
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
|
||||
imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
|
||||
if($this->image)
|
||||
imagedestroy($this->image);
|
||||
|
||||
$this->image = $dest;
|
||||
$this->setDimensions();
|
||||
@@ -138,8 +143,6 @@ class PhotoGd extends PhotoDriver {
|
||||
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
|
||||
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h);
|
||||
if($this->image)
|
||||
imagedestroy($this->image);
|
||||
|
||||
$this->image = $dest;
|
||||
$this->setDimensions();
|
||||
@@ -147,6 +150,7 @@ class PhotoGd extends PhotoDriver {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see \Zotlabs\Photo\PhotoDriver::imageString()
|
||||
*/
|
||||
public function imageString() {
|
||||
@@ -154,65 +158,30 @@ class PhotoGd extends PhotoDriver {
|
||||
if(! $this->is_valid())
|
||||
return false;
|
||||
|
||||
$quality = false;
|
||||
|
||||
ob_start();
|
||||
|
||||
switch($this->getType()){
|
||||
|
||||
case 'image/avif':
|
||||
imageavif($this->image, null, $this->getQuality()->value);
|
||||
break;
|
||||
|
||||
case 'image/png':
|
||||
$quality = Config::Get('system', 'png_quality');
|
||||
|
||||
if((! $quality) || ($quality > 9)) {
|
||||
$quality = PNG_QUALITY;
|
||||
}
|
||||
|
||||
if (function_exists('imagepng')) {
|
||||
\imagepng($this->image, NULL, $quality);
|
||||
}
|
||||
|
||||
imagepng($this->image, null, $this->getQuality()->value);
|
||||
break;
|
||||
|
||||
case 'image/webp':
|
||||
$quality = Config::Get('system', 'webp_quality');
|
||||
imagewebp($this->image, null, $this->getQuality()->value);
|
||||
break;
|
||||
|
||||
if((! $quality) || ($quality > 100)) {
|
||||
$quality = WEBP_QUALITY;
|
||||
}
|
||||
case 'image/gif':
|
||||
imagegif($this->image);
|
||||
break;
|
||||
|
||||
if (function_exists('imagewebp')) {
|
||||
\imagewebp($this->image, NULL, $quality);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'image/avif':
|
||||
$quality = Config::Get('system', 'avif_quality');
|
||||
|
||||
if((! $quality) || ($quality > 100)) {
|
||||
$quality = AVIF_QUALITY;
|
||||
}
|
||||
|
||||
if (function_exists('imageavif')) {
|
||||
\imageavif($this->image, NULL, $quality);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'image/jpeg':
|
||||
// gd can lack imagejpeg(), but we verify during installation it is available
|
||||
|
||||
case 'image/jpeg':
|
||||
default:
|
||||
$quality = Config::Get('system', 'jpeg_quality');
|
||||
|
||||
if((! $quality) || ($quality > 100)) {
|
||||
$quality = JPEG_QUALITY;
|
||||
}
|
||||
|
||||
if (function_exists('imagejpeg')) {
|
||||
\imagejpeg($this->image, NULL, $quality);
|
||||
}
|
||||
|
||||
imagejpeg($this->image, null, $this->getQuality()->value);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -222,4 +191,23 @@ class PhotoGd extends PhotoDriver {
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default image quality for the given mime type.
|
||||
*
|
||||
* If the setting has been overridden in the database, we use that value,
|
||||
* otherwise the hardcoded defaults.
|
||||
*
|
||||
* @return ImageQuality The image quality value for the current mime type.
|
||||
*/
|
||||
private function getQuality(): ImageQuality {
|
||||
$key = match($this->getType()) {
|
||||
'image/avif' => 'avif_quality',
|
||||
'image/jpeg' => 'jpeg_quality',
|
||||
'image/png' => 'png_quality',
|
||||
'image/webp' => 'webp_quality',
|
||||
};
|
||||
|
||||
return new ImageQuality($this->getType(), Config::Get('system', $key));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class SmartyTemplate implements TemplateEngine {
|
||||
if ($root != '' && substr($root,-1) != '/' ) {
|
||||
$root .= '/';
|
||||
}
|
||||
foreach ( [ $root . "view/$lang/$file", $root . "view/en/$file", '' ] as $template_file) {
|
||||
foreach ( [ $root . "view/lang/$lang/$file", $root . "view/lang/en/$file", '' ] as $template_file) {
|
||||
if (is_file($template_file)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class Browser extends DAV\Browser\Plugin {
|
||||
*/
|
||||
public function __construct(&$auth) {
|
||||
$this->auth = $auth;
|
||||
parent::__construct(true, false);
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,7 +261,7 @@ class Browser extends DAV\Browser\Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
$display_path_encoded = Text::rawurlencode_parts($data['display_path']);
|
||||
$display_path_encoded = Text::rawurlencode_parts($data['display_path'] ?? '');
|
||||
$href_encoded = Text::rawurlencode_parts($href);
|
||||
|
||||
// put the array for this file together
|
||||
@@ -272,7 +272,7 @@ class Browser extends DAV\Browser\Plugin {
|
||||
$ft['rel_path'] = (($data) ? '/cloud/' . $nick .'/' . $display_path_encoded : $href_encoded);
|
||||
$ft['full_path'] = z_root() . (($data) ? '/cloud/' . $nick .'/' . $display_path_encoded : $href_encoded);
|
||||
$ft['name'] = $name;
|
||||
$ft['type'] = $type;
|
||||
$ft['type'] = $data['filetype'];
|
||||
$ft['size'] = $size;
|
||||
$ft['collection'] = (($type === 'Collection') ? true : false);
|
||||
$ft['size_formatted'] = userReadableSize($size);
|
||||
|
||||
@@ -519,8 +519,8 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
|
||||
* @return void
|
||||
*/
|
||||
function getDir() {
|
||||
|
||||
logger('GetDir: ' . $this->ext_path, LOGGER_DEBUG);
|
||||
|
||||
$this->auth->log();
|
||||
$modulename = \App::$module;
|
||||
|
||||
@@ -538,7 +538,6 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
|
||||
$file = trim($file, '/');
|
||||
$path_arr = explode('/', $file);
|
||||
|
||||
|
||||
if (! $path_arr)
|
||||
return;
|
||||
|
||||
@@ -558,9 +557,9 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
|
||||
$this->auth->owner_id = $channel_id;
|
||||
$this->auth->owner_nick = $channel_name;
|
||||
|
||||
$path = '/' . $channel_name;
|
||||
$folder = '';
|
||||
$os_path = '';
|
||||
$not_found = '';
|
||||
|
||||
for ($x = 1; $x < count($path_arr); $x++) {
|
||||
$r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0",
|
||||
@@ -568,15 +567,31 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
|
||||
dbesc($path_arr[$x]),
|
||||
intval($channel_id)
|
||||
);
|
||||
|
||||
if ($r && intval($r[0]['is_dir'])) {
|
||||
$folder = $r[0]['hash'];
|
||||
if (strlen($os_path))
|
||||
$os_path .= '/';
|
||||
$os_path .= $folder;
|
||||
|
||||
$path = $path . '/' . $r[0]['filename'];
|
||||
if (strlen($os_path)) {
|
||||
$os_path .= '/';
|
||||
}
|
||||
|
||||
$os_path .= $folder;
|
||||
}
|
||||
else {
|
||||
// if we got a bogus path collect the
|
||||
if (strlen($not_found)) {
|
||||
$not_found .= ',';
|
||||
}
|
||||
|
||||
$not_found .= $path_arr[$x];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($not_found) {
|
||||
throw new DAV\Exception\NotFound("Path $file does not exist. One or more directories could not be found: $not_found");
|
||||
}
|
||||
|
||||
$this->folder_hash = $folder;
|
||||
$this->os_path = $os_path;
|
||||
}
|
||||
@@ -866,7 +881,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
|
||||
$path = $path . '/' . $r[0]['filename'];
|
||||
}
|
||||
if (! $r) {
|
||||
$r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach
|
||||
$r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, is_photo, os_storage, created, edited from attach
|
||||
where folder = '%s' and filename = '%s' and uid = %d $perms order by filename limit 1",
|
||||
dbesc($folder),
|
||||
dbesc(basename($file)),
|
||||
@@ -875,7 +890,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
|
||||
}
|
||||
if (! $r) {
|
||||
$errors = true;
|
||||
$r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach
|
||||
$r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, is_photo, os_storage, created, edited from attach
|
||||
where folder = '%s' and filename = '%s' and uid = %d order by filename limit 1",
|
||||
dbesc($folder),
|
||||
dbesc(basename($file)),
|
||||
|
||||
@@ -93,19 +93,20 @@ class File extends DAV\Node implements DAV\IFile {
|
||||
|
||||
$x = attach_syspaths($this->auth->owner_id,$this->data['hash']);
|
||||
|
||||
$y = q("update attach set display_path = '%s where hash = '%s' and uid = %d",
|
||||
$y = q("update attach set display_path = '%s' where hash = '%s' and uid = %d",
|
||||
dbesc($x['path']),
|
||||
dbesc($this->data['hash']),
|
||||
intval($this->auth->owner_id)
|
||||
);
|
||||
|
||||
if($this->data->is_photo) {
|
||||
if($this->data['is_photo']) {
|
||||
$r = q("update photo set filename = '%s', display_path = '%s' where resource_id = '%s' and uid = %d",
|
||||
dbesc($newName),
|
||||
dbesc($x['path']),
|
||||
dbesc($this->data['hash']),
|
||||
intval($this->auth->owner_id)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
$ch = channelx_by_n($this->auth->owner_id);
|
||||
|
||||
@@ -54,9 +54,6 @@ class Epubthumb {
|
||||
imagecopyresampled($dest, $image, 0, 0, 0, 0, $width, $height, $srcwidth, $srcheight);
|
||||
|
||||
imagejpeg($dest, "{$file}.thumb");
|
||||
|
||||
imagedestroy($image);
|
||||
imagedestroy($dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ class Mp3audio {
|
||||
imagealphablending($dest, false);
|
||||
imagesavealpha($dest, true);
|
||||
imagecopyresampled($dest, $image, 0, 0, 0, 0, $width, $height, $srcwidth, $srcheight);
|
||||
imagedestroy($image);
|
||||
imagejpeg($dest,dbunescbin($attach['content']) . '.thumb');
|
||||
}
|
||||
}
|
||||
|
||||
29
Zotlabs/Update/_1264.php
Normal file
29
Zotlabs/Update/_1264.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Update;
|
||||
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
class _1264 {
|
||||
|
||||
function run() {
|
||||
|
||||
dbq("START TRANSACTION");
|
||||
|
||||
$admin_email = trim(Config::Get('system','admin_email'));
|
||||
|
||||
$r = q("UPDATE account SET account_roles = 0 WHERE account_created > '2025-06-24 00:00:00' AND account_email <> '%s'",
|
||||
dbesc($admin_email)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
dbq("COMMIT");
|
||||
return UPDATE_SUCCESS;
|
||||
}
|
||||
|
||||
dbq("ROLLBACK");
|
||||
return UPDATE_FAILED;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
30
Zotlabs/Update/_1265.php
Normal file
30
Zotlabs/Update/_1265.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Update;
|
||||
|
||||
class _1265 {
|
||||
|
||||
function run() {
|
||||
|
||||
dbq("START TRANSACTION");
|
||||
|
||||
|
||||
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
|
||||
$r = dbq("ALTER TABLE attach ALTER COLUMN filetype TYPE VARCHAR(128), ALTER COLUMN filetype SET NOT NULL, ALTER COLUMN filetype SET DEFAULT ''");
|
||||
}
|
||||
|
||||
if(ACTIVE_DBTYPE == DBTYPE_MYSQL) {
|
||||
$r = dbq("ALTER TABLE attach CHANGE filetype filetype CHAR(128) NOT NULL DEFAULT ''");
|
||||
}
|
||||
|
||||
if($r) {
|
||||
dbq("COMMIT");
|
||||
return UPDATE_SUCCESS;
|
||||
}
|
||||
|
||||
q("ROLLBACK");
|
||||
return UPDATE_FAILED;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -124,6 +124,10 @@ class HTTPSig {
|
||||
$alg = ($found) ? $matches[1] : null;
|
||||
|
||||
$keyInfo = self::get_key($key, $keytype, $keyId);
|
||||
if (!$keyInfo) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$publicKey = $keyInfo['public_key'];
|
||||
|
||||
$messageSigner = new HttpMessageSigner();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
namespace Zotlabs\Widget;
|
||||
|
||||
use App;
|
||||
|
||||
class Archive {
|
||||
|
||||
@@ -15,37 +16,40 @@ class Archive {
|
||||
|
||||
$o = '';
|
||||
|
||||
if(! \App::$profile_uid) {
|
||||
if (!App::$profile) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$uid = \App::$profile_uid;
|
||||
$uid = App::$profile['profile_uid'];
|
||||
|
||||
if(! feature_enabled($uid,'archives'))
|
||||
if (!feature_enabled($uid, 'archives')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if(! perm_is_allowed($uid,get_observer_hash(),'view_stream'))
|
||||
if (!perm_is_allowed($uid,get_observer_hash(),'view_stream')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$wall = ((array_key_exists('wall', $arr)) ? intval($arr['wall']) : 0);
|
||||
$wall = ((array_key_exists('articles', $arr)) ? 2 : $wall);
|
||||
|
||||
$style = ((array_key_exists('style', $arr)) ? $arr['style'] : 'select');
|
||||
$showend = ((get_pconfig($uid,'system','archive_show_end_date')) ? true : false);
|
||||
$mindate = get_pconfig($uid,'system','archive_mindate');
|
||||
$visible_years = get_pconfig($uid,'system','archive_visible_years',5);
|
||||
$showend = ((get_pconfig($uid, 'system', 'archive_show_end_date')) ? true : false);
|
||||
$mindate = get_pconfig($uid, 'system', 'archive_mindate');
|
||||
$visible_years = get_pconfig($uid, 'system', 'archive_visible_years', 5);
|
||||
|
||||
$url = z_root() . '/' . \App::$cmd;
|
||||
$url = z_root() . '/' . App::$module . '/' . App::$profile['channel_address'];
|
||||
|
||||
$ret = list_post_dates($uid,$wall,$mindate);
|
||||
|
||||
if(! count($ret))
|
||||
if (empty($ret)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$cutoff_year = intval(datetime_convert('',date_default_timezone_get(),'now','Y')) - $visible_years;
|
||||
$cutoff = ((array_key_exists($cutoff_year,$ret))? true : false);
|
||||
$cutoff = ((array_key_exists($cutoff_year,$ret)) ? true : false);
|
||||
|
||||
$o = replace_macros(get_markup_template('posted_date_widget.tpl'),array(
|
||||
return replace_macros(get_markup_template('posted_date_widget.tpl'), [
|
||||
'$title' => t('Archives'),
|
||||
'$size' => $visible_years,
|
||||
'$cutoff_year' => $cutoff_year,
|
||||
@@ -54,8 +58,8 @@ class Archive {
|
||||
'$style' => $style,
|
||||
'$showend' => $showend,
|
||||
'$dates' => $ret
|
||||
));
|
||||
return $o;
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Zotlabs\Widget;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use Zotlabs\Lib\Queue;
|
||||
use Zotlabs\Lib\QueueWorkerStats;
|
||||
|
||||
class Channel_activities {
|
||||
|
||||
@@ -25,10 +27,9 @@ class Channel_activities {
|
||||
self::$uid = local_channel();
|
||||
self::$channel = App::get_channel();
|
||||
|
||||
$o = '<div id="channel-activities" class="d-none overflow-hidden">';
|
||||
$o .= '<h2 class="mb-4">' . t('Welcome') . ' ' . self::$channel['channel_name'] . '!</h2>';
|
||||
//$o .= 'Last login date: ' . get_pconfig(self::$uid, 'system', 'stored_login_date') . ' from ' . get_pconfig(self::$uid, 'system', 'stored_login_addr');
|
||||
|
||||
if (is_site_admin()) {
|
||||
self::get_system_status();
|
||||
}
|
||||
self::get_photos_activity();
|
||||
self::get_files_activity();
|
||||
self::get_webpages_activity();
|
||||
@@ -42,28 +43,36 @@ class Channel_activities {
|
||||
|
||||
call_hooks('channel_activities_widget', $hookdata);
|
||||
|
||||
if (!$hookdata['activities']) {
|
||||
$o .= '<h3>' . t('No recent activities') . '</h3>';
|
||||
$o .= '</div>';
|
||||
return $o;
|
||||
$activity_html = '';
|
||||
|
||||
if ($hookdata['activities']) {
|
||||
$keys = array_column($hookdata['activities'], 'date');
|
||||
array_multisort($keys, SORT_DESC, $hookdata['activities']);
|
||||
|
||||
foreach ($hookdata['activities'] as $a) {
|
||||
$activity_html .= replace_macros(
|
||||
get_markup_template($a['tpl']),
|
||||
[
|
||||
'$url' => $a['url'] ?? null,
|
||||
'$icon' => $a['icon'],
|
||||
'$label' => $a['label'],
|
||||
'$items' => $a['items'],
|
||||
'$labels' => $a['labels'] ?? [],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$keys = array_column($hookdata['activities'], 'date');
|
||||
$tpl = get_markup_template('channel_activities_widget.tpl');
|
||||
|
||||
array_multisort($keys, SORT_DESC, $hookdata['activities']);
|
||||
return replace_macros($tpl, [
|
||||
'$welcome' => t('Welcome'),
|
||||
'$channel_name' => self::$channel['channel_name'],
|
||||
'$no_activities' => t('No recent activities'),
|
||||
'$activities' => $hookdata['activities'],
|
||||
'$activity_html' => $activity_html
|
||||
]);
|
||||
|
||||
foreach($hookdata['activities'] as $a) {
|
||||
$o .= replace_macros(get_markup_template($a['tpl']), [
|
||||
'$url' => $a['url'],
|
||||
'$icon' => $a['icon'],
|
||||
'$label' => $a['label'],
|
||||
'$items' => $a['items']
|
||||
]);
|
||||
}
|
||||
|
||||
$o .= '</div>';
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
private static function get_photos_activity() {
|
||||
@@ -220,11 +229,17 @@ class Channel_activities {
|
||||
$footer .= intval($notices[0]['total']) . ' ' . tt('notice', 'notices', intval($notices[0]['total']), 'noun');
|
||||
}
|
||||
|
||||
$tpl = get_markup_template('manage_channel_item.tpl');
|
||||
|
||||
$i[] = [
|
||||
'url' => z_root() . '/manage/' . $rr['channel_id'],
|
||||
'title' => '',
|
||||
'summary' => '<div class="text-truncate lh-sm"><img src="' . $rr['xchan_photo_s'] . '" class="menu-img-2">' . '<strong>' . $rr['channel_name'] . '</strong><br><small class="text-body-secondary">' . $rr['xchan_addr'] . '</small></div>',
|
||||
'footer' => $footer
|
||||
'url' => z_root() . '/manage/' . $rr['channel_id'],
|
||||
'title' => '',
|
||||
'summary' => replace_macros($tpl, [
|
||||
'$photo' => $rr['xchan_photo_s'],
|
||||
'$name' => $rr['channel_name'],
|
||||
'$addr' => $rr['xchan_addr'],
|
||||
]),
|
||||
'footer' => $footer
|
||||
];
|
||||
|
||||
$channels_activity++;
|
||||
@@ -246,5 +261,29 @@ class Channel_activities {
|
||||
|
||||
}
|
||||
|
||||
private static function get_system_status(): void {
|
||||
self::$activities['status'] = [
|
||||
'label' => t('System status'),
|
||||
'icon' => 'gpu-card',
|
||||
'date' => datetime_convert(),
|
||||
'items' => [
|
||||
'loadavg' => '0 / 0 / 0',
|
||||
'dbqueries' => 0,
|
||||
'outqueue' => 0,
|
||||
'queueworkers' => 0,
|
||||
'workqsz' => 0,
|
||||
'ts' => time(),
|
||||
],
|
||||
'tpl' => 'system_status_widget.tpl',
|
||||
'labels' => [
|
||||
'loadavg' => t('Load average'),
|
||||
'dbqueries' => t('DB queries/sec'),
|
||||
'outqueue' => t('Output queue'),
|
||||
'queueworkers' => t('Queue workers'),
|
||||
'workqsz' => t('Work queue size'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user