mirror of
https://framagit.org/hubzilla/core.git
synced 2026-06-22 17:26:14 -04:00
Compare commits
363 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44a0311798 | ||
|
|
2ac06fef9a | ||
|
|
962b4e5b4c | ||
|
|
91f1f8ed7c | ||
|
|
82817b4532 | ||
|
|
a3dd4a4d3d | ||
|
|
517e14cc38 | ||
|
|
70e8648d91 | ||
|
|
ee7b982358 | ||
|
|
2740f36259 | ||
|
|
ff16810e76 | ||
|
|
da1e0b965b | ||
|
|
24316bbb03 | ||
|
|
052341d24a | ||
|
|
0244d9423a | ||
|
|
042c98cf2d | ||
|
|
48ef490f50 | ||
|
|
37f0074674 | ||
|
|
7bd4217dd8 | ||
|
|
890a5942d6 | ||
|
|
7ecbce691d | ||
|
|
8c182c9d68 | ||
|
|
384eea79dc | ||
|
|
f8bc755c20 | ||
|
|
aa38e8360a | ||
|
|
fdf6680b29 | ||
|
|
8999a96ef0 | ||
|
|
b5d2236a58 | ||
|
|
91e59d3c4f | ||
|
|
a6ff444e19 | ||
|
|
29d4edb285 | ||
|
|
295d5f06dc | ||
|
|
362be52be0 | ||
|
|
20f5e654ad | ||
|
|
8c38ee8208 | ||
|
|
2c1c12825d | ||
|
|
bbcf7e9aa1 | ||
|
|
45a2ebc662 | ||
|
|
aece22aee6 | ||
|
|
7c0f98a513 | ||
|
|
9369085835 | ||
|
|
00b3039b6e | ||
|
|
bcfdb73001 | ||
|
|
991815469f | ||
|
|
a0c98e070e | ||
|
|
4ba470318c | ||
|
|
8d45fd36a1 | ||
|
|
6ed2301a94 | ||
|
|
a0eb701503 | ||
|
|
13002af4c3 | ||
|
|
ee988edc83 | ||
|
|
7579749adc | ||
|
|
cf408c3fac | ||
|
|
6c8da6ce36 | ||
|
|
e19a050b92 | ||
|
|
546c4fcad2 | ||
|
|
2782b6d724 | ||
|
|
324b281813 | ||
|
|
69a23c604d | ||
|
|
9b13055dfe | ||
|
|
06afd8a375 | ||
|
|
6f027544d6 | ||
|
|
ebab5ff281 | ||
|
|
5fb6e5d6f7 | ||
|
|
5a84ffdcda | ||
|
|
ad993645be | ||
|
|
0701cde239 | ||
|
|
a136c288d5 | ||
|
|
1624a2620a | ||
|
|
f4769d0f04 | ||
|
|
c8e30a00e2 | ||
|
|
51745d3652 | ||
|
|
762d402dea | ||
|
|
b05a440f03 | ||
|
|
9bb4988eda | ||
|
|
49cb73c8c7 | ||
|
|
2a00bd9a28 | ||
|
|
5813b65aed | ||
|
|
f8acd1d3a5 | ||
|
|
632721da4e | ||
|
|
f3a702e927 | ||
|
|
c55662510d | ||
|
|
f01e917fff | ||
|
|
b80cba4e68 | ||
|
|
f2b28f5bbb | ||
|
|
4aeb4963a4 | ||
|
|
cf66f245b4 | ||
|
|
91baa52b8b | ||
|
|
c626985448 | ||
|
|
586cda5155 | ||
|
|
513cd3b99a | ||
|
|
f05b8bd87d | ||
|
|
e466c73407 | ||
|
|
e5aae2af0f | ||
|
|
173c4d242d | ||
|
|
76b8c36f7c | ||
|
|
f35352090e | ||
|
|
e5db47e0d5 | ||
|
|
6a52e502aa | ||
|
|
6a866fe904 | ||
|
|
d9d239bf3a | ||
|
|
429d15f009 | ||
|
|
2b44be58c3 | ||
|
|
c44db397ff | ||
|
|
a385fdff37 | ||
|
|
20aacb82c6 | ||
|
|
0d17d8dad9 | ||
|
|
2ab0118c13 | ||
|
|
30419bdbf6 | ||
|
|
c958cc6f90 | ||
|
|
38ac60e618 | ||
|
|
23a19ecf1f | ||
|
|
8b75f50f23 | ||
|
|
436b1673cf | ||
|
|
96210f5ecc | ||
|
|
9e9e8efb2d | ||
|
|
e9dc4b553b | ||
|
|
c70bd08c10 | ||
|
|
69109a558b | ||
|
|
4aff6d19d6 | ||
|
|
3cb5d14037 | ||
|
|
5f685bcf63 | ||
|
|
cb44f7e360 | ||
|
|
8f74ee67e3 | ||
|
|
b0a11537de | ||
|
|
4de9cb1142 | ||
|
|
158ddfb009 | ||
|
|
2b4f344181 | ||
|
|
2e5f922561 | ||
|
|
a6498a0cfc | ||
|
|
1073392398 | ||
|
|
76064dbb33 | ||
|
|
1ab4f36a1b | ||
|
|
cea9c88b9e | ||
|
|
babe118383 | ||
|
|
2b140e53cc | ||
|
|
fb1514a782 | ||
|
|
7679894684 | ||
|
|
520cf1015c | ||
|
|
64bd8eef4b | ||
|
|
aa42f6a6b2 | ||
|
|
13345d3cbe | ||
|
|
888ee16d52 | ||
|
|
0f3e01f343 | ||
|
|
79220ede66 | ||
|
|
b498e50f74 | ||
|
|
8c87b06caa | ||
|
|
339e702beb | ||
|
|
8750c5f7bc | ||
|
|
c3b4397ea3 | ||
|
|
a3f1189df7 | ||
|
|
67d8b977b6 | ||
|
|
5b1e532791 | ||
|
|
4f129e05e5 | ||
|
|
99bff67168 | ||
|
|
bb3caeb629 | ||
|
|
45275910e6 | ||
|
|
c04e781926 | ||
|
|
09a609ef6b | ||
|
|
db5e524e3c | ||
|
|
8f9e9116df | ||
|
|
f944f46744 | ||
|
|
3f5c45a567 | ||
|
|
b51ed67efb | ||
|
|
afdc3d6d18 | ||
|
|
6a710c3cc3 | ||
|
|
0c1d0f7498 | ||
|
|
81ba070e1a | ||
|
|
a7812657f1 | ||
|
|
18725c47a0 | ||
|
|
75e1b70584 | ||
|
|
1dc73935d9 | ||
|
|
7d7b43c5b9 | ||
|
|
b5223a4efb | ||
|
|
d71c2c245f | ||
|
|
526729c0f1 | ||
|
|
1cd3369f6a | ||
|
|
c26ae553e6 | ||
|
|
8d78698d00 | ||
|
|
d5c189753a | ||
|
|
9861e7a0c4 | ||
|
|
6d5fa9205c | ||
|
|
0fee7804fb | ||
|
|
4002dbaa8b | ||
|
|
57e32a7912 | ||
|
|
b6a72d6e4e | ||
|
|
6e592ed200 | ||
|
|
6c033fc776 | ||
|
|
7c4362db53 | ||
|
|
f7bf9ede72 | ||
|
|
1aeb05628b | ||
|
|
b464fae3bf | ||
|
|
a34ce0732d | ||
|
|
01ebd51fb2 | ||
|
|
6c6fc82f43 | ||
|
|
192ab22cda | ||
|
|
2dc1adf091 | ||
|
|
6a3d372050 | ||
|
|
c95359024c | ||
|
|
1097bcdaf9 | ||
|
|
f910de849f | ||
|
|
5c2e10c01e | ||
|
|
569f243ebd | ||
|
|
d4b1bcd641 | ||
|
|
e237cf226a | ||
|
|
0745d0616a | ||
|
|
2114779037 | ||
|
|
9600789d6b | ||
|
|
ea7559c158 | ||
|
|
baa12b7497 | ||
|
|
5860abf46f | ||
|
|
2874d3e1e1 | ||
|
|
36778850ee | ||
|
|
24132e56d9 | ||
|
|
8873c10364 | ||
|
|
77e1220cf9 | ||
|
|
d846cefade | ||
|
|
5b7387459c | ||
|
|
5d64a9c90f | ||
|
|
ffaa985339 | ||
|
|
f57fbaa5dd | ||
|
|
aba8002170 | ||
|
|
0bdffc4a2d | ||
|
|
400dfb4e6b | ||
|
|
6b951734ce | ||
|
|
051e2ed6cd | ||
|
|
ef2952b5fd | ||
|
|
d655e1d765 | ||
|
|
db70ed006d | ||
|
|
9e2a253dda | ||
|
|
b629eb5657 | ||
|
|
2e674cd0b3 | ||
|
|
3330e9a19a | ||
|
|
c0d93bbcf4 | ||
|
|
d372daff60 | ||
|
|
f742e6e394 | ||
|
|
603c5692ae | ||
|
|
abe2ab229a | ||
|
|
5ad9939bcf | ||
|
|
70470016cc | ||
|
|
c307a71f53 | ||
|
|
f62d16d274 | ||
|
|
5f942d78e6 | ||
|
|
c8158c3d62 | ||
|
|
1f4762060f | ||
|
|
8c9d2bc6f6 | ||
|
|
43c2e71b25 | ||
|
|
76a1a6da34 | ||
|
|
0fd8e02a88 | ||
|
|
55c4bfb670 | ||
|
|
ea1030f8bf | ||
|
|
ee149ed1eb | ||
|
|
6e59d95da5 | ||
|
|
5cb1a9dcc6 | ||
|
|
f8c631a3f0 | ||
|
|
0f453ae4cf | ||
|
|
82e704ec5b | ||
|
|
670228ff63 | ||
|
|
dbc712c53c | ||
|
|
bb6011ea73 | ||
|
|
1915f34de8 | ||
|
|
675f26fe90 | ||
|
|
24d862c1bc | ||
|
|
a1e583129f | ||
|
|
3a01aa40d8 | ||
|
|
ec66949b35 | ||
|
|
542a527d0d | ||
|
|
5e0c392287 | ||
|
|
05ff94941c | ||
|
|
a27d75d610 | ||
|
|
446e842904 | ||
|
|
ba153e2c18 | ||
|
|
3342ea6891 | ||
|
|
8c3d5fd295 | ||
|
|
5fc58fb10a | ||
|
|
4e2ae9cd4e | ||
|
|
4167ca2e5c | ||
|
|
9b3e9dcf02 | ||
|
|
f203fcc92e | ||
|
|
0b20069c20 | ||
|
|
f89ce93940 | ||
|
|
1ddbc8a26d | ||
|
|
7dad60bbd5 | ||
|
|
a66b4626fb | ||
|
|
74d7fa61d9 | ||
|
|
63fb8d0392 | ||
|
|
384de0925e | ||
|
|
5b5c569c82 | ||
|
|
57796a2f96 | ||
|
|
ec6cec8854 | ||
|
|
1cbaab5889 | ||
|
|
5126613825 | ||
|
|
07097d2fa3 | ||
|
|
8c9fb32ca0 | ||
|
|
330b36159d | ||
|
|
046152e798 | ||
|
|
11ca5bb7d5 | ||
|
|
e0838ff8ab | ||
|
|
1b0e268416 | ||
|
|
1a27fad9b9 | ||
|
|
c2cfe481df | ||
|
|
3a56fb6ec6 | ||
|
|
1a614ea023 | ||
|
|
65d98af24c | ||
|
|
62d35627f3 | ||
|
|
84f1ed1587 | ||
|
|
18914f2081 | ||
|
|
5bdd699c9d | ||
|
|
049fc91615 | ||
|
|
5d330e18ff | ||
|
|
07662ff076 | ||
|
|
247cf40ace | ||
|
|
4a6b450c55 | ||
|
|
efd2f78274 | ||
|
|
690ff955fa | ||
|
|
d8306fca6f | ||
|
|
4ae81d753c | ||
|
|
93dda6f1fd | ||
|
|
74436eb03e | ||
|
|
677e730235 | ||
|
|
06c0af50c3 | ||
|
|
f6b66f4cad | ||
|
|
ca913e5077 | ||
|
|
984980b170 | ||
|
|
a57739c462 | ||
|
|
92246ce3a8 | ||
|
|
4a72ea6666 | ||
|
|
dcfdf3a5d7 | ||
|
|
9083e99d2a | ||
|
|
14fc0c735b | ||
|
|
79405cf1d3 | ||
|
|
89285f1408 | ||
|
|
17c0bb2069 | ||
|
|
192736f6d7 | ||
|
|
4eee8ce770 | ||
|
|
3dcfdba74b | ||
|
|
5428053663 | ||
|
|
1e184b781b | ||
|
|
c1ddb89990 | ||
|
|
7ac7cb129f | ||
|
|
11b9e546a9 | ||
|
|
3a3fd38e3a | ||
|
|
0a679e503e | ||
|
|
e7c529f2c3 | ||
|
|
dfc70021e3 | ||
|
|
0140f9fe56 | ||
|
|
03243df16a | ||
|
|
2805520d1b | ||
|
|
fb7ca18820 | ||
|
|
1b00d5657f | ||
|
|
9ec516e5a4 | ||
|
|
5cb92b6e21 | ||
|
|
2ddb88a34d | ||
|
|
71761c9039 | ||
|
|
bd5e834b42 | ||
|
|
4c434129a6 | ||
|
|
66333aedb7 | ||
|
|
f6d9406063 | ||
|
|
6e881bcef2 | ||
|
|
12a963cc40 | ||
|
|
f89975fd0e | ||
|
|
00512579f3 | ||
|
|
697a74f37e |
@@ -1,5 +1,4 @@
|
||||
stages:
|
||||
- pretest
|
||||
- test
|
||||
- deploy
|
||||
|
||||
@@ -26,6 +25,23 @@ 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
|
||||
@@ -37,10 +53,10 @@ variables:
|
||||
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" --skip-ssl "$MYSQL_DATABASE"
|
||||
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
|
||||
# Show databases and relations/tables of hubzilla's database
|
||||
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" --skip-ssl "$MYSQL_DATABASE"
|
||||
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" --skip-ssl "$MYSQL_DATABASE"
|
||||
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
|
||||
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
|
||||
# Run the actual tests
|
||||
- touch dbfail.out
|
||||
- vendor/bin/phpunit -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=$?
|
||||
@@ -84,55 +100,29 @@ variables:
|
||||
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 libmagickcore-7.q16-dev libmagickwand-dev unzip zip
|
||||
- pecl install imagick xdebug yaml
|
||||
- docker-php-ext-enable imagick xdebug yaml
|
||||
- docker-php-ext-configure gd --with-jpeg=/usr/include/
|
||||
- docker-php-ext-install gd gmp 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
|
||||
|
||||
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:
|
||||
# PHP8.1 with MySQL 8.0
|
||||
php8.1_mysql8.0.22:
|
||||
image: php:8.1
|
||||
services:
|
||||
- mysql:8.0
|
||||
<<: *job_definition_mysql
|
||||
<<: *artifacts_template
|
||||
|
||||
# PHP8.2 with MariaDB 10.6
|
||||
php8.2_mariadb10.6:
|
||||
# PHP8.1 with MariaDB 10.6
|
||||
php8.1_mariadb10.6:
|
||||
image: php:8.1
|
||||
services:
|
||||
- name: mariadb:10.6
|
||||
alias: mysql
|
||||
<<: *job_definition_mysql
|
||||
<<: *artifacts_template
|
||||
|
||||
# PHP8.2 with PostgreSQL 14
|
||||
php8.2_postgres14:
|
||||
# PHP8.1 with PostgreSQL 12
|
||||
php8.1_postgres12:
|
||||
image: php:8.1
|
||||
services:
|
||||
- postgres:14-alpine
|
||||
- postgres:12-alpine
|
||||
<<: *job_definition_postgres
|
||||
<<: *artifacts_template
|
||||
|
||||
|
||||
232
CHANGELOG
232
CHANGELOG
@@ -1,235 +1,3 @@
|
||||
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,6 +38,7 @@ 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,7 +212,6 @@ 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) {
|
||||
|
||||
@@ -231,6 +230,70 @@ 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,8 +5,6 @@ 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 {
|
||||
|
||||
@@ -14,77 +12,52 @@ class Convo {
|
||||
|
||||
logger('convo invoked: ' . print_r($argv, true));
|
||||
|
||||
if ($argc < 4) {
|
||||
if ($argc != 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
$channels = explode(',', $argv[1]);
|
||||
if (!$channels) {
|
||||
$id = $argv[1];
|
||||
$channel_id = intval($argv[2]);
|
||||
$contact_hash = $argv[3];
|
||||
|
||||
$channel = channelx_by_n($channel_id);
|
||||
if (!$channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
$observer_hash = $argv[2];
|
||||
if (!$observer_hash) {
|
||||
$r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash
|
||||
WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1",
|
||||
intval($channel_id),
|
||||
dbesc($contact_hash)
|
||||
);
|
||||
|
||||
if (!$r) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mid = $argv[3];
|
||||
if (!$mid) {
|
||||
$contact = array_shift($r);
|
||||
|
||||
$obj = new ASCollection($id, $channel);
|
||||
|
||||
$messages = $obj->get();
|
||||
|
||||
if (!$messages) {
|
||||
return;
|
||||
}
|
||||
|
||||
$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;
|
||||
foreach ($messages as $message) {
|
||||
if (is_string($message)) {
|
||||
$message = Activity::fetch($message, $channel);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
// set client flag because comments will probably just be objects and not full blown activities
|
||||
// and that lets us use implied_create
|
||||
$AS = new ActivityStreams($message);
|
||||
if ($AS->is_valid() && is_array($AS->obj)) {
|
||||
$item = Activity::decode_note($AS);
|
||||
$item['item_fetched'] = true;
|
||||
Activity::store($channel, $contact['abook_xchan'], $AS, $item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\ObjCache;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
use Zotlabs\Lib\Libzotdir;
|
||||
|
||||
@@ -94,7 +92,7 @@ class Cron {
|
||||
// delete expired access tokens
|
||||
|
||||
$r = q("select atoken_id from atoken where atoken_expires > '%s' and atoken_expires < %s",
|
||||
dbesc(DBA::$dba->get_null_date()),
|
||||
dbesc(NULL_DATE),
|
||||
db_utcnow()
|
||||
);
|
||||
if ($r) {
|
||||
@@ -238,73 +236,6 @@ 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,8 +74,6 @@ class Externals {
|
||||
}
|
||||
}
|
||||
|
||||
$attempts++;
|
||||
|
||||
if (!$url) {
|
||||
continue;
|
||||
}
|
||||
@@ -87,6 +85,7 @@ class Externals {
|
||||
$blacklisted = true;
|
||||
}
|
||||
|
||||
$attempts++;
|
||||
|
||||
// make sure we can eventually break out if somebody blacklists all known sites
|
||||
|
||||
|
||||
@@ -36,8 +36,6 @@ class Fetchparents {
|
||||
Activity::fetch_and_store_parents($channel, $observer_hash, $mid, null, $force);
|
||||
}
|
||||
|
||||
Activity::init_background_fetch($observer_hash);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ 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;
|
||||
|
||||
@@ -266,6 +264,7 @@ class Notifier {
|
||||
}
|
||||
|
||||
if (!item_forwardable($target_item)) {
|
||||
//hz_syslog(print_r($target_item,true));
|
||||
logger('notifier: target item not forwardable', LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
@@ -318,15 +317,10 @@ class Notifier {
|
||||
return;
|
||||
}
|
||||
|
||||
$m = ObjCache::Get($target_item['mid']);
|
||||
|
||||
if (!$m) {
|
||||
$m = IConfig::Get($target_item, 'activitypub', 'rawmsg');
|
||||
}
|
||||
|
||||
$m = get_iconfig($target_item, 'activitypub', 'signed_data');
|
||||
// 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 = $m;
|
||||
self::$encoded_item = json_decode($m, true);
|
||||
}
|
||||
else {
|
||||
$activity = Activity::encode_activity($target_item);
|
||||
|
||||
@@ -9,14 +9,10 @@ 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]));
|
||||
|
||||
$update_id = intval($argv[1]);
|
||||
if (($argc > 1) && (intval($argv[1])))
|
||||
$update_id = intval($argv[1]);
|
||||
|
||||
if (!$update_id) {
|
||||
logger('onedirsync: no update id');
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\ASCollection;
|
||||
@@ -16,14 +15,10 @@ class Onepoll {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if ($argc < 2 || is_int($argv[1]) === false) {
|
||||
logger('onepoll: no contact');
|
||||
return;
|
||||
}
|
||||
|
||||
logger('onepoll: start');
|
||||
|
||||
$contact_id = intval($argv[1]);
|
||||
if (($argc > 1) && (intval($argv[1])))
|
||||
$contact_id = intval($argv[1]);
|
||||
|
||||
if (!$contact_id) {
|
||||
logger('onepoll: no contact');
|
||||
@@ -39,7 +34,7 @@ class Onepoll {
|
||||
$contacts = q("SELECT abook.*, xchan.* FROM abook
|
||||
LEFT JOIN xchan ON xchan_hash = abook_xchan
|
||||
WHERE abook_id = %d",
|
||||
$contact_id
|
||||
intval($contact_id)
|
||||
);
|
||||
|
||||
if (!$contacts) {
|
||||
@@ -58,7 +53,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'] <= DBA::$dba->get_null_date()))
|
||||
$last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= NULL_DATE))
|
||||
? datetime_convert('UTC', 'UTC', 'now - 7 days')
|
||||
: datetime_convert('UTC', 'UTC', $contact['abook_updated'] . ' - 2 days')
|
||||
);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
class Poller {
|
||||
@@ -118,7 +117,7 @@ class Poller {
|
||||
|
||||
// if we've never connected with them, start the mark for death countdown from now
|
||||
|
||||
if ($c <= DBA::$dba->get_null_date()) {
|
||||
if ($c <= NULL_DATE) {
|
||||
q("update abook set abook_connected = '%s' where abook_id = %d",
|
||||
dbesc(datetime_convert()),
|
||||
intval($contact['abook_id'])
|
||||
@@ -174,7 +173,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(DBA::$dba->get_null_date()),
|
||||
dbesc(NULL_DATE),
|
||||
db_utcnow(),
|
||||
db_quoteinterval('7 DAY')
|
||||
);
|
||||
@@ -185,7 +184,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'] > DBA::$dba->get_null_date())
|
||||
if ($rr['ud_last'] > NULL_DATE)
|
||||
if ($rr['ud_last'] > datetime_convert('UTC', 'UTC', 'now - 1 day'))
|
||||
continue;
|
||||
|
||||
|
||||
@@ -1,57 +1,15 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* A class for hooking into Hubzilla.
|
||||
* @brief Hook class.
|
||||
*
|
||||
* 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);
|
||||
@@ -87,14 +45,6 @@ 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) {
|
||||
@@ -104,20 +54,6 @@ 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);
|
||||
@@ -134,13 +70,11 @@ class Hook {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all hooks handlers from a given source file.
|
||||
* @brief Unregister all hooks with this file component.
|
||||
*
|
||||
* Useful for addon upgrades where you want to clean out old interfaces.
|
||||
*
|
||||
* @param string $file The source file where the hook handlers were defined.
|
||||
*
|
||||
* @return The result from the database delete operation.
|
||||
* @param string $file
|
||||
*/
|
||||
static public function unregister_by_file($file) {
|
||||
$r = q("DELETE FROM hook WHERE file = '%s' ",
|
||||
@@ -151,22 +85,31 @@ class Hook {
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a hook into a page request.
|
||||
* @brief Inserts a hook into a page request.
|
||||
*
|
||||
* Insert a short-lived hook into the running page request. Hooks are
|
||||
* normally persistent so that they can be called across asynchronous
|
||||
* processes such as delivery and poll processes.
|
||||
* Insert 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.
|
||||
*
|
||||
* This function lets you attach a hook callback immediately which will not
|
||||
* persist beyond the life of this page request or the current process.
|
||||
* insert_hook lets you attach a hook callback immediately
|
||||
* which will not persist beyond the life of this page request
|
||||
* or the current process.
|
||||
*
|
||||
* @param string $hook Name of hook to attach callback.
|
||||
* @param string|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.
|
||||
* @param string $hook
|
||||
* name of hook to attach callback
|
||||
* @param string $fn
|
||||
* function name of callback handler
|
||||
* @param int $version
|
||||
* hook interface version, 0 uses two callback params, 1 uses one callback param
|
||||
* @param int $priority
|
||||
* currently not implemented in this function, would require the hook array to be resorted
|
||||
*/
|
||||
static public function insert($hook, $fn, $version = 0, $priority = 0) {
|
||||
if(is_array($fn)) {
|
||||
$fn = serialize($fn);
|
||||
}
|
||||
|
||||
if(! is_array(App::$hooks))
|
||||
App::$hooks = array();
|
||||
|
||||
@@ -176,4 +119,4 @@ class Hook {
|
||||
App::$hooks[$hook][] = array('', $fn, $priority, $version);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,106 +1,23 @@
|
||||
<?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 {
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
static function register($file,$modname) {
|
||||
$rt = self::get();
|
||||
|
||||
foreach ($rt as $r) {
|
||||
if ($r[0] === $file && $r[1] === $modname) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$rt[] = [ $file, $modname ];
|
||||
self::set($rt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
static function unregister($file,$modname) {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
@@ -108,23 +25,7 @@ class Route {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
static function unregister_by_file($file) {
|
||||
$rt = self::get();
|
||||
if($rt) {
|
||||
$n = [];
|
||||
@@ -137,18 +38,11 @@ class Route {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
static function get() {
|
||||
return Config::Get('system','routes',[]);
|
||||
}
|
||||
|
||||
private static function set(array $r): mixed {
|
||||
static function set($r) {
|
||||
return Config::Set('system','routes',$r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,6 @@ 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);
|
||||
}
|
||||
@@ -24,7 +17,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,63 +7,27 @@ 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(): string
|
||||
{
|
||||
public static function getAge() {
|
||||
return Config::Get('system', 'as_object_cache_time', '10 MINUTE');
|
||||
}
|
||||
|
||||
public static function Get(string $key): array
|
||||
{
|
||||
if (!self::isEnabled()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ret = Cache::get($key, self::getAge());
|
||||
|
||||
if ($ret) {
|
||||
return json_unserialize($ret);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public static function Set(string $key, array $obj): void
|
||||
{
|
||||
public static function Get($key) {
|
||||
if (!self::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self::isCacheable($obj)) {
|
||||
return Cache::get($key, self::getAge());
|
||||
}
|
||||
|
||||
public static function Set($key, $value) {
|
||||
if (!self::isEnabled()) {
|
||||
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;
|
||||
Cache::set($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,19 +31,7 @@ class ASCollection {
|
||||
}
|
||||
|
||||
if (is_string($obj)) {
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
$data = Activity::fetch($obj, $channel);
|
||||
$this->history[] = $obj;
|
||||
}
|
||||
|
||||
@@ -95,8 +83,6 @@ class ASCollection {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = null;
|
||||
|
||||
if (is_array($this->nextpage)) {
|
||||
$data = $this->nextpage;
|
||||
}
|
||||
@@ -106,20 +92,7 @@ class ASCollection {
|
||||
// recursion detected
|
||||
return false;
|
||||
}
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
$data = Activity::fetch($this->nextpage, $this->channel);
|
||||
$this->history[] = $this->nextpage;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Access\PermissionLimits;
|
||||
use Zotlabs\Access\PermissionRoles;
|
||||
use Zotlabs\Access\Permissions;
|
||||
@@ -92,8 +91,6 @@ class Activity {
|
||||
|
||||
logger('fetch: ' . $url, LOGGER_DEBUG);
|
||||
|
||||
$start_timestamp = microtime(true);
|
||||
|
||||
if (strpos($url, 'x-zot:') === 0) {
|
||||
$x = ZotURL::fetch($url, $channel);
|
||||
}
|
||||
@@ -132,6 +129,7 @@ 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]);
|
||||
}
|
||||
|
||||
@@ -384,12 +382,7 @@ class Activity {
|
||||
if ($items) {
|
||||
$x = [];
|
||||
foreach ($items as $i) {
|
||||
$m = ObjCache::Get($i['mid']);
|
||||
|
||||
if (!$m) {
|
||||
$m = IConfig::Get($i['id'], 'activitypub', 'rawmsg');
|
||||
}
|
||||
|
||||
$m = IConfig::Get($i['id'], 'activitypub', 'rawmsg');
|
||||
if ($m) {
|
||||
if (is_string($m))
|
||||
$t = json_decode($m, true);
|
||||
@@ -508,8 +501,8 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
$ret['id'] = ((strpos($i['mid'], 'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid']));
|
||||
$ret['uuid'] = $i['uuid'];
|
||||
$ret['id'] = ((strpos($i['mid'], 'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid']));
|
||||
$ret['diaspora:guid'] = $i['uuid'];
|
||||
|
||||
$images = [];
|
||||
$audios = [];
|
||||
@@ -569,7 +562,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'] > DBA::$dba->get_null_date()) {
|
||||
if ($i['expires'] > NULL_DATE) {
|
||||
$ret['expires'] = datetime_convert('UTC', 'UTC', $i['expires'], ATOM_TIME);
|
||||
}
|
||||
|
||||
@@ -594,7 +587,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'] > DBA::$dba->get_null_date()) {
|
||||
if (array_key_exists('comments_closed', $i) && $i['comments_closed'] !== EMPTY_STR && $i['comments_closed'] > NULL_DATE) {
|
||||
if ($ret['commentPolicy']) {
|
||||
$ret['commentPolicy'] .= ' ';
|
||||
}
|
||||
@@ -627,6 +620,15 @@ 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;
|
||||
@@ -644,57 +646,6 @@ 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;
|
||||
@@ -838,7 +789,8 @@ class Activity {
|
||||
if ($iconfig && array_key_exists('iconfig', $item) && is_array($item['iconfig'])) {
|
||||
foreach ($item['iconfig'] as $att) {
|
||||
if ($att['sharing']) {
|
||||
$ret[] = ['type' => 'PropertyValue', 'name' => 'zot.' . $att['cat'] . '.' . $att['k'], 'value' => $att['v']];
|
||||
$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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -859,10 +811,6 @@ 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];
|
||||
@@ -1009,7 +957,7 @@ class Activity {
|
||||
$ret['id'] = ((strpos($i['mid'], 'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid']));
|
||||
}
|
||||
|
||||
$ret['uuid'] = $i['uuid'];
|
||||
$ret['diaspora:guid'] = $i['uuid'];
|
||||
|
||||
if (!empty($i['title']))
|
||||
$ret['name'] = html2plain(bbcode($i['title']));
|
||||
@@ -1126,7 +1074,6 @@ class Activity {
|
||||
return [];
|
||||
}
|
||||
|
||||
/* Those should not be required in activities anymore after version 11
|
||||
$t = self::encode_taxonomy($i);
|
||||
if ($t) {
|
||||
$ret['tag'] = $t;
|
||||
@@ -1136,7 +1083,6 @@ class Activity {
|
||||
if ($a) {
|
||||
$ret['attachment'] = $a;
|
||||
}
|
||||
*/
|
||||
|
||||
if (intval($i['item_private']) === 0) {
|
||||
$ret['to'] = [ACTIVITY_PUBLIC_INBOX];
|
||||
@@ -1565,7 +1511,7 @@ class Activity {
|
||||
'abook_created' => datetime_convert(),
|
||||
'abook_updated' => datetime_convert(),
|
||||
'abook_connected' => datetime_convert(),
|
||||
'abook_dob' => DBA::$dba->get_null_date(),
|
||||
'abook_dob' => NULL_DATE,
|
||||
'abook_pending' => intval(($automatic) ? 0 : 1),
|
||||
'abook_instance' => z_root()
|
||||
]
|
||||
@@ -1964,7 +1910,7 @@ class Activity {
|
||||
);
|
||||
|
||||
if ($x) {
|
||||
return sprintf('[zrl=%s]@%s[/zrl]', $x[0]['xchan_url'], $x[0]['xchan_name']);
|
||||
return sprintf('@[zrl=%s]%s[/zrl]', $x[0]['xchan_url'], $x[0]['xchan_name']);
|
||||
}
|
||||
return '@{' . $id . '}';
|
||||
|
||||
@@ -2017,8 +1963,6 @@ class Activity {
|
||||
$multi = true;
|
||||
}
|
||||
|
||||
$answer_found = false;
|
||||
|
||||
if ($response) {
|
||||
$mid = $response['mid'];
|
||||
$content = trim($response['title']);
|
||||
@@ -2048,6 +1992,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
$answer_found = false;
|
||||
$foundPrevious = false;
|
||||
if ($multi) {
|
||||
for ($c = 0; $c < count($o['anyOf']); $c++) {
|
||||
@@ -2089,7 +2034,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($pollItem['comments_closed'] > DBA::$dba->get_null_date()) {
|
||||
if ($pollItem['comments_closed'] > 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
|
||||
@@ -2141,16 +2086,15 @@ 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']) &&
|
||||
($obj_type === 'Follow' || ActivityStreams::is_an_actor($obj_type))
|
||||
($act->objprop('type') === 'Follow' || ActivityStreams::is_an_actor($act->objprop('type')))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -2223,7 +2167,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($act->type, ['Invite', 'Create']) && $obj_type === 'Event') {
|
||||
if (in_array($act->type, ['Invite', 'Create']) && $act->objprop('type') === 'Event') {
|
||||
$s['mid'] = $s['parent_mid'] = $act->id;
|
||||
}
|
||||
|
||||
@@ -2255,25 +2199,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']);
|
||||
$content['content'] = sprintf(t('Likes %1$s\'s %2$s'), $mention, $act->obj['type']) . EOL . EOL . ($content['content'] ?? '');
|
||||
}
|
||||
if ($act->type === 'Dislike') {
|
||||
$content['content'] = sprintf(t('Doesn\'t like %1$s\'s %2$s'), $mention, $act->obj['type']);
|
||||
$content['content'] = sprintf(t('Doesn\'t like %1$s\'s %2$s'), $mention, $act->obj['type']) . EOL . EOL . ($content['content'] ?? '');
|
||||
}
|
||||
|
||||
// handle event RSVPs
|
||||
if (in_array($obj_type, ['Event', 'Invite'])) {
|
||||
if (($act->objprop('type') === 'Event') || ($act->objprop('type') === 'Invite' && array_path_exists('object/type', $act->obj) && $act->obj['object']['type'] === 'Event')) {
|
||||
if ($act->type === 'Accept') {
|
||||
$content['content'] = sprintf(t('Will attend %s\'s event'), $mention);
|
||||
$content['content'] = sprintf(t('Will attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
}
|
||||
if ($act->type === 'Reject') {
|
||||
$content['content'] = sprintf(t('Will not attend %s\'s event'), $mention);
|
||||
$content['content'] = sprintf(t('Will not attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
}
|
||||
if ($act->type === 'TentativeAccept') {
|
||||
$content['content'] = sprintf(t('May attend %s\'s event'), $mention);
|
||||
$content['content'] = sprintf(t('May attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
}
|
||||
if ($act->type === 'TentativeReject') {
|
||||
$content['content'] = sprintf(t('May not attend %s\'s event'), $mention);
|
||||
$content['content'] = sprintf(t('May not attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2307,7 +2251,7 @@ class Activity {
|
||||
if ($s['mid'] === $s['parent_mid']) {
|
||||
$s['item_thread_top'] = 1;
|
||||
$s['item_nocomment'] = 0;
|
||||
$s['comments_closed'] = DBA::$dba->get_null_date();
|
||||
$s['comments_closed'] = NULL_DATE;
|
||||
|
||||
// it is a parent node - decode the comment policy info if present
|
||||
if ($act->objprop('commentPolicy')) {
|
||||
@@ -2341,49 +2285,20 @@ class Activity {
|
||||
$s['body'] = markdown_to_bb($act->objprop('content'));
|
||||
}
|
||||
|
||||
$quote_urls = [];
|
||||
if ($act->objprop('quoteUrl')) {
|
||||
$quote_bbcode = self::get_quote_bbcode($act->obj['quoteUrl']);
|
||||
|
||||
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'];
|
||||
}
|
||||
if ($s['body']) {
|
||||
$s['body'] .= "\r\n\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
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['body'] .= $quote_bbcode;
|
||||
}
|
||||
|
||||
$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' && $obj_type === 'Question' && $s['edited'] === $s['created']) {
|
||||
if ($act->type === 'Update' && $act->objprop('type') === 'Question' && $s['edited'] === $s['created']) {
|
||||
$s['edited'] = datetime_convert();
|
||||
}
|
||||
|
||||
@@ -2391,8 +2306,8 @@ class Activity {
|
||||
$s['item_deleted'] = 1;
|
||||
}
|
||||
|
||||
if ($obj_type) {
|
||||
$s['obj_type'] = self::activity_obj_mapper($obj_type);
|
||||
if ($act->objprop('type')) {
|
||||
$s['obj_type'] = self::activity_obj_mapper($act->obj['type']);
|
||||
}
|
||||
|
||||
$s['obj'] = $act->obj;
|
||||
@@ -2425,13 +2340,7 @@ class Activity {
|
||||
$a = self::decode_taxonomy($act->obj);
|
||||
|
||||
if ($a) {
|
||||
if (isset($s['term'])) {
|
||||
// term might contain content from a quote post
|
||||
$s['term'] = array_merge($s['term'], $a);
|
||||
}
|
||||
else {
|
||||
$s['term'] = $a;
|
||||
}
|
||||
$s['term'] = $a;
|
||||
}
|
||||
|
||||
$a = self::decode_attachment($act->obj);
|
||||
@@ -2453,7 +2362,7 @@ class Activity {
|
||||
$s = self::bb_attach($s);
|
||||
}
|
||||
|
||||
if ($obj_type === 'Question' && in_array($act->type, ['Create', 'Update'])) {
|
||||
if ($act->objprop('type') === 'Question' && in_array($act->type, ['Create', 'Update'])) {
|
||||
if ($act->objprop('endTime')) {
|
||||
$s['comments_closed'] = datetime_convert('UTC', 'UTC', $act->obj['endTime']);
|
||||
}
|
||||
@@ -2465,7 +2374,7 @@ class Activity {
|
||||
|
||||
if (!$response_activity) {
|
||||
|
||||
if ($obj_type === 'Profile') {
|
||||
if ($act->objprop('type') === 'Profile') {
|
||||
$s['parent_mid'] = $s['mid'];
|
||||
$s['item_thread_top'] = 1;
|
||||
}
|
||||
@@ -2475,7 +2384,7 @@ class Activity {
|
||||
// right now just link to the largest mp4 we find that will fit in our
|
||||
// standard content region
|
||||
|
||||
if ($obj_type === 'Video') {
|
||||
if ($act->objprop('type') === 'Video') {
|
||||
|
||||
$vtypes = [
|
||||
'video/mp4',
|
||||
@@ -2557,7 +2466,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if ($obj_type === 'Audio') {
|
||||
if ($act->objprop('type') === 'Audio') {
|
||||
|
||||
$atypes = [
|
||||
'audio/mpeg',
|
||||
@@ -2589,7 +2498,7 @@ class Activity {
|
||||
|
||||
}
|
||||
|
||||
if ($obj_type === 'Image' && strpos($s['body'], 'zrl=') === false) {
|
||||
if ($act->objprop('type') === 'Image' && strpos($s['body'], 'zrl=') === false) {
|
||||
|
||||
$ptr = null;
|
||||
|
||||
@@ -2604,10 +2513,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) {
|
||||
@@ -2617,7 +2526,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if ($obj_type === 'Page' && !$s['body']) {
|
||||
if ($act->objprop('type') === 'Page' && !$s['body']) {
|
||||
|
||||
$ptr = null;
|
||||
$purl = EMPTY_STR;
|
||||
@@ -2657,7 +2566,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($obj_type, ['Note', 'Article', 'Page', 'Question'])) {
|
||||
if (in_array($act->objprop('type'), ['Note', 'Article', 'Page'])) {
|
||||
$ptr = null;
|
||||
|
||||
if (array_key_exists('url', $act->obj)) {
|
||||
@@ -2696,11 +2605,55 @@ 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 ($obj_type === 'Event' && $act->objprop('timezone')) {
|
||||
if ($act->objprop('type') === 'Event' && $act->objprop('timezone')) {
|
||||
IConfig::Set($s, 'event', 'timezone', $act->objprop('timezone'), true);
|
||||
}
|
||||
|
||||
@@ -2713,9 +2666,8 @@ 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 = $channel['channel_system'];
|
||||
$is_sys_channel = is_sys_channel($channel['channel_id']);
|
||||
$is_child_node = false;
|
||||
$parent = null;
|
||||
|
||||
@@ -2762,9 +2714,7 @@ class Activity {
|
||||
$force = true;
|
||||
}
|
||||
|
||||
$attempt_parents_fetch = $fetch_parents && !in_array($channel['channel_id'], App::$cache['as_fetch_objects'][$item['mid']]['channels'] ?? []);
|
||||
|
||||
if ($attempt_parents_fetch) {
|
||||
if ($fetch_parents) {
|
||||
App::$cache['as_fetch_objects'][$item['mid']]['channels'][] = $channel['channel_id'];
|
||||
App::$cache['as_fetch_objects'][$item['mid']]['force'] = intval($force);
|
||||
return;
|
||||
@@ -2866,11 +2816,15 @@ 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) {
|
||||
$allowed = perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail');
|
||||
if (perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail')) {
|
||||
$allowed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($is_sys_channel) {
|
||||
@@ -2936,19 +2890,13 @@ class Activity {
|
||||
if (!$item['author_xchan'] || !$item['owner_xchan'])
|
||||
return;
|
||||
|
||||
if ($is_sys_channel) {
|
||||
$incl = Config::Get('system', 'pubstream_incl', '');
|
||||
$excl = Config::Get('system', 'pubstream_excl', '');
|
||||
if ($channel['channel_system']) {
|
||||
$incl = Config::Get('system','pubstream_incl');
|
||||
$excl = Config::Get('system','pubstream_excl');
|
||||
|
||||
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;
|
||||
}
|
||||
if(($incl || $excl) && !MessageFilter::evaluate($item, $incl, $excl)) {
|
||||
logger('post is filtered');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3047,22 +2995,13 @@ class Activity {
|
||||
// TODO: not implemented
|
||||
// self::rewrite_mentions($item);
|
||||
|
||||
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",
|
||||
$r = q("select id, created, edited 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);
|
||||
}
|
||||
@@ -3096,21 +3035,30 @@ class Activity {
|
||||
send_status_notifications($x['item_id'], $x['item']);
|
||||
|
||||
sync_an_item($channel['channel_id'], $x['item_id']);
|
||||
}
|
||||
|
||||
$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);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3159,7 +3107,7 @@ class Activity {
|
||||
$cached = ASCache::Get($current_item['parent_mid']);
|
||||
if ($cached) {
|
||||
// logger('cached: ' . $current_item['parent_mid']);
|
||||
$n = $cached;
|
||||
$n = unserialise($cached);
|
||||
}
|
||||
else {
|
||||
// logger('fetching: ' . $current_item['parent_mid']);
|
||||
@@ -3167,7 +3115,7 @@ class Activity {
|
||||
if (!$n) {
|
||||
break;
|
||||
}
|
||||
ASCache::Set($current_item['parent_mid'], $n);
|
||||
ASCache::Set($current_item['parent_mid'], serialise($n));
|
||||
}
|
||||
|
||||
$a = new ActivityStreams($n);
|
||||
@@ -3330,12 +3278,6 @@ 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 (
|
||||
@@ -3631,46 +3573,27 @@ class Activity {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static function get_quote($url) {
|
||||
$ret = [];
|
||||
$a = null;
|
||||
static function get_quote_bbcode($url) {
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
$ret = '';
|
||||
|
||||
$a = self::fetch($url);
|
||||
if ($a) {
|
||||
$act = new ActivityStreams($a);
|
||||
|
||||
if ($act->is_valid()) {
|
||||
$decoded = self::decode_note($act);
|
||||
$content = self::get_content($act->obj);
|
||||
|
||||
$bbcode = "[share author='" . urlencode($act->actor['name'] ?? $act->actor['preferredUsername']) .
|
||||
$ret .= "[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='" . $url .
|
||||
"' link='" . $act->obj['id'] .
|
||||
"' auth='" . ((is_matrix_url($act->actor['id'])) ? 'true' : 'false') .
|
||||
"' posted='" . $act->obj['published'] .
|
||||
"' message_id='" . $act->obj['id'] .
|
||||
"']";
|
||||
|
||||
$bbcode .= $decoded['body'];
|
||||
$bbcode .= '[/share]';
|
||||
|
||||
$ret['bbcode'] = $bbcode;
|
||||
$ret['url'] = $decoded['plink'];
|
||||
$ret['mid'] = $decoded['mid'];
|
||||
$ret['term'] = $decoded['term'] ?? [];
|
||||
|
||||
$ret .= self::bb_content($content, 'content');
|
||||
$ret .= '[/share]';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3747,11 +3670,10 @@ class Activity {
|
||||
|
||||
'conversation' => 'ostatus:conversation',
|
||||
|
||||
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
|
||||
'Hashtag' => 'as:Hashtag',
|
||||
'guid' => 'diaspora:guid',
|
||||
|
||||
'quoteUrl' => 'as:quoteUrl',
|
||||
'quoteUri' => 'http://fedibird.com/ns#quoteUri'
|
||||
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
|
||||
'Hashtag' => 'as:Hashtag'
|
||||
];
|
||||
|
||||
}
|
||||
@@ -3790,13 +3712,11 @@ class Activity {
|
||||
*/
|
||||
|
||||
public static function init_background_fetch(string $observer_hash = '') {
|
||||
|
||||
$interval = Config::Get('queueworker', 'queue_interval', 500000);
|
||||
|
||||
if (isset(App::$cache['zot_fetch_objects'])) {
|
||||
$channels_str = '';
|
||||
|
||||
foreach (App::$cache['zot_fetch_objects'] as $mid => $info) {
|
||||
$force = $info['force'];
|
||||
$channels_str = '';
|
||||
|
||||
foreach ($info['channels'] as $c) {
|
||||
if ($channels_str) {
|
||||
@@ -3806,22 +3726,19 @@ 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) {
|
||||
@@ -3831,33 +3748,8 @@ 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) {
|
||||
@@ -3967,34 +3859,4 @@ 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, $this->data);
|
||||
ASCache::Set($this->id, 'json:' . $this->raw);
|
||||
|
||||
$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 = $cached;
|
||||
$y = unserialise($cached);
|
||||
}
|
||||
else {
|
||||
// logger('AS fetching: ' . $x);
|
||||
$y = $this->fetch_property($x);
|
||||
if ($y) {
|
||||
ASCache::Set($x, $y);
|
||||
ASCache::Set($x, serialise($y));
|
||||
}
|
||||
}
|
||||
if (is_array($y)) {
|
||||
@@ -553,7 +553,6 @@ class ActivityStreams {
|
||||
}
|
||||
|
||||
$url = unparse_url($parseUrl);
|
||||
$this->signer = ['id' => $url];
|
||||
|
||||
$hublocs = Activity::get_actor_hublocs($url);
|
||||
|
||||
|
||||
@@ -1028,7 +1028,12 @@ class Apps {
|
||||
if(! $syslist)
|
||||
return;
|
||||
|
||||
$position = array_find_key($syslist, fn ($v) => $v['guid'] === $guid);
|
||||
foreach($syslist as $k => $li) {
|
||||
if($li['guid'] === $guid) {
|
||||
$position = $k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(! $position)
|
||||
return;
|
||||
|
||||
@@ -1077,7 +1082,12 @@ class Apps {
|
||||
if(! $syslist)
|
||||
return;
|
||||
|
||||
$position = array_find_key($syslist, fn ($v) => $v['guid'] === $guid);
|
||||
foreach($syslist as $k => $li) {
|
||||
if($li['guid'] === $guid) {
|
||||
$position = $k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($position >= count($syslist) - 1)
|
||||
return;
|
||||
|
||||
|
||||
@@ -132,8 +132,8 @@ class Config {
|
||||
$value = App::$config[$family][$key];
|
||||
|
||||
if (! is_array($value)) {
|
||||
if (str_starts_with($value, 'json:')) {
|
||||
return json_unserialize($value);
|
||||
if (substr($value, 0, 5) == 'json:') {
|
||||
return json_decode(substr($value, 5), true);
|
||||
} else if (preg_match('|^a:[0-9]+:{.*}$|s', $value)) {
|
||||
// Unserialize in inherently unsafe. Try to mitigate by not
|
||||
// allowing unserializing objects. Only kept for backwards
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<?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 a new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
|
||||
$preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('You\'ve received [zrl=%1$s]a new connection request[/zrl] from %2$s.'),
|
||||
$siteurl . '/connections/ifpending',
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
|
||||
@@ -511,7 +511,7 @@ class Enotify {
|
||||
*/
|
||||
|
||||
|
||||
$hash = ((in_array($params['verb'], ['Create', 'Update', 'Invite'])) ? $params['item']['uuid'] : $params['item']['thr_parent_uuid']);
|
||||
$hash = ((in_array($params['verb'], ['Create', 'Update'])) ? $params['item']['uuid'] : $params['item']['thr_parent_uuid']);
|
||||
|
||||
if (!$hash) {
|
||||
$hash = new_uuid();
|
||||
|
||||
@@ -34,20 +34,8 @@ 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 (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]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($c['iid']) && $c['iid'] == $iid && isset($c['cat']) && $c['cat'] == $family && isset($c['k']) && $c['k'] == $key)
|
||||
return $c['v'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,24 +44,12 @@ class IConfig {
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
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) {
|
||||
$r[0]['v'] = ((preg_match('|^a:[0-9]+:{.*}$|s',$r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']);
|
||||
if($is_item)
|
||||
$item['iconfig'][] = $r[0];
|
||||
}
|
||||
|
||||
return $r[0]['v'];
|
||||
}
|
||||
|
||||
return $default;
|
||||
|
||||
}
|
||||
@@ -97,7 +73,7 @@ class IConfig {
|
||||
|
||||
static public function Set(&$item, $family, $key, $value, $sharing = false) {
|
||||
|
||||
$dbvalue = ((is_array($value)) ? json_serialize($value) : $value);
|
||||
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
|
||||
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
|
||||
|
||||
$is_item = false;
|
||||
@@ -123,11 +99,11 @@ class IConfig {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if(intval($item)) {
|
||||
if(intval($item))
|
||||
$iid = intval($item);
|
||||
} else {
|
||||
|
||||
if(! $iid)
|
||||
return false;
|
||||
}
|
||||
|
||||
if(self::Get($item, $family, $key) === false) {
|
||||
$r = q("insert into iconfig( iid, cat, k, v, sharing ) values ( %d, '%s', '%s', '%s', %d ) ",
|
||||
@@ -174,11 +150,11 @@ class IConfig {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(intval($item)) {
|
||||
if(intval($item))
|
||||
$iid = intval($item);
|
||||
} else {
|
||||
|
||||
if(! $iid)
|
||||
return false;
|
||||
}
|
||||
|
||||
return q("delete from iconfig where iid = %d and cat = '%s' and k = '%s' ",
|
||||
intval($iid),
|
||||
|
||||
@@ -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,6 +116,7 @@ function getRemoteFileSize($url)
|
||||
|
||||
curl_exec($ch);
|
||||
curl_getinfo($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return $size;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use Root23\JsonCanonicalizer\JsonCanonicalizer;
|
||||
use Mmccook\JsonCanonicalizator\JsonCanonicalizatorFactory;
|
||||
use StephenHill\Base58;
|
||||
|
||||
class JcsEddsa2022 {
|
||||
@@ -62,16 +62,14 @@ class JcsEddsa2022 {
|
||||
try {
|
||||
$result = sodium_crypto_sign_verify_detached($base58->decode($encodedSignature), $optionsHash . $dataHash,
|
||||
(new Multibase())->decode($publicKey, true));
|
||||
|
||||
logger('SignatureVerify (eddsa-jcs-2022) ' . (($result) ? 'true' : 'false'));
|
||||
|
||||
return $result;
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
logger('verify exception:' . $e->getMessage());
|
||||
}
|
||||
|
||||
return false;
|
||||
logger('SignatureVerify (eddsa-jcs-2022) ' . (($result) ? 'true' : 'false'));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function signableData($data) {
|
||||
@@ -104,8 +102,8 @@ class JcsEddsa2022 {
|
||||
}
|
||||
|
||||
public function canonicalize($data) {
|
||||
$canonicalizer = new JsonCanonicalizer();
|
||||
return $canonicalizer->canonicalize($data);
|
||||
$canonicalization = JsonCanonicalizatorFactory::getInstance();
|
||||
return $canonicalization->canonicalize($data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,10 +8,9 @@ class LDSignatures {
|
||||
|
||||
|
||||
static function verify($data,$pubkey) {
|
||||
$expand_and_check_unsafe = true;
|
||||
|
||||
$ohash = self::hash(self::signable_options($data['signature']), $expand_and_check_unsafe);
|
||||
$dhash = self::hash(self::signable_data($data), $expand_and_check_unsafe);
|
||||
$ohash = self::hash(self::signable_options($data['signature']));
|
||||
$dhash = self::hash(self::signable_data($data));
|
||||
|
||||
$x = Crypto::verify($ohash . $dhash,base64_decode($data['signature']['signatureValue']), $pubkey);
|
||||
logger('LD-verify: ' . intval($x));
|
||||
@@ -75,11 +74,11 @@ class LDSignatures {
|
||||
return json_encode($newopts,JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
static function hash($obj, $expand_and_check_unsafe = false) {
|
||||
return hash('sha256', self::normalise($obj, $expand_and_check_unsafe));
|
||||
static function hash($obj) {
|
||||
return hash('sha256', self::normalise($obj));
|
||||
}
|
||||
|
||||
static function normalise($data, $expand_and_check_unsafe) {
|
||||
static function normalise($data) {
|
||||
$ret = '';
|
||||
|
||||
if(is_string($data)) {
|
||||
@@ -91,15 +90,6 @@ 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' ]);
|
||||
}
|
||||
@@ -142,41 +132,6 @@ 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,7 +199,8 @@ class Libsync {
|
||||
dbesc($sender)
|
||||
);
|
||||
|
||||
$mid = $arr['item'][0]['message_id'] ?? 'sync';
|
||||
$mid = 'sync';
|
||||
|
||||
|
||||
$DR = new DReport(z_root(), $sender, $d, $mid);
|
||||
|
||||
@@ -305,8 +306,15 @@ 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']);
|
||||
|
||||
@@ -749,11 +757,12 @@ class Libsync {
|
||||
*/
|
||||
call_hooks('process_channel_sync_delivery', $addon);
|
||||
|
||||
$DR->update('channel sync processed');
|
||||
$DR = new DReport(z_root(), $d, $d, $mid, 'channel sync processed');
|
||||
|
||||
$DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>');
|
||||
|
||||
$result[] = $DR->get();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,6 +116,10 @@ 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 {
|
||||
@@ -349,7 +353,7 @@ class Libzot {
|
||||
$next_birthday = datetime_convert('UTC', 'UTC', $record['data']['profile']['next_birthday']);
|
||||
}
|
||||
else {
|
||||
$next_birthday = DBA::$dba->get_null_date();
|
||||
$next_birthday = NULL_DATE;
|
||||
}
|
||||
|
||||
$profile_assign = get_pconfig($channel['channel_id'], 'system', 'profile_assign', '');
|
||||
@@ -1298,17 +1302,8 @@ class Libzot {
|
||||
$item['comment_policy'] = 'authenticated';
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (isset($AS->meta['signed_data']) && $AS->meta['signed_data']) {
|
||||
IConfig::Set($item, 'activitypub', 'signed_data', $AS->meta['signed_data'], false);
|
||||
}
|
||||
|
||||
logger('Activity received: ' . print_r($item, true), LOGGER_DATA, LOG_DEBUG);
|
||||
@@ -1650,19 +1645,12 @@ 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) {
|
||||
$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;
|
||||
}
|
||||
if(($incl || $excl) && !MessageFilter::evaluate($arr, $incl, $excl)) {
|
||||
$local_public = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
$r = q("select xchan_selfcensored, xchan_censored from xchan where xchan_hash = '%s'",
|
||||
@@ -1671,7 +1659,6 @@ 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;
|
||||
}
|
||||
@@ -1842,7 +1829,9 @@ class Libzot {
|
||||
}
|
||||
|
||||
if (intval($arr['item_private']) === 2) {
|
||||
$allowed = perm_is_allowed($channel['channel_id'], $sender, 'post_mail');
|
||||
if (!perm_is_allowed($channel['channel_id'], $sender, 'post_mail')) {
|
||||
$allowed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$allowed) {
|
||||
@@ -1904,13 +1893,11 @@ 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.
|
||||
|
||||
// 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']]))) {
|
||||
if (!intval($r[0]['item_origin']))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2161,9 +2148,10 @@ 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) {
|
||||
ObjCache::Set($arr['mid'], json_encode(JSalmon::unpack($j['data'])));
|
||||
IConfig::Set($arr, 'activitypub', 'rawmsg', json_encode(JSalmon::unpack($j['data'])), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Zotfinger;
|
||||
@@ -217,7 +216,7 @@ class Libzotdir {
|
||||
[
|
||||
'site_url' => DIRECTORY_FALLBACK_MASTER,
|
||||
'site_flags' => DIRECTORY_MODE_PRIMARY,
|
||||
'site_update' => DBA::$dba->get_null_date(),
|
||||
'site_update' => NULL_DATE,
|
||||
'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch',
|
||||
'site_realm' => DIRECTORY_REALM,
|
||||
'site_valid' => 1,
|
||||
@@ -248,7 +247,7 @@ class Libzotdir {
|
||||
|
||||
$token = Config::Get('system','realm_token');
|
||||
|
||||
$syncdate = (($rr['site_sync'] <= DBA::$dba->get_null_date()) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
|
||||
$syncdate = (($rr['site_sync'] <= NULL_DATE) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
|
||||
$x = z_fetch_url($rr['site_directory'] . '?f=&sync=' . urlencode($syncdate) . (($token) ? '&t=' . $token : ''));
|
||||
|
||||
if (! $x['success'])
|
||||
@@ -725,7 +724,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(DBA::$dba->get_null_date()),
|
||||
dbesc(NULL_DATE),
|
||||
dbesc(z_root()),
|
||||
dbesc($addr),
|
||||
intval($u[0]['ud_id'])
|
||||
|
||||
@@ -4,417 +4,260 @@ namespace Zotlabs\Lib;
|
||||
|
||||
require_once('include/html2plain.php');
|
||||
|
||||
class MessageFilter
|
||||
{
|
||||
protected $lastMatch = '';
|
||||
protected $item = null;
|
||||
protected $include = '';
|
||||
protected $exclude = '';
|
||||
protected $options = [];
|
||||
protected $tags = null;
|
||||
protected $language = '';
|
||||
protected $text = '';
|
||||
protected $excludeRules = [];
|
||||
protected $includeRules = [];
|
||||
class MessageFilter {
|
||||
|
||||
public function __construct($item, $include = '', $exclude = '', $options = [])
|
||||
{
|
||||
$this->item = $item;
|
||||
$this->include = $include;
|
||||
$this->exclude = $exclude;
|
||||
$this->options = $options;
|
||||
$this->setup();
|
||||
}
|
||||
public static function evaluate($item, $incl, $excl) {
|
||||
|
||||
protected function setup()
|
||||
{
|
||||
// Option: plaintext
|
||||
// Improve language detection by providing a plaintext version of $item['body'] which has no markup constructs/tags.
|
||||
$text = prepare_text($item['body'], ((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
|
||||
$text = html2plain((!empty($item['title'])) ? $item['title'] . ' ' . $text : $text);
|
||||
|
||||
if (array_key_exists('plaintext', $this->options)) {
|
||||
$this->text = $this->options['plaintext'];
|
||||
} else {
|
||||
$this->text = $this->item['body'];
|
||||
}
|
||||
$lang = null;
|
||||
if ((strpos($incl, 'lang=') !== false) || (strpos($excl, 'lang=') !== false) || (strpos($incl, 'lang!=') !== false) || (strpos($excl, 'lang!=') !== false)) {
|
||||
$lang = detect_language($text);
|
||||
}
|
||||
|
||||
$this->language = '';
|
||||
$tags = ((isset($item['term']) && is_array($item['term']) && count($item['term'])) ? $item['term'] : false);
|
||||
|
||||
// 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.
|
||||
$until = null;
|
||||
|
||||
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 always has priority
|
||||
|
||||
$this->tags = ((isset($this->item['term']) && is_array($this->item['term'])
|
||||
&& count($this->item['term'])) ? $this->item['term'] : null);
|
||||
$exclude = (($excl) ? explode("\n", $excl) : null);
|
||||
|
||||
$this->excludeRules = $this->parse($this->exclude);
|
||||
$this->includeRules = $this->parse($this->include);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
$include = (($incl) ? explode("\n", $incl) : null);
|
||||
|
||||
protected function parse($string): array
|
||||
{
|
||||
$rules = [];
|
||||
if (! strlen($string)) {
|
||||
return $rules;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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;
|
||||
/**
|
||||
* 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;
|
||||
* - ?!foo which will check for a return of a false condition for item.foo;
|
||||
*
|
||||
* The values 0, '', an empty array, and an unset value will all evaluate to false.
|
||||
*
|
||||
* @param string $s
|
||||
* @param array $item
|
||||
* @return bool
|
||||
*/
|
||||
protected function test_condition($s,$item)
|
||||
{
|
||||
$s = trim($s);
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
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 (is_string($x) && stripos($x, trim($matches[2])) !== false) {
|
||||
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 ($x == trim($matches[2])) {
|
||||
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 (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;
|
||||
}
|
||||
// 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 ($x >= trim($matches[2])) {
|
||||
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]));
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
// 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 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?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,20 +7,6 @@ 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);
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<?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,28 +118,26 @@ class Share {
|
||||
$photo_bb = $object['body'];
|
||||
}
|
||||
|
||||
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']) {
|
||||
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'])
|
||||
$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,7 +3,6 @@
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Access\AccessList;
|
||||
|
||||
require_once('include/text.php');
|
||||
@@ -121,10 +120,10 @@ class ThreadItem {
|
||||
$locktype = 0;
|
||||
}
|
||||
|
||||
$shareable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && (intval($item['item_private']) === 0) && !str_contains($item['body'], '[/share]'));
|
||||
$shareable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && (intval($item['item_private']) === 0));
|
||||
|
||||
// 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']));
|
||||
@@ -284,12 +283,9 @@ class ThreadItem {
|
||||
$reply_to = [];
|
||||
$reactions_allowed = false;
|
||||
|
||||
if($this->is_commentable()) {
|
||||
if($this->is_commentable() && $observer) {
|
||||
$reply_to = array( t("Reply to this message"), t("reply"), t("Reply to"));
|
||||
|
||||
if ($observer) {
|
||||
$reactions_allowed = true;
|
||||
}
|
||||
$reactions_allowed = true;
|
||||
}
|
||||
|
||||
$share = [];
|
||||
@@ -416,7 +412,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'] > DBA::$dba->get_null_date()) ? sprintf(t('Expires %s'), relative_time($item['expires'])) : ''),
|
||||
'expiretime' => (($item['expires'] > 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', 'json'];
|
||||
// const VALID_FILE_EXT = ['md', 'bb', 'html'];
|
||||
|
||||
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', 'json'];
|
||||
$valid_file_ext = ['md', 'bb', 'html'];
|
||||
|
||||
$base_path_with_lang = "doc/{$lang}/${base_path}";
|
||||
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
|
||||
* SPDX-FileContributor: Mario Vavti <mario@mariovavti.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
class Url {
|
||||
|
||||
/**
|
||||
* @brief Adds a zid parameter to a url.
|
||||
*
|
||||
* @param string $s
|
||||
* The url to accept the zid
|
||||
* @param string $address
|
||||
* $address to use instead of session environment
|
||||
* @return string
|
||||
*/
|
||||
public static function zid(string $url, string $address = ''): string
|
||||
{
|
||||
if (!$url || strpos($url, 'zid=') !== false) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$parts = parse_url($url);
|
||||
if ($parts === false) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$mine = get_my_url();
|
||||
$myaddr = $address ?: get_my_address();
|
||||
|
||||
if (!$mine || !$myaddr) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$mine_parts = parse_url($mine);
|
||||
$same_host = isset($mine_parts['host'], $parts['host']) && strcasecmp($mine_parts['host'], $parts['host']) === 0;
|
||||
|
||||
if ($same_host) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$query = [];
|
||||
if (!empty($parts['query'])) {
|
||||
parse_str($parts['query'], $query);
|
||||
}
|
||||
|
||||
$query['zid'] = $myaddr;
|
||||
$parts['query'] = http_build_query($query);
|
||||
|
||||
$hookdata = [
|
||||
'url' => $url,
|
||||
'zid' => urlencode($myaddr),
|
||||
'result' => self::unparse($parts)
|
||||
];
|
||||
|
||||
/**
|
||||
* @hooks zid
|
||||
* Called when adding the observer's zid to a URL.
|
||||
* * \e string \b url - url to accept zid
|
||||
* * \e string \b zid - urlencoded zid
|
||||
* * \e string \b result - the return string we calculated, change it if you want to return something else
|
||||
*/
|
||||
call_hooks('zid', $hookdata);
|
||||
|
||||
return $hookdata['result'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reconstructs a URL from its parsed components.
|
||||
*
|
||||
* This function takes a parsed URL as an associative array and reconstructs
|
||||
* the URL based on the specified components (scheme, host, port, user, pass, path, query, fragment).
|
||||
* You can specify which components should be included in the final URL by passing the optional
|
||||
* `$parts` array. The function will return the complete URL string formed by combining
|
||||
* only the parts that exist in both the parsed URL and the `$parts` array.
|
||||
*
|
||||
* @param array $parsed_url The parsed URL components as an associative array.
|
||||
* The array can include keys like 'scheme', 'host', 'port', 'user', 'pass',
|
||||
* 'path', 'query', 'fragment'.
|
||||
*
|
||||
* @param array $parts An optional array that specifies which components of the URL
|
||||
* should be included in the final string. Defaults to:
|
||||
* ['scheme', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment'].
|
||||
* If any of the components are not required, they can be omitted from the array.
|
||||
*
|
||||
* @return string The reconstructed URL as a string.
|
||||
*/
|
||||
public static function unparse(array $parsed_url, array $parts = ['scheme', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment']): string {
|
||||
$url_parts = [];
|
||||
|
||||
if (in_array('scheme', $parts) && array_key_exists('scheme', $parsed_url)) {
|
||||
$url_parts[] = $parsed_url['scheme'] . '://';
|
||||
}
|
||||
|
||||
if (in_array('user', $parts) && array_key_exists('user', $parsed_url)) {
|
||||
$url_parts[] = $parsed_url['user'];
|
||||
if (in_array('pass', $parts) && array_key_exists('pass', $parsed_url)) {
|
||||
$url_parts[] = ':' . $parsed_url['pass'];
|
||||
}
|
||||
$url_parts[] = '@';
|
||||
}
|
||||
|
||||
if (in_array('host', $parts) && array_key_exists('host', $parsed_url)) {
|
||||
$url_parts[] = $parsed_url['host'];
|
||||
}
|
||||
|
||||
if (in_array('port', $parts) && array_key_exists('port', $parsed_url)) {
|
||||
$url_parts[] = ':' . $parsed_url['port'];
|
||||
}
|
||||
|
||||
if (in_array('path', $parts) && array_key_exists('path', $parsed_url)) {
|
||||
$url_parts[] = $parsed_url['path'];
|
||||
}
|
||||
|
||||
if (in_array('query', $parts) && array_key_exists('query', $parsed_url)) {
|
||||
$url_parts[] = '?' . $parsed_url['query'];
|
||||
}
|
||||
|
||||
if (in_array('fragment', $parts) && array_key_exists('fragment', $parsed_url)) {
|
||||
$url_parts[] = '#' . $parsed_url['fragment'];
|
||||
}
|
||||
|
||||
return implode('', $url_parts);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -437,7 +437,9 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
|
||||
if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) {
|
||||
$url = z_root() . '/dirsearch';
|
||||
} else {
|
||||
}
|
||||
|
||||
if(! $url) {
|
||||
$directory = Libzotdir::find_upstream_directory($dirmode);
|
||||
$url = $directory['url'] . '/dirsearch';
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ class Activity extends Controller {
|
||||
// Give ocap tokens priority
|
||||
|
||||
if ($ob_authorize) {
|
||||
$sql_extra = " and item.uid = " . intval($item_uid) . " ";
|
||||
$sql_extra = " and item.uid = " . intval($token['uid']) . " ";
|
||||
}
|
||||
else {
|
||||
$sql_extra = item_permissions_sql(0);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
require_once('include/account.php');
|
||||
@@ -91,7 +90,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(DBA::$dba->get_null_date()),
|
||||
dbesc(NULL_DATE),
|
||||
intval(ACCOUNT_BLOCKED)
|
||||
);
|
||||
if ($r) {
|
||||
|
||||
@@ -55,10 +55,11 @@ class Account_edit {
|
||||
|
||||
|
||||
function get() {
|
||||
$account_id = intval(argv(2));
|
||||
if(argc() > 2)
|
||||
$account_id = argv(2);
|
||||
|
||||
$x = q("select * from account where account_id = %d limit 1",
|
||||
$account_id
|
||||
intval($account_id)
|
||||
);
|
||||
|
||||
if(! $x) {
|
||||
|
||||
@@ -203,6 +203,7 @@ class Accounts {
|
||||
$t = get_markup_template('admin_accounts.tpl');
|
||||
$o = replace_macros($t, array(
|
||||
// strings //
|
||||
'$debug' => $debug,
|
||||
'$title' => t('Administration'),
|
||||
'$page' => t('Accounts'),
|
||||
'$submit' => t('Submit'),
|
||||
@@ -262,7 +263,7 @@ class Accounts {
|
||||
|
||||
if ($zarop && $zarat >= 0 && $zarse && $zarse == $_SESSION[self::MYP]['h'][$zarat]) {
|
||||
|
||||
$rc = 0;
|
||||
//
|
||||
if ($zarop == 'd') {
|
||||
$rd = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d AND SUBSTR(reg_hash,1,4) = '%s' ",
|
||||
intval($_SESSION[self::MYP]['i'][$zarat]),
|
||||
@@ -278,6 +279,7 @@ class Accounts {
|
||||
intval($_SESSION[self::MYP]['i'][$zarat]),
|
||||
dbesc($_SESSION[self::MYP]['h'][$zarat])
|
||||
);
|
||||
$rc = 0;
|
||||
$rs = q("SELECT * from register WHERE reg_id = %d ",
|
||||
intval($_SESSION[self::MYP]['i'][$zarat])
|
||||
);
|
||||
@@ -339,27 +341,29 @@ class Accounts {
|
||||
* @SuppressWarnings(PHPMD.ShortVariable)
|
||||
*/
|
||||
private function block_unblock_accounts(): void {
|
||||
if (!isset($_POST['user'])) {
|
||||
if (!isset($_POST['user']) || !isset($_POST['blocked'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$users = $_POST['user'];
|
||||
$blocked = $_POST['blocked'];
|
||||
|
||||
if (!is_array($users)) {
|
||||
if (!is_array($users) || !is_array($blocked)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$xor = db_getfunc('^');
|
||||
foreach($users as $i => $id) {
|
||||
// if account is blocked remove blocked bit-flag, otherwise add blocked bit-flag
|
||||
$op = $blocked[$i] ? '& ~' : '| ';
|
||||
|
||||
foreach($users as $id) {
|
||||
q("UPDATE account SET account_flags = (account_flags $xor %d) WHERE account_id = %d",
|
||||
q("UPDATE account SET account_flags = (account_flags $op%d) WHERE account_id = %d",
|
||||
intval(ACCOUNT_BLOCKED),
|
||||
intval($id)
|
||||
);
|
||||
}
|
||||
|
||||
$count = count($users);
|
||||
$fmt = tt("%s account blocked/unblocked", "%s accounts blocked/unblocked", $count);
|
||||
$fmt = tt("%s account blocked/unblocked", "%s account blocked/unblocked", $count);
|
||||
notice(sprintf($fmt, $count));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,9 +24,6 @@ class Security {
|
||||
$cloud_disksize = ((x($_POST,'cloud_disksize')) ? 1 : 0);
|
||||
Config::Set('system','cloud_report_disksize',$cloud_disksize);
|
||||
|
||||
$propfind_depth_infinity = ((x($_POST, 'propfind_depth_infinity')) ? 1 : 0);
|
||||
Config::Set('system','propfind_depth_infinity', $propfind_depth_infinity);
|
||||
|
||||
$ws = $this->trim_array_elems(explode("\n",$_POST['whitelisted_sites']));
|
||||
Config::Set('system','whitelisted_sites',$ws);
|
||||
|
||||
@@ -112,7 +109,6 @@ class Security {
|
||||
'$block_public' => array('block_public', t("Block public"), Config::Get('system','block_public'), t("Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated.")),
|
||||
'$cloud_noroot' => [ 'cloud_noroot', t('Provide a cloud root directory'), 1 - intval(Config::Get('system','cloud_disable_siteroot')), t('The cloud root directory lists all channel names which provide public files') ],
|
||||
'$cloud_disksize' => [ 'cloud_disksize', t('Show total disk space available to cloud uploads'), intval(Config::Get('system','cloud_report_disksize')), '' ],
|
||||
'$propfind_depth_infinity' => ['propfind_depth_infinity', t('Allow propfind requests with infinity depth'), intval(Config::Get('system', 'propfind_depth_infinity')), t('Only turn this on if you know what you are doing')],
|
||||
'$transport_security' => array('transport_security', t('Set "Transport Security" HTTP header'),intval(Config::Get('system','transport_security_header')),''),
|
||||
'$content_security' => array('content_security', t('Set "Content Security Policy" HTTP header'),intval(Config::Get('system','content_security_policy')),''),
|
||||
'$allowed_email' => array('allowed_email', t("Allowed email domains"), Config::Get('system','allowed_email'), t("Comma separated list of domains which are allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains")),
|
||||
|
||||
@@ -32,9 +32,6 @@ class Apporder extends \Zotlabs\Web\Controller {
|
||||
|
||||
$syslist = Zlib\Apps::app_order(local_channel(),$syslist, $l);
|
||||
|
||||
$navbar_apps = [];
|
||||
$nav_apps = [];
|
||||
|
||||
foreach($syslist as $app) {
|
||||
if($l === 'nav_pinned_app') {
|
||||
$navbar_apps[] = Zlib\Apps::app_render($app,'nav-order-pinned');
|
||||
|
||||
@@ -82,7 +82,7 @@ class Attach_edit extends Controller {
|
||||
$admin_delete = false;
|
||||
|
||||
$is_creator = (($creator == $observer_hash) ? true : false);
|
||||
$move = ((!$delete && !$copy && ($folder !== $newfolder || (($single) ? $filename !== $newfilename : false))) ? true : false);
|
||||
$move = ((! $copy && ($folder !== $newfolder || (($single) ? $filename !== $newfilename : false))) ? true : false);
|
||||
|
||||
$perms = get_all_perms($channel_id, $observer_hash);
|
||||
|
||||
|
||||
@@ -12,31 +12,28 @@ class Authorize extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
else {
|
||||
|
||||
$name = $_GET['client_name'];
|
||||
$name = $_REQUEST['client_name'];
|
||||
if(! $name) {
|
||||
$name = $_GET['client_id'] ?: t('Unknown App');
|
||||
$name = (($_REQUEST['client_id']) ?: t('Unknown App'));
|
||||
}
|
||||
|
||||
$app = [
|
||||
'name' => escape_tags($name),
|
||||
'icon' => (x($_GET, 'logo_uri') ? $_GET['logo_uri'] : z_root() . '/images/icons/plugin.png'),
|
||||
'url' => (x($_GET, 'client_uri') ? $_GET['client_uri'] : ''),
|
||||
'name' => $name,
|
||||
'icon' => (x($_REQUEST, 'logo_uri') ? $_REQUEST['logo_uri'] : z_root() . '/images/icons/plugin.png'),
|
||||
'url' => (x($_REQUEST, 'client_uri') ? $_REQUEST['client_uri'] : ''),
|
||||
];
|
||||
|
||||
$link = $app['url']
|
||||
? '<a style="float: none;" href="' . escape_url($app['url']) . '">' . $app['name'] . '</a> '
|
||||
: $app['name'];
|
||||
$link = (($app['url']) ? '<a style="float: none;" href="' . $app['url'] . '">' . $app['name'] . '</a> ' : $app['name']);
|
||||
|
||||
return replace_macros(get_markup_template('oauth_authorize.tpl'), [
|
||||
'$title' => t('Authorize'),
|
||||
'$security' => get_form_security_token('oauth_authorize'),
|
||||
'$authorize' => sprintf( t('Do you authorize the app %s to access your channel data?'), $link ),
|
||||
'$app' => $app,
|
||||
'$yes' => t('Allow'),
|
||||
'$no' => t('Deny'),
|
||||
'$client_id' => (x($_GET, 'client_id') ? $_GET['client_id'] : ''),
|
||||
'$redirect_uri' => (x($_GET, 'redirect_uri') ? $_GET['redirect_uri'] : ''),
|
||||
'$state' => (x($_GET, 'state') ? $_GET['state'] : ''),
|
||||
'$client_id' => (x($_REQUEST, 'client_id') ? $_REQUEST['client_id'] : ''),
|
||||
'$redirect_uri' => (x($_REQUEST, 'redirect_uri') ? $_REQUEST['redirect_uri'] : ''),
|
||||
'$state' => (x($_REQUEST, 'state') ? $_REQUEST['state'] : ''),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -46,10 +43,6 @@ class Authorize extends \Zotlabs\Web\Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! check_form_security_token('oauth_authorize')) {
|
||||
http_status_exit(401, t('You are not authorized to perform this action.'));
|
||||
}
|
||||
|
||||
$storage = new OAuth2Storage(\DBA::$dba->db);
|
||||
$s = new \Zotlabs\Identity\OAuth2Server($storage);
|
||||
|
||||
|
||||
@@ -7,63 +7,63 @@ require_once('include/conversation.php');
|
||||
class Block extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
|
||||
$which = argv(1);
|
||||
$profile = 0;
|
||||
profile_load($which,$profile);
|
||||
|
||||
|
||||
if(\App::$profile['profile_uid'])
|
||||
head_set_icon(\App::$profile['thumb']);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function get() {
|
||||
|
||||
|
||||
if(! perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),'view_pages')) {
|
||||
notice( t('Permission denied.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(argc() < 3) {
|
||||
notice( t('Invalid item.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$channel_address = argv(1);
|
||||
$page_id = argv(2);
|
||||
|
||||
|
||||
$u = q("select channel_id from channel where channel_address = '%s' limit 1",
|
||||
dbesc($channel_address)
|
||||
);
|
||||
|
||||
|
||||
if(! $u) {
|
||||
notice( t('Channel not found.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if($_REQUEST['rev'])
|
||||
$revision = " and revision = " . intval($_REQUEST['rev']) . " ";
|
||||
else
|
||||
$revision = " order by revision desc ";
|
||||
|
||||
|
||||
require_once('include/security.php');
|
||||
$sql_options = item_permissions_sql($u[0]['channel_id']);
|
||||
|
||||
|
||||
$r = q("select item.* from item left join iconfig on item.id = iconfig.iid
|
||||
where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' and iconfig.k = 'BUILDBLOCK' and
|
||||
where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' and iconfig.k = 'BUILDBLOCK' and
|
||||
item_type = %d $sql_options $revision limit 1",
|
||||
intval($u[0]['channel_id']),
|
||||
dbesc($page_id),
|
||||
intval(ITEM_TYPE_BLOCK)
|
||||
);
|
||||
|
||||
|
||||
if(! $r) {
|
||||
|
||||
|
||||
// Check again with no permissions clause to see if it is a permissions issue
|
||||
|
||||
|
||||
$x = q("select item.* from item left join iconfig on item.id = iconfig.iid
|
||||
where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' and iconfig.k = 'BUILDBLOCK' and
|
||||
where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' and iconfig.k = 'BUILDBLOCK' and
|
||||
item_type = %d $revision limit 1",
|
||||
intval($u[0]['channel_id']),
|
||||
dbesc($page_id),
|
||||
@@ -78,12 +78,13 @@ class Block extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
xchan_query($r);
|
||||
$r = fetch_post_tags($r,true);
|
||||
|
||||
return prepare_page($r[0]);
|
||||
|
||||
|
||||
$o .= prepare_page($r[0]);
|
||||
return $o;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -83,31 +83,37 @@ class Bookmarks extends \Zotlabs\Web\Controller {
|
||||
|
||||
$channel = \App::get_channel();
|
||||
|
||||
$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 = '';
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
$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 .= '<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);
|
||||
}
|
||||
}
|
||||
|
||||
return replace_macros(get_markup_template('bookmarks.tpl'), [
|
||||
'$title1' => t('Bookmarks'),
|
||||
'$title2' => t('My Connections Bookmarks'),
|
||||
'$bookmarks' => $bookmarks,
|
||||
'$conn_bookmarks' => $conn_bookmarks,
|
||||
]);
|
||||
|
||||
$o .= '</div>';
|
||||
|
||||
return $o;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -136,13 +136,15 @@ class Cal extends Controller {
|
||||
}
|
||||
|
||||
$html = '';
|
||||
$tz = get_iconfig($rr, 'event', 'timezone', 'UTC');
|
||||
|
||||
if (x($_GET,'id')) {
|
||||
$rr['timezone'] = $tz;
|
||||
$html = format_event_html($rr);
|
||||
}
|
||||
|
||||
$tz = get_iconfig($rr, 'event', 'timezone');
|
||||
if(! $tz)
|
||||
$tz = 'UTC';
|
||||
|
||||
$events[] = array(
|
||||
'calendar_id' => 'channel_calendar',
|
||||
'rw' => true,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
|
||||
class Changeaddr extends \Zotlabs\Web\Controller {
|
||||
|
||||
@@ -30,7 +29,7 @@ class Changeaddr extends \Zotlabs\Web\Controller {
|
||||
if(! ($x && $x['account']))
|
||||
return;
|
||||
|
||||
if($account['account_password_changed'] > DBA::$dba->get_null_date()) {
|
||||
if($account['account_password_changed'] > 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 . '?top=0'
|
||||
'href' => z_root() . '/feed/' . $which
|
||||
]);
|
||||
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Only posts'),
|
||||
'href' => z_root() . '/feed/' . $which . '?top=1'
|
||||
'href' => z_root() . '/feed/' . $which . '?f=&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('Searching for:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8')
|
||||
'$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8')
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -386,8 +386,8 @@ class Channel extends Controller {
|
||||
|
||||
if ($noscript_content || $load) {
|
||||
if ($mid) {
|
||||
$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",
|
||||
$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",
|
||||
dbesc($mid),
|
||||
intval(App::$profile['profile_uid'])
|
||||
);
|
||||
@@ -396,7 +396,7 @@ class Channel extends Controller {
|
||||
}
|
||||
}
|
||||
else {
|
||||
$r = q("SELECT item.parent AS item_id, item.verb, $ordering FROM item
|
||||
$r = q("SELECT parent AS item_id, $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,11 +417,6 @@ 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]);
|
||||
@@ -444,7 +439,7 @@ class Channel extends Controller {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
$mode = 'channel';
|
||||
$mode = (($search) ? 'search' : 'channel');
|
||||
|
||||
if ((!$update) && (!$load)) {
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
use Zotlabs\Access\AccessList;
|
||||
@@ -301,7 +300,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(DBA::$dba->get_null_date())
|
||||
dbesc(NULL_DATE)
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -33,20 +33,26 @@ class Cloud extends Controller {
|
||||
*/
|
||||
function init() {
|
||||
|
||||
if (!is_dir('store')) {
|
||||
os_mkdir('store', STORAGE_DEFAULT_PERMISSIONS, false);
|
||||
// TODO: why is this required?
|
||||
// if we arrived at this path with any query parameters in the url, build a clean url without
|
||||
// them and redirect.
|
||||
|
||||
$parsed = parse_url(App::$query_string);
|
||||
if (!empty($parsed['query'])) {
|
||||
goaway(z_root() . '/' . $parsed['path']);
|
||||
}
|
||||
|
||||
if (! is_dir('store'))
|
||||
os_mkdir('store', STORAGE_DEFAULT_PERMISSIONS, false);
|
||||
|
||||
$which = null;
|
||||
if (argc() > 1) {
|
||||
if (argc() > 1)
|
||||
$which = argv(1);
|
||||
}
|
||||
|
||||
$profile = 0;
|
||||
|
||||
if ($which) {
|
||||
if ($which)
|
||||
profile_load( $which, $profile);
|
||||
}
|
||||
|
||||
$auth = new BasicAuth();
|
||||
|
||||
@@ -65,7 +71,7 @@ class Cloud extends Controller {
|
||||
$auth->observer = $ob_hash;
|
||||
}
|
||||
|
||||
if (!array_key_exists('cloud_sort',$_SESSION)) {
|
||||
if(! array_key_exists('cloud_sort',$_SESSION)) {
|
||||
$_SESSION['cloud_sort'] = 'name';
|
||||
}
|
||||
|
||||
@@ -93,6 +99,7 @@ class Cloud extends Controller {
|
||||
// require_once('\Zotlabs\Storage/QuotaPlugin.php');
|
||||
// $server->addPlugin(new \Zotlabs\Storage\\QuotaPlugin($auth));
|
||||
|
||||
|
||||
// over-ride the default XML output on thrown exceptions
|
||||
$server->on('exception', [ $this, 'DAVException' ]);
|
||||
|
||||
@@ -100,9 +107,8 @@ class Cloud extends Controller {
|
||||
|
||||
$server->start();
|
||||
|
||||
if ($browser->build_page) {
|
||||
if($browser->build_page)
|
||||
construct_page();
|
||||
}
|
||||
|
||||
killme();
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace Zotlabs\Module;
|
||||
|
||||
use Sabre\DAV as SDAV;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Storage;
|
||||
use Zotlabs\Web\HTTPSig;
|
||||
|
||||
@@ -107,16 +106,20 @@ class Dav extends \Zotlabs\Web\Controller {
|
||||
// A SabreDAV server-object
|
||||
$server = new SDAV\Server($rootDirectory);
|
||||
|
||||
|
||||
$authPlugin = new \Sabre\DAV\Auth\Plugin($auth);
|
||||
$server->addPlugin($authPlugin);
|
||||
|
||||
|
||||
// prevent overwriting changes each other with a lock backend
|
||||
$lockBackend = new SDAV\Locks\Backend\File('store/[data]/locks');
|
||||
$lockPlugin = new SDAV\Locks\Plugin($lockBackend);
|
||||
|
||||
$server->addPlugin($lockPlugin);
|
||||
|
||||
$server->enablePropfindDepthInfinity = Config::Get('system', 'propfind_depth_infinity', false);
|
||||
// provide a directory view for the cloud in Hubzilla
|
||||
$browser = new \Zotlabs\Storage\Browser($auth);
|
||||
$auth->setBrowserPlugin($browser);
|
||||
|
||||
// Experimental QuotaPlugin
|
||||
// $server->addPlugin(new \Zotlabs\Storage\QuotaPlugin($auth));
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
@@ -233,7 +232,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(DBA::$dba->get_null_date()),
|
||||
dbesc(NULL_DATE),
|
||||
dbesc($sync)
|
||||
);
|
||||
|
||||
|
||||
@@ -61,10 +61,12 @@ class Dreport extends \Zotlabs\Web\Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
$r = q("select * from dreport where dreport_xchan = '%s' and (dreport_mid = '%s' or dreport_mid = '%s')",
|
||||
$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')",
|
||||
dbesc($channel['channel_hash']),
|
||||
dbesc($mid),
|
||||
dbesc(str_replace('/item/', '/activity/', $mid))
|
||||
dbesc($mid . '#sync'),
|
||||
dbesc(str_replace('/item/', '/activity/', $mid)),
|
||||
dbesc(str_replace('/item/', '/activity/', $mid) . '#sync')
|
||||
);
|
||||
|
||||
if(! $r) {
|
||||
|
||||
@@ -122,10 +122,10 @@ class Editblock extends \Zotlabs\Web\Controller {
|
||||
'ptyp' => $itm[0]['type'],
|
||||
'mimeselect' => true,
|
||||
'mimetype' => $itm[0]['mimetype'],
|
||||
'body' => htmlspecialchars_decode(undo_post_tagging($content), ENT_COMPAT),
|
||||
'body' => undo_post_tagging($content),
|
||||
'post_id' => $post_id,
|
||||
'visitor' => true,
|
||||
'title' => htmlspecialchars_decode($itm[0]['title'], ENT_COMPAT),
|
||||
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
|
||||
'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' => htmlspecialchars_decode(undo_post_tagging($itm[0]['body']), ENT_COMPAT),
|
||||
'body' => undo_post_tagging($itm[0]['body']),
|
||||
'post_id' => $post_id,
|
||||
'title' => htmlspecialchars_decode($itm[0]['title'], ENT_COMPAT),
|
||||
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
|
||||
'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' => htmlspecialchars_decode(undo_post_tagging($content), ENT_COMPAT),
|
||||
'body' => undo_post_tagging($content),
|
||||
'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_decode($itm[0]['title'], ENT_COMPAT),
|
||||
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
|
||||
'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)
|
||||
|
||||
22
Zotlabs/Module/Embed.php
Normal file
22
Zotlabs/Module/Embed.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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,47 +2,49 @@
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\PConfig;
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
class Feed extends Controller {
|
||||
require_once('include/items.php');
|
||||
|
||||
class Feed extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
if (argc() < 2) {
|
||||
killme();
|
||||
}
|
||||
|
||||
$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 (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'])) {
|
||||
if(! in_array($params['direction'],['asc','desc'])) {
|
||||
$params['direction'] = 'desc';
|
||||
}
|
||||
|
||||
logger('public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $channel['channel_address']);
|
||||
if(argc() > 1) {
|
||||
|
||||
echo get_public_feed($channel, $params);
|
||||
|
||||
killme();
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -53,14 +53,7 @@ class Follow extends Controller {
|
||||
}
|
||||
|
||||
$uid = local_channel();
|
||||
$url = notags(trim($_REQUEST['url']));
|
||||
|
||||
$parsed = parse_url($url);
|
||||
if (isset($parsed['host'])) {
|
||||
$parsed['host'] = punify($parsed['host']);
|
||||
$url = unparse_url($parsed);
|
||||
}
|
||||
|
||||
$url = notags(punify(trim($_REQUEST['url'])));
|
||||
$return_url = $_SESSION['return_url'];
|
||||
$interactive = $_REQUEST['interactive'] ?? 1;
|
||||
$channel = App::get_channel();
|
||||
|
||||
@@ -104,15 +104,26 @@ class Home extends Controller {
|
||||
goaway($frontpage);
|
||||
}
|
||||
|
||||
$sitename = Config::Get('system', 'sitename', 'Hubzilla');
|
||||
$welcome = sprintf(t('Welcome to %s'), $sitename);
|
||||
$login_on_homepage = Config::Get('system', 'login_on_homepage');
|
||||
$o .= '<div class="generic-content-wrapper">';
|
||||
|
||||
$tpl = get_markup_template('home.tpl');
|
||||
return replace_macros($tpl, [
|
||||
'welcome' => $welcome,
|
||||
'loginbox' => $login_on_homepage ? login(true) : false,
|
||||
]);
|
||||
$sitename = Config::Get('system', 'sitename');
|
||||
if ($sitename) {
|
||||
$o .= '<div class="section-title-wrapper">';
|
||||
$o .= '<h2 class="">' . sprintf(t('Welcome to %s'), $sitename) . '</h2>';
|
||||
$o .= '</div>';
|
||||
|
||||
}
|
||||
|
||||
$o .= '<div class="section-content-wrapper">';
|
||||
|
||||
$loginbox = Config::Get('system', 'login_on_homepage');
|
||||
if (intval($loginbox) || $loginbox === false)
|
||||
$o .= login(true);
|
||||
|
||||
$o .= '</div>';
|
||||
$o .= '</div>';
|
||||
|
||||
return $o;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ 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;
|
||||
@@ -332,7 +331,7 @@ class Import extends Controller {
|
||||
else {
|
||||
$photos = import_xchan_photo($xchan['xchan_photo_l'], $xchan['xchan_hash']);
|
||||
if ($photos[4])
|
||||
$photodate = DBA::$dba->get_null_date();
|
||||
$photodate = NULL_DATE;
|
||||
else
|
||||
$photodate = $xchan['xchan_photo_date'];
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use URLify;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\IConfig;
|
||||
@@ -18,7 +17,6 @@ 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');
|
||||
@@ -187,8 +185,7 @@ 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']) : $item_type !== ITEM_TYPE_POST);
|
||||
|
||||
$nopush = ((!empty($_POST['nopush'])) ? intval($_POST['nopush']) : 0);
|
||||
|
||||
/*
|
||||
* Check service class limits
|
||||
@@ -210,7 +207,7 @@ class Item extends Controller {
|
||||
}
|
||||
|
||||
|
||||
$expires = DBA::$dba->get_null_date();
|
||||
$expires = NULL_DATE;
|
||||
|
||||
$route = '';
|
||||
$parent_item = null;
|
||||
@@ -560,7 +557,7 @@ class Item extends Controller {
|
||||
if (!empty($_POST['expire'])) {
|
||||
$expires = datetime_convert(date_default_timezone_get(), 'UTC', $_POST['expire']);
|
||||
if ($expires <= datetime_convert())
|
||||
$expires = DBA::$dba->get_null_date();
|
||||
$expires = NULL_DATE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -758,7 +755,7 @@ class Item extends Controller {
|
||||
$cats = explode(',', $categories);
|
||||
foreach ($cats as $cat) {
|
||||
|
||||
$catlink = channel_url($channel) . '?cat=' . urlencode(trim($cat));
|
||||
$catlink = $owner_xchan['xchan_url'] . '?f=&cat=' . urlencode(trim($cat));
|
||||
|
||||
$post_tags[] = [
|
||||
'uid' => $profile_uid,
|
||||
@@ -802,7 +799,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 : DBA::$dba->get_null_date());
|
||||
$comments_closed = (($nocomment) ? $comments_closed : NULL_DATE);
|
||||
|
||||
// determine if this is a wall post
|
||||
|
||||
@@ -876,7 +873,7 @@ class Item extends Controller {
|
||||
|
||||
if ($obj['endTime']) {
|
||||
$d = datetime_convert('UTC','UTC', $obj['endTime']);
|
||||
if ($d > DBA::$dba->get_null_date()) {
|
||||
if ($d > NULL_DATE) {
|
||||
$comments_closed = $d;
|
||||
}
|
||||
}
|
||||
@@ -1044,16 +1041,23 @@ class Item extends Controller {
|
||||
|
||||
$x = item_store_update($datarray, $execflag);
|
||||
|
||||
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);
|
||||
|
||||
if ($x['success']) {
|
||||
$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'])) {
|
||||
@@ -1076,17 +1080,9 @@ class Item extends Controller {
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
$post = item_store($datarray, $execflag);
|
||||
|
||||
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);
|
||||
|
||||
if ($post['success']) {
|
||||
$this->add_listeners($datarray);
|
||||
}
|
||||
|
||||
@@ -1166,6 +1162,19 @@ 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'];
|
||||
|
||||
@@ -1204,6 +1213,11 @@ 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['obj_type']) {
|
||||
switch ($item['object_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">' . escape_tags(translate_scope((!$item['public_policy']) ? 'specific' : $item['public_policy'])) . '</div>';
|
||||
echo '<div class="dropdown-item-text">' . translate_scope((!$item['public_policy']) ? 'specific' : $item['public_policy']) . '</div>';
|
||||
killme();
|
||||
}
|
||||
|
||||
@@ -232,17 +232,25 @@ class Lockview extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
$tpl = get_markup_template('access_dropdown.tpl');
|
||||
$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 = '';
|
||||
|
||||
echo replace_macros($tpl, [
|
||||
'$access_header' => t('Access'),
|
||||
'$guest_access_header' => t('Guest access'),
|
||||
'$ocap_access_header' => t('OCAP access'),
|
||||
if ($access_list) {
|
||||
$str .= $access_list_header . implode($access_list);
|
||||
}
|
||||
|
||||
'$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) : '',
|
||||
]);
|
||||
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;
|
||||
killme();
|
||||
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ 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 {
|
||||
|
||||
@@ -103,65 +101,26 @@ class Magic extends Controller {
|
||||
$dest = strip_zids($dest);
|
||||
$dest = strip_query_param($dest,'f');
|
||||
|
||||
// try RFC9421 first
|
||||
// We now post to the OWA endpoint. This improves security by providing a signed digest
|
||||
|
||||
$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(),
|
||||
],
|
||||
);
|
||||
$data = json_encode([ 'OpenWebAuth' => random_string() ]);
|
||||
|
||||
$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 = [];
|
||||
$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' => $curlHeaders]);
|
||||
logger('owa RFC9421 fetch returned: ' . print_r($x,true),LOGGER_DATA);
|
||||
|
||||
$rfc9421 = false;
|
||||
$x = z_fetch_url($owapath, false, $redirects, ['headers' => $headers]);
|
||||
|
||||
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);
|
||||
}
|
||||
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 = '';
|
||||
@@ -180,6 +139,7 @@ class Magic extends Controller {
|
||||
|
||||
echo $o;
|
||||
killme();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +135,7 @@ 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) {
|
||||
@@ -143,11 +144,8 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
}
|
||||
|
||||
if($search || $file || (!$pf && $cid) || $hashtags || $verb || $category || $conv || $unseen) {
|
||||
if(x($_GET, '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 = [];
|
||||
|
||||
@@ -275,6 +273,18 @@ 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) ";
|
||||
@@ -349,15 +359,15 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
// The name 'verb' is a holdover from the earlier XML
|
||||
// ActivityStreams specification.
|
||||
|
||||
if (str_starts_with($verb, '.')) {
|
||||
if (substr($verb, 0, 1) === '.') {
|
||||
$sql_verb = substr($verb, 1);
|
||||
$sql_extra .= sprintf(" AND item.obj_type = '%s' AND item.verb IN ('Create', 'Update', 'Invite') ",
|
||||
dbesc(protect_sprintf($sql_verb))
|
||||
$sql_extra .= sprintf(" AND item.obj_type like '%s' ",
|
||||
dbesc(protect_sprintf('%' . $sql_verb . '%'))
|
||||
);
|
||||
}
|
||||
else {
|
||||
$sql_extra .= sprintf(" AND item.verb = '%s' ",
|
||||
dbesc(protect_sprintf($verb))
|
||||
$sql_extra .= sprintf(" AND item.verb like '%s' ",
|
||||
dbesc(protect_sprintf('%' . $verb . '%'))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -366,23 +376,30 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
$sql_extra .= term_query('item', $file, TERM_FILE);
|
||||
}
|
||||
|
||||
if (!$dismiss_privacy_filter) {
|
||||
if ($dm) {
|
||||
$sql_extra .= ' AND item.item_private = 2 ';
|
||||
}
|
||||
else {
|
||||
$sql_extra .= ' AND item.item_private IN (0, 1) ';
|
||||
}
|
||||
if ($dm) {
|
||||
$sql_extra .= ' AND item.item_private = 2 ';
|
||||
}
|
||||
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 ) ";
|
||||
}
|
||||
|
||||
$itemspage = get_pconfig(local_channel(), 'system', 'itemspage');
|
||||
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10));
|
||||
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start']));
|
||||
if($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']));
|
||||
}
|
||||
|
||||
// cmin and cmax are both -1 when the affinity tool is disabled
|
||||
|
||||
@@ -419,6 +436,8 @@ 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
|
||||
@@ -450,6 +469,10 @@ 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 (!empty($test[0])) {
|
||||
if($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 (!empty($test[0])) {
|
||||
if($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,72 +12,51 @@ class Notifications extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
|
||||
// ajax mark all unseen items read
|
||||
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();
|
||||
}
|
||||
|
||||
if(x($_REQUEST, 'markRead')) {
|
||||
switch($_REQUEST['markRead']) {
|
||||
case 'dm':
|
||||
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_private = 2",
|
||||
$r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_private = 2",
|
||||
intval(local_channel())
|
||||
);
|
||||
break;
|
||||
case 'network':
|
||||
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1)",
|
||||
$r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_private IN (0, 1)",
|
||||
intval(local_channel())
|
||||
);
|
||||
break;
|
||||
case 'home':
|
||||
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1)",
|
||||
$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)",
|
||||
intval(local_channel())
|
||||
);
|
||||
break;
|
||||
case 'all_events':
|
||||
$evdays = intval(get_pconfig(local_channel(), 'system', 'evdays', 3));
|
||||
q("UPDATE event SET dismissed = 1 WHERE uid = %d AND dismissed = 0 AND dtstart < '%s' AND dtstart > '%s' ",
|
||||
$r = 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':
|
||||
q("UPDATE notify SET seen = 1 WHERE seen = 0 AND uid = %d",
|
||||
$r = q("UPDATE notify SET seen = 1 WHERE seen = 0 AND uid = %d",
|
||||
intval(local_channel())
|
||||
);
|
||||
break;
|
||||
case 'pubs':
|
||||
$_SESSION['sse_loadtime'] = datetime_convert();
|
||||
$_SESSION['static_loadtime'] = datetime_convert();
|
||||
unset($_SESSION['static_loadtime']);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
killme();
|
||||
}
|
||||
|
||||
// ajax mark all comments of a parent item read
|
||||
if(isset($_REQUEST['markItemRead']) && local_channel()) {
|
||||
if(x($_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();
|
||||
}
|
||||
|
||||
|
||||
69
Zotlabs/Module/Ochannel.php
Normal file
69
Zotlabs/Module/Ochannel.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?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>';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
51
Zotlabs/Module/Ofeed.php
Normal file
51
Zotlabs/Module/Ofeed.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?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,7 +3,6 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\Config;
|
||||
@@ -48,7 +47,7 @@ class Outbox extends Controller {
|
||||
|
||||
$params = [];
|
||||
|
||||
$params['begin'] = ((x($_REQUEST, 'date_begin')) ? $_REQUEST['date_begin'] : DBA::$dba->get_null_date());
|
||||
$params['begin'] = ((x($_REQUEST, 'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE);
|
||||
$params['end'] = ((x($_REQUEST, 'date_end')) ? $_REQUEST['date_end'] : '');
|
||||
$params['type'] = 'json';
|
||||
$params['pages'] = ((x($_REQUEST, 'pages')) ? intval($_REQUEST['pages']) : 0);
|
||||
|
||||
@@ -19,128 +19,96 @@ use Zotlabs\Web\Controller;
|
||||
class Owa extends Controller {
|
||||
|
||||
public function init(): void
|
||||
{
|
||||
{
|
||||
|
||||
$ret = [ 'success' => false ];
|
||||
|
||||
// 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);
|
||||
}
|
||||
if (!$this->validateAuthorizationHeader()) {
|
||||
$this->error('Missing or invalid authorization header.');
|
||||
}
|
||||
else {
|
||||
if (!$this->validateAuthorizationHeader()) {
|
||||
$this->error('Missing or invalid authorization header.');
|
||||
|
||||
$_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);
|
||||
}
|
||||
|
||||
$_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 {
|
||||
$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)
|
||||
);
|
||||
}
|
||||
}
|
||||
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 ($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']);
|
||||
}
|
||||
}
|
||||
|
||||
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']);
|
||||
}
|
||||
}
|
||||
if (!$ret['success']) {
|
||||
|
||||
if (!$ret['success']) {
|
||||
// Possible a reinstall?
|
||||
// In this case we probably already have an old hubloc
|
||||
// but not the new one yet.
|
||||
|
||||
// Possible a reinstall?
|
||||
// In this case we probably already have an old hubloc
|
||||
// but not the new one yet.
|
||||
$found = discover_by_webbie($keyId);
|
||||
|
||||
$found = discover_by_webbie($keyId);
|
||||
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 ($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']);
|
||||
}
|
||||
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']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
<?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,16 +60,26 @@ class Profile extends Controller {
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Posts and comments'),
|
||||
'href' => z_root() . '/feed/' . $which . '?top=0'
|
||||
'href' => z_root() . '/feed/' . $which
|
||||
]);
|
||||
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Only posts'),
|
||||
'href' => z_root() . '/feed/' . $which . '?top=1'
|
||||
'href' => z_root() . '/feed/' . $which . '?f=&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,7 +1,6 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
@@ -341,7 +340,7 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
$with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : '');
|
||||
|
||||
if(! strlen($howlong))
|
||||
$howlong = DBA::$dba->get_null_date();
|
||||
$howlong = NULL_DATE;
|
||||
else
|
||||
$howlong = datetime_convert(date_default_timezone_get(),'UTC',$howlong);
|
||||
|
||||
@@ -697,9 +696,10 @@ 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.') . ' ' . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))),
|
||||
'$default' => t('This is your default profile.') . EOL . 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'] <= DBA::$dba->get_null_date() ? '' : datetime_convert('UTC',date_default_timezone_get(),$r[0]['howlong']))),
|
||||
'$howlong' => array('howlong', t('Since (date)'), ($r[0]['howlong'] <= 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,8 +833,6 @@ 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(
|
||||
@@ -843,7 +841,7 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
'$alt' => t('Profile Image'),
|
||||
'$profile_name' => $rr['profile_name'],
|
||||
'$visible' => (($rr['is_default'])
|
||||
? '<strong>' . escape_tags(translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile')))) . '</strong>'
|
||||
? '<strong>' . 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,7 +44,6 @@ 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",
|
||||
@@ -75,11 +74,10 @@ class Profperm extends \Zotlabs\Web\Controller {
|
||||
dbesc($profile['profile_guid'])
|
||||
);
|
||||
|
||||
$ingroup = [];
|
||||
if($r) {
|
||||
$ingroup = array();
|
||||
if($r)
|
||||
foreach($r as $member)
|
||||
$ingroup[] = $member['abook_id'];
|
||||
}
|
||||
|
||||
$members = $r;
|
||||
|
||||
@@ -106,57 +104,68 @@ class Profperm extends \Zotlabs\Web\Controller {
|
||||
);
|
||||
|
||||
$members = $r;
|
||||
$ingroup = [];
|
||||
if($r) {
|
||||
|
||||
$ingroup = array();
|
||||
if(count($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>';
|
||||
|
||||
}
|
||||
|
||||
// Build template data
|
||||
$members_tpl = [];
|
||||
$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);
|
||||
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) ];
|
||||
}
|
||||
|
||||
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" />';
|
||||
|
||||
$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) ];
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
]);
|
||||
$o .= '</div><div id="prof-all-contacts-end"></div>';
|
||||
|
||||
if($change) {
|
||||
echo $inner_html;
|
||||
echo $o;
|
||||
killme();
|
||||
}
|
||||
|
||||
$o .= $inner_html;
|
||||
$o .= '</div>';
|
||||
return $o;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -247,7 +247,11 @@ class Pubstream extends \Zotlabs\Web\Controller {
|
||||
|
||||
if($r) {
|
||||
$items = items_by_parent_ids($r, blog_mode: $blog_mode);
|
||||
xchan_query($items);
|
||||
|
||||
// use effective_uid param of xchan_query to help sort out comment permission
|
||||
// for sys_channel owned items.
|
||||
|
||||
xchan_query($items, true, local_channel());
|
||||
$items = fetch_post_tags($items,true);
|
||||
$items = conv_sort($items, $ordering);
|
||||
}
|
||||
|
||||
@@ -142,15 +142,7 @@ class Regate extends \Zotlabs\Web\Controller {
|
||||
|
||||
if (($flags & ACCOUNT_PENDING ) == ACCOUNT_PENDING) {
|
||||
$nextpage = 'regate/' . bin2hex($did2) . $didx;
|
||||
$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));
|
||||
}
|
||||
q("COMMIT");
|
||||
}
|
||||
elseif (($flags ^ REGISTER_AGREED) == 0) {
|
||||
|
||||
@@ -394,7 +386,7 @@ class Regate extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
else {
|
||||
zar_log('ZAR1135E not awaited url parameter received');
|
||||
goaway(z_root());
|
||||
goaway(z_root);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -323,6 +323,7 @@ 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'));
|
||||
@@ -337,6 +338,7 @@ class Register extends Controller {
|
||||
'$hash' => $empin
|
||||
]
|
||||
);
|
||||
pop_lang();
|
||||
zar_reg_mail($reonar);
|
||||
|
||||
} else {
|
||||
@@ -443,7 +445,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 if preferred') . '</a>';
|
||||
$other_sites = '<a href="pubsites">' . t('Register at another affiliated hub in case when prefered') . '</a>';
|
||||
}
|
||||
|
||||
$duty = zar_register_dutystate();
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
<?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'] > DBA::$dba->get_null_date()) {
|
||||
|
||||
if($account['account_password_changed'] > NULL_DATE) {
|
||||
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
|
||||
if($account['account_password_changed'] > $d1) {
|
||||
if($account['account_password_changed'] > d1) {
|
||||
notice( t('Account removals are not allowed within 48 hours of changing the account password.') . EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
|
||||
class Removeme extends \Zotlabs\Web\Controller {
|
||||
|
||||
@@ -30,7 +29,7 @@ class Removeme extends \Zotlabs\Web\Controller {
|
||||
if(! ($x && $x['account']))
|
||||
return;
|
||||
|
||||
if($account['account_password_changed'] > DBA::$dba->get_null_date()) {
|
||||
if($account['account_password_changed'] > 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(): void
|
||||
private function processSubthreadRequest() : string
|
||||
{
|
||||
$mid = $_GET['mid'];
|
||||
$parent = intval($_GET['parent']);
|
||||
@@ -52,11 +52,11 @@ class Request extends Controller
|
||||
json_return_and_die($ret);
|
||||
}
|
||||
|
||||
public function init() : void
|
||||
public function get() : string
|
||||
{
|
||||
|
||||
if (in_array($_GET['verb'], ['comment', 'load'])) {
|
||||
self::processSubthreadRequest();
|
||||
return self::processSubthreadRequest();
|
||||
}
|
||||
|
||||
$verb = self::mapVerb($_GET['verb']);
|
||||
|
||||
@@ -42,7 +42,9 @@ class Search extends Controller {
|
||||
$observer = App::get_observer();
|
||||
$observer_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
|
||||
$title = t('Search');
|
||||
$o = '<div class="generic-content-wrapper-styled">' . "\r\n";
|
||||
|
||||
$o .= '<h2>' . t('Search') . '</h2>';
|
||||
|
||||
if (x(App::$data, 'search'))
|
||||
$search = trim(App::$data['search']);
|
||||
@@ -55,7 +57,7 @@ class Search extends Controller {
|
||||
$search = ((x($_GET, 'tag')) ? trim(escape_tags(rawurldecode($_GET['tag']))) : '');
|
||||
}
|
||||
|
||||
$searchbox = search($search, 'search-box', '/search', ((local_channel()) ? true : false));
|
||||
$o .= search($search, 'search-box', '/search', ((local_channel()) ? true : false));
|
||||
|
||||
if (local_channel() && str_starts_with($search, 'https://') && !$update && !$load) {
|
||||
|
||||
@@ -69,13 +71,7 @@ class Search extends Controller {
|
||||
$url = unpack_link_id(basename($url));
|
||||
}
|
||||
|
||||
$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);
|
||||
$f = Libzot::fetch_conversation(App::get_channel(), punify($url), true);
|
||||
|
||||
if ($f) {
|
||||
$uuid = $f[0]['message_uuid'];
|
||||
@@ -91,7 +87,7 @@ class Search extends Controller {
|
||||
else {
|
||||
// try other fetch providers (e.g. diaspora, pubcrawl)
|
||||
$hookdata = [
|
||||
'url' => $url
|
||||
'url' => punify($url)
|
||||
];
|
||||
call_hooks('fetch_provider', $hookdata);
|
||||
}
|
||||
@@ -119,17 +115,8 @@ class Search extends Controller {
|
||||
goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search);
|
||||
}
|
||||
|
||||
if (!$search) {
|
||||
$tpl = get_markup_template('search.tpl');
|
||||
|
||||
return replace_macros($tpl, [
|
||||
'$title' => $title,
|
||||
'$searchbox' => $searchbox,
|
||||
'$livesearch' => '',
|
||||
'$results_header' => '',
|
||||
'$conversation' => '',
|
||||
]);
|
||||
}
|
||||
if (!$search)
|
||||
return $o;
|
||||
|
||||
if ($tag) {
|
||||
$wildtag = str_replace('*', '%', $search);
|
||||
@@ -150,15 +137,14 @@ 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.
|
||||
|
||||
$livesearch .= '<div id="live-search"></div>' . "\r\n";
|
||||
$livesearch .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1))
|
||||
$o .= '<div id="live-search"></div>' . "\r\n";
|
||||
$o .= "<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"), [
|
||||
@@ -262,21 +248,15 @@ class Search extends Controller {
|
||||
}
|
||||
|
||||
if ($tag)
|
||||
$results_header = sprintf(t('Items tagged with: %s'), $search);
|
||||
$o .= '<h2>' . sprintf(t('Items tagged with: %s'), $search) . '</h2>';
|
||||
else
|
||||
$results_header = sprintf(t('Search results for: %s'), $search);
|
||||
$o .= '<h2>' . sprintf(t('Search results for: %s'), $search) . '</h2>';
|
||||
|
||||
$conversation = conversation($items, 'search', $update, 'client');
|
||||
$o .= conversation($items, 'search', $update, 'client');
|
||||
|
||||
$tpl = get_markup_template('search.tpl');
|
||||
$o .= '</div>';
|
||||
|
||||
return replace_macros($tpl, [
|
||||
'$title' => $title,
|
||||
'$searchbox' => $searchbox,
|
||||
'$livesearch' => $livesearch,
|
||||
'$results_header' => $results_header ?? '',
|
||||
'$conversation' => $conversation,
|
||||
]);
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -153,6 +153,14 @@ 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,6 +504,12 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -528,7 +534,6 @@ 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())) {
|
||||
@@ -585,15 +590,9 @@ 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[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.');
|
||||
if(! extension_loaded('intl')) {
|
||||
$ck_funcs[6]['status'] = false;
|
||||
$ck_funcs[6]['help'] = t('Error: intl PHP module required but not installed.');
|
||||
}
|
||||
|
||||
$checks = array_merge($checks, $ck_funcs);
|
||||
|
||||
@@ -120,7 +120,7 @@ class Share extends \Zotlabs\Web\Controller {
|
||||
$arr['changed'] = $created;
|
||||
$arr['item_type'] = ITEM_TYPE_POST;
|
||||
|
||||
$mention = '[zrl=' . $item['author']['xchan_url'] . ']@' . $item['author']['xchan_name'] . '[/zrl]';
|
||||
$mention = '@[zrl=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/zrl]';
|
||||
$arr['body'] = sprintf( t('🔁 Repeated %1$s\'s %2$s'), $mention, Activity::activity_obj_mapper($item['obj_type']));
|
||||
|
||||
$arr['author_xchan'] = $channel['channel_hash'];
|
||||
|
||||
@@ -17,9 +17,6 @@ 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'),
|
||||
@@ -43,7 +40,6 @@ 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,7 +3,6 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Web\Controller;
|
||||
@@ -137,7 +136,7 @@ class Sse extends Controller {
|
||||
|
||||
session_reset();
|
||||
|
||||
XConfig::Set(self::$ob_hash, 'sse', 'timestamp', DBA::$dba->get_null_date());
|
||||
XConfig::Set(self::$ob_hash, 'sse', 'timestamp', NULL_DATE);
|
||||
XConfig::Set(self::$ob_hash, 'sse', 'notifications', []);
|
||||
|
||||
if (ob_get_length() > 0) {
|
||||
|
||||
@@ -20,8 +20,6 @@ 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();
|
||||
@@ -43,8 +41,6 @@ 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']));
|
||||
@@ -68,6 +64,7 @@ class Sse_bs extends Controller {
|
||||
$_SESSION['sse_loadtime'] = datetime_convert();
|
||||
}
|
||||
|
||||
|
||||
$network = false;
|
||||
$dm = false;
|
||||
$home = false;
|
||||
@@ -94,14 +91,8 @@ 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($selected_forum_id ?? true);
|
||||
$result = self::$f(true);
|
||||
json_return_and_die($result);
|
||||
}
|
||||
|
||||
@@ -111,7 +102,7 @@ class Sse_bs extends Controller {
|
||||
self::bs_home($home),
|
||||
self::bs_notify(),
|
||||
self::bs_intros(),
|
||||
self::bs_forums($selected_forum_id),
|
||||
self::bs_forums(),
|
||||
self::bs_pubs($pubs),
|
||||
self::bs_files(),
|
||||
self::bs_all_events(),
|
||||
@@ -131,7 +122,7 @@ class Sse_bs extends Controller {
|
||||
$str = '';
|
||||
$slice = 0;
|
||||
|
||||
$mids_all = isset($_SESSION['sse_mids_all']) ? json_unserialize($_SESSION['sse_mids_all']) : [];
|
||||
$mids_all = isset($_SESSION['sse_mids_all']) ? unserialise($_SESSION['sse_mids_all']) : [];
|
||||
|
||||
if (count($mids_all) > 3000) {
|
||||
$slice = count($mids_all) - 3000;
|
||||
@@ -177,7 +168,7 @@ class Sse_bs extends Controller {
|
||||
$mids_all = array_merge($mids_all, $activities_arr);
|
||||
}
|
||||
|
||||
$_SESSION['sse_mids_all'] = json_serialize(array_unique($mids_all));
|
||||
$_SESSION['sse_mids_all'] = serialise(array_unique($mids_all));
|
||||
|
||||
if(! self::$uid) {
|
||||
return;
|
||||
@@ -214,8 +205,6 @@ 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)) {
|
||||
@@ -246,7 +235,7 @@ class Sse_bs extends Controller {
|
||||
$item_normal
|
||||
$sql_extra
|
||||
$sql_extra2
|
||||
ORDER BY item.created $direction LIMIT $limit OFFSET $offset",
|
||||
ORDER BY item.created DESC LIMIT $limit OFFSET $offset",
|
||||
intval(self::$uid),
|
||||
dbescdate($_SESSION['sse_loadtime']),
|
||||
dbesc(self::$ob_hash)
|
||||
@@ -273,7 +262,7 @@ class Sse_bs extends Controller {
|
||||
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
|
||||
AND author_xchan != '%s'
|
||||
$item_normal
|
||||
$sql_extra LIMIT $count_limit",
|
||||
$sql_extra LIMIT 100",
|
||||
intval(self::$uid),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
@@ -301,8 +290,6 @@ 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)) {
|
||||
@@ -333,7 +320,7 @@ class Sse_bs extends Controller {
|
||||
$item_normal
|
||||
$sql_extra
|
||||
$sql_extra2
|
||||
ORDER BY created $direction LIMIT $limit OFFSET $offset",
|
||||
ORDER BY created DESC LIMIT $limit OFFSET $offset",
|
||||
intval(self::$uid),
|
||||
dbescdate($_SESSION['sse_loadtime']),
|
||||
dbesc(self::$ob_hash)
|
||||
@@ -359,7 +346,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 $count_limit",
|
||||
AND author_xchan != '%s' LIMIT 100",
|
||||
intval(self::$uid),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
@@ -387,8 +374,6 @@ 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)) {
|
||||
@@ -420,7 +405,7 @@ class Sse_bs extends Controller {
|
||||
$item_normal
|
||||
$sql_extra
|
||||
$sql_extra2
|
||||
ORDER BY item.created $direction LIMIT $limit OFFSET $offset",
|
||||
ORDER BY item.created DESC LIMIT $limit OFFSET $offset",
|
||||
intval(self::$uid),
|
||||
dbescdate($_SESSION['sse_loadtime']),
|
||||
dbesc(self::$ob_hash)
|
||||
@@ -446,7 +431,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 $count_limit",
|
||||
AND author_xchan != '%s' LIMIT 100",
|
||||
intval(self::$uid),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
@@ -485,8 +470,6 @@ 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 = '';
|
||||
@@ -502,7 +485,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']) ? json_unserialize($_SESSION['sse_mids_all']) : [];
|
||||
$sse_mids_all = isset($_SESSION['sse_mids_all']) ? unserialise($_SESSION['sse_mids_all']) : [];
|
||||
if ($sse_mids_all) {
|
||||
$sql_extra3 = " AND item.uuid NOT IN (" . protect_sprintf(implode(',', $sse_mids_all)) . ") ";
|
||||
}
|
||||
@@ -532,9 +515,9 @@ class Sse_bs extends Controller {
|
||||
$sql_extra
|
||||
$sql_extra2
|
||||
$sql_extra3
|
||||
ORDER BY item.created $direction LIMIT $limit OFFSET $offset",
|
||||
ORDER BY item.created DESC LIMIT $limit OFFSET $offset",
|
||||
dbescdate($_SESSION['sse_loadtime']),
|
||||
dbescdate($_SESSION['static_loadtime']),
|
||||
dbescdate($_SESSION['last_login_date'] ?? $_SESSION['static_loadtime']),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
|
||||
@@ -560,9 +543,9 @@ class Sse_bs extends Controller {
|
||||
$item_normal
|
||||
$sql_extra
|
||||
$sql_extra3
|
||||
AND author_xchan != '%s' LIMIT $count_limit",
|
||||
AND author_xchan != '%s' LIMIT 100",
|
||||
dbescdate($_SESSION['sse_loadtime']),
|
||||
dbescdate($_SESSION['static_loadtime']),
|
||||
dbescdate($_SESSION['last_login_date'] ?? $_SESSION['static_loadtime']),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
|
||||
@@ -585,9 +568,7 @@ class Sse_bs extends Controller {
|
||||
if(! (self::$vnotify & VNOTIFY_SYSTEM))
|
||||
return $result;
|
||||
|
||||
$direction = self::$direction;
|
||||
|
||||
$r = q("SELECT * FROM notify WHERE uid = %d AND seen = 0 ORDER BY created $direction",
|
||||
$r = q("SELECT * FROM notify WHERE uid = %d AND seen = 0 ORDER BY created DESC",
|
||||
intval(self::$uid)
|
||||
);
|
||||
|
||||
@@ -614,9 +595,7 @@ class Sse_bs extends Controller {
|
||||
if(! (self::$vnotify & VNOTIFY_INTRO))
|
||||
return $result;
|
||||
|
||||
$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",
|
||||
$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",
|
||||
intval(self::$uid)
|
||||
);
|
||||
|
||||
@@ -631,95 +610,69 @@ class Sse_bs extends Controller {
|
||||
|
||||
}
|
||||
|
||||
function bs_forums($selected_forum_id) {
|
||||
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;
|
||||
|
||||
$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 ++) {
|
||||
|
||||
$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",
|
||||
$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",
|
||||
intval(self::$uid),
|
||||
dbesc(self::$ob_hash),
|
||||
dbesc($forums[$x]['xchan_hash'])
|
||||
dbesc($forums[$x]['xchan_hash']),
|
||||
dbesc($forums[$x]['xchan_hash']),
|
||||
dbesc(self::$ob_hash)
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
$result[$forum_id]['count'] = count($r);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
$result['forums']['count'] = $i;
|
||||
$result['forums']['notifications'] = array_values($forums);
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -738,7 +691,6 @@ 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
|
||||
@@ -751,7 +703,7 @@ class Sse_bs extends Controller {
|
||||
AND author_xchan != '%s'
|
||||
AND item_unseen = 1
|
||||
$item_normal
|
||||
ORDER BY created $direction",
|
||||
ORDER BY created DESC",
|
||||
dbesc(ACTIVITY_POST),
|
||||
intval(self::$uid),
|
||||
dbesc(self::$ob_hash)
|
||||
@@ -783,12 +735,10 @@ 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 $direction",
|
||||
ORDER BY dtstart DESC",
|
||||
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'] = DBA::$dba->get_null_date();
|
||||
$event['event_status_date'] = NULL_DATE;
|
||||
$event['event_percent'] = 0;
|
||||
$event['event_sequence'] = $event['event_sequence'] + 1;
|
||||
$event['edited'] = datetime_convert();
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use Zotlabs\Lib\AccessList;
|
||||
@@ -85,7 +84,7 @@ class Tokens extends Controller {
|
||||
if(trim($_POST['expires']))
|
||||
$expires = datetime_convert(date_default_timezone_get(),'UTC',$_POST['expires']);
|
||||
else
|
||||
$expires = DBA::$dba->get_null_date();
|
||||
$expires = 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",
|
||||
@@ -291,7 +290,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'] > DBA::$dba->get_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'] > NULL_DATE) ? datetime_convert('UTC',date_default_timezone_get(),$atoken['atoken_expires']) : ''), ''),
|
||||
'$submit' => t('Submit'),
|
||||
'$delete' => t('Delete')
|
||||
));
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\IConfig;
|
||||
use Zotlabs\Lib\ObjCache;
|
||||
|
||||
|
||||
class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
|
||||
@@ -30,16 +28,13 @@ class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
$item_normal = item_normal_search();
|
||||
|
||||
if(local_channel() && $item_id) {
|
||||
$r = q("select * from item where uid in (%d , %d) and id = %d $item_normal limit 1",
|
||||
$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",
|
||||
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;
|
||||
|
||||
@@ -50,41 +45,15 @@ class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
killme();
|
||||
}
|
||||
|
||||
$cached = true;
|
||||
|
||||
$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));
|
||||
$content = escape_tags($r[0]['body']);
|
||||
$o = (($json) ? json_encode($content) : $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>hashpath: ' . hash('sha256', $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>uuid: ' . $r[0]['uuid'] . '</div>';
|
||||
echo '<hr>';
|
||||
echo '<pre class="p-1">' . $o . '</pre>';
|
||||
echo '</div>';
|
||||
|
||||
@@ -20,35 +20,27 @@ class Vote extends Controller {
|
||||
json_return_and_die($ret);
|
||||
}
|
||||
|
||||
|
||||
$fetch = null;
|
||||
$id = argv(1);
|
||||
$response = $_REQUEST['answer'];
|
||||
|
||||
if (!$id) {
|
||||
$ret['message'] = t('Missing poll id.');
|
||||
json_return_and_die($ret);
|
||||
if ($id) {
|
||||
$fetch = q("select * from item where id = %d limit 1",
|
||||
intval($id)
|
||||
);
|
||||
}
|
||||
|
||||
$answer = q("select * from item where parent = %d and uid = %d and obj_type = 'Answer' limit 1",
|
||||
intval($id),
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
|
||||
if ($answer) {
|
||||
$ret['message'] = t('You have already submitted your vote for this poll.');
|
||||
json_return_and_die($ret);
|
||||
if ($fetch && $fetch[0]['obj_type'] === 'Question') {
|
||||
$obj = json_decode($fetch[0]['obj'],true);
|
||||
|
||||
}
|
||||
|
||||
$poll = q("select * from item where id = %d limit 1",
|
||||
intval($id)
|
||||
);
|
||||
|
||||
if (!$poll && $poll[0]['obj_type'] !== 'Question') {
|
||||
else {
|
||||
$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']) {
|
||||
@@ -89,18 +81,17 @@ class Vote extends Controller {
|
||||
$item['aid'] = $channel['channel_account_id'];
|
||||
$item['uid'] = $channel['channel_id'];
|
||||
$item['item_origin'] = 1;
|
||||
$item['parent'] = $poll[0]['id'];
|
||||
$item['parent_mid'] = $poll[0]['mid'];
|
||||
$item['thr_parent'] = $poll[0]['mid'];
|
||||
$item['parent'] = $fetch[0]['id'];
|
||||
$item['parent_mid'] = $fetch[0]['mid'];
|
||||
$item['thr_parent'] = $fetch[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'] = $poll[0]['author_xchan'];
|
||||
$item['allow_cid'] = '<' . $poll[0]['author_xchan'] . '>';
|
||||
$item['owner_xchan'] = $fetch[0]['author_xchan'];
|
||||
$item['allow_cid'] = '<' . $fetch[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);
|
||||
@@ -113,7 +104,7 @@ class Vote extends Controller {
|
||||
|
||||
$x = item_store($item);
|
||||
|
||||
retain_item($poll[0]['id']);
|
||||
retain_item($fetch[0]['id']);
|
||||
|
||||
if($x['success']) {
|
||||
Master::Summon(['Notifier', 'like', $x['item_id']]);
|
||||
@@ -134,7 +125,7 @@ class Vote extends Controller {
|
||||
}
|
||||
|
||||
$ret['success'] = true;
|
||||
$ret['message'] = t('Your vote has been submitted. Updates may not appear instantly.');
|
||||
$ret['message'] = t('Response submitted. Updates may not appear instantly.');
|
||||
json_return_and_die($ret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<?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 ];
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user