mirror of
https://framagit.org/hubzilla/core.git
synced 2026-06-21 09:01:15 -04:00
Compare commits
335 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ee82b5ccc | ||
|
|
d4401faed5 | ||
|
|
9daecfbb46 | ||
|
|
2adb8ffbb5 | ||
|
|
2e1a04f6e3 | ||
|
|
9f0fc77ac6 | ||
|
|
bb303ebc4c | ||
|
|
b8683f73ce | ||
|
|
2eae33736b | ||
|
|
4dbcbbb1af | ||
|
|
8af931a4b9 | ||
|
|
b80cb0adad | ||
|
|
b5f7e55c2b | ||
|
|
3952aba824 | ||
|
|
15db8dd945 | ||
|
|
1876762216 | ||
|
|
492e8df555 | ||
|
|
7667af5659 | ||
|
|
92ca97ea6b | ||
|
|
bf57bd34f9 | ||
|
|
f1bd35b555 | ||
|
|
5a77f23186 | ||
|
|
6b605fecb0 | ||
|
|
496dade675 | ||
|
|
82b8ed2652 | ||
|
|
2625fe9527 | ||
|
|
320019fca3 | ||
|
|
78f2bb46f9 | ||
|
|
88c1cda002 | ||
|
|
6f65d2d6ef | ||
|
|
674522c8da | ||
|
|
47c902a5ea | ||
|
|
0c7731bb76 | ||
|
|
62582509cd | ||
|
|
67d73f74ac | ||
|
|
1553bc708f | ||
|
|
99728bf038 | ||
|
|
965e643c71 | ||
|
|
8d283e0be5 | ||
|
|
0cd682d85e | ||
|
|
9aff1d4024 | ||
|
|
b0a2bf47c8 | ||
|
|
3d29c498e9 | ||
|
|
acf683a7d3 | ||
|
|
fdeab5b2d1 | ||
|
|
fa7c8e93f1 | ||
|
|
fd6b252b36 | ||
|
|
f7a29f7539 | ||
|
|
de9e2c27ea | ||
|
|
0376b5d442 | ||
|
|
b20ed4f455 | ||
|
|
a574c79f9e | ||
|
|
9c9ca7728c | ||
|
|
7fc1d58b10 | ||
|
|
782765e377 | ||
|
|
e2dbf0a785 | ||
|
|
1680bac8b5 | ||
|
|
231384a58f | ||
|
|
6edeea4b42 | ||
|
|
7559d6bb5c | ||
|
|
1c9c0dc70e | ||
|
|
bf4227bfef | ||
|
|
c296d4bbed | ||
|
|
68452fb07b | ||
|
|
7f98cd606b | ||
|
|
d0ae7a0493 | ||
|
|
81e79eec04 | ||
|
|
7d70f2f0f2 | ||
|
|
016a11ad27 | ||
|
|
1637e61681 | ||
|
|
1ec0e91405 | ||
|
|
1774140307 | ||
|
|
8152da1275 | ||
|
|
eb10820195 | ||
|
|
9d33456c89 | ||
|
|
3dd9559d9f | ||
|
|
51ac502d97 | ||
|
|
955ee217e3 | ||
|
|
6d8bfe58ef | ||
|
|
f98f540256 | ||
|
|
72c930f964 | ||
|
|
ce24b86841 | ||
|
|
f63ba541d7 | ||
|
|
a9f54473db | ||
|
|
eb5ea6536b | ||
|
|
2e9b64347d | ||
|
|
4fcff43fb5 | ||
|
|
260b61ba3b | ||
|
|
d0b7e4ea79 | ||
|
|
33c95c810a | ||
|
|
81105ff9de | ||
|
|
ced3113516 | ||
|
|
cbd208eea3 | ||
|
|
ad85825cab | ||
|
|
2fb816139a | ||
|
|
c9166b26c5 | ||
|
|
86d58065b3 | ||
|
|
1f265cc6d5 | ||
|
|
4474fdd4f9 | ||
|
|
f71eeab5be | ||
|
|
b3526415f9 | ||
|
|
471ded3efa | ||
|
|
e954d8c55e | ||
|
|
70f82c3967 | ||
|
|
707e07bbbc | ||
|
|
e89eb04427 | ||
|
|
ae0e82ee3a | ||
|
|
9ea5d20a3d | ||
|
|
fd69008484 | ||
|
|
3256aa8be9 | ||
|
|
add26a5b5f | ||
|
|
dc03263bef | ||
|
|
1897cd0b1b | ||
|
|
ba24958b37 | ||
|
|
52a2a0d89a | ||
|
|
91944da69e | ||
|
|
78e30a4d32 | ||
|
|
ccd6d1a38c | ||
|
|
f14c1be963 | ||
|
|
db5e92b72d | ||
|
|
3d3580b23f | ||
|
|
04d44c9965 | ||
|
|
82bd91d9d7 | ||
|
|
492533729d | ||
|
|
283b606c09 | ||
|
|
b0b5523f2b | ||
|
|
738797467d | ||
|
|
a2ee5705f4 | ||
|
|
cc1713b69a | ||
|
|
6f5e4c5c2e | ||
|
|
3e0b9c01b6 | ||
|
|
25218fea43 | ||
|
|
c907e569f1 | ||
|
|
b6bec6f7b7 | ||
|
|
31b5cfe7ef | ||
|
|
0efb3c5c95 | ||
|
|
d88f3169c8 | ||
|
|
3772e910df | ||
|
|
e995d45b53 | ||
|
|
669136bce7 | ||
|
|
2448e6df27 | ||
|
|
4c0b37db66 | ||
|
|
0ea3f3d36d | ||
|
|
cb7dc2059a | ||
|
|
e988bc9fae | ||
|
|
cb23a9e235 | ||
|
|
8d9623674d | ||
|
|
6d181ee69e | ||
|
|
2d97f8fa25 | ||
|
|
98a3c97820 | ||
|
|
89e1328ed0 | ||
|
|
5cb4db0353 | ||
|
|
dc43cd9a85 | ||
|
|
5a6a7386a8 | ||
|
|
4f545e31dd | ||
|
|
437c0a8913 | ||
|
|
268fccdb30 | ||
|
|
bb7689be93 | ||
|
|
bd63af69b2 | ||
|
|
e5d4358d61 | ||
|
|
0fa4962620 | ||
|
|
fe4d6229a4 | ||
|
|
99a1569d07 | ||
|
|
9fb5cd12be | ||
|
|
d85c737db7 | ||
|
|
794b456b8a | ||
|
|
a0cb5fcb3f | ||
|
|
0d382634ec | ||
|
|
225c83dfbe | ||
|
|
e736945f1d | ||
|
|
cb0102b971 | ||
|
|
8b46767d30 | ||
|
|
3f39d0d249 | ||
|
|
3130a94a4c | ||
|
|
d44c004bd0 | ||
|
|
c655046e1f | ||
|
|
b32c1c1e22 | ||
|
|
38f040f9b5 | ||
|
|
7dcaebf281 | ||
|
|
fad5f98405 | ||
|
|
ea07bd1693 | ||
|
|
deaab14c5f | ||
|
|
8258b8b088 | ||
|
|
db5c217a21 | ||
|
|
724ee7dbab | ||
|
|
fb9fe0d3c4 | ||
|
|
9019636449 | ||
|
|
933b4fbcfe | ||
|
|
7675ed0145 | ||
|
|
f25211d7ff | ||
|
|
30cf6d827c | ||
|
|
1323ea8e18 | ||
|
|
9f98f6bbd5 | ||
|
|
cedc6c4230 | ||
|
|
b6a58fbf6e | ||
|
|
e295765bef | ||
|
|
141dc2fdb7 | ||
|
|
c051c4d0aa | ||
|
|
e289078f82 | ||
|
|
87f79381d9 | ||
|
|
98840ae1d0 | ||
|
|
28746891c8 | ||
|
|
e5d0ef79ef | ||
|
|
2eb51233f6 | ||
|
|
5432819788 | ||
|
|
c03f543b54 | ||
|
|
de6506eb57 | ||
|
|
fb48bbe2c1 | ||
|
|
65132c8fdc | ||
|
|
2c0936187a | ||
|
|
48dbba2f4d | ||
|
|
16068af0bb | ||
|
|
d09b01245f | ||
|
|
bdd0b0a6fb | ||
|
|
48030617ab | ||
|
|
bb49a51be3 | ||
|
|
da266f5739 | ||
|
|
f6a4997d6f | ||
|
|
7a99089204 | ||
|
|
2e55973da2 | ||
|
|
80f4eea9e9 | ||
|
|
be5c8aa2a3 | ||
|
|
643d25b34b | ||
|
|
c29d79854e | ||
|
|
2834544451 | ||
|
|
60eb8aa42b | ||
|
|
aff39521cd | ||
|
|
f19ad4b087 | ||
|
|
fc243196d9 | ||
|
|
42e78d9666 | ||
|
|
26d1653bfb | ||
|
|
f39ccab6c1 | ||
|
|
a0e6dcbb77 | ||
|
|
320a0c1b62 | ||
|
|
84d52a9ad1 | ||
|
|
dc298ce6c1 | ||
|
|
b464cd6181 | ||
|
|
4eb7e29bab | ||
|
|
63243808b8 | ||
|
|
f271448767 | ||
|
|
3915164bb4 | ||
|
|
94e5aa172c | ||
|
|
ff68bee174 | ||
|
|
25aa7d5738 | ||
|
|
07e3a218cb | ||
|
|
e6ad00dabc | ||
|
|
23d781e348 | ||
|
|
b53cacd54c | ||
|
|
ade3da4d89 | ||
|
|
1e5f614a5e | ||
|
|
159c8dbd5f | ||
|
|
5faa285f6f | ||
|
|
c9af19cdb2 | ||
|
|
3e74e4784c | ||
|
|
bcbd7f4f6d | ||
|
|
7ead6086dc | ||
|
|
4685568e0d | ||
|
|
ec57f8293a | ||
|
|
9e5344d624 | ||
|
|
4c122fd3b3 | ||
|
|
7959dd9f57 | ||
|
|
fde03c7ae0 | ||
|
|
7fcc770fbf | ||
|
|
dcb09a8b39 | ||
|
|
57c22f4d0f | ||
|
|
ed1cfa5c7b | ||
|
|
3878dbd6bd | ||
|
|
554577fad7 | ||
|
|
3ac99479fe | ||
|
|
7cadb43029 | ||
|
|
f25ac63f18 | ||
|
|
8a79d8d06f | ||
|
|
ddf7fad82f | ||
|
|
6a9d569d5a | ||
|
|
ad017baaa6 | ||
|
|
b5d673c102 | ||
|
|
d7fa707f00 | ||
|
|
b4b7ec1693 | ||
|
|
6f8b7f177d | ||
|
|
7a2a621309 | ||
|
|
fe25bab3a7 | ||
|
|
3abd764512 | ||
|
|
dc255a4ecf | ||
|
|
fc201ae067 | ||
|
|
de5455b401 | ||
|
|
1243207b8f | ||
|
|
39f4a56348 | ||
|
|
d48c9ce562 | ||
|
|
6ced694b53 | ||
|
|
bc128c604b | ||
|
|
a2168870b9 | ||
|
|
c0197b698e | ||
|
|
f2ae99f64e | ||
|
|
14f6667687 | ||
|
|
6f9348540e | ||
|
|
51b667020e | ||
|
|
c6d54d03de | ||
|
|
8e35ef2faa | ||
|
|
b74a2dff9e | ||
|
|
6400bcc81f | ||
|
|
4abe0703d7 | ||
|
|
4b389ddba9 | ||
|
|
1e92aeb7f9 | ||
|
|
b6153edf6b | ||
|
|
93c333a9bc | ||
|
|
43142dde9f | ||
|
|
63bc905061 | ||
|
|
573c6f3df3 | ||
|
|
28692b867c | ||
|
|
74b8f1f240 | ||
|
|
0be74299ca | ||
|
|
866d88de53 | ||
|
|
69acee497d | ||
|
|
6c74672d40 | ||
|
|
6320506c27 | ||
|
|
1ee0f3ce1d | ||
|
|
0730d31c22 | ||
|
|
a2a8b2e9fb | ||
|
|
8821fa9a0e | ||
|
|
363100a612 | ||
|
|
1f7ac5e787 | ||
|
|
2233a0317e | ||
|
|
498ef78e6f | ||
|
|
8a606365c8 | ||
|
|
9c27f94709 | ||
|
|
a99e067b91 | ||
|
|
831f4324ae | ||
|
|
ad205abd90 | ||
|
|
231f4a28eb | ||
|
|
95b52b6aa9 | ||
|
|
385c23a2b6 | ||
|
|
3a81edbcb0 | ||
|
|
2f377089e6 | ||
|
|
6427e84053 | ||
|
|
73b931ed14 |
@@ -89,9 +89,9 @@ default:
|
||||
before_script:
|
||||
# Install & enable Xdebug for code coverage reports
|
||||
- apt-get update
|
||||
- apt-get install -yqq libicu-dev libjpeg-dev libpng-dev libpq-dev libyaml-dev libgmp-dev libzip-dev mariadb-client postgresql-client unzip zip
|
||||
- pecl install xdebug yaml
|
||||
- docker-php-ext-enable xdebug yaml
|
||||
- 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
|
||||
|
||||
|
||||
158
CHANGELOG
158
CHANGELOG
@@ -1,4 +1,160 @@
|
||||
Hubzilla 10.6 (2025-??-??)
|
||||
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()
|
||||
|
||||
@@ -38,7 +38,6 @@ class PermissionRoles {
|
||||
];
|
||||
$ret['limits'] = PermissionLimits::Std_Limits();
|
||||
$ret['limits']['post_comments'] = PERMS_AUTHED;
|
||||
$ret['limits']['post_mail'] = PERMS_AUTHED;
|
||||
$ret['limits']['post_like'] = PERMS_AUTHED;
|
||||
$ret['limits']['chat'] = PERMS_AUTHED;
|
||||
break;
|
||||
|
||||
@@ -212,6 +212,7 @@ class Permissions {
|
||||
* @return array Associative array with
|
||||
* * \e array \b perms Permission array
|
||||
* * \e int \b automatic 0 or 1
|
||||
* * \e srtring \b role
|
||||
*/
|
||||
static public function connect_perms($channel_id) {
|
||||
|
||||
@@ -230,70 +231,6 @@ class Permissions {
|
||||
}
|
||||
}
|
||||
|
||||
// look up the permission role to see if it specified auto-connect
|
||||
// and if there was no permcat or a default permcat, set the perms
|
||||
// from the role
|
||||
/*
|
||||
$role = get_pconfig($channel_id, 'system', 'permissions_role');
|
||||
if ($role) {
|
||||
$xx = PermissionRoles::role_perms($role);
|
||||
if ($xx['perms_auto'])
|
||||
$automatic = 1;
|
||||
|
||||
if ((!$my_perms) && ($xx['perms_connect'])) {
|
||||
$default_perms = $xx['perms_connect'];
|
||||
$my_perms = Permissions::FilledPerms($default_perms);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// If we reached this point without having any permission information,
|
||||
// it is likely a custom permissions role. First see if there are any
|
||||
// automatic permissions.
|
||||
/*
|
||||
if (!$my_perms) {
|
||||
$m = Permissions::FilledAutoperms($channel_id);
|
||||
if ($m) {
|
||||
$automatic = 1;
|
||||
$my_perms = $m;
|
||||
}
|
||||
}
|
||||
*/
|
||||
// If we reached this point with no permissions, the channel is using
|
||||
// custom perms but they are not automatic. They will be stored in abconfig with
|
||||
// the channel's channel_hash (the 'self' connection).
|
||||
|
||||
/*
|
||||
if (!$my_perms) {
|
||||
$r = q("select channel_hash from channel where channel_id = %d",
|
||||
intval($channel_id)
|
||||
);
|
||||
if ($r) {
|
||||
$x = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'my_perms'",
|
||||
intval($channel_id),
|
||||
dbesc($r[0]['channel_hash'])
|
||||
);
|
||||
if ($x) {
|
||||
foreach ($x as $xv) {
|
||||
$my_perms[$xv['k']] = intval($xv['v']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
return (['perms' => $my_perms, 'automatic' => $automatic, 'role' => $pc]);
|
||||
}
|
||||
/*
|
||||
static public function serialise($p) {
|
||||
$n = [];
|
||||
if ($p) {
|
||||
foreach ($p as $k => $v) {
|
||||
if (intval($v)) {
|
||||
$n[] = $k;
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode(',', $n);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\ASCollection;
|
||||
use Zotlabs\Lib\ASCache;
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
class Convo {
|
||||
|
||||
@@ -33,6 +34,7 @@ class Convo {
|
||||
}
|
||||
|
||||
$force = $argv[4] ?? false;
|
||||
$interval = Config::Get('queueworker', 'queue_interval', 500000);
|
||||
|
||||
foreach ($channels as $channel_id) {
|
||||
$channel = channelx_by_n($channel_id);
|
||||
@@ -46,6 +48,8 @@ class Convo {
|
||||
}
|
||||
|
||||
foreach ($messages as $message) {
|
||||
$network_fetch = false;
|
||||
|
||||
if (is_string($message)) {
|
||||
$cached = ASCache::Get($message);
|
||||
if ($cached) {
|
||||
@@ -54,6 +58,8 @@ class Convo {
|
||||
}
|
||||
else {
|
||||
// logger('convo_fetching: ' . $message);
|
||||
$network_fetch = true;
|
||||
|
||||
$data = Activity::fetch($message, $channel);
|
||||
if ($data) {
|
||||
ASCache::Set($message, $data);
|
||||
@@ -65,6 +71,12 @@ class Convo {
|
||||
$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);
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\ObjCache;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
use Zotlabs\Lib\Libzotdir;
|
||||
|
||||
@@ -92,7 +94,7 @@ class Cron {
|
||||
// delete expired access tokens
|
||||
|
||||
$r = q("select atoken_id from atoken where atoken_expires > '%s' and atoken_expires < %s",
|
||||
dbesc(NULL_DATE),
|
||||
dbesc(DBA::$dba->get_null_date()),
|
||||
db_utcnow()
|
||||
);
|
||||
if ($r) {
|
||||
@@ -236,6 +238,73 @@ class Cron {
|
||||
if (!$restart)
|
||||
Master::Summon(array('Cronhooks'));
|
||||
|
||||
|
||||
// move as obj cache to fs
|
||||
if (!Config::Get('system', 'as_objects_moved')) {
|
||||
$results = dbq("select iconfig.*, item.mid from iconfig left join item on iid = item.id where cat = 'activitypub' and k = 'rawmsg' limit 300");
|
||||
if ($results) {
|
||||
foreach ($results as $result) {
|
||||
if (is_string($result['v'])) {
|
||||
if (str_starts_with($result['v'], '{')) {
|
||||
$result['v'] = json_decode($result['v'], true);
|
||||
}
|
||||
|
||||
elseif (str_starts_with($result['v'], 'json:')) {
|
||||
$result['v'] = json_unserialize($result['v']);
|
||||
}
|
||||
|
||||
elseif (preg_match('|^a:[0-9]+:{.*}$|s', $result['v'])) {
|
||||
$result['v'] = unserialize($result['v'], ['allowed_classes' => false]);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($result['v'])) {
|
||||
ObjCache::Set($result['mid'], $result['v']);
|
||||
}
|
||||
|
||||
q("delete from iconfig where id = %d",
|
||||
intval($result['id'])
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Config::Set('system', 'as_objects_moved', 1);
|
||||
}
|
||||
}
|
||||
|
||||
// move diaspora obj cache to fs
|
||||
if (!Config::Get('system', 'diaspora_objects_moved')) {
|
||||
$results = dbq("select iconfig.*, item.mid from iconfig left join item on iid = item.id where cat = 'diaspora' and k = 'fields' limit 300");
|
||||
if ($results) {
|
||||
foreach ($results as $result) {
|
||||
if (is_string($result['v'])) {
|
||||
if (str_starts_with($result['v'], '{')) {
|
||||
$result['v'] = json_decode($result['v'], true);
|
||||
}
|
||||
|
||||
elseif (str_starts_with($result['v'], 'json:')) {
|
||||
$result['v'] = json_unserialize($result['v']);
|
||||
}
|
||||
|
||||
elseif (preg_match('|^a:[0-9]+:{.*}$|s', $result['v'])) {
|
||||
$result['v'] = unserialize($result['v'], ['allowed_classes' => false]);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($result['v'])) {
|
||||
ObjCache::Set($result['mid'], $result['v'], 'diaspora');
|
||||
}
|
||||
|
||||
q("delete from iconfig where id = %d",
|
||||
intval($result['id'])
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Config::Set('system', 'diaspora_objects_moved', 1);
|
||||
}
|
||||
}
|
||||
|
||||
Config::Set('system', 'lastcron', datetime_convert());
|
||||
|
||||
//All done - clear the lockfile
|
||||
|
||||
@@ -74,6 +74,8 @@ class Externals {
|
||||
}
|
||||
}
|
||||
|
||||
$attempts++;
|
||||
|
||||
if (!$url) {
|
||||
continue;
|
||||
}
|
||||
@@ -85,7 +87,6 @@ class Externals {
|
||||
$blacklisted = true;
|
||||
}
|
||||
|
||||
$attempts++;
|
||||
|
||||
// make sure we can eventually break out if somebody blacklists all known sites
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace Zotlabs\Daemon;
|
||||
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\IConfig;
|
||||
use Zotlabs\Lib\ObjCache;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Queue;
|
||||
|
||||
@@ -264,7 +266,6 @@ class Notifier {
|
||||
}
|
||||
|
||||
if (!item_forwardable($target_item)) {
|
||||
//hz_syslog(print_r($target_item,true));
|
||||
logger('notifier: target item not forwardable', LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
@@ -317,10 +318,15 @@ class Notifier {
|
||||
return;
|
||||
}
|
||||
|
||||
$m = get_iconfig($target_item, 'activitypub', 'signed_data');
|
||||
$m = ObjCache::Get($target_item['mid']);
|
||||
|
||||
if (!$m) {
|
||||
$m = IConfig::Get($target_item, 'activitypub', 'rawmsg');
|
||||
}
|
||||
|
||||
// Re-use existing signature unless the activity type changed to a Tombstone, which won't verify.
|
||||
if ($m && (!intval($target_item['item_deleted']))) {
|
||||
self::$encoded_item = json_decode($m, true);
|
||||
self::$encoded_item = $m;
|
||||
}
|
||||
else {
|
||||
$activity = Activity::encode_activity($target_item);
|
||||
|
||||
@@ -9,10 +9,14 @@ class Onedirsync {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if ($argc < 2 || is_int($argv[1]) === false) {
|
||||
logger('onedirsync: no update id');
|
||||
return;
|
||||
}
|
||||
|
||||
logger('onedirsync: start ' . intval($argv[1]));
|
||||
|
||||
if (($argc > 1) && (intval($argv[1])))
|
||||
$update_id = intval($argv[1]);
|
||||
$update_id = intval($argv[1]);
|
||||
|
||||
if (!$update_id) {
|
||||
logger('onedirsync: no update id');
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\ASCollection;
|
||||
@@ -15,10 +16,14 @@ class Onepoll {
|
||||
|
||||
static public function run($argc, $argv) {
|
||||
|
||||
if ($argc < 2 || is_int($argv[1]) === false) {
|
||||
logger('onepoll: no contact');
|
||||
return;
|
||||
}
|
||||
|
||||
logger('onepoll: start');
|
||||
|
||||
if (($argc > 1) && (intval($argv[1])))
|
||||
$contact_id = intval($argv[1]);
|
||||
$contact_id = intval($argv[1]);
|
||||
|
||||
if (!$contact_id) {
|
||||
logger('onepoll: no contact');
|
||||
@@ -34,7 +39,7 @@ class Onepoll {
|
||||
$contacts = q("SELECT abook.*, xchan.* FROM abook
|
||||
LEFT JOIN xchan ON xchan_hash = abook_xchan
|
||||
WHERE abook_id = %d",
|
||||
intval($contact_id)
|
||||
$contact_id
|
||||
);
|
||||
|
||||
if (!$contacts) {
|
||||
@@ -53,7 +58,7 @@ class Onepoll {
|
||||
|
||||
logger("onepoll: poll: ($contact_id) IMPORTER: {$importer['xchan_name']}, CONTACT: {$contact['xchan_name']}");
|
||||
|
||||
$last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= NULL_DATE))
|
||||
$last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= DBA::$dba->get_null_date()))
|
||||
? datetime_convert('UTC', 'UTC', 'now - 7 days')
|
||||
: datetime_convert('UTC', 'UTC', $contact['abook_updated'] . ' - 2 days')
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Daemon;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
class Poller {
|
||||
@@ -117,7 +118,7 @@ class Poller {
|
||||
|
||||
// if we've never connected with them, start the mark for death countdown from now
|
||||
|
||||
if ($c <= NULL_DATE) {
|
||||
if ($c <= DBA::$dba->get_null_date()) {
|
||||
q("update abook set abook_connected = '%s' where abook_id = %d",
|
||||
dbesc(datetime_convert()),
|
||||
intval($contact['abook_id'])
|
||||
@@ -173,7 +174,7 @@ class Poller {
|
||||
|
||||
if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
|
||||
$r = q("SELECT * FROM updates WHERE ud_update = 1 AND (ud_last = '%s' OR ud_last > %s - INTERVAL %s)",
|
||||
dbesc(NULL_DATE),
|
||||
dbesc(DBA::$dba->get_null_date()),
|
||||
db_utcnow(),
|
||||
db_quoteinterval('7 DAY')
|
||||
);
|
||||
@@ -184,7 +185,7 @@ class Poller {
|
||||
// If they didn't respond when we attempted before, back off to once a day
|
||||
// After 7 days we won't bother anymore
|
||||
|
||||
if ($rr['ud_last'] > NULL_DATE)
|
||||
if ($rr['ud_last'] > DBA::$dba->get_null_date())
|
||||
if ($rr['ud_last'] > datetime_convert('UTC', 'UTC', 'now - 1 day'))
|
||||
continue;
|
||||
|
||||
|
||||
@@ -1,15 +1,57 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Hubzilla Community
|
||||
* SPDX-FileContributor: redmatrix
|
||||
* SPDX-FileContributor: Klaus Weidenbach
|
||||
* SPDX-FileContributor: zotlabs
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Extend;
|
||||
|
||||
use App;
|
||||
|
||||
/**
|
||||
* @brief Hook class.
|
||||
* A class for hooking into Hubzilla.
|
||||
*
|
||||
* Hooks are functions that Hubzilla will invoke at certain points in the code
|
||||
* during execution. An addon can register a callback handler that will be
|
||||
* called whenever the specified hook is invoked. A callback handler is a
|
||||
* function that takes a reference to an array containing the callback
|
||||
* arguments as it's only argument.
|
||||
*
|
||||
* @see call_hooks
|
||||
* @see load_hooks
|
||||
*/
|
||||
class Hook {
|
||||
|
||||
/**
|
||||
* Register a callback handler for a hook.
|
||||
*
|
||||
* A callback handler is a function that takes a reference to an array
|
||||
* containing the callback arguments as it's only argument.
|
||||
*
|
||||
* The contents and meaning of the array depends on the hook invoked. By
|
||||
* modifying the contents of the array the hook can pass data back to the
|
||||
* caller.
|
||||
*
|
||||
* Once the `Hook::register` function has been called, the callback may be
|
||||
* invoked.
|
||||
*
|
||||
* @param string $hook The name of the hook to register a handler for.
|
||||
* @param string $file The source file of the callback handler.
|
||||
* @param string|array $function
|
||||
* The function name of the callback handler, as a
|
||||
* string or an array.
|
||||
* @param int $version Hook interface version, allways 1.
|
||||
* @param int $priority The priority of the callback handler, higher
|
||||
* numbers takes precedence.
|
||||
*
|
||||
* @return true if the handler was already registered, otherwise the result
|
||||
* from inserting the hook in the database.
|
||||
*/
|
||||
static public function register($hook,$file,$function,$version = 1,$priority = 0) {
|
||||
if(is_array($function)) {
|
||||
$function = serialize($function);
|
||||
@@ -45,6 +87,14 @@ class Hook {
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an array of hook callback handlers.
|
||||
*
|
||||
* All of the handlers must be in the same source file.
|
||||
*
|
||||
* @param string $file The source file of the callback handlers.
|
||||
* @param array $arr An array of `hookname => functionname` pairs.
|
||||
*/
|
||||
static public function register_array($file,$arr) {
|
||||
if($arr) {
|
||||
foreach($arr as $k => $v) {
|
||||
@@ -54,6 +104,20 @@ class Hook {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unregister a hook callback handler.
|
||||
*
|
||||
* @param string $hook The name of the hook to register a callback handler for.
|
||||
* @param string $file The source file of the hook callback handler.
|
||||
* @param string|array $function
|
||||
* The function name of the callback handler, as a
|
||||
* string or an array.
|
||||
* @param int $version Hook interface version, allways 1.
|
||||
* @param int $priority The priority of the callback handler, higher
|
||||
* numbers takes precedence.
|
||||
*
|
||||
* @return The result of the database delete operation.
|
||||
*/
|
||||
static public function unregister($hook,$file,$function,$version = 1,$priority = 0) {
|
||||
if(is_array($function)) {
|
||||
$function = serialize($function);
|
||||
@@ -70,11 +134,13 @@ class Hook {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister all hooks with this file component.
|
||||
* Unregister all hooks handlers from a given source file.
|
||||
*
|
||||
* Useful for addon upgrades where you want to clean out old interfaces.
|
||||
*
|
||||
* @param string $file
|
||||
* @param string $file The source file where the hook handlers were defined.
|
||||
*
|
||||
* @return The result from the database delete operation.
|
||||
*/
|
||||
static public function unregister_by_file($file) {
|
||||
$r = q("DELETE FROM hook WHERE file = '%s' ",
|
||||
@@ -85,31 +151,22 @@ class Hook {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts a hook into a page request.
|
||||
* Inserts a hook into a page request.
|
||||
*
|
||||
* Insert a short-lived hook into the running page request.
|
||||
* Hooks are normally persistent so that they can be called
|
||||
* across asynchronous processes such as delivery and poll
|
||||
* processes.
|
||||
* Insert a short-lived hook into the running page request. Hooks are
|
||||
* normally persistent so that they can be called across asynchronous
|
||||
* processes such as delivery and poll processes.
|
||||
*
|
||||
* insert_hook lets you attach a hook callback immediately
|
||||
* which will not persist beyond the life of this page request
|
||||
* or the current process.
|
||||
* This function lets you attach a hook callback immediately which will not
|
||||
* persist beyond the life of this page request or the current process.
|
||||
*
|
||||
* @param string $hook
|
||||
* name of hook to attach callback
|
||||
* @param string $fn
|
||||
* function name of callback handler
|
||||
* @param int $version
|
||||
* hook interface version, 0 uses two callback params, 1 uses one callback param
|
||||
* @param int $priority
|
||||
* currently not implemented in this function, would require the hook array to be resorted
|
||||
* @param string $hook Name of hook to attach callback.
|
||||
* @param string|array $fn Name of callback handler as a string or array.
|
||||
* @param int $version Hook interface version, allways 1.
|
||||
* @param int $priority Currently not implemented in this function,
|
||||
* would require the hook array to be resorted.
|
||||
*/
|
||||
static public function insert($hook, $fn, $version = 0, $priority = 0) {
|
||||
if(is_array($fn)) {
|
||||
$fn = serialize($fn);
|
||||
}
|
||||
|
||||
if(! is_array(App::$hooks))
|
||||
App::$hooks = array();
|
||||
|
||||
@@ -119,4 +176,4 @@ class Hook {
|
||||
App::$hooks[$hook][] = array('', $fn, $priority, $version);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,106 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2018 The Hubzilla Community
|
||||
* SPDX-FileContributor: Zotlabs
|
||||
* SPDX-FileContributor: Mario <mario@mariovavti.com>
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Extend;
|
||||
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
/**
|
||||
* Class for managing routes.
|
||||
*
|
||||
* Routes connect a URL path to a module that will handle requests to that
|
||||
* path.
|
||||
*
|
||||
* For example by registering a route like this:
|
||||
*
|
||||
* ```php
|
||||
* Route::register(
|
||||
* __DIR__ . '/Mod_Myroute.php',
|
||||
* 'myroute'
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* Hubzilla will direct requests to the '/myroute' URL path to the 'Myroute'
|
||||
* controller located in the '/Mod_Myroute.php' file in the same directory as
|
||||
* the file this code was called from.
|
||||
*
|
||||
* Routes are stored persistently, so this function will typically be called from
|
||||
* the `<addon>_load()` function if called from an addon. Accordingly, the route must
|
||||
* be unregistered when no longer needed, like this:
|
||||
*
|
||||
* ```php
|
||||
* Route::unregister(
|
||||
* __DIR__ . '/Mod_Myroute.php',
|
||||
* 'myroute'
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* This will typically be called from the `<addon>_unload()` function in an addon.
|
||||
*/
|
||||
class Route {
|
||||
|
||||
static function register($file,$modname) {
|
||||
/**
|
||||
* Register a new route.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* Route::register(
|
||||
* __DIR__ . '/Mod_Myroute.php',
|
||||
* 'myroute'
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* The route is stored persistently, and must be unregistered when no longer needed.
|
||||
*
|
||||
* @param string $file The file containing the controller for handling requests to this route.
|
||||
* @param string $modname The name of the module (URL path).
|
||||
*
|
||||
* @see {@link Zotlabs::Extend::Route.unregister() unregister()}
|
||||
* @see {@link Zotlabs::Extend::Route.unregister_by_file() unregister_by_file()}
|
||||
*/
|
||||
public static function register(string $file, string $modname): void {
|
||||
$rt = self::get();
|
||||
|
||||
foreach ($rt as $r) {
|
||||
if ($r[0] === $file && $r[1] === $modname) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$rt[] = [ $file, $modname ];
|
||||
self::set($rt);
|
||||
}
|
||||
|
||||
static function unregister($file,$modname) {
|
||||
/**
|
||||
* Unregister a route.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* Route::unregister(
|
||||
* __DIR__ . '/Mod_Myroute.php',
|
||||
* 'myroute'
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* @param string $file The file containing the controller for handling requests to this route.
|
||||
* @param string $modname The name of the module (URL path).
|
||||
*
|
||||
* @see {@link Zotlabs::Extend::Route.register() register()}
|
||||
* @see {@link Zotlabs::Extend::Route.unregister_by_file() unregister_by_file()}
|
||||
*/
|
||||
public static function unregister(string $file, string $modname): void {
|
||||
$rt = self::get();
|
||||
if($rt) {
|
||||
$n = [];
|
||||
foreach($rt as $r) {
|
||||
if($r[0] !== $file && $r[1] !== $modname) {
|
||||
if(!($r[0] === $file && $r[1] === $modname)) {
|
||||
$n[] = $r;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +108,23 @@ class Route {
|
||||
}
|
||||
}
|
||||
|
||||
static function unregister_by_file($file) {
|
||||
/**
|
||||
* Unregister all routes by source file.
|
||||
*
|
||||
* Removes all persistently stored routes with hanclers in the
|
||||
* given source file.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* Route::unregister_by_file(__DIR__ . '/Mod_Myroute.php');
|
||||
* ```
|
||||
*
|
||||
* @param string $file The file containing the controllers to remove.
|
||||
*
|
||||
* @see {@link Zotlabs::Extend::Route.register() register()}
|
||||
* @see {@link Zotlabs::Extend::Route.unregister() unregister()}
|
||||
*/
|
||||
public static function unregister_by_file(string $file): void {
|
||||
$rt = self::get();
|
||||
if($rt) {
|
||||
$n = [];
|
||||
@@ -38,11 +137,18 @@ class Route {
|
||||
}
|
||||
}
|
||||
|
||||
static function get() {
|
||||
/**
|
||||
* Get an array of all defined routes.
|
||||
*
|
||||
* @return An array of routes, where each entry is an array
|
||||
* containing two elements, the file, and the module
|
||||
* name.
|
||||
*/
|
||||
public static function get(): array {
|
||||
return Config::Get('system','routes',[]);
|
||||
}
|
||||
|
||||
static function set($r) {
|
||||
private static function set(array $r): mixed {
|
||||
return Config::Set('system','routes',$r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,13 @@ class Widget {
|
||||
|
||||
static function register($file,$widget) {
|
||||
$rt = self::get();
|
||||
|
||||
foreach ($rt as $r) {
|
||||
if ($r[0] === $file && $r[1] === $widget) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$rt[] = [ $file, $widget ];
|
||||
self::set($rt);
|
||||
}
|
||||
@@ -17,7 +24,7 @@ class Widget {
|
||||
if($rt) {
|
||||
$n = [];
|
||||
foreach($rt as $r) {
|
||||
if($r[0] !== $file && $r[1] !== $widget) {
|
||||
if(!($r[0] === $file && $r[1] === $widget)) {
|
||||
$n[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class ASCache {
|
||||
$ret = Cache::get($key, self::getAge());
|
||||
|
||||
if ($ret) {
|
||||
return unserialise($ret);
|
||||
return json_unserialize($ret);
|
||||
}
|
||||
|
||||
return [];
|
||||
@@ -42,7 +42,7 @@ class ASCache {
|
||||
return;
|
||||
}
|
||||
|
||||
Cache::set($key, serialise($obj));
|
||||
Cache::set($key, json_serialize($obj));
|
||||
}
|
||||
|
||||
public static function isCacheable(array $obj): bool
|
||||
|
||||
@@ -95,6 +95,8 @@ class ASCollection {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = null;
|
||||
|
||||
if (is_array($this->nextpage)) {
|
||||
$data = $this->nextpage;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Access\PermissionLimits;
|
||||
use Zotlabs\Access\PermissionRoles;
|
||||
use Zotlabs\Access\Permissions;
|
||||
@@ -91,6 +92,8 @@ class Activity {
|
||||
|
||||
logger('fetch: ' . $url, LOGGER_DEBUG);
|
||||
|
||||
$start_timestamp = microtime(true);
|
||||
|
||||
if (strpos($url, 'x-zot:') === 0) {
|
||||
$x = ZotURL::fetch($url, $channel);
|
||||
}
|
||||
@@ -129,7 +132,6 @@ class Activity {
|
||||
}
|
||||
|
||||
$h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel), false);
|
||||
$start_timestamp = microtime(true);
|
||||
$x = z_fetch_url($url, true, $redirects, ['headers' => $h]);
|
||||
}
|
||||
|
||||
@@ -382,7 +384,12 @@ class Activity {
|
||||
if ($items) {
|
||||
$x = [];
|
||||
foreach ($items as $i) {
|
||||
$m = IConfig::Get($i['id'], 'activitypub', 'rawmsg');
|
||||
$m = ObjCache::Get($i['mid']);
|
||||
|
||||
if (!$m) {
|
||||
$m = IConfig::Get($i['id'], 'activitypub', 'rawmsg');
|
||||
}
|
||||
|
||||
if ($m) {
|
||||
if (is_string($m))
|
||||
$t = json_decode($m, true);
|
||||
@@ -501,8 +508,8 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
$ret['id'] = ((strpos($i['mid'], 'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid']));
|
||||
$ret['diaspora:guid'] = $i['uuid'];
|
||||
$ret['id'] = ((strpos($i['mid'], 'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid']));
|
||||
$ret['uuid'] = $i['uuid'];
|
||||
|
||||
$images = [];
|
||||
$audios = [];
|
||||
@@ -562,7 +569,7 @@ class Activity {
|
||||
$ret['published'] = datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME);
|
||||
if ($i['created'] !== $i['edited'])
|
||||
$ret['updated'] = datetime_convert('UTC', 'UTC', $i['edited'], ATOM_TIME);
|
||||
if ($i['expires'] > NULL_DATE) {
|
||||
if ($i['expires'] > DBA::$dba->get_null_date()) {
|
||||
$ret['expires'] = datetime_convert('UTC', 'UTC', $i['expires'], ATOM_TIME);
|
||||
}
|
||||
|
||||
@@ -587,7 +594,7 @@ class Activity {
|
||||
|
||||
$ret['commentPolicy'] = (($i['item_wall']) ? map_scope(PermissionLimits::Get($i['uid'], 'post_comments')) : '');
|
||||
|
||||
if (array_key_exists('comments_closed', $i) && $i['comments_closed'] !== EMPTY_STR && $i['comments_closed'] > NULL_DATE) {
|
||||
if (array_key_exists('comments_closed', $i) && $i['comments_closed'] !== EMPTY_STR && $i['comments_closed'] > DBA::$dba->get_null_date()) {
|
||||
if ($ret['commentPolicy']) {
|
||||
$ret['commentPolicy'] .= ' ';
|
||||
}
|
||||
@@ -637,14 +644,17 @@ 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);
|
||||
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 (preg_match("/link='(.*?)'/ism", $share[1], $match)) {
|
||||
// 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;
|
||||
|
||||
@@ -664,6 +674,7 @@ class Activity {
|
||||
|
||||
if ($quote_urls) {
|
||||
$ret['quoteUrl'] = $quote_urls[0];
|
||||
$ret['quoteUri'] = $quote_urls[0];
|
||||
|
||||
if (empty($ret['tag'])) {
|
||||
$ret['tag'] = $obj_links;
|
||||
@@ -827,8 +838,7 @@ class Activity {
|
||||
if ($iconfig && array_key_exists('iconfig', $item) && is_array($item['iconfig'])) {
|
||||
foreach ($item['iconfig'] as $att) {
|
||||
if ($att['sharing']) {
|
||||
$value = ((is_string($att['v']) && preg_match('|^a:[0-9]+:{.*}$|s', $att['v'])) ? unserialize($att['v']) : $att['v']);
|
||||
$ret[] = ['type' => 'PropertyValue', 'name' => 'zot.' . $att['cat'] . '.' . $att['k'], 'value' => $value];
|
||||
$ret[] = ['type' => 'PropertyValue', 'name' => 'zot.' . $att['cat'] . '.' . $att['k'], 'value' => $att['v']];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -849,6 +859,10 @@ class Activity {
|
||||
$entry = [];
|
||||
if (isset($att['type']) && $att['type'] === 'PropertyValue') {
|
||||
if (isset($att['name'])) {
|
||||
if (in_array($att['name'], ['zot.activitypub.rawmsg', 'zot.diaspora.fields'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = explode('.', $att['name']);
|
||||
if (count($key) === 3 && $key[0] === 'zot') {
|
||||
$entry['cat'] = $key[1];
|
||||
@@ -995,7 +1009,7 @@ class Activity {
|
||||
$ret['id'] = ((strpos($i['mid'], 'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid']));
|
||||
}
|
||||
|
||||
$ret['diaspora:guid'] = $i['uuid'];
|
||||
$ret['uuid'] = $i['uuid'];
|
||||
|
||||
if (!empty($i['title']))
|
||||
$ret['name'] = html2plain(bbcode($i['title']));
|
||||
@@ -1112,6 +1126,7 @@ class Activity {
|
||||
return [];
|
||||
}
|
||||
|
||||
/* Those should not be required in activities anymore after version 11
|
||||
$t = self::encode_taxonomy($i);
|
||||
if ($t) {
|
||||
$ret['tag'] = $t;
|
||||
@@ -1121,6 +1136,7 @@ class Activity {
|
||||
if ($a) {
|
||||
$ret['attachment'] = $a;
|
||||
}
|
||||
*/
|
||||
|
||||
if (intval($i['item_private']) === 0) {
|
||||
$ret['to'] = [ACTIVITY_PUBLIC_INBOX];
|
||||
@@ -1549,7 +1565,7 @@ class Activity {
|
||||
'abook_created' => datetime_convert(),
|
||||
'abook_updated' => datetime_convert(),
|
||||
'abook_connected' => datetime_convert(),
|
||||
'abook_dob' => NULL_DATE,
|
||||
'abook_dob' => DBA::$dba->get_null_date(),
|
||||
'abook_pending' => intval(($automatic) ? 0 : 1),
|
||||
'abook_instance' => z_root()
|
||||
]
|
||||
@@ -1948,7 +1964,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 . '}';
|
||||
|
||||
@@ -2001,6 +2017,8 @@ class Activity {
|
||||
$multi = true;
|
||||
}
|
||||
|
||||
$answer_found = false;
|
||||
|
||||
if ($response) {
|
||||
$mid = $response['mid'];
|
||||
$content = trim($response['title']);
|
||||
@@ -2030,7 +2048,6 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
$answer_found = false;
|
||||
$foundPrevious = false;
|
||||
if ($multi) {
|
||||
for ($c = 0; $c < count($o['anyOf']); $c++) {
|
||||
@@ -2072,7 +2089,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($pollItem['comments_closed'] > NULL_DATE) {
|
||||
if ($pollItem['comments_closed'] > DBA::$dba->get_null_date()) {
|
||||
if ($pollItem['comments_closed'] > datetime_convert()) {
|
||||
$o['closed'] = datetime_convert('UTC', 'UTC', $pollItem['comments_closed'], ATOM_TIME);
|
||||
// set this to force an update
|
||||
@@ -2124,15 +2141,16 @@ class Activity {
|
||||
}
|
||||
|
||||
static function decode_note($act) {
|
||||
|
||||
$response_activity = false;
|
||||
$s = [];
|
||||
|
||||
$obj_type = is_array($act->objprop('type')) ? $act->objprop('type')[0] : $act->objprop('type');
|
||||
|
||||
// These activities should have been handled separately in the Inbox module and should not be turned into posts
|
||||
|
||||
if (
|
||||
in_array($act->type, ['Follow', 'Accept', 'Reject', 'Create', 'Update']) &&
|
||||
($act->objprop('type') === 'Follow' || ActivityStreams::is_an_actor($act->objprop('type')))
|
||||
($obj_type === 'Follow' || ActivityStreams::is_an_actor($obj_type))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -2205,7 +2223,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($act->type, ['Invite', 'Create']) && $act->objprop('type') === 'Event') {
|
||||
if (in_array($act->type, ['Invite', 'Create']) && $obj_type === 'Event') {
|
||||
$s['mid'] = $s['parent_mid'] = $act->id;
|
||||
}
|
||||
|
||||
@@ -2237,25 +2255,25 @@ class Activity {
|
||||
$mention = self::get_actor_bbmention($obj_actor['id']);
|
||||
|
||||
if ($act->type === 'Like') {
|
||||
$content['content'] = sprintf(t('Likes %1$s\'s %2$s'), $mention, $act->obj['type']) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('Likes %1$s\'s %2$s'), $mention, $act->obj['type']);
|
||||
}
|
||||
if ($act->type === 'Dislike') {
|
||||
$content['content'] = sprintf(t('Doesn\'t like %1$s\'s %2$s'), $mention, $act->obj['type']) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('Doesn\'t like %1$s\'s %2$s'), $mention, $act->obj['type']);
|
||||
}
|
||||
|
||||
// handle event RSVPs
|
||||
if (($act->objprop('type') === 'Event') || ($act->objprop('type') === 'Invite' && array_path_exists('object/type', $act->obj) && $act->obj['object']['type'] === 'Event')) {
|
||||
if (in_array($obj_type, ['Event', 'Invite'])) {
|
||||
if ($act->type === 'Accept') {
|
||||
$content['content'] = sprintf(t('Will attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('Will attend %s\'s event'), $mention);
|
||||
}
|
||||
if ($act->type === 'Reject') {
|
||||
$content['content'] = sprintf(t('Will not attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('Will not attend %s\'s event'), $mention);
|
||||
}
|
||||
if ($act->type === 'TentativeAccept') {
|
||||
$content['content'] = sprintf(t('May attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('May attend %s\'s event'), $mention);
|
||||
}
|
||||
if ($act->type === 'TentativeReject') {
|
||||
$content['content'] = sprintf(t('May not attend %s\'s event'), $mention) . EOL . EOL . ($content['content'] ?? '');
|
||||
$content['content'] = sprintf(t('May not attend %s\'s event'), $mention);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2289,7 +2307,7 @@ class Activity {
|
||||
if ($s['mid'] === $s['parent_mid']) {
|
||||
$s['item_thread_top'] = 1;
|
||||
$s['item_nocomment'] = 0;
|
||||
$s['comments_closed'] = NULL_DATE;
|
||||
$s['comments_closed'] = DBA::$dba->get_null_date();
|
||||
|
||||
// it is a parent node - decode the comment policy info if present
|
||||
if ($act->objprop('commentPolicy')) {
|
||||
@@ -2345,13 +2363,19 @@ class Activity {
|
||||
// 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'], $quote);
|
||||
$s['body'] = self::pasteQuote($s['body'] ?? EMPTY_STR, $quote);
|
||||
$s['term'] = $quote['term'];
|
||||
}
|
||||
}
|
||||
@@ -2359,7 +2383,7 @@ class Activity {
|
||||
$s['verb'] = self::activity_mapper($act->type);
|
||||
|
||||
// Mastodon does not provide update timestamps when updating poll tallies which means race conditions may occur here.
|
||||
if ($act->type === 'Update' && $act->objprop('type') === 'Question' && $s['edited'] === $s['created']) {
|
||||
if ($act->type === 'Update' && $obj_type === 'Question' && $s['edited'] === $s['created']) {
|
||||
$s['edited'] = datetime_convert();
|
||||
}
|
||||
|
||||
@@ -2367,8 +2391,8 @@ class Activity {
|
||||
$s['item_deleted'] = 1;
|
||||
}
|
||||
|
||||
if ($act->objprop('type')) {
|
||||
$s['obj_type'] = self::activity_obj_mapper($act->obj['type']);
|
||||
if ($obj_type) {
|
||||
$s['obj_type'] = self::activity_obj_mapper($obj_type);
|
||||
}
|
||||
|
||||
$s['obj'] = $act->obj;
|
||||
@@ -2429,7 +2453,7 @@ class Activity {
|
||||
$s = self::bb_attach($s);
|
||||
}
|
||||
|
||||
if ($act->objprop('type') === 'Question' && in_array($act->type, ['Create', 'Update'])) {
|
||||
if ($obj_type === 'Question' && in_array($act->type, ['Create', 'Update'])) {
|
||||
if ($act->objprop('endTime')) {
|
||||
$s['comments_closed'] = datetime_convert('UTC', 'UTC', $act->obj['endTime']);
|
||||
}
|
||||
@@ -2441,7 +2465,7 @@ class Activity {
|
||||
|
||||
if (!$response_activity) {
|
||||
|
||||
if ($act->objprop('type') === 'Profile') {
|
||||
if ($obj_type === 'Profile') {
|
||||
$s['parent_mid'] = $s['mid'];
|
||||
$s['item_thread_top'] = 1;
|
||||
}
|
||||
@@ -2451,7 +2475,7 @@ class Activity {
|
||||
// right now just link to the largest mp4 we find that will fit in our
|
||||
// standard content region
|
||||
|
||||
if ($act->objprop('type') === 'Video') {
|
||||
if ($obj_type === 'Video') {
|
||||
|
||||
$vtypes = [
|
||||
'video/mp4',
|
||||
@@ -2533,7 +2557,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if ($act->objprop('type') === 'Audio') {
|
||||
if ($obj_type === 'Audio') {
|
||||
|
||||
$atypes = [
|
||||
'audio/mpeg',
|
||||
@@ -2565,7 +2589,7 @@ class Activity {
|
||||
|
||||
}
|
||||
|
||||
if ($act->objprop('type') === 'Image' && strpos($s['body'], 'zrl=') === false) {
|
||||
if ($obj_type === 'Image' && strpos($s['body'], 'zrl=') === false) {
|
||||
|
||||
$ptr = null;
|
||||
|
||||
@@ -2580,10 +2604,10 @@ class Activity {
|
||||
foreach ($ptr as $vurl) {
|
||||
if (strpos($s['body'], $vurl['href']) === false) {
|
||||
$bb_imgs = '[zmg]' . $vurl['href'] . '[/zmg]' . "\r\n";
|
||||
$s['body'] = $bb_imgs . $s['body'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$s['body'] = $bb_imgs . $s['body'];
|
||||
}
|
||||
elseif (is_string($act->obj['url'])) {
|
||||
if (strpos($s['body'], $act->obj['url']) === false) {
|
||||
@@ -2593,7 +2617,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if ($act->objprop('type') === 'Page' && !$s['body']) {
|
||||
if ($obj_type === 'Page' && !$s['body']) {
|
||||
|
||||
$ptr = null;
|
||||
$purl = EMPTY_STR;
|
||||
@@ -2633,7 +2657,7 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($act->objprop('type'), ['Note', 'Article', 'Page', 'Question'])) {
|
||||
if (in_array($obj_type, ['Note', 'Article', 'Page', 'Question'])) {
|
||||
$ptr = null;
|
||||
|
||||
if (array_key_exists('url', $act->obj)) {
|
||||
@@ -2672,55 +2696,11 @@ class Activity {
|
||||
$s['item_private'] = 2;
|
||||
}
|
||||
|
||||
$ap_rawmsg = '';
|
||||
$diaspora_rawmsg = '';
|
||||
$raw_arr = [];
|
||||
|
||||
$raw_arr = json_decode($act->raw, true);
|
||||
|
||||
// This is a zot6 packet and the raw activitypub or diaspora message json
|
||||
// is possibly available in the attachement.
|
||||
if (array_key_exists('signed', $raw_arr) && isset($act->data['attachment']) && is_array($act->data['attachment'])) {
|
||||
foreach($act->data['attachment'] as $a) {
|
||||
if (
|
||||
isset($a['type']) && $a['type'] === 'PropertyValue' &&
|
||||
isset($a['name']) && $a['name'] === 'zot.activitypub.rawmsg' &&
|
||||
isset($a['value'])
|
||||
) {
|
||||
$ap_rawmsg = $a['value'];
|
||||
}
|
||||
if (
|
||||
isset($a['type']) && $a['type'] === 'PropertyValue' &&
|
||||
isset($a['name']) && $a['name'] === 'zot.diaspora.fields' &&
|
||||
isset($a['value'])
|
||||
) {
|
||||
$diaspora_rawmsg = $a['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!$ap_rawmsg && array_key_exists('signed', $raw_arr)) {
|
||||
// zap
|
||||
$ap_rawmsg = json_encode($act->data, JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
if ($ap_rawmsg) {
|
||||
IConfig::Set($s, 'activitypub', 'rawmsg', $ap_rawmsg, 1);
|
||||
}
|
||||
elseif (!array_key_exists('signed', $raw_arr)) {
|
||||
IConfig::Set($s, 'activitypub', 'rawmsg', $act->raw, 1);
|
||||
}
|
||||
|
||||
if ($diaspora_rawmsg) {
|
||||
IConfig::Set($s, 'diaspora', 'fields', $diaspora_rawmsg, 1);
|
||||
}
|
||||
|
||||
if ($act->raw_recips) {
|
||||
IConfig::Set($s, 'activitypub', 'recips', $act->raw_recips);
|
||||
}
|
||||
|
||||
if ($act->objprop('type') === 'Event' && $act->objprop('timezone')) {
|
||||
if ($obj_type === 'Event' && $act->objprop('timezone')) {
|
||||
IConfig::Set($s, 'event', 'timezone', $act->objprop('timezone'), true);
|
||||
}
|
||||
|
||||
@@ -2886,15 +2866,11 @@ class Activity {
|
||||
}
|
||||
|
||||
if (tgroup_check($channel['channel_id'], $item) && (!$is_child_node)) {
|
||||
// for forum deliveries, make sure we keep a copy of the signed original
|
||||
IConfig::Set($item, 'activitypub', 'rawmsg', $act->raw, 1);
|
||||
$allowed = true;
|
||||
}
|
||||
|
||||
if (intval($item['item_private']) === 2) {
|
||||
if (perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail')) {
|
||||
$allowed = true;
|
||||
}
|
||||
$allowed = perm_is_allowed($channel['channel_id'], $observer_hash, 'post_mail');
|
||||
}
|
||||
|
||||
if ($is_sys_channel) {
|
||||
@@ -2961,12 +2937,18 @@ class Activity {
|
||||
return;
|
||||
|
||||
if ($is_sys_channel) {
|
||||
$incl = Config::Get('system','pubstream_incl');
|
||||
$excl = Config::Get('system','pubstream_excl');
|
||||
$incl = Config::Get('system', 'pubstream_incl', '');
|
||||
$excl = Config::Get('system', 'pubstream_excl', '');
|
||||
|
||||
if(($incl || $excl) && !MessageFilter::evaluate($item, $incl, $excl)) {
|
||||
logger('post is filtered');
|
||||
return;
|
||||
if ($incl || $excl) {
|
||||
$plaintext = prepare_text($item['body'], ((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
|
||||
$plaintext = html2plain((isset($item['summary']) && $item['summary']) ? $item['summary'] . ' ' . $plaintext : $plaintext);
|
||||
$plaintext = html2plain((isset($item['title']) && $item['title']) ? $item['title'] . ' ' . $plaintext : $plaintext);
|
||||
|
||||
if (!(new MessageFilter($item, html_entity_decode($incl), html_entity_decode($excl), ['plaintext' => $plaintext]))->evaluate()) {
|
||||
logger('post is filtered');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3065,13 +3047,22 @@ class Activity {
|
||||
// TODO: not implemented
|
||||
// self::rewrite_mentions($item);
|
||||
|
||||
$r = q("select id, created, edited from item where mid = '%s' and uid = %d limit 1",
|
||||
if (!ObjCache::Get($item['mid'])) {
|
||||
ObjCache::Set($item['mid'], $act->data);
|
||||
}
|
||||
|
||||
$r = q("select id, created, edited, owner_xchan, author_xchan from item where mid = '%s' and uid = %d limit 1",
|
||||
dbesc($item['mid']),
|
||||
intval($item['uid'])
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
if ($item['edited'] > $r[0]['edited']) {
|
||||
// Only update the object cache if there is no owner/author mismatch.
|
||||
if ($r[0]['owner_xchan'] === $item['owner_xchan'] && $r[0]['author_xchan'] === $item['author_xchan']) {
|
||||
ObjCache::Set($item['mid'], $act->data);
|
||||
}
|
||||
|
||||
$item['id'] = $r[0]['id'];
|
||||
$x = item_store_update($item, deliver: false);
|
||||
}
|
||||
@@ -3106,13 +3097,20 @@ class Activity {
|
||||
|
||||
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 = isset($act->obj['replies']['id']) && !empty($item['item_fetched']) && !in_array($channel['channel_id'], App::$cache['as_fetch_collection'][$act->obj['replies']['id']]['channels'] ?? []);
|
||||
|
||||
$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'][$act->obj['replies']['id']]['channels'][] = $channel['channel_id'];
|
||||
App::$cache['as_fetch_collection'][$act->obj['replies']['id']]['force'] = intval($force);
|
||||
App::$cache['as_fetch_collection'][$replies_id]['channels'][] = $channel['channel_id'];
|
||||
App::$cache['as_fetch_collection'][$replies_id]['force'] = intval($force);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3332,6 +3330,12 @@ class Activity {
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: it appears sometimes $s is an array (needs invetigation)
|
||||
if (!is_string($s)) {
|
||||
btlogger('Not a string: ' . print_r($s, true));
|
||||
return true;
|
||||
}
|
||||
|
||||
$s_alt = htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
if (
|
||||
@@ -3634,7 +3638,7 @@ class Activity {
|
||||
$cached = ASCache::Get($url);
|
||||
if ($cached) {
|
||||
// logger('cached: ' . $url);
|
||||
$a = unserialise($cached);
|
||||
$a = $cached;
|
||||
}
|
||||
else {
|
||||
// logger('fetching: ' . $url);
|
||||
@@ -3644,7 +3648,6 @@ class Activity {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($a) {
|
||||
$act = new ActivityStreams($a);
|
||||
|
||||
@@ -3744,12 +3747,11 @@ class Activity {
|
||||
|
||||
'conversation' => 'ostatus:conversation',
|
||||
|
||||
'guid' => 'diaspora:guid',
|
||||
|
||||
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
|
||||
'Hashtag' => 'as:Hashtag',
|
||||
|
||||
'quoteUrl' => 'as:quoteUrl',
|
||||
'quoteUri' => 'http://fedibird.com/ns#quoteUri'
|
||||
];
|
||||
|
||||
}
|
||||
@@ -3788,6 +3790,9 @@ 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'])) {
|
||||
foreach (App::$cache['zot_fetch_objects'] as $mid => $info) {
|
||||
$force = $info['force'];
|
||||
@@ -3801,6 +3806,10 @@ class Activity {
|
||||
}
|
||||
|
||||
Master::Summon(['Zotconvo', $channels_str, $mid, $force]);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3822,6 +3831,10 @@ class Activity {
|
||||
}
|
||||
|
||||
Master::Summon(['Fetchparents', $channels_str, $observer_hash, $mid, $force]);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3838,6 +3851,10 @@ class Activity {
|
||||
}
|
||||
|
||||
Master::Summon(['Convo', $channels_str, $observer_hash, $mid, $force]);
|
||||
|
||||
if ($interval) {
|
||||
usleep($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -553,6 +553,7 @@ class ActivityStreams {
|
||||
}
|
||||
|
||||
$url = unparse_url($parseUrl);
|
||||
$this->signer = ['id' => $url];
|
||||
|
||||
$hublocs = Activity::get_actor_hublocs($url);
|
||||
|
||||
|
||||
@@ -1028,12 +1028,7 @@ class Apps {
|
||||
if(! $syslist)
|
||||
return;
|
||||
|
||||
foreach($syslist as $k => $li) {
|
||||
if($li['guid'] === $guid) {
|
||||
$position = $k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$position = array_find_key($syslist, fn ($v) => $v['guid'] === $guid);
|
||||
if(! $position)
|
||||
return;
|
||||
|
||||
@@ -1082,12 +1077,7 @@ class Apps {
|
||||
if(! $syslist)
|
||||
return;
|
||||
|
||||
foreach($syslist as $k => $li) {
|
||||
if($li['guid'] === $guid) {
|
||||
$position = $k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$position = array_find_key($syslist, fn ($v) => $v['guid'] === $guid);
|
||||
if($position >= count($syslist) - 1)
|
||||
return;
|
||||
|
||||
|
||||
@@ -132,8 +132,8 @@ class Config {
|
||||
$value = App::$config[$family][$key];
|
||||
|
||||
if (! is_array($value)) {
|
||||
if (substr($value, 0, 5) == 'json:') {
|
||||
return json_decode(substr($value, 5), true);
|
||||
if (str_starts_with($value, 'json:')) {
|
||||
return json_unserialize($value);
|
||||
} else if (preg_match('|^a:[0-9]+:{.*}$|s', $value)) {
|
||||
// Unserialize in inherently unsafe. Try to mitigate by not
|
||||
// allowing unserializing objects. Only kept for backwards
|
||||
|
||||
43
Zotlabs/Lib/DbStats.php
Normal file
43
Zotlabs/Lib/DbStats.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use DBA;
|
||||
|
||||
/**
|
||||
* Abstract class to obtain statistics from the database.
|
||||
*
|
||||
* This class should not be instantiated on it's own, but you can get
|
||||
* a concrete class for the configured database type of this site by
|
||||
* calling the `DbStats::getStats()` function.
|
||||
*/
|
||||
abstract class DbStats {
|
||||
|
||||
/**
|
||||
* Get an object for getting statistics from the database.
|
||||
*
|
||||
* @return DbStats The concrete class for obtaining the statistics from
|
||||
* this instances database.
|
||||
*/
|
||||
public static function getStats(): DbStats {
|
||||
return DBA::$dba->is_postgres()
|
||||
? new PostgresDbStats()
|
||||
: new MySQLDbStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of queries recorded by the database.
|
||||
*
|
||||
* @return int Number of queries.
|
||||
*/
|
||||
public abstract function getQueries(): int;
|
||||
|
||||
// Prevent instantiation of this class
|
||||
private function __construct() {}
|
||||
}
|
||||
@@ -431,7 +431,7 @@ class Enotify {
|
||||
|
||||
elseif (isset($params['type']) && $params['type'] === NOTIFY_INTRO) {
|
||||
$subject = sprintf( t('[$Projectname:Notify] Introduction received'));
|
||||
$preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
|
||||
$preamble = sprintf( t('You\'ve received a new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
|
||||
$epreamble = sprintf( t('You\'ve received [zrl=%1$s]a new connection request[/zrl] from %2$s.'),
|
||||
$siteurl . '/connections/ifpending',
|
||||
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
|
||||
@@ -511,7 +511,7 @@ class Enotify {
|
||||
*/
|
||||
|
||||
|
||||
$hash = ((in_array($params['verb'], ['Create', 'Update'])) ? $params['item']['uuid'] : $params['item']['thr_parent_uuid']);
|
||||
$hash = ((in_array($params['verb'], ['Create', 'Update', 'Invite'])) ? $params['item']['uuid'] : $params['item']['thr_parent_uuid']);
|
||||
|
||||
if (!$hash) {
|
||||
$hash = new_uuid();
|
||||
|
||||
@@ -34,8 +34,20 @@ class IConfig {
|
||||
|
||||
if(is_array($item) && array_key_exists('iconfig',$item) && is_array($item['iconfig'])) {
|
||||
foreach($item['iconfig'] as $c) {
|
||||
if (isset($c['iid']) && $c['iid'] == $iid && isset($c['cat']) && $c['cat'] == $family && isset($c['k']) && $c['k'] == $key)
|
||||
if (isset($c['iid']) && $c['iid'] == $iid && isset($c['cat']) && $c['cat'] == $family && isset($c['k']) && $c['k'] == $key) {
|
||||
if (is_string($c['v'])) {
|
||||
if (str_starts_with($c['v'], 'json:')) {
|
||||
$c['v'] = json_unserialize($c['v']);
|
||||
} else if (preg_match('|^a:[0-9]+:{.*}$|s', $c['v'])) {
|
||||
// Unserialize in inherently unsafe. Try to mitigate by not
|
||||
// allowing unserializing objects. Only kept for backwards
|
||||
// compatibility. JSON serialization should be prefered.
|
||||
$c['v'] = unserialize($c['v'], ['allowed_classes' => false]);
|
||||
}
|
||||
}
|
||||
|
||||
return $c['v'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +56,24 @@ class IConfig {
|
||||
dbesc($family),
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
$r[0]['v'] = ((preg_match('|^a:[0-9]+:{.*}$|s',$r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']);
|
||||
if($is_item)
|
||||
if (str_starts_with($r[0]['v'], 'json:')) {
|
||||
$r[0]['v'] = json_unserialize($r[0]['v']);
|
||||
} else if (preg_match('|^a:[0-9]+:{.*}$|s', $r[0]['v'])) {
|
||||
// Unserialize in inherently unsafe. Try to mitigate by not
|
||||
// allowing unserializing objects. Only kept for backwards
|
||||
// compatibility. JSON serialization should be prefered.
|
||||
$r[0]['v'] = unserialize($r[0]['v'], ['allowed_classes' => false]);
|
||||
}
|
||||
|
||||
if ($is_item) {
|
||||
$item['iconfig'][] = $r[0];
|
||||
}
|
||||
|
||||
return $r[0]['v'];
|
||||
}
|
||||
|
||||
return $default;
|
||||
|
||||
}
|
||||
@@ -73,7 +97,7 @@ class IConfig {
|
||||
|
||||
static public function Set(&$item, $family, $key, $value, $sharing = false) {
|
||||
|
||||
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
|
||||
$dbvalue = ((is_array($value)) ? json_serialize($value) : $value);
|
||||
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
|
||||
|
||||
$is_item = false;
|
||||
@@ -99,11 +123,11 @@ class IConfig {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if(intval($item))
|
||||
if(intval($item)) {
|
||||
$iid = intval($item);
|
||||
|
||||
if(! $iid)
|
||||
} else {
|
||||
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 ) ",
|
||||
@@ -150,11 +174,11 @@ class IConfig {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(intval($item))
|
||||
if(intval($item)) {
|
||||
$iid = intval($item);
|
||||
|
||||
if(! $iid)
|
||||
} else {
|
||||
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,7 +116,6 @@ function getRemoteFileSize($url)
|
||||
|
||||
curl_exec($ch);
|
||||
curl_getinfo($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return $size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,14 +62,16 @@ 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());
|
||||
}
|
||||
|
||||
logger('SignatureVerify (eddsa-jcs-2022) ' . (($result) ? 'true' : 'false'));
|
||||
|
||||
return $result;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function signableData($data) {
|
||||
|
||||
@@ -8,9 +8,10 @@ class LDSignatures {
|
||||
|
||||
|
||||
static function verify($data,$pubkey) {
|
||||
$expand_and_check_unsafe = true;
|
||||
|
||||
$ohash = self::hash(self::signable_options($data['signature']));
|
||||
$dhash = self::hash(self::signable_data($data));
|
||||
$ohash = self::hash(self::signable_options($data['signature']), $expand_and_check_unsafe);
|
||||
$dhash = self::hash(self::signable_data($data), $expand_and_check_unsafe);
|
||||
|
||||
$x = Crypto::verify($ohash . $dhash,base64_decode($data['signature']['signatureValue']), $pubkey);
|
||||
logger('LD-verify: ' . intval($x));
|
||||
@@ -74,11 +75,11 @@ class LDSignatures {
|
||||
return json_encode($newopts,JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
static function hash($obj) {
|
||||
return hash('sha256', self::normalise($obj));
|
||||
static function hash($obj, $expand_and_check_unsafe = false) {
|
||||
return hash('sha256', self::normalise($obj, $expand_and_check_unsafe));
|
||||
}
|
||||
|
||||
static function normalise($data) {
|
||||
static function normalise($data, $expand_and_check_unsafe) {
|
||||
$ret = '';
|
||||
|
||||
if(is_string($data)) {
|
||||
@@ -90,6 +91,15 @@ class LDSignatures {
|
||||
|
||||
jsonld_set_document_loader('jsonld_document_loader');
|
||||
|
||||
if ($expand_and_check_unsafe) {
|
||||
$expanded = jsonld_expand($data);
|
||||
|
||||
if (self::contains_unsafe_keys($expanded)) {
|
||||
logger('contains_unsafe_keys: ' . print_r($data,true));
|
||||
throw new \Exception('json-ld graph modification operation detected');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$ret = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]);
|
||||
}
|
||||
@@ -132,6 +142,41 @@ class LDSignatures {
|
||||
|
||||
}
|
||||
|
||||
static function contains_unsafe_keys(array|object $data, int $depth = 0): bool
|
||||
{
|
||||
if ($depth > 64) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$unsafe_keys = ['@graph', '@included', '@reverse'];
|
||||
|
||||
if (is_object($data)) {
|
||||
$data = (array) $data;
|
||||
}
|
||||
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => $value) {
|
||||
//
|
||||
// We can't use `in_array` since the keys may contain more than
|
||||
// just the keyword after expansion, typically "_:@included"
|
||||
// for an unnamed node with the "@included" key.
|
||||
//
|
||||
// So we use `array_filter` with a callback instead:
|
||||
$matches = array_filter($unsafe_keys, fn ($k) => strpos($key, $k) !== false);
|
||||
|
||||
if (!empty($matches)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_array($value) || is_object($value)) {
|
||||
if (self::contains_unsafe_keys($value, $depth + 1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -199,8 +199,7 @@ class Libsync {
|
||||
dbesc($sender)
|
||||
);
|
||||
|
||||
$mid = 'sync';
|
||||
|
||||
$mid = $arr['item'][0]['message_id'] ?? 'sync';
|
||||
|
||||
$DR = new DReport(z_root(), $sender, $d, $mid);
|
||||
|
||||
@@ -306,15 +305,8 @@ class Libsync {
|
||||
|
||||
if (array_key_exists('item', $arr) && $arr['item']) {
|
||||
sync_items($channel, $arr['item'], ((array_key_exists('relocate', $arr)) ? $arr['relocate'] : null));
|
||||
$mid = $arr['item'][0]['message_id'] . '#sync';
|
||||
}
|
||||
|
||||
// deprecated, maintaining for a few months for upward compatibility
|
||||
// this should sync webpages, but the logic is a bit subtle
|
||||
|
||||
//if (array_key_exists('item_id', $arr) && $arr['item_id'])
|
||||
// sync_items($channel, $arr['item_id']);
|
||||
|
||||
if (array_key_exists('menu', $arr) && $arr['menu'])
|
||||
sync_menus($channel, $arr['menu']);
|
||||
|
||||
@@ -757,12 +749,11 @@ class Libsync {
|
||||
*/
|
||||
call_hooks('process_channel_sync_delivery', $addon);
|
||||
|
||||
$DR = new DReport(z_root(), $d, $d, $mid, 'channel sync processed');
|
||||
|
||||
$DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>');
|
||||
$DR->update('channel sync processed');
|
||||
|
||||
$result[] = $DR->get();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Access\PermissionLimits;
|
||||
use Zotlabs\Access\Permissions;
|
||||
use Zotlabs\Daemon\Master;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Web\HTTPSig;
|
||||
|
||||
require_once('include/crypto.php');
|
||||
@@ -116,10 +116,6 @@ class Libzot {
|
||||
}
|
||||
|
||||
if ($msg) {
|
||||
$actors = get_hubloc_id_urls_by_x($channel['channel_hash']);
|
||||
if ($encoding === 'activitystreams' && array_key_exists('actor', $msg) && is_string($msg['actor']) && in_array($msg['actor'], $actors)) {
|
||||
$msg = JSalmon::sign($msg, $actors[0], $channel['channel_prvkey']);
|
||||
}
|
||||
$data['data'] = $msg;
|
||||
}
|
||||
else {
|
||||
@@ -353,7 +349,7 @@ class Libzot {
|
||||
$next_birthday = datetime_convert('UTC', 'UTC', $record['data']['profile']['next_birthday']);
|
||||
}
|
||||
else {
|
||||
$next_birthday = NULL_DATE;
|
||||
$next_birthday = DBA::$dba->get_null_date();
|
||||
}
|
||||
|
||||
$profile_assign = get_pconfig($channel['channel_id'], 'system', 'profile_assign', '');
|
||||
@@ -1302,8 +1298,17 @@ class Libzot {
|
||||
$item['comment_policy'] = 'authenticated';
|
||||
}
|
||||
|
||||
if (isset($AS->meta['signed_data']) && $AS->meta['signed_data']) {
|
||||
IConfig::Set($item, 'activitypub', 'signed_data', $AS->meta['signed_data'], false);
|
||||
if (!ObjCache::Get($item['mid'])) {
|
||||
ObjCache::Set($item['mid'], $AS->data);
|
||||
}
|
||||
else {
|
||||
$existing = q("SELECT owner_xchan, author_xchan FROM item WHERE mid = '%s' LIMIT 1",
|
||||
dbesc($item['mid'])
|
||||
);
|
||||
|
||||
if ($existing && $existing[0]['owner_xchan'] === $item['owner_xchan'] && $existing[0]['author_xchan'] === $item['author_xchan']) {
|
||||
ObjCache::Set($item['mid'], $AS->data);
|
||||
}
|
||||
}
|
||||
|
||||
logger('Activity received: ' . print_r($item, true), LOGGER_DATA, LOG_DEBUG);
|
||||
@@ -1645,12 +1650,19 @@ class Libzot {
|
||||
if (intval($channel['channel_system']) && (!$arr['item_private']) && (!$relay)) {
|
||||
$local_public = true;
|
||||
|
||||
$incl = Config::Get('system','pubstream_incl');
|
||||
$excl = Config::Get('system','pubstream_excl');
|
||||
$incl = Config::Get('system','pubstream_incl', '');
|
||||
$excl = Config::Get('system','pubstream_excl', '');
|
||||
|
||||
if(($incl || $excl) && !MessageFilter::evaluate($arr, $incl, $excl)) {
|
||||
$local_public = false;
|
||||
continue;
|
||||
if ($incl || $excl) {
|
||||
$plaintext = prepare_text($arr['body'], ((isset($arr['mimetype'])) ? $arr['mimetype'] : 'text/bbcode'));
|
||||
$plaintext = html2plain((isset($arr['summary']) && $arr['summary']) ? $arr['summary'] . ' ' . $plaintext : $plaintext);
|
||||
$plaintext = html2plain((isset($arr['title']) && $arr['title']) ? $arr['title'] . ' ' . $plaintext : $plaintext);
|
||||
|
||||
if (!(new MessageFilter($arr, html_entity_decode($incl), html_entity_decode($excl), ['plaintext' => $plaintext]))->evaluate()) {
|
||||
logger('post is filtered');
|
||||
$local_public = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$r = q("select xchan_selfcensored, xchan_censored from xchan where xchan_hash = '%s'",
|
||||
@@ -1659,6 +1671,7 @@ class Libzot {
|
||||
|
||||
// don't import sys channel posts from selfcensored or censored authors
|
||||
if ($r && ($r[0]['xchan_selfcensored'] || $r[0]['xchan_censored'])) {
|
||||
logger('author is censored');
|
||||
$local_public = false;
|
||||
continue;
|
||||
}
|
||||
@@ -1829,9 +1842,7 @@ class Libzot {
|
||||
}
|
||||
|
||||
if (intval($arr['item_private']) === 2) {
|
||||
if (!perm_is_allowed($channel['channel_id'], $sender, 'post_mail')) {
|
||||
$allowed = false;
|
||||
}
|
||||
$allowed = perm_is_allowed($channel['channel_id'], $sender, 'post_mail');
|
||||
}
|
||||
|
||||
if (!$allowed) {
|
||||
@@ -1893,11 +1904,13 @@ class Libzot {
|
||||
else {
|
||||
$DR->update('update ignored');
|
||||
$result[] = $DR->get();
|
||||
|
||||
// We need this line to ensure wall-to-wall comments and add/remove activities are relayed (by falling through to the relay bit),
|
||||
// and at the same time not relay any other relayable posts more than once, because to do so is very wasteful.
|
||||
if (!intval($r[0]['item_origin']))
|
||||
|
||||
// The second part should prevent possible items that come back to us from channels that source our channel from being relayed again (sender != owner or author).
|
||||
if (!intval($r[0]['item_origin']) || (intval($r[0]['item_origin']) && !in_array($sender, [$r[0]['owner_xchan'], $r[0]['author_xchan']]))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2148,10 +2161,9 @@ class Libzot {
|
||||
}
|
||||
|
||||
if (isset($AS->meta['signed_data'])) {
|
||||
IConfig::Set($arr, 'activitypub', 'signed_data', $AS->meta['signed_data'], false);
|
||||
$j = json_decode($AS->meta['signed_data'], true);
|
||||
if ($j) {
|
||||
IConfig::Set($arr, 'activitypub', 'rawmsg', json_encode(JSalmon::unpack($j['data'])), true);
|
||||
ObjCache::Set($arr['mid'], json_encode(JSalmon::unpack($j['data'])));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Zotfinger;
|
||||
@@ -216,7 +217,7 @@ class Libzotdir {
|
||||
[
|
||||
'site_url' => DIRECTORY_FALLBACK_MASTER,
|
||||
'site_flags' => DIRECTORY_MODE_PRIMARY,
|
||||
'site_update' => NULL_DATE,
|
||||
'site_update' => DBA::$dba->get_null_date(),
|
||||
'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch',
|
||||
'site_realm' => DIRECTORY_REALM,
|
||||
'site_valid' => 1,
|
||||
@@ -247,7 +248,7 @@ class Libzotdir {
|
||||
|
||||
$token = Config::Get('system','realm_token');
|
||||
|
||||
$syncdate = (($rr['site_sync'] <= NULL_DATE) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
|
||||
$syncdate = (($rr['site_sync'] <= DBA::$dba->get_null_date()) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
|
||||
$x = z_fetch_url($rr['site_directory'] . '?f=&sync=' . urlencode($syncdate) . (($token) ? '&t=' . $token : ''));
|
||||
|
||||
if (! $x['success'])
|
||||
@@ -724,7 +725,7 @@ class Libzotdir {
|
||||
|
||||
if ($u) {
|
||||
$x = q("UPDATE updates SET $date_sql $flag_sql ud_last = '%s', ud_host = '%s', ud_addr = '%s', ud_update = 0 WHERE ud_id = %d",
|
||||
dbesc(NULL_DATE),
|
||||
dbesc(DBA::$dba->get_null_date()),
|
||||
dbesc(z_root()),
|
||||
dbesc($addr),
|
||||
intval($u[0]['ud_id'])
|
||||
|
||||
@@ -4,260 +4,417 @@ namespace Zotlabs\Lib;
|
||||
|
||||
require_once('include/html2plain.php');
|
||||
|
||||
class MessageFilter {
|
||||
class MessageFilter
|
||||
{
|
||||
protected $lastMatch = '';
|
||||
protected $item = null;
|
||||
protected $include = '';
|
||||
protected $exclude = '';
|
||||
protected $options = [];
|
||||
protected $tags = null;
|
||||
protected $language = '';
|
||||
protected $text = '';
|
||||
protected $excludeRules = [];
|
||||
protected $includeRules = [];
|
||||
|
||||
public static function evaluate($item, $incl, $excl) {
|
||||
public function __construct($item, $include = '', $exclude = '', $options = [])
|
||||
{
|
||||
$this->item = $item;
|
||||
$this->include = $include;
|
||||
$this->exclude = $exclude;
|
||||
$this->options = $options;
|
||||
$this->setup();
|
||||
}
|
||||
|
||||
$text = prepare_text($item['body'], ((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
|
||||
$text = html2plain((!empty($item['title'])) ? $item['title'] . ' ' . $text : $text);
|
||||
protected function setup()
|
||||
{
|
||||
// Option: plaintext
|
||||
// Improve language detection by providing a plaintext version of $item['body'] which has no markup constructs/tags.
|
||||
|
||||
$lang = null;
|
||||
if ((strpos($incl, 'lang=') !== false) || (strpos($excl, 'lang=') !== false) || (strpos($incl, 'lang!=') !== false) || (strpos($excl, 'lang!=') !== false)) {
|
||||
$lang = detect_language($text);
|
||||
}
|
||||
if (array_key_exists('plaintext', $this->options)) {
|
||||
$this->text = $this->options['plaintext'];
|
||||
} else {
|
||||
$this->text = $this->item['body'];
|
||||
}
|
||||
|
||||
$tags = ((isset($item['term']) && is_array($item['term']) && count($item['term'])) ? $item['term'] : false);
|
||||
$this->language = '';
|
||||
|
||||
$until = null;
|
||||
// Language matching is a bit tricky, because the language can be ambiguous (detect_language() returns '').
|
||||
// If the language is ambiguous, the message will pass (be accepted) regardless of language rules.
|
||||
|
||||
// exclude always has priority
|
||||
if (str_contains($this->include, 'lang=')
|
||||
|| str_contains($this->exclude, 'lang=')
|
||||
|| str_contains($this->include, 'lang!=')
|
||||
|| str_contains($this->exclude, 'lang!=')) {
|
||||
$this->language = detect_language($this->text);
|
||||
}
|
||||
|
||||
$exclude = (($excl) ? explode("\n", $excl) : null);
|
||||
$this->tags = ((isset($this->item['term']) && is_array($this->item['term'])
|
||||
&& count($this->item['term'])) ? $this->item['term'] : null);
|
||||
|
||||
if ($exclude) {
|
||||
foreach ($exclude as $word) {
|
||||
$word = html_entity_decode(trim($word));
|
||||
if (! $word) {
|
||||
continue;
|
||||
}
|
||||
if (isset($lang) && ((strpos($word, 'lang=') === 0) || (strpos($word, 'lang!=') === 0))) {
|
||||
if (!strlen($lang)) {
|
||||
// Result is ambiguous. As we are matching deny rules only at this time, continue tests.
|
||||
// Any matching deny rule concludes testing.
|
||||
continue;
|
||||
}
|
||||
if (strpos($word, 'lang=') === 0 && strcasecmp($lang, trim(substr($word, 5))) == 0) {
|
||||
return false;
|
||||
} elseif (strpos($word, 'lang!=') === 0 && strcasecmp($lang, trim(substr($word, 6))) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
elseif (str_starts_with($word, 'until=')) {
|
||||
$until = strtotime(trim(substr($word, 6)));
|
||||
if ($until > strtotime($item['created'] . ' UTC')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
elseif (substr($word, 0, 1) === '#' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '$' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 2) === '?+') {
|
||||
if (self::test_condition(substr($word, 2), $item['obj'])) {
|
||||
return false;
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '?') {
|
||||
if (self::test_condition(substr($word, 1), $item)) {
|
||||
return false;
|
||||
}
|
||||
} elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) {
|
||||
return false;
|
||||
} elseif (stristr($text, $word) !== false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->excludeRules = $this->parse($this->exclude);
|
||||
$this->includeRules = $this->parse($this->include);
|
||||
|
||||
$include = (($incl) ? explode("\n", $incl) : null);
|
||||
}
|
||||
|
||||
if ($include) {
|
||||
foreach ($include as $word) {
|
||||
$word = html_entity_decode(trim($word));
|
||||
if (! $word) {
|
||||
continue;
|
||||
}
|
||||
if (isset($lang) && ((strpos($word, 'lang=') === 0) || (strpos($word, 'lang!=') === 0))) {
|
||||
if (!strlen($lang)) {
|
||||
// Result is ambiguous. However we are checking allow rules
|
||||
// and an ambiguous language is always permitted.
|
||||
return true;
|
||||
}
|
||||
if (strpos($word, 'lang=') === 0 && strcasecmp($lang, trim(substr($word, 5))) == 0) {
|
||||
return true;
|
||||
} elseif (strpos($word, 'lang!=') === 0 && strcasecmp($lang, trim(substr($word, 6))) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
elseif (str_starts_with($word, 'until=')) {
|
||||
$until = strtotime(trim(substr($word, 6)));
|
||||
if ($until > strtotime($item['created'] . ' UTC')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
elseif (substr($word, 0, 1) === '#' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '$' && $tags) {
|
||||
foreach ($tags as $t) {
|
||||
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (substr($word, 0, 2) === '?+') {
|
||||
if (self::test_condition(substr($word, 2), $item['obj'])) {
|
||||
return true;
|
||||
}
|
||||
} elseif (substr($word, 0, 1) === '?') {
|
||||
if (self::test_condition(substr($word, 1), $item)) {
|
||||
return true;
|
||||
}
|
||||
} elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) {
|
||||
return true;
|
||||
} elseif (stristr($text, $word) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
protected function parse($string): array
|
||||
{
|
||||
$rules = [];
|
||||
if (! strlen($string)) {
|
||||
return $rules;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
$phrases = preg_split("/(\s\|\|\s|\s&&\s|\n)/", $string, flags: PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
if (!$phrases) {
|
||||
return $rules;
|
||||
}
|
||||
for ($index = 0; $index < count($phrases); $index ++) {
|
||||
// Even indices are rules and odd indices are operations, linefeed is an implict OR.
|
||||
if (!($index & 1)) {
|
||||
$currentRule = ['operation' => '', 'rule' => $phrases[$index]];
|
||||
if ($index && isset($phrases[$index - 1])) {
|
||||
$currentRule['operation'] = $phrases[$index - 1];
|
||||
if ($currentRule['operation'] === "\n") {
|
||||
$currentRule['operation'] = ' || ';
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
$rules[] = $currentRule;
|
||||
}
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate a conditional expression with support for AND (&&) and OR (||) operators.
|
||||
*
|
||||
* - ?foo ~= baz which will check if item.foo contains the string 'baz';
|
||||
* - ?foo == baz which will check if item.foo is the string 'baz';
|
||||
* - ?foo != baz which will check if item.foo is not the string 'baz';
|
||||
* - ?foo >= 3 which will check if item.foo is greater than or equal to 3;
|
||||
* - ?foo > 3 which will check if item.foo is greater than 3;
|
||||
* - ?foo <= 3 which will check if item.foo is less than or equal to 3;
|
||||
* - ?foo < 3 which will check if item.foo is less than 3;
|
||||
*
|
||||
* - ?foo {} baz which will check if 'baz' is an array element in item.foo
|
||||
* - ?foo {*} baz which will check if 'baz' is an array key in item.foo
|
||||
* - ?foo which will check for a return of a true condition for item.foo;
|
||||
|
||||
public function evaluate(): bool
|
||||
{
|
||||
|
||||
$previousResult = $newResult = null;
|
||||
|
||||
// exclude always has priority
|
||||
|
||||
$exclude = $this->excludeRules;
|
||||
$include = $this->includeRules;
|
||||
|
||||
if ($exclude) {
|
||||
foreach ($exclude as $rule) {
|
||||
if (!strlen(trim($rule['rule']))) {
|
||||
continue;
|
||||
}
|
||||
if (!strlen($this->language) && ((str_starts_with($rule['rule'], 'lang=')) || (str_starts_with($rule['rule'], 'lang!=')))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $this->evaluateRule($rule['rule']);
|
||||
|
||||
switch ($rule['operation']) {
|
||||
case '':
|
||||
$previousResult = $newResult = $result;
|
||||
break;
|
||||
case ' || ':
|
||||
$newResult = $previousResult || $result;
|
||||
break;
|
||||
case ' && ':
|
||||
$newResult = $previousResult && $result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($newResult) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$previousResult = $newResult = null;
|
||||
|
||||
if ($include) {
|
||||
foreach ($include as $rule) {
|
||||
if (!strlen(trim($rule['rule']))) {
|
||||
continue;
|
||||
}
|
||||
if (!strlen($this->language) && ((str_starts_with($rule['rule'], 'lang=')) || (str_starts_with($rule['rule'], 'lang!=')))) {
|
||||
continue;
|
||||
}
|
||||
$result = $this->evaluateRule($rule['rule']);
|
||||
|
||||
switch ($rule['operation']) {
|
||||
case '':
|
||||
$previousResult = $newResult = $result;
|
||||
break;
|
||||
case ' || ':
|
||||
$newResult = $previousResult || $result;
|
||||
break;
|
||||
case ' && ':
|
||||
$newResult = $previousResult && $result;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return $newResult ?? true;
|
||||
}
|
||||
|
||||
protected function evaluateRule($ruleText): bool
|
||||
{
|
||||
$ruleText = trim($ruleText);
|
||||
|
||||
if (($this->language) && ((str_starts_with($ruleText, 'lang=')) || (str_starts_with($ruleText, 'lang!=')))) {
|
||||
if (str_starts_with($ruleText, 'lang=') && strcasecmp($this->language, trim(substr($ruleText, 5))) == 0) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
} elseif (str_starts_with($ruleText, 'lang!=') && strcasecmp($this->language, trim(substr($ruleText, 6))) != 0) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, 'until=')) {
|
||||
$until = strtotime(trim(substr($ruleText, 6)));
|
||||
|
||||
if ($until > strtotime($this->item['created'] . ' UTC')) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, '#') && $this->tags) {
|
||||
// #hashtag match
|
||||
foreach ($this->tags as $t) {
|
||||
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (!strcasecmp($t['term'], substr($ruleText, 1)) || (substr($ruleText, 1) === '*'))) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// hashtag count match
|
||||
if (substr($ruleText, 1, 1) === '>') {
|
||||
$hashtagLimit = (int)substr($ruleText, 2);
|
||||
$hashtagCount = 0;
|
||||
foreach ($this->tags as $t) {
|
||||
if ($t['ttype'] == TERM_HASHTAG || $t['ttype'] == TERM_COMMUNITYTAG) {
|
||||
$hashtagCount++;
|
||||
}
|
||||
}
|
||||
if ($hashtagLimit && $hashtagCount > $hashtagLimit) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, '@') && $this->tags) {
|
||||
// @mention match
|
||||
foreach ($this->tags as $t) {
|
||||
if ((($t['ttype'] == TERM_MENTION && (!strcasecmp($t['term'], substr($ruleText, 1)))) || (substr($ruleText, 1) === '*'))) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// mention count match
|
||||
if (substr($ruleText, 1, 1) === '>') {
|
||||
$mentionLimit = (int)substr($ruleText, 2);
|
||||
$mentionCount = 0;
|
||||
foreach ($this->tags as $t) {
|
||||
if ($t['ttype'] == TERM_MENTION) {
|
||||
$mentionCount++;
|
||||
}
|
||||
}
|
||||
if ($mentionLimit && $mentionCount > $mentionLimit) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, '$') && $this->tags) {
|
||||
foreach ($this->tags as $t) {
|
||||
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($ruleText, 1)) || (substr($ruleText, 1) === '*'))) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, '?+') && is_array($this->item['obj'])) {
|
||||
if ($this->test_condition(substr($ruleText, 2), $this->item['obj'])) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
} elseif (str_starts_with($ruleText, '?')) {
|
||||
$this->item['ua'] = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
if (empty($this->item['app'])) {
|
||||
$author_app = $this->item['author']['site_project'] ?? '';
|
||||
if (!$author_app && isset($this->item['author'])) {
|
||||
if (str_contains($this->item['author']['xchan_hash'], 'threads.net') || str_contains($this->item['author']['xchan_hash'], 'threads.com')) {
|
||||
$author_app = 'threads';
|
||||
}
|
||||
}
|
||||
$this->item['app'] = $author_app;
|
||||
}
|
||||
if ($this->test_condition(substr($ruleText, 1), $this->item)) {
|
||||
unset($this->item['ua']);
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
unset($this->item['ua']);
|
||||
} elseif ((str_starts_with($ruleText, '/')) && preg_match($ruleText, $this->item['body'])) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
} elseif (stristr($this->item['body'], $ruleText) !== false) {
|
||||
$this->lastMatch = $ruleText;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function getLastMatch(): string
|
||||
{
|
||||
return $this->lastMatch;
|
||||
}
|
||||
public function setLastMatch($string): MessageFilter
|
||||
{
|
||||
$this->lastMatch = $string;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @brief Test for Conditional Execution conditions. Shamelessly ripped off from src/Render/Comanche
|
||||
*
|
||||
* This is extensible. The first version of variable testing supports tests of the forms:
|
||||
*
|
||||
* - ?foo ~= baz will check if item.foo contains the string 'baz';
|
||||
* - ?foo == baz will check if item.foo is the string 'baz';
|
||||
* - ?foo != baz will check if item.foo is not the string 'baz';
|
||||
* - ?foo // baz will check if item.foo matches the regular expression 'baz';
|
||||
* - ?foo >= 3 will check if item.foo is greater than or equal to 3;
|
||||
* - ?foo > 3 will check if item.foo is greater than 3;
|
||||
* - ?foo <= 3 will check if item.foo is less than or equal to 3;
|
||||
* - ?foo < 3 will check if item.foo is less than 3;
|
||||
* - ?foo & 2 will check if item.foo has the second bit set.
|
||||
* - ?foo !& 2 will check if item.foo does not have the second bit set.
|
||||
*
|
||||
* - ?foo {} baz which will check if 'baz' is an array element in item.foo
|
||||
* - ?foo {*} baz which will check if 'baz' is an array key in item.foo
|
||||
* - ?foo which will check for a return of a true condition for item.foo;
|
||||
* - ?!foo which will check for a return of a false condition for item.foo;
|
||||
*
|
||||
* The values 0, '', an empty array, and an unset value will all evaluate to false.
|
||||
*
|
||||
* @param string $s The condition string to evaluate.
|
||||
* @param array $item The associative array providing variable values.
|
||||
* @return bool True if the condition is met, false otherwise.
|
||||
*/
|
||||
* The values 0, '', an empty array, and an unset value will all evaluate to false.
|
||||
*
|
||||
* @param string $s
|
||||
* @param array $item
|
||||
* @return bool
|
||||
*/
|
||||
protected function test_condition($s,$item)
|
||||
{
|
||||
$s = trim($s);
|
||||
|
||||
public static function test_condition($s, $item) {
|
||||
$s = trim($s);
|
||||
if (preg_match('/(.*?)\s&\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x & (int) trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle OR (||)
|
||||
// Split on '||' not inside quotes
|
||||
$or_parts = preg_split('/\s*\|\|\s*/', $s);
|
||||
if (count($or_parts) > 1) {
|
||||
foreach ($or_parts as $part) {
|
||||
if (self::test_condition(ltrim($part, '?+'), $item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (preg_match('/(.*?)\s!&\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (!($x & (int) trim($matches[2]))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle AND (&&)
|
||||
// Split on '&&' not inside quotes
|
||||
$and_parts = preg_split('/\s*\&\&\s*/', $s);
|
||||
if (count($and_parts) > 1) {
|
||||
foreach ($and_parts as $part) {
|
||||
if (!self::test_condition(ltrim($part, '?+'), $item)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/(.*?)\s~=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (is_string($x) && stripos($x, trim($matches[2])) !== false) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Basic checks
|
||||
if (preg_match('/(.*?)\s==\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x == trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Contains substring (case-insensitive)
|
||||
if (preg_match('/(.*?)\s\~\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return (stripos($x, trim($matches[2])) !== false);
|
||||
}
|
||||
if (preg_match('/(.*?)\s!=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x != trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Equality
|
||||
if (preg_match('/(.*?)\s\=\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x == trim($matches[2]));
|
||||
}
|
||||
if (preg_match('/(.*?)\s\/\/\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (substr(trim($matches[2]),0,1) !== substr(trim($matches[2]),-1)) {
|
||||
$matches[2] = '/' . trim($matches[2]) . '/';
|
||||
}
|
||||
if (preg_match(trim($matches[2]), $x)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Inequality
|
||||
if (preg_match('/(.*?)\s\!\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x != trim($matches[2]));
|
||||
}
|
||||
if (preg_match('/(.*?)\s>=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x >= trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Greater than or equal
|
||||
if (preg_match('/(.*?)\s\>\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x >= trim($matches[2]));
|
||||
}
|
||||
if (preg_match('/(.*?)\s<=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x <= trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Less than or equal
|
||||
if (preg_match('/(.*?)\s\<\=\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x <= trim($matches[2]));
|
||||
}
|
||||
if (preg_match('/(.*?)\s>\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x > trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Greater than
|
||||
if (preg_match('/(.*?)\s\>\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x > trim($matches[2]));
|
||||
}
|
||||
if (preg_match('/(.*?)\s<\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x < trim($matches[2])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Less than
|
||||
if (preg_match('/(.*?)\s\<\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return ($x < trim($matches[2]));
|
||||
}
|
||||
// Array contains value
|
||||
if (preg_match('/(.*?)\s\{\}\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (is_array($x) && in_array(trim($matches[2]), $x)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Array contains value
|
||||
if (preg_match('/(.*?)\s\{\}\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return (is_array($x) && in_array(trim($matches[2]), $x));
|
||||
}
|
||||
|
||||
// Array contains key
|
||||
if (preg_match('/(.*?)\s\{\*\}\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return (is_array($x) && array_key_exists(trim($matches[2]), $x));
|
||||
}
|
||||
|
||||
// Ordering of this check (for falsiness) with relation to the following one (check for truthiness) is important.
|
||||
// Falsy check
|
||||
if (preg_match('/\!(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return !$x;
|
||||
}
|
||||
|
||||
// Truthy check (default)
|
||||
if (preg_match('/(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
return (bool)$x;
|
||||
}
|
||||
|
||||
// If no conditions matched, return false
|
||||
return false;
|
||||
}
|
||||
// Array contains key
|
||||
if (preg_match('/(.*?)\s\{\*}\s(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (is_array($x) && array_key_exists(trim($matches[2]), $x)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ordering of this check (for falseness) with relation to the following one (check for truthiness) is important.
|
||||
if (preg_match('/!(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if (!$x) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/(.*?)$/', $s, $matches)) {
|
||||
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
|
||||
if ($x) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
37
Zotlabs/Lib/MySQLDbStats.php
Normal file
37
Zotlabs/Lib/MySQLDbStats.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use DBA;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Concrete implementation for getting stats from MySQL and MariaDB databases.
|
||||
*/
|
||||
class MySQLDbStats extends DbStats {
|
||||
|
||||
public function getQueries(): int {
|
||||
//
|
||||
// We can't use the regular Hubzilla db helper function here, as
|
||||
// it will only return information from a `SELECT` statement.
|
||||
//
|
||||
// Use the basic PDO access instead.
|
||||
//
|
||||
$query = DBA::$dba->db->prepare('SHOW STATUS LIKE "Queries"');
|
||||
$query->execute();
|
||||
|
||||
$result = $query->fetch(PDO::FETCH_ASSOC);
|
||||
logger(print_r($result, true));
|
||||
if (!empty($result)) {
|
||||
return $result['Value'] ?? -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
40
Zotlabs/Lib/ObjCache.php
Normal file
40
Zotlabs/Lib/ObjCache.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
class ObjCache
|
||||
{
|
||||
public static function Get($path, $type = 'as')
|
||||
{
|
||||
if (!$path) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$localpath = Hashpath::path($path, 'store/[data]/[obj]/' . $type, 2, alg: 'sha256');
|
||||
if (file_exists($localpath)) {
|
||||
return json_unserialize(file_get_contents($localpath));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public static function Set($path, $content, $type = 'as') {
|
||||
if (!$path) {
|
||||
return;
|
||||
}
|
||||
|
||||
$localpath = Hashpath::path($path, 'store/[data]/[obj]/' . $type, 2, alg: 'sha256');
|
||||
file_put_contents($localpath, json_serialize($content));
|
||||
}
|
||||
|
||||
public static function Delete($path, $type = 'as') {
|
||||
if (!$path) {
|
||||
return;
|
||||
}
|
||||
|
||||
$localpath = Hashpath::path($path, 'store/[data]/[obj]/' . $type, 2, alg: 'sha256');
|
||||
if (file_exists($localpath)) {
|
||||
unlink($localpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Zotlabs/Lib/PostgresDbStats.php
Normal file
32
Zotlabs/Lib/PostgresDbStats.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use DBA;
|
||||
|
||||
/**
|
||||
* Concrete implementation for getting stats from PostgreSQL databases.
|
||||
*/
|
||||
class PostgresDbStats extends DbStats {
|
||||
|
||||
public function getQueries(): int {
|
||||
$sqlGetQps = <<<'SQL'
|
||||
select (xact_commit + xact_rollback) as queries
|
||||
from pg_stat_database
|
||||
where datname='%s'
|
||||
SQL;
|
||||
|
||||
$result = q($sqlGetQps, DBA::$dba->dbname);
|
||||
if (!empty($result)) {
|
||||
return $result[0]['queries'] ?? -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,20 @@ use Zotlabs\Zot6\Zot6Handler;
|
||||
|
||||
class Queue {
|
||||
|
||||
/**
|
||||
* Get number of entries in the out queue.
|
||||
*
|
||||
* When delivery is successful, the item is removed from the out queue, so
|
||||
* the number of items in the queue reflects the number of pending delivery
|
||||
* attempts.
|
||||
*
|
||||
* @return int Number of items in the out queue.
|
||||
*/
|
||||
static function count(): int {
|
||||
$r = dbq('select count(*) as total from outq');
|
||||
return $r[0]['total'] ?? 0;
|
||||
}
|
||||
|
||||
static function update($id, $add_priority = 0) {
|
||||
|
||||
logger('queue: requeue item ' . $id,LOGGER_DEBUG);
|
||||
|
||||
28
Zotlabs/Lib/QueueWorkerStats.php
Normal file
28
Zotlabs/Lib/QueueWorkerStats.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
class QueueWorkerStats
|
||||
{
|
||||
public readonly int $size;
|
||||
public readonly int $active;
|
||||
|
||||
public function __construct() {
|
||||
$query = <<<'SQL'
|
||||
select count(*) as total from workerq
|
||||
union (select count(*) as qworkers from workerq where workerq_reservationid is not null)
|
||||
SQL;
|
||||
|
||||
$result = dbq('select count(*) as total from workerq');
|
||||
$this->size = !empty($result) ? $result[0]['total'] : -1;
|
||||
|
||||
$result = dbq('select count(*) as qworkers from workerq where workerq_reservationid is not null');
|
||||
$this->active = !empty($result) ? $result[0]['qworkers'] : -1;
|
||||
}
|
||||
}
|
||||
@@ -118,26 +118,28 @@ class Share {
|
||||
$photo_bb = $object['body'];
|
||||
}
|
||||
|
||||
if (strpos($this->item['body'], "[/share]") !== false) {
|
||||
$pos = strpos($this->item['body'], "[share");
|
||||
$bb = substr($this->item['body'], $pos);
|
||||
} else {
|
||||
$bb = "[share author='".urlencode($this->item['author']['xchan_name']).
|
||||
"' profile='" . $this->item['author']['xchan_url'] .
|
||||
"' avatar='" . $this->item['author']['xchan_photo_s'] .
|
||||
"' link='" . $this->item['plink'] .
|
||||
"' auth='" . (($this->item['author']['xchan_network'] === 'zot6') ? 'true' : 'false') .
|
||||
"' posted='" . $this->item['created'] .
|
||||
"' message_id='" . $this->item['mid'] .
|
||||
"']";
|
||||
if($this->item['title'])
|
||||
if (!str_contains($this->item['body'], '[/share]')) {
|
||||
$quote = in_array($this->item['author']['xchan_network'], ['zot6', 'activitypub']) ? "quote='true'" : '';
|
||||
|
||||
$bb .= "[share author='" . urlencode($this->item['author']['xchan_name']) . "'
|
||||
profile='" . $this->item['author']['xchan_url'] . "'
|
||||
avatar='" . $this->item['author']['xchan_photo_s'] . "'
|
||||
link='" . $this->item['plink'] . "'
|
||||
auth='" . (($this->item['author']['xchan_network'] === 'zot6') ? 'true' : 'false') . "'
|
||||
posted='" . $this->item['created'] . "'
|
||||
message_id='" . $this->item['mid'] . "'
|
||||
$quote
|
||||
]";
|
||||
|
||||
if ($this->item['title']) {
|
||||
$bb .= '[h3][b]'.$this->item['title'].'[/b][/h3]'."\r\n";
|
||||
}
|
||||
|
||||
$bb .= (($is_photo) ? $photo_bb . "\r\n" . $this->item['body'] : $this->item['body']);
|
||||
$bb .= "[/share]";
|
||||
}
|
||||
|
||||
return $bb;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Access\AccessList;
|
||||
|
||||
require_once('include/text.php');
|
||||
@@ -120,10 +121,10 @@ class ThreadItem {
|
||||
$locktype = 0;
|
||||
}
|
||||
|
||||
$shareable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && (intval($item['item_private']) === 0));
|
||||
$shareable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && (intval($item['item_private']) === 0) && !str_contains($item['body'], '[/share]'));
|
||||
|
||||
// allow an exemption for sharing stuff from your private feeds
|
||||
if($item['author']['xchan_network'] === 'rss')
|
||||
if ($item['author']['xchan_network'] === 'rss')
|
||||
$shareable = true;
|
||||
|
||||
$repeatable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && intval($item['item_private']) === 0 && in_array($item['author']['xchan_network'], ['zot6', 'activitypub']));
|
||||
@@ -283,9 +284,12 @@ class ThreadItem {
|
||||
$reply_to = [];
|
||||
$reactions_allowed = false;
|
||||
|
||||
if($this->is_commentable() && $observer) {
|
||||
if($this->is_commentable()) {
|
||||
$reply_to = array( t("Reply to this message"), t("reply"), t("Reply to"));
|
||||
$reactions_allowed = true;
|
||||
|
||||
if ($observer) {
|
||||
$reactions_allowed = true;
|
||||
}
|
||||
}
|
||||
|
||||
$share = [];
|
||||
@@ -412,7 +416,7 @@ class ThreadItem {
|
||||
'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'),
|
||||
'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created']),
|
||||
'editedtime' => (($item['edited'] != $item['created']) ? sprintf(t('Last edited %s'), relative_time($item['edited'])) : ''),
|
||||
'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf(t('Expires %s'), relative_time($item['expires'])) : ''),
|
||||
'expiretime' => (($item['expires'] > DBA::$dba->get_null_date()) ? sprintf(t('Expires %s'), relative_time($item['expires'])) : ''),
|
||||
'lock' => $lock,
|
||||
'locktype' => $locktype,
|
||||
'delayed' => (($item['item_delayed']) ? sprintf(t('Published %s'), relative_time($item['created'])) : ''),
|
||||
|
||||
132
Zotlabs/Lib/Url.php
Normal file
132
Zotlabs/Lib/Url.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?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,9 +437,7 @@ class Acl extends \Zotlabs\Web\Controller {
|
||||
|
||||
if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) {
|
||||
$url = z_root() . '/dirsearch';
|
||||
}
|
||||
|
||||
if(! $url) {
|
||||
} else {
|
||||
$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($token['uid']) . " ";
|
||||
$sql_extra = " and item.uid = " . intval($item_uid) . " ";
|
||||
}
|
||||
else {
|
||||
$sql_extra = item_permissions_sql(0);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
require_once('include/account.php');
|
||||
@@ -90,7 +91,7 @@ class Admin extends \Zotlabs\Web\Controller {
|
||||
$r = q("SELECT COUNT(CASE WHEN account_id > 0 THEN 1 ELSE NULL END) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account",
|
||||
db_utcnow(),
|
||||
db_utcnow(),
|
||||
dbesc(NULL_DATE),
|
||||
dbesc(DBA::$dba->get_null_date()),
|
||||
intval(ACCOUNT_BLOCKED)
|
||||
);
|
||||
if ($r) {
|
||||
|
||||
@@ -55,11 +55,10 @@ class Account_edit {
|
||||
|
||||
|
||||
function get() {
|
||||
if(argc() > 2)
|
||||
$account_id = argv(2);
|
||||
$account_id = intval(argv(2));
|
||||
|
||||
$x = q("select * from account where account_id = %d limit 1",
|
||||
intval($account_id)
|
||||
$account_id
|
||||
);
|
||||
|
||||
if(! $x) {
|
||||
|
||||
@@ -203,7 +203,6 @@ 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'),
|
||||
@@ -263,7 +262,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]),
|
||||
@@ -279,7 +278,6 @@ 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])
|
||||
);
|
||||
@@ -341,29 +339,27 @@ class Accounts {
|
||||
* @SuppressWarnings(PHPMD.ShortVariable)
|
||||
*/
|
||||
private function block_unblock_accounts(): void {
|
||||
if (!isset($_POST['user']) || !isset($_POST['blocked'])) {
|
||||
if (!isset($_POST['user'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$users = $_POST['user'];
|
||||
$blocked = $_POST['blocked'];
|
||||
|
||||
if (!is_array($users) || !is_array($blocked)) {
|
||||
if (!is_array($users)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($users as $i => $id) {
|
||||
// if account is blocked remove blocked bit-flag, otherwise add blocked bit-flag
|
||||
$op = $blocked[$i] ? '& ~' : '| ';
|
||||
$xor = db_getfunc('^');
|
||||
|
||||
q("UPDATE account SET account_flags = (account_flags $op%d) WHERE account_id = %d",
|
||||
foreach($users as $id) {
|
||||
q("UPDATE account SET account_flags = (account_flags $xor %d) WHERE account_id = %d",
|
||||
intval(ACCOUNT_BLOCKED),
|
||||
intval($id)
|
||||
);
|
||||
}
|
||||
|
||||
$count = count($users);
|
||||
$fmt = tt("%s account blocked/unblocked", "%s account blocked/unblocked", $count);
|
||||
$fmt = tt("%s account blocked/unblocked", "%s accounts blocked/unblocked", $count);
|
||||
notice(sprintf($fmt, $count));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ 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);
|
||||
|
||||
@@ -109,6 +112,7 @@ 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,6 +32,9 @@ 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 = ((! $copy && ($folder !== $newfolder || (($single) ? $filename !== $newfilename : false))) ? true : false);
|
||||
$move = ((!$delete && !$copy && ($folder !== $newfolder || (($single) ? $filename !== $newfilename : false))) ? true : false);
|
||||
|
||||
$perms = get_all_perms($channel_id, $observer_hash);
|
||||
|
||||
|
||||
@@ -12,28 +12,31 @@ class Authorize extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
else {
|
||||
|
||||
$name = $_REQUEST['client_name'];
|
||||
$name = $_GET['client_name'];
|
||||
if(! $name) {
|
||||
$name = (($_REQUEST['client_id']) ?: t('Unknown App'));
|
||||
$name = $_GET['client_id'] ?: t('Unknown App');
|
||||
}
|
||||
|
||||
$app = [
|
||||
'name' => $name,
|
||||
'icon' => (x($_REQUEST, 'logo_uri') ? $_REQUEST['logo_uri'] : z_root() . '/images/icons/plugin.png'),
|
||||
'url' => (x($_REQUEST, 'client_uri') ? $_REQUEST['client_uri'] : ''),
|
||||
'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'] : ''),
|
||||
];
|
||||
|
||||
$link = (($app['url']) ? '<a style="float: none;" href="' . $app['url'] . '">' . $app['name'] . '</a> ' : $app['name']);
|
||||
$link = $app['url']
|
||||
? '<a style="float: none;" href="' . escape_url($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($_REQUEST, 'client_id') ? $_REQUEST['client_id'] : ''),
|
||||
'$redirect_uri' => (x($_REQUEST, 'redirect_uri') ? $_REQUEST['redirect_uri'] : ''),
|
||||
'$state' => (x($_REQUEST, 'state') ? $_REQUEST['state'] : ''),
|
||||
'$client_id' => (x($_GET, 'client_id') ? $_GET['client_id'] : ''),
|
||||
'$redirect_uri' => (x($_GET, 'redirect_uri') ? $_GET['redirect_uri'] : ''),
|
||||
'$state' => (x($_GET, 'state') ? $_GET['state'] : ''),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +46,10 @@ 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,13 +78,12 @@ class Block extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
xchan_query($r);
|
||||
$r = fetch_post_tags($r,true);
|
||||
|
||||
$o .= prepare_page($r[0]);
|
||||
return $o;
|
||||
|
||||
|
||||
return prepare_page($r[0]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -83,37 +83,31 @@ class Bookmarks extends \Zotlabs\Web\Controller {
|
||||
|
||||
$channel = \App::get_channel();
|
||||
|
||||
$o = '';
|
||||
|
||||
$o .= '<div class="generic-content-wrapper-styled">';
|
||||
|
||||
$o .= '<h3>' . t('Bookmarks') . '</h3>';
|
||||
|
||||
$x = menu_list(local_channel(),'',MENU_BOOKMARK);
|
||||
|
||||
if($x) {
|
||||
foreach($x as $xx) {
|
||||
$y = menu_fetch($xx['menu_name'],local_channel(),get_observer_hash());
|
||||
$o .= menu_render($y,'',true);
|
||||
$bookmarks = [];
|
||||
$x = menu_list(local_channel(), '', MENU_BOOKMARK);
|
||||
if ($x) {
|
||||
foreach ($x as $xx) {
|
||||
$y = menu_fetch($xx['menu_name'], local_channel(), get_observer_hash());
|
||||
$bookmarks[] = menu_render($y, '', true);
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '<h3>' . t('My Connections Bookmarks') . '</h3>';
|
||||
|
||||
|
||||
$x = menu_list(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK);
|
||||
|
||||
if($x) {
|
||||
foreach($x as $xx) {
|
||||
$y = menu_fetch($xx['menu_name'],local_channel(),get_observer_hash());
|
||||
$o .= menu_render($y,'',true);
|
||||
$conn_bookmarks = [];
|
||||
$x = menu_list(local_channel(), '', MENU_SYSTEM | MENU_BOOKMARK);
|
||||
if ($x) {
|
||||
foreach ($x as $xx) {
|
||||
$y = menu_fetch($xx['menu_name'], local_channel(), get_observer_hash());
|
||||
$conn_bookmarks[] = menu_render($y, '', true);
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '</div>';
|
||||
|
||||
return $o;
|
||||
|
||||
return replace_macros(get_markup_template('bookmarks.tpl'), [
|
||||
'$title1' => t('Bookmarks'),
|
||||
'$title2' => t('My Connections Bookmarks'),
|
||||
'$bookmarks' => $bookmarks,
|
||||
'$conn_bookmarks' => $conn_bookmarks,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -136,15 +136,13 @@ 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,6 +1,7 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
|
||||
class Changeaddr extends \Zotlabs\Web\Controller {
|
||||
|
||||
@@ -29,7 +30,7 @@ class Changeaddr extends \Zotlabs\Web\Controller {
|
||||
if(! ($x && $x['account']))
|
||||
return;
|
||||
|
||||
if($account['account_password_changed'] > NULL_DATE) {
|
||||
if($account['account_password_changed'] > DBA::$dba->get_null_date()) {
|
||||
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
|
||||
if($account['account_password_changed'] > $d1) {
|
||||
notice( t('Channel name changes are not allowed within 48 hours of changing the account password.') . EOL);
|
||||
|
||||
@@ -148,14 +148,14 @@ class Channel extends Controller {
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Posts and comments'),
|
||||
'href' => z_root() . '/feed/' . $which
|
||||
'href' => z_root() . '/feed/' . $which . '?top=0'
|
||||
]);
|
||||
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Only posts'),
|
||||
'href' => z_root() . '/feed/' . $which . '?f=&top=1'
|
||||
'href' => z_root() . '/feed/' . $which . '?top=1'
|
||||
]);
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
use Zotlabs\Access\AccessList;
|
||||
@@ -300,7 +301,7 @@ class Channel_calendar extends Controller {
|
||||
from event left join item on item.resource_id = event.event_hash
|
||||
where event.uid = %d and event.dtstart > '%s' and event.dtend > event.dtstart",
|
||||
intval(local_channel()),
|
||||
dbesc(NULL_DATE)
|
||||
dbesc(DBA::$dba->get_null_date())
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -33,26 +33,20 @@ class Cloud extends Controller {
|
||||
*/
|
||||
function init() {
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -71,7 +65,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';
|
||||
}
|
||||
|
||||
@@ -99,7 +93,6 @@ 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' ]);
|
||||
|
||||
@@ -107,8 +100,9 @@ class Cloud extends Controller {
|
||||
|
||||
$server->start();
|
||||
|
||||
if($browser->build_page)
|
||||
if ($browser->build_page) {
|
||||
construct_page();
|
||||
}
|
||||
|
||||
killme();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Zotlabs\Module;
|
||||
|
||||
use Sabre\DAV as SDAV;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Storage;
|
||||
use Zotlabs\Web\HTTPSig;
|
||||
|
||||
@@ -106,20 +107,16 @@ 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);
|
||||
|
||||
// provide a directory view for the cloud in Hubzilla
|
||||
$browser = new \Zotlabs\Storage\Browser($auth);
|
||||
$auth->setBrowserPlugin($browser);
|
||||
$server->enablePropfindDepthInfinity = Config::Get('system', 'propfind_depth_infinity', false);
|
||||
|
||||
// Experimental QuotaPlugin
|
||||
// $server->addPlugin(new \Zotlabs\Storage\QuotaPlugin($auth));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
@@ -232,7 +233,7 @@ class Dirsearch extends Controller {
|
||||
$spkt = array('transactions' => array());
|
||||
|
||||
$r = q("SELECT * FROM updates WHERE ud_update = 0 AND ud_last = '%s' AND ud_date >= '%s' ORDER BY ud_date DESC",
|
||||
dbesc(NULL_DATE),
|
||||
dbesc(DBA::$dba->get_null_date()),
|
||||
dbesc($sync)
|
||||
);
|
||||
|
||||
|
||||
@@ -61,12 +61,10 @@ class Dreport extends \Zotlabs\Web\Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
$r = q("select * from dreport where dreport_xchan = '%s' and (dreport_mid = '%s' or dreport_mid = '%s' or dreport_mid = '%s' or dreport_mid = '%s')",
|
||||
$r = q("select * from dreport where dreport_xchan = '%s' and (dreport_mid = '%s' or dreport_mid = '%s')",
|
||||
dbesc($channel['channel_hash']),
|
||||
dbesc($mid),
|
||||
dbesc($mid . '#sync'),
|
||||
dbesc(str_replace('/item/', '/activity/', $mid)),
|
||||
dbesc(str_replace('/item/', '/activity/', $mid) . '#sync')
|
||||
dbesc(str_replace('/item/', '/activity/', $mid))
|
||||
);
|
||||
|
||||
if(! $r) {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/security.php');
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
|
||||
class Embed extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
$post_id = ((argc() > 1) ? intval(argv(1)) : 0);
|
||||
|
||||
if(! $post_id)
|
||||
killme();
|
||||
|
||||
echo '[share=' . $post_id . '][/share]';
|
||||
killme();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,49 +2,47 @@
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\PConfig;
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
require_once('include/items.php');
|
||||
|
||||
class Feed extends \Zotlabs\Web\Controller {
|
||||
class Feed extends Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
$params = [];
|
||||
|
||||
$params['begin'] = ((x($_REQUEST,'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE);
|
||||
$params['end'] = ((x($_REQUEST,'date_end')) ? $_REQUEST['date_end'] : '');
|
||||
$params['type'] = ((stristr(argv(0),'json')) ? 'json' : 'xml');
|
||||
$params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0);
|
||||
$params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0);
|
||||
$params['start'] = ((x($_REQUEST,'start')) ? intval($_REQUEST['start']) : 0);
|
||||
$params['records'] = ((x($_REQUEST,'records')) ? intval($_REQUEST['records']) : 10);
|
||||
$params['direction'] = ((x($_REQUEST,'direction')) ? dbesc($_REQUEST['direction']) : 'desc');
|
||||
$params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : '');
|
||||
$params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 0);
|
||||
if (argc() < 2) {
|
||||
killme();
|
||||
}
|
||||
|
||||
if(! in_array($params['direction'],['asc','desc'])) {
|
||||
if (observer_prohibited(true)) {
|
||||
killme();
|
||||
}
|
||||
|
||||
$channel = channelx_by_nick(argv(1));
|
||||
if (!$channel) {
|
||||
killme();
|
||||
}
|
||||
|
||||
$params['begin'] = $_REQUEST['date_begin'] ?? DBA::$dba->get_null_date();
|
||||
$params['end'] = $_REQUEST['date_end'] ?? '';
|
||||
$params['type'] = 'xml';
|
||||
$params['pages'] = ((!empty($_REQUEST['pages'])) ? intval($_REQUEST['pages']) : 0);
|
||||
$params['top'] = ((array_key_exists('top', $_REQUEST)) ? intval($_REQUEST['top']) : PConfig::Get($channel['channel_id'], 'system', 'channel_simple_feed', 1));
|
||||
$params['start'] = ((!empty($_REQUEST['start'])) ? intval($_REQUEST['start']) : 0);
|
||||
$params['records'] = ((!empty($_REQUEST['records'])) ? intval($_REQUEST['records']) : 10);
|
||||
$params['cat'] = ((!empty($_REQUEST['cat'])) ? escape_tags($_REQUEST['cat']) : '');
|
||||
$params['compat'] = ((!empty($_REQUEST['compat'])) ? intval($_REQUEST['compat']) : 0);
|
||||
$params['direction'] = ((!empty($_REQUEST['direction'])) ? dbesc($_REQUEST['direction']) : 'desc');
|
||||
|
||||
if (!in_array($params['direction'], ['asc', 'desc'])) {
|
||||
$params['direction'] = 'desc';
|
||||
}
|
||||
|
||||
if(argc() > 1) {
|
||||
logger('public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $channel['channel_address']);
|
||||
|
||||
if(observer_prohibited(true)) {
|
||||
killme();
|
||||
}
|
||||
echo get_public_feed($channel, $params);
|
||||
|
||||
killme();
|
||||
|
||||
$channel = channelx_by_nick(argv(1));
|
||||
if(! $channel) {
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
logger('public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $channel['channel_address']);
|
||||
|
||||
echo get_public_feed($channel,$params);
|
||||
|
||||
killme();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -104,26 +104,15 @@ class Home extends Controller {
|
||||
goaway($frontpage);
|
||||
}
|
||||
|
||||
$o .= '<div class="generic-content-wrapper">';
|
||||
$sitename = Config::Get('system', 'sitename', 'Hubzilla');
|
||||
$welcome = sprintf(t('Welcome to %s'), $sitename);
|
||||
$login_on_homepage = Config::Get('system', 'login_on_homepage');
|
||||
|
||||
$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;
|
||||
$tpl = get_markup_template('home.tpl');
|
||||
return replace_macros($tpl, [
|
||||
'welcome' => $welcome,
|
||||
'loginbox' => $login_on_homepage ? login(true) : false,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ require_once('include/import.php');
|
||||
require_once('include/perm_upgrade.php');
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use URLify;
|
||||
use Zotlabs\Daemon\Master;
|
||||
use Zotlabs\Lib\Config;
|
||||
@@ -331,7 +332,7 @@ class Import extends Controller {
|
||||
else {
|
||||
$photos = import_xchan_photo($xchan['xchan_photo_l'], $xchan['xchan_hash']);
|
||||
if ($photos[4])
|
||||
$photodate = NULL_DATE;
|
||||
$photodate = DBA::$dba->get_null_date();
|
||||
else
|
||||
$photodate = $xchan['xchan_photo_date'];
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use URLify;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\IConfig;
|
||||
@@ -17,6 +18,7 @@ use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
use Zotlabs\Lib\ThreadListener;
|
||||
use Zotlabs\Access\PermissionRoles;
|
||||
use Zotlabs\Lib\ObjCache;
|
||||
|
||||
require_once('include/crypto.php');
|
||||
require_once('include/items.php');
|
||||
@@ -185,7 +187,8 @@ class Item extends Controller {
|
||||
$obj_type = ((!empty($_POST['obj_type'])) ? escape_tags($_POST['obj_type']) : 'Note');
|
||||
|
||||
// allow API to bulk load a bunch of imported items with sending out a bunch of posts.
|
||||
$nopush = ((!empty($_POST['nopush'])) ? intval($_POST['nopush']) : 0);
|
||||
$nopush = ((!empty($_POST['nopush'])) ? intval($_POST['nopush']) : $item_type !== ITEM_TYPE_POST);
|
||||
|
||||
|
||||
/*
|
||||
* Check service class limits
|
||||
@@ -207,7 +210,7 @@ class Item extends Controller {
|
||||
}
|
||||
|
||||
|
||||
$expires = NULL_DATE;
|
||||
$expires = DBA::$dba->get_null_date();
|
||||
|
||||
$route = '';
|
||||
$parent_item = null;
|
||||
@@ -557,7 +560,7 @@ class Item extends Controller {
|
||||
if (!empty($_POST['expire'])) {
|
||||
$expires = datetime_convert(date_default_timezone_get(), 'UTC', $_POST['expire']);
|
||||
if ($expires <= datetime_convert())
|
||||
$expires = NULL_DATE;
|
||||
$expires = DBA::$dba->get_null_date();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -755,7 +758,7 @@ class Item extends Controller {
|
||||
$cats = explode(',', $categories);
|
||||
foreach ($cats as $cat) {
|
||||
|
||||
$catlink = $owner_xchan['xchan_url'] . '?f=&cat=' . urlencode(trim($cat));
|
||||
$catlink = channel_url($channel) . '?cat=' . urlencode(trim($cat));
|
||||
|
||||
$post_tags[] = [
|
||||
'uid' => $profile_uid,
|
||||
@@ -799,7 +802,7 @@ class Item extends Controller {
|
||||
$item_origin = (($origin) ? 1 : 0);
|
||||
$item_consensus = (($consensus) ? 1 : 0);
|
||||
$item_nocomment = (($nocomment) ? 1 : 0);
|
||||
$comments_closed = (($nocomment) ? $comments_closed : NULL_DATE);
|
||||
$comments_closed = (($nocomment) ? $comments_closed : DBA::$dba->get_null_date());
|
||||
|
||||
// determine if this is a wall post
|
||||
|
||||
@@ -873,7 +876,7 @@ class Item extends Controller {
|
||||
|
||||
if ($obj['endTime']) {
|
||||
$d = datetime_convert('UTC','UTC', $obj['endTime']);
|
||||
if ($d > NULL_DATE) {
|
||||
if ($d > DBA::$dba->get_null_date()) {
|
||||
$comments_closed = $d;
|
||||
}
|
||||
}
|
||||
@@ -1041,23 +1044,16 @@ class Item extends Controller {
|
||||
|
||||
$x = item_store_update($datarray, $execflag);
|
||||
|
||||
if ($x['success']) {
|
||||
if ($x['success'] && intval($item_type) === ITEM_TYPE_POST) {
|
||||
$item = [$x['item']];
|
||||
xchan_query($item);
|
||||
$item = fetch_post_tags($item);
|
||||
$encoded_item = Activity::build_packet(Activity::encode_activity($item[0]), $channel, false);
|
||||
ObjCache::Set($item[0]['mid'], $encoded_item);
|
||||
|
||||
$this->add_listeners($datarray);
|
||||
}
|
||||
|
||||
/* sync this is done in item_store_update()
|
||||
if (!$parent) {
|
||||
$r = q("select * from item where id = %d",
|
||||
intval($post_id)
|
||||
);
|
||||
if ($r) {
|
||||
xchan_query($r);
|
||||
$sync_item = fetch_post_tags($r);
|
||||
Libsync::build_sync_packet($profile_uid, ['item' => [encode_item($sync_item[0], true)]]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (!$nopush) {
|
||||
Master::Summon(['Notifier', 'edit_post', $post_id]);
|
||||
if (intval($x['approval_id'])) {
|
||||
@@ -1080,9 +1076,17 @@ class Item extends Controller {
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
$post = item_store($datarray, $execflag);
|
||||
|
||||
if ($post['success']) {
|
||||
if ($post['success'] && intval($item_type) === ITEM_TYPE_POST) {
|
||||
$item = [$post['item']];
|
||||
xchan_query($item);
|
||||
// TODO: fetch_post_tags() will add term and iconfig twice if called twice and it looks like they are already added here
|
||||
//$item = fetch_post_tags($item);
|
||||
$encoded_item = Activity::build_packet(Activity::encode_activity($item[0]), $channel, false);
|
||||
ObjCache::Set($item[0]['mid'], $encoded_item);
|
||||
|
||||
$this->add_listeners($datarray);
|
||||
}
|
||||
|
||||
@@ -1162,19 +1166,6 @@ class Item extends Controller {
|
||||
killme();
|
||||
}
|
||||
|
||||
/* sync this is done in item_store_update()
|
||||
if ($parent || $datarray['item_private'] == 1) {
|
||||
$r = q("select * from item where id = %d",
|
||||
intval($post_id)
|
||||
);
|
||||
if ($r) {
|
||||
xchan_query($r);
|
||||
$sync_item = fetch_post_tags($r);
|
||||
Libsync::build_sync_packet($profile_uid, ['item' => [encode_item($sync_item[0], true)]]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
$datarray['id'] = $post_id;
|
||||
$datarray['llink'] = z_root() . '/display/' . $datarray['uuid'];
|
||||
|
||||
@@ -1213,11 +1204,6 @@ class Item extends Controller {
|
||||
if ($mode === 'channel')
|
||||
profile_load($channel['channel_address']);
|
||||
|
||||
$item[] = $datarray;
|
||||
$item[0]['owner'] = $owner_xchan;
|
||||
$item[0]['author'] = $observer;
|
||||
$item[0]['attach'] = $datarray['attach'];
|
||||
|
||||
$json = [
|
||||
'success' => 1,
|
||||
'id' => $post_id,
|
||||
|
||||
@@ -232,25 +232,17 @@ class Lockview extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
$access_list_header = '<div class="dropdown-header text-uppercase h6">' . t('Access') . '</div>';
|
||||
$guest_access_list_header = '<div class="dropdown-header text-uppercase h6">' . t('Guest access') . '</div>';
|
||||
$ocap_access_list_header = '<div class="dropdown-header text-uppercase h6">' . t('OCAP access') . '</div>';
|
||||
$divider = '<div class="dropdown-divider"></div>';
|
||||
$str = '';
|
||||
$tpl = get_markup_template('access_dropdown.tpl');
|
||||
|
||||
if ($access_list) {
|
||||
$str .= $access_list_header . implode($access_list);
|
||||
}
|
||||
echo replace_macros($tpl, [
|
||||
'$access_header' => t('Access'),
|
||||
'$guest_access_header' => t('Guest access'),
|
||||
'$ocap_access_header' => t('OCAP access'),
|
||||
|
||||
if ($guest_access_list) {
|
||||
$str .= $divider . $guest_access_list_header . implode($guest_access_list);
|
||||
}
|
||||
|
||||
if ($ocap_access_list) {
|
||||
$str .= $divider . $ocap_access_list_header . implode($ocap_access_list);
|
||||
}
|
||||
|
||||
echo $str;
|
||||
'$access_list' => $access_list ? implode($access_list) : '',
|
||||
'$guest_access_list' => $guest_access_list ? implode($guest_access_list) : '',
|
||||
'$ocap_access_list' => $ocap_access_list ? implode($ocap_access_list) : '',
|
||||
]);
|
||||
killme();
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Web\HTTPSig;
|
||||
use Zotlabs\Lib\Libzot;
|
||||
use Zotlabs\Lib\SConfig;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use HttpSignature\HttpMessageSigner;
|
||||
|
||||
class Magic extends Controller {
|
||||
|
||||
@@ -101,26 +103,65 @@ class Magic extends Controller {
|
||||
$dest = strip_zids($dest);
|
||||
$dest = strip_query_param($dest,'f');
|
||||
|
||||
// We now post to the OWA endpoint. This improves security by providing a signed digest
|
||||
// try RFC9421 first
|
||||
|
||||
$data = json_encode([ 'OpenWebAuth' => random_string() ]);
|
||||
$request = new Request(
|
||||
'GET',
|
||||
$owapath,
|
||||
[
|
||||
'Host' => $parsed['host'],
|
||||
'Date' => gmdate('D, d M Y H:i:s T'),
|
||||
'Accept' => 'application/x-zot+json',
|
||||
'X-Open-Web-Auth' => random_string(),
|
||||
],
|
||||
);
|
||||
|
||||
$headers = [];
|
||||
$headers['Accept'] = 'application/x-zot+json' ;
|
||||
$headers['Content-Type'] = 'application/x-zot+json' ;
|
||||
$headers['X-Open-Web-Auth'] = random_string();
|
||||
$headers['Host'] = $parsed['host'];
|
||||
$headers['(request-target)'] = 'get /owa';
|
||||
$signer = new HttpMessageSigner();
|
||||
|
||||
$signer->setPrivateKey($channel['channel_prvkey']);
|
||||
$signer->setAlgorithm('rsa-v1_5-sha256');
|
||||
$signer->setKeyId(channel_url($channel));
|
||||
$signer->setCreated(time());
|
||||
$signer->setExpires(time() + 3600);
|
||||
|
||||
$coveredFields = '("@method" "@target-uri" "host" "date" "accept" "x-open-web-auth")';
|
||||
$request = $signer->signRequest($coveredFields, $request);
|
||||
$signedHeaders = $signer->getHeaders($request);
|
||||
|
||||
$curlHeaders = [];
|
||||
foreach ($signedHeaders as $key => $value) {
|
||||
$curlHeaders[] = $key . ': ' . $value;
|
||||
}
|
||||
|
||||
$headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512');
|
||||
$redirects = 0;
|
||||
$x = z_fetch_url($owapath, false, $redirects, ['headers' => $curlHeaders]);
|
||||
logger('owa RFC9421 fetch returned: ' . print_r($x,true),LOGGER_DATA);
|
||||
|
||||
$x = z_fetch_url($owapath, false, $redirects, ['headers' => $headers]);
|
||||
$rfc9421 = false;
|
||||
|
||||
logger('owa fetch returned: ' . print_r($x,true),LOGGER_DATA);
|
||||
if ($x['success']) {
|
||||
$rfc9421_result = json_decode($x['body'], true);
|
||||
$rfc9421 = $rfc9421_result['success'];
|
||||
}
|
||||
|
||||
if (!$rfc9421 || ($x['return_code'] >= 400 && $x['return_code'] != 404)) {
|
||||
$headers = [];
|
||||
$headers['Accept'] = 'application/x-zot+json' ;
|
||||
$headers['Content-Type'] = 'application/x-zot+json' ;
|
||||
$headers['X-Open-Web-Auth'] = random_string();
|
||||
$headers['Host'] = $parsed['host'];
|
||||
$headers['(request-target)'] = 'get /owa';
|
||||
|
||||
$headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512');
|
||||
$redirects = 0;
|
||||
|
||||
$x = z_fetch_url($owapath, false, $redirects, ['headers' => $headers]);
|
||||
logger('owa fetch returned: ' . print_r($x,true),LOGGER_DATA);
|
||||
}
|
||||
|
||||
if ($x['success']) {
|
||||
$j = json_decode($x['body'],true);
|
||||
|
||||
if ($j['success'] && $j['encrypted_token']) {
|
||||
// decrypt the token using our private key
|
||||
$token = '';
|
||||
@@ -139,7 +180,6 @@ class Magic extends Controller {
|
||||
|
||||
echo $o;
|
||||
killme();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,8 +143,11 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
}
|
||||
|
||||
if(x($_GET, 'search') || $file || (!$pf && $cid) || $hashtags || $verb || $category || $conv || $unseen)
|
||||
if($search || $file || (!$pf && $cid) || $hashtags || $verb || $category || $conv || $unseen) {
|
||||
$nouveau = true;
|
||||
}
|
||||
|
||||
$dismiss_privacy_filter = array_intersect(['cid', 'star', 'conv', 'file', 'verb', 'cat', 'search'], array_keys($_GET));
|
||||
|
||||
$cid_r = [];
|
||||
|
||||
@@ -346,15 +349,15 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
// The name 'verb' is a holdover from the earlier XML
|
||||
// ActivityStreams specification.
|
||||
|
||||
if (substr($verb, 0, 1) === '.') {
|
||||
if (str_starts_with($verb, '.')) {
|
||||
$sql_verb = substr($verb, 1);
|
||||
$sql_extra .= sprintf(" AND item.obj_type like '%s' ",
|
||||
dbesc(protect_sprintf('%' . $sql_verb . '%'))
|
||||
$sql_extra .= sprintf(" AND item.obj_type = '%s' AND item.verb IN ('Create', 'Update', 'Invite') ",
|
||||
dbesc(protect_sprintf($sql_verb))
|
||||
);
|
||||
}
|
||||
else {
|
||||
$sql_extra .= sprintf(" AND item.verb like '%s' ",
|
||||
dbesc(protect_sprintf('%' . $verb . '%'))
|
||||
$sql_extra .= sprintf(" AND item.verb = '%s' ",
|
||||
dbesc(protect_sprintf($verb))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -363,13 +366,14 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
$sql_extra .= term_query('item', $file, TERM_FILE);
|
||||
}
|
||||
|
||||
if ($dm) {
|
||||
$sql_extra .= ' AND item.item_private = 2 ';
|
||||
if (!$dismiss_privacy_filter) {
|
||||
if ($dm) {
|
||||
$sql_extra .= ' AND item.item_private = 2 ';
|
||||
}
|
||||
else {
|
||||
$sql_extra .= ' AND item.item_private IN (0, 1) ';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$sql_extra .= ' AND item.item_private IN (0, 1) ';
|
||||
}
|
||||
|
||||
|
||||
if($conv) {
|
||||
$item_thread_top = '';
|
||||
|
||||
@@ -33,7 +33,7 @@ class New_channel extends \Zotlabs\Web\Controller {
|
||||
// first name
|
||||
if(strpos($x,' '))
|
||||
$test[] = legal_webbie(substr($x,0,strpos($x,' ')));
|
||||
if($test[0]) {
|
||||
if (!empty($test[0])) {
|
||||
// first name plus first initial of last
|
||||
$test[] = ((strpos($x,' ')) ? $test[0] . legal_webbie(trim(substr($x,strpos($x,' '),2))) : '');
|
||||
// first name plus random number
|
||||
@@ -69,7 +69,7 @@ class New_channel extends \Zotlabs\Web\Controller {
|
||||
// first name
|
||||
if(strpos($x,' '))
|
||||
$test[] = legal_webbie(substr($x,0,strpos($x,' ')));
|
||||
if($test[0]) {
|
||||
if (!empty($test[0])) {
|
||||
// first name plus first initial of last
|
||||
$test[] = ((strpos($x,' ')) ? $test[0] . legal_webbie(trim(substr($x,strpos($x,' '),2))) : '');
|
||||
// first name plus random number
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/contact_widgets.php');
|
||||
require_once('include/items.php');
|
||||
require_once("include/bbcode.php");
|
||||
require_once('include/security.php');
|
||||
require_once('include/conversation.php');
|
||||
require_once('include/acl_selectors.php');
|
||||
require_once('include/permissions.php');
|
||||
|
||||
/**
|
||||
* @brief Channel Controller for broken OStatus implementations
|
||||
*
|
||||
*/
|
||||
class Ochannel extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
$which = null;
|
||||
if(argc() > 1)
|
||||
$which = argv(1);
|
||||
if(! $which) {
|
||||
if(local_channel()) {
|
||||
$channel = \App::get_channel();
|
||||
if($channel && $channel['channel_address'])
|
||||
$which = $channel['channel_address'];
|
||||
}
|
||||
}
|
||||
if(! $which) {
|
||||
notice( t('You must be logged in to see this page.') . EOL );
|
||||
return;
|
||||
}
|
||||
|
||||
$profile = 0;
|
||||
$channel = \App::get_channel();
|
||||
|
||||
if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) {
|
||||
$which = $channel['channel_address'];
|
||||
$profile = argv(1);
|
||||
}
|
||||
|
||||
head_add_link( [
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'href' => z_root() . '/ofeed/' . $which
|
||||
]);
|
||||
|
||||
|
||||
// Run profile_load() here to make sure the theme is set before
|
||||
// we start loading content
|
||||
|
||||
profile_load($which,$profile);
|
||||
}
|
||||
|
||||
function get($update = 0, $load = false) {
|
||||
|
||||
if(argc() < 2)
|
||||
return;
|
||||
|
||||
if($load)
|
||||
$_SESSION['loadtime'] = datetime_convert();
|
||||
|
||||
return '<script>window.location.href = "' . z_root() . '/' . str_replace('ochannel/','channel/',\App::$query_string) . '";</script>';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
/* Ofeed: Broken feed for software which requires broken feeds */
|
||||
|
||||
require_once('include/items.php');
|
||||
|
||||
class Ofeed extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
$params = [];
|
||||
|
||||
$params['begin'] = ((x($_REQUEST,'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE);
|
||||
$params['end'] = ((x($_REQUEST,'date_end')) ? $_REQUEST['date_end'] : '');
|
||||
$params['type'] = ((stristr(argv(0),'json')) ? 'json' : 'xml');
|
||||
$params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0);
|
||||
$params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0);
|
||||
$params['start'] = ((x($_REQUEST,'start')) ? intval($_REQUEST['start']) : 0);
|
||||
$params['records'] = ((x($_REQUEST,'records')) ? intval($_REQUEST['records']) : 10);
|
||||
$params['direction'] = ((x($_REQUEST,'direction')) ? dbesc($_REQUEST['direction']) : 'desc');
|
||||
$params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : '');
|
||||
$params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 1);
|
||||
|
||||
if(! in_array($params['direction'],['asc','desc'])) {
|
||||
$params['direction'] = 'desc';
|
||||
}
|
||||
|
||||
if(argc() > 1) {
|
||||
|
||||
if(observer_prohibited(true)) {
|
||||
killme();
|
||||
}
|
||||
|
||||
$channel = channelx_by_nick(argv(1));
|
||||
if(! $channel) {
|
||||
killme();
|
||||
}
|
||||
|
||||
|
||||
logger('public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $channel['channel_address']);
|
||||
|
||||
echo get_public_feed($channel,$params);
|
||||
|
||||
killme();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\ActivityStreams;
|
||||
use Zotlabs\Lib\Config;
|
||||
@@ -47,7 +48,7 @@ class Outbox extends Controller {
|
||||
|
||||
$params = [];
|
||||
|
||||
$params['begin'] = ((x($_REQUEST, 'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE);
|
||||
$params['begin'] = ((x($_REQUEST, 'date_begin')) ? $_REQUEST['date_begin'] : DBA::$dba->get_null_date());
|
||||
$params['end'] = ((x($_REQUEST, 'date_end')) ? $_REQUEST['date_end'] : '');
|
||||
$params['type'] = 'json';
|
||||
$params['pages'] = ((x($_REQUEST, 'pages')) ? intval($_REQUEST['pages']) : 0);
|
||||
|
||||
@@ -19,96 +19,128 @@ use Zotlabs\Web\Controller;
|
||||
class Owa extends Controller {
|
||||
|
||||
public function init(): void
|
||||
{
|
||||
{
|
||||
|
||||
$ret = [ 'success' => false ];
|
||||
|
||||
if (!$this->validateAuthorizationHeader()) {
|
||||
$this->error('Missing or invalid authorization header.');
|
||||
// try OpenWebAuth over RFC9421
|
||||
|
||||
$sigdata = HTTPSig::verify(EMPTY_STR);
|
||||
|
||||
if ($sigdata && $sigdata['portable_id'] && $sigdata['header_valid']) {
|
||||
$portable_id = $sigdata['portable_id'];
|
||||
|
||||
if (!check_channelallowed($portable_id)) {
|
||||
json_return_and_die($ret, 'application/x-zot+json');
|
||||
}
|
||||
|
||||
if (!check_siteallowed($sigdata['signer'])) {
|
||||
json_return_and_die($ret, 'application/x-zot+json');
|
||||
}
|
||||
|
||||
$hubs = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE hubloc_hash = '%s' ORDER BY hubloc_id DESC",
|
||||
dbesc($portable_id)
|
||||
);
|
||||
|
||||
if ($hubs) {
|
||||
logger('OWA RFC9421 success: ' . $hubs[0]['hubloc_id_url'], LOGGER_DATA);
|
||||
$ret['success'] = true;
|
||||
$token = random_string(32);
|
||||
Verify::create('owt', 0, $token, $hubs[0]['hubloc_id_url']);
|
||||
$result = '';
|
||||
openssl_public_encrypt($token, $result, $hubs[0]['xchan_pubkey']);
|
||||
$ret['encrypted_token'] = base64url_encode($result);
|
||||
}
|
||||
}
|
||||
|
||||
$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_REMOTE_USER'];
|
||||
|
||||
$sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']);
|
||||
if ($sigblock) {
|
||||
$keyId = $sigblock['keyId'];
|
||||
$parsed = parse_url($keyId);
|
||||
if (str_starts_with($parsed['scheme'],'http')) {
|
||||
unset($parsed['fragment']);
|
||||
unset($parsed['query']);
|
||||
$keyId = unparse_url($parsed);
|
||||
else {
|
||||
if (!$this->validateAuthorizationHeader()) {
|
||||
$this->error('Missing or invalid authorization header.');
|
||||
}
|
||||
else {
|
||||
$keyId = str_replace('acct:', '', $keyId);
|
||||
}
|
||||
if ($keyId) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s')
|
||||
AND hubloc_deleted = 0 AND xchan_pubkey != ''
|
||||
ORDER BY hubloc_id DESC",
|
||||
dbesc($keyId),
|
||||
dbesc($keyId),
|
||||
dbesc($keyId)
|
||||
);
|
||||
if (! $r) {
|
||||
$found = discover_by_webbie($keyId);
|
||||
logger('found = ' . print_r($found, true));
|
||||
if ($found) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') AND hubloc_deleted = 0 AND xchan_pubkey != '' ORDER BY hubloc_id DESC ",
|
||||
dbesc($keyId),
|
||||
dbesc($keyId),
|
||||
dbesc($keyId)
|
||||
);
|
||||
}
|
||||
|
||||
$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_REMOTE_USER'];
|
||||
|
||||
$sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']);
|
||||
if ($sigblock) {
|
||||
$keyId = $sigblock['keyId'];
|
||||
$parsed = parse_url($keyId);
|
||||
if (str_starts_with($parsed['scheme'],'http')) {
|
||||
unset($parsed['fragment']);
|
||||
unset($parsed['query']);
|
||||
$keyId = unparse_url($parsed);
|
||||
}
|
||||
|
||||
if ($r) {
|
||||
foreach ($r as $hubloc) {
|
||||
$verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']);
|
||||
if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) {
|
||||
logger('OWA header: ' . print_r($verified,true),LOGGER_DATA);
|
||||
logger('OWA success: ' . $hubloc['hubloc_id_url'],LOGGER_DATA);
|
||||
$ret['success'] = true;
|
||||
$token = random_string(32);
|
||||
Verify::create('owt',0,$token,$hubloc['hubloc_id_url']);
|
||||
$result = '';
|
||||
openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']);
|
||||
$ret['encrypted_token'] = base64url_encode($result);
|
||||
break;
|
||||
} else {
|
||||
logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']);
|
||||
else {
|
||||
$keyId = str_replace('acct:', '', $keyId);
|
||||
}
|
||||
if ($keyId) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s')
|
||||
AND hubloc_deleted = 0 AND xchan_pubkey != ''
|
||||
ORDER BY hubloc_id DESC",
|
||||
dbesc($keyId),
|
||||
dbesc($keyId),
|
||||
dbesc($keyId)
|
||||
);
|
||||
if (! $r) {
|
||||
$found = discover_by_webbie($keyId);
|
||||
logger('found = ' . print_r($found, true));
|
||||
if ($found) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') AND hubloc_deleted = 0 AND xchan_pubkey != '' ORDER BY hubloc_id DESC ",
|
||||
dbesc($keyId),
|
||||
dbesc($keyId),
|
||||
dbesc($keyId)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$ret['success']) {
|
||||
if ($r) {
|
||||
foreach ($r as $hubloc) {
|
||||
$verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']);
|
||||
if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) {
|
||||
logger('OWA header: ' . print_r($verified,true),LOGGER_DATA);
|
||||
logger('OWA success: ' . $hubloc['hubloc_id_url'],LOGGER_DATA);
|
||||
$ret['success'] = true;
|
||||
$token = random_string(32);
|
||||
Verify::create('owt',0,$token,$hubloc['hubloc_id_url']);
|
||||
$result = '';
|
||||
openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']);
|
||||
$ret['encrypted_token'] = base64url_encode($result);
|
||||
break;
|
||||
} else {
|
||||
logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']);
|
||||
}
|
||||
}
|
||||
|
||||
// Possible a reinstall?
|
||||
// In this case we probably already have an old hubloc
|
||||
// but not the new one yet.
|
||||
if (!$ret['success']) {
|
||||
|
||||
$found = discover_by_webbie($keyId);
|
||||
// Possible a reinstall?
|
||||
// In this case we probably already have an old hubloc
|
||||
// but not the new one yet.
|
||||
|
||||
if ($found) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s') AND hubloc_deleted = 0 ORDER BY hubloc_id DESC LIMIT 1",
|
||||
dbesc(str_replace('acct:', '', $keyId)),
|
||||
dbesc($keyId)
|
||||
);
|
||||
$found = discover_by_webbie($keyId);
|
||||
|
||||
if ($r) {
|
||||
$verified = HTTPSig::verify(file_get_contents('php://input'), $r[0]['xchan_pubkey']);
|
||||
if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) {
|
||||
logger('OWA header: ' . print_r($verified,true), LOGGER_DATA);
|
||||
logger('OWA success: ' . $r[0]['hubloc_id_url'], LOGGER_DATA);
|
||||
$ret['success'] = true;
|
||||
$token = random_string(32);
|
||||
Verify::create('owt', 0, $token, $r[0]['hubloc_id_url']);
|
||||
$result = '';
|
||||
openssl_public_encrypt($token, $result, $r[0]['xchan_pubkey']);
|
||||
$ret['encrypted_token'] = base64url_encode($result);
|
||||
} else {
|
||||
logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']);
|
||||
if ($found) {
|
||||
$r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash
|
||||
WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s') AND hubloc_deleted = 0 ORDER BY hubloc_id DESC LIMIT 1",
|
||||
dbesc(str_replace('acct:', '', $keyId)),
|
||||
dbesc($keyId)
|
||||
);
|
||||
|
||||
if ($r) {
|
||||
$verified = HTTPSig::verify(file_get_contents('php://input'), $r[0]['xchan_pubkey']);
|
||||
if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) {
|
||||
logger('OWA header: ' . print_r($verified,true), LOGGER_DATA);
|
||||
logger('OWA success: ' . $r[0]['hubloc_id_url'], LOGGER_DATA);
|
||||
$ret['success'] = true;
|
||||
$token = random_string(32);
|
||||
Verify::create('owt', 0, $token, $r[0]['hubloc_id_url']);
|
||||
$result = '';
|
||||
openssl_public_encrypt($token, $result, $r[0]['xchan_pubkey']);
|
||||
$ret['encrypted_token'] = base64url_encode($result);
|
||||
} else {
|
||||
logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
79
Zotlabs/Module/Perfstats.php
Normal file
79
Zotlabs/Module/Perfstats.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/* Handler for perfstats requests.
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
|
||||
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\DbStats;
|
||||
use Zotlabs\Lib\Queue;
|
||||
use Zotlabs\Lib\QueueWorkerStats;
|
||||
use Zotlabs\Web\Controller;
|
||||
|
||||
/**
|
||||
* Controller for the `/perfstats` module.
|
||||
*
|
||||
* Collects various performance stats for the site, and reponds with the stats
|
||||
* as a json array.
|
||||
*/
|
||||
class Perfstats extends Controller
|
||||
{
|
||||
public function init(): void {
|
||||
//
|
||||
// We only accept GET requests
|
||||
//
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
http_status_exit(400, 'Unsupported method');
|
||||
}
|
||||
|
||||
//
|
||||
// We only accept json requests
|
||||
//
|
||||
if (getBestSupportedMimeType(['application/json']) === null) {
|
||||
http_status_exit(400, 'No supported format');
|
||||
}
|
||||
|
||||
//
|
||||
// Only admins should be given access
|
||||
//
|
||||
if (!is_site_admin()) {
|
||||
http_status(401, 'Access denied');
|
||||
json_return_and_die(['error' => 'access denied']);
|
||||
}
|
||||
|
||||
$data = $this->getStats();
|
||||
json_return_and_die($data);
|
||||
}
|
||||
|
||||
private function getStats(): array {
|
||||
$stats = [];
|
||||
|
||||
if (function_exists('sys_getloadavg')) {
|
||||
$stats['loadavg'] = sys_getloadavg();
|
||||
}
|
||||
|
||||
$stats['dbqueries'] = $this->getNumQueries();
|
||||
$stats['outqueue'] = Queue::count();
|
||||
|
||||
$qwstats = new QueueWorkerStats();
|
||||
$stats['queueworkers'] = $qwstats->active;
|
||||
$stats['workqsz'] = $qwstats->size;
|
||||
|
||||
// Return a timestamp, so that it is possible to infer
|
||||
// changes of the stats over time. A resolution of
|
||||
// seconds should be good enough for our purposes.
|
||||
$stats['ts'] = time();
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
private function getNumQueries(): int {
|
||||
$stats = DbStats::getStats();
|
||||
return $stats->getQueries();
|
||||
}
|
||||
}
|
||||
@@ -60,26 +60,16 @@ class Profile extends Controller {
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Posts and comments'),
|
||||
'href' => z_root() . '/feed/' . $which
|
||||
'href' => z_root() . '/feed/' . $which . '?top=0'
|
||||
]);
|
||||
|
||||
head_add_link([
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'title' => t('Only posts'),
|
||||
'href' => z_root() . '/feed/' . $which . '?f=&top=1'
|
||||
'href' => z_root() . '/feed/' . $which . '?top=1'
|
||||
]);
|
||||
|
||||
|
||||
if (!$profile) {
|
||||
$x = q("select channel_id as profile_uid from channel where channel_address = '%s' limit 1",
|
||||
dbesc(argv(1))
|
||||
);
|
||||
if ($x) {
|
||||
App::$profile = $x[0];
|
||||
}
|
||||
}
|
||||
|
||||
profile_load($which, $profile);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Lib\Libsync;
|
||||
|
||||
@@ -340,7 +341,7 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
$with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : '');
|
||||
|
||||
if(! strlen($howlong))
|
||||
$howlong = NULL_DATE;
|
||||
$howlong = DBA::$dba->get_null_date();
|
||||
else
|
||||
$howlong = datetime_convert(date_default_timezone_get(),'UTC',$howlong);
|
||||
|
||||
@@ -696,10 +697,9 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
$show_presence = ['show_presence', t('Reveal my online status'), $show_presence_val, '', [t('No'), t('Yes')]];
|
||||
}
|
||||
|
||||
$extra_fields = array();
|
||||
$q = q("select * from profdef where true");
|
||||
if($q) {
|
||||
$extra_fields = array();
|
||||
|
||||
foreach($q as $qq) {
|
||||
$mine = q("select v from profext where k = '%s' and hash = '%s' and channel_id = %d limit 1",
|
||||
dbesc($qq['field_name']),
|
||||
@@ -775,7 +775,7 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
'$marital' => marital_selector($r[0]['marital']),
|
||||
'$marital_min' => marital_selector_min($r[0]['marital']),
|
||||
'$with' => array('with', t("Who (if applicable)"), $r[0]['partner'], t('Examples: cathy123, Cathy Williams, cathy@example.com')),
|
||||
'$howlong' => array('howlong', t('Since (date)'), ($r[0]['howlong'] <= NULL_DATE ? '' : datetime_convert('UTC',date_default_timezone_get(),$r[0]['howlong']))),
|
||||
'$howlong' => array('howlong', t('Since (date)'), ($r[0]['howlong'] <= DBA::$dba->get_null_date() ? '' : datetime_convert('UTC',date_default_timezone_get(),$r[0]['howlong']))),
|
||||
'$sexual' => sexpref_selector($r[0]['sexual']),
|
||||
'$sexual_min' => sexpref_selector_min($r[0]['sexual']),
|
||||
'$about' => array('about', t('Tell us about yourself'), $r[0]['about']),
|
||||
@@ -833,6 +833,8 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
);
|
||||
if($r) {
|
||||
|
||||
$profiles = '';
|
||||
|
||||
$tpl = get_markup_template('profile_entry.tpl');
|
||||
foreach($r as $rr) {
|
||||
$profiles .= replace_macros($tpl, array(
|
||||
|
||||
@@ -44,6 +44,7 @@ class Profperm extends \Zotlabs\Web\Controller {
|
||||
if($switchtotext === false)
|
||||
$switchtotext = 400;
|
||||
|
||||
$change = 0;
|
||||
|
||||
if((argc() > 2) && intval(argv(1)) && intval(argv(2))) {
|
||||
$r = q("SELECT abook_id FROM abook WHERE abook_id = %d and abook_channel = %d limit 1",
|
||||
@@ -74,10 +75,11 @@ class Profperm extends \Zotlabs\Web\Controller {
|
||||
dbesc($profile['profile_guid'])
|
||||
);
|
||||
|
||||
$ingroup = array();
|
||||
if($r)
|
||||
$ingroup = [];
|
||||
if($r) {
|
||||
foreach($r as $member)
|
||||
$ingroup[] = $member['abook_id'];
|
||||
}
|
||||
|
||||
$members = $r;
|
||||
|
||||
@@ -104,68 +106,57 @@ class Profperm extends \Zotlabs\Web\Controller {
|
||||
);
|
||||
|
||||
$members = $r;
|
||||
|
||||
$ingroup = array();
|
||||
if(count($r))
|
||||
$ingroup = [];
|
||||
if($r) {
|
||||
foreach($r as $member)
|
||||
$ingroup[] = $member['abook_id'];
|
||||
}
|
||||
|
||||
$o .= '<h2>' . t('Profile Visibility Editor') . '</h2>';
|
||||
|
||||
$o .= '<h3>' . t('Profile') . ' \'' . $profile['profile_name'] . '\'</h3>';
|
||||
|
||||
$o .= '<div id="prof-edit-desc">' . t('Click on a contact to add or remove.') . '</div>';
|
||||
|
||||
}
|
||||
|
||||
$o .= '<div id="prof-update-wrapper">';
|
||||
if($change)
|
||||
$o = '';
|
||||
|
||||
$o .= '<div id="prof-members-title">';
|
||||
$o .= '<h3>' . t('Visible To') . '</h3>';
|
||||
$o .= '</div>';
|
||||
$o .= '<div id="prof-members">';
|
||||
|
||||
$textmode = (($switchtotext && (count($members) > $switchtotext)) ? true : false);
|
||||
|
||||
foreach($members as $member) {
|
||||
if($member['xchan_url']) {
|
||||
$member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;';
|
||||
$o .= micropro($member,true,'mpprof', $textmode);
|
||||
}
|
||||
}
|
||||
$o .= '</div><div id="prof-members-end"></div>';
|
||||
$o .= '<hr id="prof-separator" />';
|
||||
|
||||
$o .= '<div id="prof-all-contcts-title">';
|
||||
$o .= '<h3>' . t("All Connections") . '</h3>';
|
||||
$o .= '</div>';
|
||||
$o .= '<div id="prof-all-contacts">';
|
||||
|
||||
$r = abook_connections(local_channel());
|
||||
|
||||
if($r) {
|
||||
$textmode = (($switchtotext && (count($r) > $switchtotext)) ? true : false);
|
||||
foreach($r as $member) {
|
||||
if(! in_array($member['abook_id'],$ingroup)) {
|
||||
$member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;';
|
||||
$o .= micropro($member,true,'mpprof',$textmode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '</div><div id="prof-all-contacts-end"></div>';
|
||||
$o .= '<h2>' . t('Profile Visibility Editor') . '</h2>';
|
||||
$o .= '<h3>' . t('Profile') . ' \'' . $profile['profile_name'] . '\'</h3>';
|
||||
$o .= '<div id="prof-edit-desc">' . t('Click on a contact to add or remove.') . '</div>';
|
||||
}
|
||||
|
||||
// Build template data
|
||||
$members_tpl = [];
|
||||
$textmode = (($switchtotext && (count($members) > $switchtotext)) ? true : false);
|
||||
if($members) {
|
||||
foreach($members as $member) {
|
||||
if($member['xchan_url']) {
|
||||
$member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;';
|
||||
$members_tpl[] = [ 'micro' => micropro($member, true, 'mpprof', $textmode) ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$all_members_tpl = [];
|
||||
$r = abook_connections(local_channel());
|
||||
if($r) {
|
||||
$textmode = (($switchtotext && (count($r) > $switchtotext)) ? true : false);
|
||||
foreach($r as $member) {
|
||||
if(! in_array($member['abook_id'], $ingroup)) {
|
||||
$member['click'] = 'profChangeMember(' . $profile['id'] . ',' . $member['abook_id'] . '); return false;';
|
||||
$all_members_tpl[] = [ 'micro' => micropro($member, true, 'mpprof', $textmode) ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use tpl for the inner part
|
||||
$inner_html = replace_macros(get_markup_template('profile_members.tpl'), [
|
||||
'$visible_to' => t('Visible To'),
|
||||
'$all_connections' => t('All Connections'),
|
||||
'$members' => $members_tpl,
|
||||
'$all_members' => $all_members_tpl,
|
||||
]);
|
||||
|
||||
if($change) {
|
||||
echo $o;
|
||||
echo $inner_html;
|
||||
killme();
|
||||
}
|
||||
$o .= '</div>';
|
||||
|
||||
$o .= $inner_html;
|
||||
return $o;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -247,11 +247,7 @@ class Pubstream extends \Zotlabs\Web\Controller {
|
||||
|
||||
if($r) {
|
||||
$items = items_by_parent_ids($r, blog_mode: $blog_mode);
|
||||
|
||||
// use effective_uid param of xchan_query to help sort out comment permission
|
||||
// for sys_channel owned items.
|
||||
|
||||
xchan_query($items, true, local_channel());
|
||||
xchan_query($items);
|
||||
$items = fetch_post_tags($items,true);
|
||||
$items = conv_sort($items, $ordering);
|
||||
}
|
||||
|
||||
@@ -142,7 +142,15 @@ class Regate extends \Zotlabs\Web\Controller {
|
||||
|
||||
if (($flags & ACCOUNT_PENDING ) == ACCOUNT_PENDING) {
|
||||
$nextpage = 'regate/' . bin2hex($did2) . $didx;
|
||||
q("COMMIT");
|
||||
$approve = send_reg_approval_email_from_register($r['reg_id']);
|
||||
if ($approve['success']) {
|
||||
q("COMMIT");
|
||||
} else {
|
||||
q("ROLLBACK");
|
||||
$msg_code = 'ZAR1237E';
|
||||
$msg = t('Account verification notify error');
|
||||
zar_log($msg_code . ' ' . $msg . ': ' . print_r($approve, true));
|
||||
}
|
||||
}
|
||||
elseif (($flags ^ REGISTER_AGREED) == 0) {
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
|
||||
class Removeaccount extends \Zotlabs\Web\Controller {
|
||||
|
||||
@@ -29,7 +30,7 @@ class Removeaccount extends \Zotlabs\Web\Controller {
|
||||
if(! ($x && $x['account']))
|
||||
return;
|
||||
|
||||
if($account['account_password_changed'] > NULL_DATE) {
|
||||
if($account['account_password_changed'] > DBA::$dba->get_null_date()) {
|
||||
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
|
||||
if($account['account_password_changed'] > $d1) {
|
||||
notice( t('Account removals are not allowed within 48 hours of changing the account password.') . EOL);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use DBA;
|
||||
|
||||
class Removeme extends \Zotlabs\Web\Controller {
|
||||
|
||||
@@ -29,7 +30,7 @@ class Removeme extends \Zotlabs\Web\Controller {
|
||||
if(! ($x && $x['account']))
|
||||
return;
|
||||
|
||||
if($account['account_password_changed'] > NULL_DATE) {
|
||||
if($account['account_password_changed'] > DBA::$dba->get_null_date()) {
|
||||
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
|
||||
if($account['account_password_changed'] > $d1) {
|
||||
notice( t('Channel removals are not allowed within 48 hours of changing the account password.') . EOL);
|
||||
|
||||
@@ -42,9 +42,7 @@ class Search extends Controller {
|
||||
$observer = App::get_observer();
|
||||
$observer_hash = (($observer) ? $observer['xchan_hash'] : '');
|
||||
|
||||
$o = '<div class="generic-content-wrapper-styled">' . "\r\n";
|
||||
|
||||
$o .= '<h2>' . t('Search') . '</h2>';
|
||||
$title = t('Search');
|
||||
|
||||
if (x(App::$data, 'search'))
|
||||
$search = trim(App::$data['search']);
|
||||
@@ -57,7 +55,7 @@ class Search extends Controller {
|
||||
$search = ((x($_GET, 'tag')) ? trim(escape_tags(rawurldecode($_GET['tag']))) : '');
|
||||
}
|
||||
|
||||
$o .= search($search, 'search-box', '/search', ((local_channel()) ? true : false));
|
||||
$searchbox = search($search, 'search-box', '/search', ((local_channel()) ? true : false));
|
||||
|
||||
if (local_channel() && str_starts_with($search, 'https://') && !$update && !$load) {
|
||||
|
||||
@@ -121,8 +119,17 @@ class Search extends Controller {
|
||||
goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search);
|
||||
}
|
||||
|
||||
if (!$search)
|
||||
return $o;
|
||||
if (!$search) {
|
||||
$tpl = get_markup_template('search.tpl');
|
||||
|
||||
return replace_macros($tpl, [
|
||||
'$title' => $title,
|
||||
'$searchbox' => $searchbox,
|
||||
'$livesearch' => '',
|
||||
'$results_header' => '',
|
||||
'$conversation' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
if ($tag) {
|
||||
$wildtag = str_replace('*', '%', $search);
|
||||
@@ -143,14 +150,15 @@ class Search extends Controller {
|
||||
// OR your own posts if you are a logged in member
|
||||
// No items will be shown if the member has a blocked profile wall.
|
||||
|
||||
$livesearch = '';
|
||||
|
||||
if ((!$update) && (!$load)) {
|
||||
|
||||
// This is ugly, but we can't pass the profile_uid through the session to the ajax updater,
|
||||
// because browser prefetching might change it on us. We have to deliver it with the page.
|
||||
|
||||
$o .= '<div id="live-search"></div>' . "\r\n";
|
||||
$o .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1))
|
||||
$livesearch .= '<div id="live-search"></div>' . "\r\n";
|
||||
$livesearch .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1))
|
||||
. "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . "; </script>\r\n";
|
||||
|
||||
App::$page['htmlhead'] = replace_macros(get_markup_template("build_query.tpl"), [
|
||||
@@ -254,15 +262,21 @@ class Search extends Controller {
|
||||
}
|
||||
|
||||
if ($tag)
|
||||
$o .= '<h2>' . sprintf(t('Items tagged with: %s'), $search) . '</h2>';
|
||||
$results_header = sprintf(t('Items tagged with: %s'), $search);
|
||||
else
|
||||
$o .= '<h2>' . sprintf(t('Search results for: %s'), $search) . '</h2>';
|
||||
$results_header = sprintf(t('Search results for: %s'), $search);
|
||||
|
||||
$o .= conversation($items, 'search', $update, 'client');
|
||||
$conversation = conversation($items, 'search', $update, 'client');
|
||||
|
||||
$o .= '</div>';
|
||||
$tpl = get_markup_template('search.tpl');
|
||||
|
||||
return $o;
|
||||
return replace_macros($tpl, [
|
||||
'$title' => $title,
|
||||
'$searchbox' => $searchbox,
|
||||
'$livesearch' => $livesearch,
|
||||
'$results_header' => $results_header ?? '',
|
||||
'$conversation' => $conversation,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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,6 +17,9 @@ class Siteinfo extends \Zotlabs\Web\Controller {
|
||||
$federated = [];
|
||||
call_hooks('federated_transports',$federated);
|
||||
|
||||
$themes = Config::Get('system', 'allowed_themes');
|
||||
$themes = array_map('trim', explode(',', $themes));
|
||||
|
||||
$siteinfo = replace_macros(get_markup_template('siteinfo.tpl'),
|
||||
[
|
||||
'$title' => t('About this site'),
|
||||
@@ -40,6 +43,7 @@ class Siteinfo extends \Zotlabs\Web\Controller {
|
||||
'$prj_link' => \Zotlabs\Lib\System::get_project_link(),
|
||||
'$prj_src' => \Zotlabs\Lib\System::get_project_srclink(),
|
||||
'$addons' => array( t('Active addons'), \App::$plugins ),
|
||||
'$themes' => array( t('Active themes'), $themes ),
|
||||
'$blocked_sites' => array( t('Blocked sites'), \Zotlabs\Lib\Config::Get('system', 'blacklisted_sites') )
|
||||
]
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use Zotlabs\Lib\Config;
|
||||
use Zotlabs\Web\Controller;
|
||||
@@ -136,7 +137,7 @@ class Sse extends Controller {
|
||||
|
||||
session_reset();
|
||||
|
||||
XConfig::Set(self::$ob_hash, 'sse', 'timestamp', NULL_DATE);
|
||||
XConfig::Set(self::$ob_hash, 'sse', 'timestamp', DBA::$dba->get_null_date());
|
||||
XConfig::Set(self::$ob_hash, 'sse', 'notifications', []);
|
||||
|
||||
if (ob_get_length() > 0) {
|
||||
|
||||
@@ -131,7 +131,7 @@ class Sse_bs extends Controller {
|
||||
$str = '';
|
||||
$slice = 0;
|
||||
|
||||
$mids_all = isset($_SESSION['sse_mids_all']) ? unserialise($_SESSION['sse_mids_all']) : [];
|
||||
$mids_all = isset($_SESSION['sse_mids_all']) ? json_unserialize($_SESSION['sse_mids_all']) : [];
|
||||
|
||||
if (count($mids_all) > 3000) {
|
||||
$slice = count($mids_all) - 3000;
|
||||
@@ -177,7 +177,7 @@ class Sse_bs extends Controller {
|
||||
$mids_all = array_merge($mids_all, $activities_arr);
|
||||
}
|
||||
|
||||
$_SESSION['sse_mids_all'] = serialise(array_unique($mids_all));
|
||||
$_SESSION['sse_mids_all'] = json_serialize(array_unique($mids_all));
|
||||
|
||||
if(! self::$uid) {
|
||||
return;
|
||||
@@ -502,7 +502,7 @@ class Sse_bs extends Controller {
|
||||
$sql_extra2 = " AND CASE WHEN item.verb = '" . ACTIVITY_SHARE . "' THEN item.owner_xchan ELSE item.author_xchan END IN (" . self::$xchans . ") ";
|
||||
|
||||
$sql_extra3 = '';
|
||||
$sse_mids_all = isset($_SESSION['sse_mids_all']) ? unserialise($_SESSION['sse_mids_all']) : [];
|
||||
$sse_mids_all = isset($_SESSION['sse_mids_all']) ? json_unserialize($_SESSION['sse_mids_all']) : [];
|
||||
if ($sse_mids_all) {
|
||||
$sql_extra3 = " AND item.uuid NOT IN (" . protect_sprintf(implode(',', $sse_mids_all)) . ") ";
|
||||
}
|
||||
|
||||
@@ -3,21 +3,21 @@ namespace Zotlabs\Module;
|
||||
|
||||
require_once('include/event.php');
|
||||
|
||||
|
||||
use DBA;
|
||||
|
||||
class Tasks extends \Zotlabs\Web\Controller {
|
||||
|
||||
function init() {
|
||||
|
||||
|
||||
|
||||
|
||||
// logger('request: ' . print_r($_REQUEST,true));
|
||||
|
||||
|
||||
$arr = array();
|
||||
|
||||
if(argc() > 1 && argv(1) === 'fetch') {
|
||||
|
||||
if(argc() > 1 && argv(1) === 'fetch') {
|
||||
if(argc() > 2 && argv(2) === 'all')
|
||||
$arr['all'] = 1;
|
||||
|
||||
|
||||
$x = tasks_fetch($arr);
|
||||
$x['html'] = '';
|
||||
if($x['tasks']) {
|
||||
@@ -53,7 +53,7 @@ class Tasks extends \Zotlabs\Web\Controller {
|
||||
$event = $r[0];
|
||||
if($event['event_status'] === 'COMPLETED') {
|
||||
$event['event_status'] = 'IN-PROCESS';
|
||||
$event['event_status_date'] = NULL_DATE;
|
||||
$event['event_status_date'] = DBA::$dba->get_null_date();
|
||||
$event['event_percent'] = 0;
|
||||
$event['event_sequence'] = $event['event_sequence'] + 1;
|
||||
$event['edited'] = datetime_convert();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use DBA;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use Zotlabs\Lib\AccessList;
|
||||
@@ -84,7 +85,7 @@ class Tokens extends Controller {
|
||||
if(trim($_POST['expires']))
|
||||
$expires = datetime_convert(date_default_timezone_get(),'UTC',$_POST['expires']);
|
||||
else
|
||||
$expires = NULL_DATE;
|
||||
$expires = DBA::$dba->get_null_date();
|
||||
$max_atokens = service_class_fetch($channel['channel_id'],'access_tokens');
|
||||
if($max_atokens) {
|
||||
$r = q("select count(atoken_id) as total where atoken_uid = %d",
|
||||
@@ -290,7 +291,7 @@ class Tokens extends Controller {
|
||||
'$atoken' => $atoken,
|
||||
'$name' => array('name', t('Login Name') . ' <span class="required">*</span>', $atoken['atoken_name'] ?? '',''),
|
||||
'$token'=> array('token', t('Login Password') . ' <span class="required">*</span>', $atoken['atoken_token'] ?? new_token(), ''),
|
||||
'$expires'=> array('expires', t('Expires (yyyy-mm-dd)'), ((isset($atoken['atoken_expires']) && $atoken['atoken_expires'] > NULL_DATE) ? datetime_convert('UTC',date_default_timezone_get(),$atoken['atoken_expires']) : ''), ''),
|
||||
'$expires'=> array('expires', t('Expires (yyyy-mm-dd)'), ((isset($atoken['atoken_expires']) && $atoken['atoken_expires'] > DBA::$dba->get_null_date()) ? datetime_convert('UTC',date_default_timezone_get(),$atoken['atoken_expires']) : ''), ''),
|
||||
'$submit' => t('Submit'),
|
||||
'$delete' => t('Delete')
|
||||
));
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
|
||||
use Zotlabs\Lib\Activity;
|
||||
use Zotlabs\Lib\IConfig;
|
||||
use Zotlabs\Lib\ObjCache;
|
||||
|
||||
class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
|
||||
@@ -28,13 +30,16 @@ class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
$item_normal = item_normal_search();
|
||||
|
||||
if(local_channel() && $item_id) {
|
||||
$r = q("select id, mid, uuid, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1",
|
||||
$r = q("select * from item where uid in (%d , %d) and id = %d $item_normal limit 1",
|
||||
intval(local_channel()),
|
||||
intval($sys['channel_id']),
|
||||
intval($item_id)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
xchan_query($r, true);
|
||||
$r = fetch_post_tags($r);
|
||||
|
||||
if(intval($r[0]['item_obscured']))
|
||||
$dload = true;
|
||||
|
||||
@@ -45,15 +50,41 @@ class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
killme();
|
||||
}
|
||||
|
||||
$cached = true;
|
||||
|
||||
$content = escape_tags($r[0]['body']);
|
||||
$o = (($json) ? json_encode($content) : $content);
|
||||
$obj = ObjCache::Get($r[0]['mid']);
|
||||
|
||||
if (!$obj) {
|
||||
$obj = IConfig::Get($r[0], 'activitypub', 'rawmsg');
|
||||
}
|
||||
|
||||
if (in_array($r[0]['owner']['xchan_network'], ['diaspora'])) {
|
||||
$obj = ObjCache::Get($r[0]['mid'], 'diaspora');
|
||||
|
||||
if (!$obj) {
|
||||
$obj = IConfig::Get($r[0], 'diaspora', 'fields');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$obj) {
|
||||
$cached = false;
|
||||
$obj = Activity::encode_activity($r[0]);
|
||||
}
|
||||
|
||||
if ($obj) {
|
||||
$content = (($cached) ? 'Cached: ' : '') . '<pre>' . escape_tags(json_encode($obj, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) . '</pre>';
|
||||
}
|
||||
else {
|
||||
$content = escape_tags($r[0]['body']);
|
||||
}
|
||||
|
||||
$o = (($json) ? json_encode($content) : str_replace("\n", '<br>', $content));
|
||||
}
|
||||
}
|
||||
|
||||
if(is_ajax()) {
|
||||
echo '<div class="p-1">';
|
||||
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a><br>mid: ' . $r[0]['mid'] . '<br>uuid: ' . $r[0]['uuid'] . '</div>';
|
||||
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a><br>mid: ' . $r[0]['mid'] . '<br>hashpath: ' . hash('sha256', $r[0]['mid']) . '<br>uuid: ' . $r[0]['uuid'] . '</div>';
|
||||
echo '<hr>';
|
||||
echo '<pre class="p-1">' . $o . '</pre>';
|
||||
echo '</div>';
|
||||
|
||||
@@ -115,9 +115,10 @@ abstract class PhotoDriver {
|
||||
*/
|
||||
public function __construct($data, $type = '') {
|
||||
$this->types = $this->supportedTypes();
|
||||
if(! array_key_exists($type, $this->types)) {
|
||||
if(!$type || !array_key_exists($type, $this->types)) {
|
||||
$type = 'image/jpeg';
|
||||
}
|
||||
|
||||
$this->type = $type;
|
||||
$this->valid = false;
|
||||
$this->load($data, $type);
|
||||
|
||||
@@ -63,10 +63,13 @@ class PhotoGd extends PhotoDriver {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief GD imagedestroy() is deprected and noop since PHP version 8.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function destroy() {
|
||||
if($this->is_valid()) {
|
||||
imagedestroy($this->image);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,8 +98,6 @@ class PhotoGd extends PhotoDriver {
|
||||
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
|
||||
imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
|
||||
if($this->image)
|
||||
imagedestroy($this->image);
|
||||
|
||||
$this->image = $dest;
|
||||
$this->setDimensions();
|
||||
@@ -142,8 +143,6 @@ class PhotoGd extends PhotoDriver {
|
||||
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
|
||||
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h);
|
||||
if($this->image)
|
||||
imagedestroy($this->image);
|
||||
|
||||
$this->image = $dest;
|
||||
$this->setDimensions();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Zotlabs\Photo;
|
||||
|
||||
use Imagick;
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
/**
|
||||
@@ -16,8 +17,14 @@ class PhotoImagick extends PhotoDriver {
|
||||
'image/png' => 'png',
|
||||
'image/gif' => 'gif'
|
||||
];
|
||||
if(\Imagick::queryFormats("WEBP"))
|
||||
|
||||
if (Imagick::queryFormats('WEBP')) {
|
||||
$ret['image/webp'] = 'webp';
|
||||
}
|
||||
|
||||
if (Imagick::queryFormats('AVIF')) {
|
||||
$ret['image/avif'] = 'avif';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class SmartyTemplate implements TemplateEngine {
|
||||
if ($root != '' && substr($root,-1) != '/' ) {
|
||||
$root .= '/';
|
||||
}
|
||||
foreach ( [ $root . "view/$lang/$file", $root . "view/en/$file", '' ] as $template_file) {
|
||||
foreach ( [ $root . "view/lang/$lang/$file", $root . "view/lang/en/$file", '' ] as $template_file) {
|
||||
if (is_file($template_file)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ class Browser extends DAV\Browser\Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
$display_path_encoded = Text::rawurlencode_parts($data['display_path']);
|
||||
$display_path_encoded = Text::rawurlencode_parts($data['display_path'] ?? '');
|
||||
$href_encoded = Text::rawurlencode_parts($href);
|
||||
|
||||
// put the array for this file together
|
||||
@@ -272,7 +272,7 @@ class Browser extends DAV\Browser\Plugin {
|
||||
$ft['rel_path'] = (($data) ? '/cloud/' . $nick .'/' . $display_path_encoded : $href_encoded);
|
||||
$ft['full_path'] = z_root() . (($data) ? '/cloud/' . $nick .'/' . $display_path_encoded : $href_encoded);
|
||||
$ft['name'] = $name;
|
||||
$ft['type'] = $type;
|
||||
$ft['type'] = $data['filetype'];
|
||||
$ft['size'] = $size;
|
||||
$ft['collection'] = (($type === 'Collection') ? true : false);
|
||||
$ft['size_formatted'] = userReadableSize($size);
|
||||
|
||||
@@ -519,8 +519,8 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
|
||||
* @return void
|
||||
*/
|
||||
function getDir() {
|
||||
|
||||
logger('GetDir: ' . $this->ext_path, LOGGER_DEBUG);
|
||||
|
||||
$this->auth->log();
|
||||
$modulename = \App::$module;
|
||||
|
||||
@@ -538,7 +538,6 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
|
||||
$file = trim($file, '/');
|
||||
$path_arr = explode('/', $file);
|
||||
|
||||
|
||||
if (! $path_arr)
|
||||
return;
|
||||
|
||||
@@ -558,9 +557,9 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
|
||||
$this->auth->owner_id = $channel_id;
|
||||
$this->auth->owner_nick = $channel_name;
|
||||
|
||||
$path = '/' . $channel_name;
|
||||
$folder = '';
|
||||
$os_path = '';
|
||||
$not_found = '';
|
||||
|
||||
for ($x = 1; $x < count($path_arr); $x++) {
|
||||
$r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0",
|
||||
@@ -568,15 +567,31 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
|
||||
dbesc($path_arr[$x]),
|
||||
intval($channel_id)
|
||||
);
|
||||
|
||||
if ($r && intval($r[0]['is_dir'])) {
|
||||
$folder = $r[0]['hash'];
|
||||
if (strlen($os_path))
|
||||
$os_path .= '/';
|
||||
$os_path .= $folder;
|
||||
|
||||
$path = $path . '/' . $r[0]['filename'];
|
||||
if (strlen($os_path)) {
|
||||
$os_path .= '/';
|
||||
}
|
||||
|
||||
$os_path .= $folder;
|
||||
}
|
||||
else {
|
||||
// if we got a bogus path collect the
|
||||
if (strlen($not_found)) {
|
||||
$not_found .= ',';
|
||||
}
|
||||
|
||||
$not_found .= $path_arr[$x];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($not_found) {
|
||||
throw new DAV\Exception\NotFound("Path $file does not exist. One or more directories could not be found: $not_found");
|
||||
}
|
||||
|
||||
$this->folder_hash = $folder;
|
||||
$this->os_path = $os_path;
|
||||
}
|
||||
|
||||
@@ -54,9 +54,6 @@ class Epubthumb {
|
||||
imagecopyresampled($dest, $image, 0, 0, 0, 0, $width, $height, $srcwidth, $srcheight);
|
||||
|
||||
imagejpeg($dest, "{$file}.thumb");
|
||||
|
||||
imagedestroy($image);
|
||||
imagedestroy($dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ class Mp3audio {
|
||||
imagealphablending($dest, false);
|
||||
imagesavealpha($dest, true);
|
||||
imagecopyresampled($dest, $image, 0, 0, 0, 0, $width, $height, $srcwidth, $srcheight);
|
||||
imagedestroy($image);
|
||||
imagejpeg($dest,dbunescbin($attach['content']) . '.thumb');
|
||||
}
|
||||
}
|
||||
|
||||
30
Zotlabs/Update/_1265.php
Normal file
30
Zotlabs/Update/_1265.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Update;
|
||||
|
||||
class _1265 {
|
||||
|
||||
function run() {
|
||||
|
||||
dbq("START TRANSACTION");
|
||||
|
||||
|
||||
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
|
||||
$r = dbq("ALTER TABLE attach ALTER COLUMN filetype TYPE VARCHAR(128), ALTER COLUMN filetype SET NOT NULL, ALTER COLUMN filetype SET DEFAULT ''");
|
||||
}
|
||||
|
||||
if(ACTIVE_DBTYPE == DBTYPE_MYSQL) {
|
||||
$r = dbq("ALTER TABLE attach CHANGE filetype filetype CHAR(128) NOT NULL DEFAULT ''");
|
||||
}
|
||||
|
||||
if($r) {
|
||||
dbq("COMMIT");
|
||||
return UPDATE_SUCCESS;
|
||||
}
|
||||
|
||||
q("ROLLBACK");
|
||||
return UPDATE_FAILED;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,8 @@ namespace Zotlabs\Widget;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use Zotlabs\Lib\Queue;
|
||||
use Zotlabs\Lib\QueueWorkerStats;
|
||||
|
||||
class Channel_activities {
|
||||
|
||||
@@ -25,12 +27,14 @@ class Channel_activities {
|
||||
self::$uid = local_channel();
|
||||
self::$channel = App::get_channel();
|
||||
|
||||
$o = '<div id="channel-activities" class="d-none overflow-hidden">';
|
||||
$o .= '<h2 class="mb-4">' . t('Welcome') . ' ' . self::$channel['channel_name'] . '!</h2>';
|
||||
//$o .= 'Last login date: ' . get_pconfig(self::$uid, 'system', 'stored_login_date') . ' from ' . get_pconfig(self::$uid, 'system', 'stored_login_addr');
|
||||
|
||||
if (is_site_admin()) {
|
||||
self::get_system_status();
|
||||
}
|
||||
self::get_photos_activity();
|
||||
self::get_files_activity();
|
||||
self::get_files_activity('uncategorized');
|
||||
self::get_files_activity('document');
|
||||
self::get_files_activity('audio');
|
||||
self::get_files_activity('video');
|
||||
self::get_webpages_activity();
|
||||
self::get_channels_activity();
|
||||
|
||||
@@ -42,28 +46,36 @@ class Channel_activities {
|
||||
|
||||
call_hooks('channel_activities_widget', $hookdata);
|
||||
|
||||
if (!$hookdata['activities']) {
|
||||
$o .= '<h3>' . t('No recent activities') . '</h3>';
|
||||
$o .= '</div>';
|
||||
return $o;
|
||||
$activity_html = '';
|
||||
|
||||
if ($hookdata['activities']) {
|
||||
$keys = array_column($hookdata['activities'], 'date');
|
||||
array_multisort($keys, SORT_DESC, $hookdata['activities']);
|
||||
|
||||
foreach ($hookdata['activities'] as $a) {
|
||||
$activity_html .= replace_macros(
|
||||
get_markup_template($a['tpl']),
|
||||
[
|
||||
'$url' => $a['url'] ?? null,
|
||||
'$icon' => $a['icon'],
|
||||
'$label' => $a['label'],
|
||||
'$items' => $a['items'],
|
||||
'$labels' => $a['labels'] ?? [],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$keys = array_column($hookdata['activities'], 'date');
|
||||
$tpl = get_markup_template('channel_activities_widget.tpl');
|
||||
|
||||
array_multisort($keys, SORT_DESC, $hookdata['activities']);
|
||||
return replace_macros($tpl, [
|
||||
'$welcome' => t('Welcome'),
|
||||
'$channel_name' => self::$channel['channel_name'],
|
||||
'$no_activities' => t('No recent activities'),
|
||||
'$activities' => $hookdata['activities'],
|
||||
'$activity_html' => $activity_html
|
||||
]);
|
||||
|
||||
foreach($hookdata['activities'] as $a) {
|
||||
$o .= replace_macros(get_markup_template($a['tpl']), [
|
||||
'$url' => $a['url'],
|
||||
'$icon' => $a['icon'],
|
||||
'$label' => $a['label'],
|
||||
'$items' => $a['items']
|
||||
]);
|
||||
}
|
||||
|
||||
$o .= '</div>';
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
private static function get_photos_activity() {
|
||||
@@ -100,10 +112,28 @@ class Channel_activities {
|
||||
|
||||
}
|
||||
|
||||
private static function get_files_activity() {
|
||||
private static function get_files_activity($category) {
|
||||
|
||||
$not = '';
|
||||
$mime_types = stringify_array(self::get_mime_types_by_category($category));
|
||||
|
||||
switch($category) {
|
||||
case 'audio':
|
||||
$label = t('Audios');
|
||||
break;
|
||||
case 'video':
|
||||
$label = t('Videos');
|
||||
break;
|
||||
case 'document':
|
||||
$label = t('Documents');
|
||||
break;
|
||||
default:
|
||||
$label = t('Uploads');
|
||||
$not = 'NOT';
|
||||
}
|
||||
|
||||
$r = q("SELECT * FROM attach WHERE uid = %d
|
||||
AND is_dir = 0 AND is_photo = 0
|
||||
AND is_dir = 0 AND is_photo = 0 AND filetype $not IN ($mime_types)
|
||||
ORDER BY edited DESC LIMIT %d",
|
||||
intval(self::$uid),
|
||||
intval(self::$limit)
|
||||
@@ -121,8 +151,8 @@ class Channel_activities {
|
||||
];
|
||||
}
|
||||
|
||||
self::$activities['files'] = [
|
||||
'label' => t('Files'),
|
||||
self::$activities[$category] = [
|
||||
'label' => $label,
|
||||
'icon' => 'folder',
|
||||
'url' => z_root() . '/cloud/' . self::$channel['channel_address'],
|
||||
'date' => $r[0]['edited'],
|
||||
@@ -132,6 +162,129 @@ class Channel_activities {
|
||||
|
||||
}
|
||||
|
||||
private static function get_mime_types_by_category($category): array
|
||||
{
|
||||
$mime_types = [
|
||||
'document' => [
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.sun.xml.writer',
|
||||
'application/vnd.oasis.opendocument.text',
|
||||
'application/vnd.oasis.opendocument.text-flat-xml',
|
||||
'application/vnd.sun.xml.calc',
|
||||
'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'application/vnd.oasis.opendocument.spreadsheet-flat-xml',
|
||||
'application/vnd.sun.xml.impress',
|
||||
'application/vnd.oasis.opendocument.presentation',
|
||||
'application/vnd.oasis.opendocument.presentation-flat-xml',
|
||||
'application/vnd.sun.xml.draw',
|
||||
'application/vnd.oasis.opendocument.graphics',
|
||||
'application/vnd.oasis.opendocument.graphics-flat-xml',
|
||||
'application/vnd.oasis.opendocument.chart',
|
||||
'application/vnd.sun.xml.writer.global',
|
||||
'application/vnd.oasis.opendocument.text-master',
|
||||
'application/vnd.sun.xml.writer.template',
|
||||
'application/vnd.oasis.opendocument.text-template',
|
||||
'application/vnd.oasis.opendocument.text-master-template',
|
||||
'application/vnd.sun.xml.calc.template',
|
||||
'application/vnd.oasis.opendocument.spreadsheet-template',
|
||||
'application/vnd.sun.xml.impress.template',
|
||||
'application/vnd.oasis.opendocument.presentation-template',
|
||||
'application/vnd.sun.xml.draw.template',
|
||||
'application/vnd.oasis.opendocument.graphics-template',
|
||||
'application/msword',
|
||||
'application/msword',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.ms-word.document.macroEnabled.12',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'application/vnd.ms-word.template.macroEnabled.12',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
'application/vnd.ms-excel.template.macroEnabled.12',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||
'application/vnd.ms-excel.sheet.macroEnabled.12',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.template',
|
||||
'application/vnd.ms-powerpoint.template.macroEnabled.12',
|
||||
'application/vnd.wordperfect',
|
||||
'application/x-aportisdoc',
|
||||
'application/x-hwp',
|
||||
'application/vnd.ms-works',
|
||||
'application/vnd.ms-office',
|
||||
'application/x-mswrite',
|
||||
'application/x-dif-document',
|
||||
'text/spreadsheet',
|
||||
'application/x-dbase',
|
||||
'application/vnd.lotus-1-2-3',
|
||||
'application/coreldraw',
|
||||
'application/vnd.visio2013',
|
||||
'application/vnd.visio',
|
||||
'application/vnd.ms-visio.drawing',
|
||||
'application/x-mspublisher',
|
||||
'application/x-sony-bbeb',
|
||||
'application/x-gnumeric',
|
||||
'application/macwriteii',
|
||||
'application/x-iwork-numbers-sffnumbers',
|
||||
'application/vnd.oasis.opendocument.text-web',
|
||||
'application/x-pagemaker',
|
||||
'text/rtf',
|
||||
'text/plain',
|
||||
'application/x-fictionbook+xml',
|
||||
'application/clarisworks',
|
||||
'application/x-iwork-pages-sffpages',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||
'application/x-iwork-keynote-sffkey',
|
||||
'application/x-abiword',
|
||||
'application/vnd.sun.xml.chart',
|
||||
'application/x-t602',
|
||||
'application/pdf',
|
||||
],
|
||||
|
||||
'audio' => [
|
||||
'audio/mpeg', // MP3
|
||||
'audio/mp3',
|
||||
'audio/wav', // WAV
|
||||
'audio/x-wav',
|
||||
'audio/webm', // WebM audio
|
||||
'audio/ogg', // OGG
|
||||
'audio/aac', // AAC
|
||||
'audio/flac', // FLAC
|
||||
'audio/x-flac',
|
||||
'audio/mp4', // M4A / MP4 audio
|
||||
'audio/x-m4a',
|
||||
'audio/3gpp', // 3GP audio
|
||||
'audio/3gpp2',
|
||||
'audio/amr', // AMR
|
||||
'audio/x-ms-wma', // Windows Media Audio
|
||||
'audio/basic', // µ-law / basic audio
|
||||
],
|
||||
|
||||
'video' => [
|
||||
'video/mp4', // MP4
|
||||
'video/x-msvideo', // AVI
|
||||
'video/x-ms-wmv', // WMV
|
||||
'video/mpeg', // MPEG
|
||||
'video/ogg', // OGG/Theora
|
||||
'video/webm', // WebM
|
||||
'video/3gpp', // 3GP
|
||||
'video/3gpp2',
|
||||
'video/quicktime', // MOV
|
||||
'video/x-flv', // Flash Video
|
||||
'video/x-matroska', // MKV
|
||||
'video/mp2t', // MPEG-TS (.ts)
|
||||
]
|
||||
];
|
||||
|
||||
if ($category === 'uncategorized') {
|
||||
return array_merge(...array_values($mime_types));
|
||||
}
|
||||
|
||||
return $mime_types[$category];
|
||||
}
|
||||
|
||||
private static function get_webpages_activity() {
|
||||
|
||||
if(!Apps::system_app_installed(self::$uid, 'Webpages')) {
|
||||
@@ -220,11 +373,17 @@ class Channel_activities {
|
||||
$footer .= intval($notices[0]['total']) . ' ' . tt('notice', 'notices', intval($notices[0]['total']), 'noun');
|
||||
}
|
||||
|
||||
$tpl = get_markup_template('manage_channel_item.tpl');
|
||||
|
||||
$i[] = [
|
||||
'url' => z_root() . '/manage/' . $rr['channel_id'],
|
||||
'title' => '',
|
||||
'summary' => '<div class="text-truncate lh-sm"><img src="' . $rr['xchan_photo_s'] . '" class="menu-img-2">' . '<strong>' . $rr['channel_name'] . '</strong><br><small class="text-body-secondary">' . $rr['xchan_addr'] . '</small></div>',
|
||||
'footer' => $footer
|
||||
'url' => z_root() . '/manage/' . $rr['channel_id'],
|
||||
'title' => '',
|
||||
'summary' => replace_macros($tpl, [
|
||||
'$photo' => $rr['xchan_photo_s'],
|
||||
'$name' => $rr['channel_name'],
|
||||
'$addr' => $rr['xchan_addr'],
|
||||
]),
|
||||
'footer' => $footer
|
||||
];
|
||||
|
||||
$channels_activity++;
|
||||
@@ -246,5 +405,29 @@ class Channel_activities {
|
||||
|
||||
}
|
||||
|
||||
private static function get_system_status(): void {
|
||||
self::$activities['status'] = [
|
||||
'label' => t('System status'),
|
||||
'icon' => 'gpu-card',
|
||||
'date' => datetime_convert(),
|
||||
'items' => [
|
||||
'loadavg' => '0 / 0 / 0',
|
||||
'dbqueries' => 0,
|
||||
'outqueue' => 0,
|
||||
'queueworkers' => 0,
|
||||
'workqsz' => 0,
|
||||
'ts' => time(),
|
||||
],
|
||||
'tpl' => 'system_status_widget.tpl',
|
||||
'labels' => [
|
||||
'loadavg' => t('Load average'),
|
||||
'dbqueries' => t('DB queries/sec'),
|
||||
'outqueue' => t('Output queue'),
|
||||
'queueworkers' => t('Queue workers'),
|
||||
'workqsz' => t('Work queue size'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -8,15 +8,18 @@
|
||||
|
||||
namespace Zotlabs\Widget;
|
||||
|
||||
use App;
|
||||
|
||||
class Fullprofile {
|
||||
|
||||
function widget($arr) {
|
||||
|
||||
if(!(isset(\App::$profile['profile_uid']) && \App::$profile['profile_uid']))
|
||||
if (empty(App::$profile['profile_uid'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$block = observer_prohibited();
|
||||
|
||||
return profile_sidebar(\App::$profile, $block, true, true);
|
||||
return profile_sidebar(App::$profile, $block, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,10 +312,10 @@ class Messages {
|
||||
$author_sql = " AND notify.url = '" . protect_sprintf(dbesc($author_url)) . "' ";
|
||||
}
|
||||
|
||||
$notices = q("SELECT notify.*, xchan.xchan_addr FROM notify
|
||||
LEFT JOIN xchan ON notify.url = xchan.xchan_url
|
||||
WHERE uid = %d $author_sql
|
||||
GROUP BY notify.id ORDER BY created DESC LIMIT $limit OFFSET $offset",
|
||||
$notices = q("SELECT notify.*, max(hubloc.hubloc_addr) as hubloc_addr FROM notify
|
||||
LEFT JOIN hubloc ON notify.url = hubloc.hubloc_id_url
|
||||
WHERE notify.uid = %d $author_sql AND hubloc.hubloc_primary = 1
|
||||
GROUP BY notify.id ORDER BY notify.created DESC LIMIT $limit OFFSET $offset",
|
||||
intval(local_channel())
|
||||
);
|
||||
|
||||
@@ -331,7 +331,7 @@ class Messages {
|
||||
}
|
||||
|
||||
$entries[$i]['author_name'] = $notice['xname'];
|
||||
$entries[$i]['author_addr'] = $notice['xchan_addr'];
|
||||
$entries[$i]['author_addr'] = $notice['hubloc_addr'];
|
||||
$entries[$i]['author_img'] = $notice['photo'];// $item['author']['xchan_photo_s'];
|
||||
$entries[$i]['info'] = '';
|
||||
$entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $notice['created']);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Zotlabs\Widget;
|
||||
|
||||
use DBA;
|
||||
use Zotlabs\Lib\Config;
|
||||
|
||||
/**
|
||||
@@ -60,7 +61,7 @@ class Pinned {
|
||||
$profile_link = chanlink_hash($item['author_xchan']);
|
||||
$profile_name = $author['xchan_name'];
|
||||
|
||||
$commentable = ($item['item_nocomment'] == 0 && $item['comments_closed'] == NULL_DATE ? true : false);
|
||||
$commentable = ($item['item_nocomment'] == 0 && $item['comments_closed'] == DBA::$dba->get_null_date() ? true : false);
|
||||
|
||||
$location = format_location($item);
|
||||
$isevent = false;
|
||||
@@ -131,7 +132,7 @@ class Pinned {
|
||||
'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'),
|
||||
'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'),
|
||||
'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r') ) : ''),
|
||||
'expiretime' => ($item['expires'] > NULL_DATE ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r') ) : ''),
|
||||
'expiretime' => ($item['expires'] > DBA::$dba->get_null_date() ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r') ) : ''),
|
||||
'verified' => $verified,
|
||||
'forged' => $forged,
|
||||
'location' => $location,
|
||||
|
||||
@@ -13,7 +13,7 @@ use App;
|
||||
|
||||
class Profile {
|
||||
function widget($args) {
|
||||
if(!isset(App::$profile['profile_uid'])) {
|
||||
if (empty(App::$profile['profile_uid'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user