Compare commits

...

725 Commits
10.2.2 ... 10.6

Author SHA1 Message Date
Mario
f1def0c03a Merge branch '10.6RC' 2025-11-04 09:26:29 +00:00
Mario
b8a5ebe0f7 version 10.6 2025-11-04 09:24:43 +00:00
Mario
24a9ad6260 Merge branch 'dev' into 10.6RC 2025-11-03 21:12:42 +00:00
Mario
88577e1e97 Fix encoding for webpage, layout and block title and body when editing - issue #1946 2025-11-03 21:12:04 +00:00
Mario
38225aabc9 Merge branch 'dev' into 10.6RC 2025-11-03 20:43:12 +00:00
Mario
3653ae1af0 fix typo in sql query and some missing data which prevented files and photos to be updated correctly via dav - issue #1944 2025-11-03 09:31:45 +00:00
Mario
f2aa42f18a fix typo 2025-11-03 08:51:21 +00:00
Mario
aa5bd9bbfc do not allow observer posting files to wall if they do not have permission - issue #1940 part two 2025-11-03 08:50:39 +00:00
Mario
42f75842c6 Merge branch 'dev' into 10.6RC 2025-10-30 21:46:41 +00:00
Mario
73fafd9f2b update changelog 2025-10-30 21:46:13 +00:00
Mario
13c51e519d Merge branch 'dev' into 10.6RC 2025-10-30 21:42:38 +00:00
Mario
ef05cecaeb make sure to only call init_contact_edit() on pageload if we have a fragment and are in /connections. this is to prevent fragment hijacking for other pages 2025-10-30 21:37:41 +00:00
Mario
5b6d70fd60 Revert "make sure to only call init_contact_edit() on pageload if we have a fragment and are in /connections. this is to prevent fragment hijacking for other pages"
This reverts commit cfb8bf3ab9.
2025-10-30 21:37:08 +00:00
Mario
cfb8bf3ab9 make sure to only call init_contact_edit() on pageload if we have a fragment and are in /connections. this is to prevent fragment hijacking for other pages 2025-10-30 21:32:30 +00:00
Mario Vavti
3cf7e09609 switch from mmccooks php-json-canonicalization library to root23s which, according to mike, better deals with floating point values 2025-10-30 21:30:18 +01:00
Mario Vavti
a127f76bad Merge branch 'dev' into 10.6RC 2025-10-30 19:37:25 +01:00
Mario Vavti
d11b05de71 unserialise is being called in the get method now 2025-10-30 19:36:01 +01:00
Mario
a03b440139 Merge branch 'dev' into 10.6RC 2025-10-30 10:12:31 +00:00
Mario
2865dc45eb update changelog 2025-10-30 10:12:06 +00:00
Mario
923e80d143 fix merge conflict 2025-10-30 09:58:55 +00:00
Mario
ba96b2ce85 Merge branch 'upgrade-ci-php-82' into 'dev'
CI: Upgrade images to php8.2

See merge request hubzilla/core!2239
2025-10-30 09:41:12 +00:00
Mario
d9f467f215 Merge branch 'fix-test-deprecation' into 'dev'
Fix deprecation notice in MessageWidgetTest

See merge request hubzilla/core!2238
2025-10-30 09:39:44 +00:00
Harald Eilertsen
23a8c2f0a8 CI: Upgrade images to php8.2
PHP 8.1 is is reaching end-of-life in about two months, so to prepare
for setting PHP 8.2 as minimum supported version we upgrade the CI
runners right away.

PostgreSQL 13 is also reaching EOL in mid november, so upgrade that as
well.
2025-10-29 22:21:51 +01:00
Mario
76856188e1 remove not in abook restriction for convo operations. it is not required anymore since we run convo on fetched items only 2025-10-29 11:02:53 +00:00
Harald Eilertsen
f3d8838256 Fix deprecation notice in MessageWidgetTest
Creating dynamic properties in an object is deprecated in PHP 8.2.

Reference: https://www.php.net/manual/en/migration82.deprecated.php
2025-10-29 10:33:06 +01:00
Mario
2871ef2897 fix another case where the whole URL was punified 2025-10-28 11:55:32 +00:00
Mario
aa7358c837 bump version 2025-10-27 21:13:13 +00:00
Mario
57b553a79f no need for is_sys_channel() here - we already have the complete channel. Saves a DB lookup. 2025-10-27 21:12:20 +00:00
Mario
f08498440a further improve how and in which cases we will attempt fetching the replies collection 2025-10-27 21:03:26 +00:00
Mario
e9c07c554a move the connected check to Activity::init_background_fetch() to omit redundant abook queries in each Activity::store() 2025-10-26 20:32:43 +00:00
Mario Vavti
a570ef4691 add test for ASCache::isCacheable() 2025-10-26 19:19:36 +01:00
Mario
7cf7aa397e refactor Lib/ASCache so that we can easily determine cacheable activities (we will not cache limited activities anymore), refactor the Daemon/Convo so that we will not waste a process for each channel (similar to Daemon/Fetchparents) 2025-10-26 17:43:10 +00:00
Mario
8ba378bc6f Merge branch 'dev' into 10.6RC 2025-10-24 19:01:13 +00:00
Mario
03af9d415f Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-10-24 18:37:08 +00:00
Mario
e5acf404ab punify() will turn everything to lower case. We should just use it on the host part, otherwise case senstive URLs will be rendered useless 2025-10-24 18:36:27 +00:00
Mario Vavti
2e89cfe5ed fix pattern to make tests happy again 2025-10-24 20:19:55 +02:00
Mario
21951ec5ee update changelog 2025-10-24 13:36:08 +00:00
Mario
10786336c5 Merge branch 'dev' into 10.6RC 2025-10-24 09:58:48 +00:00
Mario
9e33fa8ee2 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-10-24 09:52:52 +00:00
Mario
1658244b09 only match a single whitespace at the end when pasting a quote, lookup url for plink in question activities 2025-10-24 09:52:28 +00:00
Mario Vavti
3c60e33d74 fix rc version 2025-10-23 14:47:58 +02:00
Mario Vavti
5fb28cb51c fix setup checks 2025-10-23 14:46:04 +02:00
Mario
1677c664f0 bump dev version 2025-10-23 09:09:22 +00:00
Mario
d35a79515f version and strings 2025-10-23 09:06:01 +00:00
Mario
872fced729 bump version 2025-10-22 18:32:03 +00:00
Mario
4ae65a63c6 photo_upload(): moderate photo items where observer has write_storage perm but not post_wall perm - fix issue #1940 2025-10-22 18:31:49 +00:00
Mario
acc43bbbad fix not all notification icons updated and use anon function shortcut for consistency 2025-10-22 16:54:25 +00:00
Mario
947d827431 bump version 2025-10-22 07:35:59 +00:00
Mario
339f787b92 retrieve available notification types from dom element instead of passing them around (which had issues) 2025-10-22 07:35:29 +00:00
Mario
c71a6dbbd2 phpstan: remove conditional constants from ignoreErrors for level 0 2025-10-22 06:45:01 +00:00
Mario
cd5815dcdc install libgmp-dev in gitlab-ci 2025-10-22 06:38:09 +00:00
Mario
19c478421d add gmp extension to gitlab-ci 2025-10-22 06:21:13 +00:00
Mario
a5f311665c bump version 2025-10-21 20:53:06 +00:00
Mario
d8c14eec58 fix issue with notification on screen size where notifications are hidden and isVisible would always return false 2025-10-21 20:52:31 +00:00
Mario Vavti
4798fa79c3 update composer libs - requires gmp extension (in favor of bcmath) 2025-10-21 22:06:03 +02:00
Mario Vavti
b50aa43542 remove redundant args 2025-10-21 21:09:13 +02:00
Mario Vavti
6bf1b15666 remove redundant args 2025-10-21 21:08:28 +02:00
Mario Vavti
9d4f2c7817 email change logic is in Settings/Account 2025-10-21 19:05:27 +02:00
Mario Vavti
040b52a1d5 fix undefined constant 2025-10-21 18:57:15 +02:00
Mario
c8c9e4a901 fix some of the constant not found errors 2025-10-21 16:00:06 +00:00
Mario Vavti
b4f6241357 composer update macgirvin/http-message-signer to version 0.2.6 2025-10-21 16:01:28 +02:00
Mario
4ba414fb8d HTTPSig: return early if we got no key info 2025-10-21 10:23:47 +00:00
Mario
33718ff16f fix error in case when no forums are available 2025-10-21 10:02:43 +00:00
Mario
19437f45ef Merge branch 'helpindex-fix' into 'dev'
add support for json formatted table of contents and specific tpl for helpindex

See merge request hubzilla/core!2237
2025-10-21 09:31:38 +00:00
Harald Eilertsen
7a7ff97e63 Unbreak test failure in Helpindex test
We need to mock the call to `is_readable`, since the file requested in
the test does not actually exist in the file system.
2025-10-20 10:54:12 +02:00
Mario
ef7cedca2d implement ASCache in ASCollection to possibly prevent timeouts when fetching large collections 2025-10-19 09:07:16 +00:00
SK
cbb2cabd74 corrections with accordion id and data-bs-parent 2025-10-19 14:28:47 +05:30
SK
f7cc249b50 better accordion 2025-10-19 08:55:55 +05:30
SK
9530b5b313 removed else conditional 2025-10-19 08:31:45 +05:30
SK
f91409a9ec removed unused toc.html 2025-10-19 08:18:53 +05:30
SK
4330e0bb94 add support for json formatted table of contents and specific tpl
Allows for precise control over layout of table of contents so that
theme specific changes can be implemented. Introduced a new layout file
help-index.tpl.
Table of contents is stored in json format within toc.json, support for
toc.html is retained since other languages still use it but can be
migrated to json if needed.
2025-10-19 08:18:43 +05:30
Mario
6ad561dc4c remove deprecated conv_list.tpl 2025-10-17 05:49:36 +00:00
Mario
70c7817a5c fix undefined variable 2025-10-16 13:11:39 +00:00
Mario
cc9237c7ce more work on notification: mark seen for pubstream, hidden configs for inverted notifications order and count limit (issue #1917) 2025-10-16 12:08:29 +00:00
Mario
e75e8ed8f1 add missing changelog 2025-10-16 08:40:51 +00:00
Mario
2e85852a9c fix en string as per @ferret 2025-10-16 08:20:55 +00:00
Mario
c49a646bef fix undefined var warning 2025-10-16 08:15:24 +00:00
Mario
c130c3ee45 enable filters and mark-all-seen button to forum notifications 2025-10-16 07:55:05 +00:00
Mario
c96fe501ef bump version 2025-10-15 16:52:06 +00:00
Mario
2f0f7b9df6 make sure we add the channel address to the url field 2025-10-15 16:51:08 +00:00
Mario
9fec396fa2 whitespace 2025-10-15 06:41:15 +00:00
Mario
bf774caf0e we should probably also handle image/gif here 2025-10-15 06:37:43 +00:00
Mario
f08b0edad2 only request quality for types that have support for it. otherwise getQuality() will choke with
UnhandledMatchError
2025-10-15 06:35:42 +00:00
Mario Vavti
63e41c233f the correct mime type for .jpg images is image/jpeg 2025-10-15 07:55:08 +02:00
Mario
48433c9479 Merge branch 'some-photo-cleanup' into 'dev'
Some cleanup in Zotlabs\Photo\PhotoGd

See merge request hubzilla/core!2236
2025-10-14 06:28:04 +00:00
Mario
4691bd37c9 Merge branch 'compile-templates-in-ci' into 'dev'
Improve compile_smarty3 script, and add CI check

See merge request hubzilla/core!2235
2025-10-14 06:22:06 +00:00
Mario
d24a3a2713 fix issue where not all seen items where removed from the notifications 2025-10-13 19:58:12 +00:00
Harald Eilertsen
57af09ad47 Simplify PhotoGd::imageString
Since the validity of the image type is checked upon construction, we
can assume that the functions for writing the valid types are already
available, and we don't need to check it again here.
2025-10-13 18:49:17 +02:00
Harald Eilertsen
c5fc4827c8 Refactor PhotoGd::imageString to reduce complexity 2025-10-13 18:47:46 +02:00
Harald Eilertsen
3c89c7d4f4 Add ImageQuality class to hold quality values 2025-10-13 18:43:13 +02:00
Harald Eilertsen
2a3dc6cc50 Fix trivial phpcs warnings in PhotoGD 2025-10-13 17:05:19 +02:00
Mario
c748d3f0f8 remove logging 2025-10-13 06:14:39 +00:00
Mario
9b2ea44000 fix regression setting the offset and fix js error if notify_menu is null (can happen when forum notifications are ddisabled) 2025-10-13 06:11:24 +00:00
Mario
83f6846143 properly implement forum notifications 2025-10-12 15:29:50 +00:00
Harald Eilertsen
3b2311f356 CI: Check that templates compile
Introduce a quick test to check that templates compile in the pretest
stage. This is to prevent merge requests unintentionally breaking
templates like in issue #1938.

https://framagit.org/hubzilla/core/-/issues/1938
2025-10-11 10:54:27 +02:00
Harald Eilertsen
ea0229dcfc Return with error code if precompile_smarty fails
The Smarty API for compiling templates don't return any meaningful error
status if a template fails, so we buffer the output and scan it for
error messages instead. If we find one, we return a non-zero error code
to signal the error to the shell or calling process.

Also added more detailed copyright headers.
2025-10-10 22:22:39 +02:00
Mario
3cdfa94c29 announced toplevel items will have type Announce in our case - possible fix for issue #1936 2025-10-10 15:47:49 +00:00
Mario
2fd5762125 wrong array key 2025-10-10 15:41:54 +00:00
Mario
52ecdb007f use space instead of EOL constant which contains HTML 2025-10-10 09:01:00 +00:00
Mario
9b8fed738b escape sequence must not contain spaces 2025-10-10 09:00:09 +00:00
Mario
ee3fafc9fb Merge branch 'ci-pretest-and-upgrade-psql' into 'dev'
CI: Add pretest CI step and run PHPStan

See merge request hubzilla/core!2233
2025-10-09 09:53:14 +00:00
Mario
47867a0993 Merge branch 'fix-phpunit-deprecation' into 'dev'
tests: Dataproviders should be static

See merge request hubzilla/core!2232
2025-10-09 09:51:52 +00:00
Mario Vavti
a2ba81afe1 version 10.4.4 2025-10-06 23:27:14 +02:00
Mario
a15254a1e2 remove check for current user and apply update 2025-10-06 23:16:22 +02:00
Mario
1a68dbccf1 remove check for current user and apply update 2025-10-06 20:42:27 +00:00
Harald Eilertsen
fc87ef282e CI: Upgrade to postgres 13
Postgres 12 has been EOL for almost a year!
2025-10-05 23:07:02 +02:00
Harald Eilertsen
ab0e5e3eee CI: Add pretest CI step and run PHPStan
The idea is to avoid the more expensive phpunit tests if a set of
simpler checks fail. These should check the code without actually
running it. For now we only run PHPStan, but this may expand in the
future.
2025-10-05 23:03:42 +02:00
Harald Eilertsen
356b6127d3 Better check if default_timezone is set 2025-10-05 23:03:42 +02:00
Harald Eilertsen
87c62db41e tests: Dataproviders should be static 2025-10-05 20:27:08 +02:00
Mario
0d3855cd63 whitespace 2025-10-05 15:48:21 +00:00
Mario Vavti
ffce0a705b added test for Activity::pasteQuote() 2025-10-05 15:43:42 +02:00
Mario
49e65902d7 improve placing quote in post and move it to separate function - test will follow 2025-10-05 13:19:26 +00:00
Mario
2e1fd79c7a implement as cache in get_quote() 2025-10-05 07:46:22 +00:00
Mario
0c577b5fd0 replace both variants 2025-10-04 20:22:10 +00:00
Mario
3178a96a89 improved quote handling 2025-10-04 18:49:21 +00:00
Mario
c1cec623bb this restriction should not be required anymore 2025-10-04 18:47:08 +00:00
Mario
d3cc098070 Revert "this restriction should not be required anymore"
This reverts commit 69e7812646.
2025-10-04 18:45:53 +00:00
Mario
69e7812646 this restriction should not be required anymore 2025-10-04 18:44:07 +00:00
Mario
d39320475c remove logging 2025-10-03 16:08:37 +00:00
Mario
0faf47b3ca deal with multiple quoteUrl's - just in case 2025-10-03 16:07:45 +00:00
Mario
660ff3da7b for consistency show webfinger addr instead of url like in the other tabs 2025-10-03 06:51:23 +00:00
Mario
7ec8a2267a we should also store the terms of a quote post for custom emojis, tags, etc. 2025-10-03 05:39:43 +00:00
Mario
4a35764e13 we must decode the entire message for attached images etc. 2025-10-02 17:35:38 +00:00
Mario
3a50b185ce fix heading
(cherry picked from commit 57e2904cc0)

Co-authored-by: Mario <mario@mariovavti.com>
2025-10-02 10:02:37 +00:00
Mario
57e2904cc0 fix heading 2025-10-02 10:01:38 +00:00
Mario
77866625cc fix path
(cherry picked from commit b49c844a23)

Co-authored-by: Mario <mario@mariovavti.com>
2025-10-02 09:58:06 +00:00
Mario
b49c844a23 fix path 2025-10-02 09:57:31 +00:00
Mario
f6275fdb79 Ãadd en tos which seems to have been lost in transition
(cherry picked from commit 2c55ee5a73)

Co-authored-by: Mario <mario@mariovavti.com>
2025-10-02 09:52:05 +00:00
Mario
2c55ee5a73 Ãadd en tos which seems to have been lost in transition 2025-10-02 09:17:07 +00:00
Mario
548b0c4931 Merge branch 'misc-phpstan-fixes' into 'dev'
Fix remaining issues flagged by PHPStan level 0

See merge request hubzilla/core!2228
2025-10-02 08:36:00 +00:00
Mario
8c61f89a14 get_quote_bbcode() > get_quote() 2025-10-02 08:16:39 +00:00
Mario
7cc0cb78a0 inbound support for mastodon quote posts and slightly improved rendering 2025-10-02 07:13:45 +00:00
Mario
6aa945cde1 Merge branch 'cherry-pick-357eabbe' into 'dev'
Update Spanish strings

See merge request hubzilla/core!2229
2025-10-01 09:25:38 +00:00
Mario
375b3f3f67 bump version 2025-10-01 08:55:42 +00:00
Mario
b68b5a60c5 fix issue where loadingPage state was not reset in some situations and return if we retry live update due to incomplete data 2025-10-01 08:55:15 +00:00
Manuel Jiménez Friaza
f4c29d13f4 Update Spanish strings
(cherry picked from commit 357eabbe9e2375a23715b12bcf22fe5b7b4b837a)

Co-authored-by: mjfriaza:4GF~eYj,-iAv <mjfriaza@disroot.org>
2025-09-15 16:42:01 +02:00
Mario
121fb74c6c tagcloud: remove redundant f arg 2025-09-15 07:12:09 +00:00
Mario
4a098cbeaf whitespace 2025-09-15 07:04:37 +00:00
Mario
6123fd2be8 archive widget: point url to module/channel_addr instead of App:$cmd and some cleanup - fix issue #1911 2025-09-15 07:04:00 +00:00
Harald Eilertsen
176a7cb9b5 Comment duplicate keys in xchan_change_key func
PHP arrays can't have duplicate keys, so adding an entry with the same
key again will replace the existing entry with the later one. This patch
comment out the entries that in any case are replaced, so functionally
it should be equivalent to what was before. (But still probably wrong!)
2025-09-12 21:51:24 +02:00
Harald Eilertsen
a7e734b700 Move named inner function out in po2php.php
While PHP seems to be able to handle this construct, PHPStan don't. In
this case there's also no reason for this to be an inner function, so
just move it out to the global scope.
2025-09-12 21:51:24 +02:00
Harald Eilertsen
b31123ae2f Fix missing return from translate_scope function
Also make sure the output is escaped wherever this function is used.
2025-09-12 21:51:24 +02:00
Harald Eilertsen
330c8ca890 Fix ref to undefined var in account.php 2025-09-12 21:51:24 +02:00
Harald Eilertsen
e645ea3bf0 Fix missing return warnings in Module\Request
As the Request module never returns a HTML payload it was changed to
override the Controller::init() method rather than Controlled::get().

The internal private functions were fixed accordingly, and return type
annotations fixed up as well.

Discovered with PHPStan.
2025-09-12 21:51:24 +02:00
Harald Eilertsen
50f439ae8e Fix precompile_smarty3.php
The Smarty class is in the Smarty namespace, and should not be loaded
manually, but through composer. This is taken care of by requiring
`boot.php`. Also make the script slightly more robust as to where it is
invoked from.
2025-09-12 21:51:24 +02:00
Harald Eilertsen
3a102d9dc3 Remove ref to undefined var in Module\Register
The $reg variable is defined in another if-statement further up the
function, but does not make sense in the part where it was referenced.
The code tried to switch to the registered users prefered language
before sending an email to the user, but as this part of the code don't
have a record for the registered user, this makes no sense.

Iow, we leave the currenly selected language alone, and hope for the
best.
2025-09-12 21:51:24 +02:00
Harald Eilertsen
bb4c08caf0 PHPStan: Ignore missing function make_xchan_hash
This function does not exist, but is called from a function that is
never used. For rationale for why this code is kept, see the discussion
here:

https://framagit.org/hubzilla/core/-/merge_requests/2209
2025-09-12 21:51:24 +02:00
Harald Eilertsen
f8795288a1 More misc fixes in verify_email_address 2025-09-12 21:51:24 +02:00
Harald Eilertsen
107b8e67d2 Fix/complete template args in verify_email_address 2025-09-12 21:51:24 +02:00
Harald Eilertsen
5a36a37b9f Fix template language in verify_email_address 2025-09-12 21:51:24 +02:00
Harald Eilertsen
0910ae0e52 Fix db query in verify_email_address 2025-09-12 21:51:24 +02:00
Harald Eilertsen
c6c5a8b5e0 Cleanup verify_email_address
- Add type annotations, and change arg to a string instead of array.
  Only the email address was used anyways.
- Fix indentation
- Improve API doc and remove unnecessary comments.
2025-09-12 21:51:24 +02:00
Harald Eilertsen
da2c57c432 Fix undefined var $hash in verify_email_address 2025-09-12 21:51:24 +02:00
Harald Eilertsen
e7523e7fc5 Remove ref to undefined variables in get_zcard() 2025-09-12 21:51:24 +02:00
Harald Eilertsen
e52d3639de Fix missing returns in plugin.php
Flagged by phpstan
2025-09-12 21:51:24 +02:00
Mario
1a27edf6dd Merge branch 'remove-dead-code-in-handle-tag' into 'dev'
Remove dead code in handle_tag function

See merge request hubzilla/core!2227
2025-09-12 16:35:32 +00:00
Mario
9750543157 Merge branch 'register_account_hook' into 'dev'
uncommented register_account hook

See merge request hubzilla/core!2226
2025-09-12 16:31:43 +00:00
Mario
95dab5c764 Merge branch 'test-include-extensions' into 'dev'
Include extensions in default PHPUnit config

See merge request hubzilla/core!2223
2025-09-12 16:31:10 +00:00
Mario
619df9ca22 fix for issue #1929 and updated wording 2025-09-12 16:24:22 +00:00
Mario
5442bac707 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-09-12 14:50:21 +00:00
Mario
99688cdde5 remove redundant variable and include 2025-09-12 14:49:22 +00:00
Harald Eilertsen
6b7478c5a5 Remove dead code in handle_tag function
Since $profile is only ever set in the other branch of the if/else
construct, this code will never execute.
2025-09-05 23:19:07 +02:00
SK
644e3b790c uncommented register_account hook
Hook is required for notify-admin addon to work.
2025-09-02 19:43:35 +05:30
Harald Eilertsen
c064cfde91 Look for unit tests for extensions as well as core 2025-09-02 08:38:50 +02:00
Harald Eilertsen
2826d030f4 Remove obsolete phpunit configurations
These are not in use, probably outdated, and just confusing.
2025-09-02 08:38:50 +02:00
Mario
1ab1da5816 CI: Tell mysql cli to skip ssl checks
(cherry picked from commit 120292bd6c)

Co-authored-by: Harald Eilertsen <haraldei@anduin.net>
2025-09-02 06:34:44 +00:00
Mario
05800a5ff3 Merge branch 'fix-ci' into 'dev'
CI: Tell mysql cli to skip ssl checks

See merge request hubzilla/core!2224
2025-09-02 06:29:23 +00:00
Harald Eilertsen
120292bd6c CI: Tell mysql cli to skip ssl checks 2025-08-31 11:20:03 +02:00
Mario
aaa863cda7 Merge branch 'dev' 2025-08-15 08:45:45 +00:00
Mario
3c722f9aa7 changelog 2025-08-15 08:45:05 +00:00
Mario
14f28907bc version 10.4.3 2025-08-15 08:38:40 +00:00
Mario
185f53d298 bump version 2025-08-14 17:46:21 +00:00
Mario
3ae42d471a vote: minor refactor tto catch dubble votes early 2025-08-12 09:13:55 +00:00
Mario
0a817f952e mark our own vote seen because it will not be marked seen later 2025-08-11 23:28:59 +00:00
Mario
bc7f8b15a8 dismiss our own votes from select query. they are not flagged with item_hidden because otherwise they would not be delivered to the poll owner. received votes are (and should be) flagged with item_hidden so that they are not being relayed. 2025-08-11 23:13:26 +00:00
Mario
02111ad649 start transition deprecated AS1 item.verb vocabulary to AS2 on demand 2025-08-11 22:36:35 +00:00
Mario
6f00964d47 fix regression in retrieving channel address in wtagblock() and whitespace fixes
(cherry picked from commit eafa1d56c3)

Co-authored-by: Mario <mario@mariovavti.com>
2025-08-08 17:49:10 +00:00
Mario
eafa1d56c3 fix regression in retrieving channel address in wtagblock() and whitespace fixes 2025-08-08 14:03:28 +00:00
Mario
44a0311798 Merge branch 'master' of https://framagit.org/hubzilla/core 2025-08-08 07:51:42 +00:00
Mario
2ac06fef9a version 10.4.2 2025-08-08 07:51:12 +00:00
Mario
5b6a38aa2d changelog 2025-08-08 07:48:14 +00:00
Mario
37b9919905 bump version 2025-08-07 22:53:26 +00:00
Mario
283c018d07 implement item_custom_display hook in mod HQ 2025-08-07 22:51:40 +00:00
Mario
51c79aecac make item_normal() deal with various item types and add missing reactions modal 2025-08-07 22:36:35 +00:00
Mario
9343f00918 improve memory consumption when deleting big threads 2025-08-07 19:05:58 +00:00
Mario
962b4e5b4c fix php error
(cherry picked from commit eeef6cfd00)

Co-authored-by: Mario <mario@mariovavti.com>
2025-08-07 14:57:11 +00:00
Mario
eeef6cfd00 fix php error 2025-08-07 14:55:00 +00:00
Mario
96c7912fa6 update the contact edit header so that both, image and text are linked to the profile 2025-08-02 11:48:50 +00:00
Mario
91f1f8ed7c version 10.4.1 2025-07-31 08:46:43 +00:00
Mario
3f7ed72ece changelogà 2025-07-31 08:43:31 +00:00
Mario
3385c694aa add delay before collapsing 2025-07-31 08:22:01 +00:00
Mario
a922f5ca6c bump version 2025-07-30 19:20:35 +00:00
Mario
88977a406c imagesLoaded() should not be required in updateConvItems() anymore since we call it before updateConvItems() already (famous last words). 2025-07-30 19:19:00 +00:00
Mario
e98f019b16 fix regression in pubstream tag view, use correct prefixes in pub_tagadelic() and some whitespace fixes 2025-07-30 16:22:08 +00:00
Mario
50f5fcb16b always update photo DB entries on move - fixes issue where photos were not found by photo picker after folder rename as reported by chris 2025-07-30 06:45:25 +00:00
Mario
bf35a661bf remove trailing comma 2025-07-29 11:02:43 +00:00
Mario
ecc94cdecd fix a weird rendering issue which was triggered by load events fireing after the timeout was already in action 2025-07-29 09:28:00 +00:00
Mario
e79a70d2b1 var > const and style 2025-07-27 10:24:53 +00:00
Mario
6916a825ee move the freshly created content to the center instead of top and use smooth scrolling 2025-07-27 10:23:23 +00:00
Mario
b5a798f068 fix comment preview and scroll to the just created comment after posting it 2025-07-27 09:52:48 +00:00
Mario
e5c54fadea Merge branch 'update-derived-theme-tutorial' into 'dev'
Update derived theme tutorial

See merge request hubzilla/core!2219
2025-07-27 08:15:15 +00:00
Mario
f2217e9016 Merge branch 'fix-pubstream-tagcloud-tpl' into 'dev'
Fix pubstream tagcloud and category cloud

See merge request hubzilla/core!2218
2025-07-27 08:14:41 +00:00
Saiwal K
4d6bebb846 Fix pubstream tagcloud and category cloud 2025-07-27 08:14:41 +00:00
Harald Eilertsen
2e9b1d25e1 Replace mention of theme_init function
The theme_init function is no longer needed, and the example isn't using
it. So we replace it with a bit more description of the theme info in
the comment, and how that works.
2025-07-24 12:50:50 +02:00
Harald Eilertsen
8604ba016e Update derived theme tutorial
Adds another lesson on customising the theme using styles and overriding
templates.

Also some minor language cleanup and add some introductory text before
lesson 1.
2025-07-24 12:40:13 +02:00
Mario
cc35afa4c9 bump version 2025-07-23 12:07:45 +00:00
Mario
f01c5ad0c1 remove logging 2025-07-23 12:07:21 +00:00
Mario
7b6a8fdba1 fix issue where reply modal would not show anymore after reactions loaded 2025-07-23 12:06:48 +00:00
Mario
2c9f184a09 tagcloud fixes 2025-07-23 08:19:40 +00:00
Mario
6f158f37c9 Merge branch 'improve-derived-themes' into 'dev'
Add 'extends' attribute to theme info

See merge request hubzilla/core!2217
2025-07-23 07:54:30 +00:00
Mario
f88d2ba0c4 Merge branch 'tag-cloud-tpl-fix' into 'dev'
Add tpl for tagcloud and directory keyword cloud

See merge request hubzilla/core!2216
2025-07-23 07:53:10 +00:00
Saiwal K
170f157aae Add tpl for tagcloud and directory keyword cloud 2025-07-23 07:53:09 +00:00
Mario
82817b4532 tagadelic: look up toplevels only
(cherry picked from commit 3eeaca4aab)

Co-authored-by: Mario <mario@mariovavti.com>
2025-07-22 10:34:19 +00:00
Mario
3eeaca4aab tagadelic: look up toplevels only 2025-07-22 10:32:57 +00:00
Mario
a3dd4a4d3d revert cleanup template
(cherry picked from commit c383e6af80)

Co-authored-by: Mario <mario@mariovavti.com>
2025-07-22 10:22:38 +00:00
Mario
c383e6af80 revert cleanup template 2025-07-22 10:21:34 +00:00
Mario
517e14cc38 cleanup template
(cherry picked from commit eb3dfb2488)

Co-authored-by: Mario <mario@mariovavti.com>
2025-07-22 10:17:05 +00:00
Mario
eb3dfb2488 cleanup template 2025-07-22 10:16:09 +00:00
Mario
70e8648d91 the item_wall directive is not required here since we are looking for parent items which we own - remove it since it will make the DB engine use the wrong index in certain cases. also remove duplication of item_type directive when looking up articles
(cherry picked from commit ae8e9bc353)

Co-authored-by: Mario <mario@mariovavti.com>
2025-07-22 09:33:34 +00:00
Mario
ae8e9bc353 the item_wall directive is not required here since we are looking for parent items which we own - remove it since it will make the DB engine use the wrong index in certain cases. also remove duplication of item_type directive when looking up articles 2025-07-22 09:32:20 +00:00
Harald Eilertsen
88227b9155 Rename derived theme tutorial file 2025-07-20 20:33:56 +02:00
Harald Eilertsen
ae77ab7f20 Add derived theme tutorial to help index 2025-07-20 20:28:39 +02:00
Harald Eilertsen
308380fe52 Add 'extends' attribute to theme info
To create a derived (or child) theme, the array held in
`App::$theme_info` must contain a key `extends` that contains the name
of the parent theme as a value.

The derived theme tutorial would suggest adding the key in the
`themename_init()` function, however this does not work. The theme info
is repopulated from the theme info file after the theme init function
has run, and so the `extends` key disappears.

This patch adds `extends` as a first class attribute for themes, so that
it can be specified directly in the theme info in the top file comment
block. It also updates the derived theme tutorial to reflect this
change.
2025-07-20 20:23:25 +02:00
Mario
ee7b982358 Merge branch '10.4RC' 2025-07-15 06:40:43 +00:00
Mario
2740f36259 version 10.4 2025-07-15 06:40:19 +00:00
Mario
ff16810e76 Merge branch 'dev' into 10.4RC 2025-07-15 06:35:47 +00:00
Mario
161cfcd64d update changelog 2025-07-15 06:33:41 +00:00
Mario
d16d1fdcd5 Merge branch 'more-nb-translation-and-fixes' into 'dev'
More translations and fix for Norwegian nb

See merge request hubzilla/core!2214
2025-07-15 06:13:46 +00:00
Harald Eilertsen
06c0652b05 More translations and fix for Norwegian nb
Wiki's and profiles both used the "Create new" button, so don't add what
is being created to the translation :)
2025-07-14 22:23:58 +02:00
Mario
a2fb2e04f3 add support for did:key verificationMethod in checkEddsaSignature() 2025-07-14 14:13:34 +00:00
Mario
b376f810bf strict check and whitespace 2025-07-14 13:56:51 +00:00
Mario
0c2bcda431 some documentation 2025-07-14 13:48:59 +00:00
Mario
22cb0d3c6b use a custom function to build the psr7 request to work around mod rewrite issues with ServerRequest::fromGlobals() 2025-07-14 13:45:23 +00:00
Mario Vavti
b6b4eb4c22 composer update http message signer to version 0.2.3 2025-07-14 15:35:03 +02:00
Mario
da1e0b965b Merge branch 'dev' into 10.4RC 2025-07-13 19:43:01 +00:00
Mario
d566199423 Merge branch 'update-nb-translations' into 'dev'
Update translations for Norsk Bokmål

See merge request hubzilla/core!2213
2025-07-13 19:40:28 +00:00
Mario
35be83c5f6 Merge branch 'fix-permission-context-help' into 'dev'
Fix context help link for permissions

See merge request hubzilla/core!2212
2025-07-13 19:39:21 +00:00
Harald Eilertsen
76eed3c06c Fix context help link for permissions
The target was changed when the new docs was merged.
2025-07-13 16:09:33 +02:00
Harald Eilertsen
d994e1be48 Update translations for Norsk Bokmål 2025-07-13 16:01:06 +02:00
Mario
24316bbb03 Merge branch 'dev' into 10.4RC 2025-07-13 10:32:55 +00:00
Mario
7b5a638e00 update changelog 2025-07-13 10:32:23 +00:00
Mario
fa4c86eabf update install document 2025-07-13 10:03:46 +00:00
Mario
9cf610280c add init_sys_channel utility to create the sys channel in case of headless installation - issue #1909 2025-07-13 09:57:26 +00:00
Mario
052341d24a Merge branch 'dev' into 10.4RC 2025-07-13 09:49:29 +00:00
Mario
a55bba7aa8 fix blog mode if threaded view is disabled 2025-07-13 09:47:38 +00:00
Mario
0244d9423a RC3 2025-07-10 12:02:15 +00:00
Mario
042c98cf2d Merge branch 'dev' into 10.4RC 2025-07-10 12:00:53 +00:00
Mario
2958ea8c89 update changelog 2025-07-10 12:00:32 +00:00
Mario
81a12be654 changelog 2025-07-10 11:59:27 +00:00
Mario
9f99e401bc Merge branch 'fix-markdown-mentions' into 'dev'
Prevent mentions from mangling by markdown parser

See merge request hubzilla/core!2211
2025-07-10 11:43:10 +00:00
Mario
48ef490f50 Merge branch 'dev' into 10.4RC 2025-07-10 11:40:58 +00:00
Mario Vavti
d782229ba0 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-07-10 11:37:01 +02:00
Mario Vavti
52291fd371 update macgirvin/http-message-signer to version 2.2 2025-07-10 11:36:38 +02:00
Mario
e0298a4378 add logging and catch exception 2025-07-09 08:00:52 +00:00
Mario
08b7eb52c7 remove unneeded code from test 2025-07-09 08:00:27 +00:00
Mario
43ebf69d09 trim the value 2025-07-08 10:47:44 +00:00
Mario
18ae629f19 remove the check for supported protocols. we can store them all because we check later if we support one of them 2025-07-08 10:46:40 +00:00
Mario
8deedd1979 remove long deprecated crypto functions 2025-07-08 10:01:10 +00:00
Mario Vavti
525594d529 update to phpseclib3 which is now a requirement for the http signer library 2025-07-08 11:35:56 +02:00
Mario Vavti
1162615c52 missing file from http-message-signer 2025-07-07 13:17:55 +02:00
Mario Vavti
c462c928e0 composer update to phpseclib version 3 2025-07-07 13:17:14 +02:00
Mario
fcb0ef9633 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-07-06 21:22:53 +00:00
Mario
8a4273a2f5 for now only attempt RFC9421 verification if we have the request 2025-07-06 21:22:32 +00:00
Mario Vavti
3014ae2071 update macgirvin/http-message-signer to version 2.0 2025-07-06 22:58:09 +02:00
Mario Vavti
d396043faf composer dump-autoload 2025-07-06 22:42:55 +02:00
Mario Vavti
2054359f6c testing and fixes 2025-07-06 22:41:02 +02:00
Mario
9a3735cd37 tests for Activity::get_actor_protocols() 2025-07-06 18:32:08 +00:00
Mario
222b74ec05 extend get_actor_protocols() to also check fep-fb2a actor metadata 2025-07-06 18:06:28 +00:00
Harald Eilertsen
cb491c53f7 Prevent mentions from mangling by markdown parser
Mentioning people whose handles include valid markdown would not survive
markdown to bbcode conversion wihout being mangled. An example that
brought this to my attention was a handle similar to this:

    _someuser_@testsite.example

The underscores would be interpreted as emphasis in markdown, and
converted to:

    [i]someuser[/i]@testsite.example

This patch should fix this, and hopefully any other issue with mentions
being mangled.
2025-06-28 13:35:05 +02:00
Mario
37f0074674 Merge branch 'dev' into 10.4RC 2025-06-26 08:58:56 +00:00
Mario Vavti
e6c8c47721 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-06-26 10:55:31 +02:00
Mario Vavti
0a98a49eeb composer update macgirvin/http-message-signer 2025-06-26 10:54:42 +02:00
Mario
7bd4217dd8 Merge branch 'dev' into 10.4RC 2025-06-26 08:12:28 +00:00
Mario
af5733888c changelog 2025-06-26 08:11:46 +00:00
Mario
c346210735 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-06-26 08:08:31 +00:00
Mario
6028f0490e minor refactor to get rid of the $webpage variable (it does not cut it anymore) and use $item_type instead 2025-06-26 08:08:08 +00:00
Mario Vavti
890a5942d6 Merge branch 'dev' into 10.4RC 2025-06-25 17:43:52 +02:00
Mario Vavti
7782183ae3 changelog 2025-06-25 17:40:55 +02:00
Mario Vavti
7ecbce691d RC2 2025-06-25 17:35:58 +02:00
Mario Vavti
2aeed27b9b emiting a notice from include/auth does not work. emit it from mod login if the login failed. 2025-06-25 17:33:35 +02:00
Mario Vavti
d636e71024 remove code that would never execute and make sure the first account to register is the admin account 2025-06-25 17:26:41 +02:00
Mario
2606517c1d changelog 2025-06-24 10:09:36 +00:00
Mario
0ab94d42f4 bump dev version 2025-06-24 07:58:19 +00:00
Mario
7c32b11605 update strings 2025-06-24 07:55:21 +00:00
Mario
7b6c76abf1 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-06-24 07:54:33 +00:00
Mario
0e31d61868 Merge branch 'some-account-cleanup' into 'dev'
A bit of cleanup for account functions

See merge request hubzilla/core!2210
2025-06-24 07:53:43 +00:00
Harald Eilertsen
66e02c5e3a A bit of cleanup for account functions 2025-06-24 07:53:42 +00:00
Mario
122747ae76 version 10.4RC1 2025-06-24 07:28:50 +00:00
Mario
9a410f57e6 fix php warnings 2025-06-24 07:25:56 +00:00
Mario
2047e9bca7 fix php warning 2025-06-23 20:06:07 +00:00
Mario
aeb54655a0 exit if we have not found anything 2025-06-23 09:24:10 +00:00
Mario
1a3ca49eba apply downstream fix from streams: do not sign (request-target) on response 2025-06-23 09:10:57 +00:00
Mario
579cf86335 rfc9421 verify: parse more fields if applicable 2025-06-23 09:03:05 +00:00
Mario
fcf42eeae0 Revert "rfc9421 verify: parse more fields if applicable"
This reverts commit 721c0b1e5b.
2025-06-23 09:02:08 +00:00
Mario
721c0b1e5b rfc9421 verify: parse more fields if applicable 2025-06-23 08:59:27 +00:00
Mario
82b2f06bdd bump version 2025-06-22 07:36:08 +00:00
Mario
b241e14c06 import App 2025-06-21 20:56:41 +00:00
Mario Vavti
89c17bac50 start verifying RFC9421 HTTP Signatures (hopefully) 2025-06-21 16:39:59 +02:00
Mario
e6bd5ef520 css fix 2025-06-20 19:08:27 +00:00
Mario
7320149fd8 only show the pin if we display the label and fix the test 2025-06-19 08:26:31 +00:00
Mario
7766e5d420 prepend geo links withe a pushpin emoji so that they can be distinguished from regular links 2025-06-19 08:18:25 +00:00
Mario
4a813c150c make sure to set the state on the correct button 2025-06-19 08:08:16 +00:00
Mario
44600c46c6 make sure there is a comment button before attempting to add a class to it 2025-06-18 17:01:26 +00:00
Mario
b5c7fff316 re-add check for sub thread wrapper 2025-06-18 16:49:33 +00:00
Mario
ce618facc8 properly set the comment button and sub-thread-wrapper state if we are loading a conversation in partly expanded mode 2025-06-18 15:55:15 +00:00
Mario Vavti
18016ab36a remove some redundant spaces in bbcode to html conversion and fix tests 2025-06-18 11:55:47 +02:00
Mario
d788233bd7 handle naked geo URIs and add tests 2025-06-18 09:18:11 +00:00
Mario
86ebef7e08 allow geo: uri and be more specific when checking uri schemes also remove zhttp which is not used anymore and ftp which is not supported by most browsers anymore 2025-06-17 09:29:27 +00:00
Mario
37159937d7 bump version 2025-06-14 10:51:48 +00:00
Mario
4d6d3befa0 leave zoom handling to the comment button click handler 2025-06-14 10:51:18 +00:00
Mario
a21785980d Merge branch 'remove-obsolete-funcs' into 'dev'
Remove obsolete functions in account.php

See merge request hubzilla/core!2208
2025-06-14 10:29:14 +00:00
Mario
14fe08240f Merge branch 'fix-album-widgets' into 'dev'
Fix undefined variable in album widgets

See merge request hubzilla/core!2207
2025-06-14 10:28:33 +00:00
Mario
73d50d8294 Merge branch 'remove-xref-module' into 'dev'
Remove unused Xref module

See merge request hubzilla/core!2206
2025-06-14 10:27:50 +00:00
Mario
767eeee6fe invert the verb query part 2025-06-13 09:42:09 +00:00
Mario
abd864a762 css fixes 2025-06-13 09:20:30 +00:00
Mario
25dba4423b bump version 2025-06-13 08:07:53 +00:00
Mario
2046abf26f trick sql to use a better suited index 2025-06-13 08:05:05 +00:00
Mario
ad7871270c add EmojiReact to the countable comment verbs 2025-06-12 11:57:13 +00:00
Mario
66512f4e51 blog mode fixes 2025-06-12 11:51:01 +00:00
Mario
71a06af8a9 consistent naming 2025-06-12 10:47:04 +00:00
Mario
14088b4b2e do not expand all 3 thread levels even if the content is already there. This would be inconsistent behaviour. 2025-06-12 08:02:52 +00:00
Mario
ec2c4fe673 css fixes 2025-06-12 07:02:14 +00:00
Mario
49a065e50b bump version 2025-06-11 19:35:40 +00:00
Mario
391c8d7cba implement indenting of already expanded but flattened items and add dblclick info badge 2025-06-11 19:35:17 +00:00
Mario
76296e72be refactor TLL to zoom in on every expansion and better maintainable code 2025-06-11 11:34:08 +00:00
Mario
d36f806ece more autoexpand iterations fixes 2025-06-10 09:06:48 +00:00
Mario
d3d8c088f3 bump version 2025-06-10 08:46:24 +00:00
Mario
4e01e21230 fix autoexpand iterations 2025-06-10 08:46:01 +00:00
Mario
673790126b dismiss summary if it is equal to content and css fixes 2025-06-09 19:09:32 +00:00
Mario
9eecd0aa3c css fixes 2025-06-09 14:11:20 +00:00
Mario
f8fb1bc3b4 use a more prominent color 2025-06-09 09:30:31 +00:00
Mario
340f5bb791 do not count iterations where we have not found a new thread 2025-06-09 08:11:41 +00:00
Mario
5504c8650e do not call preview twice and not at all if we are in modal action 2025-06-08 21:18:53 +00:00
Mario
9405f2e2d3 bump version 2025-06-08 21:09:51 +00:00
Mario
3de0d304fd fix comment image embed after transition to modal 2025-06-08 21:09:27 +00:00
Harald Eilertsen
0e4924d7c5 Remove obsolete verify_email_addressNOP function 2025-06-08 22:46:26 +02:00
Mario
20377c1ca1 css fixes 2025-06-08 19:05:58 +00:00
Mario
8af9bbb75e slightly darker colors 2025-06-08 18:53:36 +00:00
Mario
34c3a77db4 improved thread lense 2025-06-08 17:07:14 +00:00
Mario
dbc3613af5 more cleanup 2025-06-08 15:13:45 +00:00
Mario
e1d395b6ed more cleanup and introduce the thread lense 2025-06-08 09:06:01 +00:00
Harald Eilertsen
5316ebfe7c Remove obsolete functions in account.php 2025-06-08 09:43:14 +02:00
Harald Eilertsen
13755060d7 Fix undefined variable in album widgets
The Album and Portfolio widgets would reference the undefined
$phototypes array to determine the file extensions of the image sources.

It appears that the extension is not really necessary, but for
completeness I fixed the issue so that the appropriate extension will be
used by for the image source attribute.
2025-06-07 23:09:46 +02:00
Harald Eilertsen
eeb8a124fc Remove unused Xref module
See discussion here:
https://hubzilla.org/item/c611090c-1a5e-5fa5-8836-06f393fd71ef
2025-06-07 22:14:22 +02:00
Mario
5db855abfe it is hard to keep the overview with more than 3 levels 2025-06-07 09:11:12 +00:00
Mario
ab14743146 bump version 2025-06-06 12:04:47 +00:00
Mario
409c062c4f reset indents for possible followup autoexpand 2025-06-06 12:04:32 +00:00
Mario
1b494cdd27 bump version 2025-06-06 11:11:06 +00:00
Mario
f5f853cdfe improved autoexpand 2025-06-06 11:10:36 +00:00
Mario
37aa16a6c0 bump version 2025-06-05 19:43:42 +00:00
Mario
a4ebf93cb8 cleanup, fix iteration counter and bump literations to 10 2025-06-05 19:43:19 +00:00
Mario
358e7c9512 bump version 2025-06-05 16:08:44 +00:00
Mario
11b42a371e revert to previous color generation logic 2025-06-05 16:08:22 +00:00
Mario
61da32dc3f Merge branch 'new-de-docs' into 'dev'
New de docs

See merge request hubzilla/core!2205
2025-06-05 15:50:22 +00:00
Mario
5f26e77785 Merge branch 'more-phpstan-fixes' into 'dev'
More PHPStan fixes

See merge request hubzilla/core!2204
2025-06-05 15:48:17 +00:00
Mario
1d289e619e Merge branch 'crash-in-jcxeddsa2022-sign' into 'dev'
Throw if channel keys missing in JcsEddsa2022::sign

See merge request hubzilla/core!2201
2025-06-05 15:45:47 +00:00
Harald Eilertsen
0c41265613 Throw if channel keys missing in JcsEddsa2022::sign 2025-06-05 15:45:46 +00:00
Mario
b5ad0589cb bump version 2025-06-05 15:44:25 +00:00
Mario
9e87070f86 iterate only 3 levels when autoexpanding, improve color generation to get more random colors 2025-06-05 15:44:01 +00:00
Mario
3da16a4bae bump version 2025-06-05 15:09:23 +00:00
Mario
1ad76ec83f fix other reactions buttons and bring some color to indents 2025-06-05 15:09:03 +00:00
Mario
fa67852278 better center the gt 2025-06-05 08:16:51 +00:00
Mario
d9ebd880be more better center the dash 2025-06-05 08:13:19 +00:00
Mario
9d7c5d2fe2 better center the dash 2025-06-05 08:05:46 +00:00
Mario
8c268d24df version 2025-06-05 08:03:52 +00:00
Mario
f8fb550255 threading: implement manual indent and collapse 2025-06-05 08:03:33 +00:00
Mario
c1aa5a2954 bump version 2025-06-04 19:55:42 +00:00
Mario
342f736eb7 improved autoexpand 2025-06-04 19:55:05 +00:00
Mario
0283c77961 do not prevent default too early 2025-06-04 16:38:30 +00:00
Mario
7740a7501b improved indentation and its logic 2025-06-04 16:33:05 +00:00
Mario
ed97164f01 remove unused code 2025-06-04 14:35:35 +00:00
Mario
6649eca2bf move expand to item menu to get rid of the single click lag 2025-06-04 14:34:44 +00:00
Mario
64ae1eb5e8 add ability to auto-expand a comment reply thread 2025-06-03 08:31:19 +00:00
Mario
d42a89d4b5 disable reactions in pinned widget 2025-06-02 07:55:24 +00:00
Mario
bb05f520c1 fix pinned widget 2025-06-02 07:38:11 +00:00
Mario
f8afa77369 add internal follow and ignore verbs to the list of filtered verbs and add commented out code to display/count only one reaction per author. It appears there can be multiple. We should probably look into how this can happen and if we should prevent it earlier. 2025-06-01 18:48:16 +00:00
Mario
940fd26db7 add the thr_parents if we are in channel single view mode 2025-06-01 10:44:33 +00:00
Mario
fca6ddd35b change the animation so that it will not interfere with bootstrap stuff 2025-06-01 10:14:18 +00:00
Mario
a5ded5884c no need to set animation constant 2025-06-01 04:58:06 +00:00
Mario
d0686796cd it is the fill: both option that woes with dropdown visibility - set it to none 2025-06-01 04:57:05 +00:00
Mario
3ea323bfb0 handle intro notifications via /notify/view so that it will eventually be marked seen 2025-05-31 20:16:24 +00:00
Mario
c826b307c2 fix display issue 2025-05-31 18:43:07 +00:00
Mario
00edbe4393 bump version 2025-05-31 17:07:00 +00:00
Mario
525a45f69c make sure to load the profile when in channel module 2025-05-31 16:56:30 +00:00
Mario
e35d4b6be0 only show load more if total comments > loaded comments 2025-05-30 09:55:26 +00:00
Mario
83c963378e remove more redundant sql (perms for the given IDs MUST be checked at the calling module already) and update documentation 2025-05-29 19:47:53 +00:00
Mario
7705d7a87f this is not required because we already filter by a given set of ids here 2025-05-29 19:31:23 +00:00
Mario
3060126123 bump version 2025-05-29 19:12:27 +00:00
Mario
29f14ff96b only count comments if we are in threaded mode 2025-05-29 19:10:20 +00:00
Mario
8a8219fe72 improved wording 2025-05-29 18:38:11 +00:00
Mario
0aef9b486d fix typo 2025-05-29 17:58:48 +00:00
Mario
d584e4f7d5 fix queries in certain situations returning more results than we need producing unexpected rendering results 2025-05-29 17:58:04 +00:00
Mario
71fbc3ff5c bump version 2025-05-29 14:25:20 +00:00
Mario
3613257cdc pass permission sql to item_reaction_sql() in case we are a visitor and rename the variable 2025-05-29 14:24:53 +00:00
Mario
f61fd2d337 if we are not in threaded mode default comments limit to 1000 2025-05-29 11:55:02 +00:00
Mario
bd8ffcd28d bump version 2025-05-29 10:14:20 +00:00
Mario
20148d23f1 toplevel comments pagination: initial commit 2025-05-29 10:13:32 +00:00
Mario
bdcee46138 do not check for possibly unset var 2025-05-28 12:53:53 +00:00
Mario
bc40c3f7c0 bump version 2025-05-28 09:18:49 +00:00
Mario
7e3d86bc37 more refactor and streamline for re-usability and easier maintenance 2025-05-28 09:15:18 +00:00
PepeCyB
133ac409e3 remove unneeded files 2025-05-27 22:49:16 +02:00
PepeCyB
bda32015b9 new german help-files added 2025-05-27 22:48:54 +02:00
Mario
f0229c9f42 update todo 2025-05-27 09:02:52 +00:00
Mario
7cd44e2112 minor cleanup 2025-05-27 08:31:37 +00:00
Mario
3a3ae2b204 fix a undefined var warning and move some definitions closer to where the value is set for better readability 2025-05-27 08:12:33 +00:00
Mario
4df3c8d2a1 bump version 2025-05-27 08:06:20 +00:00
Mario
8d1fea7ec3 new version of items_by_parent_ids() and item_reaction_sql() with much better performance, streamline rsvp naming convention and some preparatory work for comment lazy loading (unfinshed) 2025-05-27 08:04:36 +00:00
Mario
3535cb3b96 remove the word only. the filter icon should be clear enough. 2025-05-25 17:56:38 +00:00
Mario
48af5b354d bump version 2025-05-25 14:16:47 +00:00
Mario
19df2f596b revert to previous iteration of the parents query function until performance issues will be resolved and adapt the modules accordingly 2025-05-25 14:05:08 +00:00
Harald Eilertsen
51cd6381a6 Ignore PHPStan warning of missing .htpreconfig
The existence of the file is checked in the if-statement wrapping the
include.
2025-05-24 10:52:29 +02:00
Harald Eilertsen
311e0159c9 Fix undefined static function in Zot6Handler
The static function `reply_rekey_request` did not exist, but a function
`rekey_request` that was never called did. Changed the call to use the
latter function instead.

Note: This is completely untested code, and probably needs some
validation. But then it did not work before either.
2025-05-24 10:43:39 +02:00
Harald Eilertsen
63c3f4dc20 Fix missing return in Render\Theme::current
Also made the function halt with a status 500 and log a message if no
theme was found.
2025-05-23 22:33:52 +02:00
Harald Eilertsen
b6e65a76ea Add App::$page_layouts attribute for comanche
Zotlabs\Render\Comanche::register_page_template assigns this variable in
App (for some reason), but it is not declared. As dynamic attributes are
deprecated, and will be removed in PHP 9, this patch fixes the issue by
defining the attribute in the App class.
2025-05-23 20:53:55 +02:00
Mario
d31221e61b use else instead of checking for $ret 2025-05-23 11:54:30 +00:00
Mario
043c1610fd refactor items_by_parent_ids() to accept $thr_parents as an array and stringify it here with the escape flag instead of in the caller modules 2025-05-23 11:35:10 +00:00
Mario
84a4927866 bump version 2025-05-23 10:01:18 +00:00
Mario
2c3ec1ae81 Merge branch 'phpstan-fixes' into 'dev'
Fix a selection of ussues reported by PHPStan

See merge request hubzilla/core!2203
2025-05-23 09:58:59 +00:00
Mario
5655962370 Merge branch 'update-doxygen-config' into 'dev'
Minor doxygen config update

See merge request hubzilla/core!2202
2025-05-23 09:55:15 +00:00
Mario
cd7fab3d37 refactor query in items_by_parent_ids() to prepare for pagination. Until pagination will be implemented we will load the last 100 comments. 2025-05-23 09:51:11 +00:00
Mario
980fedadc0 dba_pdo: return results when using common table expressions (CTE) 2025-05-23 09:47:25 +00:00
Harald Eilertsen
0121e4ed12 Fix undefined variables in Module/Regate.php 2025-05-22 19:03:06 +02:00
Harald Eilertsen
ea6893e3a3 Fix undefined var in Module/Invite.php
The variables $invexpire_n and $invexpire_u was undefined, but
references multiple times in a conditional. Just removed them which
should cause no changed behaviour.
2025-05-22 19:03:06 +02:00
Harald Eilertsen
8a211aff97 Remove unused code in Module/Invite.php
The $feedbk variable was set, but never used in the Module\Invite::get()
function.
2025-05-22 19:03:06 +02:00
Harald Eilertsen
daf8531804 Fix undefined var in Module/Invite.php
Fir ref to undefined $ihave variable in the Module\Invite::get() method.
The var was defined later in the method. Also refactor to fix some other
strange logic in the same area while I was there.

- Moved the logic to cound invites currently in use by a channel to a
  private function. This should probably be moved further to a Regiter
  class once we have these things more in order.
- Changed the logic checking the configuration for how many invites a
  channel can use at the same time to just return 4 (the default)
  instead of going via 'na' and then checking if it's 'na' to then use
  the default value.
- Dropped setting the config to the default if the module is visited by
  an admin. That belongs somewhere else.
2025-05-22 19:03:06 +02:00
Harald Eilertsen
961996a051 Remove duplicate array key in Lib/ThreadItem.php
The key `parent` was set twice in ThreadItem::get_template_data(), once
cast to an intvalue, and later raw from whatever type $item['parent']
held.

Since the value not converted to an int was the one used, this is what
has been kept in this patch, so as to not change existing behaviour.
2025-05-22 19:03:06 +02:00
Harald Eilertsen
0f8500244b Fix undefined variable in Lib/Activity.php
This looks like a simple typo, using camelCase instead of snake case for
the $is_collection_operation variable.

Note:
	However, as it has been undefined, this means it's been treated as
	always false until now, so this may change some behaviour.
2025-05-22 19:03:06 +02:00
Harald Eilertsen
5dcafde617 Fix exclude pattern in phpstan config 2025-05-22 19:03:06 +02:00
Harald Eilertsen
e101e4f511 Minor doxygen config update
- Enable dark mode toggle
- Remove empty footer that broke scrolling
2025-05-22 15:27:35 +02:00
Mario
a5e171a594 Revert "implement thread limit"
This reverts commit be35f612eb.
2025-05-21 11:11:21 +00:00
Mario
be35f612eb implement thread limit 2025-05-21 10:22:37 +00:00
Mario
6d306bc0b3 more return early if we have not got the required data 2025-05-21 09:24:48 +00:00
Mario
813a5b3621 return early if we have not got the required data 2025-05-21 09:23:23 +00:00
Mario
48162fc8d6 fix php warning 2025-05-21 09:13:58 +00:00
Mario
868f9a6b92 bump version 2025-05-20 20:43:10 +00:00
Mario Vavti
e022e59d0a check if function exists - aparently that is not always the case 2025-05-20 22:38:29 +02:00
Mario Vavti
9947f1ec99 add exif to extensions to install 2025-05-20 22:24:25 +02:00
Mario Vavti
6a2748724c guess_image_type(): fix minor issue and add some tests (guessing from getimagesize() and Imagick is not tested) 2025-05-20 22:10:52 +02:00
Mario
457cb74883 Merge branch 'messages-widget-tests' into 'dev'
Add a couple of tests for Widget\Messages

See merge request hubzilla/core!2200
2025-05-20 09:17:05 +00:00
Mario
90b8e08637 since photo_cache images depend on items uese default expire days instead of cache expire days otherwise they might be removed too early (if they have not been removed yet due to cache expire policy) 2025-05-19 22:00:03 +00:00
Mario
572b4a45da fix syntax error 2025-05-19 21:53:49 +00:00
Mario
2e7f806c15 add logging on failure 2025-05-19 21:52:29 +00:00
Mario
4ed8e7b469 refactor guess_image_type() 2025-05-19 21:50:20 +00:00
Harald Eilertsen
1eb3357584 Add a couple of tests for Widget\Messages
Just a couple of basic tests around listing file tags, to make sure the
code runs without any warnings.
2025-05-19 10:24:13 +02:00
Mario
3850cd9641 remove leftover after recent changes 2025-05-19 08:18:47 +00:00
Mario
6f501b37e8 Merge branch 'new-docs' into 'dev'
Replace english docs with pepecyb's docs

See merge request hubzilla/core!2189
2025-05-18 18:42:45 +00:00
Harald Eilertsen
456a741809 Replace english docs with pepecyb's docs 2025-05-18 18:42:45 +00:00
Mario
8a0c9648fe define variable 2025-05-18 16:56:12 +00:00
Mario
4625a8927c Revert "define variable"
This reverts commit a773ca8a24.
2025-05-18 16:55:55 +00:00
Mario
a773ca8a24 define variable 2025-05-18 16:55:20 +00:00
Mario
eb00fe3575 bump version 2025-05-17 18:43:42 +00:00
Mario
1666ca7cb7 actually fix thr_parent arrows 2025-05-17 18:42:42 +00:00
Mario
89aecbbc1c version 2025-05-17 16:21:26 +00:00
Mario
7b8e47b49d revert moving thr_parent retrieval to SQL in favor of a more simple solution (fixes SQL error in postgres and strict mysql) 2025-05-17 16:19:05 +00:00
Mario
cf317aa1a1 revert moving thr_parent retrieval to SQL in favor of a more simple solution (fixes SQL error in postgres and strict mysql)Zotlabs/Lib/ThreadItem.php 2025-05-17 16:18:20 +00:00
Mario
bd1e282499 bump version 2025-05-16 21:03:59 +00:00
Mario
d8a5e8c550 pinned item fixes 2025-05-16 20:43:04 +00:00
Mario
43d0e42425 refactor cache_embeds daemon to be called with uuid (instead of item id) so that it will only be processed once (the queueworker will dismiss duplicates) and add a hook for the photocache addon 2025-05-16 20:22:27 +00:00
Mario
c31b3b6b67 add avif support for php-gd 2025-05-16 07:19:50 +00:00
Mario
18be92ba22 version 2025-05-15 18:58:01 +00:00
Mario
2b38eca986 refactor sql_extra > permission_sql 2025-05-15 18:56:30 +00:00
Mario
4ec895b891 make sure sql_extra is prepared for the c table 2025-05-15 18:40:03 +00:00
Mario
757237cbb4 Revert "make sure sql_extra is prepared for the c table"
This reverts commit 869b047fde.
2025-05-15 18:39:05 +00:00
Mario
869b047fde make sure sql_extra is prepared for the c table 2025-05-15 18:38:42 +00:00
Mario
4bf6752982 implement extra sql for mod channel and display 2025-05-15 16:57:02 +00:00
Mario
5c3f2f9469 first part of cleanups for the conv libs and move thr_parent_uuid retrieval to sql 2025-05-15 12:55:44 +00:00
Mario
6e21be2d94 remove unused var 2025-05-15 11:10:21 +00:00
Mario
3e07eb78fd fix announce source title (addr) not correct 2025-05-15 11:00:18 +00:00
Mario
dceaee691f no need to scroll to toplevels 2025-05-15 07:41:22 +00:00
Mario
7cf380e390 bump version 2025-05-15 07:24:16 +00:00
Mario
9165f766c2 fix offset calculation if element position is relative and use inset box shadow for highlighting items plus some css fixes 2025-05-15 07:23:42 +00:00
Mario
d77d56dfb0 version 2025-05-14 18:54:47 +00:00
Mario
0ae938a4bf another attempt to fix collapsing 2025-05-14 18:54:14 +00:00
Mario
8a0ef4c809 Reapply "fix collapsing"
This reverts commit a3f37e2f1e.
2025-05-14 18:09:46 +00:00
Mario
a3f37e2f1e Revert "fix collapsing"
This reverts commit 948ceb3fac.
2025-05-14 17:57:08 +00:00
Mario
271b9f66ea revert css fixes for messages widget 2025-05-14 16:10:39 +00:00
Mario
4914438479 pinned item template fixes (requires more work) and minor css fixes 2025-05-14 16:07:42 +00:00
Mario
7a01eaa90f version 2025-05-14 11:01:17 +00:00
Mario
948ceb3fac fix collapsing 2025-05-14 11:00:47 +00:00
Mario
d26a3ec22b bump version 2025-05-14 09:41:31 +00:00
Mario
c4a3c7feb7 fix autosave for comments 2025-05-14 09:40:53 +00:00
Mario
6e8481e90e fix notfication issue with update activities 2025-05-14 08:57:47 +00:00
Mario
47d988419f bump version 2025-05-14 08:21:05 +00:00
Mario
a84e6e9747 streamline show more/less handling 2025-05-14 08:19:42 +00:00
Mario
aa9210921d exclude Add/Remove from network nouveau query 2025-05-13 08:49:58 +00:00
Mario
e91f8f3aef bump version 2025-05-13 08:21:20 +00:00
Mario
9b36c39eff remove and deprecate image preload setting - we will now always preload 2025-05-13 08:20:46 +00:00
Mario
a28dd9f2cf remove redundant var 2025-05-13 08:08:27 +00:00
Mario
bf79a2cf4f bump version 2025-05-13 08:05:56 +00:00
Mario
a03ca38409 use box-shadow instead of border to prevent layout moving 2025-05-13 08:05:30 +00:00
Mario
1b246e2ba5 remove redundant item_normal() from item_activity_sql() and use protect_sprintf() on thr_parents 2025-05-13 07:12:20 +00:00
Mario
f905266e4b fix php warning 2025-05-11 19:33:40 +00:00
Mario
e511220022 Merge branch 'remove-direct-access-to-app-observer' into 'dev'
Don't access App::$observer directly in core

See merge request hubzilla/core!2197
2025-05-11 18:08:16 +00:00
Mario
5655807337 fix wrong variable name 2025-05-11 17:56:46 +00:00
Mario
0930539ab4 Merge branch 'lazy' into 'dev'
Implement lazy loading of reactions and sub threads, default to threaded conversation view, some bugfixes and adjusted nomenclature

See merge request hubzilla/core!2198
2025-05-11 17:54:58 +00:00
Mario
b674a1869e undo introduction of new argument in get_template_data(). this can be accomplished more straightforward 2025-05-11 16:01:49 +00:00
Mario
16fafdbbb0 remove redundant query for stored item 2025-05-11 13:39:37 +00:00
Mario
b5acde1420 bump version for merge 2025-05-09 20:34:04 +00:00
Mario
4fa9f30189 cleanup, add some doco an add missing counts of observer event rsvps 2025-05-09 20:24:25 +00:00
Mario
57ae3325e0 allow to switch conv mode per channel 2025-05-09 19:13:49 +00:00
Harald Eilertsen
8141ef9511 Add some API docs for the observer file 2025-05-09 15:23:01 +02:00
Harald Eilertsen
e70870ce4e Move observer helper functions to separate source
The main goal was to move the functions out of the already overcrowded
`boot.php`. While I would ideally have liked to move them properly under
a namespace under `Zotlabs\`, that would break too much existing code at
this point.

Thus leaving it in include and under the global namespace for now.
2025-05-09 15:20:38 +02:00
Harald Eilertsen
7cb8a56b6a Don't access APP:$observer directly in core
Introduce helper functions to access the various fields of the xchan
stored in `App::$observer'. This removes direct access to the attribute
from core, with the aim of allowing further refactoring later.

We can not yet make the `App::$observer` attribute private, though, as
it is also accessed directly by some addons.
2025-05-09 15:15:35 +02:00
Mario
07ca5d54c8 version 2025-05-09 09:43:02 +00:00
Mario
dfde321a3b fix statObj 2025-05-09 09:42:29 +00:00
Mario
aab53c9fc9 provide owner uid for item_normal() in items_by_thr_parent() and set history.pushState when requesting a subthread 2025-05-09 09:35:31 +00:00
Mario
9def0f280f notification fixes 2025-05-09 08:53:47 +00:00
Mario
e71c95a5a5 refactor item_normal() to allow a profile_uid and a prefix as arguments 2025-05-08 12:05:31 +00:00
Mario
1be6c11bad implement moderation of reactions 2025-05-08 11:48:58 +00:00
Mario
030fe8c656 bump version 2025-05-08 10:36:45 +00:00
Mario
8f539e3707 fix announce action label and modal not closing 2025-05-08 10:35:32 +00:00
Mario
918377a67b provide commentable state and minor cleanup 2025-05-08 10:27:57 +00:00
Mario
25e29ec544 fix wrong order of args 2025-05-08 08:39:32 +00:00
Mario
71f80aa4d2 cleanup template 2025-05-07 19:23:21 +00:00
Mario
070d839ad9 version 2025-05-07 19:03:26 +00:00
Mario
ee9b53132e wording, fix comment preview and remove logging 2025-05-07 19:02:53 +00:00
Mario
ca4bb5927e whitespace 2025-05-07 12:36:40 +00:00
Mario
6ed56f9996 sess_data has been changed to mediumtext a while ago via update 2025-05-07 12:33:17 +00:00
Mario
ac5d22d514 refactor doreply() and move reply functionality to modal 2025-05-07 12:31:47 +00:00
Mario
21279b7d4e version 2025-05-06 09:14:17 +00:00
Mario
77c310e393 mark lazy loaded items read and minor cleanup 2025-05-06 09:13:35 +00:00
Mario
c5d75f23e5 fix title and summary converted to bbcode 2025-05-06 06:57:45 +00:00
Mario
a00db63b3e imagesLoaded() can deal with strings 2025-05-06 06:45:58 +00:00
Mario
1d60d38cdc version 2025-05-06 06:41:57 +00:00
Mario
0b42197a99 allow preloading images if dom element is not yet in page 2025-05-06 06:41:26 +00:00
Mario
07da20d00c fix verb and hash for notifications 2025-05-05 07:44:39 +00:00
Mario
d413cf8a30 wrong logic 2025-05-04 20:15:30 +00:00
Mario
1da0ea86f0 more wording 2025-05-04 19:45:57 +00:00
Mario
72f36920ef more on wording 2025-05-04 19:35:14 +00:00
Mario Vavti
7a4d568bfb fix query for postgres 2025-05-04 21:27:41 +02:00
Mario Vavti
0d1c5e26a7 do not add the item-highlight class to any toplevel item 2025-05-04 21:13:36 +02:00
Mario Vavti
ff0a5a0507 mod network fixes 2025-05-04 20:46:49 +02:00
Mario
da780e4e08 this got removed by accident 2025-05-04 16:06:58 +00:00
Mario
0c783773d8 more streamline wording 2025-05-04 15:41:43 +00:00
Mario
5edeb7bd53 remove uuid from mid queries (it is not needed) and slightly alter the query to fetch the parents 2025-05-04 15:40:17 +00:00
Mario
20c8f69efa use profile_uid for mod channel 2025-05-04 13:22:18 +00:00
Mario
b694ed6229 handle blog_mode in conv template 2025-05-04 04:36:12 +00:00
Mario
b4f7daadfb improved blog_mode query 2025-05-03 20:43:26 +00:00
Mario
fb3eae96ab implement blog_mode 2025-05-03 19:04:20 +00:00
Mario
cc91446c18 unseen items: only show announce activities but not the resulting item. also fix a missing operator 2025-05-03 18:03:24 +00:00
Mario
22bda936a5 more streamline wording 2025-05-03 15:58:04 +00:00
Mario
95a2de4da2 Reapply "streamline wording"
This reverts commit ee3fc54be6.
2025-05-03 15:55:13 +00:00
Mario
ee3fc54be6 Revert "streamline wording"
This reverts commit e2c477d775.
2025-05-03 15:52:30 +00:00
Mario
e2c477d775 streamline wording 2025-05-03 15:42:26 +00:00
Mario
6ddd31bfba bump version 2025-05-03 09:42:08 +00:00
Mario
3653f769d9 fix notification button for medium screen size (right aside collapsed) 2025-05-03 09:41:41 +00:00
Mario
261f0e4120 default ordering to created 2025-05-03 09:06:15 +00:00
Mario
6fce724f69 yet another refactor to cover unseen pubstream notifications 2025-05-03 08:32:01 +00:00
Mario
dedab1ad4f this performs way better 2025-05-02 16:48:34 +00:00
Mario
cf1a404945 bump version 2025-05-02 16:24:54 +00:00
Mario
ac4af6f9ef Ãfix marking read of reactions 2025-05-02 16:02:47 +00:00
Mario
69ade6d2cb cleanup 2025-05-02 14:03:45 +00:00
Mario
6db5236bf5 provide thr_parent uuid in the item array and make use of it in various places 2025-05-02 13:58:01 +00:00
Mario
b39fb945f5 move look up of thr_parents to separate function and minor fixes 2025-05-02 13:24:58 +00:00
Mario
3fcd5a0f0f fix display issue 2025-05-02 07:52:41 +00:00
Mario
62635aa803 version 2025-05-01 10:52:32 +00:00
Mario
32717d910c add counts for rsvp activities and some visual shenenigans 2025-05-01 10:52:15 +00:00
Mario
881f540e43 some visual changes 2025-04-30 19:31:31 +00:00
Mario
e5cd6330b3 default to threaded 2025-04-30 14:42:09 +00:00
Mario
e91021f648 rename wrapper class 2025-04-30 09:06:01 +00:00
Mario
1463c1a526 refactor 2025-04-30 08:28:47 +00:00
Mario
e195897dc7 slightly change the way we deal with perms 2025-04-29 21:18:53 +00:00
Mario
b6f0fe7583 refactor with permissions 2025-04-29 09:52:30 +00:00
Mario Vavti
377fe32795 add branch to version string 2025-04-26 22:41:35 +02:00
Mario Vavti
73d444bd8a some refactoring and implement subthread fetching 2025-04-26 22:33:32 +02:00
Mario Vavti
80415f6bd3 remove unused code 2025-04-26 14:33:45 +02:00
Mario Vavti
7ab531025c streamline sql functions 2025-04-26 12:19:04 +02:00
Mario Vavti
763a4f1fbb implement observer activity visibility 2025-04-26 10:46:48 +02:00
Mario Vavti
cde558a43f also remove the parent line from the query 2025-04-25 23:47:04 +02:00
Mario Vavti
bafbbc9d5b revert adding of id - we can not use it 2025-04-25 23:22:06 +02:00
Mario Vavti
ad4eec1145 kind of fix the like response 2025-04-25 23:02:28 +02:00
Mario Vavti
0584094f05 use normal size modal 2025-04-25 22:36:27 +02:00
Mario Vavti
c614f899b7 implement requesting of reactions 2025-04-25 22:35:13 +02:00
Mario Vavti
0940be57fd should compare against item.mid 2025-04-25 16:47:15 +02:00
Mario Vavti
7e148c1e30 the c table must also include item_normal() 2025-04-25 16:16:37 +02:00
Mario
babc3df364 this should not be required anymore 2025-04-25 12:53:47 +00:00
Mario
ba9637e129 lazy load - initial commit 2025-04-25 12:46:30 +00:00
Mario Vavti
a48a72d1cd use a separator 2025-04-24 10:05:00 +02:00
Mario Vavti
1edea291a4 we have update an existing result - do not create another result set for this action 2025-04-24 09:58:37 +02:00
Mario
b7ef3fab3c dreport: do not store dismissed create activities 2025-04-24 07:09:29 +00:00
Mario
afe31160db mod item: deprecate x() and use $_POST instead of $_REQUEST 2025-04-24 06:41:43 +00:00
Mario
4849d50610 add some styling to dreport 2025-04-23 20:17:02 +00:00
Mario
d5f7b620c4 fix regex to catch codeblocks with params like class etc 2025-04-23 19:37:57 +00:00
Mario
6515443957 more deprecate x() 2025-04-23 19:09:12 +00:00
Mario
8b352e4c64 more deprecate x() 2025-04-23 18:41:26 +00:00
Mario
7c59dd9fd7 more deprecate x() 2025-04-23 18:17:23 +00:00
Mario
118a223ee1 fix term.imgurl not stored in item_store_update() 2025-04-23 09:10:58 +00:00
Mario
481113f641 set $plink to null if not provided 2025-04-22 09:30:35 +00:00
Mario
bed929c966 define variable 2025-04-21 19:33:46 +00:00
Mario
10345a5418 more php warnings 2025-04-21 19:28:44 +00:00
Mario
efc9581798 comment out localize_item() in Enotify::submit() and fix some PHP warnings 2025-04-21 19:18:37 +00:00
Mario
005b9919d0 x() to !empty() 2025-04-21 17:25:20 +00:00
Mario
86e88c6bb0 also continue if we have not got a filepath for some reasonà 2025-04-21 15:27:55 +00:00
Mario
7505f7039a define variable 2025-04-21 11:52:34 +00:00
Mario
5a52b6afea define variable 2025-04-21 11:42:24 +00:00
Mario
86f8d8ecdf fix php warnings 2025-04-21 11:39:35 +00:00
Mario
81559e9bcf fix php warnings 2025-04-21 11:30:42 +00:00
Mario
1b458c7d65 missing include 2025-04-21 10:15:51 +00:00
Mario
d2177f27f9 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-04-21 10:09:54 +00:00
Mario
da6db54122 some cleanup for boot.php 2025-04-21 10:09:33 +00:00
Mario Vavti
1e41c83b35 fix test 2025-04-20 21:43:32 +02:00
Mario
8bc079aa3d cleanup Web/Router 2025-04-20 19:23:54 +00:00
Mario
a5a58d73ff cleanup Lib/WebServer 2025-04-20 18:42:26 +00:00
Mario
68967e93d6 wrong class 2025-04-20 16:16:15 +00:00
Mario Vavti
88f1a7d652 whitespace 2025-04-20 18:11:12 +02:00
Mario Vavti
21c2fabae0 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-04-20 18:06:03 +02:00
Mario Vavti
a36afc0bb7 rawurlencode the cloud paths - issue #1903 (second part) 2025-04-20 18:04:59 +02:00
Mario Vavti
7855aaf53c set App::$query_string from from server.request_uri instead of server.query_string because the latter will mostly be urldecoded by the server already - issue #1903 (first part) 2025-04-20 18:04:03 +02:00
Mario Vavti
6535475a99 add some padding for breadcrumb 2025-04-20 17:59:40 +02:00
Mario
ddaaac1103 Merge branch 'debian_install_20250415' into 'dev'
debian install script

See merge request hubzilla/core!2196
2025-04-19 05:45:17 +00:00
Mario
295266cb10 Merge branch 'refactor-language-selector' into 'dev'
Move lang_selector function to Lang module and refactor

See merge request hubzilla/core!2195
2025-04-19 05:43:26 +00:00
Harald Eilertsen
e800d176fb Move lang_selector function to Lang module and refactor 2025-04-19 05:43:25 +00:00
Mario Vavti
cace4c6c65 strtotime() accounts with the timezone - set it to UTC for the stored timestamp 2025-04-18 22:38:45 +02:00
Mario Vavti
d7aff9a4dd fix wrong logic 2025-04-18 21:19:38 +02:00
Mario Vavti
a4a7794315 implement until= in message filter 2025-04-18 21:00:33 +02:00
Mario Vavti
c1d87fa65d more doco 2025-04-18 10:27:13 +02:00
Mario Vavti
e69763f86d add more message filter tests 2025-04-18 10:21:16 +02:00
Mario Vavti
5db5a5cfe9 fixes and more test samples 2025-04-17 14:29:27 +02:00
Mario Vavti
24f71dfcf8 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-04-17 11:35:24 +02:00
Mario Vavti
6d62acb446 extend MessageFilter::test_condition() to deal with && and || conditions and add tests 2025-04-17 11:35:06 +02:00
Mario
32ab6344c4 use mb_strlen() instead of strlen() 2025-04-16 18:51:59 +00:00
Mario
60b4c003af $_REQUEST > $_POST 2025-04-16 11:07:03 +00:00
Mario
4c962417ff pÃrevent storing files/folder with filenames exceeding their max name length 2025-04-16 11:03:08 +00:00
Mario
d2aad8a41a stephenhill/base58 seems not maintained anymore and throws a deprecation warning - fix here for now 2025-04-16 08:36:10 +00:00
Mario
fd0f6d4fa8 check if the uuid is mapped before using it 2025-04-16 08:15:30 +00:00
Mario
87d68b175e fix undefined variable warning 2025-04-16 08:08:02 +00:00
Mario
64652c1d6e fix variable name 2025-04-16 07:51:22 +00:00
Mario
9342a92682 deal with link type attachments 2025-04-16 07:44:40 +00:00
Mario
443c5495e9 do not translate network to stream 2025-04-16 07:44:12 +00:00
OJ Randon
80f3892ba0 check if update script for selfHOST was downloaded 2025-04-15 17:39:14 +02:00
OJ Randon
16ee954048 use wget to download composer and checksum 2025-04-15 17:36:47 +02:00
OJ Randon
4e367e1e67 Merge remote-tracking branch 'official/dev' into fix_debianinstall 2025-04-15 17:31:28 +02:00
Mario
7f7763ee0c remove redundant var declaration 2025-04-15 14:38:28 +00:00
Mario
868a8ccfd9 fix more php warnings 2025-04-15 14:37:38 +00:00
Mario
b7b1a5574f fix more php warnings 2025-04-15 14:28:42 +00:00
Mario
aa0c8973fa fix more php warnings 2025-04-15 14:25:48 +00:00
Mario
f6b91f97bf this should be info instead of notice 2025-04-15 14:21:20 +00:00
Mario
d82e7c9f6c fix php warning 2025-04-15 14:20:27 +00:00
Mario
083b2b1bbc add suport for strong bbcode tag 2025-04-15 09:10:45 +00:00
Mario
805bbd1c3e fix color bbcode markup 2025-04-15 09:03:32 +00:00
Mario
077ca1aea5 change photo.filename to type text (some platforms have very long filenames for their profile photos) 2025-04-15 08:41:10 +00:00
Mario
1217ae3b3e remove it 2025-04-15 08:05:22 +00:00
Mario
17777981ac this should not be required because it only affects likes which are already handled before (reaction) 2025-04-15 07:43:11 +00:00
Mario
ddc4bdcebe set default value and add comment 2025-04-14 16:41:39 +00:00
Mario Vavti
126c7f9d62 improved tests 2025-04-14 17:35:35 +02:00
Mario
75d1be092e default to null 2025-04-14 14:34:21 +00:00
Mario Vavti
0d51ff1906 add test for Activity::getUUID() and Avtivity::getMessageID() methods 2025-04-14 13:23:49 +02:00
Mario
679b5098da provide methods to get mid and uuid from activity object, add quirks to retrieve markdown content 2025-04-14 09:24:20 +00:00
Mario
224b32e767 remove borders from some buttons 2025-04-14 07:25:08 +00:00
Mario
1f943474cb boxy: up padding left also for the itrm tools 2025-04-13 18:49:38 +00:00
Mario
6891db1b23 boxy: minor css fix 2025-04-13 13:55:11 +00:00
Mario
a19951528d minor css fix 2025-04-13 09:16:03 +00:00
Mario
a4ea4ecc3b video poster requires a single quote 2025-04-11 20:03:01 +00:00
Mario Vavti
8c182c9d68 version 10.2.3 2025-04-11 21:30:03 +02:00
Mario
384eea79dc changelog
(cherry picked from commit 6068c0e8c2)

Co-authored-by: Mario <mario@mariovavti.com>
2025-04-11 19:28:46 +00:00
Mario
c2a09434ca Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-04-11 19:28:01 +00:00
Mario
6068c0e8c2 changelog 2025-04-11 19:27:28 +00:00
Mario Vavti
f8bc755c20 fix bougus merge to master 2025-04-11 21:13:40 +02:00
Mario
fd86ccb4eb Merge branch 'increase-memory-limit-for-ci' into 'dev'
CI: Up the memory limit to allow for coverage reporting

See merge request hubzilla/core!2194
2025-04-11 09:38:16 +00:00
Mario
c36d159997 Merge branch 'fix_debianinstall' into 'dev'
Fix debianinstall

See merge request hubzilla/core!2192
2025-04-11 09:37:29 +00:00
OJ Random
efd61cb43a Fix debianinstall 2025-04-11 09:37:29 +00:00
Mario
b79c19c66d changelog 2025-04-11 08:36:28 +00:00
Harald Eilertsen
ee4cc51a05 CI: Up the memory limit to allow for coverage reporting 2025-04-10 21:45:22 +02:00
Mario
7c79ec9626 fix an obscure delivering issue which incidentally also fixes a delivering issue that required an ugly hack to work around 2025-04-10 13:03:11 +00:00
Mario
66f793cb83 more fix downstream test for an upstream bug 2025-04-09 16:07:00 +00:00
Mario
6d926f4271 fix downstream test for an upstream bug 2025-04-09 15:47:21 +00:00
Mario Vavti
6ffd3fd9d4 composer update chillerlan/php-qrcode 2025-04-09 15:54:58 +02:00
Mario Vavti
3d204c782b --no-dev! 2025-04-09 15:47:23 +02:00
Mario Vavti
20f8239b44 composer update commerceguys/intl 2025-04-09 15:41:01 +02:00
Mario Vavti
10c2bd83d3 Merge branch 'dev' of https://framagit.org/hubzilla/core into dev 2025-04-09 14:22:23 +02:00
Mario Vavti
4b02bd60cb composer update libs 2025-04-09 14:19:48 +02:00
Mario
50ea3afcb0 re-add red-koala.png which has been removed by accident 2025-04-09 10:43:57 +00:00
Mario
982d7540cc wrong ticks 2025-04-09 10:29:23 +00:00
Mario
b9aec78dde rename README > README.md 2025-04-09 10:26:00 +00:00
Mario
f5b1fda6b7 reorganize emojis and allow custom site emojis 2025-04-09 10:21:01 +00:00
Mario
4dfdb2b2e1 bump item.obj and item.target to mediumtext for mysql 2025-04-05 18:48:07 +00:00
Mario
a5a2d80c50 fix some plink for forum posts and comments 2025-04-05 15:55:58 +00:00
Mario
8da7fe8dbe whitespace 2025-04-04 15:44:04 +00:00
Mario
f64c5977f8 move weblink to after embedPhotos 2025-04-04 15:42:17 +00:00
Mario
a16e024b51 cleanup 2025-04-04 15:34:55 +00:00
Mario
860de5e28e bump version 2025-04-04 15:22:46 +00:00
Mario
b38da72535 move jot related functions to jot-header and some cleanup 2025-04-04 15:22:09 +00:00
Mario
2c4f1dcf03 use the syntax which allows image descriptions 2025-04-04 15:20:57 +00:00
Mario
0a1255518f bump version 2025-04-04 10:56:01 +00:00
Mario
a822e94f9b move jot popup handling to template, port photo selector to vanilla javascript and enable it for comments if applicable 2025-04-04 10:51:51 +00:00
Mario
484971c90a use png for hubzilla emoji 2025-04-04 07:15:23 +00:00
Mario
e11035acf4 optimize svg 2025-04-03 18:17:25 +00:00
Mario
3d522b99d8 add hubzilla emoji, make sure the compared term type is an integer 2025-04-03 18:12:23 +00:00
Mario
e62c4e6088 some emoji type tags do not have an id, we will use icon.url instead in those cases 2025-04-03 17:15:22 +00:00
Mario
703ccc2a9a this is required e.g. when relaying emoji reactions otherwise the relevant data will be missing 2025-04-03 12:01:11 +00:00
Mario
b2d09134c1 more smilies() fixes 2025-04-03 11:58:13 +00:00
Mario
de3f491966 make sure object actor is an array if we are going to use it 2025-04-03 11:57:25 +00:00
Mario
322a021765 for consistency use just the name as short name 2025-04-03 10:01:09 +00:00
Mario
d191b66bb8 move custom emoji rendering to smilies() 2025-04-03 09:55:59 +00:00
Mario
5241a159d2 bump version 2025-04-02 10:53:50 +00:00
Mario
fe8ae2f88e make sure we have an encoded activity before we go on building the packet, do not json_encode() a possibly empty item.target 2025-04-02 10:53:13 +00:00
Mario
3550609d29 do not return success if Libzot::fetch() did not return anything useful and remove redundant parameter $hub 2025-04-02 10:49:38 +00:00
OJ Randon
c6df3b0b97 fix stop of mysql 2025-04-02 11:38:22 +02:00
OJ Randon
e6ca21965b check if updates work 2025-04-01 13:39:41 +02:00
OJ Randon
c44c522d7d wording and confirmation that Raspi 5 is working 2025-04-01 01:10:16 +02:00
OJ Randon
f48bf8e366 fix certbot installation 2025-04-01 00:45:59 +02:00
OJ Randon
6e1cdebbf4 use https for selfhost updater (dyn dns) 2025-04-01 00:45:10 +02:00
Mario
eb592bb741 bump version 2025-03-28 20:49:27 +00:00
Mario
db5d67a4e0 cleanup deprecated forum queries and possibly improved performance 2025-03-28 20:46:17 +00:00
Mario
6e390a06e1 remove deprecated emojiReaction 2025-03-28 20:43:08 +00:00
Mario
30d552255e lazy load profile images for likes and remove zid function for the profile image src attribute 2025-03-28 13:29:43 +00:00
Mario
0522bd5593 include unapproved connections in deliverable_abook_xchans(). here is why: if they are at a hub, where an approved connection already exists, they will already receive the message since our defaultult permission allows view_stream. If view_stream is not allowed they will be filtered later in the process. We will however exclude blocked connections (approved or unapproved) already here now. 2025-03-27 12:43:53 +00:00
Mario
a46d837267 minor cleanup 2025-03-27 10:31:07 +00:00
Mario
bee493f628 remove redundant variables and use document.body.clientWidth instead of rounded window.innerWidth 2025-03-26 11:20:11 +00:00
Mario
a4f4a082af fix another js error on small screens and use const instead of let where applicable 2025-03-25 10:38:00 +00:00
Mario
dd9ab07044 restrict access to tool to local channels 2025-03-24 09:11:26 +00:00
Mario
afd811a875 more deprercate jquery 2025-03-23 14:14:19 +00:00
Mario Vavti
122d46b79b use the new version of unparse_url() and fall through to destination if nothing before applied - otherwise remote redirects will fail (e.g. being logged in to a remote server and vewing a linked image with an attached zid at another server) 2025-03-22 18:18:55 +01:00
Mario
36448adad4 minor refactor 2025-03-21 16:58:27 +00:00
Mario
8f93d9aa75 do not check length, it will not be there if the var is null 2025-03-21 11:33:56 +00:00
Mario
76f50d7150 fix some js errors on mobile devices 2025-03-21 11:30:40 +00:00
Mario
80c9880a67 do not include our own activities in unseen forum notifications 2025-03-20 13:39:32 +00:00
Mario
d1f54507f6 Disable browser rotating image based on EXIF metadata. We remove EXIF data for thumbnails but not for the original files. If the preview image has different orientation than the original size it has a strange effect when viewing. 2025-03-20 13:12:45 +00:00
Mario
334852d733 bump version 2025-03-18 08:15:30 +00:00
Mario
34b2bdcf2c changelog 2025-03-18 08:08:14 +00:00
Mario
5188b9cef5 we require the HTTP signature to be set in the HTTP_AUTHORIZATION field somewhere down the line (probably in Web/HTTPSig). Make sure it is there. 2025-03-18 07:52:39 +00:00
Mario
8c0b7ce4de Merge branch 'help_davs' into 'dev'
access cloud files under linux using davs

See merge request hubzilla/core!2190
2025-03-17 20:02:25 +00:00
Mario
17d0d6f75d Merge branch 'dedup-head_get_icon' into 'dev'
Remove duped head_get_icon function

See merge request hubzilla/core!2191
2025-03-17 20:00:58 +00:00
Harald Eilertsen
059113d2a8 Remove duped head_get_icon function
This function was existing both as a static function in the App class,
and as a standalone global function. While only the standalone function
was used, this patch changes the callsite to use the static function in
the App class instead, and removed the global variant.

Note: While unlikely, this _could_ break some addons or custom themes
_if_ they call the global head_get_icon function themselves. I have not
found any addons or themes that do so.

This patch also fixes an issue where an incorrect url would be produced
if the URL set by App::head_set_icon would start with '://' for some
reason.

Also added doc comments for both App::head_set_icon and
App::head_get_icon.
2025-03-17 20:17:54 +01:00
OJ Random
f60dff788e access cloud files under linux using davs 2025-03-17 19:03:19 +01:00
4847 changed files with 459462 additions and 145928 deletions

View File

@@ -34,14 +34,14 @@ if you look for more choices. The main differences are:
Hardware
+ internet connection and router at home
+ computer connected to your router (a Raspberry 3 will do for very small Hubs)
+ computer connected to your router (a Raspberry 4 will do for very small Hubs)
Software
+ fresh installation of Debian 12 (bookworm)
+ fresh installation of Debian 12 (bookworm) or Raspberry Pi OS
+ router with open ports 80 and 443 for your web server
You can of course run the script on a VPS or any distant server as long as the above sotfware requirements are satisfied.
You can of course run the script on a VPS or any distant server as long as the above software requirements are satisfied.
## How to run the script
@@ -80,6 +80,11 @@ Switch the verification off
util/config system verify_email 0
Check if updates from the repository do work
util/udall
## What the script will do for you...
+ install everything required by your hubzilla instance, basically a web server (Apache), PHP, a database (MySQL), certbot,...
@@ -99,7 +104,7 @@ The script is known to work without adjustments with
+ Hardware
- standard PC with Debian 12 (bookworm)
- Raspberry 4 with Raspbian, Debian 12 (TODO: needs confirmation after swich to Debian12)
- Raspberry 5 with Raspberry Pi OS, Debian 12
- for tesing purposes: under localhost inside a virtual machine, [KVM](https://wiki.debian.org/KVM)
+ DynDNS
- selfHOST.de
@@ -149,5 +154,3 @@ It is recommended to run the Raspi without graphical frontend (X-Server). Use...
to boot the Rapsi to the client console.
DO NOT FORGET TO CHANGE THE DEFAULT PASSWORD FOR USER PI!

View File

@@ -160,8 +160,8 @@ function install_composer {
print_info "We check if Composer is already downloaded"
if [ ! -f /usr/local/bin/composer ]
then
EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')"
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
EXPECTED_CHECKSUM="`wget -qO- https://composer.github.io/installer.sig`"
wget https://getcomposer.org/installer -O composer-setup.php
ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"
if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]
then
@@ -171,6 +171,7 @@ function install_composer {
fi
php composer-setup.php --quiet
RESULT=$?
composer --version
rm composer-setup.php
# exit $RESULT
# We install Composer globally
@@ -181,7 +182,7 @@ function install_composer {
fi
cd $install_path
export COMPOSER_ALLOW_SUPERUSER=1;
/usr/local/bin/composer install --no-dev
/usr/local/bin/composer install --no-dev --quiet
/usr/local/bin/composer show
export COMPOSER_ALLOW_SUPERUSER=0;
}
@@ -307,10 +308,19 @@ function install_run_selfhost {
# https://carol.selfhost.de/update?username=123456&password=supersafe
#
# the prefered way
wget --output-document=$selfhostdir/$selfhostscript http://jonaspasche.de/selfhost-updater
echo "router" > $selfhostdir/device
echo "$selfhost_user" > $selfhostdir/user
echo "$selfhost_pass" > $selfhostdir/pass
if [ ! -f $selfhostdir/$selfhostscript ]
then
wget --output-document=$selfhostdir/$selfhostscript https://jonaspasche.de/selfhost-updater
if [ ! -s $selfhostdir/$selfhostscript ]
then
die "Failed to download selfHOST file for dynDNS"
fi
echo "router" > $selfhostdir/device
echo "$selfhost_user" > $selfhostdir/user
echo "$selfhost_pass" > $selfhostdir/pass
print_info "Wrote file to update dynamic IP. File: $selfhostdir/$selfhostscript"
fi
print_info "executing $selfhostdir/$selfhostscript update..."
bash $selfhostdir/$selfhostscript update
fi
}
@@ -386,7 +396,7 @@ function install_letsencrypt {
then
die "Failed to install let's encrypt: 'le_email' is empty in $configfile"
fi
nocheck_install "certbot python-certbot-apache"
nocheck_install "certbot python3-certbot-apache"
print_info "run certbot ..."
certbot --apache -w $install_path -d $le_domain -m $le_email --agree-tos --non-interactive --redirect --hsts --uir
service apache2 restart
@@ -441,7 +451,7 @@ function configure_cron_daily {
echo "echo \" \"" >> /var/www/$cron_job
echo "echo \"\$(date) - stopping apache and mysql...\"" >> /var/www/$cron_job
echo "service apache2 stop" >> /var/www/$cron_job
echo "/etc/init.d/mysql stop # to avoid inconsistencies" >> /var/www/$cron_job
echo "systemctl stop mysql.service # to avoid inconsistencies" >> /var/www/$cron_job
echo "#" >> /var/www/$cron_job
echo "echo \"\$(date) - renew certificate...\"" >> /var/www/$cron_job
echo "certbot renew --noninteractive" >> /var/www/$cron_job

View File

@@ -1,4 +1,5 @@
stages:
- pretest
- test
- deploy
@@ -25,23 +26,6 @@ variables:
POSTGRES_USER: ci-user
POSTGRES_PASSWORD: ci-pass
before_script:
# Install & enable Xdebug for code coverage reports
- apt-get update
- apt-get install -yqq libicu-dev libjpeg-dev libpng-dev libpq-dev libyaml-dev libzip-dev mariadb-client postgresql-client unzip zip
- pecl install xdebug yaml
- docker-php-ext-enable xdebug yaml
- docker-php-ext-configure gd --with-jpeg=/usr/include/
- docker-php-ext-install gd bcmath intl pdo_mysql pdo_pgsql zip
# Install composer
- curl -sS https://getcomposer.org/installer | php
# Install dev libraries from composer
- php ./composer.phar install --no-progress
# php.ini settings
- echo 'xdebug.mode=coverage' >> /usr/local/etc/php/php.ini
# hidden job definition with template for MySQL/MariaDB
.job_template_mysql: &job_definition_mysql
stage: test
@@ -53,13 +37,13 @@ before_script:
HZ_TEST_DB_DATABASE: $MYSQL_DATABASE
script:
# Import hubzilla's DB schema
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" --skip-ssl "$MYSQL_DATABASE"
# Show databases and relations/tables of hubzilla's database
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" "$MYSQL_DATABASE"
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" --skip-ssl "$MYSQL_DATABASE"
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" --skip-ssl "$MYSQL_DATABASE"
# Run the actual tests
- touch dbfail.out
- vendor/bin/phpunit --configuration tests/phpunit.xml --no-progress --stop-on-error --coverage-text --colors=never --log-junit tests/results/junit.xml || exit_code=$?
- vendor/bin/phpunit -d memory_limit=256M --configuration tests/phpunit.xml --no-progress --stop-on-error --coverage-text --colors=never --log-junit tests/results/junit.xml || exit_code=$?
- if [ $exit_code -ne 0 ]; then echo "Test barfed!"; cat dbfail.out; exit $exit_code; fi
coverage: '/^\s*Lines:\s*\d+.\d+\%/'
@@ -84,7 +68,7 @@ before_script:
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt;"
# Run the actual tests
- touch dbfail.out
- vendor/bin/phpunit --configuration tests/phpunit.xml --no-progress --stop-on-error --coverage-text --colors=never --log-junit tests/results/junit.xml || exit_code=$?
- vendor/bin/phpunit -d memory_limit=256M --configuration tests/phpunit.xml --no-progress --stop-on-error --coverage-text --colors=never --log-junit tests/results/junit.xml || exit_code=$?
- if [ $exit_code -ne 0 ]; then echo "Test barfed!"; cat dbfail.out; exit $exit_code; fi
coverage: '/^\s*Lines:\s*\d+.\d+\%/'
@@ -100,29 +84,55 @@ before_script:
paths:
- tests/results/
default:
image: php:8.2
before_script:
# Install & enable Xdebug for code coverage reports
- apt-get update
- apt-get install -yqq libicu-dev libjpeg-dev libpng-dev libpq-dev libyaml-dev libgmp-dev libzip-dev mariadb-client postgresql-client unzip zip
- pecl install xdebug yaml
- docker-php-ext-enable xdebug yaml
- docker-php-ext-configure gd --with-jpeg=/usr/include/
- docker-php-ext-install gd gmp intl pdo_mysql pdo_pgsql zip exif
# PHP8.1 with MySQL 8.0
php8.1_mysql8.0.22:
image: php:8.1
# Install composer
- curl -sS https://getcomposer.org/installer | php
# Install dev libraries from composer
- php ./composer.phar install --no-progress
# php.ini settings
- echo 'xdebug.mode=coverage' >> /usr/local/etc/php/php.ini
check_templates:
stage: pretest
script:
- touch .htconfig.php
- php util/precompile_smarty3.php
phpstan:
stage: pretest
script:
- touch .htconfig.php
- vendor/bin/phpstan --memory-limit=512M
# PHP8.2 with MySQL 8.0
php8.2_mysql8.0.22:
services:
- mysql:8.0
<<: *job_definition_mysql
<<: *artifacts_template
# PHP8.1 with MariaDB 10.6
php8.1_mariadb10.6:
image: php:8.1
# PHP8.2 with MariaDB 10.6
php8.2_mariadb10.6:
services:
- name: mariadb:10.6
alias: mysql
<<: *job_definition_mysql
<<: *artifacts_template
# PHP8.1 with PostgreSQL 12
php8.1_postgres12:
image: php:8.1
# PHP8.2 with PostgreSQL 14
php8.2_postgres14:
services:
- postgres:12-alpine
- postgres:14-alpine
<<: *job_definition_postgres
<<: *artifacts_template

220
CHANGELOG
View File

@@ -1,3 +1,223 @@
Hubzilla 10.6 (2025-11-04)
Features
- Improved background fetching of replies collection
- Refactor ASCache and implement ASCache::isCacheable()
- Implement ASCache in ASCollection to improve performance
- Add json support for help index widget and add a new template
- Implement hidden pconfig system.notifications_count_limit
- Implement hidden pconfig system.invert_notifications_order
- Implement mark seen button for unseen pubstream notifications
- Iplemented per forum unseen items notifications
- Intoduce Activity::pasteQuote() to paste the quote into the body at the right place if applicable and add test
- Implement ASCache in Activity::get_quote()
- Display webfinger address instead of channel URL in HQ widget notifications tab for consistency with other tabs
- Implement FEP-e232 object links
Maintenance
- Use a better supported json canonicalization library
- CI: up PHP images to version 8.2
- Enable gmp PHP extension in gitlab-ci
- Update composer libs which now require gmp PHP extension
- HTTPSig: return early if we got no key info
- Removed deprecated conv_list template
- Refactor PhotoGd::imageString() to reduce complexity
- Add ImageQuality class to hold quality values
- CI: introduce a quick test to check that smarty templates compile in the pretest stage
- CI: upgrade to postgres 13
- CI: add pretest step and run PHPStan
- Updated Spanish strings
- Remove redundant f arg in tagcloud widget
- Remove dead code in handle_tag()
- Enable unit tests for extensions
- Remove obsolete phpunit configurations
- Code cleanup
Bugfixes
- Fix encoding for webpage, layout and block title and body when editing - issue #1946
- Fix issues which prevented files and photos to be updated correctly after rename via DAV
- Fix contact edit modal logic hijacking URL fragment on pages other than /connections
- Fix deprecation notice in MessagesWidgetTest
- Fix whole URL punified in mod follow
- Fix whole URL punified in mod search
- Fix setup check for zip PHP extension
- Fix issue where remote channels could post to wall if they had write_storage permission - issue #1940
- Fix not all notification icons updated
- Fix notification icon not updated on medium screen size
- Fix image/gif not handled in PhotoGd::imageString()
- Fix issue where not all seen items where removed from the unseen notifications
- Fix item_by_item_id() not returning Announce items - issue #1936
- Fix archive widget not returning results if in single item mode - issue #1911
- Fix db query in verify_email_address()
- Fix search in mod channel only returning single post items - issue #1929
- Fix commented out register_account hook breaking notify admin addon
Addon
- Superblock: add tests
- Wiki: fix photo embed buton for markdown
- Pubcrawl: return early if we could not find an AS actor id and add some logging
- Cart: adapt logic to fetch tpl from theme folder first
- Cavatar: remove redundant arg from cavatar_init()
Hubzilla 10.4.4 (2025-10-06)
- Fix issue when confirming pending registrations
- Fix TOS headings
- Fix TOS paths
- Add english TermsOfService.md
- CI: skip ssl check
Hubzilla 10.4.3 (2025-08-15)
- Refactor module vote to prohibit double votes at the sender side
- Fix vote answers counted as comments
- Start transition of deprecated AS1 item.verb vocabulary to AS2 on demand in mod channel, articles and cards
- Fix regression in retrieving channel address in wtagblock() and whitespace fixes
Hubzilla 10.4.2 (2025-08-08)
- Implement item_custom_display hook in mod HQ
- Refactor item fetching functions to reflect item_normal() changes
- Refactor item_normal() to optionally deal with various item types
- Fix missing reactions modal in threaded_conversation.tpl
- Improve memory consumption in drop_related() to fix deleting of big threads
- Fix PHP error with the potential to stuff up the queueworker
- Update the contact edit header so that both, image and text are linked to the profile
- Articles: refactor to reflect item_normal() changes
- Cards: refactor to reflect item_normal() changes
Hubzilla 10.4.1 (2025-07-31)
- Fix regression in pubstream tag view
- Fix photos meta data not updated when renaming folder in files app
- Fix syntax error in custom emoji sample code
- Fix rendering issue when image load event triggered after timeout
- Fix comment preview not always displayed
- Fix new created comment rendering offscreen
- Update derived theme tutorial and add it to the help index
- Add 'extends' attribute to theme info
- Fix reply modal remaining hidden after reactions loaded
- Refactor tagcloud to use smarty template file
- Fix regression in tagadelic
- Fix possible performance issue with archive widget
- Fix various addons which still used the deprecated $a global
Hubzilla 10.4 (2025-07-15)
- Add support for did:key verification method to checkEddsaSignature()
- Introduce util/init_sys_channel to create the sys channel if required
- Update norwegian translations
- Add init_sys_channel utility to create the sys channel in case of headless installation or failure
- Upgrade http-message-signer to version 2.3
- Upgrade phpseclib to version 3
- Minor cleanup to the account functions and added tests
- Do not sign (request-target) on response
- Start verifying incoming RFC9421 HTTP signatures
- Convert geo URIs into clickable links
- Allow geo URIs in url bbcode tags
- Cleanup obsolete and unused functions
- Remove unused Xref module
- Make sure we have the keys before attempting to sign with JcsEddsa2022
- Implement lazy loading of toplevel comments
- Update german help files
- Add App::$page_layouts attribute for comanche
- Refactor and fix numerous issues in guess_image_type()
- Add tests for Widget\Messages
- Refactor cache_embeds daemon to be called with uuid (instead of item id) so that it will only be processed once
- Add avif support for php-gd
- Exclude Add/Remove items from network nouveau query
- Always preload images and remove pre image preload setting
- Introduce per channel conversation mode setting
- Add API docs for the observer file
- Move observer helper functions to separate source
- Introduce helper functions to access the various fields of the xchan stored in App::$observer
- Refactor item_normal() to accept an owner uid
- Implement reply modal if comment replies are enabled
- Streamline wording conversation > comment > reply
- Streamline default ordering to created date
- Default to threaded conversation mode
- Implement lazy loading of reactions
- Do not store dismissed create activities in dreport
- Refactor mod item to deprecate x() and use $_POST instead of $_REQUEST superglobal
- Improved styling for dreport module
- Start deprecation of the function x()
- Minor cleanup and refactor for Web/Router
- Minor cleanup and refactor for Lib/Webserver
- Set App::$query_string from from server.request_uri instead of server.query_string because the latter will mostly be urldecoded by the server already
- Refactor language selector
- Extend message filter to support until=2025-04-18 20:49:00 for date/time based filtering and add tests
- Extend message filter to deal with && and || conditions and add tests
- Prevent storing files/folder with filenames exceeding their max name length
- Deal with attachment of type link
- Revert translation of network to stream
- Updated debian install script
- Add suport for strong bbcode tag
- Change photo.filename to type text for new installs
- Provide methods to get mid and uuid from activity object
- Minor update for boxy schema
- Update composer libs
- Reorganize emojis and allow custom site emojis
- Change item.obj and item.target to mediumtext for mysql new installs
- Move jot related functions to jot-header and some cleanup
- Port photo selector to vanilla javascript
- Enable photo selector for comments if OCAP access is enabled
- Add :hubzilla: emoji
- Add :zot: emoji
- Include unapproved connections in deliverable_abook_xchans()
- Port showHideComments() to vanilla javascript
- Disable browser rotating image based on EXIF metadata
Bugfixes
- Fix blog mode if threaded view is disabled
- Fix markdown issue with mentions
- Fix issue where item_wall was not set for article and card item types
- Fix first created account was not necessarily the admin account
- Fix notice not emited on failed login
- Fix intro notifications not handled via /notify/view and hence not marked seen
- Fix undefined static function in Zot6Handler
- Fix missing return in Render\Theme::current
- Fix announce source title (addr) not correct
- Fix offset calculation if element position is relative
- Fix autosave for comments
- Fix notfication issue with update activities
- Fix sess_data not updated to mediumtext in mysql schema file
- Fix title and summary converted to bbcode
- Fix preloading images if dom element is not yet in page
- Fix verb and hash for notifications
- Fix notification button for medium screen size (right aside collapsed)
- Fix new result set created for updated results (dreport)
- Fix regex to catch codeblocks with params like class in smilies()
- Fix term.imgurl not stored in item_store_update()
- Fix folder names are not URL escaped in Files app (issue #1903)
- Fix stephenhill/base58 PHP warnings
- Fix color bbcode markup
- Fix video poster display issue
- Fix relayed emoji reactions
- Fix some javascript errors on mobile devices
- Fix our own activities visible in unseen forum notifications
- Fix duplicated head_get_icon()
Addons
- Wiki: fix spacing in wikilist widget
- Gallery: look for templates in theme directory first
- Articles: look for templates in theme directory first
- Wiki: look for templates in theme directory first
- Pubcrawl: remove unused force note setting
- Flashcards: major refactor and added functionality
- Superblock: refactor and new siteblock option for admins
- Cart: fix issue related to HTTP3
- Pubcrawl: avoid DB lookup if not valid AS request in mod followers and mod following
- Photocache: implement prefetch via cache_embeds daemon and minor refactor
- Articles: fix Add/Remove activities not dismissed in channel activities query
- Cards: fix Add/Remove activities not dismissed in channel activities query
- Diaspora: make sure item_thread_top is set for reshares (info for filters)
- Gallery: fix missing folder field from query
- Pubcrawl: update mod ap_probe to show a visual representation if applicable
Hubzilla 10.2.3 (2025-04-11)
- Fix bogus merge from 10.2.2 release
Hubzilla 10.2.2 (2025-04-11)
- Cleanup deprecated forum queries, improved performance
- Fix zot6 handler returning success allthough Libzot::fetch() did not return anything useful

View File

@@ -6,23 +6,28 @@ class Cache_embeds {
static public function run($argc,$argv) {
if(! $argc == 2)
if(!$argc == 2) {
return;
}
$c = q("select body from item where id = %d ",
dbesc(intval($argv[1]))
$c = q("select uid, aid, body, item_private from item where uuid = '%s'",
dbesc($argv[1])
);
if(! $c)
if(!$c) {
return;
}
$item = $c[0];
// bbcode conversion by default processes embeds that aren't already cached.
// Ignore the returned html output.
bbcode($item['body']);
// photocache addon hook to prefetch one copy of public item images for the sys channel
call_hooks('cache_prefetch_hook', $item);
return;
}
}

View File

@@ -5,6 +5,7 @@ namespace Zotlabs\Daemon;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\ASCollection;
use Zotlabs\Lib\ASCache;
class Convo {
@@ -12,52 +13,66 @@ class Convo {
logger('convo invoked: ' . print_r($argv, true));
if ($argc != 4) {
if ($argc < 4) {
return;
}
$id = $argv[1];
$channel_id = intval($argv[2]);
$contact_hash = $argv[3];
$channel = channelx_by_n($channel_id);
if (!$channel) {
$channels = explode(',', $argv[1]);
if (!$channels) {
return;
}
$r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1",
intval($channel_id),
dbesc($contact_hash)
);
if (!$r) {
$observer_hash = $argv[2];
if (!$observer_hash) {
return;
}
$contact = array_shift($r);
$obj = new ASCollection($id, $channel);
$messages = $obj->get();
if (!$messages) {
$mid = $argv[3];
if (!$mid) {
return;
}
foreach ($messages as $message) {
if (is_string($message)) {
$message = Activity::fetch($message, $channel);
$force = $argv[4] ?? false;
foreach ($channels as $channel_id) {
$channel = channelx_by_n($channel_id);
$obj = new ASCollection($mid, $channel);
$messages = $obj->get();
if (!$messages) {
continue;
}
// set client flag because comments will probably just be objects and not full blown activities
// and that lets us use implied_create
$AS = new ActivityStreams($message);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);
$item['item_fetched'] = true;
Activity::store($channel, $contact['abook_xchan'], $AS, $item);
foreach ($messages as $message) {
if (is_string($message)) {
$cached = ASCache::Get($message);
if ($cached) {
// logger('convo_cached: ' . $message);
$data = $cached;
}
else {
// logger('convo_fetching: ' . $message);
$data = Activity::fetch($message, $channel);
if ($data) {
ASCache::Set($message, $data);
}
}
}
else {
$data = $message;
}
$AS = new ActivityStreams($data);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);
$item['item_fetched'] = true;
Activity::store($channel, $observer_hash, $AS, $item, false, $force);
}
}
}
return;

View File

@@ -125,14 +125,15 @@ class Cron {
$r = q("SELECT DISTINCT xchan, content FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
db_quoteinterval(Config::Get('system', 'cache_expire_days', 7) . ' DAY')
db_quoteinterval(Config::Get('system', 'default_expire_days', 30) . ' DAY')
);
if ($r) {
q("DELETE FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
db_quoteinterval(Config::Get('system', 'cache_expire_days', 7) . ' DAY')
db_quoteinterval(Config::Get('system', 'default_expire_days', 30) . ' DAY')
);
foreach ($r as $rr) {
$file = dbunescbin($rr['content']);
if (is_file($file)) {

View File

@@ -36,6 +36,8 @@ class Fetchparents {
Activity::fetch_and_store_parents($channel, $observer_hash, $mid, null, $force);
}
Activity::init_background_fetch($observer_hash);
return;
}

View File

@@ -7,27 +7,63 @@ namespace Zotlabs\Lib;
*/
class ASCache {
public static function isEnabled() {
public static function isEnabled()
{
return Config::Get('system', 'as_object_cache_enabled', true);
}
public static function getAge() {
public static function getAge(): string
{
return Config::Get('system', 'as_object_cache_time', '10 MINUTE');
}
public static function Get($key) {
public static function Get(string $key): array
{
if (!self::isEnabled()) {
return;
return [];
}
return Cache::get($key, self::getAge());
$ret = Cache::get($key, self::getAge());
if ($ret) {
return unserialise($ret);
}
return [];
}
public static function Set($key, $value) {
public static function Set(string $key, array $obj): void
{
if (!self::isEnabled()) {
return;
}
Cache::set($key, $value);
if (!self::isCacheable($obj)) {
return;
}
Cache::set($key, serialise($obj));
}
public static function isCacheable(array $obj): bool
{
$to = [];
$cc = [];
if (isset($obj['to'])) {
$to = is_array($obj['to']) ? $obj['to'] : [$obj['to']];
}
if (isset($obj['cc'])) {
$cc = is_array($obj['cc']) ? $obj['cc'] : [$obj['cc']];
}
$receivers = array_merge($to, $cc);
if ($receivers && !in_array(ACTIVITY_PUBLIC_INBOX, $receivers)) {
return false;
}
return true;
}
}

View File

@@ -31,7 +31,19 @@ class ASCollection {
}
if (is_string($obj)) {
$data = Activity::fetch($obj, $channel);
$cached = ASCache::Get($obj);
if ($cached) {
// logger('cached: ' . $obj);
$data = $cached;
}
else {
// logger('fetching: ' . $obj);
$data = Activity::fetch($obj, $channel);
if ($data) {
ASCache::Set($obj, $data);
}
}
$this->history[] = $obj;
}
@@ -92,7 +104,20 @@ class ASCollection {
// recursion detected
return false;
}
$data = Activity::fetch($this->nextpage, $this->channel);
$cached = ASCache::Get($this->nextpage);
if ($cached) {
// logger('cached: ' . $this->nextpage);
$data = $cached;
}
else {
$data = Activity::fetch($this->nextpage, $this->channel);
if ($data) {
// logger('fetching: ' . $this->nextpage);
ASCache::Set($this->nextpage, $data);
}
}
$this->history[] = $this->nextpage;
}

View File

@@ -13,6 +13,7 @@ use Zotlabs\Entity\Item;
require_once('include/event.php');
require_once('include/html2plain.php');
require_once('include/items.php');
require_once('include/markdown.php');
class Activity {
@@ -68,10 +69,10 @@ class Activity {
if ($j) {
xchan_query($j, true);
$items = fetch_post_tags($j);
}
if ($items) {
return self::encode_item(array_shift($items));
if ($items) {
return self::encode_item(array_shift($items));
}
}
return null;
@@ -165,7 +166,7 @@ class Activity {
}
else {
logger('fetch failed: ' . $url);
logger($x['body']);
logger(print_r($x, true), LOGGER_DEBUG);
}
@@ -580,14 +581,12 @@ class Activity {
}
}
if (intval($i['item_wall'])) {
$ret['commentPolicy'] = map_scope(PermissionLimits::Get($i['uid'], 'post_comments'));
}
if (intval($i['item_private']) === 2) {
$ret['directMessage'] = true;
}
$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 ($ret['commentPolicy']) {
$ret['commentPolicy'] .= ' ';
@@ -621,15 +620,6 @@ class Activity {
$ret['context'] = $cnv;
}
if ($i['mimetype'] === 'text/bbcode') {
if ($i['title'])
$ret['name'] = unescape_tags($i['title']);
if ($i['summary'])
$ret['summary'] = unescape_tags($i['summary']);
$ret['content'] = bbcode(unescape_tags($i['body']), ['cache' => true]);
$ret['source'] = ['content' => unescape_tags($i['body']), 'mediaType' => 'text/bbcode'];
}
$actor = self::encode_person($i['author'], false);
if ($actor)
$ret['actor'] = $actor;
@@ -647,6 +637,53 @@ class Activity {
$ret['tag'] = $t;
}
if (str_contains($i['body'], '[/share]')) {
preg_match_all('/\[share(.*?)\[\/share\]/ism', $i['body'], $all_shares, PREG_SET_ORDER);
$quote_urls = [];
foreach ($all_shares as $share) {
// Extract the link attribute from each [share] block
if (preg_match("/link='(.*?)'/ism", $share[1], $match)) {
$url = $match[1];
$quote_urls[] = $url;
$quote_name = 'RE: ' . $url;
// Replace this share block with a formatted URL reference
$i['body'] = str_replace($share[0], $quote_name, $i['body']);
$obj_links[] = [
'type' => 'Link',
'mediaType' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
'href' => $url,
'name' => $quote_name
];
}
}
if ($quote_urls) {
$ret['quoteUrl'] = $quote_urls[0];
if (empty($ret['tag'])) {
$ret['tag'] = $obj_links;
}
else {
$ret['tag'] = array_merge($ret['tag'], $obj_links);
}
}
}
if ($i['mimetype'] === 'text/bbcode') {
if ($i['title'])
$ret['name'] = unescape_tags($i['title']);
if ($i['summary'])
$ret['summary'] = unescape_tags($i['summary']);
$ret['content'] = bbcode(unescape_tags($i['body']));
$ret['source'] = ['content' => unescape_tags($i['body']), 'mediaType' => 'text/bbcode'];
}
$a = self::encode_attachment($i);
if ($a) {
$ret['attachment'] = $a;
@@ -695,7 +732,7 @@ class Activity {
if (is_array($t) && !array_key_exists('type', $t))
$t['type'] = 'Hashtag';
if (is_array($t) && (array_key_exists('href', $t) || array_key_exists('id', $t)) && array_key_exists('name', $t)) {
if (is_array($t) && (array_key_exists('href', $t) || array_key_exists('id', $t) || isset($t['icon']['url'])) && array_key_exists('name', $t)) {
switch ($t['type']) {
case 'Hashtag':
$ret[] = ['ttype' => TERM_HASHTAG, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'], 0, 1) === '#') ? substr($t['name'], 1) : $t['name'])];
@@ -710,7 +747,7 @@ class Activity {
break;
case 'Emoji':
$ret[] = ['ttype' => TERM_EMOJI, 'url' => $t['id'], 'term' => escape_tags($t['name']), 'imgurl' => $t['icon']['url']];
$ret[] = ['ttype' => TERM_EMOJI, 'url' => $t['id'] ?? $t['icon']['url'], 'term' => escape_tags($t['name']), 'imgurl' => $t['icon']['url']];
break;
default:
@@ -853,6 +890,8 @@ class Activity {
$entry['type'] = $att['mediaType'];
} elseif (array_key_exists('type', $att) && $att['type'] === 'Image') {
$entry['type'] = 'image/jpeg';
} elseif (array_key_exists('type', $att) && $att['type'] === 'Link') {
$entry['type'] = 'text/uri-list';
}
if (array_key_exists('name', $att) && $att['name']) {
$entry['name'] = html2plain(purify_html($att['name']), 256);
@@ -942,36 +981,8 @@ class Activity {
}
if ($ret['type'] === 'emojiReaction') {
// There may not be an object for these items for legacy reasons - it should be the conversation parent.
$p = q("select * from item where mid = '%s' and uid = %d",
dbesc($i['parent_mid']),
intval($i['uid'])
);
if ($p) {
xchan_query($p, true);
$p = fetch_post_tags($p);
$i['obj'] = self::encode_item($p[0]);
// convert to zot6 emoji reaction encoding which uses the target object to indicate the
// specific emoji instead of overloading the verb or type.
$im = explode('#', $i['verb']);
if ($im && count($im) > 1)
$emoji = $im[1];
if (preg_match("/\[img(.*?)\](.*?)\[\/img\]/ism", $i['body'], $match)) {
$ln = $match[2];
}
$i['tgt_type'] = 'Image';
$i['target'] = [
'type' => 'Image',
'name' => $emoji,
'url' => (($ln) ? $ln : z_root() . '/images/emoji/' . $emoji . '.png')
];
}
if ($ret['type'] === 'EmojiReact') {
$ret['content'] = $i['body'];
}
if (strpos($i['mid'], z_root() . '/item/') !== false) {
@@ -986,15 +997,15 @@ class Activity {
$ret['diaspora:guid'] = $i['uuid'];
if (isset($i['title']) && $i['title'])
$ret['name'] = html2plain(bbcode($i['title'], ['cache' => true]));
if (!empty($i['title']))
$ret['name'] = html2plain(bbcode($i['title']));
if (isset($i['summary']) && $i['summary'])
$ret['summary'] = bbcode($i['summary'], ['cache' => true]);
if (!empty($i['summary']))
$ret['summary'] = bbcode($i['summary']);
if ($ret['type'] === 'Announce') {
$tmp = preg_replace('/\[share(.*?)\[\/share\]/ism', EMPTY_STR, $i['body']);
$ret['content'] = bbcode($tmp, ['cache' => true]);
$ret['content'] = bbcode($tmp);
$ret['source'] = [
'content' => $i['body'],
'mediaType' => 'text/bbcode'
@@ -1010,7 +1021,7 @@ class Activity {
}
}
if (isset($i['app']) && $i['app']) {
if (!empty($i['app'])) {
$ret['generator'] = ['type' => 'Application', 'name' => $i['app']];
}
if (!empty($i['location']) || !empty($i['coord'])) {
@@ -1062,7 +1073,7 @@ class Activity {
else
return [];
if (isset($i['obj']) && $i['obj']) {
if (!empty($i['obj'])) {
if (!is_array($i['obj'])) {
$i['obj'] = json_decode($i['obj'], true);
}
@@ -1090,7 +1101,7 @@ class Activity {
$ret['type'] = 'Invite';
}
if (isset($i['target']) && $i['target']) {
if (!empty($i['target'])) {
if (!is_array($i['target'])) {
$i['target'] = json_decode($i['target'], true);
}
@@ -1101,12 +1112,10 @@ class Activity {
return [];
}
/* this should not be needed
$t = self::encode_taxonomy($i);
if ($t) {
$ret['tag'] = $t;
}
*/
$a = self::encode_attachment($i, true);
if ($a) {
@@ -1117,7 +1126,6 @@ class Activity {
$ret['to'] = [ACTIVITY_PUBLIC_INBOX];
}
$hookinfo = [
'item' => $i,
'encoded' => $ret
@@ -2148,34 +2156,24 @@ class Activity {
$s['owner_xchan'] = $act->actor['id'];
$s['author_xchan'] = $act->actor['id'];
$content = [];
if (is_array($act->obj)) {
$content = self::get_content($act->obj);
}
$s['mid'] = $act->objprop('id');
if (!$s['mid'] && is_string($act->obj)) {
$s['mid'] = $act->obj;
}
// pleroma fetched activities
if (!$s['mid'] && isset($act->obj['data']['id'])) {
$s['mid'] = $act->obj['data']['id'];
}
if ($act->objprop('type') === 'Profile') {
$s['mid'] = $act->id;
}
$s['mid'] = self::getMessageID($act);
if (!$s['mid']) {
return false;
}
// Friendica sends the diaspora guid in a nonstandard field via AP
// If no uuid is provided we will create an uuid v5 from the mid
$s['uuid'] = (($act->objprop('diaspora:guid')) ?: uuid_from_url($s['mid']));
$s['uuid'] = self::getUUID($act);
if (!$s['uuid']) {
// If we have not found anything useful, create an uuid v5 from the mid
$s['uuid'] = uuid_from_url($s['mid']);
}
$content = [];
if (is_array($act->obj)) {
$content = self::get_content($act->obj);
}
$s['parent_mid'] = $act->parent_id;
@@ -2215,23 +2213,8 @@ class Activity {
$response_activity = true;
$s['mid'] = $act->id;
$s['uuid'] = ((!empty($act->data['diaspora:guid'])) ? $act->data['diaspora:guid'] : uuid_from_url($s['mid']));
$s['parent_mid'] = $act->objprop('id') ?: $act->obj;
/*
if ($act->objprop('inReplyTo')) {
$s['parent_mid'] = $act->objprop('inReplyTo');
}
$s['thr_parent'] = $act->objprop('id') ?: $act->obj;
if (empty($s['parent_mid']) || empty($s['thr_parent'])) {
logger('response activity without parent_mid or thr_parent');
return;
}
*/
// over-ride the object timestamp with the activity
if (isset($act->data['published'])) {
@@ -2242,9 +2225,9 @@ class Activity {
$s['edited'] = datetime_convert('UTC', 'UTC', $act->data['updated']);
}
$obj_actor = $act->objprop('actor') ?: $act->get_actor('attributedTo', $act->obj);
$obj_actor = is_array($act->objprop('actor')) ? $act->objprop('actor') : $act->get_actor('attributedTo', $act->obj);
if (!isset($obj_actor['id'])) {
if (empty($obj_actor['id'])) {
return false;
}
@@ -2280,12 +2263,8 @@ class Activity {
$content['content'] = sprintf(t('&#x1f501; Repeated %1$s\'s %2$s'), $mention, $act->obj['type']);
}
// TODO: Deprecated
if ($act->type === 'emojiReaction') {
$content['content'] = (($act->tgt && $act->tgt['type'] === 'Image') ? '[img=32x32]' . $act->tgt['url'] . '[/img]' : '&#x' . $act->tgt['name'] . ';');
}
if (in_array($act->type, ['EmojiReact'])) {
// Pleroma reactions
$t = trim(self::get_textfield($act->data, 'content'));
@@ -2335,18 +2314,46 @@ class Activity {
if (!array_key_exists('edited', $s))
$s['edited'] = $s['created'];
$s['title'] = (($response_activity) ? EMPTY_STR : self::bb_content($content, 'name'));
$s['summary'] = self::bb_content($content, 'summary');
$s['title'] = (($response_activity) ? EMPTY_STR : html2plain($content['name']));
$s['summary'] = (($content['summary'] !== $content['content']) ? html2plain($content['summary']) : '');
$s['body'] = ((self::bb_content($content, 'bbcode') && (!$response_activity)) ? self::bb_content($content, 'bbcode') : self::bb_content($content, 'content'));
if ($act->objprop('quoteUrl')) {
$quote_bbcode = self::get_quote_bbcode($act->obj['quoteUrl']);
// peertube quirks
if ($act->objprop('mediaType') === 'text/markdown') {
$s['body'] = markdown_to_bb($act->objprop('content'));
}
if ($s['body']) {
$s['body'] .= "\r\n\r\n";
$quote_urls = [];
if (isset($act->obj['tag'])) {
foreach($act->obj['tag'] as $t) {
if (is_array($t) && $t['type'] === 'Link' && $t['mediaType'] === 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"') {
$quote_urls[] = $t['href'];
}
}
}
$s['body'] .= $quote_bbcode;
if (!$quote_urls) {
$quote_url = $act->obj['quoteUrl'] ?? $act->obj['quoteUri'] ?? $act->obj['_misskey_quote'] ?? $act->obj['quote'] ?? null;
if ($quote_url) {
$quote_urls = [$quote_url];
}
}
// Backwards compatibility: only process quote items if there is no share tag in them.
// Otherwise they will appear doubled.
if ($quote_urls && !str_contains($s['body'], '[/share]')) {
foreach($quote_urls as $quote_url) {
$quote = self::get_quote($quote_url);
if (!$quote) {
continue;
}
$s['body'] = self::pasteQuote($s['body'], $quote);
$s['term'] = $quote['term'];
}
}
$s['verb'] = self::activity_mapper($act->type);
@@ -2394,7 +2401,13 @@ class Activity {
$a = self::decode_taxonomy($act->obj);
if ($a) {
$s['term'] = $a;
if (isset($s['term'])) {
// term might contain content from a quote post
$s['term'] = array_merge($s['term'], $a);
}
else {
$s['term'] = $a;
}
}
$a = self::decode_attachment($act->obj);
@@ -2470,7 +2483,8 @@ class Activity {
}
}
$tag = (($poster) ? '[video poster=&quot;' . $poster . '&quot;]' : '[video]' );
$tag = (($poster) ? '[video poster=\'' . $poster . '\']' : '[video]' );
$ptr = null;
if ($act->objprop('url')) {
@@ -2619,7 +2633,7 @@ class Activity {
}
}
if (in_array($act->objprop('type'), ['Note', 'Article', 'Page'])) {
if (in_array($act->objprop('type'), ['Note', 'Article', 'Page', 'Question'])) {
$ptr = null;
if (array_key_exists('url', $act->obj)) {
@@ -2631,7 +2645,7 @@ class Activity {
$ptr = [$act->obj['url']];
}
foreach ($ptr as $vurl) {
if (array_key_exists('mediaType', $vurl) && $vurl['mediaType'] === 'text/html') {
if (isset($vurl['mediaType']) && $vurl['mediaType'] === 'text/html') {
$s['plink'] = $vurl['href'];
break;
}
@@ -2643,7 +2657,7 @@ class Activity {
}
}
if (!(isset($s['plink']) && $s['plink'])) {
if (empty($s['plink'])) {
$s['plink'] = $s['mid'];
}
@@ -2719,8 +2733,9 @@ class Activity {
return $hookinfo['s'];
}
static function store($channel, $observer_hash, $act, $item, $fetch_parents = true, $force = false, $is_collection_operation = false) {
$is_sys_channel = is_sys_channel($channel['channel_id']);
$is_sys_channel = $channel['channel_system'];
$is_child_node = false;
$parent = null;
@@ -2767,7 +2782,9 @@ class Activity {
$force = true;
}
if ($fetch_parents) {
$attempt_parents_fetch = $fetch_parents && !in_array($channel['channel_id'], App::$cache['as_fetch_objects'][$item['mid']]['channels'] ?? []);
if ($attempt_parents_fetch) {
App::$cache['as_fetch_objects'][$item['mid']]['channels'][] = $channel['channel_id'];
App::$cache['as_fetch_objects'][$item['mid']]['force'] = intval($force);
return;
@@ -2780,7 +2797,7 @@ class Activity {
$relay = $channel['channel_hash'] === $parent[0]['owner_xchan'];
if (str_contains($parent[0]['tgt_type'], 'Collection') && !$relay && !$isCollectionOperation) {
if (str_contains($parent[0]['tgt_type'], 'Collection') && !$relay && !$is_collection_operation) {
logger('not a collection activity');
return;
}
@@ -2943,7 +2960,7 @@ class Activity {
if (!$item['author_xchan'] || !$item['owner_xchan'])
return;
if ($channel['channel_system']) {
if ($is_sys_channel) {
$incl = Config::Get('system','pubstream_incl');
$excl = Config::Get('system','pubstream_excl');
@@ -3012,7 +3029,7 @@ class Activity {
if (intval($parent[0]['item_private']) === 0) {
if (intval($item['item_private'])) {
$item['item_restrict'] = $item['item_restrict'] | 1;
$item['item_restrict'] = ((isset($item['item_restrict'])) ? $item['item_restrict'] | 1 : 1);
$item['allow_cid'] = '<' . $channel['channel_hash'] . '>';
$item['allow_gid'] = $item['deny_cid'] = $item['deny_gid'] = '';
}
@@ -3029,7 +3046,7 @@ class Activity {
}
}
}
/*
if (isset($item['term']) && !PConfig::Get($channel['channel_id'], 'system', 'no_smilies')) {
foreach ($item['term'] as $t) {
if ($t['ttype'] === TERM_EMOJI) {
@@ -3043,6 +3060,7 @@ class Activity {
}
}
}
*/
// TODO: not implemented
// self::rewrite_mentions($item);
@@ -3066,8 +3084,7 @@ class Activity {
}
if ($x['success']) {
if ($relay && $channel['channel_hash'] === $x['item']['owner_xchan'] && $x['item']['verb'] !== 'Add' && !$isCollectionOperation) {
if ($relay && $channel['channel_hash'] === $x['item']['owner_xchan'] && $x['item']['verb'] !== 'Add' && !$is_collection_operation) {
$approval = Activity::addToCollection($channel, $act->data, $x['item']['parent_mid'], $x['item'], deliver: false);
}
@@ -3085,38 +3102,17 @@ class Activity {
}
}
$r = q("select * from item where id = %d limit 1",
intval($x['item_id'])
);
send_status_notifications($x['item_id'], $x['item']);
if ($r) {
send_status_notifications($x['item_id'], $r[0]);
}
sync_an_item($channel['channel_id'], $x['item_id']);
}
if ($fetch_parents && $parent && !intval($parent[0]['item_private'])) {
logger('topfetch', LOGGER_DEBUG);
// if the thread owner is a connnection, we will already receive any additional comments to their posts
// but if they are not we can try to fetch others in the background
$connected = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1",
intval($channel['channel_id']),
dbesc($parent[0]['owner_xchan'])
);
if (!$connected) {
// determine if the top-level post provides a replies collection
if ($parent[0]['obj']) {
$parent[0]['obj'] = json_decode($parent[0]['obj'], true);
}
logger('topfetch: ' . print_r($parent[0], true), LOGGER_ALL);
$id = ((array_path_exists('obj/replies/id', $parent[0])) ? $parent[0]['obj']['replies']['id'] : false);
if (!$id) {
$id = ((array_path_exists('obj/replies', $parent[0]) && is_string($parent[0]['obj']['replies'])) ? $parent[0]['obj']['replies'] : false);
}
if ($id) {
Master::Summon(['Convo', $id, $channel['channel_id'], $observer_hash]);
}
// 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'] ?? []);
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);
}
}
}
@@ -3165,7 +3161,7 @@ class Activity {
$cached = ASCache::Get($current_item['parent_mid']);
if ($cached) {
// logger('cached: ' . $current_item['parent_mid']);
$n = unserialise($cached);
$n = $cached;
}
else {
// logger('fetching: ' . $current_item['parent_mid']);
@@ -3173,7 +3169,7 @@ class Activity {
if (!$n) {
break;
}
ASCache::Set($current_item['parent_mid'], serialise($n));
ASCache::Set($current_item['parent_mid'], $n);
}
$a = new ActivityStreams($n);
@@ -3408,10 +3404,10 @@ class Activity {
if (array_key_exists('startTime', $act) && strpos($act['startTime'], -1, 1) === 'Z') {
$adjust = true;
$event['adjust'] = 1;
$event['dtstart'] = datetime_convert('UTC', 'UTC', $event['startTime'] . (($adjust) ? '' : 'Z'));
$event['dtstart'] = datetime_convert('UTC', 'UTC', $act['startTime'] . (($adjust) ? '' : 'Z'));
}
if (array_key_exists('endTime', $act)) {
$event['dtend'] = datetime_convert('UTC', 'UTC', $event['endTime'] . (($adjust) ? '' : 'Z'));
$event['dtend'] = datetime_convert('UTC', 'UTC', $act['endTime'] . (($adjust) ? '' : 'Z'));
}
else {
$event['nofinish'] = true;
@@ -3610,38 +3606,68 @@ class Activity {
}
foreach ($actor['tag'] as $t) {
// TODO: implement FEP-fb2a at the sending side and deprecate PropertyValue
if ((isset($t['type']) && $t['type'] === 'PropertyValue') &&
(isset($t['name']) && $t['name'] === 'Protocol') &&
(isset($t['value']) && in_array($t['value'], ['zot6', 'activitypub', 'diaspora']))
isset($t['value'])
) {
$ret[] = $t['value'];
$ret[] = trim($t['value']);
}
// FEP-fb2a - actor metadata
if ((isset($t['type']) && $t['type'] === 'Note') &&
(isset($t['name']) && $t['name'] === 'Protocols') &&
isset($t['content'])
) {
$ret = array_map('trim', explode(',', $t['content']));
}
}
return $ret;
}
static function get_quote_bbcode($url) {
static function get_quote($url) {
$ret = [];
$a = null;
$cached = ASCache::Get($url);
if ($cached) {
// logger('cached: ' . $url);
$a = unserialise($cached);
}
else {
// logger('fetching: ' . $url);
$a = self::fetch($url);
if ($a) {
ASCache::Set($url, $a);
}
}
$ret = '';
$a = self::fetch($url);
if ($a) {
$act = new ActivityStreams($a);
if ($act->is_valid()) {
$content = self::get_content($act->obj);
$decoded = self::decode_note($act);
$ret .= "[share author='" . urlencode($act->actor['name'] ?? $act->actor['preferredUsername']) .
$bbcode = "[share author='" . urlencode($act->actor['name'] ?? $act->actor['preferredUsername']) .
"' profile='" . $act->actor['id'] .
"' avatar='" . ($act->actor['icon']['url'] ?? z_root() . '/' . get_default_profile_photo(80)) .
"' link='" . $act->obj['id'] .
"' link='" . $url .
"' auth='" . ((is_matrix_url($act->actor['id'])) ? 'true' : 'false') .
"' posted='" . $act->obj['published'] .
"' message_id='" . $act->obj['id'] .
"']";
$ret .= self::bb_content($content, 'content');
$ret .= '[/share]';
$bbcode .= $decoded['body'];
$bbcode .= '[/share]';
$ret['bbcode'] = $bbcode;
$ret['url'] = $decoded['plink'];
$ret['mid'] = $decoded['mid'];
$ret['term'] = $decoded['term'] ?? [];
}
}
@@ -3721,7 +3747,9 @@ class Activity {
'guid' => 'diaspora:guid',
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
'Hashtag' => 'as:Hashtag'
'Hashtag' => 'as:Hashtag',
'quoteUrl' => 'as:quoteUrl',
];
}
@@ -3761,10 +3789,9 @@ class Activity {
public static function init_background_fetch(string $observer_hash = '') {
if (isset(App::$cache['zot_fetch_objects'])) {
$channels_str = '';
foreach (App::$cache['zot_fetch_objects'] as $mid => $info) {
$force = $info['force'];
$channels_str = '';
foreach ($info['channels'] as $c) {
if ($channels_str) {
@@ -3777,16 +3804,15 @@ class Activity {
}
}
if (!$observer_hash) {
logger('Attempt to initiate Fetchparents or Convo daemon without observer');
return;
}
if (isset(App::$cache['as_fetch_objects'])) {
if (!$observer_hash) {
logger('Attempt to initiate Fetchparents daemon without observer');
return;
}
$channels_str = '';
foreach (App::$cache['as_fetch_objects'] as $mid => $info) {
$force = $info['force'];
$channels_str = '';
foreach ($info['channels'] as $c) {
if ($channels_str) {
@@ -3798,6 +3824,23 @@ class Activity {
Master::Summon(['Fetchparents', $channels_str, $observer_hash, $mid, $force]);
}
}
if (isset(App::$cache['as_fetch_collection'])) {
foreach (App::$cache['as_fetch_collection'] as $mid => $info) {
$force = $info['force'];
$channels_str = '';
foreach ($info['channels'] as $c) {
if ($channels_str) {
$channels_str .= ',';
}
$channels_str .= $c;
}
Master::Summon(['Convo', $channels_str, $observer_hash, $mid, $force]);
}
}
}
public static function addToCollection($channel, $object, $target, $sourceItem = null, $deliver = true) {
@@ -3872,4 +3915,69 @@ class Activity {
return $result;
}
/**
* @brief Retrieves message ID from activity object.
* @param object $act Activity object
* @return string Message ID or empty string if not found
*/
public static function getMessageID($act): string
{
if (ActivityStreams::is_response_activity($act->type) || $act->objprop('type') === 'Profile') {
return $act->id;
}
return $act->objprop('id', null)
?? (is_string($act->obj) ? $act->obj : null)
?? '';
}
/**
* @brief Retrieves the UUID from an activity object.
* @param object $act Activity object
* @return string UUID or empty string if not found
*/
public static function getUUID($act): string
{
if (ActivityStreams::is_response_activity($act->type)) {
return $act->data['uuid']
?? $act->data['diaspora:guid']
?? '';
}
return $act->objprop('uuid', null)
?? $act->objprop('diaspora:guid', null)
?? '';
}
public static function pasteQuote(string $body, array $quote): string
{
// Escape URLs for regex safety
$urls = array_map('preg_quote', [$quote['url'], $quote['mid']], array_fill(0, 2, '/'));
$patterns = [];
foreach ($urls as $url) {
// Match both plain and BBCode-style references, with optional line breaks or spaces
$patterns[] = '/RE:\s*(?:\[url=' . $url . '\]' . $url . '\[\/url\]|' . $url . ')[\s\r\n]?/i';
}
$found = false;
foreach ($patterns as $pattern) {
if (preg_match($pattern, $body)) {
$found = true;
$body = preg_replace($pattern, $quote['bbcode'], $body);
break;
}
}
if (!$found) {
if (!empty($body)) {
$body .= "\r\n\r\n";
}
$body .= $quote['bbcode'];
}
return $body;
}
}

View File

@@ -97,7 +97,7 @@ class ActivityStreams {
}
// cache for future use
ASCache::Set($this->id, 'json:' . $this->raw);
ASCache::Set($this->id, $this->data);
$this->type = $this->get_primary_type();
$this->actor = $this->get_actor('actor', '', '');
@@ -413,13 +413,13 @@ class ActivityStreams {
$cached = ASCache::Get($x);
if ($cached) {
// logger('AS cached: ' . $x);
$y = unserialise($cached);
$y = $cached;
}
else {
// logger('AS fetching: ' . $x);
$y = $this->fetch_property($x);
if ($y) {
ASCache::Set($x, serialise($y));
ASCache::Set($x, $y);
}
}
if (is_array($y)) {
@@ -528,49 +528,59 @@ class ActivityStreams {
}
public function checkEddsaSignature() {
$publicKey = null;
$signer = $this->get_property_obj('verificationMethod', $this->sig);
$parseUrl = parse_url($signer);
if (isset($parseUrl['fragment'])) {
if (str_starts_with($parseUrl['fragment'], 'z6Mk')) {
$publicKey = $parseUrl['fragment'];
}
unset($parseUrl['fragment']);
}
if (isset($parseUrl['query'])) {
unset($parseUrl['query']);
}
$url = unparse_url($parseUrl);
$hublocs = Activity::get_actor_hublocs($url);
$hasStoredKey = false;
if ($hublocs) {
foreach ($hublocs as $hubloc) {
if ($publicKey && $hubloc['xchan_epubkey'] === $publicKey) {
$hasStoredKey = true;
break;
}
if ($signer && str_starts_with($signer, 'did:key:')) {
$publicKey = str_replace('did:key:', '', $signer);
$this->signer = ['id' => $signer];
if (strpos($publicKey, '#') !== false) {
$publicKey = substr($publicKey,0, strpos($publicKey, '#'));
}
}
else {
$parseUrl = parse_url($signer);
if (!$hasStoredKey) {
$this->signer = Activity::get_actor($url);
if (isset($this->signer['assertionMethod'])) {
if (!isset($this->signer['assertionMethod'][0])) {
$this->signer['assertionMethod'] = [$this->signer['assertionMethod']];
if (isset($parseUrl['fragment'])) {
if (str_starts_with($parseUrl['fragment'], 'z6Mk')) {
$publicKey = $parseUrl['fragment'];
}
unset($parseUrl['fragment']);
}
foreach($this->signer['assertionMethod'] as $am) {
if ($url === $am['controller'] &&
$am['type'] === 'Multikey' &&
str_starts_with($am['publicKeyMultibase'], 'z6Mk')
) {
$publicKey = $am['publicKeyMultibase'];
if (isset($parseUrl['query'])) {
unset($parseUrl['query']);
}
$url = unparse_url($parseUrl);
$hublocs = Activity::get_actor_hublocs($url);
$hasStoredKey = false;
if ($hublocs) {
foreach ($hublocs as $hubloc) {
if ($publicKey && $hubloc['xchan_epubkey'] === $publicKey) {
$hasStoredKey = true;
break;
}
}
}
if (!$hasStoredKey) {
$this->signer = Activity::get_actor($url);
if (isset($this->signer['assertionMethod'])) {
if (!isset($this->signer['assertionMethod'][0])) {
$this->signer['assertionMethod'] = [$this->signer['assertionMethod']];
}
foreach($this->signer['assertionMethod'] as $am) {
if ($url === $am['controller'] &&
$am['type'] === 'Multikey' &&
str_starts_with($am['publicKeyMultibase'], 'z6Mk')
) {
$publicKey = $am['publicKeyMultibase'];
}
}
}
}

View File

@@ -341,7 +341,7 @@ class Apps {
'Suggest Channels' => t('Suggest Channels'),
'Login' => t('Login'),
'Channel Manager' => t('Channel Manager'),
'Network' => t('Stream'),
'Network' => t('Network'),
'Settings' => t('Settings'),
'Files' => t('Files'),
'Webpages' => t('Webpages'),

View File

@@ -35,7 +35,7 @@ class DReport {
}
function addto_update($status) {
$this->status = $this->status . ' ' . $status;
$this->status = $this->status . ', ' . $status;
}
@@ -89,8 +89,14 @@ class DReport {
if(array_key_exists('reject',$dr) && intval($dr['reject']))
return false;
if(! ($dr['sender']))
if (!$dr['sender']) {
return false;
}
// do not store dismissed create activities
if ($dr['status'] === 'not a collection activity') {
return false;
}
// Is the sender one of our channels?

View File

@@ -95,8 +95,8 @@ class Enotify {
if (array_key_exists('verb', $params['item'])) {
// localize_item() alters the original item so make a copy first
$i = $params['item'];
logger('calling localize');
localize_item($i);
// logger('calling localize');
// localize_item($i);
$title = $i['title'];
$body = $i['body'];
$private = (($i['item_private']) || intval($i['item_obscured']));
@@ -131,9 +131,9 @@ class Enotify {
logger('notification: mail');
$subject = sprintf( t('[$Projectname:Notify] New direct message received at %s'), $sitename);
$preamble = sprintf( t('%1$s sent you a new direct message at %2$s'), $sender['xchan_name'], $sitename);
$preamble = sprintf( t('%1$s sent you a new private message at %2$s'), $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s sent you %2$s.'), '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a direct message') . '[/zrl]');
$sitelink = t('Please visit %s to view and/or reply to your direct messages.');
$sitelink = t('Please visit %s to view and/or reply to your private messages.');
$tsitelink = sprintf( $sitelink, $siteurl . '/hq/' . gen_link_id($params['item']['mid']));
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/hq/' . gen_link_id($params['item']['mid']) . '">' . $sitename . '</a>');
$itemlink = $siteurl . '/hq/' . gen_link_id($params['item']['mid']);
@@ -146,7 +146,7 @@ class Enotify {
$itemlink = $params['link'];
$action = (($moderated) ? t('requested to comment on') : t('commented on'));
$action = (($moderated) ? t('requested to post in') : t('posted in'));
if(array_key_exists('item',$params)) {
@@ -164,8 +164,8 @@ class Enotify {
if(activity_match($params['verb'], ['Dislike', ACTIVITY_DISLIKE]))
$action = (($moderated) ? t('requested to dislike') : t('disliked'));
if(activity_match($params['verb'], ACTIVITY_SHARE))
$action = t('repeated');
if(activity_match($params['verb'], [ACTIVITY_SHARE]))
$action = (($moderated) ? t('requested to repeat') : t('repeated'));
}
@@ -271,7 +271,7 @@ class Enotify {
$itemlink = $params['link'];
if (array_key_exists('item',$params) && (activity_match($params['item']['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE]))) {
if (array_key_exists('item',$params) && (activity_match($params['item']['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE, 'Announce']))) {
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE) || !feature_enabled($recip['channel_id'], 'dislike')) {
logger('notification: not a visible activity. Ignoring.');
pop_lang();
@@ -327,6 +327,9 @@ class Enotify {
if(activity_match($params['item']['verb'], ['Dislike', ACTIVITY_DISLIKE]))
$verb = (($moderated) ? t('requested to dislike') : t('disliked'));
if(activity_match($params['item']['verb'], [ACTIVITY_SHARE]))
$verb = (($moderated) ? t('requested to repeat') : t('repeated'));
// "your post"
if ($parent_item['author']['xchan_hash'] === $recip['channel_hash']) {
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]your %4$s[/zrl]'),
@@ -508,9 +511,14 @@ class Enotify {
*/
$hash = ((in_array($params['verb'], ['Create', 'Update'])) ? $params['item']['uuid'] : $params['item']['thr_parent_uuid']);
if (!$hash) {
$hash = new_uuid();
}
$datarray = [];
$datarray['hash'] = $params['item']['uuid'] ?? new_uuid();
$datarray['hash'] = $hash;
$datarray['sender_hash'] = $sender['xchan_hash'];
$datarray['xname'] = $sender['xchan_name'];
$datarray['url'] = $sender['xchan_url'];
@@ -569,8 +577,9 @@ class Enotify {
dbesc($datarray['otype'])
);
$r = q("select id from notify where hash = '%s' and ntype = %d and uid = %d limit 1",
$r = q("select id from notify where hash = '%s' and link = '%s' and ntype = %d and uid = %d limit 1",
dbesc($datarray['hash']),
dbesc($itemlink),
intval($datarray['ntype']),
intval($recip['channel_id'])
);
@@ -848,8 +857,8 @@ class Enotify {
}
else {
$itemem_text = (($item['item_thread_top'])
? (($item['obj_type'] === 'Question') ? t('created a new poll') : t('created a new post'))
: (($item['obj_type'] === 'Answer') ? sprintf( t('voted on %s\'s poll'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]') : sprintf( t('commented on %s\'s post'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]'))
? (($item['obj_type'] === 'Question') ? t('started a poll') : t('started a conversation'))
: (($item['obj_type'] === 'Answer') ? sprintf( t('voted on %s\'s poll'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]') : sprintf( t('posted in %s\'s conversation'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]'))
);
if(in_array($item['obj_type'], ['Document', 'Video', 'Audio', 'Image'])) {
@@ -861,12 +870,7 @@ class Enotify {
if($item['edited'] > $item['created']) {
$edit = true;
if($item['item_thread_top']) {
$itemem_text = sprintf( t('edited a post dated %s'), relative_date($item['created']));
}
else {
$itemem_text = sprintf( t('edited a comment dated %s'), relative_date($item['created']));
}
$itemem_text = sprintf( t('edited a message dated %s'), relative_date($item['created']));
}
@@ -886,7 +890,7 @@ class Enotify {
'when' => (($edit) ? datetime_convert('UTC', date_default_timezone_get(), $item['edited']) : datetime_convert('UTC', date_default_timezone_get(), $item['created'])),
'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'),
// 'b64mid' => (($item['mid']) ? gen_link_id($item['mid']) : ''),
'b64mid' => (($item['uuid']) ? $item['uuid'] : ''),
'b64mid' => ((in_array($item['verb'] , ['Like', 'Dislike', 'Announce']) && !empty($item['thr_parent_uuid'])) ? $item['thr_parent_uuid'] : $item['uuid'] ?? ''),
//'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? gen_link_id($item['thr_parent']) : gen_link_id($item['mid'])),
'thread_top' => (($item['item_thread_top']) ? true : false),
'message' => bbcode(escape_tags($itemem_text)),
@@ -906,14 +910,13 @@ class Enotify {
}
static public function format_notify($tt) {
$message = trim(strip_tags(bbcode($tt['msg'])));
if(strpos($message, $tt['xname']) === 0)
$message = substr($message, strlen($tt['xname']) + 1);
$x = [
'notify_link' => (($tt['ntype'] === NOTIFY_MAIL) ? $tt['link'] : z_root() . '/notify/view/' . $tt['id']),
'notify_link' => (($tt['ntype'] === NOTIFY_INTRO) ? z_root() . '/notify/view/' . $tt['id'] : $tt['link']),
'name' => $tt['xname'],
'url' => $tt['url'],
'photo' => $tt['photo'],
@@ -925,11 +928,9 @@ class Enotify {
];
return $x;
}
static public function format_intros($rr) {
return [
'notify_link' => z_root() . '/connections#' . $rr['abook_id'],
'name' => $rr['xchan_name'],

View File

@@ -13,6 +13,7 @@ class IConfig {
static public function Get(&$item, $family, $key, $default = false) {
$is_item = false;
$iid = null;
if(is_array($item)) {
$is_item = true;
@@ -27,12 +28,13 @@ class IConfig {
elseif(intval($item))
$iid = $item;
if(! $iid)
if (!$iid)
return $default;
if(is_array($item) && array_key_exists('iconfig',$item) && is_array($item['iconfig'])) {
foreach($item['iconfig'] as $c) {
if($c['iid'] == $iid && $c['cat'] == $family && $c['k'] == $key)
if (isset($c['iid']) && $c['iid'] == $iid && isset($c['cat']) && $c['cat'] == $family && isset($c['k']) && $c['k'] == $key)
return $c['v'];
}
}

View File

@@ -2,16 +2,33 @@
namespace Zotlabs\Lib;
use Mmccook\JsonCanonicalizator\JsonCanonicalizatorFactory;
use Root23\JsonCanonicalizer\JsonCanonicalizer;
use StephenHill\Base58;
class JcsEddsa2022 {
public function __construct() {
return $this;
}
/**
* Sign arbitrary data with the keys of the provided channel.
*
* @param $data The data to be signed.
* @param array $channel A channel as an array of key/value pairs.
*
* @return An array with the following fields:
* - `type`: The type of signature, always `DataIntegrityProof`.
* - `cryptosuite`: The cryptographic algorithm used, always `eddsa-jcs-2022`.
* - `created`: The UTC date and timestamp when the signature was created.
* - `verificationMethod`: The channel URL and the public key separated by a `#`.
* - `proofPurpose`: The purpose of the signature, always `assertionMethod`.
* - `proofValue`: The signature itself.
*
* @throws JcsEddsa2022SignatureException if the channel is missing, or
* don't have valid keys.
*/
public function sign($data, $channel): array {
if (!is_array($channel) || !isset($channel['channel_epubkey'], $channel['channel_eprvkey'])) {
throw new JcsEddsa2022SignException('Invalid or missing channel provided.');
}
$base58 = new Base58();
$pubkey = (new Multibase())->publicKey($channel['channel_epubkey']);
$options = [
@@ -85,8 +102,8 @@ class JcsEddsa2022 {
}
public function canonicalize($data) {
$canonicalization = JsonCanonicalizatorFactory::getInstance();
return $canonicalization->canonicalize($data);
$canonicalizer = new JsonCanonicalizer();
return $canonicalizer->canonicalize($data);
}
}

View File

@@ -0,0 +1,15 @@
<?php
/*
* SPDX-FileCopyrightText: 2025 The Hubzilla Community
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Lib;
use Exception;
class JcsEddsa2022SignException extends Exception
{
}

View File

@@ -2,8 +2,8 @@
namespace Zotlabs\Lib;
use phpseclib\Crypt\RSA;
use phpseclib\Math\BigInteger;
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Math\BigInteger;
/**
* Keyutils
@@ -16,41 +16,42 @@ class Keyutils {
* @param string $e exponent
* @return string
*/
public static function meToPem($m, $e) {
$rsa = new RSA();
$rsa->loadKey([
public static function meToPem(string $m, string $e): string
{
$parsedKey = PublicKeyLoader::load([
'e' => new BigInteger($e, 256),
'n' => new BigInteger($m, 256)
]);
return $rsa->getPublicKey();
if (method_exists($parsedKey, 'getPublicKey')) {
$parsedKey = $parsedKey->getPublicKey();
}
return $parsedKey->toString('PKCS8');
}
/**
* @param string key
* @return string
*/
public static function rsaToPem($key) {
$rsa = new RSA();
$rsa->setPublicKey($key);
return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS8);
public static function rsaToPem(string $key): string
{
$parsedKey = PublicKeyLoader::load($key);
if (method_exists($parsedKey, 'getPublicKey')) {
$parsedKey = $parsedKey->getPublicKey();
}
return $parsedKey->toString('PKCS8');
}
/**
* @param string key
* @return string
*/
public static function pemToRsa($key) {
$rsa = new RSA();
$rsa->setPublicKey($key);
return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
public static function pemToRsa(string $key): string
{
$parsedKey = PublicKeyLoader::load($key);
if (method_exists($parsedKey, 'getPublicKey')) {
$parsedKey = $parsedKey->getPublicKey();
}
return $parsedKey->toString('PKCS1');
}
/**
@@ -58,23 +59,28 @@ class Keyutils {
* @param string $m reference modulo
* @param string $e reference exponent
*/
public static function pemToMe($key, &$m, &$e) {
public static function pemToMe(string $key): array
{
$parsedKey = PublicKeyLoader::load($key);
if (method_exists($parsedKey, 'getPublicKey')) {
$parsedKey = $parsedKey->getPublicKey();
}
$raw = $parsedKey->toString('Raw');
$rsa = new RSA();
$rsa->loadKey($key);
$rsa->setPublicKey();
$m = $rsa->modulus->toBytes();
$e = $rsa->exponent->toBytes();
$m = $raw['n'];
$e = $raw['e'];
return [$m->toBytes(), $e->toBytes()];
}
/**
* @param string $pubkey
* @return string
*/
public static function salmonKey($pubkey) {
self::pemToMe($pubkey, $m, $e);
public static function salmonKey(string $pubkey): string
{
[$m, $e] = self::pemToMe($pubkey);
/** @noinspection PhpRedundantOptionalArgumentInspection */
return 'RSA' . '.' . base64url_encode($m, true) . '.' . base64url_encode($e, true);
}
@@ -82,11 +88,13 @@ class Keyutils {
* @param string $key
* @return string
*/
public static function convertSalmonKey($key) {
if (strstr($key, ','))
public static function convertSalmonKey(string $key): string
{
if (str_contains($key, ',')) {
$rawkey = substr($key, strpos($key, ',') + 1);
else
} else {
$rawkey = substr($key, 5);
}
$key_info = explode('.', $rawkey);
@@ -96,4 +104,4 @@ class Keyutils {
return self::meToPem($m, $e);
}
}
}

View File

@@ -1542,6 +1542,7 @@ class Libzot {
$local_public = $public;
$item_result = null;
$parent = null;
$DR = new DReport(z_root(), $sender, $d, $arr['mid'], $arr['uuid']);
@@ -1996,7 +1997,7 @@ class Libzot {
}
$DR->addto_update('relayed');
$result[] = $DR->get();
$result = [$DR->get()];
}
}

View File

@@ -8,17 +8,18 @@ class MessageFilter {
public static function evaluate($item, $incl, $excl) {
$text = prepare_text($item['body'],((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
$text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text);
$text = prepare_text($item['body'], ((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
$text = html2plain((!empty($item['title'])) ? $item['title'] . ' ' . $text : $text);
$lang = null;
if ((strpos($incl, 'lang=') !== false) || (strpos($excl, 'lang=') !== false) || (strpos($incl, 'lang!=') !== false) || (strpos($excl, 'lang!=') !== false)) {
$lang = detect_language($text);
}
$tags = ((isset($item['term']) && is_array($item['term']) && count($item['term'])) ? $item['term'] : false);
$until = null;
// exclude always has priority
$exclude = (($excl) ? explode("\n", $excl) : null);
@@ -41,7 +42,13 @@ class MessageFilter {
return false;
}
}
elseif (substr($word, 0, 1) === '#' && $tags) {
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;
@@ -89,7 +96,13 @@ class MessageFilter {
return true;
}
}
elseif (substr($word, 0, 1) === '#' && $tags) {
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;
@@ -124,9 +137,7 @@ class MessageFilter {
/**
* @brief Test for Conditional Execution conditions. Shamelessly ripped off from Code/Render/Comanche
*
* This is extensible. The first version of variable testing supports tests of the forms:
* 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';
@@ -143,103 +154,110 @@ class MessageFilter {
*
* The values 0, '', an empty array, and an unset value will all evaluate to false.
*
* @param string $s
* @param array $item
* @return bool
* @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.
*/
public static function test_condition($s,$item) {
public static function test_condition($s, $item) {
$s = trim($s);
// 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;
}
// 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;
}
// Basic checks
// 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);
if (stripos($x, trim($matches[2])) !== false) {
return true;
}
return false;
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
return (stripos($x, trim($matches[2])) !== false);
}
// Equality
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;
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
return ($x == trim($matches[2]));
}
// Inequality
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;
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
return ($x != trim($matches[2]));
}
// 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);
if ($x >= trim($matches[2])) {
return true;
}
return false;
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
return ($x >= trim($matches[2]));
}
// 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);
if ($x <= trim($matches[2])) {
return true;
}
return false;
$x = ((array_key_exists(trim($matches[1]), $item)) ? $item[trim($matches[1])] : EMPTY_STR);
return ($x <= trim($matches[2]));
}
// Greater than
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;
$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]));
}
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);
if (is_array($x) && array_key_exists(trim($matches[2]), $x)) {
return true;
}
return false;
$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);
if (!$x) {
return true;
}
return false;
$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);
if ($x) {
return true;
}
return false;
$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;
}
}

View File

@@ -21,4 +21,13 @@ class Text {
return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false);
}
public static function rawurlencode_parts(string $string): string {
if (!$string) {
return EMPTY_STR;
}
return implode('/', array_map('rawurlencode', explode('/', $string)));
}
}

View File

@@ -4,8 +4,6 @@ namespace Zotlabs\Lib;
use App;
use Zotlabs\Access\AccessList;
use Zotlabs\Lib\Apps;
use Zotlabs\Lib\Config;
require_once('include/text.php');
@@ -26,6 +24,7 @@ class ThreadItem {
private $parent = null;
private $conversation = null;
private $redirect_url = null;
private $owner_addr = '';
private $owner_url = '';
private $owner_photo = '';
private $owner_name = '';
@@ -35,14 +34,12 @@ class ThreadItem {
private $channel = null;
private $display_mode = 'normal';
private $reload = '';
private $mid_uuid_map = [];
public function __construct($data) {
$this->data = $data;
$this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
$this->threaded = Config::Get('system','thread_allow');
$this->threaded = ((local_channel()) ? PConfig::Get(local_channel(), 'system', 'thread_allow', true) : Config::Get('system', 'thread_allow', true));
// Prepare the children
if(isset($data['children'])) {
@@ -65,8 +62,6 @@ class ThreadItem {
unset($this->data['children']);
}
// allow a site to configure the order and content of the reaction emoji list
if($this->toplevel) {
$x = Config::Get('system','reactions');
@@ -84,7 +79,7 @@ class ThreadItem {
* _ false on failure
*/
public function get_template_data($conv_responses, $mid_uuid_map, $thread_level=1, $conv_flags = []) {
public function get_template_data($thread_level=1, $conv_flags = []) {
$result = [];
$item = $this->get_data();
@@ -103,6 +98,8 @@ class ThreadItem {
$conv = $this->get_conversation();
$observer = $conv->get_observer();
$conv->mid_uuid_map[$item['mid']] = $item['uuid'];
$acl = new AccessList([]);
$acl->set($item);
@@ -114,7 +111,7 @@ class ThreadItem {
$locktype = intval($item['item_private']);
if ($locktype === 2) {
$lock = t('Direct message');
$lock = t('Private message');
}
// 0 = limited based on public policy
@@ -209,9 +206,9 @@ class ThreadItem {
}
if (in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) {
$response_verbs[] = 'attendyes';
$response_verbs[] = 'attendno';
$response_verbs[] = 'attendmaybe';
$response_verbs[] = 'accept';
$response_verbs[] = 'reject';
$response_verbs[] = 'tentativeaccept';
if($this->is_commentable() && $observer) {
$isevent = true;
$attend = array( t('I will attend'), t('I will not attend'), t('I might attend'));
@@ -222,17 +219,8 @@ class ThreadItem {
$response_verbs[] = 'answer';
}
if (!feature_enabled($conv->get_profile_owner(),'dislike')) {
unset($conv_responses['dislike']);
}
$responses = get_responses($conv_responses,$response_verbs,$this,$item);
$my_responses = [];
foreach($response_verbs as $v) {
$my_responses[$v] = ((isset($conv_responses[$v][$item['mid'] . '-m'])) ? 1 : 0);
}
$response_verbs[] = 'comment';
$responses = get_responses($response_verbs, $item);
/*
* We should avoid doing this all the time, but it depends on the conversation mode
@@ -242,7 +230,13 @@ class ThreadItem {
$this->check_wall_to_wall();
$children = $this->get_children();
$children_count = count($children);
if($this->is_toplevel()) {
$conv->comments_total = $responses['comment']['count'] ?? 0;
$conv->comments_loaded = $children_count;
if((local_channel() && $conv->get_profile_owner() === local_channel()) || (local_channel() && App::$module === 'pubstream')) {
$star = [
'toggle' => t("Toggle Star Status"),
@@ -254,7 +248,6 @@ class ThreadItem {
$is_comment = true;
}
$verified = (intval($item['item_verified']) ? t('Message signature validated') : '');
$forged = ((($item['sig']) && (! intval($item['item_verified']))) ? t('Message signature incorrect') : '');
$unverified = '' ; // (($this->is_wall_to_wall() && (! intval($item['item_verified']))) ? t('Message cannot be verified') : '');
@@ -287,15 +280,11 @@ class ThreadItem {
if((in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) && $conv->get_profile_owner() == local_channel())
$has_event = true;
$like = [];
$dislike = [];
$reply_to = [];
$reactions_allowed = false;
if($this->is_commentable() && $observer) {
$like = array( t("I like this \x28toggle\x29"), t("like"));
$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
$reply_to = array( t("Reply to this comment"), t("reply"), t("Reply to"));
$reply_to = array( t("Reply to this message"), t("reply"), t("Reply to"));
$reactions_allowed = true;
}
@@ -339,9 +328,8 @@ class ThreadItem {
$viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . urlencode(gen_link_id($item['mid']));
$comment_count_txt = ['label' => sprintf(tt('%d comment', '%d comments', $total_children), $total_children), 'count' => $total_children];
$list_unseen_txt = $unseen_comments ? ['label' => sprintf(t('%d unseen'), $unseen_comments), 'count' => $unseen_comments] : [];
$children = $this->get_children();
$list_unseen_txt = $unseen_comments ? ['label' => sprintf(t('%d unseen'), $unseen_comments), 'count' => $unseen_comments] : [];
$has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false);
@@ -351,14 +339,7 @@ class ThreadItem {
$midb64 = $item['uuid'];
$mids = [ $item['uuid'] ];
$response_mids = [];
foreach($response_verbs as $v) {
if(isset($conv_responses[$v]['mids'][$item['mid']])) {
$response_mids = array_merge($response_mids, $conv_responses[$v]['mids'][$item['mid']]);
}
}
$mids = array_merge($mids, $response_mids);
$json_mids = json_encode($mids);
// Pinned item processing
@@ -372,6 +353,22 @@ class ThreadItem {
$contact = App::$contacts[$item['author_xchan']];
}
$blog_mode = $this->get_display_mode() === 'list';
$load_more = false;
$load_more_title = '';
$comments_total_percent = 0;
if (($conv->comments_total > $conv->comments_loaded) || ($blog_mode && $conv->comments_total > 3)) {
// provide a load more comments button
$load_more = true;
$load_more_title = sprintf(t('Load the next few of total %d comments'), $conv->comments_total);
$comments_total_percent = round(100 * 3 / $conv->comments_total);
}
$expand = '';
if ($this->threaded && !empty($item['comment_count'] && !$this->is_toplevel())) {
$expand = t('Expand Replies');
}
$tmp_item = array(
'template' => $this->get_template(),
'mode' => $mode,
@@ -384,9 +381,9 @@ class ThreadItem {
'folders' => $body['folders'],
'text' => strip_tags($body['html']),
'id' => $this->get_id(),
'parent' => $item['parent'],
'mid' => $midb64,
'mids' => $json_mids,
'parent' => $item['parent'],
'author_id' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']),
'author_is_group_actor' => (($item['author']['xchan_pubforum']) ? t('Forum') : ''),
'isevent' => $isevent,
@@ -431,6 +428,7 @@ class ThreadItem {
'vote_title' => t('Voting Options'),
'is_comment' => $is_comment,
'is_new' => $is_new,
'owner_addr' => $this->get_owner_addr(),
'owner_url' => $this->get_owner_url(),
'owner_photo' => $this->get_owner_photo(),
'owner_name' => $this->get_owner_name(),
@@ -440,13 +438,12 @@ class ThreadItem {
'reactions' => $this->reactions,
// Item toolbar buttons
'emojis' => (($this->is_toplevel() && $this->is_commentable() && $observer && feature_enabled($conv->get_profile_owner(),'emojis')) ? '1' : ''),
'like' => $like,
'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
'reply_to' => (((! $this->is_toplevel()) && feature_enabled($conv->get_profile_owner(),'reply_to')) ? $reply_to : ''),
'reply_to' => ((feature_enabled($conv->get_profile_owner(),'reply_to')) ? $reply_to : ''),
'top_hint' => t("Go to previous comment"),
'share' => $share,
'embed' => $embed,
'rawmid' => $item['mid'],
'parent_mid' => $item['parent_mid'],
'plink' => get_plink($item),
'edpost' => $edpost,
'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts') && ($item['item_type'] == ITEM_TYPE_POST)) ? $star : ''),
@@ -466,16 +463,17 @@ class ThreadItem {
'list_unseen_txt' => $list_unseen_txt,
'markseen' => t('Mark all comments seen'),
'responses' => $responses,
'my_responses' => $my_responses,
// 'my_responses' => $my_responses,
'modal_dismiss' => t('Close'),
'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box()),
'comment_hidden' => feature_enabled($conv->get_profile_owner(),'reply_to'),
'no_comment' => (($item['item_thread_top'] && $item['item_nocomment'])? t('Comments disabled') : ''),
'previewing' => ($conv->is_preview() ? true : false ),
'preview_lbl' => t('This is an unsaved preview'),
'wait' => t('Please wait'),
'thread_level' => $thread_level,
'settings' => $settings,
'thr_parent_uuid' => (($item['parent_mid'] != $item['thr_parent']) ? $mid_uuid_map[$item['thr_parent']] : ''),
'thr_parent_uuid' => (($item['parent_mid'] !== $item['thr_parent'] && isset($conv->mid_uuid_map[$item['thr_parent']])) ? $conv->mid_uuid_map[$item['thr_parent']] : ''),
'contact_id' => (($contact) ? $contact['abook_id'] : ''),
'moderate' => ($item['item_blocked'] == ITEM_MODERATED),
'moderate_approve' => t('Approve'),
@@ -483,7 +481,25 @@ class ThreadItem {
'rtl' => in_array($item['lang'], rtl_languages()),
'reactions_allowed' => $reactions_allowed,
'reaction_str' => [t('Add yours'), t('Remove yours')],
'is_contained' => $this->is_toplevel() && str_contains($item['tgt_type'], 'Collection')
'is_contained' => $this->is_toplevel() && str_contains($item['tgt_type'], 'Collection'),
'observer_activity' => [
'like' => intval($item['observer_like_count'] ?? 0),
'dislike' => intval($item['observer_dislike_count'] ?? 0),
'announce' => intval($item['observer_announce_count'] ?? 0),
'comment' => intval($item['observer_comment_count'] ?? 0),
'accept' => intval($item['observer_accept_count'] ?? 0),
'reject' => intval($item['observer_reject_count'] ?? 0),
'tentativeaccept' => intval($item['observer_tentativeaccept_count'] ?? 0)
],
'threaded' => $this->threaded,
'blog_mode' => $blog_mode,
'collapse_comments' => t('show less'),
'expand_comments' => $this->threaded ? t('show more') : t('show all'),
'load_more' => $load_more,
'load_more_title' => $load_more_title,
'comments_total' => $conv->comments_total,
'comments_total_percent' => $comments_total_percent,
'expand' => $expand
);
$arr = array('item' => $item, 'output' => $tmp_item);
@@ -492,25 +508,19 @@ class ThreadItem {
$result = $arr['output'];
$result['children'] = array();
$nb_children = count($children);
$visible_comments = Config::Get('system', 'expanded_comments', 3);
$visible_comments = 3; // Config::Get('system', 'expanded_comments', 3);
if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
if(($this->get_display_mode() === 'normal') && ($children_count > 0)) {
foreach($children as $child) {
$result['children'][] = $child->get_template_data($conv_responses, $mid_uuid_map, $thread_level + 1,$conv_flags);
$result['children'][] = $child->get_template_data($thread_level + 1, $conv_flags);
}
// Collapse
if(($nb_children > $visible_comments) || ($thread_level > 1)) {
if($thread_level === 1 && $children_count > $visible_comments) {
$result['children'][0]['comment_firstcollapsed'] = true;
$result['children'][0]['num_comments'] = $comment_count_txt['label'];
$result['children'][0]['hide_text'] = t('show all');
if($thread_level > 1) {
$result['children'][$nb_children - 1]['comment_lastcollapsed'] = true;
}
else {
$result['children'][$nb_children - ($visible_comments + 1)]['comment_lastcollapsed'] = true;
}
$result['children'][$children_count - ($visible_comments + 1)]['comment_lastcollapsed'] = true;
}
}
@@ -763,7 +773,7 @@ class ThreadItem {
*/
private function get_comment_box() {
if(!$this->is_toplevel() && !Config::Get('system','thread_allow')) {
if(!$this->is_toplevel()) {
return '';
}
@@ -803,7 +813,7 @@ class ThreadItem {
'$eduline' => t('Underline'),
'$edquote' => t('Quote'),
'$edcode' => t('Code'),
'$edimg' => t('Image'),
'$edimg' => t('Embed (existing) photo from your photo albums'),
'$edatt' => t('Attach/Upload file'),
'$edurl' => t('Insert Link'),
'$edvideo' => t('Video'),
@@ -834,6 +844,7 @@ class ThreadItem {
$conv = $this->get_conversation();
$this->wall_to_wall = false;
$this->owner_url = '';
$this->owner_addr = '';
$this->owner_photo = '';
$this->owner_name = '';
@@ -842,12 +853,14 @@ class ThreadItem {
if($this->is_toplevel() && ($this->get_data_value('author_xchan') != $this->get_data_value('owner_xchan'))) {
$this->owner_url = chanlink_hash($this->data['owner']['xchan_hash']);
$this->owner_addr = $this->data['owner']['xchan_addr'];
$this->owner_photo = $this->data['owner']['xchan_photo_s'];
$this->owner_name = $this->data['owner']['xchan_name'];
$this->wall_to_wall = true;
}
elseif($this->is_toplevel() && $this->get_data_value('verb') === 'Announce' && isset($this->data['source'])) {
$this->owner_url = chanlink_hash($this->data['source']['xchan_hash']);
$this->owner_addr = $this->data['source']['xchan_addr'];
$this->owner_photo = $this->data['source']['xchan_photo_s'];
$this->owner_name = $this->data['source']['xchan_name'];
$this->wall_to_wall = true;
@@ -862,6 +875,10 @@ class ThreadItem {
return $this->owner_url;
}
private function get_owner_addr() {
return $this->owner_addr;
}
private function get_owner_photo() {
return $this->owner_photo;
}

View File

@@ -24,6 +24,10 @@ class ThreadStream {
private $prepared_item = '';
public $reload = '';
private $cipher = 'AES-128-CCM';
public $mid_uuid_map = [];
public $comments_total = 0;
public $comments_loaded = 0;
// $prepared_item is for use by alternate conversation structures such as photos
// wherein we've already prepared a top level item which doesn't look anything like
@@ -211,16 +215,15 @@ class ThreadStream {
* _ The data requested on success
* _ false on failure
*/
public function get_template_data($conv_responses, $mid_uuid_map) {
public function get_template_data() {
$result = array();
foreach($this->threads as $item) {
if(($item->get_data_value('id') == $item->get_data_value('parent')) && $this->prepared_item) {
$item_data = $this->prepared_item;
}
else {
$item_data = $item->get_template_data($conv_responses, $mid_uuid_map);
$item_data = $item->get_template_data();
}
if(!$item_data) {
logger('Failed to get item template data ('. $item->get_id() .').', LOGGER_DEBUG, LOG_ERR);

View File

@@ -9,7 +9,7 @@ trait HelpHelperTrait {
// PHP versions before 8.2 does not support trait constants,
// Leave this commented out until we drop support for PHP 8.1.
//
// const VALID_FILE_EXT = ['md', 'bb', 'html'];
// const VALID_FILE_EXT = ['md', 'bb', 'html', 'json'];
private string $file_name = '';
private string $file_type = '';
@@ -58,7 +58,7 @@ trait HelpHelperTrait {
private function find_help_file(string $base_path, string $lang): void {
// Use local variable until we can use trait constants.
$valid_file_ext = ['md', 'bb', 'html'];
$valid_file_ext = ['md', 'bb', 'html', 'json'];
$base_path_with_lang = "doc/{$lang}/${base_path}";
@@ -89,7 +89,7 @@ trait HelpHelperTrait {
);
return bbcode(
t("This page is not yet available in {$prefered_language_name}. See [observer.baseurl]/help/developer/developer_guide#Translations for information about how to help.")
t("This page is not yet available in {$prefered_language_name}. See [observer.baseurl]/help/developer/developers_guide#Translations for information about how to help.")
);
}
}

View File

@@ -29,11 +29,11 @@ class Acl extends \Zotlabs\Web\Controller {
// logger('mod_acl: ' . print_r($_GET,true),LOGGER_DATA);
$start = (x($_REQUEST,'start') ? $_REQUEST['start'] : 0);
$count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 500);
$search = (x($_REQUEST,'search') ? $_REQUEST['search'] : '');
$type = (x($_REQUEST,'type') ? $_REQUEST['type'] : '');
$noforums = (x($_REQUEST,'n') ? $_REQUEST['n'] : false);
$start = (!empty($_REQUEST['start']) ? $_REQUEST['start'] : 0);
$count = (!empty($_REQUEST['count']) ? $_REQUEST['count'] : 500);
$search = (!empty($_REQUEST['search']) ? $_REQUEST['search'] : '');
$type = (!empty($_REQUEST['type']) ? $_REQUEST['type'] : '');
$noforums = (!empty($_REQUEST['n']) ? $_REQUEST['n'] : false);
// $type =
@@ -53,7 +53,7 @@ class Acl extends \Zotlabs\Web\Controller {
// List of channels whose connections to also suggest,
// e.g. currently viewed channel or channels mentioned in a post
$extra_channels = (x($_REQUEST,'extra_channels') ? $_REQUEST['extra_channels'] : array());
$extra_channels = (!empty($_REQUEST['extra_channels']) ? $_REQUEST['extra_channels'] : []);
// The different autocomplete libraries use different names for the search text
// parameter. Internally we'll use $search to represent the search text no matter
@@ -416,7 +416,7 @@ class Acl extends \Zotlabs\Web\Controller {
}
$dirmode = intval(Config::Get('system','directory_mode'));
$search = ((x($_REQUEST,'search')) ? htmlentities($_REQUEST['search'],ENT_COMPAT,'UTF-8',false) : '');
$search = ((!empty($_REQUEST['search'])) ? htmlentities($_REQUEST['search'], ENT_COMPAT, 'UTF-8', false) : '');
if(! $search || mb_strlen($search) < 2)
return array();
@@ -446,7 +446,7 @@ class Acl extends \Zotlabs\Web\Controller {
$token = Config::Get('system','realm_token');
$count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 100);
$count = (!empty($_REQUEST['count']) ? $_REQUEST['count'] : 100);
if($url) {
$query = $url . '?f=' . (($token) ? '&t=' . urlencode($token) : '');
$query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode(punify($search)) : '');

View File

@@ -23,7 +23,7 @@ class Activity extends Controller {
if (! $item_id)
http_status_exit(404, 'Not found');
$portable_id = EMPTY_STR;
$portable_id = null;
$item_normal_extra = sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ",
dbesc(ACTIVITY_FOLLOW),
@@ -166,6 +166,7 @@ class Activity extends Controller {
return;
}
$portable_id = null;
$ob_authorize = false;
$item_uid = 0;

View File

@@ -133,6 +133,11 @@ class Attach_edit extends Controller {
}
$x = attach_move($channel_id, $resource, $newfolder, (($single) ? $newfilename : ''));
if (!$x['success']) {
notice($x['message'] . EOL);
goaway($return_path);
}
$actions_done .= 'move,';
}

View File

@@ -85,7 +85,7 @@ class Channel extends Controller {
$headers = [
'Content-Type' => 'application/x-zot+json',
'Digest' => HTTPSig::generate_digest_header($data),
'(request-target)' => strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']
'Date' => datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T')
];
$h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel));
@@ -243,7 +243,7 @@ class Channel extends Controller {
// search terms header
if ($search) {
$o .= replace_macros(get_markup_template("section_title.tpl"), [
'$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8')
'$title' => t('Searching for:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8')
]);
}
@@ -266,7 +266,7 @@ class Channel extends Controller {
'default_location' => (($is_owner) ? App::$profile['channel_location'] : ''),
'nickname' => App::$profile['channel_address'],
'lockstate' => (((strlen(App::$profile['channel_allow_cid'])) || (strlen(App::$profile['channel_allow_gid'])) || (strlen(App::$profile['channel_deny_cid'])) || (strlen(App::$profile['channel_deny_gid']))) ? 'lock' : 'unlock'),
'acl' => (($is_owner) ? populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post') : ''),
'acl' => (($is_owner) ? populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions') : ''),
'permissions' => $channel_acl,
'showacl' => (($is_owner) ? 'yes' : ''),
'bang' => '',
@@ -298,12 +298,15 @@ class Channel extends Controller {
$item_normal = item_normal();
$item_normal_update = item_normal_update();
$sql_extra = item_permissions_sql(App::$profile['profile_uid']);
$sql_extra = '';
$permission_sql = item_permissions_sql(App::$profile['profile_uid']);
if (feature_enabled(App::$profile['profile_uid'], 'channel_list_mode') && (!$mid))
$page_mode = 'client';
$blog_mode = feature_enabled(App::$profile['profile_uid'], 'channel_list_mode') && !$mid;
if ($blog_mode) {
$page_mode = 'list';
else
$page_mode = 'client';
}
$abook_uids = " and abook.abook_channel = " . intval(App::$profile['profile_uid']) . " ";
@@ -334,8 +337,8 @@ class Channel extends Controller {
if (($update) && (!$load)) {
if ($mid) {
$r = q("SELECT parent AS item_id, uuid from item where $identifier = '%s' and uid = %d $item_normal_update
AND item_wall = 1 $simple_update $sql_extra limit 1",
$r = q("SELECT *, parent AS item_id from item where $identifier = '%s' and uid = %d $item_normal_update
AND item_wall = 1 $simple_update $permission_sql $sql_extra limit 1",
dbesc($mid),
intval(App::$profile['profile_uid'])
);
@@ -346,6 +349,7 @@ class Channel extends Controller {
WHERE uid = %d $item_normal_update
AND item_wall = 1 $simple_update
AND (abook.abook_blocked = 0 or abook.abook_flags is null)
$permission_sql
$sql_extra
ORDER BY created DESC",
intval(App::$profile['profile_uid'])
@@ -382,8 +386,8 @@ class Channel extends Controller {
if ($noscript_content || $load) {
if ($mid) {
$r = q("SELECT parent AS item_id, uuid from item where $identifier = '%s' and uid = %d $item_normal
AND item_wall = 1 $sql_extra limit 1",
$r = q("SELECT item.parent AS item_id, item.verb from item where $identifier = '%s' and item.uid = %d $item_normal
AND item.item_wall = 1 $permission_sql $sql_extra limit 1",
dbesc($mid),
intval(App::$profile['profile_uid'])
);
@@ -392,13 +396,18 @@ class Channel extends Controller {
}
}
else {
$r = q("SELECT DISTINCT item.parent AS item_id, $ordering FROM item
left join abook on ( item.author_xchan = abook.abook_xchan $abook_uids )
WHERE true and item.uid = %d $item_normal
$r = q("SELECT item.parent AS item_id, item.verb, $ordering FROM item
LEFT JOIN abook ON (item.author_xchan = abook.abook_xchan $abook_uids)
WHERE item.uid = %d
AND item.id = item.parent
AND (abook.abook_blocked = 0 or abook.abook_flags is null)
AND item.item_wall = 1 AND item.item_thread_top = 1
$sql_extra $sql_extra2
ORDER BY $ordering DESC, item_id $pager_sql ",
AND item.item_wall = 1
$item_normal
$permission_sql
$sql_extra
$sql_extra2
ORDER BY $ordering DESC, item_id
$pager_sql",
intval(App::$profile['profile_uid'])
);
}
@@ -408,19 +417,20 @@ class Channel extends Controller {
}
}
if ($r) {
$parents_str = ids_to_querystr($r, 'item_id');
$r = q("SELECT item.*, item.id AS item_id
FROM item
WHERE item.uid = %d $item_normal
AND item.parent IN ( %s )
$sql_extra ",
intval(App::$profile['profile_uid']),
dbesc($parents_str)
);
// 11.08.2025 start transition deprecated AS1 item.verb vocabulary to AS2 on demand.
// Keep this until we officially deprecate AS1 data.
AS1_to_AS2_verbs($r);
xchan_query($r);
$items = fetch_post_tags($r, true);
$thr_parents = null;
if ($mid) {
$thr_parents = get_recursive_thr_parents($r[0]);
}
$items = items_by_parent_ids($r, $thr_parents, $permission_sql, $blog_mode);
xchan_query($items);
$items = fetch_post_tags($items, true);
$items = conv_sort($items, $ordering);
if ($load && $mid && (!count($items))) {
@@ -434,10 +444,7 @@ class Channel extends Controller {
$items = [];
}
$mode = (($search) ? 'search' : 'channel');
$mode = 'channel';
if ((!$update) && (!$load)) {

View File

@@ -7,6 +7,7 @@ namespace Zotlabs\Module;
* Module for accessing the DAV storage area.
*/
use App;
use Sabre\DAV as SDAV;
use Zotlabs\Web\Controller;
use Zotlabs\Storage\BasicAuth;
@@ -32,6 +33,15 @@ 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);
@@ -44,15 +54,13 @@ class Cloud extends Controller {
if ($which)
profile_load( $which, $profile);
$auth = new BasicAuth();
$ob_hash = get_observer_hash();
if ($ob_hash) {
if (local_channel()) {
$channel = \App::get_channel();
$channel = App::get_channel();
$auth->setCurrentUser($channel['channel_address']);
$auth->channel_account_id = $channel['channel_account_id'];
$auth->channel_id = $channel['channel_id'];
@@ -63,19 +71,12 @@ class Cloud extends Controller {
$auth->observer = $ob_hash;
}
// if we arrived at this path with any query parameters in the url, build a clean url without
// them and redirect.
if(! array_key_exists('cloud_sort',$_SESSION)) {
$_SESSION['cloud_sort'] = 'name';
}
$_SESSION['cloud_sort'] = ((isset($_REQUEST['sort']) && $_REQUEST['sort']) ? trim(notags($_REQUEST['sort'])) : $_SESSION['cloud_sort']);
$x = clean_query_string();
if($x !== \App::$query_string)
goaway(z_root() . '/' . $x);
$rootDirectory = new Directory('/', [], $auth);
// A SabreDAV server-object
@@ -116,16 +117,16 @@ class Cloud extends Controller {
function DAVException($err) {
if($err instanceof \Sabre\DAV\Exception\NotFound) {
\App::$page['content'] = '<h2>404 Not found</h2>';
App::$page['content'] = '<h2>404 Not found</h2>';
}
elseif($err instanceof \Sabre\DAV\Exception\Forbidden) {
\App::$page['content'] = '<h2>403 Forbidden</h2>';
App::$page['content'] = '<h2>403 Forbidden</h2>';
}
elseif($err instanceof \Sabre\DAV\Exception\NotImplemented) {
goaway(z_root() . '/' . \App::$query_string);
goaway(z_root() . '/' . App::$query_string);
}
else {
\App::$page['content'] = '<h2>Unknown error</h2>';
App::$page['content'] = '<h2>Unknown error</h2>';
}
construct_page();

View File

@@ -81,7 +81,7 @@ class Display extends Controller {
'default_location' => $channel['channel_location'],
'nickname' => $channel['channel_address'],
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'),
'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions'),
'permissions' => $channel_acl,
'bang' => '',
'visitor' => true,
@@ -212,7 +212,7 @@ class Display extends Controller {
$observer_hash = get_observer_hash();
$item_normal = item_normal();
$item_normal_update = item_normal_update();
$sql_extra = '';
$permission_sql = '';
$r = [];
if($noscript_content || $load) {
@@ -231,7 +231,7 @@ class Display extends Controller {
}
if(!$r) {
$sql_extra = item_permissions_sql(0, $observer_hash);
$permission_sql = item_permissions_sql(0, $observer_hash);
$r = q("SELECT item.id AS item_id FROM item
WHERE ((mid = '%s'
@@ -239,7 +239,7 @@ class Display extends Controller {
AND item.deny_gid = '' AND item_private = 0 )
AND uid IN ( " . stream_perms_api_uids(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " ))
OR uid = %d ))) OR
(mid = '%s' $sql_extra ))
(mid = '%s' $permission_sql ))
$item_normal
limit 1",
dbesc($target_item['parent_mid']),
@@ -269,7 +269,7 @@ class Display extends Controller {
}
if(!$r) {
$sql_extra = item_permissions_sql(0, $observer_hash);
$permission_sql = item_permissions_sql(0, $observer_hash);
$r = q("SELECT item.id as item_id from item
WHERE ((parent_mid = '%s'
@@ -277,7 +277,7 @@ class Display extends Controller {
AND item.deny_gid = '' AND item_private = 0 )
and uid in ( " . stream_perms_api_uids(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " ))
OR uid = %d ))) OR
(parent_mid = '%s' $sql_extra ))
(parent_mid = '%s' $permission_sql ))
$item_normal
limit 1",
dbesc($target_item['parent_mid']),
@@ -288,17 +288,12 @@ class Display extends Controller {
}
if($r) {
$parents_str = ids_to_querystr($r,'item_id');
if($parents_str) {
$items = q("SELECT item.*, item.id AS item_id
FROM item
WHERE parent in ( %s ) $sql_extra $item_normal ",
dbesc($parents_str)
);
xchan_query($items);
$items = fetch_post_tags($items,true);
$items = conv_sort($items,'created');
}
$thr_parents = get_recursive_thr_parents($target_item);
$items = items_by_parent_ids($r, $thr_parents, $permission_sql);
xchan_query($items);
$items = fetch_post_tags($items,true);
$items = conv_sort($items,'created');
}
else {
$items = array();

View File

@@ -122,10 +122,10 @@ class Editblock extends \Zotlabs\Web\Controller {
'ptyp' => $itm[0]['type'],
'mimeselect' => true,
'mimetype' => $itm[0]['mimetype'],
'body' => undo_post_tagging($content),
'body' => htmlspecialchars_decode(undo_post_tagging($content), ENT_COMPAT),
'post_id' => $post_id,
'visitor' => true,
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
'title' => htmlspecialchars_decode($itm[0]['title'], ENT_COMPAT),
'placeholdertitle' => t('Title (optional)'),
'pagetitle' => $block_title,
'profile_uid' => (intval($channel['channel_id'])),

View File

@@ -121,9 +121,9 @@ class Editlayout extends \Zotlabs\Web\Controller {
'hide_preview' => true,
'disable_comments' => true,
'ptyp' => $itm[0]['obj_type'],
'body' => undo_post_tagging($itm[0]['body']),
'body' => htmlspecialchars_decode(undo_post_tagging($itm[0]['body']), ENT_COMPAT),
'post_id' => $post_id,
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
'title' => htmlspecialchars_decode($itm[0]['title'], ENT_COMPAT),
'pagetitle' => $layout_title,
'ptlabel' => t('Layout Name'),
'placeholdertitle' => t('Layout Description (Optional)'),

View File

@@ -144,7 +144,7 @@ class Editwebpage extends \Zotlabs\Web\Controller {
'hide_location' => true,
'hide_voting' => true,
'ptyp' => $itm[0]['type'],
'body' => undo_post_tagging($content),
'body' => htmlspecialchars_decode(undo_post_tagging($content), ENT_COMPAT),
'post_id' => $post_id,
'visitor' => ($is_owner) ? true : false,
'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')),
@@ -154,7 +154,7 @@ class Editwebpage extends \Zotlabs\Web\Controller {
'mimeselect' => true,
'layout' => $layout,
'layoutselect' => true,
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
'title' => htmlspecialchars_decode($itm[0]['title'], ENT_COMPAT),
'lockstate' => (((strlen($itm[0]['allow_cid'])) || (strlen($itm[0]['allow_gid'])) || (strlen($itm[0]['deny_cid'])) || (strlen($itm[0]['deny_gid']))) ? 'lock' : 'unlock'),
'profile_uid' => (intval($owner)),
'bbcode' => (($mimetype == 'text/bbcode') ? true : false)

View File

@@ -43,7 +43,7 @@ class Embedphotos extends \Zotlabs\Web\Controller {
$arr = explode('/', $href);
$resource_id = array_pop($arr);
$x = self::photolink($resource_id);
if($x)
if($x)
json_return_and_die(array('status' => true, 'photolink' => $x, 'resource_id' => $resource_id));
json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false));
}
@@ -55,7 +55,7 @@ class Embedphotos extends \Zotlabs\Web\Controller {
$output = EMPTY_STR;
if($channel) {
$resolution = ((feature_enabled($channel['channel_id'],'large_photos')) ? 1 : 2);
$r = q("select mimetype, height, width from photo where resource_id = '%s' and $resolution = %d and uid = %d limit 1",
$r = q("select mimetype, filename from photo where resource_id = '%s' and $resolution = %d and uid = %d limit 1",
dbesc($resource),
intval($resolution),
intval($channel['channel_id'])
@@ -63,6 +63,8 @@ class Embedphotos extends \Zotlabs\Web\Controller {
if(! $r)
return $output;
$filename = $r[0]['filename'];
if($r[0]['mimetype'] === 'image/jpeg')
$ext = '.jpg';
elseif($r[0]['mimetype'] === 'image/png')
@@ -75,7 +77,7 @@ class Embedphotos extends \Zotlabs\Web\Controller {
$ext = EMPTY_STR;
$output = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $resource . ']' .
'[zmg=' . $r[0]['width'] . 'x' . $r[0]['height'] . ']' . z_root() . '/photo/' . $resource . '-' . $resolution . $ext . '[/zmg][/zrl]';
'[zmg=' . z_root() . '/photo/' . $resource . '-' . $resolution . $ext . ']' . $filename . '[/zmg][/zrl]' . "\r\n";
return $output;
}

View File

@@ -11,39 +11,42 @@ require_once('include/photos.php');
class File_upload extends \Zotlabs\Web\Controller {
function post() {
logger('file upload: ' . print_r($_REQUEST,true));
logger('file upload: ' . print_r($_POST,true));
logger('file upload: ' . print_r($_FILES,true));
$channel = (($_REQUEST['channick']) ? channelx_by_nick($_REQUEST['channick']) : null);
$channel = (($_POST['channick']) ? channelx_by_nick($_POST['channick']) : null);
if(! $channel) {
if (!$channel) {
logger('channel not found');
killme();
is_ajax() ? killme() : goaway(z_root() . '/' . $_POST['return_url']);
}
$_REQUEST['source'] = 'file_upload';
$_POST['source'] = 'file_upload';
if($channel['channel_id'] != local_channel()) {
$_REQUEST['contact_allow'] = expand_acl($channel['channel_allow_cid']);
$_REQUEST['group_allow'] = expand_acl($channel['channel_allow_gid']);
$_REQUEST['contact_deny'] = expand_acl($channel['channel_deny_cid']);
$_REQUEST['group_deny'] = expand_acl($channel['channel_deny_gid']);
$_POST['contact_allow'] = expand_acl($channel['channel_allow_cid']);
$_POST['group_allow'] = expand_acl($channel['channel_allow_gid']);
$_POST['contact_deny'] = expand_acl($channel['channel_deny_cid']);
$_POST['group_deny'] = expand_acl($channel['channel_deny_gid']);
}
$_REQUEST['allow_cid'] = ((isset($_REQUEST['contact_allow'])) ? perms2str($_REQUEST['contact_allow']) : '');
$_REQUEST['allow_gid'] = ((isset($_REQUEST['group_allow'])) ? perms2str($_REQUEST['group_allow']) : '');
$_REQUEST['deny_cid'] = ((isset($_REQUEST['contact_deny'])) ? perms2str($_REQUEST['contact_deny']) : '');
$_REQUEST['deny_gid'] = ((isset($_REQUEST['group_deny'])) ? perms2str($_REQUEST['group_deny']) : '');
$_POST['allow_cid'] = ((isset($_POST['contact_allow'])) ? perms2str($_POST['contact_allow']) : '');
$_POST['allow_gid'] = ((isset($_POST['group_allow'])) ? perms2str($_POST['group_allow']) : '');
$_POST['deny_cid'] = ((isset($_POST['contact_deny'])) ? perms2str($_POST['contact_deny']) : '');
$_POST['deny_gid'] = ((isset($_POST['group_deny'])) ? perms2str($_POST['group_deny']) : '');
if(isset($_REQUEST['filename']) && strlen($_REQUEST['filename'])) {
$r = attach_mkdir($channel, get_observer_hash(), $_REQUEST);
if($r['success']) {
$hash = $r['data']['hash'];
$sync = attach_export_data($channel,$hash);
if($sync) {
Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync)));
}
goaway(z_root() . '/' . $_REQUEST['return_url']);
if(isset($_POST['filename']) && strlen($_POST['filename'])) {
$r = attach_mkdir($channel, get_observer_hash(), $_POST);
if (!$r['success']) {
notice($r['message'] . EOL);
is_ajax() ? killme() : goaway(z_root() . '/' . $_POST['return_url']);
}
$hash = $r['data']['hash'];
$sync = attach_export_data($channel,$hash);
if ($sync) {
Libsync::build_sync_packet($channel['channel_id'], ['file' => [$sync]]);
}
}
else {
@@ -90,19 +93,19 @@ class File_upload extends \Zotlabs\Web\Controller {
}
}
$r = attach_store($channel, get_observer_hash(), '', $_REQUEST);
if($r['success']) {
$sync = attach_export_data($channel,$r['data']['hash']);
if($sync)
Libsync::build_sync_packet($channel['channel_id'],array('file' => array($sync)));
$r = attach_store($channel, get_observer_hash(), '', $_POST);
if (!$r['success']) {
notice($r['message'] . EOL);
is_ajax() ? killme() : goaway(z_root() . '/' . $_POST['return_url']);
}
$sync = attach_export_data($channel,$r['data']['hash']);
if ($sync) {
Libsync::build_sync_packet($channel['channel_id'], ['file' => [$sync]]);
}
}
if(is_ajax())
killme();
goaway(z_root() . '/' . $_REQUEST['return_url']);
is_ajax() ? killme() : goaway(z_root() . '/' . $_POST['return_url']);
}

View File

@@ -53,7 +53,14 @@ class Follow extends Controller {
}
$uid = local_channel();
$url = notags(punify(trim($_REQUEST['url'])));
$url = notags(trim($_REQUEST['url']));
$parsed = parse_url($url);
if (isset($parsed['host'])) {
$parsed['host'] = punify($parsed['host']);
$url = unparse_url($parsed);
}
$return_url = $_SESSION['return_url'];
$interactive = $_REQUEST['interactive'] ?? 1;
$channel = App::get_channel();

View File

@@ -30,7 +30,7 @@ class Help extends \Zotlabs\Web\Controller {
$this->determine_help_language();
if (empty($_REQUEST['search']) && argc() === 1) {
goaway("/help/about/about");
goaway("/help/about");
killme();
}
}
@@ -85,7 +85,7 @@ class Help extends \Zotlabs\Web\Controller {
}
if(argc() > 2 && argv(argc()-2) === 'assets') {
if(argc() > 2 && argv(argc()-2) === 'pic') {
$path = '';
for($x = 1; $x < argc(); $x ++) {
if(strlen($path))

View File

@@ -24,9 +24,13 @@ class Home extends Controller {
$key = Config::Get('system', 'prvkey');
$ret = json_encode(Libzot::site_info());
$headers = ['Content-Type' => 'application/x-zot+json', 'Digest' => HTTPSig::generate_digest_header($ret)];
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
$h = HTTPSig::create_sig($headers, $key, z_root());
$headers = [
'Content-Type' => 'application/x-zot+json',
'Digest' => HTTPSig::generate_digest_header($ret),
'Date' => datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T')
];
$h = HTTPSig::create_sig($headers, $key, z_root());
HTTPSig::set_headers($h);
echo $ret;
@@ -68,9 +72,9 @@ class Home extends Controller {
$o = '';
if (x($_SESSION, 'theme'))
if (isset($_SESSION['theme']))
unset($_SESSION['theme']);
if (x($_SESSION, 'mobile_theme'))
if (isset($_SESSION['mobile_theme']))
unset($_SESSION['mobile_theme']);
$splash = ((argc() > 1 && argv(1) === 'splash') ? true : false);

View File

@@ -3,6 +3,7 @@ namespace Zotlabs\Module;
use App;
use Zotlabs\Widget\Messages;
use Zotlabs\Lib\Config;
class Hq extends \Zotlabs\Web\Controller {
@@ -50,14 +51,18 @@ class Hq extends \Zotlabs\Web\Controller {
// select the target item with a bias to our own item
$sql_order = ((local_channel() > $sys['channel_id']) ? 'DESC' : 'ASC');
$r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where uid in (%d, %d) and $identifier = '%s' order by uid $sql_order limit 2",
$r = q("select id, uid, mid, parent, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where uid in (%d, %d) and $identifier = '%s' order by uid $sql_order limit 2",
intval(local_channel()),
intval($sys['channel_id']),
dbesc($item_hash)
);
if($r) {
$target_item = $r[0];
call_hooks('item_custom_display', $target_item);
if (intval($target_item['uid']) === intval($sys['channel_id'])) {
$sys_item = true;
}
@@ -86,7 +91,7 @@ class Hq extends \Zotlabs\Web\Controller {
'default_location' => $channel['channel_location'],
'nickname' => $channel['channel_address'],
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'),
'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions'),
'permissions' => $channel_acl,
'bang' => '',
'visitor' => true,
@@ -129,7 +134,7 @@ class Hq extends \Zotlabs\Web\Controller {
'$nouveau' => '0',
'$wall' => '0',
'$page' => '1',
'$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0),
'$list' => ((!empty($_REQUEST['list'])) ? intval($_REQUEST['list']) : 0),
'$search' => '',
'$xchan' => '',
'$order' => '',
@@ -145,7 +150,6 @@ class Hq extends \Zotlabs\Web\Controller {
}
if($load && $target_item) {
if (!$sys_item) {
$r = q("SELECT item.id AS item_id FROM item
WHERE uid = %d
@@ -199,11 +203,8 @@ class Hq extends \Zotlabs\Web\Controller {
}
if($r) {
$items = q("SELECT item.*, item.id AS item_id
FROM item
WHERE parent = '%s' $item_normal $sql_extra",
dbesc($r[0]['item_id'])
);
$thr_parents = get_recursive_thr_parents($target_item);
$items = items_by_parent_ids($r, $thr_parents);
xchan_query($items,true,(($sys_item) ? local_channel() : 0));
$items = fetch_post_tags($items,true);

View File

@@ -6,8 +6,8 @@ namespace Zotlabs\Module;
*
* Controller for responding to x-zot: protocol requests
* x-zot:_jkfRG85nJ-714zn-LW_VbTFW8jSjGAhAydOcJzHxqHkvEHWG2E0RbA_pbch-h4R63RG1YJZifaNzgccoLa3MQ/453c1678-1a79-4af7-ab65-6b012f6cab77
*
*/
*
*/
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
@@ -104,7 +104,7 @@ class Id extends Controller {
$headers['Content-Type'] = 'application/x-zot+json' ;
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
$headers['Date'] = datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T');
$h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],channel_url($chan));
HTTPSig::set_headers($h);
echo $ret;

View File

@@ -310,9 +310,9 @@ class Invite extends Controller {
function get() {
// zai1
$channel_id = local_channel();
if(! local_channel()) {
if ($channel_id === false || $channel_id < 1) {
notice( 'ZAI0101E,' . t('Permission denied.') . EOL);
return;
}
@@ -330,15 +330,15 @@ class Invite extends Controller {
return $o;
}
// invitation_by_user may still not configured, the default 'na' will tell this
// if configured, 0 disables invitations by users, other numbers are how many invites a user may propagate
$invuser = Config::Get('system','invitation_by_user', 'na');
$ihave = $this->count_invites_by_user($channel_id);
// if the mortal user drives the invitation
If (! is_site_admin()) {
// when not configured, 4 is the default
$invuser = ($invuser === 'na') ? 4 : $invuser;
if (is_site_admin()) {
// Admins have unlimited invites
$invuser = '∞';
} else {
// invitation_by_user may still not configured, the default 'na' will tell this
// if configured, 0 disables invitations by users, other numbers are how many invites a user may propagate
$invuser = Config::Get('system','invitation_by_user', 4);
// a config value 0 disables invitation by users
if (!$invuser) {
@@ -350,12 +350,6 @@ class Invite extends Controller {
notice( 'ZAI0105W,' . t('You have no more invitations available') . EOL);
return '';
}
} else {
// general deity admin invite limit infinite (theoretical)
if ($invuser === 'na') Config::Set('system','invitation_by_user', 4);
// for display only
$invuser = '∞';
}
// xchan record of the page observer
@@ -394,17 +388,6 @@ class Invite extends Controller {
}
}
if ($wehave > $invmaxau) {
if (! is_site_admin()) {
$feedbk .= 'ZAI0200E,' . t('All users invitation limit exceeded.') . $eol;
}
}
// let see how many invites currently used by the user
$r = q("SELECT count(reg_id) AS n FROM register WHERE reg_vital = 1 AND reg_byc = %d",
intval(local_channel()));
$ihave = $r ? $r[0]['n'] : 0;
$tpl = get_markup_template('invite.tpl');
$inv_rabots = array(
@@ -420,11 +403,11 @@ class Invite extends Controller {
'field' => array(
'name' => 'expire',
'title' => t('duration up from now'),
'value' => ($invexpire_n ? $invexpire_n : 2),
'value' => 2,
'min' => '1',
'max' => '99',
'size' => '2',
'default' => ($invexpire_u ? $invexpire_u : 'd')
'default' => 'd',
),
'rabot' => $inv_rabots
)
@@ -583,5 +566,18 @@ class Invite extends Controller {
}
return false;
}
/**
* Find how many invites the given channel is currently using.
*
* @param int $channel_id The id of the channel
*
* @return int Number of invites this channel is currently using.
*/
private function count_invites_by_user(int $channel): int {
$r = q("SELECT count(reg_id) AS n FROM register WHERE reg_vital = 1 AND reg_byc = %d", $channel);
return $r ? $r[0]['n'] : 0;
}
}

View File

@@ -53,12 +53,12 @@ class Item extends Controller {
if (argc() > 1 && argv(1) !== 'drop') {
$x = q("select uid, item_wall, llink, uuid from item where uuid = '%s' order by item_wall desc",
$x = q("select uid, item_wall, item_type, llink, uuid from item where uuid = '%s' order by item_wall desc",
dbesc(argv(1))
);
if ($x) {
if ($x[0]['item_wall']) {
if ($x[0]['item_wall'] && $x[0]['item_type'] === ITEM_TYPE_POST) {
$c = channelx_by_n($x[0]['uid']);
if ($c) {
goaway(z_root() . '/channel/' . $c['channel_address'] . '?mid=' . $x[0]['uuid']);
@@ -78,7 +78,7 @@ class Item extends Controller {
// This will change. Figure out who the observer is and whether or not
// they have permission to post here. Else ignore the post.
if ((!local_channel()) && (!remote_channel()) && (!x($_REQUEST, 'anonname')))
if ((!local_channel()) && (!remote_channel()) && (empty($_POST['anonname'])))
return;
$uid = local_channel();
@@ -107,12 +107,13 @@ class Item extends Controller {
* Is this a reply to something?
*/
$parent = ((x($_REQUEST, 'parent')) ? intval($_REQUEST['parent']) : 0);
$parent_mid = ((x($_REQUEST, 'parent_mid')) ? trim($_REQUEST['parent_mid']) : '');
$mode = ((isset($_REQUEST['conv_mode']) && $_REQUEST['conv_mode'] === 'channel') ? 'channel' : 'network');
$parent = ((!empty($_POST['parent'])) ? intval($_POST['parent']) : 0);
$thr_parent_id = $parent;
$parent_mid = ((!empty($_POST['parent_mid'])) ? trim($_POST['parent_mid']) : '');
$mode = ((isset($_POST['conv_mode']) && $_POST['conv_mode'] === 'channel') ? 'channel' : 'network');
$remote_xchan = ((x($_REQUEST, 'remote_xchan')) ? trim($_REQUEST['remote_xchan']) : false);
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
$remote_xchan = ((!empty($_POST['remote_xchan'])) ? trim($_POST['remote_xchan']) : false);
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($remote_xchan)
);
if ($r)
@@ -120,7 +121,7 @@ class Item extends Controller {
else
$remote_xchan = $remote_observer = false;
$profile_uid = ((x($_REQUEST, 'profile_uid')) ? intval($_REQUEST['profile_uid']) : 0);
$profile_uid = ((!empty($_POST['profile_uid'])) ? intval($_POST['profile_uid']) : 0);
require_once('include/channel.php');
$sys = get_sys_channel();
@@ -130,25 +131,25 @@ class Item extends Controller {
$observer = $sys;
}
if (x($_REQUEST, 'dropitems')) {
if (!empty($_POST['dropitems'])) {
require_once('include/items.php');
$arr_drop = explode(',', $_REQUEST['dropitems']);
$arr_drop = explode(',', $_POST['dropitems']);
drop_items($arr_drop);
$json = ['success' => 1];
echo json_encode($json);
killme();
}
call_hooks('post_local_start', $_REQUEST);
call_hooks('post_local_start', $_POST);
// logger('postvars ' . print_r($_REQUEST,true), LOGGER_DATA);
// logger('postvars ' . print_r($_POST,true), LOGGER_DATA);
$api_source = ((x($_REQUEST, 'api_source') && $_REQUEST['api_source']) ? true : false);
$api_source = ((!empty($_POST['api_source'])) ? true : false);
$consensus = $_REQUEST['consensus'] ?? 0;
$nocomment = $_REQUEST['nocomment'] ?? 0;
$consensus = $_POST['consensus'] ?? 0;
$nocomment = $_POST['nocomment'] ?? 0;
$is_poll = ((isset($_REQUEST['poll_answers'][0]) && $_REQUEST['poll_answers'][0]) && (isset($_REQUEST['poll_answers'][1]) && $_REQUEST['poll_answers'][1]));
$is_poll = ((isset($_POST['poll_answers'][0]) && $_POST['poll_answers'][0]) && (isset($_POST['poll_answers'][1]) && $_POST['poll_answers'][1]));
// 'origin' (if non-zero) indicates that this network is where the message originated,
// for the purpose of relaying comments to other conversation members.
@@ -159,43 +160,43 @@ class Item extends Controller {
// If you are unsure, it is prudent (and important) to leave it unset.
$origin = (($api_source && array_key_exists('origin', $_REQUEST)) ? intval($_REQUEST['origin']) : 1);
$origin = (($api_source && array_key_exists('origin', $_POST)) ? intval($_REQU_POSTEST['origin']) : 1);
// To represent message-ids on other networks - this will create an iconfig record
$namespace = (($api_source && array_key_exists('namespace', $_REQUEST)) ? strip_tags($_REQUEST['namespace']) : '');
$remote_id = (($api_source && array_key_exists('remote_id', $_REQUEST)) ? strip_tags($_REQUEST['remote_id']) : '');
$namespace = (($api_source && array_key_exists('namespace', $_POST)) ? strip_tags($_POST['namespace']) : '');
$remote_id = (($api_source && array_key_exists('remote_id', $_POST)) ? strip_tags($_POST['remote_id']) : '');
$owner_hash = null;
$message_id = ((x($_REQUEST, 'message_id') && $api_source) ? strip_tags($_REQUEST['message_id']) : null);
$created = ((x($_REQUEST, 'created')) ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['created']) : datetime_convert());
$post_id = ((x($_REQUEST, 'post_id')) ? intval($_REQUEST['post_id']) : 0);
$app = ((x($_REQUEST, 'source')) ? strip_tags($_REQUEST['source']) : '');
$return_path = ((x($_REQUEST, 'return')) ? $_REQUEST['return'] : '');
$preview = ((x($_REQUEST, 'preview')) ? intval($_REQUEST['preview']) : 0);
$categories = ((x($_REQUEST, 'category')) ? escape_tags($_REQUEST['category']) : '');
$webpage = ((x($_REQUEST, 'webpage')) ? intval($_REQUEST['webpage']) : 0);
$item_obscured = ((x($_REQUEST, 'obscured')) ? intval($_REQUEST['obscured']) : 0);
$item_delayed = ((x($_REQUEST, 'delayed')) ? intval($_REQUEST['delayed']) : 0);
$pagetitle = ((x($_REQUEST, 'pagetitle')) ? escape_tags($_REQUEST['pagetitle']) : '');
$layout_mid = ((x($_REQUEST, 'layout_mid')) ? escape_tags($_REQUEST['layout_mid']) : '');
$plink = ((x($_REQUEST, 'permalink')) ? escape_tags($_REQUEST['permalink']) : '');
$obj_type = ((x($_REQUEST, 'obj_type')) ? escape_tags($_REQUEST['obj_type']) : 'Note');
$message_id = ((!empty($_POST['message_id']) && $api_source) ? strip_tags($_POST['message_id']) : null);
$created = ((!empty($_POST['created'])) ? datetime_convert(date_default_timezone_get(), 'UTC', $_POST['created']) : datetime_convert());
$post_id = ((!empty($_POST['post_id'])) ? intval($_POST['post_id']) : 0);
$app = ((!empty($_POST['source'])) ? strip_tags($_POST['source']) : '');
$return_path = ((!empty($_POST['return'])) ? $_POST['return'] : '');
$preview = ((!empty($_POST['preview'])) ? intval($_POST['preview']) : 0);
$categories = ((!empty($_POST['category'])) ? escape_tags($_POST['category']) : '');
$item_type = ((!empty($_POST['webpage'])) ? intval($_POST['webpage']) : ITEM_TYPE_POST);
$item_obscured = ((!empty($_POST['obscured'])) ? intval($_POST['obscured']) : 0);
$item_delayed = ((!empty($_POST['delayed'])) ? intval($_POST['delayed']) : 0);
$pagetitle = ((!empty($_POST['pagetitle'])) ? escape_tags($_POST['pagetitle']) : '');
$layout_mid = ((!empty($_POST['layout_mid'])) ? escape_tags($_POST['layout_mid']) : '');
$plink = ((!empty($_POST['permalink'])) ? escape_tags($_POST['permalink']) : null);
$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 = ((x($_REQUEST, 'nopush')) ? intval($_REQUEST['nopush']) : 0);
$nopush = ((!empty($_POST['nopush'])) ? intval($_POST['nopush']) : 0);
/*
* Check service class limits
*/
if ($uid && !(x($_REQUEST, 'parent')) && !(x($_REQUEST, 'post_id'))) {
$ret = $this->item_check_service_class($uid, (($_REQUEST['webpage'] == ITEM_TYPE_WEBPAGE) ? true : false));
if ($uid && empty($_POST['parent']) && empty($_POST['post_id'])) {
$ret = $this->item_check_service_class($uid, (($_POST['webpage'] == ITEM_TYPE_WEBPAGE) ? true : false));
if (!$ret['success']) {
notice(t($ret['message']) . EOL);
if ($api_source)
return (['success' => false, 'message' => 'service class exception']);
if (x($_REQUEST, 'return'))
if (!empty($_POST['return']))
goaway(z_root() . "/" . $return_path);
killme();
}
@@ -216,8 +217,8 @@ class Item extends Controller {
if ($parent || $parent_mid) {
if (!x($_REQUEST, 'type'))
$_REQUEST['type'] = 'net-comment';
if (empty($_POST['type']))
$_POST['type'] = 'net-comment';
if ($parent) {
$r = q("SELECT * FROM item WHERE id = %d LIMIT 1",
@@ -253,7 +254,7 @@ class Item extends Controller {
notice(t('Unable to locate original post.') . EOL);
if ($api_source)
return (['success' => false, 'message' => 'invalid post id']);
if (x($_REQUEST, 'return'))
if (!empty($_POST['return']))
goaway(z_root() . "/" . $return_path);
killme();
}
@@ -276,7 +277,7 @@ class Item extends Controller {
if (!$observer) {
$observer = App::get_observer();
if (!$observer) {
$observer = anon_identity_init($_REQUEST);
$observer = anon_identity_init($_POST);
if ($observer) {
$moderated = true;
$remote_xchan = $remote_observer = $observer;
@@ -288,7 +289,7 @@ class Item extends Controller {
notice(t('Permission denied.') . EOL);
if ($api_source)
return (['success' => false, 'message' => 'permission denied']);
if (x($_REQUEST, 'return'))
if (!empty($_POST['return']))
goaway(z_root() . "/" . $return_path);
killme();
}
@@ -307,17 +308,17 @@ class Item extends Controller {
notice(t('Permission denied.') . EOL);
if ($api_source)
return (['success' => false, 'message' => 'permission denied']);
if (x($_REQUEST, 'return'))
if (!empty($_POST['return']))
goaway(z_root() . "/" . $return_path);
killme();
}
}
else {
if (!perm_is_allowed($profile_uid, $observer['xchan_hash'], ($webpage) ? 'write_pages' : 'post_wall')) {
if (!perm_is_allowed($profile_uid, $observer['xchan_hash'], (intval($item_type) === ITEM_TYPE_POST) ? 'post_wall' : 'write_pages')) {
notice(t('Permission denied.') . EOL);
if ($api_source)
return (['success' => false, 'message' => 'permission denied']);
if (x($_REQUEST, 'return'))
if (!empty($_POST['return']))
goaway(z_root() . "/" . $return_path);
killme();
}
@@ -373,7 +374,7 @@ class Item extends Controller {
logger("mod_item: no channel.");
if ($api_source)
return (['success' => false, 'message' => 'no channel']);
if (x($_REQUEST, 'return'))
if (!empty($_POST['return']))
goaway(z_root() . "/" . $return_path);
killme();
}
@@ -391,7 +392,7 @@ class Item extends Controller {
logger("mod_item: no owner.");
if ($api_source)
return (['success' => false, 'message' => 'no owner']);
if (x($_REQUEST, 'return'))
if (!empty($_POST['return']))
goaway(z_root() . "/" . $return_path);
killme();
}
@@ -425,17 +426,21 @@ class Item extends Controller {
$view_policy = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'], 'view_stream');
$comment_policy = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'], 'post_comments');
$public_policy = ((x($_REQUEST, 'public_policy')) ? escape_tags($_REQUEST['public_policy']) : map_scope($view_policy, true));
if ($webpage)
$public_policy = '';
if ($public_policy)
$public_policy = '';
if (intval($item_type) === ITEM_TYPE_POST) {
$public_policy = ((!empty($_POST['public_policy'])) ? escape_tags($_POST['public_policy']) : map_scope($view_policy, true));
}
if ($public_policy) {
$private = 1;
}
if ($orig_post) {
$private = 0;
// webpages are allowed to change ACLs after the fact. Normal conversation items aren't.
if ($webpage) {
$acl->set_from_array($_REQUEST);
// Normal conversation items are not allowed to change ACL.
if (intval($item_type) !== ITEM_TYPE_POST) {
$acl->set_from_array($_POST);
}
else {
$acl->set($orig_post);
@@ -451,9 +456,9 @@ class Item extends Controller {
$coord = $orig_post['coord'];
$verb = $orig_post['verb'];
$app = $orig_post['app'];
$title = escape_tags(trim($_REQUEST['title']));
$summary = escape_tags(trim($_REQUEST['summary']));
$body = trim($_REQUEST['body']);
$title = escape_tags(trim($_POST['title']));
$summary = escape_tags(trim($_POST['summary']));
$body = trim($_POST['body']);
$item_flags = $orig_post['item_flags'];
$item_origin = $orig_post['item_origin'];
$item_unseen = $orig_post['item_unseen'];
@@ -491,11 +496,11 @@ class Item extends Controller {
}
else {
if (!$walltowall) {
if ((array_key_exists('contact_allow', $_REQUEST))
|| (array_key_exists('group_allow', $_REQUEST))
|| (array_key_exists('contact_deny', $_REQUEST))
|| (array_key_exists('group_deny', $_REQUEST))) {
$acl->set_from_array($_REQUEST);
if ((array_key_exists('contact_allow', $_POST))
|| (array_key_exists('group_allow', $_POST))
|| (array_key_exists('contact_deny', $_POST))
|| (array_key_exists('group_deny', $_POST))) {
$acl->set_from_array($_POST);
}
elseif (!$api_source) {
@@ -510,16 +515,16 @@ class Item extends Controller {
}
$location = ((isset($_REQUEST['location'])) ? notags(trim($_REQUEST['location'])) : '');
$coord = ((isset($_REQUEST['coord'])) ? notags(trim($_REQUEST['coord'])) : '');
$verb = ((isset($_REQUEST['verb'])) ? notags(trim($_REQUEST['verb'])) : '');
$title = ((isset($_REQUEST['title'])) ? escape_tags(trim($_REQUEST['title'])) : '');
$summary = ((isset($_REQUEST['summary'])) ? escape_tags(trim($_REQUEST['summary'])) : '');
$body = ((isset($_REQUEST['body'])) ? trim($_REQUEST['body']) : '');
$body .= ((isset($_REQUEST['attachment'])) ? trim($_REQUEST['attachment']) : '');
$location = ((isset($_POST['location'])) ? notags(trim($_POST['location'])) : '');
$coord = ((isset($_POST['coord'])) ? notags(trim($_POST['coord'])) : '');
$verb = ((isset($_POST['verb'])) ? notags(trim($_POST['verb'])) : '');
$title = ((isset($_POST['title'])) ? escape_tags(trim($_POST['title'])) : '');
$summary = ((isset($_POST['summary'])) ? escape_tags(trim($_POST['summary'])) : '');
$body = ((isset($_POST['body'])) ? trim($_POST['body']) : '');
$body .= ((isset($_POST['attachment'])) ? trim($_POST['attachment']) : '');
$postopts = '';
$allow_empty = ((array_key_exists('allow_empty', $_REQUEST)) ? intval($_REQUEST['allow_empty']) : 0);
$allow_empty = ((array_key_exists('allow_empty', $_POST)) ? intval($_POST['allow_empty']) : 0);
$private = ((isset($private) && $private) ? $private : intval($acl->is_private() || ($public_policy)));
@@ -530,7 +535,7 @@ class Item extends Controller {
$private = intval($parent_item['item_private']);
$public_policy = $parent_item['public_policy'];
$owner_hash = $parent_item['owner_xchan'];
$webpage = $parent_item['item_type'];
$item_type = $parent_item['item_type'];
}
@@ -541,7 +546,7 @@ class Item extends Controller {
info(t('Empty post discarded.') . EOL);
if ($api_source)
return (['success' => false, 'message' => 'no content']);
if (x($_REQUEST, 'return'))
if (!empty($_POST['return']))
goaway(z_root() . "/" . $return_path);
killme();
}
@@ -549,15 +554,15 @@ class Item extends Controller {
if (feature_enabled($profile_uid, 'content_expire')) {
if (x($_REQUEST, 'expire')) {
$expires = datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['expire']);
if (!empty($_POST['expire'])) {
$expires = datetime_convert(date_default_timezone_get(), 'UTC', $_POST['expire']);
if ($expires <= datetime_convert())
$expires = NULL_DATE;
}
}
$mimetype = ((isset($_REQUEST['mimetype'])) ? notags(trim($_REQUEST['mimetype'])) : '');
$mimetype = ((isset($_POST['mimetype'])) ? notags(trim($_POST['mimetype'])) : '');
if (!$mimetype)
$mimetype = 'text/bbcode';
@@ -591,7 +596,7 @@ class Item extends Controller {
$is_group = get_pconfig($profile_uid, 'system', 'group_actor');
if ($is_group && $walltowall && !$walltowall_comment && !$webpage) {
if ($is_group && $walltowall && !$walltowall_comment && (intval($item_type) === ITEM_TYPE_POST)) {
$groupww = true;
$str_contact_allow = $owner_xchan['xchan_hash'];
$str_group_allow = '';
@@ -790,7 +795,7 @@ class Item extends Controller {
}
$item_unseen = ((local_channel() != $profile_uid) ? 1 : 0);
$item_wall = ((isset($_REQUEST['type']) && ($_REQUEST['type'] === 'wall' || $_REQUEST['type'] === 'wall-comment')) ? 1 : 0);
$item_wall = ((isset($_POST['type']) && ($_POST['type'] === 'wall' || $_POST['type'] === 'wall-comment')) ? 1 : 0);
$item_origin = (($origin) ? 1 : 0);
$item_consensus = (($consensus) ? 1 : 0);
$item_nocomment = (($nocomment) ? 1 : 0);
@@ -798,15 +803,13 @@ class Item extends Controller {
// determine if this is a wall post
if (in_array($item_type, [ITEM_TYPE_POST, ITEM_TYPE_CARD, ITEM_TYPE_ARTICLE])) {
$item_wall = 1;
}
if ($parent) {
$item_wall = $parent_item['item_wall'];
}
else {
if (!$webpage) {
$item_wall = 1;
}
}
if ($moderated) {
$item_blocked = ITEM_MODERATED;
@@ -847,10 +850,10 @@ class Item extends Controller {
if ($is_poll) {
$poll = [
'question' => $body,
'answers' => $_REQUEST['poll_answers'],
'multiple_answers' => $_REQUEST['poll_multiple_answers'],
'expire_value' => $_REQUEST['poll_expire_value'],
'expire_unit' => $_REQUEST['poll_expire_unit']
'answers' => $_POST['poll_answers'],
'multiple_answers' => $_POST['poll_multiple_answers'],
'expire_value' => $_POST['poll_expire_value'],
'expire_unit' => $_POST['poll_expire_unit']
];
$obj = $this->extract_poll_data($poll, ['item_private' => $private, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_contact_deny]);
}
@@ -929,7 +932,7 @@ class Item extends Controller {
$datarray['item_unseen'] = intval($item_unseen);
$datarray['item_wall'] = intval($item_wall);
$datarray['item_origin'] = intval($item_origin);
$datarray['item_type'] = $webpage;
$datarray['item_type'] = $item_type;
$datarray['item_private'] = intval($private);
$datarray['item_thread_top'] = intval($item_thread_top);
$datarray['item_starred'] = intval($item_starred);
@@ -1008,14 +1011,14 @@ class Item extends Controller {
call_hooks('post_local', $datarray);
if (x($datarray, 'cancel')) {
if (!empty($datarray['cancel'])) {
logger('mod_item: post cancelled by plugin or duplicate suppressed.');
if ($return_path)
goaway(z_root() . "/" . $return_path);
if ($api_source)
return (['success' => false, 'message' => 'operation cancelled']);
$json = ['cancel' => 1];
$json['reload'] = z_root() . '/' . $_REQUEST['jsreload'];
$json['reload'] = z_root() . '/' . $_POST['jsreload'];
echo json_encode($json);
killme();
}
@@ -1024,8 +1027,8 @@ class Item extends Controller {
if (mb_strlen($datarray['title']) > 191)
$datarray['title'] = mb_substr($datarray['title'], 0, 191);
if ($webpage) {
IConfig::Set($datarray, 'system', webpage_to_namespace($webpage),
if (intval($item_type) !== ITEM_TYPE_POST) {
IConfig::Set($datarray, 'system', item_type_to_namespace($item_type),
(($pagetitle) ? $pagetitle : basename($datarray['mid'])), true);
}
elseif ($namespace) {
@@ -1065,7 +1068,7 @@ class Item extends Controller {
if ($api_source)
return ($x);
if ((x($_REQUEST, 'return')) && strlen($return_path)) {
if ((!empty($_POST['return'])) && strlen($return_path)) {
logger('return: ' . $return_path);
if ($return_path === 'hq') {
@@ -1218,11 +1221,12 @@ class Item extends Controller {
$json = [
'success' => 1,
'id' => $post_id,
'thr_parent_id' => $thr_parent_id,
'html' => conversation($item, $mode, true, 'r_preview'),
];
if (x($_REQUEST, 'jsreload') && strlen($_REQUEST['jsreload']))
$json['reload'] = z_root() . '/' . $_REQUEST['jsreload'];
if (!empty($_POST['jsreload']))
$json['reload'] = z_root() . '/' . $_POST['jsreload'];
logger('post_json: ' . print_r($json, true), LOGGER_DEBUG);

View File

@@ -65,8 +65,22 @@ class Lang extends Controller {
}
nav_set_selected('Language');
return lang_selector();
return $this->lang_selector();
}
private function lang_selector(): string
{
$lang_options = language_list();
array_unshift($lang_options, t('default'));
$tpl = get_markup_template('lang_selector.tpl');
return replace_macros($tpl, [
'$title' => t('Select an alternate language'),
'$langs' => array($lang_options, App::$language),
]);
}
}

View File

@@ -22,9 +22,9 @@ class Like extends Controller {
'like' => 'Like',
'dislike' => 'Dislike',
'announce' => ACTIVITY_SHARE,
'attendyes' => 'Accept',
'attendno' => 'Reject',
'attendmaybe' => 'TentativeAccept'
'accept' => 'Accept',
'reject' => 'Reject',
'tentativeaccept' => 'TentativeAccept'
];
// unlike (etc.) reactions are an undo of positive reactions, rather than a negative action.
@@ -52,43 +52,31 @@ class Like extends Controller {
profile_load($parts[0]);
}
$item_normal = item_normal();
if ($page_mode === 'list') {
$item_normal = item_normal();
$items = q("SELECT item.*, item.id AS item_id FROM item
WHERE uid = %d $item_normal
AND parent = %d",
intval($arr['item']['uid']),
intval($arr['item']['parent'])
);
xchan_query($items, true);
$items = fetch_post_tags($items, true);
$items = conv_sort($items, 'commented');
}
else {
$activities = q("SELECT item.*, item.id AS item_id FROM item
WHERE uid = %d $item_normal
AND thr_parent = '%s'
AND verb IN ('%s', '%s', '%s', '%s', '%s', '%s', 'Accept', 'Reject', 'TentativeAccept')",
intval($arr['item']['uid']),
dbesc($arr['item']['mid']),
dbesc('Like'),
dbesc('Dislike'),
dbesc(ACTIVITY_SHARE),
dbesc(ACTIVITY_ATTEND),
dbesc(ACTIVITY_ATTENDNO),
dbesc(ACTIVITY_ATTENDMAYBE)
);
xchan_query($activities, true);
$items = array_merge([$arr['item']], $activities);
$items = fetch_post_tags($items, true);
$item = item_by_item_id($arr['item']['id'], $arr['item']['parent'], type: $arr['item']['item_type']);
xchan_query($item, true);
$item = fetch_post_tags($item, true);
}
$ret = [
'success' => 1,
'orig_id' => $arr['orig_item_id'], //this is required for pubstream items where $item_id != $item['id']
'id' => $arr['item']['id'],
'html' => conversation($items, $conv_mode, true, $page_mode),
'html' => conversation($item, $conv_mode, true, $page_mode),
];
// mod photos
@@ -139,7 +127,7 @@ class Like extends Controller {
$extended_like = false;
$object = $target = null;
$post_type = EMPTY_STR;
$obj_type = EMPTY_STR;
$obj_type = EMPTY_STR;
if (argc() == 3) {
@@ -317,8 +305,6 @@ class Like extends Controller {
// parent, copy that as well.
if ($r) {
$obj_type = $r[0]['obj_type'];
if ($r[0]['uid'] === $sys_channel['channel_id'] && local_channel()) {
$r = [copy_of_pubitem(App::get_channel(), $r[0]['mid'])];
}
@@ -334,6 +320,8 @@ class Like extends Controller {
$item = $r[0];
$owner_uid = $r[0]['uid'];
$owner_aid = $r[0]['aid'];
$obj_type = $r[0]['obj_type'];
$item_type = $r[0]['item_type'];
if ((array_key_exists('owner', $item)) && intval($item['owner']['abook_self']))
$can_comment = perm_is_allowed($item['uid'], $observer['xchan_hash'], 'post_comments');
@@ -374,7 +362,7 @@ class Like extends Controller {
$multi_undo = true;
}
$item_normal = item_normal();
$item_normal = item_normal(type: $item_type);
$r = q("SELECT id, parent, uid, verb FROM item WHERE verb in ( $verbs ) $item_normal
AND author_xchan = '%s' AND thr_parent = '%s' and uid = %d ",
@@ -445,7 +433,7 @@ class Like extends Controller {
$arr['item_wall'] = 1;
}
else {
switch ($item['object_type']) {
switch ($item['obj_type']) {
case 'Image':
$post_type = t('image');
break;
@@ -486,11 +474,11 @@ class Like extends Controller {
$bodyverb = t('%1$s likes %2$s\'s %3$s');
if ($verb === 'dislike')
$bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s');
if ($verb === 'attendyes')
if ($verb === 'accept')
$bodyverb = t('%1$s is attending %2$s\'s %3$s');
if ($verb === 'attendno')
if ($verb === 'reject')
$bodyverb = t('%1$s is not attending %2$s\'s %3$s');
if ($verb === 'attendmaybe')
if ($verb === 'tentativeaccept')
$bodyverb = t('%1$s may attend %2$s\'s %3$s');
if (!isset($bodyverb))
@@ -573,7 +561,7 @@ class Like extends Controller {
call_hooks('post_local_end', $arr);
if ($is_rsvp && in_array($verb, ['attendyes', 'attendmaybe'])) {
if ($is_rsvp && in_array($verb, ['accept', 'tentativeaccept'])) {
event_addtocal($item_id, local_channel());
}

View File

@@ -110,7 +110,7 @@ class Lockview extends Controller {
// as unknown specific recipients. The sender will have the visibility list and will fall through to the
// next section.
echo '<div class="dropdown-item-text">' . translate_scope((!$item['public_policy']) ? 'specific' : $item['public_policy']) . '</div>';
echo '<div class="dropdown-item-text">' . escape_tags(translate_scope((!$item['public_policy']) ? 'specific' : $item['public_policy'])) . '</div>';
killme();
}

View File

@@ -5,10 +5,17 @@ namespace Zotlabs\Module;
class Login extends \Zotlabs\Web\Controller {
function get() {
if(local_channel())
if (local_channel()) {
goaway(z_root());
if(remote_channel() && $_SESSION['atoken'])
}
if (remote_channel() && $_SESSION['atoken']) {
goaway(z_root());
}
if (!empty($_GET['retry'])) {
notice( t('Login failed.') . EOL );
}
$o = '<div class="generic-content-wrapper">';
$o .= '<div class="section-title-wrapper">';

View File

@@ -41,11 +41,7 @@ class Magic extends Controller {
http_status_exit(400, 'Bad Request');
}
$basepath = unparse_url(array_filter(
$parsed,
fn (string $key) => in_array($key, ['scheme', 'host', 'port']),
ARRAY_FILTER_USE_KEY
));
$basepath = unparse_url($parsed, ['scheme', 'host', 'port']);
$owapath = SConfig::get($basepath, 'system', 'openwebauth', $basepath . '/owa');
@@ -142,12 +138,14 @@ class Magic extends Controller {
$o .= '<a href=' . $dest . '>' . $dest . '</a>';
echo $o;
killme();
}
}
}
}
killme();
goaway($dest);
}

View File

@@ -67,7 +67,7 @@ class Moderate extends \Zotlabs\Web\Controller {
$item['item_blocked'] = 0;
item_update_parent_commented($item);
notice( t('Item approved') . EOL);
info(t('Item approved') . EOL);
}
elseif($action === 'drop') {
// TODO: not implemented
@@ -75,7 +75,7 @@ class Moderate extends \Zotlabs\Web\Controller {
// Activity::send_rejection_activity(App::get_channel(), $item['author_xchan'], $item);
drop_item($post_id);
notice( t('Item deleted') . EOL);
info(t('Item deleted') . EOL);
}
// refetch the item after changes have been made

View File

@@ -70,17 +70,19 @@ class Network extends \Zotlabs\Web\Controller {
$dm = ((x($_REQUEST,'dm')) ? $_REQUEST['dm'] : 0);
$order = get_pconfig(local_channel(), 'mod_network', 'order', 0);
$order = get_pconfig(local_channel(), 'mod_network', 'order', 'created');
switch($order) {
case 0:
$order = 'comment';
case 'commented':
$ordering = 'commented';
break;
case 1:
$order = 'post';
case 'created':
$ordering = 'created';
break;
case 2:
case 'unthreaded':
$nouveau = true;
break;
default:
$ordering = 'created';
}
$search = $_GET['search'] ?? '';
@@ -92,7 +94,7 @@ class Network extends \Zotlabs\Web\Controller {
}
if($datequery)
$order = 'post';
$order = 'created';
// filter by collection (e.g. group)
@@ -133,7 +135,6 @@ class Network extends \Zotlabs\Web\Controller {
$status_editor = '';
if (Apps::system_app_installed(local_channel(), 'Affinity Tool')) {
$affinity_locked = intval(get_pconfig(local_channel(), 'affinity', 'lock', 1));
if ($affinity_locked) {
@@ -201,7 +202,7 @@ class Network extends \Zotlabs\Web\Controller {
'default_location' => $channel['channel_location'],
'nickname' => $channel['channel_address'],
'lockstate' => (($private_editing || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
'acl' => populate_acl((($private_editing) ? $def_acl : $channel_acl), true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'),
'acl' => populate_acl((($private_editing) ? $def_acl : $channel_acl), true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions'),
'permissions' => (($private_editing) ? $def_acl : $channel_acl),
'bang' => (($private_editing) ? $bang : ''),
'visitor' => true,
@@ -271,15 +272,6 @@ class Network extends \Zotlabs\Web\Controller {
// This is for nouveau view cid queries (not a public forum)
$sql_extra = " AND author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' ";
}
elseif($pf && $unseen && $nouveau) {
$vnotify = get_pconfig(local_channel(), 'system', 'vnotify');
if(! ($vnotify & VNOTIFY_LIKE))
$likes_sql = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
// This is for nouveau view public forum cid queries (if a forum notification is clicked)
$sql_extra = " AND item.parent IN (SELECT DISTINCT parent FROM item WHERE uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' OR owner_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' ) $item_normal) AND item_unseen = 1 AND verb != 'Announce' $likes_sql ";
}
else {
// This is for threaded view cid queries (e.g. if a forum is selected from the forum filter)
$sql_extra = " AND item.parent IN (SELECT DISTINCT parent FROM item WHERE uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' OR owner_xchan = '" . dbesc($cid_r[0]['abook_xchan']) . "' ) $item_normal) ";
@@ -372,10 +364,10 @@ class Network extends \Zotlabs\Web\Controller {
}
if ($dm) {
$sql_extra .= ' AND item_private = 2 ';
$sql_extra .= ' AND item.item_private = 2 ';
}
else {
$sql_extra .= ' AND item_private IN (0, 1) ';
$sql_extra .= ' AND item.item_private IN (0, 1) ';
}
@@ -384,17 +376,9 @@ class Network extends \Zotlabs\Web\Controller {
$sql_extra .= " AND ( author_xchan = '" . dbesc($channel['channel_hash']) . "' OR item_mentionsme = 1 ) ";
}
if($update && ! $load) {
// only setup pagination on initial page view
$pager_sql = '';
}
else {
$itemspage = get_pconfig(local_channel(), 'system', 'itemspage');
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10));
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start']));
}
$itemspage = get_pconfig(local_channel(), 'system', 'itemspage');
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 10));
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(App::$pager['itemspage']), intval(App::$pager['start']));
// cmin and cmax are both -1 when the affinity tool is disabled
@@ -424,12 +408,12 @@ class Network extends \Zotlabs\Web\Controller {
$abook_uids = ' and abook.abook_channel = ' . local_channel() . ' ';
$uids = ' and item.uid = ' . local_channel() . ' ';
if(feature_enabled(local_channel(), 'network_list_mode'))
$page_mode = 'list';
else
$page_mode = 'client';
$page_mode = 'client';
$parents_str = '';
$blog_mode = feature_enabled(local_channel(), 'network_list_mode');
if ($blog_mode) {
$page_mode = 'list';
}
// This fixes a very subtle bug so I'd better explain it. You wake up in the morning or return after a day
// or three and look at your matrix page - after opening up your browser. The first page loads just as it
@@ -451,33 +435,23 @@ class Network extends \Zotlabs\Web\Controller {
if($nouveau && $load) {
// "New Item View" - show all items unthreaded in reverse created date order
$items = dbq("SELECT item.*, item.id AS item_id, created FROM item
$items = dbq("SELECT item.*, item.id AS item_id FROM item
left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids )
$net_query
WHERE true $uids $item_normal
and (abook.abook_blocked = 0 or abook.abook_flags is null)
AND item.verb NOT IN ('Add', 'Remove')
$sql_extra $sql_options $sql_nets
$net_query2
ORDER BY item.created DESC $pager_sql "
);
$parents_str = ids_to_querystr($items, 'item_id');
require_once('include/items.php');
xchan_query($items);
$items = fetch_post_tags($items, true);
}
elseif($update) {
// Normal conversation view
if($order === 'post')
$ordering = 'created';
else
$ordering = 'commented';
if($load) {
// Fetch a page full of parent items for this page
$r = dbq("SELECT item.parent AS item_id FROM item
@@ -506,12 +480,7 @@ class Network extends \Zotlabs\Web\Controller {
// Then fetch all the children of the parents that are on this page
if($r) {
$parents_str = ids_to_querystr($r, 'item_id');
$items = dbq("SELECT item.*, item.id AS item_id FROM item
WHERE true $uids $item_normal
AND item.parent IN ( $parents_str )
$sql_extra "
);
$items = items_by_parent_ids($r, blog_mode: $blog_mode);
xchan_query($items, true);
$items = fetch_post_tags($items, true);

View File

@@ -12,51 +12,72 @@ class Notifications extends \Zotlabs\Web\Controller {
}
// ajax mark all unseen items read
if(x($_REQUEST, 'markRead')) {
if(isset($_REQUEST['markRead'])) {
if (str_starts_with($_REQUEST['markRead'], 'forum_')) {
$forum_id = substr($_REQUEST['markRead'], 6);
$abook = q("SELECT abook_xchan FROM abook WHERE abook_channel = %d AND abook_id = %d",
intval(local_channel()),
intval($forum_id)
);
if ($abook) {
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND owner_xchan = '%s' AND item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1)",
intval(local_channel()),
dbesc($abook[0]['abook_xchan'])
);
}
killme();
}
switch($_REQUEST['markRead']) {
case 'dm':
$r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_private = 2",
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_private = 2",
intval(local_channel())
);
break;
case 'network':
$r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_private IN (0, 1)",
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1)",
intval(local_channel())
);
break;
case 'home':
$r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1)",
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1)",
intval(local_channel())
);
break;
case 'all_events':
$evdays = intval(get_pconfig(local_channel(), 'system', 'evdays', 3));
$r = q("UPDATE event SET dismissed = 1 WHERE uid = %d AND dismissed = 0 AND dtstart < '%s' AND dtstart > '%s' ",
q("UPDATE event SET dismissed = 1 WHERE uid = %d AND dismissed = 0 AND dtstart < '%s' AND dtstart > '%s' ",
intval(local_channel()),
dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')),
dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days'))
);
break;
case 'notify':
$r = q("UPDATE notify SET seen = 1 WHERE seen = 0 AND uid = %d",
q("UPDATE notify SET seen = 1 WHERE seen = 0 AND uid = %d",
intval(local_channel())
);
break;
case 'pubs':
unset($_SESSION['static_loadtime']);
$_SESSION['sse_loadtime'] = datetime_convert();
$_SESSION['static_loadtime'] = datetime_convert();
break;
default:
break;
}
killme();
}
// ajax mark all comments of a parent item read
if(x($_REQUEST, 'markItemRead') && local_channel()) {
if(isset($_REQUEST['markItemRead']) && local_channel()) {
$r = q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND parent = %d",
intval(local_channel()),
intval($_REQUEST['markItemRead'])
);
killme();
}

View File

@@ -23,6 +23,7 @@ class Oep extends \Zotlabs\Web\Controller {
if(! $url)
http_status_exit(404, 'Not found');
$arr = [];
$maxwidth = $_REQUEST['maxwidth'] ?? 0;
$maxheight = $_REQUEST['maxheight'] ?? 0;
$format = $_REQUEST['format'] ?? '';

View File

@@ -557,7 +557,9 @@ class Photos extends \Zotlabs\Web\Controller {
$can_post = false;
$visitor = 0;
$link_item = null;
$like = null;
$dislike = null;
$owner_uid = \App::$data['channel']['channel_id'];
$owner_aid = \App::$data['channel']['channel_account_id'];
@@ -965,7 +967,6 @@ class Photos extends \Zotlabs\Web\Controller {
$map = null;
if($linked_items) {
xchan_query($linked_items);
$linked_items = fetch_post_tags($linked_items,true);
@@ -1103,20 +1104,8 @@ class Photos extends \Zotlabs\Web\Controller {
$alike = array();
$dlike = array();
$like = '';
$dislike = '';
$conv_responses = array(
'like' => array('title' => t('Likes','title')),'dislike' => array('title' => t('Dislikes','title')),
'attendyes' => array('title' => t('Attending','title')), 'attendno' => array('title' => t('Not attending','title')), 'attendmaybe' => array('title' => t('Might attend','title'))
);
if($r) {
foreach($r as $item) {
builtin_activity_puller($item, $conv_responses);
}
$like_count = ((x($alike,$link_item['mid'])) ? $alike[$link_item['mid']] : '');
$like_list = ((x($alike,$link_item['mid'])) ? $alike[$link_item['mid'] . '-l'] : '');
@@ -1217,12 +1206,17 @@ class Photos extends \Zotlabs\Web\Controller {
$like_e = $like;
$dislike_e = $dislike;
$paginate = paginate();
$responses = [];
$response_verbs = array('like');
if(feature_enabled($owner_uid,'dislike'))
$response_verbs[] = 'dislike';
if ($link_item) {
$response_verbs = ['like'];
$responses = get_responses($conv_responses,$response_verbs,'',$link_item);
if(feature_enabled($owner_uid,'dislike')) {
$response_verbs[] = 'dislike';
}
$responses = get_responses($response_verbs, $link_item);
}
$hookdata = [
'onclick' => '$.colorbox({href: \'' . $photo['href'] . '\'}); return false;',

View File

@@ -29,8 +29,9 @@ class Pin extends \Zotlabs\Web\Controller {
if(! $observer)
http_status_exit(403, 'Forbidden');
$r = q("SELECT * FROM item WHERE id = %d AND id = parent AND item_private = 0 LIMIT 1",
$item_id
$r = q("SELECT * FROM item WHERE id = %d AND uid = %d AND id = parent AND item_private = 0 LIMIT 1",
intval($item_id),
intval(local_channel())
);
if(! $r) {
notice(t('Unable to locate original post.'));

View File

@@ -759,7 +759,7 @@ class Profiles extends \Zotlabs\Web\Controller {
'$profile_id' => $r[0]['id'],
'$profile_name' => array('profile_name', t('Profile name'), $r[0]['profile_name'], t('Required'), '*'),
'$is_default' => $is_default,
'$default' => t('This is your default profile.') . EOL . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))),
'$default' => t('This is your default profile.') . ' ' . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))),
'$advanced' => $advanced,
'$name' => array('name', t('Your full name'), $r[0]['fullname'], t('Required'), '*'),
'$pdesc' => array('pdesc', t('Short title/description'), $r[0]['pdesc'], t('Maximal 190 characters'), '', 'maxlength="190"'),
@@ -841,7 +841,7 @@ class Profiles extends \Zotlabs\Web\Controller {
'$alt' => t('Profile Image'),
'$profile_name' => $rr['profile_name'],
'$visible' => (($rr['is_default'])
? '<strong>' . translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile'))) . '</strong>'
? '<strong>' . escape_tags(translate_scope(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_profile')))) . '</strong>'
: '<a href="' . z_root() . '/profperm/' . $rr['id'] . '" />' . t('Edit visibility') . '</a>')
));
}

View File

@@ -84,7 +84,7 @@ class Pubstream extends \Zotlabs\Web\Controller {
'default_location' => $channel['channel_location'],
'nickname' => $channel['channel_address'],
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'),
'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions'),
'permissions' => $channel_acl,
'bang' => '',
'visitor' => true,
@@ -137,7 +137,7 @@ class Pubstream extends \Zotlabs\Web\Controller {
'$page' => ((\App::$pager['page'] != 1) ? \App::$pager['page'] : 1),
'$search' => '',
'$xchan' => '',
'$order' => 'comment',
'$order' => 'post',
'$file' => '',
'$cats' => '',
'$tags' => (($hashtags) ? urlencode($hashtags) : ''),
@@ -173,16 +173,15 @@ class Pubstream extends \Zotlabs\Web\Controller {
$site_firehose_sql = " and owner_xchan in (select channel_hash from channel where channel_system = 0 and channel_removed = 0) ";
}
if(Config::Get('system','public_list_mode'))
$page_mode = 'list';
else
$page_mode = 'client';
$page_mode = 'client';
$blog_mode = Config::Get('system',' public_list_mode');
if ($blog_mode) {
$page_mode = 'list';
}
if(x($hashtags)) {
$sql_extra .= protect_sprintf(term_query('item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG));
$sql_extra_order = " ORDER BY item.created DESC ";
$thread_top = '';
}
$net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : '');
@@ -196,7 +195,7 @@ class Pubstream extends \Zotlabs\Web\Controller {
if($update) {
$ordering = Config::Get('system', 'pubstream_ordering', 'commented');
$ordering = Config::Get('system', 'pubstream_ordering', 'created');
if($load) {
if($mid) {
@@ -246,38 +245,20 @@ class Pubstream extends \Zotlabs\Web\Controller {
}
}
// Then fetch all the children of the parents that are on this page
$parents_str = '';
if($r) {
$parents_str = ids_to_querystr($r,'item_id');
$items = dbq("SELECT item.*, item.id AS item_id FROM item
WHERE true $uids $item_normal
AND item.parent IN ( $parents_str )
$sql_extra $sql_extra_order"
);
$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());
$items = fetch_post_tags($items,true);
if (!$hashtags) {
$items = conv_sort($items, $ordering);
}
$items = conv_sort($items, $ordering);
}
}
$mode = (($hashtags) ? 'pubstream-new' : 'pubstream');
$o .= conversation($items,$mode,$update,$page_mode);
$o .= conversation($items, 'pubstream', $update, $page_mode);
if($mid)
$o .= '<div id="content-complete"></div>';

View File

@@ -375,7 +375,7 @@ class Regate extends \Zotlabs\Web\Controller {
]);
$reonar = json_decode( $r['reg_stuff'], true);
$reonar['deny'] = $now . ',' . $ip . ' ' . $did2 . ' ' . $msg;
$reonar['deny'] = $now . ',' . $ip . ' ' . $did2;
$flags = ( $r['reg_flags'] &= ( $r['reg_flags'] ^ ACCOUNT_UNVERIFIED) )
| ( $r['reg_flags'] |= REGISTER_DENIED);
$rd = q("UPDATE register SET reg_stuff='%s', reg_vital=0, reg_flags=%d WHERE reg_id = %d ",
@@ -386,7 +386,7 @@ class Regate extends \Zotlabs\Web\Controller {
}
else {
zar_log('ZAR1135E not awaited url parameter received');
goaway(z_root);
goaway(z_root());
}
}
else {
@@ -456,7 +456,7 @@ class Regate extends \Zotlabs\Web\Controller {
// $log = ' from § ' . $ip . ' §' . ' (' . dbesc($did2) . ')';
zar_log($msg);
$o = replace_macros(get_markup_template('plain.tpl'), [
'$title' => $title,
'$title' => $msg,
'$now' => $nowfmt,
'$infos' => $msg
]);

View File

@@ -323,7 +323,6 @@ class Register extends Controller {
$did2 = $email;
$didx = 'e';
push_lang(($reg['lang']) ? $reg['lang'] : App::$language);
$reonar['from'] = Config::Get('system', 'from_email');
$reonar['to'] = $email;
$reonar['subject'] = sprintf( t('Registration confirmation for %s'), Config::Get('system','sitename'));
@@ -338,7 +337,6 @@ class Register extends Controller {
'$hash' => $empin
]
);
pop_lang();
zar_reg_mail($reonar);
} else {
@@ -445,7 +443,7 @@ class Register extends Controller {
if(intval(Config::Get('system','register_policy')) == REGISTER_APPROVE) {
$register_msg = ['register_msg', t('Why do you want to join this hub?'), ((x($_REQUEST,'register_msg')) ? $_REQUEST['register_msg'] : ''), t('This will help to review your registration')];
$registration_is = t('Registration on this hub is by approval only.');
$other_sites = '<a href="pubsites">' . t('Register at another affiliated hub in case when prefered') . '</a>';
$other_sites = '<a href="pubsites">' . t('Register at another affiliated hub if preferred') . '</a>';
}
$duty = zar_register_dutystate();

View File

@@ -5,33 +5,33 @@ namespace Zotlabs\Module;
class Removeaccount extends \Zotlabs\Web\Controller {
function post() {
if(! local_channel())
return;
if($_SESSION['delegate'])
return;
if((! x($_POST,'qxz_password')) || (! strlen(trim($_POST['qxz_password']))))
return;
if((! x($_POST,'verify')) || (! strlen(trim($_POST['verify']))))
return;
if($_POST['verify'] !== $_SESSION['remove_account_verify'])
return;
$account = \App::get_account();
$account_id = get_account_id();
$x = account_verify_password($account['account_email'],$_POST['qxz_password']);
if(! ($x && $x['account']))
return;
if($account['account_password_changed'] > NULL_DATE) {
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
if($account['account_password_changed'] > d1) {
if($account['account_password_changed'] > $d1) {
notice( t('Account removals are not allowed within 48 hours of changing the account password.') . EOL);
return;
}

View File

@@ -0,0 +1,89 @@
<?php
namespace Zotlabs\Module;
use Zotlabs\Web\Controller;
class Request extends Controller
{
private function mapVerb(string $verb) : string
{
$verbs = [
'like' => 'Like',
'dislike' => 'Dislike',
'announce' => 'Announce',
'accept' => 'Accept',
'reject' => 'Reject',
'tentativeaccept' => 'TentativeAccept'
];
if (array_key_exists($verb, $verbs)) {
return $verbs[$verb];
}
return EMPTY_STR;
}
private function processSubthreadRequest(): void
{
$mid = $_GET['mid'];
$parent = intval($_GET['parent']);
$offset = null;
if ($_GET['verb'] === 'load') {
$offset = intval($_GET['offset']);
}
$module = strip_tags($_GET['module']);
$items = items_by_thr_parent($mid, $parent, $offset);
xchan_query($items);
$items = fetch_post_tags($items,true);
if ($module === 'channel') {
$parts = explode('@', $items[0]['owner']['xchan_addr']);
profile_load($parts[0]);
}
$ret['html'] = conversation($items, $module, true, 'r_preview');
json_return_and_die($ret);
}
public function init() : void
{
if (in_array($_GET['verb'], ['comment', 'load'])) {
self::processSubthreadRequest();
}
$verb = self::mapVerb($_GET['verb']);
if (!$verb) {
killme();
}
$text = get_response_button_text($_GET['verb']);
$mid = strip_tags($_GET['mid']);
$parent = intval($_GET['parent']);
$observer_hash = get_observer_hash();
$ret['result'] = item_activity_xchans($mid, $parent, $verb);
$commentable = $ret['result']['is_commentable'];
unset($ret['result']['is_commentable']);
if ($commentable) {
$ret['action'] = (($verb === 'Announce') ? 'jotShare' : 'dolike');
$ret['action_label'] = ((find_xchan_in_array($observer_hash, $ret['result'])) ? (($verb === 'Announce') ? t('+ Repeat again') : t('- Remove yours')) : t('+ Add yours'));
}
$ret['title'] = $text['label'];
json_return_and_die($ret);
}
}

View File

@@ -94,7 +94,7 @@ class Rpost extends \Zotlabs\Web\Controller {
'default_location' => $channel['channel_location'],
'nickname' => $channel['channel_address'],
'lockstate' => (($acl->is_private()) ? 'lock' : 'unlock'),
'acl' => populate_acl($channel_acl, true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'),
'acl' => populate_acl($channel_acl, true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions'),
'permissions' => $channel_acl,
'bang' => '',
'visitor' => true,

View File

@@ -71,7 +71,13 @@ class Search extends Controller {
$url = unpack_link_id(basename($url));
}
$f = Libzot::fetch_conversation(App::get_channel(), punify($url), true);
$parsed = parse_url($url);
if (isset($parsed['host'])) {
$parsed['host'] = punify($parsed['host']);
$url = unparse_url($parsed);
}
$f = Libzot::fetch_conversation(App::get_channel(), $url, true);
if ($f) {
$uuid = $f[0]['message_uuid'];
@@ -87,7 +93,7 @@ class Search extends Controller {
else {
// try other fetch providers (e.g. diaspora, pubcrawl)
$hookdata = [
'url' => punify($url)
'url' => $url
];
call_hooks('fetch_provider', $hookdata);
}

View File

@@ -153,14 +153,6 @@ class Channel {
Master::Summon(['Directory', local_channel()]);
Libsync::build_sync_packet();
$email_changed = false;
if ($email_changed && App::$config['system']['register_policy'] == REGISTER_VERIFY) {
// FIXME - set to un-verified, blocked and redirect to logout
// Q: Why? Are we verifying people or email addresses?
// A: the policy is to verify email addresses
}
goaway(z_root() . '/settings');
return; // NOTREACHED
}

View File

@@ -24,7 +24,7 @@ class Display {
$theme = 'redbasic';
$preload_images = ((x($_POST,'preload_images')) ? intval($_POST['preload_images']) : 0);
$thread_allow = ((!empty($_POST['thread_allow'])) ? intval($_POST['thread_allow']) : 0);
$user_scalable = ((x($_POST,'user_scalable')) ? intval($_POST['user_scalable']) : 0);
$nosmile = ((x($_POST,'nosmile')) ? intval($_POST['nosmile']) : 0);
$title_tosource = ((x($_POST,'title_tosource')) ? intval($_POST['title_tosource']) : 0);
@@ -40,7 +40,7 @@ class Display {
$itemspage = 30;
set_pconfig(local_channel(),'system','preload_images',$preload_images);
set_pconfig(local_channel(), 'system', 'thread_allow', $thread_allow);
set_pconfig(local_channel(),'system','user_scalable',$user_scalable);
set_pconfig(local_channel(),'system','update_interval', $browser_update);
set_pconfig(local_channel(),'system','itemspage', $itemspage);
@@ -146,8 +146,7 @@ class Display {
$start_menu = get_pconfig(local_channel(), 'system', 'start_menu', 0);
}
$preload_images = get_pconfig(local_channel(),'system','preload_images');
$preload_images = (($preload_images===false)? '0': $preload_images); // default if not set: 0
$thread_allow = get_pconfig(local_channel(), 'system', 'thread_allow', true);
$user_scalable = get_pconfig(local_channel(),'system','user_scalable');
$user_scalable = (($user_scalable===false)? '0': $user_scalable); // default if not set: 0
@@ -192,7 +191,7 @@ class Display {
'$theme' => (($themes) ? array('theme', t('Display Theme:'), $theme_selected, '', $themes, 'preview') : false),
'$schema' => (($schemas) ? array('schema', t('Select scheme'), $existing_schema, '' , $schemas) : false),
'$preload_images' => array('preload_images', t("Preload images before rendering the page"), $preload_images, t("The subjective page load time will be longer but the page will be ready when displayed"), $yes_no),
'$thread_allow' => ['thread_allow', t('Threaded conversation view'), $thread_allow, t('Display replies below their parent message (default yes)'), $yes_no],
'$user_scalable' => array('user_scalable', t("Enable user zoom on mobile devices"), $user_scalable, '', $yes_no),
'$ajaxint' => array('browser_update', t("Update browser every xx seconds"), $browser_update, t('Minimum of 10 seconds, no maximum')),
'$itemspage' => array('itemspage', t("Maximum number of conversations to load at any time:"), $itemspage, t('Maximum of 30 items')),

View File

@@ -504,12 +504,6 @@ class Setup extends \Zotlabs\Web\Controller {
}
$this->check_add($checks, t('Generate ed25519 encryption keys'), $res, true, $help);
$res1 = extension_loaded('bcmath');
$res2 = extension_loaded('gmp');
if (! ($res1 || $res2)) {
$help = t('Error: one of "bcmath" or "gmp" (bigmath library) extensions are required.') . EOL;
}
$this->check_add($checks, t('Bigmath library (either bcmath or gmp)'), $res1||$res2, $help);
}
/**
@@ -534,6 +528,7 @@ class Setup extends \Zotlabs\Web\Controller {
$this->check_add($ck_funcs, t('xml PHP module'), true, true);
$this->check_add($ck_funcs, t('zip PHP module'), true, true);
$this->check_add($ck_funcs, t('intl PHP module'), true, true);
$this->check_add($ck_funcs, t('gmp PHP module'), true, true);
if(function_exists('apache_get_modules')){
if(! in_array('mod_rewrite', apache_get_modules())) {
@@ -590,9 +585,15 @@ class Setup extends \Zotlabs\Web\Controller {
$ck_funcs[6]['status'] = false;
$ck_funcs[6]['help'] = t('Error: zip PHP module required but not installed.');
}
if(! extension_loaded('intl')) {
$ck_funcs[6]['status'] = false;
$ck_funcs[6]['help'] = t('Error: intl PHP module required but not installed.');
if (!extension_loaded('intl')) {
$ck_funcs[7]['status'] = false;
$ck_funcs[7]['help'] = t('Error: intl PHP module required but not installed.');
}
if (!extension_loaded('gmp')) {
$ck_funcs[8]['status'] = false;
$ck_funcs[8]['help'] = t('Error: gmp PHP module required but not installed.');
}
$checks = array_merge($checks, $ck_funcs);

View File

@@ -20,6 +20,8 @@ class Sse_bs extends Controller {
public static $limit;
public static $offset;
public static $xchans;
public static $direction;
public static $count_limit;
function init() {
self::$uid = local_channel();
@@ -41,8 +43,10 @@ class Sse_bs extends Controller {
self::$limit = 30;
self::$offset = 0;
self::$xchans = '';
self::$direction = get_pconfig(self::$uid, 'system', 'invert_notifications_order', false) ? 'ASC' : 'DESC';
self::$count_limit = get_pconfig(self::$uid, 'system', 'notifications_count_limit', 100);
if (isset($_REQUEST['sse_rmids'])) {
if (!empty($_REQUEST['sse_rmids'])) {
self::mark_read(explode(',', $_REQUEST['sse_rmids']));
}
@@ -64,7 +68,6 @@ class Sse_bs extends Controller {
$_SESSION['sse_loadtime'] = datetime_convert();
}
$network = false;
$dm = false;
$home = false;
@@ -91,8 +94,14 @@ class Sse_bs extends Controller {
default:
}
$selected_forum_id = null;
if (str_starts_with(argv(1), 'forum_')) {
$selected_forum_id = argv(1);
$f = 'bs_forums';
}
if(self::$offset && $f) {
$result = self::$f(true);
$result = self::$f($selected_forum_id ?? true);
json_return_and_die($result);
}
@@ -102,7 +111,7 @@ class Sse_bs extends Controller {
self::bs_home($home),
self::bs_notify(),
self::bs_intros(),
self::bs_forums(),
self::bs_forums($selected_forum_id),
self::bs_pubs($pubs),
self::bs_files(),
self::bs_all_events(),
@@ -118,7 +127,6 @@ class Sse_bs extends Controller {
}
function mark_read($arr) {
$mids = [];
$str = '';
$slice = 0;
@@ -142,24 +150,51 @@ class Sse_bs extends Controller {
}
}
$_SESSION['sse_mids_all'] = serialise($mids_all);
$str = implode(',', $mids);
$sys = get_sys_channel();
$sql_order = ((self::$uid > $sys['channel_id']) ? 'DESC' : 'ASC');
$r = q("SELECT uid, uuid FROM item
WHERE uid in (%d, %d)
AND verb IN ('Like', 'Dislike', 'Announce', 'Accept', 'Reject', 'TentativeAccept')
AND thr_parent IN (
SELECT mid FROM item WHERE uid IN (%d, %d) AND uuid IN (%s) ORDER BY uid $sql_order
)
GROUP BY uid, uuid
ORDER BY uid $sql_order",
intval(self::$uid),
intval($sys['channel_id']),
intval(self::$uid),
intval($sys['channel_id']),
$str // this is dbesc() in the above foreach loop
);
if ($r) {
$activities_str = ids_to_querystr($r, 'uuid', true);
$str .= ',' . $activities_str;
$activities_arr = explode(',', $activities_str);
$mids_all = array_merge($mids_all, $activities_arr);
}
$_SESSION['sse_mids_all'] = serialise(array_unique($mids_all));
if(! self::$uid) {
return;
}
$str = implode(',', $mids);
$x = [ 'channel_id' => self::$uid, 'update' => 'unset' ];
call_hooks('update_unseen',$x);
if($x['update'] === 'unset' || intval($x['update'])) {
q("UPDATE item SET item_unseen = 0 WHERE uid = %d AND uuid in (%s) AND item_unseen = 1",
if ($x['update'] === 'unset' || intval($x['update'])) {
q("UPDATE item SET item_unseen = 0
WHERE uid = %d
AND uuid in (%s)
AND item_unseen = 1",
intval(self::$uid),
$str // this is dbesc() in the above foreach loop
);
}
}
function bs_network($notifications) {
@@ -179,35 +214,39 @@ class Sse_bs extends Controller {
$limit = intval(self::$limit);
$offset = self::$offset;
$direction = self::$direction;
$count_limit = intval(self::$count_limit);
$sql_extra = '';
if (!(self::$vnotify & VNOTIFY_LIKE)) {
$sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
$sql_extra = " AND item.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
elseif (!feature_enabled(self::$uid, 'dislike')) {
$sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
$sql_extra = " AND item.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
$sql_extra2 = '';
if(self::$xchans)
$sql_extra2 = " AND CASE WHEN verb = '" . dbesc(ACTIVITY_SHARE) . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") ";
$sql_extra2 = " AND CASE WHEN item.verb = '" . dbesc(ACTIVITY_SHARE) . "' THEN item.owner_xchan ELSE item.author_xchan END IN (" . self::$xchans . ") ";
$item_normal = item_normal();
// Filter internal follow activities and strerams add/remove activities
$item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
$item_normal .= " AND item.verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
if ($notifications) {
$items = q("SELECT * FROM item
WHERE uid = %d
AND created <= '%s'
AND item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1)
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
$items = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item
LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid
WHERE item.uid = %d
AND item.created <= '%s'
AND item.item_unseen = 1 AND item.item_wall = 0 AND item.item_private IN (0, 1)
AND item.obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND NOT (item.verb = 'Announce' AND item.item_thread_top = 1) -- only show the announce activity and not the resulting item
AND NOT item.author_xchan = '%s'
$item_normal
$sql_extra
$sql_extra2
ORDER BY created DESC LIMIT $limit OFFSET $offset",
ORDER BY item.created $direction LIMIT $limit OFFSET $offset",
intval(self::$uid),
dbescdate($_SESSION['sse_loadtime']),
dbesc(self::$ob_hash)
@@ -234,7 +273,7 @@ class Sse_bs extends Controller {
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
$item_normal
$sql_extra LIMIT 100",
$sql_extra LIMIT $count_limit",
intval(self::$uid),
dbesc(self::$ob_hash)
);
@@ -262,35 +301,39 @@ class Sse_bs extends Controller {
$limit = intval(self::$limit);
$offset = self::$offset;
$direction = self::$direction;
$count_limit = intval(self::$count_limit);
$sql_extra = '';
if (!(self::$vnotify & VNOTIFY_LIKE)) {
$sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
$sql_extra = " AND item.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
elseif (!feature_enabled(self::$uid, 'dislike')) {
$sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
$sql_extra = " AND item.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
$sql_extra2 = '';
if(self::$xchans)
$sql_extra2 = " AND CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") ";
$sql_extra2 = " AND CASE WHEN item.verb = '" . ACTIVITY_SHARE . "' THEN item.owner_xchan ELSE item.author_xchan END IN (" . self::$xchans . ") ";
$item_normal = item_normal();
// Filter internal follow activities and strerams add/remove activities
$item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
$item_normal .= " AND item.verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
if ($notifications) {
$items = q("SELECT * FROM item
WHERE uid = %d
AND created <= '%s'
AND item_unseen = 1 AND item_private = 2
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
$items = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item
LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid
WHERE item.uid = %d
AND item.created <= '%s'
AND item.item_unseen = 1 AND item.item_private = 2
AND item.obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND NOT (item.verb = 'Announce' AND item.item_thread_top = 1) -- only show the announce activity and not the resulting item
AND NOT item.author_xchan = '%s'
$item_normal
$sql_extra
$sql_extra2
ORDER BY created DESC LIMIT $limit OFFSET $offset",
ORDER BY created $direction LIMIT $limit OFFSET $offset",
intval(self::$uid),
dbescdate($_SESSION['sse_loadtime']),
dbesc(self::$ob_hash)
@@ -316,7 +359,7 @@ class Sse_bs extends Controller {
WHERE uid = %d and item_unseen = 1 AND item_private = 2
$item_normal
$sql_extra
AND author_xchan != '%s' LIMIT 100",
AND author_xchan != '%s' LIMIT $count_limit",
intval(self::$uid),
dbesc(self::$ob_hash)
);
@@ -344,36 +387,40 @@ class Sse_bs extends Controller {
$limit = intval(self::$limit);
$offset = self::$offset;
$direction = self::$direction;
$count_limit = intval(self::$count_limit);
$sql_extra = '';
if (!(self::$vnotify & VNOTIFY_LIKE)) {
$sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
$sql_extra = " AND item.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
elseif (!feature_enabled(self::$uid, 'dislike')) {
$sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
$sql_extra = " AND item.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
$sql_extra2 = '';
if(self::$xchans)
$sql_extra2 = " AND CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") ";
$sql_extra2 = " AND CASE WHEN item.verb = '" . ACTIVITY_SHARE . "' THEN item.owner_xchan ELSE item.author_xchan END IN (" . self::$xchans . ") ";
$item_normal = item_normal();
// Filter internal follow activities and strerams add/remove activities
$item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
$item_normal .= " AND item.verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
if ($notifications) {
$items = q("SELECT * FROM item
WHERE uid = %d
AND created <= '%s'
AND item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1)
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
$items = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item
LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid
WHERE item.uid = %d
AND item.created <= '%s'
AND item.item_unseen = 1 AND item.item_wall = 1 AND item.item_private IN (0, 1)
AND item.obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND NOT (item.verb = 'Announce' AND item.item_thread_top = 1) -- only show the announce activity and not the resulting item
AND NOT item.author_xchan = '%s'
$item_normal
$sql_extra
$sql_extra2
ORDER BY created DESC LIMIT $limit OFFSET $offset",
ORDER BY item.created $direction LIMIT $limit OFFSET $offset",
intval(self::$uid),
dbescdate($_SESSION['sse_loadtime']),
dbesc(self::$ob_hash)
@@ -399,7 +446,7 @@ class Sse_bs extends Controller {
WHERE uid = %d and item_unseen = 1 AND item_wall = 1 AND item_private IN (0, 1)
$item_normal
$sql_extra
AND author_xchan != '%s' LIMIT 100",
AND author_xchan != '%s' LIMIT $count_limit",
intval(self::$uid),
dbesc(self::$ob_hash)
);
@@ -438,53 +485,57 @@ class Sse_bs extends Controller {
$limit = intval(self::$limit);
$offset = self::$offset;
$direction = self::$direction;
$count_limit = intval(self::$count_limit);
$sys = get_sys_channel();
$sql_extra = '';
if (!(self::$vnotify & VNOTIFY_LIKE)) {
$sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
$sql_extra = " AND item.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
elseif (!feature_enabled(self::$uid, 'dislike')) {
$sql_extra = " AND verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
$sql_extra = " AND item.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
$sql_extra2 = '';
if(self::$xchans)
$sql_extra2 = " AND CASE WHEN verb = '" . ACTIVITY_SHARE . "' THEN owner_xchan ELSE author_xchan END IN (" . self::$xchans . ") ";
$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 = unserialise($_SESSION['sse_mids_all']) ?? [];
$sse_mids_all = isset($_SESSION['sse_mids_all']) ? unserialise($_SESSION['sse_mids_all']) : [];
if ($sse_mids_all) {
$sql_extra3 = " AND uuid NOT IN (" . protect_sprintf(implode(',', $sse_mids_all)) . ") ";
$sql_extra3 = " AND item.uuid NOT IN (" . protect_sprintf(implode(',', $sse_mids_all)) . ") ";
}
$uids = " AND uid IN ( " . $sys['channel_id'] . " ) ";
$uids = " AND item.uid IN ( " . $sys['channel_id'] . " ) ";
$site_firehose = Config::Get('system', 'site_firehose', 0);
if($site_firehose) {
$uids = " AND uid IN ( " . stream_perms_api_uids(PERMS_PUBLIC) . " ) AND item_private = 0 AND item_wall = 1 ";
$uids = " AND item.uid IN ( " . stream_perms_api_uids(PERMS_PUBLIC) . " ) AND item.item_private = 0 AND item.item_wall = 1 ";
}
$item_normal = item_normal();
// Filter internal follow activities and strerams add/remove activities
$item_normal .= " AND verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
$item_normal .= " AND item.verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
if ($notifications) {
$items = q("SELECT * FROM item
$items = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item
LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid
WHERE true $uids
AND created <= '%s'
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
AND created > '%s'
AND item.created <= '%s'
AND item.created > '%s'
AND item.obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND NOT (item.verb = 'Announce' AND item.item_thread_top = 1) -- only show the announce activity not the resulting item
AND NOT item.author_xchan = '%s'
$item_normal
$sql_extra
$sql_extra2
$sql_extra3
ORDER BY created DESC LIMIT $limit OFFSET $offset",
ORDER BY item.created $direction LIMIT $limit OFFSET $offset",
dbescdate($_SESSION['sse_loadtime']),
dbesc(self::$ob_hash),
dbescdate($_SESSION['last_login_date'] ?? $_SESSION['static_loadtime'])
dbescdate($_SESSION['static_loadtime']),
dbesc(self::$ob_hash)
);
if($items) {
@@ -509,9 +560,9 @@ class Sse_bs extends Controller {
$item_normal
$sql_extra
$sql_extra3
AND author_xchan != '%s' LIMIT 100",
AND author_xchan != '%s' LIMIT $count_limit",
dbescdate($_SESSION['sse_loadtime']),
dbescdate($_SESSION['last_login_date'] ?? $_SESSION['static_loadtime']),
dbescdate($_SESSION['static_loadtime']),
dbesc(self::$ob_hash)
);
@@ -534,7 +585,9 @@ class Sse_bs extends Controller {
if(! (self::$vnotify & VNOTIFY_SYSTEM))
return $result;
$r = q("SELECT * FROM notify WHERE uid = %d AND seen = 0 ORDER BY created DESC",
$direction = self::$direction;
$r = q("SELECT * FROM notify WHERE uid = %d AND seen = 0 ORDER BY created $direction",
intval(self::$uid)
);
@@ -561,7 +614,9 @@ class Sse_bs extends Controller {
if(! (self::$vnotify & VNOTIFY_INTRO))
return $result;
$r = q("SELECT * FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ORDER BY abook_created DESC LIMIT 50",
$direction = self::$direction;
$r = q("SELECT * FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ORDER BY abook_created $direction LIMIT 50",
intval(self::$uid)
);
@@ -576,68 +631,95 @@ class Sse_bs extends Controller {
}
function bs_forums() {
$result['forums']['notifications'] = [];
$result['forums']['count'] = 0;
$result['forums']['offset'] = -1;
if(! self::$uid)
return $result;
if(! (self::$vnotify & VNOTIFY_FORUMS))
return $result;
function bs_forums($selected_forum_id) {
$forums = get_forum_channels(self::$uid);
if(!self::$uid || !(self::$vnotify & VNOTIFY_FORUMS) || !$forums) {
$result['forum']['notifications'] = [];
$result['forum']['count'] = 0;
$result['forum']['offset'] = -1;
return $result;
}
if($forums) {
$item_normal = item_normal();
$p_sql = '';
$sql_extra = '';
if(! (self::$vnotify & VNOTIFY_LIKE))
$sql_extra = " AND verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
$fcount = count($forums);
$i = 0;
for($x = 0; $x < $fcount; $x ++) {
$r = q("select count(*) as total from item
where uid = %d and (owner_xchan = '%s' or author_xchan = '%s') and author_xchan != '%s' and verb != 'Announce' and item_unseen = 1 $sql_extra $item_normal",
$forum_id = 'forum_' . $forums[$x]['abook_id'];
$result[$forum_id]['notifications'] = [];
$result[$forum_id]['count'] = 0;
$limit = intval(self::$limit);
$offset = self::$offset;
$direction = self::$direction;
$count_limit = intval(self::$count_limit);
$sql_extra = '';
if (!(self::$vnotify & VNOTIFY_LIKE)) {
$sql_extra = " AND item.verb NOT IN ('Like', 'Dislike', '" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
elseif (!feature_enabled(self::$uid, 'dislike')) {
$sql_extra = " AND item.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
$item_normal = item_normal();
// Filter internal follow activities and strerams add/remove activities
$item_normal .= " AND item.verb NOT IN ('Add', 'Remove', 'Follow', 'Ignore', '" . dbesc(ACTIVITY_FOLLOW) . "') ";
if ($forum_id === $selected_forum_id) {
$items = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item
LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid
WHERE item.uid = %d
AND item.created <= '%s'
AND item.owner_xchan = '%s'
AND item.item_unseen = 1 AND item.item_wall = 0 AND item.item_private IN (0, 1)
AND item.obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND NOT (item.verb = 'Announce' AND item.item_thread_top = 1) -- only show the announce activity and not the resulting item
AND NOT item.author_xchan = '%s'
$item_normal
$sql_extra
ORDER BY item.created $direction LIMIT $limit OFFSET $offset",
intval(self::$uid),
dbescdate($_SESSION['sse_loadtime']),
dbescdate($forums[$x]['xchan_hash']),
dbesc(self::$ob_hash)
);
if ($items) {
$result[$forum_id]['offset'] = ((count($items) == $limit) ? intval($offset + $limit) : -1);
xchan_query($items);
foreach($items as $item) {
$parsed = Enotify::format($item);
if($parsed) {
$result[$forum_id]['notifications'][] = $parsed;
}
}
}
else {
$result[$forum_id]['offset'] = -1;
}
}
$r = q("SELECT id FROM item
WHERE uid = %d and item_unseen = 1 AND item_wall = 0 AND item_private IN (0, 1)
AND obj_type NOT IN ('Document', 'Video', 'Audio', 'Image')
AND author_xchan != '%s'
AND item.owner_xchan = '%s'
$item_normal
$sql_extra LIMIT $count_limit",
intval(self::$uid),
dbesc($forums[$x]['xchan_hash']),
dbesc(self::$ob_hash),
dbesc($forums[$x]['xchan_hash'])
);
if($r[0]['total']) {
$forums[$x]['notify_link'] = z_root() . '/network/?f=&pf=1&unseen=1&cid=' . $forums[$x]['abook_id'];
$forums[$x]['name'] = $forums[$x]['xchan_name'];
$forums[$x]['addr'] = $forums[$x]['xchan_addr'] ?? $forums[$x]['xchan_url'];
$forums[$x]['url'] = $forums[$x]['xchan_url'];
$forums[$x]['photo'] = $forums[$x]['xchan_photo_s'];
$forums[$x]['unseen'] = $r[0]['total'];
$forums[$x]['private_forum'] = ((isset($forums[$x]['private_forum']) && $forums[$x]['private_forum']) ? 'lock' : '');
$forums[$x]['message'] = ((isset($forums[$x]['private_forum']) && $forums[$x]['private_forum']) ? t('Private forum') : t('Public forum'));
unset($forums[$x]['abook_id']);
unset($forums[$x]['xchan_hash']);
unset($forums[$x]['xchan_name']);
unset($forums[$x]['xchan_url']);
unset($forums[$x]['xchan_photo_s']);
$i = $i + $r[0]['total'];
}
else {
unset($forums[$x]);
if ($r) {
$result[$forum_id]['count'] = count($r);
}
}
$result['forums']['count'] = $i;
$result['forums']['notifications'] = array_values($forums);
}
return $result;
@@ -656,6 +738,7 @@ class Sse_bs extends Controller {
if(! (self::$vnotify & VNOTIFY_FILES))
return $result;
$direction = self::$direction;
$item_normal = item_normal();
// Filter internal follow activities and strerams add/remove activities
@@ -668,7 +751,7 @@ class Sse_bs extends Controller {
AND author_xchan != '%s'
AND item_unseen = 1
$item_normal
ORDER BY created DESC",
ORDER BY created $direction",
dbesc(ACTIVITY_POST),
intval(self::$uid),
dbesc(self::$ob_hash)
@@ -700,10 +783,12 @@ class Sse_bs extends Controller {
if(! (self::$vnotify & VNOTIFY_EVENT))
return $result;
$direction = self::$direction;
$r = q("SELECT * FROM event left join xchan on event_xchan = xchan_hash
WHERE event.uid = %d AND dtstart < '%s' AND dtstart > '%s' and dismissed = 0
and etype in ( 'event', 'birthday' )
ORDER BY dtstart DESC",
ORDER BY dtstart $direction",
intval(self::$uid),
dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval(self::$evdays) . ' days')),
dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days'))

View File

@@ -6,34 +6,34 @@ namespace Zotlabs\Module;
class Viewsrc extends \Zotlabs\Web\Controller {
function get() {
$o = '';
$sys = get_sys_channel();
$item_id = ((argc() > 1) ? intval(argv(1)) : 0);
$json = ((argc() > 2 && argv(2) === 'json') ? true : false);
$dload = ((argc() > 2 && argv(2) === 'download') ? true : false);
if(! local_channel()) {
notice( t('Permission denied.') . EOL);
}
if(! $item_id) {
\App::$error = 404;
notice( t('Item not found.') . EOL);
}
$item_normal = item_normal_search();
if(local_channel() && $item_id) {
$r = q("select id, mid, 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 id, mid, uuid, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1",
intval(local_channel()),
intval($sys['channel_id']),
intval($item_id)
);
if($r) {
if(intval($r[0]['item_obscured']))
$dload = true;
@@ -50,18 +50,18 @@ class Viewsrc extends \Zotlabs\Web\Controller {
$o = (($json) ? json_encode($content) : $content);
}
}
if(is_ajax()) {
echo '<div class="p-1">';
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a><br>mid: ' . $r[0]['mid'] . '</div>';
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a><br>mid: ' . $r[0]['mid'] . '<br>uuid: ' . $r[0]['uuid'] . '</div>';
echo '<hr>';
echo '<pre class="p-1">' . $o . '</pre>';
echo '</div>';
killme();
}
}
return $o;
}
}

View File

@@ -20,27 +20,35 @@ class Vote extends Controller {
json_return_and_die($ret);
}
$fetch = null;
$id = argv(1);
$response = $_REQUEST['answer'];
if ($id) {
$fetch = q("select * from item where id = %d limit 1",
intval($id)
);
if (!$id) {
$ret['message'] = t('Missing poll id.');
json_return_and_die($ret);
}
$answer = q("select * from item where parent = %d and uid = %d and obj_type = 'Answer' limit 1",
intval($id),
intval($channel['channel_id'])
);
if ($fetch && $fetch[0]['obj_type'] === 'Question') {
$obj = json_decode($fetch[0]['obj'],true);
if ($answer) {
$ret['message'] = t('You have already submitted your vote for this poll.');
json_return_and_die($ret);
}
else {
$poll = q("select * from item where id = %d limit 1",
intval($id)
);
if (!$poll && $poll[0]['obj_type'] !== 'Question') {
$ret['message'] = t('Poll not found.');
json_return_and_die($ret);
}
$response = $_REQUEST['answer'];
$obj = json_decode($poll[0]['obj'],true);
$valid = false;
if ($obj['oneOf']) {
@@ -81,17 +89,18 @@ class Vote extends Controller {
$item['aid'] = $channel['channel_account_id'];
$item['uid'] = $channel['channel_id'];
$item['item_origin'] = 1;
$item['parent'] = $fetch[0]['id'];
$item['parent_mid'] = $fetch[0]['mid'];
$item['thr_parent'] = $fetch[0]['mid'];
$item['parent'] = $poll[0]['id'];
$item['parent_mid'] = $poll[0]['mid'];
$item['thr_parent'] = $poll[0]['mid'];
$item['uuid'] = new_uuid();
$item['mid'] = z_root() . '/item/' . $item['uuid'];
$item['verb'] = 'Create';
$item['title'] = $res;
$item['author_xchan'] = $channel['channel_hash'];
$item['owner_xchan'] = $fetch[0]['author_xchan'];
$item['allow_cid'] = '<' . $fetch[0]['author_xchan'] . '>';
$item['owner_xchan'] = $poll[0]['author_xchan'];
$item['allow_cid'] = '<' . $poll[0]['author_xchan'] . '>';
$item['item_private'] = 1;
$item['item_unseen'] = 0;
$item['obj_type'] = 'Note';
$item['author'] = channelx_by_n($channel['channel_id']);
$item['obj'] = Activity::encode_item($item);
@@ -104,7 +113,7 @@ class Vote extends Controller {
$x = item_store($item);
retain_item($fetch[0]['id']);
retain_item($poll[0]['id']);
if($x['success']) {
Master::Summon(['Notifier', 'like', $x['item_id']]);
@@ -125,7 +134,7 @@ class Vote extends Controller {
}
$ret['success'] = true;
$ret['message'] = t('Response submitted. Updates may not appear instantly.');
$ret['message'] = t('Your vote has been submitted. Updates may not appear instantly.');
json_return_and_die($ret);
}
}

View File

@@ -10,7 +10,7 @@ class Wall_attach extends \Zotlabs\Web\Controller {
function init() {
logger('request_method: ' . $_SERVER['REQUEST_METHOD'],LOGGER_DATA,LOG_INFO);
logger('wall_attach: ' . print_r($_REQUEST,true),LOGGER_DEBUG,LOG_INFO);
logger('wall_attach: ' . print_r($_POST,true),LOGGER_DEBUG,LOG_INFO);
logger('wall_attach files: ' . print_r($_FILES,true),LOGGER_DEBUG,LOG_INFO);
// for testing without actually storing anything
// http_status_exit(200,'OK');
@@ -18,12 +18,11 @@ class Wall_attach extends \Zotlabs\Web\Controller {
function post() {
$using_api = false;
$result = [];
if($_REQUEST['api_source'] && array_key_exists('media',$_FILES)) {
if($_POST['api_source'] && array_key_exists('media',$_FILES)) {
$using_api = true;
}
@@ -98,8 +97,8 @@ class Wall_attach extends \Zotlabs\Web\Controller {
$r = attach_store($channel, get_observer_hash(), '', $data);
if(! $r['success']) {
notice( $r['message'] . EOL);
if (!$r['success']) {
notice($r['message'] . EOL);
killme();
}

View File

@@ -1,26 +0,0 @@
<?php
namespace Zotlabs\Module;
class Xref extends \Zotlabs\Web\Controller {
function init() {
// Sets a referral URL using an xchan directly
// Link format: example.com/xref/[xchan]/[TargetURL]
// Target URL is optional.
// Cookie lasts 24 hours to survive a browser restart. Contains no personal
// information at all - just somebody else's xchan.
$referrer = argv(1);
$expire=time()+60*60*2;
$path = 'xref';
setcookie($path, $referrer, $expire, "/");
$url = '';
if (argc() > 2)
$url = argv(2);
goaway (z_root() . '/' . $url);
}
}

View File

@@ -9,6 +9,10 @@ class Zot_probe extends \Zotlabs\Web\Controller {
function get() {
if (!local_channel()) {
return;
}
$addr = $_GET['addr'] ?? '';
$o = '<h3>Zot6 Probe Diagnostic</h3>';

View File

@@ -0,0 +1,49 @@
<?php
/*
* SPDX-FileCopyrightText: 2025 The Hubzilla Community
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Photo;
/**
* A class to represent image quality values.
*/
class ImageQuality
{
readonly string $mimeType;
readonly int $value;
private const DEFAULT_VALUE = [
'image/jpeg' => JPEG_QUALITY,
'image/png' => PNG_QUALITY,
'image/avif' => AVIF_QUALITY,
'image/webp' => WEBP_QUALITY,
];
public function __construct(string $mimeType, int $value) {
[ $min, $max ] = $this->validRange($mimeType);
if ($value < $min || $value > $max) {
$value = self::DEFAULT_VALUE[$mimeType];
}
$this->value = $value;
$this->mimeType = $mimeType;
}
/**
* Return the valid quality setting range for the given mime type.
*
* @param string $mimeType The mime type whose range to return.
*
* @return The valid range as an array with two elements as
* [ $min, $max ].
*/
public static function validRange(string $mimeType): array {
// Max quality for PNG is 10, for all others it's 100.
return $mimeType === 'image/png' ? [ 0, 10 ] : [ 1, 100 ];
}
}

View File

@@ -12,6 +12,7 @@ class PhotoGd extends PhotoDriver {
/**
* {@inheritDoc}
*
* @see \Zotlabs\Photo\PhotoDriver::supportedTypes()
*/
public function supportedTypes() {
@@ -25,16 +26,21 @@ class PhotoGd extends PhotoDriver {
$t['image/gif'] = 'gif';
if(\imagetypes() & IMG_WEBP)
$t['image/webp'] = 'webp';
if(\imagetypes() & IMG_AVIF)
$t['image/avif'] = 'avif';
return $t;
}
/**
* phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter
*/
protected function load($data, $type) {
$this->valid = false;
if(! $data)
return;
$this->image = @imagecreatefromstring($data);
$this->image = imagecreatefromstring($data);
if($this->image !== false) {
$this->valid = true;
$this->setDimensions();
@@ -145,6 +151,7 @@ class PhotoGd extends PhotoDriver {
/**
* {@inheritDoc}
*
* @see \Zotlabs\Photo\PhotoDriver::imageString()
*/
public function imageString() {
@@ -152,52 +159,30 @@ class PhotoGd extends PhotoDriver {
if(! $this->is_valid())
return false;
$quality = false;
ob_start();
switch($this->getType()){
case 'image/avif':
imageavif($this->image, null, $this->getQuality()->value);
break;
case 'image/png':
$quality = Config::Get('system', 'png_quality');
if((! $quality) || ($quality > 9)) {
$quality = PNG_QUALITY;
}
if (function_exists('imagepng')) {
\imagepng($this->image, NULL, $quality);
}
imagepng($this->image, null, $this->getQuality()->value);
break;
case 'image/webp':
$quality = Config::Get('system', 'webp_quality');
imagewebp($this->image, null, $this->getQuality()->value);
break;
if((! $quality) || ($quality > 100)) {
$quality = WEBP_QUALITY;
}
case 'image/gif':
imagegif($this->image);
break;
if (function_exists('imagewebp')) {
\imagewebp($this->image, NULL, $quality);
}
break;
case 'image/jpeg':
// gd can lack imagejpeg(), but we verify during installation it is available
case 'image/jpeg':
default:
$quality = Config::Get('system', 'jpeg_quality');
if((! $quality) || ($quality > 100)) {
$quality = JPEG_QUALITY;
}
if (function_exists('imagejpeg')) {
\imagejpeg($this->image, NULL, $quality);
}
imagejpeg($this->image, null, $this->getQuality()->value);
break;
}
@@ -207,4 +192,23 @@ class PhotoGd extends PhotoDriver {
return $string;
}
/**
* Return the default image quality for the given mime type.
*
* If the setting has been overridden in the database, we use that value,
* otherwise the hardcoded defaults.
*
* @return ImageQuality The image quality value for the current mime type.
*/
private function getQuality(): ImageQuality {
$key = match($this->getType()) {
'image/avif' => 'avif_quality',
'image/jpeg' => 'jpeg_quality',
'image/png' => 'png_quality',
'image/webp' => 'webp_quality',
};
return new ImageQuality($this->getType(), Config::Get('system', $key));
}
}

View File

@@ -19,7 +19,7 @@ class SmartyInterface extends Smarty {
// The order is thus very important here
$template_dirs = array('theme' => "view/theme/$thname/tpl/");
if ( x(App::$theme_info,"extends") ) {
if (!empty(App::$theme_info['extends'])) {
$template_dirs = $template_dirs + array('extends' => "view/theme/" . App::$theme_info["extends"] . "/tpl/");
}
$template_dirs = $template_dirs + array('base' => 'view/tpl/');

View File

@@ -26,9 +26,9 @@ class Theme {
*/
static public function current() {
self::$system_theme = ((isset(App::$config['system']['theme']))
self::$system_theme = ((!empty(App::$config['system']['theme']))
? App::$config['system']['theme'] : '');
self::$session_theme = ((isset($_SESSION) && x($_SESSION, 'theme'))
self::$session_theme = ((!empty($_SESSION['theme']))
? $_SESSION['theme'] : self::$system_theme);
$page_theme = null;
@@ -87,8 +87,13 @@ class Theme {
// Find any theme at all and use it.
$fallback = array_merge(glob('view/theme/*/css/style.css'), glob('view/theme/*/php/style.php'));
if(count($fallback))
return(array(str_replace('view/theme/', '', substr($fallback[0], 0, -14))));
if (empty($fallback)) {
logger('Unable to find a theme');
http_status_exit(500, 'internal server error');
}
return(array(str_replace('view/theme/', '', substr($fallback[0], 0, -14))));
}
@@ -111,7 +116,7 @@ class Theme {
$opts = '';
$opts = (($uid) ? '?puid=' . $uid : '');
$schema_str = ((x(App::$layout,'schema')) ? '&schema=' . App::$layout['schema'] : '');
$schema_str = ((!empty(App::$layout['schema'])) ? '&schema=' . App::$layout['schema'] : '');
if(($s) && (! $schema_str))
$schema_str = '&schema=' . $s;

View File

@@ -5,6 +5,7 @@ namespace Zotlabs\Storage;
use Sabre\DAV;
use App;
use Zotlabs\Lib\Config;
use Zotlabs\Lib\Text;
/**
* @brief Provides a DAV frontend for the webbrowser.
@@ -43,7 +44,7 @@ class Browser extends DAV\Browser\Plugin {
*/
public function __construct(&$auth) {
$this->auth = $auth;
parent::__construct(true, false);
parent::__construct();
}
/**
@@ -260,13 +261,16 @@ class Browser extends DAV\Browser\Plugin {
}
}
$display_path_encoded = Text::rawurlencode_parts($data['display_path']);
$href_encoded = Text::rawurlencode_parts($href);
// put the array for this file together
$ft['attach_id'] = $id;
// $ft['icon'] = $icon;
$ft['photo_icon'] = $photo_icon;
$ft['is_creator'] = $is_creator;
$ft['rel_path'] = (($data) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href);
$ft['full_path'] = z_root() . (($data) ? '/cloud/' . $nick .'/' . $data['display_path'] : $href);
$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['size'] = $size;

View File

@@ -866,7 +866,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
$path = $path . '/' . $r[0]['filename'];
}
if (! $r) {
$r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach
$r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, is_photo, os_storage, created, edited from attach
where folder = '%s' and filename = '%s' and uid = %d $perms order by filename limit 1",
dbesc($folder),
dbesc(basename($file)),
@@ -875,7 +875,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
}
if (! $r) {
$errors = true;
$r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach
$r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, is_photo, os_storage, created, edited from attach
where folder = '%s' and filename = '%s' and uid = %d order by filename limit 1",
dbesc($folder),
dbesc(basename($file)),

View File

@@ -93,19 +93,20 @@ class File extends DAV\Node implements DAV\IFile {
$x = attach_syspaths($this->auth->owner_id,$this->data['hash']);
$y = q("update attach set display_path = '%s where hash = '%s' and uid = %d",
$y = q("update attach set display_path = '%s' where hash = '%s' and uid = %d",
dbesc($x['path']),
dbesc($this->data['hash']),
intval($this->auth->owner_id)
);
if($this->data->is_photo) {
if($this->data['is_photo']) {
$r = q("update photo set filename = '%s', display_path = '%s' where resource_id = '%s' and uid = %d",
dbesc($newName),
dbesc($x['path']),
dbesc($this->data['hash']),
intval($this->auth->owner_id)
);
}
$ch = channelx_by_n($this->auth->owner_id);

29
Zotlabs/Update/_1264.php Normal file
View File

@@ -0,0 +1,29 @@
<?php
namespace Zotlabs\Update;
use Zotlabs\Lib\Config;
class _1264 {
function run() {
dbq("START TRANSACTION");
$admin_email = trim(Config::Get('system','admin_email'));
$r = q("UPDATE account SET account_roles = 0 WHERE account_created > '2025-06-24 00:00:00' AND account_email <> '%s'",
dbesc($admin_email)
);
if($r) {
dbq("COMMIT");
return UPDATE_SUCCESS;
}
dbq("ROLLBACK");
return UPDATE_FAILED;
}
}

View File

@@ -2,6 +2,7 @@
namespace Zotlabs\Web;
use App;
use DateTime;
use DateTimeZone;
use Zotlabs\Lib\Activity;
@@ -11,6 +12,9 @@ use Zotlabs\Lib\Keyutils;
use Zotlabs\Lib\Webfinger;
use Zotlabs\Lib\Zotfinger;
use Zotlabs\Lib\Libzot;
use HttpSignature\HttpMessageSigner;
use HttpSignature\UnProcessableSignatureException;
/**
* @brief Implements HTTP Signatures per draft-cavage-http-signatures-10.
@@ -88,7 +92,7 @@ class HTTPSig {
// See draft-cavage-http-signatures-10
static function verify($data, $key = '', $keytype = '') {
public static function verify($data, $key = '', $keytype = '') {
$body = $data;
$headers = null;
@@ -102,11 +106,63 @@ class HTTPSig {
'content_valid' => false
];
$headers = self::find_headers($data, $body);
if (!$headers)
if (!$headers) {
return $result;
}
if (App::$request && array_key_exists('signature-input', $headers) && array_key_exists('signature', $headers)) {
$found = preg_match('/keyid="(.*?)"/', $headers['signature-input'], $matches);
$keyId = ($found) ? $matches[1] : '';
if (!$keyId) {
return $result;
}
$found = preg_match('/alg="(.*?)"/', $headers['signature-input'], $matches);
$alg = ($found) ? $matches[1] : null;
$keyInfo = self::get_key($key, $keytype, $keyId);
if (!$keyInfo) {
return $result;
}
$publicKey = $keyInfo['public_key'];
$messageSigner = new HttpMessageSigner();
$messageSigner->setPublicKey($publicKey);
$messageSigner->setAlgorithm($alg);
$messageSigner->setKeyId($keyId);
$messageSigner->setNonce(preg_match('/nonce="(.*?)"/', $headers['signature-input'], $matches) ? $matches[1] : '');
$messageSigner->setTag(preg_match('/tag="(.*?)"/', $headers['signature-input'], $matches) ? $matches[1] : '');
$messageSigner->setCreated(preg_match('/created=([0-9]+)/', $headers['signature-input'], $matches) ? $matches[1] : '');
$messageSigner->setExpires(preg_match('/expires=([0-9]+)/', $headers['signature-input'], $matches) ? $matches[1] : '');
try {
$verified = $messageSigner->verifyRequest(App::$request);
if (!$verified) {
btlogger('RFC9421: Unable to verify request: ' . print_r($headers, true), LOGGER_DATA);
}
}
catch (\Exception $exception) {
btlogger($exception->getMessage(), LOGGER_DATA);
$verified = false;
}
logger('verified (RFC9421): ' . (($verified) ? 'true' : 'false'), LOGGER_DEBUG);
return [
'signer' => $keyId,
'portable_id' => $keyInfo['portable_id'] ?? '',
'header_signed' => true,
'header_valid' => $verified,
'content_signed' => array_key_exists('content-digest', $headers),
'content_valid' => $verified
];
}
if (is_array($body)) {
btlogger('body is array:' . print_r($body, true));

View File

@@ -4,6 +4,7 @@ namespace Zotlabs\Web;
use App;
use Zotlabs\Extend\Route;
use Zotlabs\Render\Theme;
use Zotlabs\Lib\Config;
use Exception;
@@ -33,6 +34,7 @@ use Exception;
* so within the module init and/or post functions and then invoke killme() to terminate
* further processing.
*/
class Router {
private $modname = '';
@@ -49,7 +51,7 @@ class Router {
$module = App::$module;
$modname = "Zotlabs\\Module\\" . ucfirst($module);
if(strlen($module)) {
if(!empty($module)) {
/*
* We will always have a module name.
@@ -57,11 +59,11 @@ class Router {
*/
$routes = Route::get();
if($routes) {
if ($routes) {
foreach($routes as $route) {
if(is_array($route) && file_exists($route[0]) && strtolower($route[1]) === $module) {
if (is_array($route) && file_exists($route[0]) && strtolower($route[1]) === $module) {
include_once($route[0]);
if(class_exists($modname)) {
if (class_exists($modname)) {
$this->controller = new $modname;
$this->module_loaded = true;
}
@@ -71,14 +73,14 @@ class Router {
// legacy plugins - this can be removed when they have all been converted
if(! ($this->module_loaded)) {
if(is_array(App::$plugins) && in_array($module, App::$plugins) && file_exists("addon/{$module}/{$module}.php")) {
if (!$this->module_loaded) {
if (is_array(App::$plugins) && in_array($module, App::$plugins) && file_exists("addon/{$module}/{$module}.php")) {
include_once("addon/{$module}/{$module}.php");
if(class_exists($modname)) {
if (class_exists($modname)) {
$this->controller = new $modname;
$this->module_loaded = true;
}
elseif(function_exists($module . '_module')) {
elseif (function_exists($module . '_module')) {
$this->module_loaded = true;
}
}
@@ -89,7 +91,7 @@ class Router {
* Otherwise, look for the standard program module
*/
if(! ($this->module_loaded)) {
if (!$this->module_loaded) {
try {
$filename = 'Zotlabs/SiteModule/'. ucfirst($module). '.php';
if(file_exists($filename)) {
@@ -105,15 +107,16 @@ class Router {
$this->module_loaded = true;
}
}
if(! $this->module_loaded)
if (!$this->module_loaded) {
throw new Exception('Module not found');
}
}
catch(Exception $e) {
if(file_exists("mod/site/{$module}.php")) {
catch (Exception $e) {
if (file_exists("mod/site/{$module}.php")) {
include_once("mod/site/{$module}.php");
$this->module_loaded = true;
}
elseif(file_exists("mod/{$module}.php")) {
elseif (file_exists("mod/{$module}.php")) {
include_once("mod/{$module}.php");
$this->module_loaded = true;
}
@@ -125,6 +128,7 @@ class Router {
'installed' => $this->module_loaded,
'controller' => $this->controller
];
/**
* @hooks module_loaded
* Called when a module has been successfully locate to server a URL request.
@@ -138,7 +142,8 @@ class Router {
* * \e mixed \b controller - The initialized module object
*/
call_hooks('module_loaded', $x);
if($x['installed']) {
if ($x['installed']) {
$this->module_loaded = true;
$this->controller = $x['controller'];
}
@@ -147,7 +152,7 @@ class Router {
* The URL provided does not resolve to a valid module.
*/
if(! ($this->module_loaded)) {
if (!$this->module_loaded) {
// undo the setting of a letsencrypt acme-challenge rewrite rule
// which blocks access to our .well-known routes.
@@ -155,8 +160,8 @@ class Router {
// for a custom .htaccess in the .well-known directory; but they should
// make the file read-only so letsencrypt doesn't modify it
if(strpos($_SERVER['REQUEST_URI'],'/.well-known/') === 0) {
if(file_exists('.well-known/.htaccess') && Config::Get('system','fix_apache_acme',true)) {
if (strpos($_SERVER['REQUEST_URI'],'/.well-known/') === 0) {
if (file_exists('.well-known/.htaccess') && Config::Get('system','fix_apache_acme',true)) {
rename('.well-known/.htaccess','.well-known/.htaccess.old');
}
}
@@ -166,16 +171,17 @@ class Router {
'installed' => $this->module_loaded,
'controller' => $this->controller
];
call_hooks('page_not_found',$x);
// Stupid browser tried to pre-fetch our Javascript img template.
// Don't log the event or return anything - just quietly exit.
if((x($_SERVER, 'QUERY_STRING')) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) {
if (!empty($_SERVER['QUERY_STRING']) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) {
killme();
}
if(Config::Get('system','log_404',true)) {
if (Config::Get('system','log_404',true)) {
logger("Module {$module} not found.", LOGGER_DEBUG, LOG_WARNING);
logger('index.php: page not found: ' . $_SERVER['REQUEST_URI']
. ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: '
@@ -206,7 +212,7 @@ class Router {
* Call module functions
*/
if($this->module_loaded) {
if ($this->module_loaded) {
App::$page['page_title'] = App::$module;
$placeholder = '';
@@ -218,13 +224,18 @@ class Router {
* to over-ride them.
*/
$arr = array('init' => true, 'replace' => false);
$arr = [
'init' => true,
'replace' => false
];
call_hooks(App::$module . '_mod_init', $arr);
if(! $arr['replace']) {
if($this->controller && method_exists($this->controller,'init')) {
if (!$arr['replace']) {
if ($this->controller && method_exists($this->controller,'init')) {
$this->controller->init();
}
elseif(function_exists(App::$module . '_init')) {
elseif (function_exists(App::$module . '_init')) {
$func = App::$module . '_init';
$func();
}
@@ -250,52 +261,58 @@ class Router {
* load current theme info
*/
$current_theme = \Zotlabs\Render\Theme::current();
$current_theme = Theme::current();
$theme_info_file = 'view/theme/' . $current_theme[0] . '/php/theme.php';
if (file_exists($theme_info_file)){
require_once($theme_info_file);
}
if(function_exists(str_replace('-', '_', $current_theme[0]) . '_init')) {
if (function_exists(str_replace('-', '_', $current_theme[0]) . '_init')) {
$func = str_replace('-', '_', $current_theme[0]) . '_init';
$func();
}
elseif (x(App::$theme_info, 'extends') && file_exists('view/theme/' . App::$theme_info['extends'] . '/php/theme.php')) {
elseif (!empty(App::$theme_info['extends']) && file_exists('view/theme/' . App::$theme_info['extends'] . '/php/theme.php')) {
require_once('view/theme/' . App::$theme_info['extends'] . '/php/theme.php');
if(function_exists(str_replace('-', '_', App::$theme_info['extends']) . '_init')) {
if (function_exists(str_replace('-', '_', App::$theme_info['extends']) . '_init')) {
$func = str_replace('-', '_', App::$theme_info['extends']) . '_init';
$func();
}
}
if(($_SERVER['REQUEST_METHOD'] === 'POST') && (! App::$error) && (! x($_POST, 'auth-params'))) {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !App::$error && empty($_POST['auth-params'])) {
call_hooks(App::$module . '_mod_post', $_POST);
if($this->controller && method_exists($this->controller,'post')) {
if ($this->controller && method_exists($this->controller, 'post')) {
$this->controller->post();
}
elseif(function_exists(App::$module . '_post')) {
elseif (function_exists(App::$module . '_post')) {
$func = App::$module . '_post';
$func();
}
}
if(! App::$error) {
$arr = array('content' => App::$page['content'], 'replace' => false);
if (!App::$error) {
$arr = [
'content' => App::$page['content'],
'replace' => false
];
call_hooks(App::$module . '_mod_content', $arr);
if(! $arr['replace']) {
if($this->controller && method_exists($this->controller,'get')) {
if (!$arr['replace']) {
if ($this->controller && method_exists($this->controller, 'get')) {
$arr = array('content' => $this->controller->get());
}
elseif(function_exists(App::$module . '_content')) {
elseif (function_exists(App::$module . '_content')) {
$func = App::$module . '_content';
$arr = array('content' => $func());
}
}
call_hooks(App::$module . '_mod_aftercontent', $arr);
App::$page['content'] = ((isset($arr['replace'])) ? $arr['content'] : App::$page['content'] . $arr['content']);
App::$page['content'] = ((empty($arr['replace'])) ? App::$page['content'] . $arr['content'] : $arr['content']);
}
}
}

View File

@@ -132,7 +132,7 @@ class Session {
else
logger('no session handler');
if (x($_COOKIE, 'jsdisabled')) {
if (!empty($_COOKIE['jsdisabled'])) {
setcookie(
'jsdisabled',
$_COOKIE['jsdisabled'],

View File

@@ -2,7 +2,10 @@
namespace Zotlabs\Web;
use App;
use Zotlabs\Lib\Text;
use GuzzleHttp\Psr7\Request;
class WebServer {
@@ -17,9 +20,10 @@ class WebServer {
$installed = sys_boot();
$this->createRequest();
\App::$language = get_best_language();
load_translation_table(\App::$language, !$installed);
App::$language = get_best_language();
load_translation_table(App::$language, !$installed);
/**
@@ -33,8 +37,8 @@ class WebServer {
*
*/
if(\App::$session) {
\App::$session->start();
if(App::$session) {
App::$session->start();
}
else {
session_start();
@@ -53,13 +57,13 @@ class WebServer {
unset($_SESSION['language']);
}
if ((x($_SESSION, 'language')) && ($_SESSION['language'] !== \App::$language)) {
\App::$language = $_SESSION['language'];
if ((!empty($_SESSION['language'])) && ($_SESSION['language'] !== App::$language)) {
App::$language = $_SESSION['language'];
load_translation_table(\App::$language);
}
if (x($_GET,'zid') && $installed) {
\App::$query_string = strip_zids(\App::$query_string);
if (!empty($_GET['zid']) && $installed) {
App::$query_string = strip_zids(App::$query_string);
if(! local_channel()) {
if (!isset($_SESSION['my_address'])) {
$_SESSION['my_address'] = Text::escape_tags($_GET['zid']);
@@ -71,26 +75,28 @@ class WebServer {
}
}
if (x($_GET,'zat') && $installed) {
\App::$query_string = strip_zats(\App::$query_string);
if (!empty($_GET['zat']) && $installed) {
App::$query_string = strip_zats(App::$query_string);
if(! local_channel()) {
zat_init();
}
}
if (x($_REQUEST,'owt') && $installed) {
if (!empty($_REQUEST['owt']) && $installed) {
$token = $_REQUEST['owt'];
\App::$query_string = strip_query_param(\App::$query_string,'owt');
App::$query_string = strip_query_param(App::$query_string,'owt');
owt_init($token);
}
if((x($_SESSION, 'authenticated')) || (x($_POST, 'auth-params')) || (\App::$module === 'login'))
if(!empty($_SESSION['authenticated']) || !empty($_POST['auth-params']) || App::$module === 'login') {
require('include/auth.php');
}
if (!$installed) {
/* Allow an exception for the view module so that pcss will be interpreted during installation */
if(\App::$module != 'view')
\App::$module = 'setup';
if(App::$module != 'view') {
App::$module = 'setup';
}
}
else {
@@ -111,17 +117,7 @@ class WebServer {
$Router->Dispatch();
// TODO: this is not used for anything atm and messes up comanche templates by adding some javascript
//$this->set_homebase();
// now that we've been through the module content, see if the page reported
// a permission problem and if so, a 403 response would seem to be in order.
if(isset($_SESSION['sysmsg']) && is_array($_SESSION['sysmsg']) && stristr(implode("", $_SESSION['sysmsg']), t('Permission denied'))) {
header($_SERVER['SERVER_PROTOCOL'] . ' 403 ' . t('Permission denied.'));
}
call_hooks('page_end', \App::$page['content']);
call_hooks('page_end', App::$page['content']);
construct_page();
@@ -129,14 +125,49 @@ class WebServer {
}
public function createRequest()
{
$input = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$input = file_get_contents('php://input');
}
$headers = [];
if (isset($_SERVER['CONTENT_TYPE'])) {
$headers['content-type'] = $_SERVER['CONTENT_TYPE'];
}
if (isset($_SERVER['CONTENT_LENGTH'])) {
$headers['content-length'] = $_SERVER['CONTENT_LENGTH'];
}
foreach ($_SERVER as $k => $v) {
if (str_starts_with($k, 'HTTP_')) {
$field = str_replace('_', '-', strtolower(substr($k, 5)));
$headers[$field] = $v;
}
}
App::$request = new Request(
$_SERVER['REQUEST_METHOD'],
z_root() . ((App::$originalRequest) ?? $_SERVER['REQUEST_URI']),
$headers,
$input
);
}
private function initialise_content() {
/* initialise content region */
if(! x(\App::$page, 'content'))
\App::$page['content'] = '';
if(empty(App::$page['content'])) {
App::$page['content'] = '';
}
call_hooks('page_content_top', \App::$page['content']);
call_hooks('page_content_top', App::$page['content']);
}
@@ -148,44 +179,24 @@ class WebServer {
* to all protocol drivers; thus doing it here avoids duplication.
*/
if (( \App::$module === 'channel' ) && argc() > 1) {
\App::$channel_links = [
if (App::$module === 'channel' && argc() > 1) {
App::$channel_links = [
[
'rel' => 'lrdd',
'type' => 'application/xrd+xml',
'url' => z_root() . '/xrd?f=&uri=acct%3A' . argv(1) . '%40' . \App::get_hostname()
'url' => z_root() . '/xrd?f=&uri=acct%3A' . argv(1) . '%40' . App::get_hostname()
],
[
'rel' => 'jrd',
'type' => 'application/jrd+json',
'url' => z_root() . '/.well-known/webfinger?f=&resource=acct%3A' . argv(1) . '%40' . \App::get_hostname()
'url' => z_root() . '/.well-known/webfinger?f=&resource=acct%3A' . argv(1) . '%40' . App::get_hostname()
],
];
$x = [ 'channel_address' => argv(1), 'channel_links' => \App::$channel_links ];
$x = [ 'channel_address' => argv(1), 'channel_links' => App::$channel_links ];
call_hooks('channel_links', $x );
\App::$channel_links = $x['channel_links'];
App::$channel_links = $x['channel_links'];
header('Link: ' . \App::get_channel_links());
}
}
private function set_homebase() {
// If you're just visiting, let javascript take you home
if(x($_SESSION, 'visitor_home')) {
$homebase = $_SESSION['visitor_home'];
}
elseif(local_channel()) {
$homebase = z_root() . '/channel/' . \App::$channel['channel_address'];
}
if(isset($homebase)) {
\App::$page['content'] .= '<script>var homebase = "' . $homebase . '";</script>';
}
}
}

View File

@@ -16,7 +16,7 @@ class Activity_order {
return '';
if(! feature_enabled(local_channel(),'order_tab')) {
set_pconfig(local_channel(), 'mod_network', 'order', 0);
set_pconfig(local_channel(), 'mod_network', 'order', 'created');
return '';
}
@@ -26,17 +26,17 @@ class Activity_order {
if(x($_GET, 'order')) {
switch($_GET['order']){
case 'post':
case 'created':
$postord_active = 'active';
set_pconfig(local_channel(), 'mod_network', 'order', 1);
set_pconfig(local_channel(), 'mod_network', 'order', 'created');
break;
case 'comment':
case 'commented':
$commentord_active = 'active';
set_pconfig(local_channel(), 'mod_network', 'order', 0);
set_pconfig(local_channel(), 'mod_network', 'order', 'commented');
break;
case 'unthreaded':
$unthreaded_active = 'active';
set_pconfig(local_channel(), 'mod_network', 'order', 2);
set_pconfig(local_channel(), 'mod_network', 'order', 'unthreaded');
break;
default:
$commentord_active = 'active';
@@ -44,19 +44,19 @@ class Activity_order {
}
}
else {
$order = get_pconfig(local_channel(), 'mod_network', 'order', 0);
$order = get_pconfig(local_channel(), 'mod_network', 'order', 'created');
switch($order) {
case 0:
case 'commented':
$commentord_active = 'active';
break;
case 1:
case 'created':
$postord_active = 'active';
break;
case 2:
case 'unthreaded':
$unthreaded_active = 'active';
break;
default:
$commentord_active = 'active';
$postord_active = 'active';
}
}
@@ -90,26 +90,26 @@ class Activity_order {
// tabs
$tabs = [];
$tabs[] = [
'label' => t('Posted Date'),
'icon' => '',
'url'=>z_root() . '/' . $cmd . '?order=created' . $filter,
'sel'=> $postord_active,
'title' => t('Order by last posted date'),
];
$tabs[] = [
'label' => t('Commented Date'),
'icon' => '',
'url'=>z_root() . '/' . $cmd . '?f=&order=comment' . $filter,
'url'=>z_root() . '/' . $cmd . '?order=commented' . $filter,
'sel'=> $commentord_active,
'title' => t('Order by last commented date'),
];
$tabs[] = [
'label' => t('Posted Date'),
'icon' => '',
'url'=>z_root() . '/' . $cmd . '?f=&order=post' . $filter,
'sel'=> $postord_active,
'title' => t('Order by last posted date'),
];
$tabs[] = array(
'label' => t('Date Unthreaded'),
'icon' => '',
'url' => z_root() . '/' . $cmd . '?f=&order=unthreaded' . $filter,
'url' => z_root() . '/' . $cmd . '?order=unthreaded' . $filter,
'sel' => $unthreaded_active,
'title' => t('Order unthreaded by date'),
);

View File

@@ -59,6 +59,9 @@ class Album {
//edit album name
$album_edit = null;
$ph = photo_factory('');
$phototypes = $ph->supportedTypes();
$photos = array();
if($r) {
$twist = 'rotright';

View File

@@ -8,6 +8,7 @@
namespace Zotlabs\Widget;
use App;
class Archive {
@@ -15,37 +16,40 @@ class Archive {
$o = '';
if(! \App::$profile_uid) {
if (!App::$profile) {
return '';
}
$uid = \App::$profile_uid;
$uid = App::$profile['profile_uid'];
if(! feature_enabled($uid,'archives'))
if (!feature_enabled($uid, 'archives')) {
return '';
}
if(! perm_is_allowed($uid,get_observer_hash(),'view_stream'))
if (!perm_is_allowed($uid,get_observer_hash(),'view_stream')) {
return '';
}
$wall = ((array_key_exists('wall', $arr)) ? intval($arr['wall']) : 0);
$wall = ((array_key_exists('articles', $arr)) ? 2 : $wall);
$style = ((array_key_exists('style', $arr)) ? $arr['style'] : 'select');
$showend = ((get_pconfig($uid,'system','archive_show_end_date')) ? true : false);
$mindate = get_pconfig($uid,'system','archive_mindate');
$visible_years = get_pconfig($uid,'system','archive_visible_years',5);
$showend = ((get_pconfig($uid, 'system', 'archive_show_end_date')) ? true : false);
$mindate = get_pconfig($uid, 'system', 'archive_mindate');
$visible_years = get_pconfig($uid, 'system', 'archive_visible_years', 5);
$url = z_root() . '/' . \App::$cmd;
$url = z_root() . '/' . App::$module . '/' . App::$profile['channel_address'];
$ret = list_post_dates($uid,$wall,$mindate);
if(! count($ret))
if (empty($ret)) {
return '';
}
$cutoff_year = intval(datetime_convert('',date_default_timezone_get(),'now','Y')) - $visible_years;
$cutoff = ((array_key_exists($cutoff_year,$ret))? true : false);
$cutoff = ((array_key_exists($cutoff_year,$ret)) ? true : false);
$o = replace_macros(get_markup_template('posted_date_widget.tpl'),array(
return replace_macros(get_markup_template('posted_date_widget.tpl'), [
'$title' => t('Archives'),
'$size' => $visible_years,
'$cutoff_year' => $cutoff_year,
@@ -54,8 +58,8 @@ class Archive {
'$style' => $style,
'$showend' => $showend,
'$dates' => $ret
));
return $o;
]);
}
}

View File

@@ -223,7 +223,7 @@ class Channel_activities {
$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="opacity-75">' . $rr['xchan_addr'] . '</small></div>',
'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
];

View File

@@ -21,14 +21,25 @@ class Helpindex {
$this->determine_help_language();
$this->find_help_file('toc', $this->lang['language']);
if (! empty($this->file_name)) {
$this->contents = translate_projectname(
file_get_contents($this->file_name)
);
}
$sections = [];
$this->contents = '';
$tpl = get_markup_template('widget.tpl');
return replace_macros($tpl, [ '$widget' => $this ]);
if (!empty($this->file_name) && is_readable($this->file_name)) {
$json = file_get_contents($this->file_name);
$this->contents = translate_projectname($json);
$decoded = json_decode($json, true);
if (is_array($decoded)) {
$sections = $decoded;
}
}
$tpl = get_markup_template('help-index.tpl');
return replace_macros($tpl, [
'$title' => t('Documentation Index'),
'$sections' => $sections,
'$contents' => $this->contents
]);
}
public function title(): string {

View File

@@ -28,6 +28,8 @@ class Messages {
intval(TERM_FILE)
);
$file_tags = [];
if ($r) {
foreach($r as $rr) {
$file_tags[] = $rr['term'];
@@ -42,14 +44,14 @@ class Messages {
'$feature_file' => feature_enabled(local_channel(), 'filing'),
'$file_tags' => $file_tags,
'$strings' => [
'messages_title' => t('Public and restricted messages'),
'direct_messages_title' => t('Direct messages'),
'starred_messages_title' => t('Starred messages'),
'messages_title' => t('Public and restricted conversations'),
'direct_messages_title' => t('Private conversations'),
'starred_messages_title' => t('Starred conversations'),
'filed_messages_title' => t('Filed messages'),
'notice_messages_title' => t('Notices'),
'notice_messages_title' => t('Notifications'),
'loading' => t('Loading'),
'empty' => t('No messages'),
'unseen_count' => t('Unseen'),
'empty' => t('No conversations'),
'unseen_count' => t('Unseen reactions'),
'filter' => t('Filter by name or address'),
'file_filter' => t('Filter by file name')
]
@@ -84,8 +86,6 @@ class Messages {
$entries = [];
$limit = 30;
$order_sql = 'i.created DESC';
$dummy_order_sql = '';
$filter_sql = '';
$loadtime = (($offset) ? $_SESSION['messages_loadtime'] : datetime_convert());
$vnotify = get_pconfig(local_channel(), 'system', 'vnotify', -1);
@@ -101,14 +101,18 @@ class Messages {
$vnotify_sql_i = " AND i.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
$filter_sql = '';
if($type !== 'filed' && $author) {
$filter_sql = " AND (i.owner_xchan = '" . protect_sprintf(dbesc($author)) . "') ";
}
$filed_filter_sql = '';
if($type === 'filed' && $file) {
$filed_filter_sql = " AND (term.term = '" . protect_sprintf(dbesc($file)) . "') ";
}
$dummy_order_sql = '';
switch($type) {
case 'direct':
$type_sql = ' AND i.item_private = 2 AND i.item_thread_top = 1 ';
@@ -305,11 +309,13 @@ class Messages {
$author_sql = '';
if($author_url) {
$author_sql = " AND url = '" . protect_sprintf(dbesc($author_url)) . "' ";
$author_sql = " AND notify.url = '" . protect_sprintf(dbesc($author_url)) . "' ";
}
$notices = q("SELECT * FROM notify WHERE uid = %d $author_sql
ORDER BY created DESC LIMIT $limit OFFSET $offset",
$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",
intval(local_channel())
);
@@ -325,7 +331,7 @@ class Messages {
}
$entries[$i]['author_name'] = $notice['xname'];
$entries[$i]['author_addr'] = $notice['url'];
$entries[$i]['author_addr'] = $notice['xchan_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']);

View File

@@ -8,11 +8,12 @@
namespace Zotlabs\Widget;
use App;
class Notifications {
function widget($arr) {
$channel = \App::get_channel();
$notifications = [];
if(local_channel()) {
@@ -21,36 +22,36 @@ class Notifications {
'icon' => 'grid-3x3',
'severity' => 'secondary',
'label' => t('Network'),
'title' => t('New network activity notifications'),
'title' => t('Unseen network activity'),
'viewall' => [
'url' => 'network',
'label' => t('Network stream')
],
'markall' => [
'label' => t('Mark all notifications read')
'label' => t('Mark all seen')
],
'filter' => [
'posts_label' => t('Show new posts only'),
'posts_label' => t('Conversation starters'),
'name_label' => t('Filter by name or address')
]
];
$channel = App::get_channel();
$notifications[] = [
'type' => 'home',
'icon' => 'house',
'severity' => 'danger',
'label' => t('Home'),
'title' => t('New home activity notifications'),
'label' => t('Channel'),
'title' => t('Unseen channel activity'),
'viewall' => [
'url' => 'channel/' . $channel['channel_address'],
'label' => t('Home stream')
'label' => t('Channel stream')
],
'markall' => [
'label' => t('Mark all notifications seen')
'label' => t('Mark all seen')
],
'filter' => [
'posts_label' => t('Show new posts only'),
'posts_label' => t('Conversation starters'),
'name_label' => t('Filter by name or address')
]
];
@@ -59,17 +60,17 @@ class Notifications {
'type' => 'dm',
'icon' => 'envelope',
'severity' => 'danger',
'label' => t('Direct Messages'),
'title' => t('New direct messages notifications'),
'label' => t('Private'),
'title' => t('Unseen private activity'),
'viewall' => [
'url' => 'network/?dm=1',
'label' => t('Direct messages stream')
'label' => t('Private stream')
],
'markall' => [
'label' => t('Mark all notifications read')
'label' => t('Mark all seen')
],
'filter' => [
'posts_label' => t('Show new posts only'),
'posts_label' => t('Conversation starters'),
'name_label' => t('Filter by name or address')
]
];
@@ -79,13 +80,13 @@ class Notifications {
'icon' => 'calendar-date',
'severity' => 'secondary',
'label' => t('Events'),
'title' => t('New events notifications'),
'title' => t('Unseen events activity'),
'viewall' => [
'url' => 'cdav/calendar',
'label' => t('View events')
],
'markall' => [
'label' => t('Mark all events seen')
'label' => t('Mark all seen')
]
];
@@ -94,10 +95,10 @@ class Notifications {
'icon' => 'people',
'severity' => 'danger',
'label' => t('New Connections'),
'title' => t('New connections notifications'),
'title' => t('New connections'),
'viewall' => [
'url' => 'connections',
'label' => t('View all connections')
'label' => t('View all')
]
];
@@ -106,34 +107,46 @@ class Notifications {
'icon' => 'folder',
'severity' => 'danger',
'label' => t('Files'),
'title' => t('New files notifications'),
'title' => t('Useen files activity'),
];
$notifications[] = [
'type' => 'notify',
'icon' => 'exclamation-circle',
'severity' => 'danger',
'label' => t('Notices'),
'title' => t('Notices'),
'label' => t('Notifications'),
'title' => t('Unseen notifications'),
'viewall' => [
'url' => 'notifications/system',
'label' => t('View all notices')
'label' => t('View all')
],
'markall' => [
'label' => t('Mark all notices seen')
'label' => t('Mark all seen')
]
];
$notifications[] = [
'type' => 'forums',
'icon' => 'chat-quote',
'severity' => 'secondary',
'label' => t('Forums'),
'title' => t('Forums'),
'filter' => [
'name_label' => t('Filter by name or address')
]
];
$forums = get_forum_channels(local_channel());
foreach($forums as $forum) {
$notifications[] = [
'type' => 'forum_' . $forum['abook_id'],
'icon' => 'chat-quote',
'severity' => 'secondary',
'label' => $forum['xchan_name'],
'title' => t('Unseen forum activity'),
'filter' => [
'posts_label' => t('Conversation starters'),
'name_label' => t('Filter by name or address')
],
'viewall' => [
'url' => 'network?pf=1&cid=' . $forum['abook_id'],
'label' => t('View all')
],
'markall' => [
'label' => t('Mark all seen')
],
];
}
}
if(local_channel() && is_site_admin()) {
@@ -142,7 +155,7 @@ class Notifications {
'icon' => 'person-exclamation',
'severity' => 'danger',
'label' => t('Registrations'),
'title' => t('New registrations notifications'),
'title' => t('Unseen registration activity'),
];
}
@@ -152,33 +165,30 @@ class Notifications {
'icon' => 'globe',
'severity' => 'secondary',
'label' => t('Public Stream'),
'title' => t('New public stream notifications'),
'title' => t('Unseen public stream activity'),
'viewall' => [
'url' => 'pubstream',
'label' => t('Public stream')
],
/*
'markall' => [
'label' => t('Mark all notifications seen')
],
*/
'filter' => [
'posts_label' => t('Show new posts only'),
'posts_label' => t('Conversation starters'),
'name_label' => t('Filter by name or address')
]
],
'markall' => [
'label' => t('Mark all seen')
],
];
}
$o = replace_macros(get_markup_template('notifications_widget.tpl'), [
return replace_macros(get_markup_template('notifications_widget.tpl'), [
'$notifications' => $notifications,
'$no_notifications' => t('Sorry, you have got no notifications at the moment'),
'$loading' => t('Loading'),
'$sys_only' => empty($arr['sys_only']) ? 0 : 1
'$sys_only' => empty($arr['sys_only']) ? 0 : 1,
'$invert_notifications_order' => get_pconfig(local_channel(), 'system', 'invert_notifications_order', false),
'$count_limit' => get_pconfig(local_channel(), 'system', 'notifications_count_limit', 100)
]);
return $o;
}
}

View File

@@ -67,19 +67,19 @@ class Pinned {
$attend = null;
$canvote = false;
$conv_responses = [];
$response_verbs[] = 'like';
if(in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) {
$conv_responses['attendyes'] = [ 'title' => t('Attending','title') ];
$conv_responses['attendno'] = [ 'title' => t('Not attending','title') ];
$conv_responses['attendmaybe'] = [ 'title' => t('Might attend','title') ];
if($commentable && $observer) {
$attend = [ t('I will attend'), t('I will not attend'), t('I might attend') ];
$isevent = true;
}
if(feature_enabled(\App::$profile['profile_uid'],'dislike')) {
$response_verbs[] = 'dislike';
}
$this->activity($item, $conv_responses);
$response_verbs[] = 'announce';
if ($item['obj_type'] === 'Question') {
$response_verbs[] = 'answer';
}
$responses = get_responses($response_verbs, $item);
$verified = (intval($item['item_verified']) ? t('Message signature validated') : '');
$forged = ((! intval($item['item_verified']) && $item['sig']) ? t('Message signature incorrect') : '');
@@ -110,6 +110,9 @@ class Pinned {
'text' => strip_tags($body['html']),
'id' => $item['id'],
'mids' => json_encode([ $midb64 ]),
'mid' => $item['uuid'],
'rawmid' => $item['mid'],
'parent' => $item['parent'],
'isevent' => $isevent,
'attend' => $attend,
'conlabels' => [],
@@ -151,7 +154,7 @@ class Pinned {
'hide' => (! $is_new && isset($observer['xchan_hash']) && $observer['xchan_hash'] != $owner['xchan_hash'] ? t("Don't show") : ''),
// end toolbar buttons
'modal_dismiss' => t('Close'),
'responses' => $conv_responses,
'responses' => $responses,
'author_id' => (($author['xchan_addr']) ? $author['xchan_addr'] : $author['xchan_url'])
];
@@ -193,74 +196,14 @@ class Pinned {
if(empty($mids_list))
return [];
$r = q("SELECT * FROM item WHERE uuid IN ( '%s' ) AND uid = %d AND id = parent AND item_private = 0 ORDER BY created DESC",
$r = q("SELECT parent AS item_id FROM item WHERE uuid IN ( '%s' ) AND uid = %d AND id = parent AND item_private = 0",
dbesc(implode(",", $mids_list)),
intval($this->uid)
);
if($r)
return $r;
return [];
return items_by_parent_ids($r, blog_mode: true);
}
/*
* @brief List activities on item
*
* @param array $item
* @param array $conv_responses
* @return array
*
*/
private function activity($item, &$conv_responses) {
foreach(array_keys($conv_responses) as $verb) {
$verb_sql = '';
switch($verb) {
case 'like':
$verb_sql = " AND verb IN ('Like', '" . ACTIVITY_LIKE . "') ";
break;
case 'dislike':
$verb_sql = " AND verb IN ('Dislike', '" . ACTIVITY_DISLIKE . "') ";
break;
case 'attendyes':
$verb_sql = " AND verb IN ('Accept', '" . ACTIVITY_ATTEND . "') ";
break;
case 'attendno':
$verb_sql = " AND verb IN ('Reject', '" . ACTIVITY_ATTENDNO . "') ";
break;
case 'attendmaybe':
$verb_sql = " AND verb IN ('TentativeAccept', '" . ACTIVITY_ATTENDMAYBE . "') ";
break;
default:
break;
}
$r = q("SELECT * FROM item WHERE parent = %d AND id <> parent $verb_sql AND item_deleted = 0",
intval($item['id'])
);
if(! $r) {
unset($conv_responses[$verb]);
continue;
}
$conv_responses[$verb]['count'] = count($r);
$conv_responses[$verb]['button'] = get_response_button_text($verb, $conv_responses[$verb]['count']);
foreach($r as $rr) {
$author = q("SELECT * FROM xchan WHERE xchan_hash = '%s' LIMIT 1",
dbesc($rr['author_xchan'])
);
$name = (($author && $author[0]['xchan_name']) ? $author[0]['xchan_name'] : t('Unknown'));
$conv_responses[$verb]['list'][] = (($rr['author_xchan'] && $author && $author[0]['xchan_photo_s']) ?
'<a class="dropdown-item" href="' . chanlink_hash($rr['author_xchan']) . '">' . '<img class="menu-img-1" src="' . zid($author[0]['xchan_photo_s']) . '" alt="' . urlencode($name) . '" /> ' . $name . '</a>' :
'<a class="dropdown-item" href="#" class="disabled">' . $name . '</a>'
);
}
}
$conv_responses['count'] = count($conv_responses);
}
}

View File

@@ -66,6 +66,10 @@ class Portfolio {
//edit album name
$album_edit = null;
$ph = photo_factory('');
$phototypes = $ph->supportedTypes();
$photos = array();
if($r) {
$twist = 'rotright';

View File

@@ -12,7 +12,7 @@ class Zot6Handler implements IHandler {
}
function Rekey($sender, $data, $hub) {
return self::reply_rekey_request($sender, $data, $hub);
return self::rekey_request($sender, $data, $hub);
}
function Refresh($sender, $recipients, $hub, $force) {

427
boot.php
View File

@@ -36,6 +36,9 @@ use Zotlabs\Daemon\Master;
use Zotlabs\Lib\DB_Upgrade;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Config;
use Zotlabs\Render\SmartyTemplate;
use Zotlabs\Render\Theme;
use Zotlabs\Web\HttpMeta;
require_once('vendor/autoload.php');
@@ -46,6 +49,7 @@ require_once('include/text.php');
require_once('include/datetime.php');
require_once('include/language.php');
require_once('include/nav.php');
require_once('include/observer.php');
require_once('include/permissions.php');
require_once('include/features.php');
require_once('include/taxonomy.php');
@@ -66,10 +70,10 @@ require_once('include/security.php');
define('PLATFORM_NAME', 'hubzilla');
define('STD_VERSION', '10.2.2');
define('STD_VERSION', '10.6');
define('ZOT_REVISION', '6.0');
define('DB_UPDATE_VERSION', 1263);
define('DB_UPDATE_VERSION', 1264);
define('PROJECT_BASE', __DIR__);
@@ -133,6 +137,12 @@ define('PNG_QUALITY', 8);
*/
define('WEBP_QUALITY', 80);
/**
* App::$config['system']['avif_quality'] from 1 (maximum compressed) to 100 (uncompressed)
*/
define('AVIF_QUALITY', 80);
/**
* Language detection parameters
*/
@@ -175,6 +185,13 @@ if (!defined('STORAGE_DEFAULT_PERMISSIONS')) {
*/
define('MAX_IMAGE_LENGTH', -1);
/**
* Those are the current limits we can store in the DB
*/
define('MAX_FILENAME_LENGTH', 191);
define('MAX_FOLDER_LENGTH', 64);
/**
* log levels
@@ -640,10 +657,12 @@ function sys_boot(): bool {
// allow somebody to set some initial settings just in case they can't
// install without special fiddling
if (App::$install && file_exists('.htpreconfig.php'))
if (App::$install && file_exists('.htpreconfig.php')) {
// @phpstan-ignore include.fileNotFound
@include('.htpreconfig.php');
}
if (array_key_exists('default_timezone', get_defined_vars())) {
if (isset($default_timezone)) {
App::$config['system']['timezone'] = $default_timezone;
}
@@ -654,7 +673,6 @@ function sys_boot(): bool {
App::$timezone = ((App::$config['system']['timezone']) ? App::$config['system']['timezone'] : 'UTC');
date_default_timezone_set(App::$timezone);
if (!defined('DEFAULT_PLATFORM_ICON')) {
define('DEFAULT_PLATFORM_ICON', '/images/hz-32.png');
}
@@ -756,7 +774,8 @@ class miniApp {
*
*/
class App {
public static $request = null; // psr7 request
public static $originalRequest = null; // not used currently, will be needed when we start signing messages with RFC 9421
public static $install = false; // true if we are installing the software
public static $account = null; // account record of the logged-in account
public static $channel = null; // channel record of the current channel of the logged-in account
@@ -854,6 +873,9 @@ class App {
*/
public static $template_engine_instance = [];
/// Page layouts for comanche
public static array $page_layouts = [];
private static $ldelim = [
'internal' => '',
'smarty3' => '{{'
@@ -896,46 +918,43 @@ class App {
set_include_path(
'include' . PATH_SEPARATOR
. 'library' . PATH_SEPARATOR
. 'library/langdet' . PATH_SEPARATOR
. '.');
self::$scheme = 'http';
if (x($_SERVER, 'HTTPS') && $_SERVER['HTTPS'])
self::$scheme = 'https';
elseif (x($_SERVER, 'SERVER_PORT') && (intval($_SERVER['SERVER_PORT']) == 443))
self::$scheme = 'https';
if (x($_SERVER, 'SERVER_NAME')) {
if (!empty($_SERVER['HTTPS'])) {
self::$scheme = 'https';
}
elseif (!empty($_SERVER['SERVER_PORT']) && intval($_SERVER['SERVER_PORT']) == 443) {
self::$scheme = 'https';
}
if (!empty($_SERVER['SERVER_NAME'])) {
self::$hostname = punify($_SERVER['SERVER_NAME']);
if (x($_SERVER, 'SERVER_PORT') && $_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443)
if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
self::$hostname .= ':' . $_SERVER['SERVER_PORT'];
}
/*
* Figure out if we are running at the top of a domain
* or in a sub-directory and adjust accordingly
*/
$path = trim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
if (isset($path) && strlen($path) && ($path != self::$path))
if (isset($path) && strlen($path) && ($path != self::$path)) {
self::$path = $path;
}
}
if ((x($_SERVER, 'QUERY_STRING')) && substr($_SERVER['QUERY_STRING'], 0, 2) === "q=") {
self::$query_string = str_replace(['<', '>'], ['&lt;', '&gt;'], substr($_SERVER['QUERY_STRING'], 2));
// removing trailing / - maybe a nginx problem
if (substr(self::$query_string, 0, 1) == "/")
self::$query_string = substr(self::$query_string, 1);
// trim trailing '&' if no extra args are present
self::$query_string = rtrim(self::$query_string, '&');
// change the first & to ?
self::$query_string = preg_replace('/&/', '?', self::$query_string, 1);
if (!empty($_SERVER['REQUEST_URI'])) {
self::$query_string = str_replace(['<', '>'], ['&lt;', '&gt;'], $_SERVER['REQUEST_URI']);
self::$query_string = ltrim(self::$query_string, '/');
}
if (x($_GET, 'q'))
if (!empty($_GET['q'])) {
self::$cmd = escape_tags(trim($_GET['q'], '/\\'));
}
// Serve raw files from the file system in certain cases.
$filext = pathinfo(self::$cmd, PATHINFO_EXTENSION);
@@ -985,8 +1004,9 @@ class App {
// unix style "homedir"
if ((substr(self::$cmd, 0, 1) === '~') || (substr(self::$cmd, 0, 1) === '@'))
if (substr(self::$cmd, 0, 1) === '~' || substr(self::$cmd, 0, 1) === '@') {
self::$cmd = 'channel/' . substr(self::$cmd, 1);
}
/*
* Break the URL path into C style argc/argv style arguments for our
@@ -1006,7 +1026,7 @@ class App {
self::$argv = explode('/', self::$cmd);
self::$argc = count(self::$argv);
if ((array_key_exists('0', self::$argv)) && strlen(self::$argv[0])) {
if (array_key_exists('0', self::$argv) && strlen(self::$argv[0])) {
if (strpos(self::$argv[0], '.')) {
$_REQUEST['module_format'] = substr(self::$argv[0], strpos(self::$argv[0], '.') + 1);
self::$argv[0] = substr(self::$argv[0], 0, strpos(self::$argv[0], '.'));
@@ -1014,8 +1034,9 @@ class App {
self::$module = str_replace(".", "_", self::$argv[0]);
self::$module = str_replace("-", "_", self::$module);
if (strpos(self::$module, '_') === 0)
if (strpos(self::$module, '_') === 0) {
self::$module = substr(self::$module, 1);
}
}
else {
self::$argc = 1;
@@ -1029,22 +1050,26 @@ class App {
* pagination
*/
self::$pager['page'] = ((x($_GET, 'page') && intval($_GET['page']) > 0) ? intval($_GET['page']) : 1);
self::$pager['page'] = ((!empty($_GET['page']) && intval($_GET['page']) > 0) ? intval($_GET['page']) : 1);
self::$pager['itemspage'] = 60;
self::$pager['start'] = (self::$pager['page'] * self::$pager['itemspage']) - self::$pager['itemspage'];
if (self::$pager['start'] < 0)
if (self::$pager['start'] < 0) {
self::$pager['start'] = 0;
}
self::$pager['total'] = 0;
/*
* register template engines
*/
self::$meta = new Zotlabs\Web\HttpMeta();
self::$meta = new HttpMeta();
// create an instance of the smarty template engine so we can register it.
$smarty = new Zotlabs\Render\SmartyTemplate();
$smarty = new SmartyTemplate();
/// @todo validate if this is still the desired behavior
self::register_template_engine(get_class($smarty));
@@ -1089,10 +1114,12 @@ class App {
self::$scheme = $parsed['scheme'];
self::$hostname = punify($parsed['host']);
if (x($parsed, 'port'))
if (!empty($parsed['port'])) {
self::$hostname .= ':' . $parsed['port'];
if (x($parsed, 'path'))
}
if (!empty($parsed['path'])) {
self::$path = trim($parsed['path'], '\\/');
}
}
}
@@ -1197,28 +1224,22 @@ class App {
public static function build_pagehead() {
$user_scalable = ((local_channel()) ? get_pconfig(local_channel(), 'system', 'user_scalable') : 0);
if ($user_scalable === false)
$user_scalable = 0;
$preload_images = ((local_channel()) ? get_pconfig(local_channel(), 'system', 'preload_images') : 0);
if ($preload_images === false)
$preload_images = 0;
$user_scalable = ((local_channel()) ? get_pconfig(local_channel(), 'system', 'user_scalable', 0) : 0);
$interval = ((local_channel()) ? get_pconfig(local_channel(), 'system', 'update_interval') : 80000);
if ($interval < 10000)
if ($interval < 10000) {
$interval = 80000;
$theme_color = ((local_channel()) ? get_pconfig(local_channel(), 'redbasic', 'nav_bg') : App::$theme_info['theme_color']);
if (!$theme_color) {
$theme_color = App::$theme_info['theme_color'];
}
if (!isset(self::$page['title']) && isset(self::$config['system']['sitename']))
self::$page['title'] = self::$config['system']['sitename'];
$theme_color = ((local_channel()) ? get_pconfig(local_channel(), 'redbasic', 'nav_bg', App::$theme_info['theme_color']) : App::$theme_info['theme_color']);
if (isset(self::$page['title']))
if (empty(self::$page['title']) && !empty(self::$config['system']['sitename'])) {
self::$page['title'] = self::$config['system']['sitename'];
}
if (!empty(self::$page['title'])) {
$pagemeta = ['og:title' => self::$page['title']];
}
call_hooks('page_meta', $pagemeta);
@@ -1236,7 +1257,7 @@ class App {
self::$meta->set('generator', Zotlabs\Lib\System::get_platform_name());
self::$meta->set('theme-color', $theme_color);
head_add_link(['rel' => 'shortcut icon', 'href' => head_get_icon()]);
head_add_link(['rel' => 'shortcut icon', 'href' => static::head_get_icon()]);
head_add_link(['rel' => 'apple-touch-icon', 'href' => '/images/app/hz-192.png']);
@@ -1255,7 +1276,6 @@ class App {
self::$page['htmlhead'] = replace_macros(get_markup_template('head.tpl'),
[
'$preload_images' => $preload_images,
'$user_scalable' => $user_scalable,
'$query' => urlencode(self::$query_string),
'$baseurl' => self::get_baseurl(),
@@ -1291,7 +1311,7 @@ class App {
public static function register_template_engine($class, $name = '') {
if (!$name) {
$v = get_class_vars($class);
if (x($v, "name")) {
if (!empty($v['name'])) {
$name = $v['name'];
}
}
@@ -1317,7 +1337,7 @@ class App {
}
else {
$template_engine = 'smarty3';
if (x(self::$theme, 'template_engine')) {
if (!empty(self::$theme['template_engine'])) {
$template_engine = self::$theme['template_engine'];
}
}
@@ -1359,15 +1379,29 @@ class App {
return self::$rdelim[$engine];
}
/**
* Sets the shortcut icon to be used for the current page.
*
* @param string $icon A URL to the image to use for the
* shortcut icon.
*/
public static function head_set_icon($icon) {
self::$data['pageicon'] = $icon;
}
/**
* Get the shortcut icon as an absolute URL.
*
* @return string The absolute URL of the current shortcur icon.
*/
public static function head_get_icon() {
$icon = self::$data['pageicon'];
if (!strpos($icon, '://'))
$icon = z_root() . $icon;
return $icon;
if (str_contains($icon, '://')) {
return $icon;
}
return z_root() . $icon;
}
} // End App class
@@ -1420,21 +1454,6 @@ function system_unavailable() {
}
function clean_urls() {
// if(App::$config['system']['clean_urls'])
return true;
// return false;
}
function z_path() {
$base = z_root();
if (!clean_urls())
$base .= '/?q=';
return $base;
}
/**
* @brief Returns the baseurl.
*
@@ -1446,19 +1465,6 @@ function z_root() {
return App::get_baseurl();
}
/**
* @brief Return absolute URL for given $path.
*
* @param string $path
*
* @return string
*/
function absurl($path) {
if (strpos($path, '/') === 0)
return z_path() . $path;
return $path;
}
function os_mkdir($path, $mode = 0777, $recursive = false) {
$oldumask = @umask(0);
@@ -1511,10 +1517,11 @@ function is_ajax() {
function check_config() {
$saved = Config::Get('system', 'urlverify');
if (!$saved)
if (!$saved) {
Config::Set('system', 'urlverify', bin2hex(z_root()));
}
if (($saved) && ($saved != bin2hex(z_root()))) {
if ($saved && $saved !== bin2hex(z_root())) {
// our URL changed. Do something.
$oldurl = hex2bin($saved);
@@ -1526,12 +1533,13 @@ function check_config() {
$is_ip_addr = ((preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $host)) ? true : false);
$was_ip_addr = ((preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $oldhost)) ? true : false);
// only change the url to an ip address if it was already an ip and not a dns name
if ((!$is_ip_addr) || ($is_ip_addr && $was_ip_addr)) {
if (!$is_ip_addr || ($is_ip_addr && $was_ip_addr)) {
fix_system_urls($oldurl, z_root());
Config::Set('system', 'urlverify', bin2hex(z_root()));
}
else
else {
logger('Attempt to change baseurl from a DNS name to an IP address was refused.');
}
}
// This will actually set the url to the one stored in .htconfig, and ignore what
@@ -1778,7 +1786,6 @@ function login($register = false, $form_id = 'main_login', $hiddens = false, $lo
* @brief Used to end the current process, after saving session state.
*/
function killme() {
register_shutdown_function('shutdown');
exit;
}
@@ -1795,25 +1802,6 @@ function shutdown() {
}
/**
* @brief Returns the entity id of locally logged in account or false.
*
* Returns numeric account_id if authenticated or 0. It is possible to be
* authenticated and not connected to a channel.
*
* @return int|bool account_id or false
*/
function get_account_id() {
if (isset($_SESSION['account_id']))
return intval($_SESSION['account_id']);
if (App::$account)
return intval(App::$account['account_id']);
return false;
}
/**
* @brief Returns the entity id (channel_id) of locally logged in channel or false.
*
@@ -1828,8 +1816,9 @@ function get_account_id() {
function local_channel() {
if (session_id()
&& array_key_exists('authenticated', $_SESSION) && $_SESSION['authenticated']
&& array_key_exists('uid', $_SESSION) && intval($_SESSION['uid']))
&& array_key_exists('uid', $_SESSION) && intval($_SESSION['uid'])) {
return intval($_SESSION['uid']);
}
return false;
}
@@ -1849,8 +1838,9 @@ function local_channel() {
function remote_channel() {
if (session_id()
&& array_key_exists('authenticated', $_SESSION) && $_SESSION['authenticated']
&& array_key_exists('visitor_id', $_SESSION) && $_SESSION['visitor_id'])
&& array_key_exists('visitor_id', $_SESSION) && $_SESSION['visitor_id']) {
return $_SESSION['visitor_id'];
}
return false;
}
@@ -1891,27 +1881,6 @@ function can_view_public_stream() {
*/
function notice($s) {
/*
if (!session_id()) {
return;
}
if (!isset($_SESSION['sysmsg'])) {
$_SESSION['sysmsg'] = [];
}
// ignore duplicated error messages which haven't yet been displayed
if (in_array($s, $_SESSION['sysmsg'])) {
return;
}
if (App::$interactive) {
$_SESSION['sysmsg'][] = $s;
}
*/
$hash = get_observer_hash();
$sse_id = false;
@@ -1960,25 +1929,6 @@ function notice($s) {
* @param string $s Text to display
*/
function info($s) {
/*
if (!session_id()) {
return;
}
if (!isset($_SESSION['sysmsg_info'])) {
$_SESSION['sysmsg_info'] = [];
}
// ignore duplicated error messages which haven't yet been displayed
if (in_array($s, $_SESSION['sysmsg_info'])) {
return;
}
if (App::$interactive) {
$_SESSION['sysmsg_info'][] = $s;
}
*/
$hash = get_observer_hash();
$sse_id = false;
@@ -2065,7 +2015,7 @@ function proc_run() {
return;
if (count($args) && $args[0] === 'php') {
$args[0] = ((x(App::$config, 'system')) && (x(App::$config['system'], 'php_path')) && (strlen(App::$config['system']['php_path'])) ? App::$config['system']['php_path'] : 'php');
$args[0] = ((!empty(App::$config['system']['php_path'])) ? App::$config['system']['php_path'] : 'php');
}
$args = array_map('escapeshellarg', $args);
@@ -2077,10 +2027,12 @@ function proc_run() {
proc_close(proc_open($cmd, [], $foo));
}
else {
if (Config::Get('system', 'use_proc_open'))
if (Config::Get('system', 'use_proc_open')) {
proc_close(proc_open($cmdline . " &", [], $foo));
else
}
else {
exec($cmdline . ' > /dev/null &');
}
}
}
@@ -2105,15 +2057,17 @@ function is_windows() {
* @return bool true if user is an admin
*/
function is_site_admin() {
if (!session_id())
if (!session_id()) {
return false;
}
if (isset($_SESSION['delegate']))
if (isset($_SESSION['delegate'])) {
return false;
}
if (isset($_SESSION['authenticated']) && is_array(App::$account) && (App::$account['account_roles'] & ACCOUNT_ROLE_ADMIN))
if (isset($_SESSION['authenticated']) && is_array(App::$account) && (App::$account['account_roles'] & ACCOUNT_ROLE_ADMIN)) {
return true;
}
return false;
}
@@ -2127,13 +2081,15 @@ function is_site_admin() {
*/
function is_developer() {
if (!session_id())
if (!session_id()) {
return false;
}
if ((intval($_SESSION['authenticated']))
&& (is_array(App::$account))
&& (App::$account['account_roles'] & ACCOUNT_ROLE_DEVELOPER))
if (intval($_SESSION['authenticated'])
&& is_array(App::$account)
&& (App::$account['account_roles'] & ACCOUNT_ROLE_DEVELOPER)) {
return true;
}
return false;
}
@@ -2143,8 +2099,9 @@ function load_contact_links($uid) {
$ret = [];
if (!$uid || x(App::$contacts, 'empty'))
if (!$uid || !empty(App::$contacts['empty'])) {
return;
}
// logger('load_contact_links');
@@ -2156,44 +2113,11 @@ function load_contact_links($uid) {
$ret[$rv['xchan_hash']] = $rv;
}
}
else
else {
$ret['empty'] = true;
App::$contacts = $ret;
}
/**
* @brief Returns querystring as string from a mapped array.
*
* @param array $params mapped array with query parameters
* @param string $name of parameter, default null
*
* @return string
*/
function build_querystring($params, $name = null) {
$ret = '';
foreach ($params as $key => $val) {
if (is_array($val)) {
if ($name === null) {
$ret .= build_querystring($val, $key);
}
else {
$ret .= build_querystring($val, $name . "[$key]");
}
}
else {
$val = urlencode($val);
if ($name != null) {
$ret .= $name . "[$key]" . "=$val&";
}
else {
$ret .= "$key=$val&";
}
}
}
return $ret;
App::$contacts = $ret;
}
@@ -2205,8 +2129,9 @@ function argc() {
}
function argv($x) {
if (array_key_exists($x, App::$argv))
if (array_key_exists($x, App::$argv)) {
return App::$argv[$x];
}
return '';
}
@@ -2215,21 +2140,6 @@ function dba_timer() {
return microtime(true);
}
/**
* @brief Returns xchan_hash from the observer.
*
* Observer can be a local or remote channel.
*
* @return string xchan_hash from observer, otherwise empty string if no observer
*/
function get_observer_hash() {
$observer = App::get_observer();
if (is_array($observer))
return $observer['xchan_hash'];
return '';
}
/**
* @brief Returns the complete URL of the current page, e.g.: http(s)://something.com/network
*
@@ -2253,22 +2163,6 @@ function curPageURL() {
return $pageURL;
}
/**
* @brief Returns a custom navigation by name???
*
* If no $navname provided load default page['nav']
*
* @param string $navname
*
* @return mixed
* @todo not fully implemented yet
*
*/
function get_custom_nav($navname) {
if (!$navname)
return App::$page['nav'];
// load custom nav menu by name here
}
/**
* @brief Loads a page definition file for a module.
@@ -2368,30 +2262,36 @@ function construct_page() {
nav($navbar);
}
$current_theme = Zotlabs\Render\Theme::current();
$current_theme = Theme::current();
// logger('current_theme: ' . print_r($current_theme,true));
// Zotlabs\Render\Theme::debug();
if (($p = theme_include($current_theme[0] . '.js')) != '')
if (($p = theme_include($current_theme[0] . '.js')) != '') {
head_add_js('/' . $p);
}
if (($p = theme_include('mod_' . App::$module . '.php')) != '')
if (($p = theme_include('mod_' . App::$module . '.php')) != '') {
require_once($p);
}
require_once('include/js_strings.php');
if (x(App::$page, 'template_style'))
if (!empty(App::$page['template_style'])) {
head_add_css(App::$page['template_style'] . '.css');
else
head_add_css(((x(App::$page, 'template')) ? App::$page['template'] : 'default') . '.css');
}
else {
head_add_css(((!empty(App::$page['template'])) ? App::$page['template'] : 'default') . '.css');
}
if (($p = theme_include('mod_' . App::$module . '.css')) != '')
if (($p = theme_include('mod_' . App::$module . '.css')) != '') {
head_add_css('mod_' . App::$module . '.css');
}
head_add_css(Zotlabs\Render\Theme::url());
head_add_css(Theme::url());
if (($p = theme_include('mod_' . App::$module . '.js')) != '')
if (($p = theme_include('mod_' . App::$module . '.js')) != '') {
head_add_js('mod_' . App::$module . '.js');
}
App::build_pagehead();
@@ -2423,7 +2323,6 @@ function construct_page() {
call_hooks('construct_page', $arr);
App::$layout = $arr['layout'];
foreach (App::$layout as $k => $v) {
if ((strpos($k, 'region_') === 0) && strlen($v)) {
if (strpos($v, '$region_') !== false) {
@@ -2458,8 +2357,9 @@ function construct_page() {
// security headers - see https://securityheaders.io
if (App::get_scheme() === 'https' && isset(App::$config['system']['transport_security_header']) && intval(App::$config['system']['transport_security_header']) == 1)
if (App::get_scheme() === 'https' && isset(App::$config['system']['transport_security_header']) && intval(App::$config['system']['transport_security_header']) == 1) {
header("Strict-Transport-Security: max-age=31536000");
}
if (isset(App::$config['system']['content_security_policy']) && intval(App::$config['system']['content_security_policy']) == 1) {
$cspsettings = [
@@ -2511,7 +2411,7 @@ function construct_page() {
}
require_once(theme_include(
((x(App::$page, 'template')) ? App::$page['template'] : 'default') . '.php')
((!empty(App::$page['template'])) ? App::$page['template'] : 'default') . '.php')
);
}
@@ -2530,23 +2430,7 @@ function appdirpath() {
* @param string $icon
*/
function head_set_icon($icon) {
App::$data['pageicon'] = $icon;
}
/**
* @brief Get the pageicon.
*
* @return string absolut path to pageicon
*/
function head_get_icon() {
$icon = App::$data['pageicon'];
if (!strpos($icon, '://'))
$icon = z_root() . $icon;
return $icon;
}
/**
@@ -2555,8 +2439,9 @@ function head_get_icon() {
* @return string
*/
function get_directory_realm() {
if ($x = Config::Get('system', 'directory_realm'))
if ($x = Config::Get('system', 'directory_realm')) {
return $x;
}
return DIRECTORY_REALM;
}
@@ -2574,8 +2459,9 @@ function get_directory_primary() {
return z_root();
}
if ($x = Config::Get('system', 'directory_primary'))
if ($x = Config::Get('system', 'directory_primary')) {
return $x;
}
return DIRECTORY_FALLBACK_MASTER;
}
@@ -2594,18 +2480,22 @@ function get_poller_runtime() {
function z_get_upload_dir() {
$upload_dir = Config::Get('system', 'uploaddir');
if (!$upload_dir)
if (!$upload_dir) {
$upload_dir = ini_get('upload_tmp_dir');
if (!$upload_dir)
}
if (!$upload_dir) {
$upload_dir = sys_get_temp_dir();
}
return $upload_dir;
}
function z_get_temp_dir() {
$temp_dir = Config::Get('system', 'tempdir');
if (!$temp_dir)
if (!$temp_dir) {
$temp_dir = sys_get_temp_dir();
}
return $temp_dir;
}
@@ -2622,8 +2512,9 @@ function z_check_cert() {
if (!$x['success']) {
$recurse = 0;
$y = z_fetch_url(z_root() . '/siteinfo.json', false, $recurse, ['novalidate' => true]);
if ($y['success'])
if ($y['success']) {
cert_bad_email();
}
}
}
}
@@ -2656,8 +2547,9 @@ function check_for_new_perms() {
// Do not execute if we are in the middle of a git update and the relevant versions don't match
if (Permissions::version() != PermissionRoles::version())
if (Permissions::version() != PermissionRoles::version()) {
return;
}
$pregistered = Config::Get('system', 'perms');
@@ -2720,8 +2612,9 @@ function check_for_new_perms() {
}
// We should probably call perms_refresh here, but this should get pushed in 24 hours and there is no urgency
if ($found_new_perm)
if ($found_new_perm) {
Config::Set('system', 'perms', $pcurrent);
}
}

View File

@@ -33,7 +33,7 @@
"ext-json": "*",
"ext-posix": "*",
"ext-sodium": "*",
"ext-bcmath": "*",
"ext-gmp": "*",
"sabre/dav": "^4.0",
"michelf/php-markdown": "^2.0",
"bshaffer/oauth2-server-php": "^1.9",
@@ -41,22 +41,23 @@
"simplepie/simplepie": "~1.5",
"league/html-to-markdown": "^5.0",
"pear/text_languagedetect": "^1.0",
"commerceguys/intl": "~1.1.0",
"commerceguys/intl": "^2.0.6",
"lukasreschke/id3parser": "^0.0.3",
"smarty/smarty": "^5.4",
"ramsey/uuid": "^4.1",
"twbs/bootstrap": "^5.3",
"blueimp/jquery-file-upload": "^10.3",
"desandro/imagesloaded": "^4.1",
"phpseclib/phpseclib": "^2.0.47",
"phpseclib/phpseclib": "^3.0.46",
"jbroadway/urlify": "^1.2",
"chillerlan/php-qrcode": "^4.3",
"chillerlan/php-qrcode": "^5.0.3",
"spomky-labs/otphp": "^11.1",
"patrickschur/language-detection": "^5.3",
"stephenhill/base58": "^1.1",
"mmccook/php-json-canonicalization-scheme": "^1.0",
"scssphp/scssphp": "^1.12",
"twbs/bootstrap-icons": "^1.11"
"scssphp/scssphp": "^2.0.1",
"twbs/bootstrap-icons": "^1.11",
"macgirvin/http-message-signer": "^0.2.6",
"root23/php-json-canonicalization": "^1.0"
},
"require-dev": {
"ext-yaml": "*",

2224
composer.lock generated

File diff suppressed because it is too large Load Diff

428
doc/LICENSE Normal file
View File

@@ -0,0 +1,428 @@
Attribution-ShareAlike 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution-ShareAlike 4.0 International Public
License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-ShareAlike 4.0 International Public License ("Public
License"). To the extent this Public License may be interpreted as a
contract, You are granted the Licensed Rights in consideration of Your
acceptance of these terms and conditions, and the Licensor grants You
such rights in consideration of benefits the Licensor receives from
making the Licensed Material available under these terms and
conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. BY-SA Compatible License means a license listed at
creativecommons.org/compatiblelicenses, approved by Creative
Commons as essentially the equivalent of this Public License.
d. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
e. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
f. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
g. License Elements means the license attributes listed in the name
of a Creative Commons Public License. The License Elements of this
Public License are Attribution and ShareAlike.
h. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
i. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
j. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
k. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
l. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
m. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. Additional offer from the Licensor -- Adapted Material.
Every recipient of Adapted Material from You
automatically receives an offer from the Licensor to
exercise the Licensed Rights in the Adapted Material
under the conditions of the Adapter's License You apply.
c. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
b. ShareAlike.
In addition to the conditions in Section 3(a), if You Share
Adapted Material You produce, the following conditions also apply.
1. The Adapter's License You apply must be a Creative Commons
license with the same License Elements, this version or
later, or a BY-SA Compatible License.
2. You must include the text of, or the URI or hyperlink to, the
Adapter's License You apply. You may satisfy this condition
in any reasonable manner based on the medium, means, and
context in which You Share Adapted Material.
3. You may not offer or impose any additional or different terms
or conditions on, or apply any Effective Technological
Measures to, Adapted Material that restrict exercise of the
rights granted under the Adapter's License You apply.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material,
including for purposes of Section 3(b); and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.
Creative Commons may be contacted at creativecommons.org.

142
doc/de/SUMMARY.md Normal file
View File

@@ -0,0 +1,142 @@
# Summary
- [Über](./about.md)
- [Grundlegende Konzepte von Hubzilla](./basicconcepts.md)
- [Funktionen](./functions.md)
- [Glossar](./glossary.md)
- [Oberfläche / Bezeichnungen](ui.md)
- [Benutzerhandbuch](./usermanual/overview.md)
- [Anmeldung / Registrierung](./usermanual/registration.md)
- [Konten, Profile, Kanäle](./usermanual/accounts_profiles_channels_basics.md)
- [Kanäle](./usermanual/channels.md)
- [Kanäle erstellen](./usermanual/create_channels.md)
- [Kanalrollen](./usermanual/channel_roles.md)
- [Profile](./usermanual/profiles.md)
- [Einstellungen](./usermanual/settings.md)
- [Konto-Einstellungen](./usermanual/account_settings.md)
- [Kanal-Einstellungen](./usermanual/channel_settings.md)
- [Privacy-Einstellungen](./usermanual/privacy_settings.md)
- [Anzeige-Einstellungen](./usermanual/display_settings.md)
- [Klon-Adressen verwalten](./usermanual/channel_locations.md)
- [Stream-Einstellungen](./usermanual/stream_settings.md)
- [Zusätzliche Funktionen (verborgene Einstellungen)](./usermanual/additional_features.md)
- [Verbinden mit Kanälen](./usermanual/connecting_with_channels.md)
- [Posten](./usermanual/posting.md)
- [Kommentieren](./usermanual/commenting.md)
- [Bilder einfügen](./usermanual/insert_images.md)
- [bbCode](./usermanual/bbcode.md)
- [Das Grid](./usermanual/the_grid.md)
- [Der Stream](./usermanual/the_stream.md)
- [Mit Postings interagieren](./usermanual/interact.md)
- [Repeat](./usermanual/repeat.md)
- [Teilen](./usermanual/share.md)
- [Link zur Quelle](./usermanual/link_to_source.md)
- [In Ordner speichern](./usermanual/save_to_folder.md)
- [Markierungsstatus (Stern) umschalten](./usermanual/toggle_star_status.md)
- [Quellcode anzeigen](./usermanual/show_source_code.md)
- [Unterhaltung folgen / nicht mehr folgen](./usermanual/follow_conversation.md)
- [Löschen](./usermanual/delete.md)
- [Konversationsmerkmale](./usermanual/conversation_features.md)
- [Inhaltsfilter](./usermanual/content_filters.md)
- [Öffentlicher Beitrags-Stream](./usermanual/public_stream.md)
- [Apps](./usermanual/apps.md)
- [wichtige Apps](./usermanual/important_apps.md)
- [Verbindungen](./usermanual/connections.md)
- [Verbindungs-Editor](./usermanual/connection_editor.md)
- [Diaspora-Kompatibilität](./usermanual/diaspora_compat.md)
- [Verzeichnis](./usermanual/directory.md)
- [Erweiterte Verzeichnissuche](./usermanual/AdvancedSearch.md)
- [Kanäle blockieren/ignorieren/archivieren/verstecken](./usermanual/blocking_channels.md)
- [Superblock](./usermanual/superblock.md)
- [Berechtigungen](./usermanual/permissions.md)
- [Berechtigungen für Inhalte](./usermanual/permissions_content.md)
- [Berechtigungen - Benutzerdefinierte Kanalrollen](./usermanual/permissions_channel_roles.md)
- [Berechtigungen - Kontaktrollen](./usermanual/permissions_contact_roles.md)
- [Direktnachrichten](./usermanual/direct_messages.md)
- [Erwähnungen](./usermanual/mentions.md)
- [Tags](./usermanual/tags.md)
- [Bookmarks](./usermanual/bookmarks.md)
- [Suche](./usermanual/search.md)
- [Artikel](./usermanual/article.md)
- [Dateien](./usermanual/files.md)
- [Fotos](./usermanual/photos.md)
- [Cloudspeicher](./usermanual/cloud_storage.md)
- [Galerie](./usermanual/gallery.md)
- [Chaträume](./usermanual/chat_rooms.md)
- [Gastzugang](./usermanual/guest_access.md)
- [Privacy Gruppen](./usermanual/privacy_groups.md)
- [Kalender](./usermanual/calendar.md)
- [Adressbuch](./usermanual/addressbook.md)
- [Wikis](./usermanual/wikis.md)
- [Inhaltswarnung/NSFW](./usermanual/NSFW.md)
- [Klonen](./usermanual/clone.md)
- [Webseiten](./usermanual/websites.md)
- [Comanche Seitenbeschreibungssprache](./usermanual/comanche.md)
- [Verschlüsselung](./usermanual/encryption.md)
- [Tipps zum Schutz der Privatsphäre](./usermanual/protection_of_privacy.md)
- [Kanal löschen](./usermanual/deleting_channel.md)
- [Account löschen](./usermanual/delete_account.md)
- [Tutorials](./tutorials/tutorials.md)
- [Mit Hubzilla Schritt für Schritt ins Fediverse](./tutorials/step_with_hubzilla.md)
- [Hubzilla optisch anpassen](./tutorials/customise_look.md)
- [Ein abgeleitetes Thema erstellen](./tutorials/DerivedTheme1.md)
- [Handbuch für Administratoren](./adminmanual/manual_for_administrators.md)
- [Wo Sie weitere Hilfe finden](./adminmanual/further_help.md)
- [Bevor Sie beginnen](./adminmanual/before_you_start.md)
- [Installation](./adminmanual/installation.md)
- [Anforderungen](./adminmanual/installation_requirements.md)
- [Manuelle Installation](./adminmanual/manual_installation.md)
- [Automatisierte Installation über das Shell-Skript .homeinstall](./adminmanual/automated_installation.md)
- [Installation mittels Docker](./adminmanual/Installation_using_docker.md)
- [Empfohlene Addons](./adminmanual/recommended_addons.md)
- [Föderations Addons](./adminmanual/federation_addons.md)
- [Probleme nach einer Aktualisierung](./adminmanual/problems-following-an-update.md)
- [Service-Klassen](./adminmanual/service_classes.md)
- [Verwaltung der Themen](./adminmanual/theme_management.md)
- [Erstellen von Seitenvorlagen](./adminmanual/Creating-Templates.md)
- [Hubzilla-Entwicklung - ein Leitfaden für das Schemasystem](./adminmanual/Schema-development.md)
- [Grundlegende Widgets](./adminmanual/Widgets.md)
- [Kanal-Verzeichnis](./adminmanual/channel_directory.md)
- [Primäres Verzeichnis](./adminmanual/Primary-Directory.md)
- [Administration](./adminmanual/administration.md)
- [Fehlersuche](./adminmanual/troubleshooting.md)
- [Hub-Snapshot-Tools](./adminmanual/hub_snapshot_tools.md)
- [Datenbank](./adminmanual/database.md)
- [Erweiterte Konfigurationen für Administratoren](./adminmanual/advanced_configurations.md)
- [CLI-Tools (utils)](./adminmanual/CLI_tools.md)
- [Datei-Synchronisation und Klonen](./adminmanual/filesync.md)
- [Nomad - Ein Überblick auf oberer Ebene](./adminmanual/Nomad---A-High-Level-Overview.md)
- [Admin FAQ](./adminmanual/faq_admins.md)
- [Entwickler](./develmanual/developers_guide.md)
- [Wer ist ein Hubzilla-Entwickler? Sollte ich das lesen?](./develmanual/who_is_a_hubzilla_developer.md)
- [Sie möchten Code beisteuern?](./develmanual/dev_beginner.md)
- [Versionen und Releases](./develmanual/versions.md)
- [Git-Repository-Zweige](./develmanual/git_repository.md)
- [Git für Nicht-Entwickler](./develmanual/git_for_non_developers.md)
- [Tools und Arbeitsabläufe für Entwickler](./develmanual/tools_workflows.md)
- [Dokumentation erstellen](./develmanual/doco.md)
- [Übersetzungen](./develmanual/translations.md)
- [Lizenzvergabe](./develmanual/licensing.md)
- [Codierungsstil](./develmanual/coding_style.md)
- [Dateisystem-Layout](./develmanual/file_system_layout.md)
- [Hubzilla-Entwicklung - einige nützliche Grundfunktionen](./develmanual/dev-function-overview.md)
- [Erstellen von Plugins/Addons für Hubzilla](./develmanual/Plugins.md)
- [Testen](./develmanual/testing.md)
- [Erstellen von Protokoll- Föderationsdiensten](./develmanual/federate.md)
- [Verhaltenskodex für Mitwirkende](./develmanual/code_of_conduct.md)
- [Unser Versprechen](./develmanual/our_pledge.md)
- [Unsere Standards](./develmanual/our_standards.md)
- [Unsere Verantwortlichkeiten](./develmanual/our_responsibilities.md)
- [Geltungsbereich](./develmanual/scope.md)
- [Durchsetzung](./develmanual/enforcement.md)
- [Namensnennung](./develmanual/attribution.md)
- [Das Nomad Protokoll](./develmanual/nomad_protocol.md)
- [Technische Einführung](./develmanual/technical_introduction.md)
- [Magic Auth](./develmanual/magic_auth.md)
- [Nomad-Strukturen](./develmanual/nomad_structures.md)
- [Zot API](./develmanual/API.md)
- [Hooks](./develmanual/hooks.md)
- [Noch zu organisierende Informationen](./develmanual/unorganized.md)
- [Das Hubzilla-Projekt](./the_hubzilla_project.md)
- [Credits](./credits.md)
- [Über diesen Hub](./platzhalter.md)

88
doc/de/about.md Normal file
View File

@@ -0,0 +1,88 @@
# Was ist Hubzilla?
Hubzilla ist ein dezentralisiertes Kommunikationsnetzwerk mit dem Ziel, Kommunikationsmöglichkeiten bereitzustellen, die Zensur umgehen, die Privatsphäre respektieren und somit frei sind von den Einschränkungen, die die heutigen kommerziellen Kommunikationsgiganten uns auferlegen. Diese stellen in erster Linie Spionagenetzwerke für zahlende Kunden aller Art zur Verfügung und monopolisieren und zentralisieren das ganze Internet was ursprünglich eben gerade nicht unter den revolutionären Zielen war, die einst zum World Wide Web führten.
Hubzilla ist frei, kostenlos und Open Source. Es wurde entwickelt, um auf einem Raspberry Pi ebenso zu laufen wie auf den größten AMD- und Intel-Xeon-Multiprozessor-Servern. Es kann für die Kommunikation zwischen einigen wenigen Einzelpersonen genutzt werden oder viele tausend Leute und mehr miteinander verbinden.
Ein weiteres Ziel ist es, von Können und Ressourcen unabhängig zu sein. Hubzilla ist für den einfachen Computernutzer ebenso leicht bedienbar wie für Systemadministratoren und Entwickler.
Wie Du es benutzt hängt davon ab, wie Du es benutzen *willst.*
Hubzilla ist in PHP geschrieben, dadurch ist es einfach, sie auf jedweder heutigen Hosting-Plattform zu installieren, inklusive Self-Hosting zu Hause, auf Shared Servern oder auf virtuellen und dedizierten Servern.
Mit anderen Worten, Hubzilla kann auf jeder Plattform laufen, die einen Web-Server, eine MySQL-kompatible Datenbank und PHP mitbringt.
---
## Grundlegende Konzepte und Funktionen von Hubzilla
### Kanäle (im Sinne von Identitäten im Gegensatz zu einer App)
Als Nutzer (d. h. als Privatperson oder als Verein) können Sie eine oder mehrere Web-**Identitäten** erstellen. Die Webpräsenz einer Identität wird innerhalb von Hubzilla gebündelt: Ein Besucher der Web-Identität sieht Inhalte, die mit dieser Identität in Verbindung stehen, an einem Ort, der über das Hauptmenü *Apps* für diese Identität verfügbar ist.
Intern, innerhalb der Software, wird eine Identität als „Kanal“ bezeichnet. Für einen Besucher bedeutet das Wort „Kanal“ jedoch **eine** bestimmte App, die zu dieser Identität gehört, nämlich die **Pinnwand**, auf der die (möglicherweise) föderierten Beiträge der Identität in einer zeitachsenbasierten Timeline angezeigt werden.
Im Folgenden verstehen wir Kanäle als Identitäten: Als angemeldeter Benutzer können Sie zwischen Ihren Kanälen wechseln, um die Inhalte für jeden Kanal zu bearbeiten, d. h. Beiträge zu veröffentlichen oder ein oder mehrere Kanalprofile, Websites, Wiki-Seiten und mehr zu erstellen. Pro Kanal können Sie auch Dateien in einer Cloud verwalten, Fotos mit Tags versehen und benennen und die Fotos in einer Webgalerie anzeigen. Ereignisse des Kanals können in einem Kalender angezeigt werden.
### Dezentrales Netzwerk: Zugriff über Grenzen hinweg (Zot/Nomad) vs. Bereitstellung über Grenzen hinweg (Zot/Nomad, ActivityPub und Diaspora)
Wenn dies erlaubt ist, verbinden sich Identitäten auf Hubzilla über Server- und Verwaltungsgrenzen hinweg über das Kommunikationsprotokoll Zot/Nomad (und, sofern von Ihrem Server bereitgestellt, auch über die Protokolle ActivityPub und Diaspora).
Mit Ausnahme von Beiträgen/Nachrichten bleiben alle veröffentlichten Inhalte lokal auf Ihrem Server. Sie können Inhalte zwar öffentlich veröffentlichen, aber auch lokale Inhalte nur mit bestimmten Verbindungen teilen. Letzteres ist nur mit Verbindungen über Zot/Nomad (also mit Identitäten auf Hubzilla und Streams) möglich.
### Zugriffskontrolle
Ihre lokalen Inhalte können von den Zot/Nomad-basierten Verbindungen „besucht“ werden, denen Sie die entsprechende Berechtigung erteilt haben. Indem Sie Ihre Verbindungen Verbindungslisten (sogenannten „Datenschutzgruppen“) zuordnen, können Sie allen Mitgliedern dieser Liste Zugriff auf bestimmte Inhalte gewähren.
Sie können Beiträge/Nachrichten (auch über die Protokolle ActivityPub oder Disaspora) an eine Verbindung, an eine Datenschutzgruppe oder an die Öffentlichkeit senden, indem Sie eine Zugriffskontrollliste verwenden.
Es ist möglich, eine Dauer für das Ablaufen eines Beitrags/einer Nachricht festzulegen. Auf diese Weise können Sie den Zugriff zeitlich begrenzen.
### Fernauthentifizierung
„Kennst du mich? Ja, ich kenne dich, willkommen!“
Der Zugriff auf nicht öffentliche Inhalte auf einem Remote-Server sollte nur möglich sein, wenn du dich gegenüber diesem Server authentifizieren kannst. Fediverse-Software, die nur das ActivityPub- oder das Diaspora-Protokoll verwendet, kann jedoch nur Konten vom lokalen Server authentifizieren.
Das Zot-Protokoll hingegen verfügt über einen integrierten Mechanismus namens MagicAuth, der es einem Server ermöglicht, Identitäten, die auf einem anderen (entfernten) Server registriert sind, Zugriff auf Inhalte und Aktionen zu gewähren oder zu verweigern.
### Nomadische Identität
Das Zot/Nomad-Protokoll ermöglicht es, Ihre Kanäle von dem Hub zu trennen, auf dem Sie sie erstellt haben. Sie können sie auf einen anderen Hub übertragen oder klonen. In diesem Fall existieren die Identität und die Daten des Kanals gleichzeitig an mehreren Orten. Dies sorgt für Ausfallsicherheit der Kanäle, falls ein Hub abgeschaltet wird oder nicht mehr verfügbar ist.
### Modulares Ökosystem durch Apps
Hubzilla stellt für jede Funktion eine separate **App** zur Verfügung, d. h. die Cloud, Fotos, Galerie, Chat, Wiki, Kalender, Kontakte oder die Verbindungs-App. Ein Serveradministrator entscheidet, welche Apps für die Benutzer dieses Servers verfügbar sein sollen. Ein Benutzer kann dann die verfügbaren Apps installieren oder deinstallieren.
### Zusammenarbeit
Die folgenden gemeinsamen privaten Bereiche ermöglichen die Arbeit im Team mit Hubzilla:
- Als Benutzer erlauben Sie ausgewählten Verbindungen, Ihre Webseiten und Wiki-Seiten zu lesen und zu bearbeiten.
- Erlauben Sie ihnen, Ihre Dateien, Ihren Kalender und Ihre Kontakte über die Weboberfläche (die Cloud-, Kalender- und Kontakt-Apps) oder WebDAV, CalDAV und CardDAV zu lesen und zu bearbeiten.
- Verwenden Sie Konversations-Threads (Beiträge), die nur für Ihre Kollaborationsgruppe sichtbar sind, indem Sie die Zugriffskontrollliste entsprechend einstellen.
### Cloud-Speicher
Sie können Dokumente direkt auf Ihrer Website speichern und mit anderen teilen. Öffentlicher, privater oder eingeschränkter Zugriff. Da die Dateien auf Ihrem Domainnamen gespeichert sind, wissen die Empfänger, dass sie von Ihnen stammen.
### Teilen vs. Boosten
Mit Hubzilla können Sie Beiträge anderer Fediverse-Nutzer (erneut) teilen. Das (erneute) Teilen in Hubzilla ist so, als würden Sie Ihren Freunden Jennie und Omar erzählen, was Giaco gesagt hat. Und dann sagen sie: „Cool, das gefällt mir“.
Hubzilla ermöglicht auch das Boosten. Etwas zu boosten ist so, als würdest du deinen Freunden Jennie und Omar erzählen, was Giaco gesagt hat, während Giaco und alle seine Freunde und seine Familie (die du nicht kennst) zuhören. Giaco und alle seine Freunde und seine Familie können jetzt mit dir sprechen.

View File

Some files were not shown because too many files have changed in this diff Show More