Compare commits

...

167 Commits

Author SHA1 Message Date
Tobias Diekershoff bda829665e
Merge pull request #14166 from annando/streams
Improved streams detection
2024-05-16 18:44:33 +02:00
Michael 846addf7b3 Improved streams detection 2024-05-16 15:26:46 +00:00
Hypolite Petovan 1f12d1e668
Merge pull request #14165 from annando/probe
Fixes "Undefined array key url"
2024-05-16 11:15:59 -04:00
Hypolite Petovan 570de6a91a
Merge pull request #14164 from loma-one/develop
Colour for fading in a comment adjusted
2024-05-16 11:14:26 -04:00
Hypolite Petovan c81ee0d1c3
Merge pull request #14162 from annando/sensitive
Respect the "sensitive" flag for posts
2024-05-16 11:12:28 -04:00
Michael 4ce1911889 Fixes " Undefined array key url" 2024-05-16 10:54:59 +00:00
loma-one 367170a92a
Colour for fading in a comment adjusted
When a comment is called up via the notification, the comment is highlighted brightly. With a dark theme, this is disproportionately bright. 

The changed colour values represent a compromise between light and dark themes. 

https://github.com/friendica/friendica/issues/14052
2024-05-16 11:28:33 +02:00
Michael 0426572e92 Respect the "sensitive" flag for summaries 2024-05-15 12:17:06 +00:00
Hypolite Petovan cad3a01b1c
Merge pull request #14161 from annando/error
Fix " Call to undefined method Friendica\App::getLoggedInUserNickname"
2024-05-15 07:56:56 -04:00
Michael 69b1958483 Fix " Call to undefined method Friendica\App::getLoggedInUserNickname" 2024-05-15 06:15:50 +00:00
Hypolite Petovan 3493b2c1b5
Merge pull request #14159 from annando/unparseurl
"Network::unparseURL" is replaced with "Uri::fromParts"
2024-05-14 19:49:33 -04:00
Michael 1c66d49599 "Network::unparseURL" is replaced with "Uri::fromParts" 2024-05-14 21:47:57 +00:00
Hypolite Petovan a6f3c961dc
Merge pull request #14157 from annando/deprecated2
Some more deprecated function calls are replaced
2024-05-14 15:23:47 -04:00
Michael e12f92e516 Some more deprecated function calls are replaced 2024-05-14 19:21:25 +00:00
Hypolite Petovan bd6616e84f
Merge pull request #14155 from annando/deprecated
Many deprecated function calls are replaced
2024-05-13 18:40:38 -04:00
Michael 60f5fd8188 Many deprecated function calls are replaced 2024-05-13 21:37:15 +00:00
Hypolite Petovan cfad5809ff
Merge pull request #14154 from annando/probe
Friendica probing simplified / Zot probing improved
2024-05-13 14:15:27 -04:00
Hypolite Petovan 104d355096
Merge pull request #14146 from annando/loglevels
Improve the log level display
2024-05-13 14:14:10 -04:00
Michael 82327b0b06 Improve the log level display 2024-05-13 12:34:35 +00:00
Michael 8a100e847d Friendica probing simplified / Zot probing improved 2024-05-13 12:33:32 +00:00
Tobias Diekershoff 9ae4a17977
Merge pull request #14152 from annando/feed-no-html
Log the feed fix only if it has an effect.
2024-05-13 06:29:02 +02:00
Hypolite Petovan c604477cac
Merge pull request #14151 from annando/local-link
Probing for Zot improved and Pumpio removed
2024-05-12 21:03:20 -04:00
Michael 21fc28029a Probing for Zot improved and Pumpio removed 2024-05-13 00:58:54 +00:00
Michael 52b11856fa Log the feed fix only if it has an effect. 2024-05-13 00:36:30 +00:00
Hypolite Petovan 9e1362ba46
Merge pull request #14150 from annando/request-type
Request type set for all HTTP requests
2024-05-12 15:33:58 -04:00
Michael 5751e024c0 Request type set for all HTTP requests 2024-05-12 17:53:21 +00:00
Hypolite Petovan d788cb82cc
Merge pull request #14148 from annando/summary
Use the field for the summary instead of the "abstract" element
2024-05-11 18:14:32 -04:00
Hypolite Petovan ec5e8a55b5
Merge pull request #14147 from annando/feed-no-html
Support for non HTML content for feed imports
2024-05-11 18:13:40 -04:00
Hypolite Petovan a73ba083f9
Merge pull request #14142 from annando/attachments
Issue 11963: Set Permissions for attachments
2024-05-11 18:09:19 -04:00
Michael e43f96740b Use the field for the summary instead of the "abstract" element 2024-05-11 20:14:01 +00:00
Michael bca86beda0 Support for non HTML content for feed imports 2024-05-11 20:03:19 +00:00
Michael 567292533e Issue 11963: Set Permissions for attachments 2024-05-11 19:19:18 +00:00
Tobias Diekershoff ee9510e17d
Merge pull request #14140 from haheute/docs-channels-de
German translation of Channels Documentation
2024-05-11 14:40:36 +02:00
Tobias Diekershoff 99426d4188
Merge pull request #14144 from annando/unneeded-log-entry
Unneeded log entry removed
2024-05-11 14:34:47 +02:00
Michael Vogel 0be622e049
Merge pull request #14139 from MrPetovan/bug/apexrabbit-vulns
Fix a couple of stored XSS vulnerabilities
2024-05-11 14:03:24 +02:00
Michael f574bc72ca Unneeded log entry removed 2024-05-11 08:52:50 +00:00
Michael Vogel 5b55ba2176
Merge pull request #14141 from MrPetovan/bug/14045-addon-unregistration
Add support for absolute file paths when removing addon
2024-05-11 10:46:57 +02:00
Tobias Diekershoff db8cab77c1
Merge pull request #14143 from MrPetovan/bug/fatal-errors
Fix wrong return value in Item::gettopLevelParent()
2024-05-11 10:01:13 +02:00
Hypolite Petovan 40949483f3 Fix wrong return value in Item::gettopLevelParent()
- Address https://github.com/friendica/friendica/issues/14025#issuecomment-2105033182
2024-05-10 21:54:19 -04:00
Hypolite Petovan 28784eef85 Update main translation file after changing several strings 2024-05-10 20:41:22 -04:00
Hypolite Petovan 42c3faa450 Add support for absolute file paths when removing addon
- This handles a rare case where absolute addon file paths were saved to the hook table
2024-05-10 20:34:47 -04:00
Hannes Heute 786a8dbd3b (-group) posts of group 2024-05-10 18:30:22 +02:00
Hannes Heute afda3de363 tabs to 4 spaces 2024-05-10 18:26:31 +02:00
Hypolite Petovan 25475b4838 Escape album name select list in photo upload form
- Thanks to @apexrabbit for the report!
2024-05-10 11:25:59 -04:00
Hypolite Petovan a6cb3ed903 Escape output of PermissionTooltip module
- Create AclReceivers and AddressedReceivers entities to collect contact names
- Create privacy/permission_tooltip.tpl to escape contact names
- Move PermissionTooltip module to Privacy namespace
- Thanks to @apexrabbit for the report!
2024-05-10 11:25:59 -04:00
Hypolite Petovan c19a68dc64 Remove DI dependency from PermissionTooltip module
- Update PHPDoc of APContact::getByURL
2024-05-10 11:25:27 -04:00
Hannes Heute a356fe65d0 typo 2024-05-10 17:24:38 +02:00
Hypolite Petovan f566c52624
Merge pull request #14138 from annando/user-agent
Improved user agent string
2024-05-10 11:07:39 -04:00
Hypolite Petovan 9dc0e5393e
Merge pull request #14135 from tobiasd/colorize-loglevels
Colorize loglevels
2024-05-10 10:59:05 -04:00
Hannes Heute e049fd5e47 Unified Indentation 2024-05-10 16:58:17 +02:00
Hannes Heute 579a0b43ce German translation of Channels documentation 2024-05-10 16:47:15 +02:00
Michael afff2b949f Improved user agent string 2024-05-10 09:01:43 +00:00
Tobias Diekershoff 0ae91b59ca
Merge pull request #14136 from annando/issue-14134
Issue 14134: Allow reshare posts from Bluesky and Tumblr
2024-05-10 08:04:33 +02:00
Tobias Diekershoff e7b9d97672
Merge pull request #14137 from annando/issue-14132
Issue 14132: Title for videos
2024-05-10 08:02:45 +02:00
Michael e7b861388d Issue 14132: Title for videos 2024-05-10 05:42:45 +00:00
Michael 55fb52299d Issue 14134: Allow reshare posts from Bluesky and Tumblr 2024-05-10 04:55:57 +00:00
Tobias Diekershoff 38e082949d adopt colors 2024-05-10 06:34:29 +02:00
Tobias Diekershoff aeb1382039 make warnings bold as well
now the three most severe levels are bold, while the three less severe levels have a normal font weight
2024-05-09 11:45:30 +02:00
Tobias Diekershoff 63a7536624 remove whitespaces 2024-05-09 10:33:51 +02:00
Tobias Diekershoff bf278adb15 admin panel - colorize error levels
With this patch the error levels shown in the Admin Panel -> Log view are coloured according to severity. Starting from green for debug, over orange for notices to bold red for errors and criticals.
2024-05-09 10:32:55 +02:00
Hypolite Petovan 765af10f00
Merge pull request #14119 from MrPetovan/imgbot
[ImgBot] Optimize images
2024-04-27 11:04:01 -04:00
ImgBotApp e98b3a307b [ImgBot] Optimize images
*Total -- 1,838.08kb -> 1,439.34kb (21.69%)

/images/screenshots/friendica-2023-12-frio-desktop.png -- 1,011.26kb -> 706.65kb (30.12%)
/images/screenshots/friendica-2023-10-frio-mobile-options-dark-blue.png -- 184.63kb -> 149.93kb (18.8%)
/images/screenshots/friendica-2023-10-frio-mobile-options-light-blue.png -- 166.19kb -> 138.00kb (16.96%)
/images/screenshots/friendica-2023-10-frio-mobile-timeline-dark-blue.png -- 400.47kb -> 370.62kb (7.45%)
/images/bluesky.jpg -- 7.75kb -> 7.55kb (2.58%)
/view/js/vanillaEmojiPicker/screenshot.png -- 67.77kb -> 66.58kb (1.76%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2024-04-27 10:59:46 -04:00
Michael Vogel e8aaa9cfec
Merge pull request #14114 from MrPetovan/bug/14110-reports-query
Rework reports query in Moderation\Reports module class
2024-04-25 06:47:51 +02:00
Hypolite Petovan b1b2e9bd11 Rework reports query in Moderation\Reports module class
- References to non-existent fields removed
- Added computed rules field
- Patch originally submitted by @TheTomcat14
2024-04-24 22:43:59 -04:00
Hypolite Petovan fe9ef20392
Merge pull request #14103 from annando/api-channel-list
API: Access channels and groups via lists
2024-04-19 18:16:34 -04:00
Michael 984a972e72 API: Access channels and groups via lists 2024-04-19 21:42:34 +00:00
Hypolite Petovan 1cabf53bf5
Merge pull request #14101 from annando/self-this
"self::" should be "$this->" on non static functions
2024-04-18 10:40:56 -04:00
Michael 2776411c6c "self::" should be "$this->" on non static functions 2024-04-18 05:18:44 +00:00
Hypolite Petovan 894c56ea22
Merge pull request #14100 from annando/origin
Performance improvements when displaying local posts
2024-04-17 17:21:18 -04:00
Michael c82a1ed467 Performance improvements when displaying local posts 2024-04-17 19:16:47 +00:00
Hypolite Petovan bae1f63424
Merge pull request #14097 from annando/enable-user-defined-channel
Enable user defined channels upon adding/editing
2024-04-15 19:16:02 -04:00
Michael 652802f758 Enable user defined channels upon adding/editing 2024-04-15 20:20:42 +00:00
Tobias Diekershoff ac195c7061
Merge pull request #14096 from annando/noproxy
The legacy proxy functionality is removed
2024-04-15 21:17:31 +02:00
Michael 9cf8678323 Unused function removed 2024-04-15 19:06:12 +00:00
Michael 0e79b5373b The legacy proxy functionality is removed 2024-04-15 18:58:02 +00:00
Hypolite Petovan c30e4c02de
Merge pull request #14095 from annando/warning
Fix warning: Undefined array key "post-reason"
2024-04-15 00:09:01 -04:00
Michael b351819986 Fix: Undefined array key "allow_cid" 2024-04-15 03:25:06 +00:00
Michael 642c55ee3e Fix: "Undefined property: stdClass::$personal" 2024-04-15 03:24:38 +00:00
Michael 6e0118f3fe Fix warning: Undefined array key "post-reason" 2024-04-15 03:10:15 +00:00
Hypolite Petovan 49a0b0fc3c
Merge pull request #14090 from annando/bbcode
The BBCode conversion is split into several smaller functions
2024-04-14 21:59:46 -04:00
Hypolite Petovan 4a0b989386
Merge pull request #14094 from mexon/mat/testing-doc
update testing documentation
2024-04-14 21:58:26 -04:00
Hypolite Petovan 9329eebec0
Merge pull request #14093 from mexon/mat/autotest-password
allow overriding password during local testing
2024-04-14 21:54:59 -04:00
Matthew Exon 619a63af6b update testing documentation 2024-04-14 11:11:00 +02:00
Michael 38da9013ff The BBCode conversion is split into several smaller functions 2024-04-14 07:45:56 +00:00
Matthew Exon e24d1da13b tidy up more password definitions 2024-04-14 09:06:48 +02:00
Hypolite Petovan ed01b0f409
Merge pull request #14075 from mexon/mat/empty-picture-scale
round scaled dimensions up to avoid zero size
2024-04-13 18:30:44 -04:00
Matthew Exon f616dc054f allow overriding password during local testing 2024-04-13 22:24:30 +02:00
Matthew Exon a20b876d67 add unit tests for getScalingDimensions 2024-04-13 21:50:17 +02:00
Matthew Exon c5b8abcaf0 round scaled dimensions up to avoid zero size 2024-04-13 21:50:17 +02:00
Hypolite Petovan 9fe0d72461
Merge pull request #14080 from annando/issue-14079
Issue #14079:  Shorten the displayed URL
2024-04-11 21:55:59 -04:00
Michael 45b30825f0 Isolate the link conversion into a dedicated function 2024-04-11 22:10:30 +00:00
Michael 7dc5622dca Issue #14079: Shorten the displayed URL 2024-04-11 04:37:42 +00:00
Hypolite Petovan ce580241e2
Merge pull request #14089 from annando/ping-lock
Prevent concurring ping requests
2024-04-10 19:19:24 -04:00
Michael a440619769 Prevent concurring ping requests 2024-04-10 22:25:14 +00:00
Tobias Diekershoff 9626a76b18
Merge pull request #14083 from annando/issue-13812
Issue 13812: Public groups with manual request approval
2024-04-09 18:46:44 +02:00
Tobias Diekershoff b02a5ed701
Merge pull request #14085 from annando/generator
Use similar values for generator and system actor
2024-04-09 18:38:22 +02:00
Michael 69fc2c04e4 Issue 13812: Public groups with manual request approval 2024-04-08 07:58:45 +00:00
Michael d7e8ee51ae Use similar values for generator and system actor 2024-04-08 06:33:03 +00:00
Tobias Diekershoff 02d8cc2f71
Merge pull request #14076 from annando/follow-vcard
Don't display the "follow/unfollow" vcard-link on pages meant for follow/unfollow
2024-04-08 07:35:16 +02:00
Tobias Diekershoff 0a88d5a94a
Merge pull request #14082 from annando/issue-14055
Issue 14055: Set link to group server for group posts
2024-04-08 07:31:53 +02:00
Michael 8b75aab4ad Don't display the "follow/unfollow" vcard-link on pages meant for follow/unfollow 2024-04-07 21:30:39 +00:00
Michael a2da42640c Issue 14055: Set link to group server for group posts 2024-04-07 15:46:55 +00:00
Tobias Diekershoff ad65e56b16
Merge pull request #14077 from annando/idn
Fallback mechanism for missing IDN functions
2024-04-07 08:11:35 +02:00
Michael 78dc61b59e Fallback mechanism for missing IDN functions 2024-04-06 11:26:12 +00:00
Hypolite Petovan e9dcf15d86
Merge pull request #14074 from annando/searchtext
Menu option to display the search text
2024-04-05 08:40:45 -04:00
Michael b40687081e The data for the language display is now fetched on demand 2024-04-05 10:29:27 +00:00
Michael 50b1de5959 Menu option to display the search text 2024-04-05 07:35:21 +00:00
Michael Vogel 44344af055
Merge pull request #14072 from jlamothe/from-to
fixed Channels documentation
2024-04-04 00:51:40 +02:00
Jonathan Lamothe cbcec17bcb fixed Channels documentation
The "to" keyword documentation seemed incorrect.
2024-04-03 15:48:03 -04:00
Hypolite Petovan 9ab5d8a83a
Merge pull request #14069 from annando/invalid-link
Avoid exception "Unable to retrieve the host in URL" in the search
2024-04-03 09:25:46 -04:00
Michael 0d4f956fba Avoid exception "Unable to retrieve the host in URL" in the search 2024-04-03 07:51:02 +00:00
Hypolite Petovan c71f4eb1df
Merge pull request #14068 from annando/wall-read
Don't set posts to seen on channel ping
2024-04-02 21:22:13 -04:00
Michael 626ab7cb54 Don't set posts to seen on channel ping 2024-04-02 21:31:57 +00:00
Hypolite Petovan c8fff8c50c
Merge pull request #14064 from haheute/mobile-padding-2
Add some padding for the login page at mobile width (frio)
2024-04-01 20:33:07 -04:00
Hypolite Petovan 3bb00f36f8 [frio] Restore lateral margins on home/login page for mobile display
- They had been removed site-wide to give more space to posts in the network view
2024-04-01 09:41:08 -04:00
Hypolite Petovan fda2873a92
Merge pull request #14060 from eibhear-from-athlone/friendica_14059_nexttry
Show the next try date and time on the deferred worker job list in the admin pages
2024-03-29 23:00:27 -04:00
Éibhear Ó hAnluain 394c388a46
Fixing the queue.tpl files 2024-03-29 20:36:40 +00:00
Éibhear Ó hAnluain 6e7178022b
Updates messages file following run of `bin/run_xgettext.sh` 2024-03-29 20:29:31 +00:00
Éibhear Ó hAnluain 1d86146f64
Show next_try only for the deferred worker job. 2024-03-29 20:27:10 +00:00
Éibhear Ó hAnluain 751ffe6bc6
Add the next_try field to the worker job queue list 2024-03-29 20:27:10 +00:00
Hypolite Petovan 31b74b1c8a
Merge pull request #14046 from annando/channel-counter
Improvements for channel counter
2024-03-27 11:15:44 -04:00
Michael 0fde21ff28 Improvements for channel counter 2024-03-27 11:12:44 +00:00
Hypolite Petovan 53a2fb648f
Merge pull request #14043 from haheute/typo-independent
fix typo: Independant --> Independent
2024-03-26 13:40:39 -04:00
Hannes Heute 13b890605b add messages.po again 2024-03-26 18:35:50 +01:00
Hypolite Petovan 97f7a99382
Merge pull request #14044 from haheute/img-div-class
Img div class
2024-03-26 13:07:30 -04:00
Hannes Heute e602b16dfb follow naming convention 2024-03-26 17:33:45 +01:00
Hannes Heute 5c7dd34fba remove the typo fix from this PR 2024-03-26 17:29:52 +01:00
Hannes Heute 4b647d288a added messages.po 2024-03-26 17:21:19 +01:00
Hannes Heute 71a9e6112e Add class to div for custom styling 2024-03-26 14:04:05 +01:00
Hannes Heute 0fd5c00010 fix typo: Independant --> Independent 2024-03-26 13:14:19 +01:00
Hypolite Petovan 250b3b813c
Merge pull request #14041 from annando/feature-admin
Simplified admin frontend for features
2024-03-26 07:30:24 -04:00
Michael f537d7a64f Simplified admin frontend for features 2024-03-25 21:55:42 +00:00
Hypolite Petovan 5ec445e90b
Merge pull request #14040 from annando/widget-lock
The visibility of network widgets can now be locked
2024-03-25 10:42:10 +00:00
Michael 67e0b6357e The visibility of network widgets can now be locked 2024-03-25 08:00:46 +00:00
Tobias Diekershoff f928372266
Merge pull request #14039 from annando/widget-features
Configuration for widgets
2024-03-25 08:45:16 +01:00
Michael 200cf29a8d Configuration for widgets 2024-03-25 07:38:36 +00:00
Michael Vogel f78b0e7c51
Merge pull request #14037 from MrPetovan/bug/warnings
Add expected field 'uri-id' in Mastodon\Statuses\Bookmark
2024-03-24 20:57:20 +01:00
Hypolite Petovan b21604a720
Merge pull request #14038 from annando/feature-constants
Constants for features
2024-03-24 19:37:39 +00:00
Hypolite Petovan c67225c62d Add expected field 'uri-id' in Mastodon\Statuses\Bookmark
- Address https://github.com/friendica/friendica/issues/14026#issuecomment-2016469896
2024-03-24 15:30:44 -04:00
Michael Vogel 449464a2b1
Merge pull request #14036 from MrPetovan/bug/deprecated
Add `'$VERSION'` template variable to make Friendica version available in templates
2024-03-24 15:55:29 +01:00
Michael c041c65c1d Comstants for features 2024-03-24 14:48:23 +00:00
Hypolite Petovan e293de04f5 Add '$VERSION' template variable to make Friendica version available in templates
- constant() Smarty function is deprecated
- Remove unused site-wide template variable '$APP'
- Address https://github.com/friendica/friendica/issues/14027#issuecomment-2016469408
2024-03-24 09:20:58 -04:00
Hypolite Petovan b4d71f1855 Remove duplicated '$baseurl' template variable declarations
- This variable is declared for all templates in Renderer
2024-03-24 09:11:46 -04:00
Tobias Diekershoff d824bb536f
Merge pull request #14032 from annando/quoted-posts
Implementation of FEP-e232 for quoted posts
2024-03-24 07:59:53 +01:00
Tobias Diekershoff 1b9fa0fe00
Merge pull request #14033 from annando/content-link
Add a link to the post if "no preview" is selected
2024-03-24 07:54:35 +01:00
Michael b72e32a842 Add a link to the post if "no preview" is selected 2024-03-24 06:46:48 +00:00
Michael b39c48fb02 Implementation of FEP-e232 for quoted posts 2024-03-24 06:05:37 +00:00
Hypolite Petovan b5a69da872
Merge pull request #14029 from annando/issue-13910
Issue 13910: Display the unseen counter based on the channel
2024-03-24 00:08:25 +00:00
Michael 618a3153ab Issue 13910: Display the unseen counter based on the channel 2024-03-23 17:17:56 +00:00
Hypolite Petovan 0ccb3e7efe
Merge pull request #14017 from annando/active-admin-check
Automatically close the registration when the admin is inactive
2024-03-22 11:52:24 +00:00
Michael 4b695e361c Automatically close the registration when the admin is inactive 2024-03-22 04:19:40 +00:00
Hypolite Petovan 4834255acf
Merge pull request #14015 from annando/did
Internal support for Bluesky tokens
2024-03-21 22:10:09 +00:00
Michael 325932dc5a Internal support for Bluesky tokens 2024-03-21 21:33:12 +00:00
Hypolite Petovan faac52e078
Merge pull request #14020 from annando/issue-13714
Issue 13714: Support for "commentsEnabled" and "capabilities"
2024-03-21 17:21:51 +00:00
Michael 7a0c5d141e Issue 13714: Support for "commentsEnabled" and "capabilities" 2024-03-21 17:11:20 +00:00
Hypolite Petovan 732d738b82
Merge pull request #14022 from annando/channel-only
Possibility to mark contacts as "channel only"
2024-03-21 16:11:49 +00:00
Michael 3d267c7b8f Possibility to mark contacts as "channel only" 2024-03-21 13:20:52 +00:00
Hypolite Petovan 987e6c5ea6
Merge pull request #14016 from annando/issue-13787
Issue 13787: Filter in circles editor by contact relation
2024-03-21 13:07:57 +00:00
Hypolite Petovan 9e240c24ce
Merge pull request #14023 from annando/files
Updated database.sql / messages.po
2024-03-21 13:03:31 +00:00
Michael 3b419cae1e Issue 13787: Filter in circles editor by contact relation 2024-03-21 12:58:54 +00:00
Hypolite Petovan 56f3743e75
Merge pull request #14021 from annando/worker-idletime
Execute a worker task when there hadn't one for some seconds
2024-03-21 12:41:20 +00:00
Hypolite Petovan f26f35f009
Merge pull request #14018 from annando/content-type-check
Improved Content-Type check on incoming requests
2024-03-21 11:58:08 +00:00
Michael fdd777d75d Updated database.sql / messages.po 2024-03-21 10:49:42 +00:00
Michael aff45278e1 Execute a worker task when there hadn't one for some seconds 2024-03-21 09:10:07 +00:00
Michael 11a16589da Improved Content-Type check on incoming requests 2024-03-21 09:02:25 +00:00
Tobias Diekershoff f60638787e added empty 2024.06 block to the CHANGELOG 2024-03-21 08:10:42 +01:00
Tobias Diekershoff c5936bb51e bump version to 2024.06-dev 2024-03-21 08:09:16 +01:00
268 changed files with 6282 additions and 4277 deletions

View File

@ -1,3 +1,10 @@
Version 2024.06 (unreleased)
Friendica Core
Friendica Addons
Closed Issues
Version 2024.03 (2024-03-21)
Friendica Core
Updates to the translations AR, BG, CS, DE, EO, ES, FR, GD, HU, IS, IT, JA, PL, RO, RU, SV

View File

@ -1 +1 @@
2024.03
2024.06-dev

View File

@ -138,9 +138,9 @@ function execute_tests() {
if [ -n "${USEDOCKER}" ]; then
echo "Fire up the mysql docker"
DOCKER_CONTAINER_ID=$(docker run \
-e MYSQL_ROOT_PASSWORD=friendica \
-e MYSQL_ROOT_PASSWORD="${DATABASE_PASSWORD}" \
-e MYSQL_USER="${DATABASE_USER}" \
-e MYSQL_PASSWORD=friendica \
-e MYSQL_PASSWORD="${DATABASE_PASSWORD}" \
-e MYSQL_DATABASE="${DATABASE_NAME}" \
-d mysql)
DATABASE_HOST=$(docker inspect --format="{{.NetworkSettings.IPAddress}}" "${DOCKER_CONTAINER_ID}")
@ -152,8 +152,8 @@ function execute_tests() {
echo "To use the docker container set the USEDOCKER environment variable"
exit 3
fi
mysql -u "${DATABASE_USER}" -pfriendica -e "DROP DATABASE IF EXISTS ${DATABASE_NAME}" -h ${DATABASE_HOST} || true
mysql -u "${DATABASE_USER}" -pfriendica -e "CREATE DATABASE ${DATABASE_NAME} DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" -h ${DATABASE_HOST}
mysql -u "${DATABASE_USER}" -p"${DATABASE_PASSWORD}" -e "DROP DATABASE IF EXISTS ${DATABASE_NAME}" -h ${DATABASE_HOST} || true
mysql -u "${DATABASE_USER}" -p"${DATABASE_PASSWORD}" -e "CREATE DATABASE ${DATABASE_NAME} DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" -h ${DATABASE_HOST}
else
DATABASE_HOST=mysql
fi
@ -171,9 +171,9 @@ function execute_tests() {
if [ -n "${USEDOCKER}" ]; then
echo "Fire up the mariadb docker"
DOCKER_CONTAINER_ID=$(docker run \
-e MYSQL_ROOT_PASSWORD=friendica \
-e MYSQL_ROOT_PASSWORD="${DATABASE_PASSWORD}" \
-e MYSQL_USER="${DATABASE_USER}" \
-e MYSQL_PASSWORD=friendica \
-e MYSQL_PASSWORD="${DATABASE_PASSWORD}" \
-e MYSQL_DATABASE="${DATABASE_NAME}" \
-d mariadb)
DATABASE_HOST=$(docker inspect --format="{{.NetworkSettings.IPAddress}}" "${DOCKER_CONTAINER_ID}")
@ -185,8 +185,8 @@ function execute_tests() {
echo "To use the docker container set the USEDOCKER environment variable"
exit 3
fi
mysql -u "${DATABASE_USER}" -pfriendica -e "DROP DATABASE IF EXISTS ${DATABASE_NAME}" -h ${DATABASE_HOST} || true
mysql -u "${DATABASE_USER}" -pfriendica -e "CREATE DATABASE ${DATABASE_NAME} DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" -h ${DATABASE_HOST}
mysql -u "${DATABASE_USER}" -p"${DATABASE_PASSWORD}" -e "DROP DATABASE IF EXISTS ${DATABASE_NAME}" -h ${DATABASE_HOST} || true
mysql -u "${DATABASE_USER}" -p"${DATABASE_PASSWORD}" -e "CREATE DATABASE ${DATABASE_NAME} DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" -h ${DATABASE_HOST}
else
DATABASE_HOST=mariadb
fi
@ -203,14 +203,14 @@ function execute_tests() {
if [ -n "${USEDOCKER}" ]; then
echo "Initialize database..."
docker exec ${DOCKER_CONTAINER_ID} mysql -u root -pfriendica -e "CREATE DATABASE IF NOT EXISTS ${DATABASE_NAME};"
docker exec ${DOCKER_CONTAINER_ID} mysql -u root -p"${DATABASE_PASSWORD}" -e "CREATE DATABASE IF NOT EXISTS ${DATABASE_NAME};"
fi
export MYSQL_HOST="${DATABASE_HOST}"
#call installer
echo "Installing Friendica..."
"${PHP}" ./bin/console.php autoinstall --dbuser="${DATABASE_USER}" --dbpass=friendica --dbdata="${DATABASE_NAME}" --dbhost="${DATABASE_HOST}" --url=https://friendica.local --admin=admin@friendica.local
"${PHP}" ./bin/console.php autoinstall --dbuser="${DATABASE_USER}" --dbpass="${DATABASE_PASSWORD}" --dbdata="${DATABASE_NAME}" --dbhost="${DATABASE_HOST}" --url=https://friendica.local --admin=admin@friendica.local
fi
#test execution

View File

@ -1,6 +1,6 @@
-- ------------------------------------------
-- Friendica 2024.03 (Yellow Archangel)
-- DB_UPDATE_VERSION 1557
-- Friendica 2024.06-dev (Yellow Archangel)
-- DB_UPDATE_VERSION 1560
-- ------------------------------------------
@ -1347,7 +1347,7 @@ CREATE TABLE IF NOT EXISTS `post-engagement` (
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
`owner-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Item owner',
`contact-type` tinyint NOT NULL DEFAULT 0 COMMENT 'Person, organisation, news, community, relay',
`media-type` tinyint NOT NULL DEFAULT 0 COMMENT 'Type of media in a bit array (1 = image, 2 = video, 4 = audio',
`media-type` tinyint NOT NULL DEFAULT 0 COMMENT 'Type of media in a bit array (1 = image, 2 = video, 4 = audio)',
`language` char(2) COMMENT 'Language information about this post in the ISO 639-1 format',
`searchtext` mediumtext COMMENT 'Simplified text for the full text search',
`size` int unsigned COMMENT 'Body size',
@ -1439,6 +1439,36 @@ CREATE TABLE IF NOT EXISTS `post-media` (
FOREIGN KEY (`media-uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Attached media';
--
-- TABLE post-origin
--
CREATE TABLE IF NOT EXISTS `post-origin` (
`id` int unsigned NOT NULL,
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
`uid` mediumint unsigned NOT NULL COMMENT 'Owner id which owns this copy of the item',
`parent-uri-id` int unsigned COMMENT 'Id of the item-uri table that contains the parent uri',
`thr-parent-id` int unsigned COMMENT 'Id of the item-uri table that contains the thread parent uri',
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Creation timestamp.',
`received` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime',
`gravity` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
`vid` smallint unsigned COMMENT 'Id of the verb table entry that contains the activity verbs',
`private` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0=public, 1=private, 2=unlisted',
`wall` boolean NOT NULL DEFAULT '0' COMMENT 'This item was posted to the wall of uid',
PRIMARY KEY(`id`),
UNIQUE INDEX `uid_uri-id` (`uid`,`uri-id`),
INDEX `uri-id` (`uri-id`),
INDEX `parent-uri-id` (`parent-uri-id`),
INDEX `thr-parent-id` (`thr-parent-id`),
INDEX `vid` (`vid`),
INDEX `parent-uri-id_uid` (`parent-uri-id`,`uid`),
INDEX `uid_wall_received` (`uid`,`wall`,`received`),
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`parent-uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`thr-parent-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`vid`) REFERENCES `verb` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Posts from local users';
--
-- TABLE post-question
--
@ -1471,7 +1501,7 @@ CREATE TABLE IF NOT EXISTS `post-question-option` (
CREATE TABLE IF NOT EXISTS `post-searchindex` (
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
`owner-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Item owner',
`media-type` tinyint NOT NULL DEFAULT 0 COMMENT 'Type of media in a bit array (1 = image, 2 = video, 4 = audio',
`media-type` tinyint NOT NULL DEFAULT 0 COMMENT 'Type of media in a bit array (1 = image, 2 = video, 4 = audio)',
`language` char(2) COMMENT 'Language information about this post in the ISO 639-1 format',
`searchtext` mediumtext COMMENT 'Simplified text for the full text search',
`size` int unsigned COMMENT 'Body size',
@ -1550,6 +1580,7 @@ CREATE TABLE IF NOT EXISTS `post-user` (
`post-reason` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Reason why the post arrived at the user',
`vid` smallint unsigned COMMENT 'Id of the verb table entry that contains the activity verbs',
`private` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0=public, 1=private, 2=unlisted',
`restrictions` tinyint unsigned COMMENT 'Bit array of post restrictions (1 = Reply, 2 = Like, 4 = Announce)',
`global` boolean NOT NULL DEFAULT '0' COMMENT '',
`visible` boolean NOT NULL DEFAULT '0' COMMENT '',
`deleted` boolean NOT NULL DEFAULT '0' COMMENT 'item has been marked for deletion',
@ -1947,6 +1978,7 @@ CREATE TABLE IF NOT EXISTS `user-contact` (
`ignored` boolean COMMENT 'Posts from this contact are ignored',
`collapsed` boolean COMMENT 'Posts from this contact are collapsed',
`hidden` boolean COMMENT 'This contact is hidden from the others',
`channel-only` boolean COMMENT 'This contact is displayed only in channels, but not in the network stream.',
`is-blocked` boolean COMMENT 'User is blocked by this contact',
`channel-frequency` tinyint unsigned COMMENT 'Controls the frequency of the appearance of this contact in channels',
`pending` boolean COMMENT '',
@ -2000,7 +2032,7 @@ CREATE TABLE IF NOT EXISTS `worker-ipc` (
-- VIEW application-view
--
DROP VIEW IF EXISTS `application-view`;
CREATE VIEW `application-view` AS SELECT
CREATE VIEW `application-view` AS SELECT
`application`.`id` AS `id`,
`application-token`.`uid` AS `uid`,
`application`.`name` AS `name`,
@ -2024,7 +2056,7 @@ CREATE VIEW `application-view` AS SELECT
-- VIEW circle-member-view
--
DROP VIEW IF EXISTS `circle-member-view`;
CREATE VIEW `circle-member-view` AS SELECT
CREATE VIEW `circle-member-view` AS SELECT
`group_member`.`id` AS `id`,
`group`.`uid` AS `uid`,
`group_member`.`contact-id` AS `contact-id`,
@ -2055,7 +2087,7 @@ CREATE VIEW `circle-member-view` AS SELECT
-- VIEW post-counts-view
--
DROP VIEW IF EXISTS `post-counts-view`;
CREATE VIEW `post-counts-view` AS SELECT
CREATE VIEW `post-counts-view` AS SELECT
`post-counts`.`uri-id` AS `uri-id`,
`post-counts`.`vid` AS `vid`,
`verb`.`name` AS `verb`,
@ -2069,7 +2101,7 @@ CREATE VIEW `post-counts-view` AS SELECT
-- VIEW post-timeline-view
--
DROP VIEW IF EXISTS `post-timeline-view`;
CREATE VIEW `post-timeline-view` AS SELECT
CREATE VIEW `post-timeline-view` AS SELECT
`post-user`.`uid` AS `uid`,
`post-user`.`uri-id` AS `uri-id`,
`post-user`.`gravity` AS `gravity`,
@ -2110,11 +2142,57 @@ CREATE VIEW `post-timeline-view` AS SELECT
STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = `post-user`.`owner-id`
LEFT JOIN `contact` AS `causer` ON `causer`.`id` = `post-user`.`causer-id`;
--
-- VIEW post-timeline-origin-view
--
DROP VIEW IF EXISTS `post-timeline-origin-view`;
CREATE VIEW `post-timeline-origin-view` AS SELECT
`post-origin`.`uid` AS `uid`,
`post-origin`.`uri-id` AS `uri-id`,
`post-origin`.`gravity` AS `gravity`,
`post-origin`.`created` AS `created`,
`post-user`.`edited` AS `edited`,
`post-thread-user`.`commented` AS `commented`,
`post-origin`.`received` AS `received`,
`post-thread-user`.`changed` AS `changed`,
`post-origin`.`private` AS `private`,
`post-user`.`visible` AS `visible`,
`post-user`.`deleted` AS `deleted`,
true AS `origin`,
`post-user`.`global` AS `global`,
`post-user`.`network` AS `network`,
`post-user`.`protocol` AS `protocol`,
`post-origin`.`vid` AS `vid`,
`post-user`.`contact-id` AS `contact-id`,
`contact`.`blocked` AS `contact-blocked`,
`contact`.`readonly` AS `contact-readonly`,
`contact`.`pending` AS `contact-pending`,
`contact`.`rel` AS `contact-rel`,
`contact`.`uid` AS `contact-uid`,
`contact`.`self` AS `self`,
`post-user`.`author-id` AS `author-id`,
`author`.`blocked` AS `author-blocked`,
`author`.`hidden` AS `author-hidden`,
`author`.`gsid` AS `author-gsid`,
`post-user`.`owner-id` AS `owner-id`,
`owner`.`blocked` AS `owner-blocked`,
`owner`.`gsid` AS `owner-gsid`,
`post-user`.`causer-id` AS `causer-id`,
`causer`.`blocked` AS `causer-blocked`,
`causer`.`gsid` AS `causer-gsid`
FROM `post-origin`
INNER JOIN `post-user` ON `post-user`.`id` = `post-origin`.`id`
LEFT JOIN `post-thread-user` ON `post-thread-user`.`uri-id` = `post-origin`.`parent-uri-id` AND `post-thread-user`.`uid` = `post-origin`.`uid`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `post-user`.`contact-id`
STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = `post-user`.`author-id`
STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = `post-user`.`owner-id`
LEFT JOIN `contact` AS `causer` ON `causer`.`id` = `post-user`.`causer-id`;
--
-- VIEW post-searchindex-user-view
--
DROP VIEW IF EXISTS `post-searchindex-user-view`;
CREATE VIEW `post-searchindex-user-view` AS SELECT
CREATE VIEW `post-searchindex-user-view` AS SELECT
`post-thread-user`.`uid` AS `uid`,
`post-searchindex`.`uri-id` AS `uri-id`,
`post-searchindex`.`owner-id` AS `owner-id`,
@ -2142,11 +2220,385 @@ CREATE VIEW `post-searchindex-user-view` AS SELECT
AND NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = `post-thread-user`.`uid` AND `cid` IN (`authorcontact`.`id`, `ownercontact`.`id`) AND (`blocked` OR `ignored`))
AND NOT EXISTS(SELECT `gsid` FROM `user-gserver` WHERE `uid` = `post-thread-user`.`uid` AND `gsid` IN (`authorcontact`.`gsid`, `ownercontact`.`gsid`) AND `ignored`);
--
-- VIEW post-origin-view
--
DROP VIEW IF EXISTS `post-origin-view`;
CREATE VIEW `post-origin-view` AS SELECT
`post-origin`.`id` AS `id`,
`post-origin`.`id` AS `post-user-id`,
`post-origin`.`uid` AS `uid`,
`post-thread-user`.`post-user-id` AS `parent`,
`item-uri`.`uri` AS `uri`,
`post-origin`.`uri-id` AS `uri-id`,
`parent-item-uri`.`uri` AS `parent-uri`,
`post-origin`.`parent-uri-id` AS `parent-uri-id`,
`thr-parent-item-uri`.`uri` AS `thr-parent`,
`post-origin`.`thr-parent-id` AS `thr-parent-id`,
`conversation-item-uri`.`uri` AS `conversation`,
`post-thread-user`.`conversation-id` AS `conversation-id`,
`quote-item-uri`.`uri` AS `quote-uri`,
`post-content`.`quote-uri-id` AS `quote-uri-id`,
`item-uri`.`guid` AS `guid`,
`post-origin`.`wall` AS `wall`,
`post-origin`.`gravity` AS `gravity`,
`external-item-uri`.`uri` AS `extid`,
`post-user`.`external-id` AS `external-id`,
`post-origin`.`created` AS `created`,
`post-user`.`edited` AS `edited`,
`post-thread-user`.`commented` AS `commented`,
`post-origin`.`received` AS `received`,
`post-thread-user`.`changed` AS `changed`,
`post-user`.`post-type` AS `post-type`,
`post-user`.`post-reason` AS `post-reason`,
`post-origin`.`private` AS `private`,
`post-thread-user`.`pubmail` AS `pubmail`,
`post-user`.`visible` AS `visible`,
`post-thread-user`.`starred` AS `starred`,
`post-user`.`unseen` AS `unseen`,
`post-user`.`deleted` AS `deleted`,
true AS `origin`,
`post-thread-user`.`origin` AS `parent-origin`,
`post-thread-user`.`mention` AS `mention`,
`post-user`.`global` AS `global`,
EXISTS(SELECT `type` FROM `post-collection` WHERE `type` = 0 AND `uri-id` = `post-origin`.`uri-id`) AS `featured`,
`post-user`.`network` AS `network`,
`post-user`.`protocol` AS `protocol`,
`post-origin`.`vid` AS `vid`,
`post-user`.`psid` AS `psid`,
IF (`post-origin`.`vid` IS NULL, '', `verb`.`name`) AS `verb`,
`post-content`.`title` AS `title`,
`post-content`.`content-warning` AS `content-warning`,
`post-content`.`raw-body` AS `raw-body`,
IFNULL (`post-content`.`body`, '') AS `body`,
`post-content`.`rendered-hash` AS `rendered-hash`,
`post-content`.`rendered-html` AS `rendered-html`,
`post-content`.`language` AS `language`,
`post-content`.`plink` AS `plink`,
`post-content`.`location` AS `location`,
`post-content`.`coord` AS `coord`,
`post-content`.`sensitive` AS `sensitive`,
`post-user`.`restrictions` AS `restrictions`,
`post-content`.`app` AS `app`,
`post-content`.`object-type` AS `object-type`,
`post-content`.`object` AS `object`,
`post-content`.`target-type` AS `target-type`,
`post-content`.`target` AS `target`,
`post-content`.`resource-id` AS `resource-id`,
`post-user`.`contact-id` AS `contact-id`,
`contact`.`uri-id` AS `contact-uri-id`,
`contact`.`url` AS `contact-link`,
`contact`.`addr` AS `contact-addr`,
`contact`.`name` AS `contact-name`,
`contact`.`nick` AS `contact-nick`,
`contact`.`thumb` AS `contact-avatar`,
`contact`.`network` AS `contact-network`,
`contact`.`blocked` AS `contact-blocked`,
`contact`.`hidden` AS `contact-hidden`,
`contact`.`readonly` AS `contact-readonly`,
`contact`.`archive` AS `contact-archive`,
`contact`.`pending` AS `contact-pending`,
`contact`.`rel` AS `contact-rel`,
`contact`.`uid` AS `contact-uid`,
`contact`.`contact-type` AS `contact-contact-type`,
IF (`post-user`.`network` IN ('apub', 'dfrn', 'dspr', 'stat'), true, `contact`.`writable`) AS `writable`,
`contact`.`self` AS `self`,
`contact`.`id` AS `cid`,
`contact`.`alias` AS `alias`,
`contact`.`photo` AS `photo`,
`contact`.`name-date` AS `name-date`,
`contact`.`uri-date` AS `uri-date`,
`contact`.`avatar-date` AS `avatar-date`,
`contact`.`thumb` AS `thumb`,
`post-user`.`author-id` AS `author-id`,
`author`.`uri-id` AS `author-uri-id`,
`author`.`url` AS `author-link`,
`author`.`addr` AS `author-addr`,
IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`,
`author`.`nick` AS `author-nick`,
`author`.`alias` AS `author-alias`,
IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`,
`author`.`network` AS `author-network`,
`author`.`blocked` AS `author-blocked`,
`author`.`hidden` AS `author-hidden`,
`author`.`updated` AS `author-updated`,
`author`.`contact-type` AS `author-contact-type`,
`author`.`gsid` AS `author-gsid`,
`author`.`baseurl` AS `author-baseurl`,
`post-user`.`owner-id` AS `owner-id`,
`owner`.`uri-id` AS `owner-uri-id`,
`owner`.`url` AS `owner-link`,
`owner`.`addr` AS `owner-addr`,
IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`,
`owner`.`nick` AS `owner-nick`,
`owner`.`alias` AS `owner-alias`,
IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`,
`owner`.`network` AS `owner-network`,
`owner`.`blocked` AS `owner-blocked`,
`owner`.`hidden` AS `owner-hidden`,
`owner`.`updated` AS `owner-updated`,
`owner`.`gsid` AS `owner-gsid`,
`owner`.`contact-type` AS `owner-contact-type`,
`post-user`.`causer-id` AS `causer-id`,
`causer`.`uri-id` AS `causer-uri-id`,
`causer`.`url` AS `causer-link`,
`causer`.`addr` AS `causer-addr`,
`causer`.`name` AS `causer-name`,
`causer`.`nick` AS `causer-nick`,
`causer`.`alias` AS `causer-alias`,
`causer`.`thumb` AS `causer-avatar`,
`causer`.`network` AS `causer-network`,
`causer`.`blocked` AS `causer-blocked`,
`causer`.`hidden` AS `causer-hidden`,
`causer`.`gsid` AS `causer-gsid`,
`causer`.`contact-type` AS `causer-contact-type`,
`post-delivery-data`.`postopts` AS `postopts`,
`post-delivery-data`.`inform` AS `inform`,
`post-delivery-data`.`queue_count` AS `delivery_queue_count`,
`post-delivery-data`.`queue_done` AS `delivery_queue_done`,
`post-delivery-data`.`queue_failed` AS `delivery_queue_failed`,
IF (`post-user`.`psid` IS NULL, '', `permissionset`.`allow_cid`) AS `allow_cid`,
IF (`post-user`.`psid` IS NULL, '', `permissionset`.`allow_gid`) AS `allow_gid`,
IF (`post-user`.`psid` IS NULL, '', `permissionset`.`deny_cid`) AS `deny_cid`,
IF (`post-user`.`psid` IS NULL, '', `permissionset`.`deny_gid`) AS `deny_gid`,
`post-user`.`event-id` AS `event-id`,
`event`.`created` AS `event-created`,
`event`.`edited` AS `event-edited`,
`event`.`start` AS `event-start`,
`event`.`finish` AS `event-finish`,
`event`.`summary` AS `event-summary`,
`event`.`desc` AS `event-desc`,
`event`.`location` AS `event-location`,
`event`.`type` AS `event-type`,
`event`.`nofinish` AS `event-nofinish`,
`event`.`ignore` AS `event-ignore`,
`post-question`.`id` AS `question-id`,
`post-question`.`multiple` AS `question-multiple`,
`post-question`.`voters` AS `question-voters`,
`post-question`.`end-time` AS `question-end-time`,
EXISTS(SELECT `uri-id` FROM `post-category` WHERE `post-category`.`uri-id` = `post-origin`.`uri-id` AND `post-category`.`uid` = `post-origin`.`uid`) AS `has-categories`,
EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post-origin`.`uri-id`) AS `has-media`,
`diaspora-interaction`.`interaction` AS `signed_text`,
`parent-item-uri`.`guid` AS `parent-guid`,
`post-thread-user`.`network` AS `parent-network`,
`post-thread-user`.`author-id` AS `parent-author-id`,
`parent-post-author`.`url` AS `parent-author-link`,
`parent-post-author`.`name` AS `parent-author-name`,
`parent-post-author`.`nick` AS `parent-author-nick`,
`parent-post-author`.`network` AS `parent-author-network`
FROM `post-origin`
INNER JOIN `post-user` ON `post-user`.`id` = `post-origin`.`id`
INNER JOIN `post-thread-user` ON `post-thread-user`.`uri-id` = `post-origin`.`parent-uri-id` AND `post-thread-user`.`uid` = `post-origin`.`uid`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `post-user`.`contact-id`
STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = `post-user`.`author-id`
STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = `post-user`.`owner-id`
LEFT JOIN `contact` AS `causer` ON `causer`.`id` = `post-user`.`causer-id`
LEFT JOIN `item-uri` ON `item-uri`.`id` = `post-origin`.`uri-id`
LEFT JOIN `item-uri` AS `thr-parent-item-uri` ON `thr-parent-item-uri`.`id` = `post-origin`.`thr-parent-id`
LEFT JOIN `item-uri` AS `parent-item-uri` ON `parent-item-uri`.`id` = `post-origin`.`parent-uri-id`
LEFT JOIN `item-uri` AS `conversation-item-uri` ON `conversation-item-uri`.`id` = `post-thread-user`.`conversation-id`
LEFT JOIN `item-uri` AS `external-item-uri` ON `external-item-uri`.`id` = `post-user`.`external-id`
LEFT JOIN `verb` ON `verb`.`id` = `post-origin`.`vid`
LEFT JOIN `event` ON `event`.`id` = `post-user`.`event-id`
LEFT JOIN `diaspora-interaction` ON `diaspora-interaction`.`uri-id` = `post-origin`.`uri-id`
LEFT JOIN `post-content` ON `post-content`.`uri-id` = `post-origin`.`uri-id`
LEFT JOIN `item-uri` AS `quote-item-uri` ON `quote-item-uri`.`id` = `post-content`.`quote-uri-id`
LEFT JOIN `post-delivery-data` ON `post-delivery-data`.`uri-id` = `post-origin`.`uri-id`
LEFT JOIN `post-question` ON `post-question`.`uri-id` = `post-origin`.`uri-id`
LEFT JOIN `permissionset` ON `permissionset`.`id` = `post-user`.`psid`
LEFT JOIN `contact` AS `parent-post-author` ON `parent-post-author`.`id` = `post-thread-user`.`author-id`;
--
-- VIEW post-thread-origin-view
--
DROP VIEW IF EXISTS `post-thread-origin-view`;
CREATE VIEW `post-thread-origin-view` AS SELECT
`post-origin`.`id` AS `id`,
`post-origin`.`id` AS `post-user-id`,
`post-origin`.`uid` AS `uid`,
`post-thread-user`.`post-user-id` AS `parent`,
`item-uri`.`uri` AS `uri`,
`post-origin`.`uri-id` AS `uri-id`,
`parent-item-uri`.`uri` AS `parent-uri`,
`post-origin`.`parent-uri-id` AS `parent-uri-id`,
`thr-parent-item-uri`.`uri` AS `thr-parent`,
`post-origin`.`thr-parent-id` AS `thr-parent-id`,
`conversation-item-uri`.`uri` AS `conversation`,
`post-thread-user`.`conversation-id` AS `conversation-id`,
`quote-item-uri`.`uri` AS `quote-uri`,
`post-content`.`quote-uri-id` AS `quote-uri-id`,
`item-uri`.`guid` AS `guid`,
`post-origin`.`wall` AS `wall`,
`post-origin`.`gravity` AS `gravity`,
`external-item-uri`.`uri` AS `extid`,
`post-user`.`external-id` AS `external-id`,
`post-origin`.`created` AS `created`,
`post-user`.`edited` AS `edited`,
`post-thread-user`.`commented` AS `commented`,
`post-origin`.`received` AS `received`,
`post-thread-user`.`changed` AS `changed`,
`post-user`.`post-type` AS `post-type`,
`post-user`.`post-reason` AS `post-reason`,
`post-origin`.`private` AS `private`,
`post-thread-user`.`pubmail` AS `pubmail`,
`post-thread-user`.`ignored` AS `ignored`,
`post-user`.`visible` AS `visible`,
`post-thread-user`.`starred` AS `starred`,
`post-thread-user`.`unseen` AS `unseen`,
`post-user`.`deleted` AS `deleted`,
true AS `origin`,
`post-thread-user`.`mention` AS `mention`,
`post-user`.`global` AS `global`,
EXISTS(SELECT `type` FROM `post-collection` WHERE `type` = 0 AND `uri-id` = `post-thread-user`.`uri-id`) AS `featured`,
`post-thread-user`.`network` AS `network`,
`post-origin`.`vid` AS `vid`,
`post-thread-user`.`psid` AS `psid`,
IF (`post-origin`.`vid` IS NULL, '', `verb`.`name`) AS `verb`,
`post-content`.`title` AS `title`,
`post-content`.`content-warning` AS `content-warning`,
`post-content`.`raw-body` AS `raw-body`,
`post-content`.`body` AS `body`,
`post-content`.`rendered-hash` AS `rendered-hash`,
`post-content`.`rendered-html` AS `rendered-html`,
`post-content`.`language` AS `language`,
`post-content`.`plink` AS `plink`,
`post-content`.`location` AS `location`,
`post-content`.`coord` AS `coord`,
`post-content`.`sensitive` AS `sensitive`,
`post-user`.`restrictions` AS `restrictions`,
`post-content`.`app` AS `app`,
`post-content`.`object-type` AS `object-type`,
`post-content`.`object` AS `object`,
`post-content`.`target-type` AS `target-type`,
`post-content`.`target` AS `target`,
`post-content`.`resource-id` AS `resource-id`,
`post-thread-user`.`contact-id` AS `contact-id`,
`contact`.`uri-id` AS `contact-uri-id`,
`contact`.`url` AS `contact-link`,
`contact`.`addr` AS `contact-addr`,
`contact`.`name` AS `contact-name`,
`contact`.`nick` AS `contact-nick`,
`contact`.`thumb` AS `contact-avatar`,
`contact`.`network` AS `contact-network`,
`contact`.`blocked` AS `contact-blocked`,
`contact`.`hidden` AS `contact-hidden`,
`contact`.`readonly` AS `contact-readonly`,
`contact`.`archive` AS `contact-archive`,
`contact`.`pending` AS `contact-pending`,
`contact`.`rel` AS `contact-rel`,
`contact`.`uid` AS `contact-uid`,
`contact`.`gsid` AS `contact-gsid`,
`contact`.`contact-type` AS `contact-contact-type`,
IF (`post-user`.`network` IN ('apub', 'dfrn', 'dspr', 'stat'), true, `contact`.`writable`) AS `writable`,
`contact`.`self` AS `self`,
`contact`.`id` AS `cid`,
`contact`.`alias` AS `alias`,
`contact`.`photo` AS `photo`,
`contact`.`name-date` AS `name-date`,
`contact`.`uri-date` AS `uri-date`,
`contact`.`avatar-date` AS `avatar-date`,
`contact`.`thumb` AS `thumb`,
`post-thread-user`.`author-id` AS `author-id`,
`author`.`uri-id` AS `author-uri-id`,
`author`.`url` AS `author-link`,
`author`.`addr` AS `author-addr`,
IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`,
`author`.`nick` AS `author-nick`,
`author`.`alias` AS `author-alias`,
IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`,
`author`.`network` AS `author-network`,
`author`.`blocked` AS `author-blocked`,
`author`.`hidden` AS `author-hidden`,
`author`.`updated` AS `author-updated`,
`author`.`contact-type` AS `author-contact-type`,
`author`.`gsid` AS `author-gsid`,
`post-thread-user`.`owner-id` AS `owner-id`,
`owner`.`uri-id` AS `owner-uri-id`,
`owner`.`url` AS `owner-link`,
`owner`.`addr` AS `owner-addr`,
IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`,
`owner`.`nick` AS `owner-nick`,
`owner`.`alias` AS `owner-alias`,
IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`,
`owner`.`network` AS `owner-network`,
`owner`.`blocked` AS `owner-blocked`,
`owner`.`hidden` AS `owner-hidden`,
`owner`.`updated` AS `owner-updated`,
`owner`.`gsid` AS `owner-gsid`,
`owner`.`contact-type` AS `owner-contact-type`,
`post-thread-user`.`causer-id` AS `causer-id`,
`causer`.`uri-id` AS `causer-uri-id`,
`causer`.`url` AS `causer-link`,
`causer`.`addr` AS `causer-addr`,
`causer`.`name` AS `causer-name`,
`causer`.`nick` AS `causer-nick`,
`causer`.`alias` AS `causer-alias`,
`causer`.`thumb` AS `causer-avatar`,
`causer`.`network` AS `causer-network`,
`causer`.`blocked` AS `causer-blocked`,
`causer`.`hidden` AS `causer-hidden`,
`causer`.`gsid` AS `causer-gsid`,
`causer`.`contact-type` AS `causer-contact-type`,
`post-delivery-data`.`postopts` AS `postopts`,
`post-delivery-data`.`inform` AS `inform`,
`post-delivery-data`.`queue_count` AS `delivery_queue_count`,
`post-delivery-data`.`queue_done` AS `delivery_queue_done`,
`post-delivery-data`.`queue_failed` AS `delivery_queue_failed`,
IF (`post-thread-user`.`psid` IS NULL, '', `permissionset`.`allow_cid`) AS `allow_cid`,
IF (`post-thread-user`.`psid` IS NULL, '', `permissionset`.`allow_gid`) AS `allow_gid`,
IF (`post-thread-user`.`psid` IS NULL, '', `permissionset`.`deny_cid`) AS `deny_cid`,
IF (`post-thread-user`.`psid` IS NULL, '', `permissionset`.`deny_gid`) AS `deny_gid`,
`post-user`.`event-id` AS `event-id`,
`event`.`created` AS `event-created`,
`event`.`edited` AS `event-edited`,
`event`.`start` AS `event-start`,
`event`.`finish` AS `event-finish`,
`event`.`summary` AS `event-summary`,
`event`.`desc` AS `event-desc`,
`event`.`location` AS `event-location`,
`event`.`type` AS `event-type`,
`event`.`nofinish` AS `event-nofinish`,
`event`.`ignore` AS `event-ignore`,
`post-question`.`id` AS `question-id`,
`post-question`.`multiple` AS `question-multiple`,
`post-question`.`voters` AS `question-voters`,
`post-question`.`end-time` AS `question-end-time`,
EXISTS(SELECT `uri-id` FROM `post-category` WHERE `post-category`.`uri-id` = `post-thread-user`.`uri-id` AND `post-category`.`uid` = `post-thread-user`.`uid`) AS `has-categories`,
EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post-thread-user`.`uri-id`) AS `has-media`,
`diaspora-interaction`.`interaction` AS `signed_text`,
`parent-item-uri`.`guid` AS `parent-guid`,
`post-thread-user`.`network` AS `parent-network`,
`post-thread-user`.`author-id` AS `parent-author-id`,
`author`.`url` AS `parent-author-link`,
`author`.`name` AS `parent-author-name`,
`author`.`nick` AS `parent-author-nick`,
`author`.`network` AS `parent-author-network`
FROM `post-origin`
INNER JOIN `post-thread-user` ON `post-thread-user`.`uri-id` = `post-origin`.`uri-id` AND `post-thread-user`.`uid` = `post-origin`.`uid`
INNER JOIN `post-user` ON `post-user`.`id` = `post-origin`.`id`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `post-thread-user`.`contact-id`
STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = `post-thread-user`.`author-id`
STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = `post-thread-user`.`owner-id`
LEFT JOIN `contact` AS `causer` ON `causer`.`id` = `post-thread-user`.`causer-id`
LEFT JOIN `item-uri` ON `item-uri`.`id` = `post-origin`.`uri-id`
LEFT JOIN `item-uri` AS `thr-parent-item-uri` ON `thr-parent-item-uri`.`id` = `post-origin`.`thr-parent-id`
LEFT JOIN `item-uri` AS `parent-item-uri` ON `parent-item-uri`.`id` = `post-origin`.`parent-uri-id`
LEFT JOIN `item-uri` AS `conversation-item-uri` ON `conversation-item-uri`.`id` = `post-thread-user`.`conversation-id`
LEFT JOIN `item-uri` AS `external-item-uri` ON `external-item-uri`.`id` = `post-user`.`external-id`
LEFT JOIN `verb` ON `verb`.`id` = `post-origin`.`vid`
LEFT JOIN `event` ON `event`.`id` = `post-user`.`event-id`
LEFT JOIN `diaspora-interaction` ON `diaspora-interaction`.`uri-id` = `post-origin`.`uri-id`
LEFT JOIN `post-content` ON `post-content`.`uri-id` = `post-origin`.`uri-id`
LEFT JOIN `item-uri` AS `quote-item-uri` ON `quote-item-uri`.`id` = `post-content`.`quote-uri-id`
LEFT JOIN `post-delivery-data` ON `post-delivery-data`.`uri-id` = `post-origin`.`uri-id`
LEFT JOIN `post-question` ON `post-question`.`uri-id` = `post-origin`.`uri-id`
LEFT JOIN `permissionset` ON `permissionset`.`id` = `post-thread-user`.`psid`;
--
-- VIEW post-user-view
--
DROP VIEW IF EXISTS `post-user-view`;
CREATE VIEW `post-user-view` AS SELECT
CREATE VIEW `post-user-view` AS SELECT
`post-user`.`id` AS `id`,
`post-user`.`id` AS `post-user-id`,
`post-user`.`uid` AS `uid`,
@ -2200,6 +2652,7 @@ CREATE VIEW `post-user-view` AS SELECT
`post-content`.`location` AS `location`,
`post-content`.`coord` AS `coord`,
`post-content`.`sensitive` AS `sensitive`,
`post-user`.`restrictions` AS `restrictions`,
`post-content`.`app` AS `app`,
`post-content`.`object-type` AS `object-type`,
`post-content`.`object` AS `object`,
@ -2332,7 +2785,7 @@ CREATE VIEW `post-user-view` AS SELECT
-- VIEW post-thread-user-view
--
DROP VIEW IF EXISTS `post-thread-user-view`;
CREATE VIEW `post-thread-user-view` AS SELECT
CREATE VIEW `post-thread-user-view` AS SELECT
`post-user`.`id` AS `id`,
`post-user`.`id` AS `post-user-id`,
`post-thread-user`.`uid` AS `uid`,
@ -2385,6 +2838,7 @@ CREATE VIEW `post-thread-user-view` AS SELECT
`post-content`.`location` AS `location`,
`post-content`.`coord` AS `coord`,
`post-content`.`sensitive` AS `sensitive`,
`post-user`.`restrictions` AS `restrictions`,
`post-content`.`app` AS `app`,
`post-content`.`object-type` AS `object-type`,
`post-content`.`object` AS `object`,
@ -2516,7 +2970,7 @@ CREATE VIEW `post-thread-user-view` AS SELECT
-- VIEW post-view
--
DROP VIEW IF EXISTS `post-view`;
CREATE VIEW `post-view` AS SELECT
CREATE VIEW `post-view` AS SELECT
`item-uri`.`uri` AS `uri`,
`post`.`uri-id` AS `uri-id`,
`parent-item-uri`.`uri` AS `parent-uri`,
@ -2663,7 +3117,7 @@ CREATE VIEW `post-view` AS SELECT
-- VIEW post-thread-view
--
DROP VIEW IF EXISTS `post-thread-view`;
CREATE VIEW `post-thread-view` AS SELECT
CREATE VIEW `post-thread-view` AS SELECT
`item-uri`.`uri` AS `uri`,
`post-thread`.`uri-id` AS `uri-id`,
`parent-item-uri`.`uri` AS `parent-uri`,
@ -2811,7 +3265,7 @@ CREATE VIEW `post-thread-view` AS SELECT
-- VIEW category-view
--
DROP VIEW IF EXISTS `category-view`;
CREATE VIEW `category-view` AS SELECT
CREATE VIEW `category-view` AS SELECT
`post-category`.`uri-id` AS `uri-id`,
`post-category`.`uid` AS `uid`,
`post-category`.`type` AS `type`,
@ -2825,7 +3279,7 @@ CREATE VIEW `category-view` AS SELECT
-- VIEW collection-view
--
DROP VIEW IF EXISTS `collection-view`;
CREATE VIEW `collection-view` AS SELECT
CREATE VIEW `collection-view` AS SELECT
`post-collection`.`uri-id` AS `uri-id`,
`post-collection`.`type` AS `type`,
`post-collection`.`author-id` AS `cid`,
@ -2846,7 +3300,7 @@ CREATE VIEW `collection-view` AS SELECT
-- VIEW media-view
--
DROP VIEW IF EXISTS `media-view`;
CREATE VIEW `media-view` AS SELECT
CREATE VIEW `media-view` AS SELECT
`post-media`.`uri-id` AS `uri-id`,
`post-media`.`type` AS `type`,
`post`.`received` AS `received`,
@ -2864,7 +3318,7 @@ CREATE VIEW `media-view` AS SELECT
-- VIEW tag-view
--
DROP VIEW IF EXISTS `tag-view`;
CREATE VIEW `tag-view` AS SELECT
CREATE VIEW `tag-view` AS SELECT
`post-tag`.`uri-id` AS `uri-id`,
`post-tag`.`type` AS `type`,
`post-tag`.`tid` AS `tid`,
@ -2876,41 +3330,11 @@ CREATE VIEW `tag-view` AS SELECT
LEFT JOIN `tag` ON `post-tag`.`tid` = `tag`.`id`
LEFT JOIN `contact` ON `post-tag`.`cid` = `contact`.`id`;
--
-- VIEW network-item-view
--
DROP VIEW IF EXISTS `network-item-view`;
CREATE VIEW `network-item-view` AS SELECT
`post-user`.`uri-id` AS `uri-id`,
`post-thread-user`.`post-user-id` AS `parent`,
`post-user`.`received` AS `received`,
`post-thread-user`.`commented` AS `commented`,
`post-user`.`created` AS `created`,
`post-user`.`uid` AS `uid`,
`post-thread-user`.`starred` AS `starred`,
`post-thread-user`.`mention` AS `mention`,
`post-user`.`network` AS `network`,
`post-user`.`unseen` AS `unseen`,
`post-user`.`gravity` AS `gravity`,
`post-user`.`contact-id` AS `contact-id`,
`ownercontact`.`contact-type` AS `contact-type`
FROM `post-user`
INNER JOIN `post-thread-user` ON `post-thread-user`.`uri-id` = `post-user`.`parent-uri-id` AND `post-thread-user`.`uid` = `post-user`.`uid`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `post-thread-user`.`contact-id`
STRAIGHT_JOIN `contact` AS `authorcontact` ON `authorcontact`.`id` = `post-thread-user`.`author-id`
STRAIGHT_JOIN `contact` AS `ownercontact` ON `ownercontact`.`id` = `post-thread-user`.`owner-id`
WHERE `post-user`.`visible` AND NOT `post-user`.`deleted`
AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`)
AND (`post-user`.`hidden` IS NULL OR NOT `post-user`.`hidden`)
AND NOT `authorcontact`.`blocked` AND NOT `ownercontact`.`blocked`
AND NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = `post-thread-user`.`uid` AND `cid` IN (`authorcontact`.`id`, `ownercontact`.`id`) AND (`blocked` OR `ignored`))
AND NOT EXISTS(SELECT `gsid` FROM `user-gserver` WHERE `uid` = `post-thread-user`.`uid` AND `gsid` IN (`authorcontact`.`gsid`, `ownercontact`.`gsid`) AND `ignored`);
--
-- VIEW network-thread-view
--
DROP VIEW IF EXISTS `network-thread-view`;
CREATE VIEW `network-thread-view` AS SELECT
CREATE VIEW `network-thread-view` AS SELECT
`post-thread-user`.`uri-id` AS `uri-id`,
`post-thread-user`.`post-user-id` AS `parent`,
`post-thread-user`.`received` AS `received`,
@ -2931,14 +3355,14 @@ CREATE VIEW `network-thread-view` AS SELECT
AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`)
AND (`post-thread-user`.`hidden` IS NULL OR NOT `post-thread-user`.`hidden`)
AND NOT `authorcontact`.`blocked` AND NOT `ownercontact`.`blocked`
AND NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = `post-thread-user`.`uid` AND `cid` IN (`authorcontact`.`id`, `ownercontact`.`id`) AND (`blocked` OR `ignored`))
AND NOT EXISTS(SELECT `gsid` FROM `user-gserver` WHERE `uid` = `post-thread-user`.`uid` AND `gsid` IN (`authorcontact`.`gsid`, `ownercontact`.`gsid`) AND `ignored`);
AND NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = `post-thread-user`.`uid` AND `cid` IN (`post-thread-user`.`author-id`, `post-thread-user`.`owner-id`, `post-thread-user`.`causer-id`) AND (`blocked` OR `ignored` OR `channel-only`))
AND NOT EXISTS(SELECT `gsid` FROM `user-gserver` WHERE `uid` = `post-thread-user`.`uid` AND `gsid` IN (`authorcontact`.`gsid`, `ownercontact`.`gsid`) AND `ignored`);
--
-- VIEW owner-view
--
DROP VIEW IF EXISTS `owner-view`;
CREATE VIEW `owner-view` AS SELECT
CREATE VIEW `owner-view` AS SELECT
`contact`.`id` AS `id`,
`contact`.`uid` AS `uid`,
`contact`.`created` AS `created`,
@ -3066,7 +3490,7 @@ CREATE VIEW `owner-view` AS SELECT
-- VIEW account-view
--
DROP VIEW IF EXISTS `account-view`;
CREATE VIEW `account-view` AS SELECT
CREATE VIEW `account-view` AS SELECT
`contact`.`id` AS `id`,
`contact`.`url` AS `url`,
`contact`.`nurl` AS `nurl`,
@ -3154,7 +3578,7 @@ CREATE VIEW `account-view` AS SELECT
-- VIEW account-user-view
--
DROP VIEW IF EXISTS `account-user-view`;
CREATE VIEW `account-user-view` AS SELECT
CREATE VIEW `account-user-view` AS SELECT
`ucontact`.`id` AS `id`,
`contact`.`id` AS `pid`,
`ucontact`.`uid` AS `uid`,
@ -3260,7 +3684,7 @@ CREATE VIEW `account-user-view` AS SELECT
-- VIEW pending-view
--
DROP VIEW IF EXISTS `pending-view`;
CREATE VIEW `pending-view` AS SELECT
CREATE VIEW `pending-view` AS SELECT
`register`.`id` AS `id`,
`register`.`hash` AS `hash`,
`register`.`created` AS `created`,
@ -3282,7 +3706,7 @@ CREATE VIEW `pending-view` AS SELECT
-- VIEW tag-search-view
--
DROP VIEW IF EXISTS `tag-search-view`;
CREATE VIEW `tag-search-view` AS SELECT
CREATE VIEW `tag-search-view` AS SELECT
`post-tag`.`uri-id` AS `uri-id`,
`post-user`.`uid` AS `uid`,
`post-user`.`id` AS `iid`,
@ -3304,7 +3728,7 @@ CREATE VIEW `tag-search-view` AS SELECT
-- VIEW workerqueue-view
--
DROP VIEW IF EXISTS `workerqueue-view`;
CREATE VIEW `workerqueue-view` AS SELECT
CREATE VIEW `workerqueue-view` AS SELECT
`process`.`pid` AS `pid`,
`workerqueue`.`priority` AS `priority`
FROM `process`
@ -3315,7 +3739,7 @@ CREATE VIEW `workerqueue-view` AS SELECT
-- VIEW profile_field-view
--
DROP VIEW IF EXISTS `profile_field-view`;
CREATE VIEW `profile_field-view` AS SELECT
CREATE VIEW `profile_field-view` AS SELECT
`profile_field`.`id` AS `id`,
`profile_field`.`uid` AS `uid`,
`profile_field`.`label` AS `label`,
@ -3335,7 +3759,7 @@ CREATE VIEW `profile_field-view` AS SELECT
-- VIEW diaspora-contact-view
--
DROP VIEW IF EXISTS `diaspora-contact-view`;
CREATE VIEW `diaspora-contact-view` AS SELECT
CREATE VIEW `diaspora-contact-view` AS SELECT
`diaspora-contact`.`uri-id` AS `uri-id`,
`item-uri`.`uri` AS `url`,
`item-uri`.`guid` AS `guid`,

View File

@ -850,10 +850,6 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
Hook::callAll('register_account', $uid);
Hook::callAll('remove_user', $user);
### src/Module/Notifications/Ping.php
Hook::callAll('network_ping', $arr);
### src/Module/PermissionTooltip.php
Hook::callAll('lockview_content', $item);

View File

@ -23,7 +23,7 @@ Predefined Channels
* For you: Posts from contacts you interact with and who interact with you. In detail, it consists of:
* Posts from people you interact with on a more than average level.
* Posts from the accounts that you follow with a more than average number of interactions-
* Posts from the accounts that you follow with a more than average number of interactions.
* Posts from accounts where you activated "notify on new posts" or where you have set the channel frequency accordingly.
* Discover: Posts from contacts you don't follow, but that might be of interest for you to follow. In detail, it consists of:
* Posts from people you don't follow but you interact with on a more than average level.
@ -48,11 +48,11 @@ Each channel is defined by these values:
* Label: This value is mandatory and is used for the menu label.
* Description: A short description of the content. This can help to keep the overview, when you have got a lot of channels.
* Access Key: When you want to access this channel via an access key, you can define it here. Pay attention to not use an already used one.
* Circle: This defines the data source for this channel. By default it is set to the public timeline. There are some predefined values, like the accounts that you follow or the accounts that follow you. Also all of your circles can be selected.
* Circle: This defines the data source for this channel. By default it is set to the public timeline. There are some predefined values, like the accounts that you follow or the accounts that follow you. Also all of your circles can be selected.
* Include Tags: Comma separated list of tags. A post will be used when it contains any of the listed tags.
* Exclude Tags: Comma separated list of tags. If a post contain any of these tags, then it will not be part of nthis channel.
* Exclude Tags: Comma separated list of tags. If a post contain any of these tags, then it will not be part of this channel.
* Full Text Search: This can be used to include or exclude content, based on the content and some additional keywords. It uses the "boolean mode" operators from MariaDB: https://mariadb.com/kb/en/full-text-index-overview/#in-boolean-mode
* Images, Videos, Audio: When selected, you will see content with the selected media type. This can be combined. If none of these fields are checked, you will see any content, with or without attacked media.
* Images, Videos, Audio: When selected, you will see content with the selected media type. This can be combined. If none of these fields are checked, you will see any content, with or without attached media.
Additional keywords for the full text search
---
@ -61,8 +61,8 @@ Additionally to the search for content, there are keywords that can be used in t
Alternatives are presented with "|".
* from - Use "from:nickname" or "from:nickname@domain.tld" to search for posts from a specific author.
* to - Use "from:nickname" or "from:nickname@domain.tld" to search for posts with the given contact as receiver.
* group - Use "group:nickname" or "group:nickname@domain.tld" to search for group post of the given group.
* to - Use "to:nickname" or "to:nickname@domain.tld" to search for posts with the given contact as receiver.
* group - Use "group:nickname" or "group:nickname@domain.tld" to search for posts of the given group.
* application | relay - Use "application:nickname" or "application:nickname@domain.tld" to search for posts that had been reshared by the given relay application.
* server - Use "server:hostname" to search for posts from a specific server. In the case of group postings, the search text contains both the hostname of the group server and the author's hostname.
* source - The ActivityPub type of the post source. Use this for example to include or exclude group posts or posts from services (aka bots).
@ -93,7 +93,7 @@ Alternatives are presented with "|".
* visibility:public
* visibility:unlisted
* visibility:private
* language | lang - Use "language:code" to search for posts with the given language in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
* language | lang - Use "language:code" to search for posts with the given language in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
Remember that you can combine these kerywords.
So for example you can create a channel with all posts that talk about the Fediverse - that aren't posted in the Fediverse with the search terms: "fediverse -network:apub -network:dfrn"
Remember that you can combine these keywords.
So for example you can create a channel with all posts that talk about the Fediverse - that aren't posted in the Fediverse with the search terms: "fediverse -network:apub -network:dfrn"

View File

@ -30,7 +30,7 @@ Due to the large variety of operating systems and PHP platforms in existence we
* Apache with mod-rewrite enabled and "Options All" so you can use a local `.htaccess` file
* PHP 7.4+
* PHP *command line* access with register_argc_argv set to true in the php.ini file
* Curl, GD, GMP, PDO, mbstrings, MySQLi, hash, xml, zip, IntlChar and OpenSSL extensions
* Curl, GD, GMP, PDO, mbstrings, MySQLi, hash, xml, zip, IntlChar, IDN and OpenSSL extensions
* The POSIX module of PHP needs to be activated (e.g. [RHEL, CentOS](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) have disabled it)
* Some form of email server or email gateway such that PHP mail() works.
If you cannot set up your own email server, you can use the [phpmailer](https://github.com/friendica/friendica-addons/tree/develop/phpmailer) addon and use a remote SMTP server.

View File

@ -68,6 +68,7 @@ Database Tables
| [post-history](help/database/db_post-history) | Post history |
| [post-link](help/database/db_post-link) | Post related external links |
| [post-media](help/database/db_post-media) | Attached media |
| [post-origin](help/database/db_post-origin) | Posts from local users |
| [post-question](help/database/db_post-question) | Question |
| [post-question-option](help/database/db_post-question-option) | Question option |
| [post-searchindex](help/database/db_post-searchindex) | Content for all posts |

View File

@ -11,7 +11,7 @@ Fields
| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | PRI | NULL | |
| owner-id | Item owner | int unsigned | NO | | 0 | |
| contact-type | Person, organisation, news, community, relay | tinyint | NO | | 0 | |
| media-type | Type of media in a bit array (1 = image, 2 = video, 4 = audio | tinyint | NO | | 0 | |
| media-type | Type of media in a bit array (1 = image, 2 = video, 4 = audio) | tinyint | NO | | 0 | |
| language | Language information about this post in the ISO 639-1 format | char(2) | YES | | NULL | |
| searchtext | Simplified text for the full text search | mediumtext | YES | | NULL | |
| size | Body size | int unsigned | YES | | NULL | |

View File

@ -0,0 +1,48 @@
Table post-origin
===========
Posts from local users
Fields
------
| Field | Description | Type | Null | Key | Default | Extra |
| ------------- | ------------------------------------------------------------ | ------------------ | ---- | --- | ------------------- | ----- |
| id | | int unsigned | NO | PRI | NULL | |
| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | | NULL | |
| uid | Owner id which owns this copy of the item | mediumint unsigned | NO | | NULL | |
| parent-uri-id | Id of the item-uri table that contains the parent uri | int unsigned | YES | | NULL | |
| thr-parent-id | Id of the item-uri table that contains the thread parent uri | int unsigned | YES | | NULL | |
| created | Creation timestamp. | datetime | NO | | 0001-01-01 00:00:00 | |
| received | datetime | datetime | NO | | 0001-01-01 00:00:00 | |
| gravity | | tinyint unsigned | NO | | 0 | |
| vid | Id of the verb table entry that contains the activity verbs | smallint unsigned | YES | | NULL | |
| private | 0=public, 1=private, 2=unlisted | tinyint unsigned | NO | | 0 | |
| wall | This item was posted to the wall of uid | boolean | NO | | 0 | |
Indexes
------------
| Name | Fields |
| ----------------- | ------------------- |
| PRIMARY | id |
| uid_uri-id | UNIQUE, uid, uri-id |
| uri-id | uri-id |
| parent-uri-id | parent-uri-id |
| thr-parent-id | thr-parent-id |
| vid | vid |
| parent-uri-id_uid | parent-uri-id, uid |
| uid_wall_received | uid, wall, received |
Foreign Keys
------------
| Field | Target Table | Target Field |
|-------|--------------|--------------|
| uri-id | [item-uri](help/database/db_item-uri) | id |
| uid | [user](help/database/db_user) | uid |
| parent-uri-id | [item-uri](help/database/db_item-uri) | id |
| thr-parent-id | [item-uri](help/database/db_item-uri) | id |
| vid | [verb](help/database/db_verb) | id |
Return to [database documentation](help/database)

View File

@ -10,7 +10,7 @@ Fields
| ---------- | --------------------------------------------------------------------- | ------------ | ---- | --- | ------- | ----- |
| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | PRI | NULL | |
| owner-id | Item owner | int unsigned | NO | | 0 | |
| media-type | Type of media in a bit array (1 = image, 2 = video, 4 = audio | tinyint | NO | | 0 | |
| media-type | Type of media in a bit array (1 = image, 2 = video, 4 = audio) | tinyint | NO | | 0 | |
| language | Language information about this post in the ISO 639-1 format | char(2) | YES | | NULL | |
| searchtext | Simplified text for the full text search | mediumtext | YES | | NULL | |
| size | Body size | int unsigned | YES | | NULL | |

View File

@ -25,6 +25,7 @@ Fields
| post-reason | Reason why the post arrived at the user | tinyint unsigned | NO | | 0 | |
| vid | Id of the verb table entry that contains the activity verbs | smallint unsigned | YES | | NULL | |
| private | 0=public, 1=private, 2=unlisted | tinyint unsigned | NO | | 0 | |
| restrictions | Bit array of post restrictions (1 = Reply, 2 = Like, 4 = Announce) | tinyint unsigned | YES | | NULL | |
| global | | boolean | NO | | 0 | |
| visible | | boolean | NO | | 0 | |
| deleted | item has been marked for deletion | boolean | NO | | 0 | |

View File

@ -6,29 +6,30 @@ User specific public contact data
Fields
------
| Field | Description | Type | Null | Key | Default | Extra |
| ------------------------- | ----------------------------------------------------------------------- | ------------------ | ---- | --- | ------- | ----- |
| cid | Contact id of the linked public contact | int unsigned | NO | PRI | 0 | |
| uid | User id | mediumint unsigned | NO | PRI | 0 | |
| uri-id | Id of the item-uri table entry that contains the contact url | int unsigned | YES | | NULL | |
| blocked | Contact is completely blocked for this user | boolean | YES | | NULL | |
| ignored | Posts from this contact are ignored | boolean | YES | | NULL | |
| collapsed | Posts from this contact are collapsed | boolean | YES | | NULL | |
| hidden | This contact is hidden from the others | boolean | YES | | NULL | |
| is-blocked | User is blocked by this contact | boolean | YES | | NULL | |
| channel-frequency | Controls the frequency of the appearance of this contact in channels | tinyint unsigned | YES | | NULL | |
| pending | | boolean | YES | | NULL | |
| rel | The kind of the relation between the user and the contact | tinyint unsigned | YES | | NULL | |
| info | | mediumtext | YES | | NULL | |
| notify_new_posts | | boolean | YES | | NULL | |
| remote_self | 0 => No mirroring, 1-2 => Mirror as own post, 3 => Mirror as reshare | tinyint unsigned | YES | | NULL | |
| fetch_further_information | 0 => None, 1 => Fetch information, 3 => Fetch keywords, 2 => Fetch both | tinyint unsigned | YES | | NULL | |
| ffi_keyword_denylist | | text | YES | | NULL | |
| subhub | | boolean | YES | | NULL | |
| hub-verify | | varbinary(383) | YES | | NULL | |
| protocol | Protocol of the contact | char(4) | YES | | NULL | |
| rating | Automatically detected feed poll frequency | tinyint | YES | | NULL | |
| priority | Feed poll priority | tinyint unsigned | YES | | NULL | |
| Field | Description | Type | Null | Key | Default | Extra |
| ------------------------- | -------------------------------------------------------------------------- | ------------------ | ---- | --- | ------- | ----- |
| cid | Contact id of the linked public contact | int unsigned | NO | PRI | 0 | |
| uid | User id | mediumint unsigned | NO | PRI | 0 | |
| uri-id | Id of the item-uri table entry that contains the contact url | int unsigned | YES | | NULL | |
| blocked | Contact is completely blocked for this user | boolean | YES | | NULL | |
| ignored | Posts from this contact are ignored | boolean | YES | | NULL | |
| collapsed | Posts from this contact are collapsed | boolean | YES | | NULL | |
| hidden | This contact is hidden from the others | boolean | YES | | NULL | |
| channel-only | This contact is displayed only in channels, but not in the network stream. | boolean | YES | | NULL | |
| is-blocked | User is blocked by this contact | boolean | YES | | NULL | |
| channel-frequency | Controls the frequency of the appearance of this contact in channels | tinyint unsigned | YES | | NULL | |
| pending | | boolean | YES | | NULL | |
| rel | The kind of the relation between the user and the contact | tinyint unsigned | YES | | NULL | |
| info | | mediumtext | YES | | NULL | |
| notify_new_posts | | boolean | YES | | NULL | |
| remote_self | 0 => No mirroring, 1-2 => Mirror as own post, 3 => Mirror as reshare | tinyint unsigned | YES | | NULL | |
| fetch_further_information | 0 => None, 1 => Fetch information, 3 => Fetch keywords, 2 => Fetch both | tinyint unsigned | YES | | NULL | |
| ffi_keyword_denylist | | text | YES | | NULL | |
| subhub | | boolean | YES | | NULL | |
| hub-verify | | varbinary(383) | YES | | NULL | |
| protocol | Protocol of the contact | char(4) | YES | | NULL | |
| rating | Automatically detected feed poll frequency | tinyint | YES | | NULL | |
| priority | Feed poll priority | tinyint unsigned | YES | | NULL | |
Indexes
------------

View File

@ -418,10 +418,6 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
Hook::callAll('storage_instance', $data);
Hook::callAll('storage_config', $data);
### src/Module/Notifications/Ping.php
Hook::callAll('network_ping', $arr);
### src/Module/PermissionTooltip.php
Hook::callAll('lockview_content', $item);

99
doc/de/Channels.md Normal file
View File

@ -0,0 +1,99 @@
Kanäle (Channels)
=====
* [Home](help)
Kanäle sind eine Möglichkeit neue Inhalte zu finden, oder Inhalte anzuzeigen, die du sonst möglicherweise verpasst hättest.
Es gibt mehrere vordefinierte Kanäle und zusätzlich kannst du deine eigenen, basierend auf ein paar Regeln, erstellen.
Kanäle zeigen nur Beiträge aus den letzten 24 Stunden an. (Dieser Wert kann vom Administrator geändert werden.)
In den Anzeige-Einstellungen, im Bereich "Timelines", kannst du definieren, welche Kanäle und andere Timelines du im "Kanäle"-Widget auf der "Network"-Seite sehen möchtest und welche Kanäle in der Menüleiste oben auf der Seite erscheinen sollen.
Ebenfalls in den Anzeige-Einstellungen, im Bereich "Kanäle", kannst du alle die Sprachen einstellen, die du in deinen Kanälen sehen möchtest. Hier kannst du mehr als eine Sprache auswählen.
Auf der Profilseite kannst du die Kanal-Frequenz für jeden Kontakt definieren. Die Optionen sind:
* Standardhäufigkeit: Beiträge dieses Kontakts werden im "Für Dich"-Kanal angezeigt, wenn du häufig mit diesem Kontakt interagiert hast oder wenn ein Beitrag ein gewisses Maß an Interaktion erreicht hat.
* Alle Beiträge dieses Kontakts anzeigen: Alle Beiträge dieses Kontakts werden auf dem Kanal "Für Dich" erscheinen
* Zeige nur einige Beiträge an: Wenn ein Kontakt viele Beiträge in einem kurzen Zeitraum erstellt, reduziert diese Einstellung die Anzahl der angezeigten Beiträge in jedem Kanal.
* Zeige keine Beiträge an: Beiträge von diesem Kontakt werden in keinem Kanal angezeigt.
Voreingestellte Kanäle
---
* Für Dich: Beiträge von Kontakten mit denen du interagierst und die mit dir interagieren. Im Detail bestehend aus:
* Beiträge von Leuten, mit denen du überdurchschnittlich viel interagierst.
* Beiträge von Kontakten, denen du folgst und mit denen du überdurchschnittlich viel interagierst.
* Beiträge von Kontakten, bei denen du "Benachrichtigung bei neuen Beiträgen" aktiviert hast oder wo du die Kanalfrequenz entsprechend eingestellt hast.
* Entdecken: Beiträge von Kontakten denen du nicht folgst, aber denen zu folgen für dich interessant sein könnte. Im Detail bestehend aus:
* Beiträge von Leuten denen du nicht folgst, aber mit denen du überdurchschnittlich viel interagierst.
* Beiträge von Leuten denen du nicht folgst, aber die mit dir überdurchschnittlich viel interagieren.
* Beliebte Beiträge von Leuten denen du nicht folgst, aber mit denen du interagiert hast oder die mit dir interagiert haben.
* Angesagt: Beiträge mit überdurchschnittlich hoher Anzahl von Interaktionen.
* Sprache: Beiträge in deiner Sprache.
* Folgende: Beiträge von Leuten die dir folgen, aber denen du nicht folgst.
* Geteilt von teilenden: Beiträge von Kontakten denen die Leute folgen, denen du folgst.
* Ruhige teilende: Beiträge von Konten denen du folgst, aber die nicht sehr oft posten.
* Bilder: Beiträge mit Bildern.
* Audio: Beiträge mit Audio.
* Videos: Beiträge mit Videos.
Vom Benutzer eingestellte Kanäle
---
In den Einstellungen, unter "Kanäle", kannst du deine eigenen Kanäle erstellen.
Jeder Kanal wird durch diese Werte definiert:
* Bezeichnung: Dieses Feld ist notwendig und wird für die Kanalbezeichnung verwendet.
* Beschreibung: Eine kurze Beschreibung des Inhalts. Dies kann helfen den Überblick zu behalten, wenn du viele Kanäle hast.
* Zugriffsschlüssel: Wenn du auf diesen Kanal über einen Zugriffsschlüssel zugreifen willst, kannst du ihn hier festlegen. Achte darauf, dass du nicht einen bereits verwendeten Schlüssel benutzt.
* Circle/Kanal: Dies definiert die Datenquelle für diesen Kanal. Voreingestellt ist die Globale Gemeinschaft. Es gibt ein paar vorgegebene Werte, wie die Konten denen du folgst, oder die Kontakte, die dir folgen. Außerdem können alle deine Circles ausgewählt werden.
* Tags einschließen: Durch Kommata getrennte Liste von Tags. Ein Beitrag wird verwendet, wenn er eines der aufgeführten Tags enthält.
* Tags ausschließen: Durch Kommata getrennte Liste von Tags. Wenn ein Beitrag eines dieser Tags enthält, wird er nicht Teil dieses Kanals sein.
* Volltextsuche: Dies kann genutzt werden um Inhalte, basierend auf dem Inhalt und ein paar zusätzlichen Schlüsselwörtern, ein- oder auszuschließen. Es nutzt die "boolean mode"-Operatoren von MariaDB: https://mariadb.com/kb/en/full-text-index-overview/#in-boolean-mode
* Bilder, Videos, Audio: Wenn ausgewählt, wirst du Inhalte mit dem gewählten Medientyp sehen. Diese Optionen können kombiniert werden. Wenn keines dieser Felder ausgewählt wurde, wirst du alle Inhalte, mit oder ohne angefügten Medien, sehen.
Zusätzliche Schlüsselwörter für die Volltextsuche
---
Zusätzlich zu der Suche nach Inhalten, gibt es Schlüsselwörter, die in der Volltextsuche genutzt werden können.
Alternativen werden durch "|" dargestellt.
* from - Verwende "from:nickname" oder "from:nickname@domain.tld" um nach Beiträgen von einem bestimmten Autor zu suchen.
* to - Verwende "to:nickname" oder "to:nickname@domain.tld" um nach Beiträgen mit dem gegebenen Empfänger zu suchen.
* group - Verwende "group:nickname" oder "group:nickname@domain.tld" um nach Beiträgen aus der gegebenen Gruppe zu suchen.
* application | relay - Nutze "application:nickname" oder "application:nickname@domain.tld" um Beiträge zu finden, die von der gegebenen relay application geteilt wurden.
* server - Verwende "server:hostname" um Beiträge von einem bestimmten Server zu suchen. Im Falle eine Gruppen-Postings enthält der Suchtext beides, den Hostname des Gruppen-Servers und den Hostname des Autors.
* source - Der ActivityPub-Typ der Beitragsquelle. Nutze dies um beispielsweise Gruppenpostings oder Beiträge von Services (aka Bots) ein- oder auszuschließen.
* source:person - Der Beitrag wurde von einem regulären Nutzerkonto erstellt.
* source:organization - Der Beitrag wurde von einer Organisation erstellt.
* source:group - Dieser Beitrag wurde über eine Gruppe erstellt oder verteilt.
* source:service | source:news - Dieser Beitrag stammt aus einem 'service' Account. Dieser Quellen(source)-Typ wird oft genutzt um Bot Accounts zu markieren.
* source:application | source:relay - Dieser Beitrag wurde von einer Anwendung (application) erstellt. Dies wird im Fediverse höchstwahrscheinlich für die Beitragserstellung nicht genutzt.
* tag - Nutze "tag:tagname" um nach einem bestimmten tag (Schlagwort) zu suchen.
* media - Mit diesem Schlüsselwort kannst du nach angefügten Medien suchen.
* media:image | media:photo | media:picture - Dieser Beitrag enthält ein Bild
* media:video - Dieser Beitrag enthält ein Video
* media:audio - Dieser Beitrag enthält Audio
* media:card - Dieser Beitrag enthält eine Linkvorschau-'card'
* media:post - Dieser Beitrag verweist auf einen anderen Beitrag, was bedeutet, es ist ein zitierter Beitrag
* network | net - Verwende dies um Netzwerke in deinen Kanal einzuschließen oder von ihm auszuschließen.
* network:apub | network:activitypub - ActivityPub (verwendet von den Systemen im Fediverse)
* network:dfrn | network:friendica - altes Friendica-Protokoll. Heutzutage nutzt Friendica meist ActivityPub.
* network:dspr | network:diaspora - Das Diaspora-Protokoll wird hauptsächlich von Diaspora selbst genutzt. Ein paar andere Systeme unterstützen dieses Protokoll ebenfalls, wie Hubzilla, Socialhome or Ganggo.
* network:feed - RSS/Atom feeds
* network:mail - Mails die via IMAP importiert worden sind.
* network:stat | network:ostatus - Das OStatus-Protokoll wird hauptsächlich von alten GNU Social-Installationen genutzt.
* network:dscs | network:discourse - Beiträge, die über den Discourse connector empfangen werden.
* network:tmbl | network:tumblr - Beiträge, die über den Tumblr connector empfangen werden.
* network:bsky | network:bluesky - Beiträge, die über den Bluesky connector empfangen werden.
* platform - Benutze dies, um Plattformen in deinen Kanal einzuschließen, oder von ihm auszuschließen, d.h. "+platform:friendica". Im Falle eines Gruppen-Postings enthält der Suchtext beides, die Plattform des Gruppen-Servers und die Plattform des Autors.
* visibility - Du hast die Wahl zwischen verschiedenen Sichtbarkeiten. Du kannst nur die ungelisteten oder privaten Beiträge sehen, zu denen du Zugang hast.
* visibility:public - (öffentlich)
* visibility:unlisted - (ungelistet)
* visibility:private - (privat)
* language | lang - Verwende "language:code" um nach Beiträgen in der gewünschten Sprache (im [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format) zu suchen.
Denke daran, dass du diese Schlüsselwörter kombinieren kannst.
So kannst du zum Beispiel einen Kanal erstellen, mit allen Beiträgen, die über das Fediverse sprechen, aber nicht im Fediverse veröffentlich wurden, mit diesen Suchbegriffen: "fediverse -network:apub -network:dfrn".

View File

@ -17,7 +17,7 @@ Friendica - Dokumentation und Ressourcen
* [Circles und Privatsphäre](help/Circles-and-Privacy)
* [Tags und Erwähnungen](help/Tags-and-Mentions)
* [Community-Gruppen](help/Groups)
* [Channels](help/Channels)
* [Kanäle (Channels)](help/Channels)
* [Chats](help/Chats)
* Weiterführende Informationen
* [Account umziehen](help/Move-Account)

View File

@ -27,7 +27,7 @@ Requirements
* Apache mit einer aktiverten mod-rewrite-Funktion und dem Eintrag "Options All", so dass du die lokale .htaccess-Datei nutzen kannst
* PHP 7.4+
* PHP *Kommandozeilen*-Zugang mit register_argc_argv auf "true" gesetzt in der php.ini-Datei
* Curl, GD, GMP, PDO, mbstrings, MySQLi, hash, xml, zip, IntlChar and OpenSSL-Erweiterung
* Curl, GD, GMP, PDO, mbstrings, MySQLi, hash, xml, zip, IntlChar, IDN und OpenSSL-Erweiterung
* Das POSIX Modul muss aktiviert sein ([CentOS, RHEL](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) haben dies z.B. deaktiviert)
* Einen E-Mail Server, so dass PHP `mail()` funktioniert.
Wenn kein eigener E-Mail Server zur Verfügung steht, kann alternativ das [phpmailer](https://github.com/friendica/friendica-addons/tree/develop/phpmailer) Addon mit einem externen SMTP Account verwendet werden.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1011 KiB

After

Width:  |  Height:  |  Size: 707 KiB

View File

@ -45,7 +45,8 @@ use Friendica\Model\Post;
use Friendica\Network\HTTPException;
use Friendica\Util\DateTimeFormat;
function item_post(App $a) {
function item_post()
{
$uid = DI::userSession()->getLocalUserId();
if (!$uid) {
@ -248,7 +249,7 @@ function item_process(array $post, array $request, bool $preview, string $return
$post['body'] .= DI::contentItem()->storeAttachmentFromRequest($request);
}
$post = DI::contentItem()->finalizePost($post);
$post = DI::contentItem()->finalizePost($post, $preview);
if (!strlen($post['body'])) {
if ($preview) {
@ -279,13 +280,14 @@ function item_process(array $post, array $request, bool $preview, string $return
$post['body'] = BBCode::removeSharedData(Item::setHashtags($post['body']));
$post['writable'] = true;
$post['sensitive'] = false;
$post['post-reason'] = Item::PR_LOCAL;
$o = DI::conversation()->render([$post], Conversation::MODE_SEARCH, false, true);
System::jsonExit(['preview' => $o]);
}
Hook::callAll('post_local',$post);
Hook::callAll('post_local', $post);
unset($post['edit']);
unset($post['self']);

View File

@ -19,7 +19,6 @@
*
*/
use Friendica\App;
use Friendica\Content\Nav;
use Friendica\Content\Pager;
use Friendica\Content\Text\BBCode;
@ -34,7 +33,7 @@ use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
use Friendica\Util\Temporal;
function message_init(App $a)
function message_init()
{
$tabs = '';
@ -61,7 +60,7 @@ function message_init(App $a)
]);
}
function message_post(App $a)
function message_post()
{
if (!DI::userSession()->getLocalUserId()) {
DI::sysmsg()->addNotice(DI::l10n()->t('Permission denied.'));
@ -104,7 +103,7 @@ function message_post(App $a)
}
}
function message_content(App $a)
function message_content()
{
$o = '';
Nav::setSelected('messages');
@ -114,7 +113,7 @@ function message_content(App $a)
return Login::form();
}
$myprofile = DI::baseUrl() . '/profile/' . $a->getLoggedInUserNickname();
$myprofile = DI::baseUrl() . '/profile/' . DI::userSession()->getLocalUserNickname();
$tpl = Renderer::getMarkupTemplate('mail_head.tpl');
if (DI::args()->getArgc() > 1 && DI::args()->getArgv()[1] == 'new') {
@ -177,7 +176,7 @@ function message_content(App $a)
$tpl = Renderer::getMarkupTemplate('msg-header.tpl');
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
'$nickname' => $a->getLoggedInUserNickname(),
'$nickname' => DI::userSession()->getLocalUserNickname(),
'$linkurl' => DI::l10n()->t('Please enter a link URL:')
]);
@ -282,7 +281,7 @@ function message_content(App $a)
$tpl = Renderer::getMarkupTemplate('msg-header.tpl');
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
'$nickname' => $a->getLoggedInUserNickname(),
'$nickname' => DI::userSession()->getLocalUserNickname(),
'$linkurl' => DI::l10n()->t('Please enter a link URL:')
]);
@ -415,12 +414,10 @@ function get_messages(int $uid, int $start, int $limit): array
function render_messages(array $msg, string $t): string
{
$a = DI::app();
$tpl = Renderer::getMarkupTemplate($t);
$rslt = '';
$myprofile = DI::baseUrl() . '/profile/' . $a->getLoggedInUserNickname();
$myprofile = DI::baseUrl() . '/profile/' . DI::userSession()->getLocalUserNickname();
foreach ($msg as $rr) {
if ($rr['unknown']) {

View File

@ -46,7 +46,7 @@ function notes_content(App $a, bool $update = false)
return;
}
$o = BaseProfile::getTabsHTML('notes', true, $a->getLoggedInUserNickname(), false);
$o = BaseProfile::getTabsHTML('notes', true, DI::userSession()->getLocalUserNickname(), false);
if (!$update) {
$o .= '<h3>' . DI::l10n()->t('Personal Notes') . '</h3>';

View File

@ -19,11 +19,9 @@
*
*/
use Friendica\App;
use Friendica\Content\Nav;
use Friendica\Content\Pager;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Widget;
use Friendica\Core\ACL;
use Friendica\Core\Addon;
use Friendica\Core\Hook;
@ -53,7 +51,7 @@ use Friendica\Util\Strings;
use Friendica\Util\Temporal;
use Friendica\Util\XML;
function photos_init(App $a)
function photos_init()
{
if (DI::config()->get('system', 'block_public') && !DI::userSession()->isAuthenticated()) {
return;
@ -67,8 +65,6 @@ function photos_init(App $a)
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
}
$is_owner = (DI::userSession()->getLocalUserId() && (DI::userSession()->getLocalUserId() == $owner['uid']));
$albums = Photo::getAlbums($owner['uid']);
$albums_visible = ((intval($owner['hidewall']) && !DI::userSession()->isAuthenticated()) ? false : true);
@ -125,7 +121,7 @@ function photos_init(App $a)
return;
}
function photos_post(App $a)
function photos_post()
{
$user = User::getByNickname(DI::args()->getArgv()[1]);
if (!DBA::isResult($user)) {
@ -136,7 +132,7 @@ function photos_post(App $a)
$visitor = 0;
$page_owner_uid = intval($user['uid']);
$community_page = $user['page-flags'] == User::PAGE_FLAGS_COMMUNITY;
$community_page = in_array($user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN]);
if (DI::userSession()->getLocalUserId() && (DI::userSession()->getLocalUserId() == $page_owner_uid)) {
$can_post = true;
@ -200,7 +196,7 @@ function photos_post(App $a)
// Update the photo albums cache
Photo::clearAlbumCache($page_owner_uid);
DI::baseUrl()->redirect('photos/' . $a->getLoggedInUserNickname() . '/album/' . bin2hex($newalbum));
DI::baseUrl()->redirect('photos/' . DI::userSession()->getLocalUserNickname() . '/album/' . bin2hex($newalbum));
return; // NOTREACHED
}
@ -559,7 +555,7 @@ function photos_post(App $a)
}
}
function photos_content(App $a)
function photos_content()
{
// URLs:
// photos/name/upload
@ -618,7 +614,7 @@ function photos_content(App $a)
$owner_uid = $user['uid'];
$community_page = (($user['page-flags'] == User::PAGE_FLAGS_COMMUNITY) ? true : false);
$community_page = in_array($user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN]);
if (DI::userSession()->getLocalUserId() && (DI::userSession()->getLocalUserId() == $owner_uid)) {
$can_post = true;
@ -672,18 +668,14 @@ function photos_content(App $a)
$selname = (!is_null($datum) && Strings::isHex($datum)) ? hex2bin($datum) : '';
$albumselect = '';
$albumselect = ['' => '<current year>'];
$albumselect .= '<option value="" ' . (!$selname ? ' selected="selected" ' : '') . '>&lt;current year&gt;</option>';
$albums = Photo::getAlbums($owner_uid);
if (!empty($albums)) {
foreach ($albums as $album) {
if ($album['album'] === '') {
continue;
}
$selected = (($selname === $album['album']) ? ' selected="selected" ' : '');
$albumselect .= '<option value="' . $album['album'] . '"' . $selected . '>' . $album['album'] . '</option>';
foreach (Photo::getAlbums($owner_uid) as $album) {
if ($album['album'] === '') {
continue;
}
$albumselect[$album['album']] = $album['album'];
}
$uploader = '';
@ -718,7 +710,7 @@ function photos_content(App $a)
$tpl = Renderer::getMarkupTemplate('photos_upload.tpl');
$aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML(DI::page(), $a->getLoggedInUserId()));
$aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML(DI::page(), DI::userSession()->getLocalUserId()));
$o .= Renderer::replaceMacros($tpl, [
'$pagename' => DI::l10n()->t('Upload Photos'),
@ -729,9 +721,10 @@ function photos_content(App $a)
'$existalbumtext' => DI::l10n()->t('or select existing album:'),
'$nosharetext' => DI::l10n()->t('Do not show a status post for this upload'),
'$albumselect' => $albumselect,
'$selname' => $selname,
'$permissions' => DI::l10n()->t('Permissions'),
'$aclselect' => $aclselect_e,
'$lockstate' => ACL::getLockstateForUserId($a->getLoggedInUserId()) ? 'lock' : 'unlock',
'$lockstate' => ACL::getLockstateForUserId(DI::userSession()->getLocalUserId()) ? 'lock' : 'unlock',
'$alt_uploader' => $ret['addon_text'],
'$default_upload_box' => ($ret['default_upload'] ? $default_upload_box : ''),
'$default_upload_submit' => ($ret['default_upload'] ? $default_upload_submit : ''),
@ -1077,7 +1070,7 @@ function photos_content(App $a)
$album_e = $ph[0]['album'];
$caption_e = $ph[0]['desc'];
$aclselect_e = ACL::getFullSelectorHTML(DI::page(), $a->getLoggedInUserId(), false, ACL::getDefaultUserPermissions($ph[0]));
$aclselect_e = ACL::getFullSelectorHTML(DI::page(), DI::userSession()->getLocalUserId(), false, ACL::getDefaultUserPermissions($ph[0]));
$edit = Renderer::replaceMacros($edit_tpl, [
'$id' => $ph[0]['id'],

View File

@ -43,6 +43,7 @@ use Friendica\Model\Contact;
use Friendica\Model\Profile;
use Friendica\Module\Special\HTTPException as ModuleHTTPException;
use Friendica\Network\HTTPException;
use Friendica\Protocol\ATProtocol\DID;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\HTTPInputData;
use Friendica\Util\HTTPSignature;
@ -64,7 +65,7 @@ class App
{
const PLATFORM = 'Friendica';
const CODENAME = 'Yellow Archangel';
const VERSION = '2024.03';
const VERSION = '2024.06-dev';
// Allow themes to control internal parameters
// by changing App values in theme.php
@ -133,42 +134,6 @@ class App
*/
private $session;
/**
* @deprecated 2022.03
* @see IHandleUserSessions::isAuthenticated()
*/
public function isLoggedIn(): bool
{
return $this->session->isAuthenticated();
}
/**
* @deprecated 2022.03
* @see IHandleUserSessions::isSiteAdmin()
*/
public function isSiteAdmin(): bool
{
return $this->session->isSiteAdmin();
}
/**
* @deprecated 2022.03
* @see IHandleUserSessions::getLocalUserId()
*/
public function getLoggedInUserId(): int
{
return $this->session->getLocalUserId();
}
/**
* @deprecated 2022.03
* @see IHandleUserSessions::getLocalUserNickname()
*/
public function getLoggedInUserNickname(): string
{
return $this->session->getLocalUserNickname();
}
/**
* Set the profile owner ID
*
@ -565,8 +530,8 @@ class App
*/
public function runFrontend(App\Router $router, IManagePersonalConfigValues $pconfig, Authentication $auth, App\Page $page, Nav $nav, ModuleHTTPException $httpException, HTTPInputData $httpInput, float $start_time, array $server)
{
$requeststring = ($_SERVER['REQUEST_METHOD'] ?? '') . ' ' . ($_SERVER['REQUEST_URI'] ?? '') . ' ' . ($_SERVER['SERVER_PROTOCOL'] ?? '');
$this->logger->debug('Request received', ['address' => $_SERVER['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $_SERVER['HTTP_REFERER'] ?? '', 'user-agent' => $_SERVER['HTTP_USER_AGENT'] ?? '']);
$requeststring = ($server['REQUEST_METHOD'] ?? '') . ' ' . ($server['REQUEST_URI'] ?? '') . ' ' . ($server['SERVER_PROTOCOL'] ?? '');
$this->logger->debug('Request received', ['address' => $server['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $server['HTTP_REFERER'] ?? '', 'user-agent' => $server['HTTP_USER_AGENT'] ?? '']);
$request_start = microtime(true);
$this->profiler->set($start_time, 'start');
@ -593,8 +558,10 @@ class App
Core\Hook::callAll('init_1');
}
DID::routeRequest($this->args->getCommand(), $server);
if ($this->mode->isNormal() && !$this->mode->isBackend()) {
$requester = HTTPSignature::getSigner('', $_SERVER);
$requester = HTTPSignature::getSigner('', $server);
if (!empty($requester)) {
Profile::addVisitorCookieForHandle($requester);
}
@ -713,13 +680,13 @@ class App
// Wrapping HTML responses in the theme template
if ($response->getHeaderLine(ICanCreateResponses::X_HEADER) === ICanCreateResponses::TYPE_HTML) {
$response = $page->run($this, $this->baseURL, $this->args, $this->mode, $response, $this->l10n, $this->profiler, $this->config, $pconfig, $nav, $this->session->getLocalUserId());
$response = $page->run($this, $this->session, $this->baseURL, $this->args, $this->mode, $response, $this->l10n, $this->profiler, $this->config, $pconfig, $nav, $this->session->getLocalUserId());
}
$this->logger->debug('Request processed sucessfully', ['response' => $response->getStatusCode(), 'address' => $_SERVER['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $_SERVER['HTTP_REFERER'] ?? '', 'user-agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
$this->logger->debug('Request processed sucessfully', ['response' => $response->getStatusCode(), 'address' => $server['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $server['HTTP_REFERER'] ?? '', 'user-agent' => $server['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
System::echoResponse($response);
} catch (HTTPException $e) {
$this->logger->debug('Request processed with exception', ['response' => $e->getCode(), 'address' => $_SERVER['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $_SERVER['HTTP_REFERER'] ?? '', 'user-agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
$this->logger->debug('Request processed with exception', ['response' => $e->getCode(), 'address' => $server['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $server['HTTP_REFERER'] ?? '', 'user-agent' => $server['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
$httpException->rawContent($e);
}
$page->logRuntime($this->config, 'runFrontend');

View File

@ -32,6 +32,7 @@ use Friendica\Core\L10n;
use Friendica\Core\Logger;
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
use Friendica\Core\Renderer;
use Friendica\Core\Session\Model\UserSession;
use Friendica\Core\System;
use Friendica\Core\Theme;
use Friendica\Module\Response;
@ -325,13 +326,13 @@ class Page implements ArrayAccess
*
* @throws HTTPException\InternalServerErrorException
*/
private function initFooter(App $app, Mode $mode, L10n $l10n)
private function initFooter(UserSession $session, Mode $mode, L10n $l10n)
{
// If you're just visiting, let javascript take you home
if (!empty($_SESSION['visitor_home'])) {
$homebase = $_SESSION['visitor_home'];
} elseif (!empty($app->getLoggedInUserNickname())) {
$homebase = 'profile/' . $app->getLoggedInUserNickname();
} elseif (!empty($session->getLocalUserNickname())) {
$homebase = 'profile/' . $session->getLocalUserNickname();
}
if (isset($homebase)) {
@ -420,7 +421,7 @@ class Page implements ArrayAccess
* @throws HTTPException\InternalServerErrorException
* @throws HTTPException\ServiceUnavailableException
*/
public function run(App $app, BaseURL $baseURL, Arguments $args, Mode $mode, ResponseInterface $response, L10n $l10n, Profiler $profiler, IManageConfigValues $config, IManagePersonalConfigValues $pconfig, Nav $nav, int $localUID)
public function run(App $app, UserSession $session, BaseURL $baseURL, Arguments $args, Mode $mode, ResponseInterface $response, L10n $l10n, Profiler $profiler, IManageConfigValues $config, IManagePersonalConfigValues $pconfig, Nav $nav, int $localUID)
{
$moduleName = $args->getModuleName();
@ -459,7 +460,7 @@ class Page implements ArrayAccess
/* Build the page ending -- this is stuff that goes right before
* the closing </body> tag
*/
$this->initFooter($app, $mode, $l10n);
$this->initFooter($session, $mode, $l10n);
$profiler->set(microtime(true) - $timestamp, 'aftermath');

View File

@ -356,7 +356,7 @@ abstract class BaseModule implements ICanHandleRequests
*/
public static function getFormSecurityToken(string $typename = ''): string
{
$user = User::getById(DI::app()->getLoggedInUserId(), ['guid', 'prvkey']);
$user = User::getById(DI::userSession()->getLocalUserId(), ['guid', 'prvkey']);
$timestamp = time();
$sec_hash = hash('whirlpool', ($user['guid'] ?? '') . ($user['prvkey'] ?? '') . session_id() . $timestamp . $typename);
@ -390,7 +390,7 @@ abstract class BaseModule implements ICanHandleRequests
$max_livetime = 10800; // 3 hours
$user = User::getById(DI::app()->getLoggedInUserId(), ['guid', 'prvkey']);
$user = User::getById(DI::userSession()->getLocalUserId(), ['guid', 'prvkey']);
$x = explode('.', $hash);
if (time() > (intval($x[0]) + $max_livetime)) {
@ -410,7 +410,7 @@ abstract class BaseModule implements ICanHandleRequests
public static function checkFormSecurityTokenRedirectOnError(string $err_redirect, string $typename = '', string $formname = 'form_security_token')
{
if (!self::checkFormSecurityToken($typename, $formname)) {
Logger::notice('checkFormSecurityToken failed: user ' . DI::app()->getLoggedInUserNickname() . ' - form element ' . $typename);
Logger::notice('checkFormSecurityToken failed: user ' . DI::userSession()->getLocalUserNickname() . ' - form element ' . $typename);
Logger::debug('checkFormSecurityToken failed', ['request' => $_REQUEST]);
DI::sysmsg()->addNotice(self::getFormSecurityStandardErrorMessage());
DI::baseUrl()->redirect($err_redirect);
@ -420,7 +420,7 @@ abstract class BaseModule implements ICanHandleRequests
public static function checkFormSecurityTokenForbiddenOnError(string $typename = '', string $formname = 'form_security_token')
{
if (!self::checkFormSecurityToken($typename, $formname)) {
Logger::notice('checkFormSecurityToken failed: user ' . DI::app()->getLoggedInUserNickname() . ' - form element ' . $typename);
Logger::notice('checkFormSecurityToken failed: user ' . DI::userSession()->getLocalUserNickname() . ' - form element ' . $typename);
Logger::debug('checkFormSecurityToken failed', ['request' => $_REQUEST]);
throw new \Friendica\Network\HTTPException\ForbiddenException();

View File

@ -57,7 +57,7 @@ class Avatar
return $fields;
}
if (Network::isLocalLink($avatar) || empty($avatar)) {
if (DI::baseUrl()->isLocalUrl($avatar) || empty($avatar)) {
self::deleteCache($contact);
return $fields;
}
@ -120,7 +120,7 @@ class Avatar
return $fields;
}
if (Network::isLocalLink($contact['avatar']) || empty($contact['avatar'])) {
if (DI::baseUrl()->isLocalUrl($contact['avatar']) || empty($contact['avatar'])) {
self::deleteCache($contact);
return $fields;
}

View File

@ -23,8 +23,8 @@ namespace Friendica\Content;
use Friendica\Core\L10n;
use Friendica\Core\Renderer;
use Friendica\Util\Network;
use Friendica\Util\Strings;
use GuzzleHttp\Psr7\Uri;
/**
* This pager should be used by lists using the min_id†/max_id† parameters
@ -67,7 +67,7 @@ class BoundariesPager extends Pager
$parsed['query'] = http_build_query($queryParameters);
$url = Network::unparseURL($parsed);
$url = (string)Uri::fromParts((array)$parsed);
$this->setQueryString($url);
}

View File

@ -25,8 +25,8 @@ use Friendica\Core\Hook;
use Friendica\Core\Protocol;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Util\Network;
use Friendica\Util\Strings;
use GuzzleHttp\Psr7\Uri;
/**
* ContactSelector class
@ -102,7 +102,7 @@ class ContactSelector
// Create the server url out of the profile url
$parts = parse_url($profile);
unset($parts['path']);
$server_url = Strings::normaliseLink(Network::unparseURL($parts));
$server_url = Strings::normaliseLink((string)Uri::fromParts((array)$parts));
}
self::$server_url[$profile] = $server_url;

View File

@ -308,7 +308,7 @@ class Conversation
public function statusEditor(array $x = [], int $notes_cid = 0, bool $popup = false): string
{
$user = User::getById($this->app->getLoggedInUserId(), ['uid', 'nickname', 'allow_location', 'default-location']);
$user = User::getById($this->session->getLocalUserId(), ['uid', 'nickname', 'allow_location', 'default-location']);
if (empty($user['uid'])) {
return '';
}
@ -332,7 +332,6 @@ class Conversation
$tpl = Renderer::getMarkupTemplate('jot-header.tpl');
$this->page['htmlhead'] .= Renderer::replaceMacros($tpl, [
'$newpost' => 'true',
'$baseurl' => $this->baseURL,
'$geotag' => $geotag,
'$nickname' => $x['nickname'],
'$ispublic' => $this->l10n->t('Visible to <strong>everybody</strong>'),
@ -389,7 +388,7 @@ class Conversation
'$title' => $x['title'] ?? '',
'$placeholdertitle' => $this->l10n->t('Set title'),
'$category' => $x['category'] ?? '',
'$placeholdercategory' => Feature::isEnabled($this->session->getLocalUserId(), 'categories') ? $this->l10n->t("Categories \x28comma-separated list\x29") : '',
'$placeholdercategory' => Feature::isEnabled($this->session->getLocalUserId(), Feature::CATEGORIES) ? $this->l10n->t("Categories \x28comma-separated list\x29") : '',
'$scheduled_at' => Temporal::getDateTimeField(
new \DateTime(),
new \DateTime('now + 6 months'),
@ -405,7 +404,6 @@ class Conversation
'$posttype' => $notes_cid ? ItemModel::PT_PERSONAL_NOTE : ItemModel::PT_ARTICLE,
'$content' => $x['content'] ?? '',
'$post_id' => $x['post_id'] ?? '',
'$baseurl' => $this->baseURL,
'$defloc' => $x['default_location'],
'$visitor' => $x['visitor'],
'$pvisit' => $notes_cid ? 'none' : $x['visitor'],
@ -591,7 +589,6 @@ class Conversation
}
$o = Renderer::replaceMacros($page_template, [
'$baseurl' => $this->baseURL,
'$return_path' => $this->args->getQueryString(),
'$live_update' => $live_update_div,
'$remove' => $this->l10n->t('remove'),
@ -1517,14 +1514,6 @@ class Conversation
[$categories, $folders] = $this->item->determineCategoriesTerms($item, $this->session->getLocalUserId());
if (!empty($item['title'])) {
$title = $item['title'];
} elseif (!empty($item['content-warning']) && $this->pConfig->get($this->session->getLocalUserId(), 'system', 'disable_cw', false)) {
$title = ucfirst($item['content-warning']);
} else {
$title = '';
}
if (!empty($item['featured'])) {
$pinned = $this->l10n->t('Pinned item');
} else {
@ -1550,7 +1539,8 @@ class Conversation
'sparkle' => $sparkle,
'lock' => false,
'thumb' => $this->baseURL->remove($this->item->getAuthorAvatar($item)),
'title' => $title,
'title' => $item['title'],
'summary' => $item['content-warning'],
'body_html' => $body_html,
'tags' => $tags['tags'],
'hashtags' => $tags['hashtags'],

View File

@ -26,6 +26,25 @@ use Friendica\DI;
class Feature
{
const ACCOUNTS = 'accounts';
const ADD_ABSTRACT = 'add_abstract';
const ARCHIVE = 'archive';
const CATEGORIES = 'categories';
const CHANNELS = 'channels';
const CIRCLES = 'circles';
const COMMUNITY = 'community';
const EXPLICIT_MENTIONS = 'explicit_mentions';
const FOLDERS = 'folders';
const GROUPS = 'forumlist_profile';
const MEMBER_SINCE = 'profile_membersince';
const NETWORKS = 'networks';
const NOSHARER = 'nosharer';
const PHOTO_LOCATION = 'photo_location';
const PUBLIC_CALENDAR = 'public_calendar';
const SEARCHES = 'searches';
const TAGCLOUD = 'tagadelic';
const TRENDING_TAGS = 'trending_tags';
/**
* check if feature is enabled
*
@ -34,25 +53,18 @@ class Feature
* @return boolean
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function isEnabled(int $uid, $feature)
public static function isEnabled(int $uid, $feature): bool
{
$x = DI::config()->get('feature_lock', $feature, false);
if ($x === false) {
$x = DI::pConfig()->get($uid, 'feature', $feature, false);
if (!DI::config()->get('feature_lock', $feature, false)) {
$enabled = DI::config()->get('feature', $feature) ?? self::getDefault($feature);
$enabled = DI::pConfig()->get($uid, 'feature', $feature) ?? $enabled;
} else {
$enabled = true;
}
if ($x === false) {
$x = DI::config()->get('feature', $feature, false);
}
if ($x === false) {
$x = self::getDefault($feature);
}
$arr = ['uid' => $uid, 'feature' => $feature, 'enabled' => $x];
$arr = ['uid' => $uid, 'feature' => $feature, 'enabled' => $enabled];
Hook::callAll('isEnabled', $arr);
return($arr['enabled']);
return (bool)$arr['enabled'];
}
/**
@ -64,8 +76,7 @@ class Feature
*/
private static function getDefault($feature)
{
$f = self::get();
foreach ($f as $cat) {
foreach (self::get() as $cat) {
foreach ($cat as $feat) {
if (is_array($feat) && $feat[0] === $feature) {
return $feat[3];
@ -95,36 +106,49 @@ class Feature
'general' => [
DI::l10n()->t('General Features'),
//array('expire', DI::l10n()->t('Content Expiration'), DI::l10n()->t('Remove old posts/comments after a period of time')),
['photo_location', DI::l10n()->t('Photo Location'), DI::l10n()->t("Photo metadata is normally stripped. This extracts the location \x28if present\x29 prior to stripping metadata and links it to a map."), false, DI::config()->get('feature_lock', 'photo_location', false)],
['trending_tags', DI::l10n()->t('Trending Tags'), DI::l10n()->t('Show a community page widget with a list of the most popular tags in recent public posts.'), false, DI::config()->get('feature_lock', 'trending_tags', false)],
[self::PHOTO_LOCATION, DI::l10n()->t('Photo Location'), DI::l10n()->t("Photo metadata is normally stripped. This extracts the location \x28if present\x29 prior to stripping metadata and links it to a map."), false, DI::config()->get('feature_lock', self::PHOTO_LOCATION, false)],
[self::COMMUNITY, DI::l10n()->t('Display the community in the navigation'), DI::l10n()->t('If enabled, the community can be accessed via the navigation menu. Independent from this setting, the community timelines can always be accessed via the channels.'), true, DI::config()->get('feature_lock', self::COMMUNITY, false)],
],
// Post composition
'composition' => [
DI::l10n()->t('Post Composition Features'),
['aclautomention', DI::l10n()->t('Auto-mention Groups'), DI::l10n()->t('Add/remove mention when a group page is selected/deselected in ACL window.'), false, DI::config()->get('feature_lock', 'aclautomention', false)],
['explicit_mentions', DI::l10n()->t('Explicit Mentions'), DI::l10n()->t('Add explicit mentions to comment box for manual control over who gets mentioned in replies.'), false, DI::config()->get('feature_lock', 'explicit_mentions', false)],
['add_abstract', DI::l10n()->t('Add an abstract from ActivityPub content warnings'), DI::l10n()->t('Add an abstract when commenting on ActivityPub posts with a content warning. Abstracts are displayed as content warning on systems like Mastodon or Pleroma.'), false, DI::config()->get('feature_lock', 'add_abstract', false)],
[self::EXPLICIT_MENTIONS, DI::l10n()->t('Explicit Mentions'), DI::l10n()->t('Add explicit mentions to comment box for manual control over who gets mentioned in replies.'), false, DI::config()->get('feature_lock', Feature::EXPLICIT_MENTIONS, false)],
[self::ADD_ABSTRACT, DI::l10n()->t('Add an abstract from ActivityPub content warnings'), DI::l10n()->t('Add an abstract when commenting on ActivityPub posts with a content warning. Abstracts are displayed as content warning on systems like Mastodon or Pleroma.'), false, DI::config()->get('feature_lock', self::ADD_ABSTRACT, false)],
],
// Item tools
'tools' => [
DI::l10n()->t('Post/Comment Tools'),
['categories', DI::l10n()->t('Post Categories'), DI::l10n()->t('Add categories to your posts'), false, DI::config()->get('feature_lock', 'categories', false)],
[self::CATEGORIES, DI::l10n()->t('Post Categories'), DI::l10n()->t('Add categories to your posts'), false, DI::config()->get('feature_lock', self::CATEGORIES, false)],
],
// Widget visibility on the network stream
'network' => [
DI::l10n()->t('Network Widgets'),
[self::CIRCLES, DI::l10n()->t('Circles'), DI::l10n()->t('Display posts that have been created by accounts of the selected circle.'), true, DI::config()->get('feature_lock', self::CIRCLES, false)],
[self::GROUPS, DI::l10n()->t('Groups'), DI::l10n()->t('Display posts that have been distributed by the selected group.'), true, DI::config()->get('feature_lock', self::GROUPS, false)],
[self::ARCHIVE, DI::l10n()->t('Archives'), DI::l10n()->t('Display an archive where posts can be selected by month and year.'), true, DI::config()->get('feature_lock', self::ARCHIVE, false)],
[self::NETWORKS, DI::l10n()->t('Protocols'), DI::l10n()->t('Display posts with the selected protocols.'), true, DI::config()->get('feature_lock', self::NETWORKS, false)],
[self::ACCOUNTS, DI::l10n()->t('Account Types'), DI::l10n()->t('Display posts done by accounts with the selected account type.'), true, DI::config()->get('feature_lock', self::ACCOUNTS, false)],
[self::CHANNELS, DI::l10n()->t('Channels'), DI::l10n()->t('Display posts in the system channels and user defined channels.'), true, DI::config()->get('feature_lock', self::CHANNELS, false)],
[self::SEARCHES, DI::l10n()->t('Saved Searches'), DI::l10n()->t('Display posts that contain subscribed hashtags.'), true, DI::config()->get('feature_lock', self::SEARCHES, false)],
[self::FOLDERS, DI::l10n()->t('Saved Folders'), DI::l10n()->t('Display a list of folders in which posts are stored.'), true, DI::config()->get('feature_lock', self::FOLDERS, false)],
[self::NOSHARER, DI::l10n()->t('Own Contacts'), DI::l10n()->t('Include or exclude posts from subscribed accounts. This widget is not visible on all channels.'), true, DI::config()->get('feature_lock', self::NOSHARER, false)],
[self::TRENDING_TAGS, DI::l10n()->t('Trending Tags'), DI::l10n()->t('Display a list of the most popular tags in recent public posts.'), false, DI::config()->get('feature_lock', self::TRENDING_TAGS, false)],
],
// Advanced Profile Settings
'advanced_profile' => [
DI::l10n()->t('Advanced Profile Settings'),
['forumlist_profile', DI::l10n()->t('List Groups'), DI::l10n()->t('Show visitors public groups at the Advanced Profile Page'), false, DI::config()->get('feature_lock', 'forumlist_profile', false)],
['tagadelic', DI::l10n()->t('Tag Cloud'), DI::l10n()->t('Provide a personal tag cloud on your profile page'), false, DI::config()->get('feature_lock', 'tagadelic', false)],
['profile_membersince', DI::l10n()->t('Display Membership Date'), DI::l10n()->t('Display membership date in profile'), false, DI::config()->get('feature_lock', 'profile_membersince', false)],
[self::TAGCLOUD, DI::l10n()->t('Tag Cloud'), DI::l10n()->t('Provide a personal tag cloud on your profile page'), false, DI::config()->get('feature_lock', self::TAGCLOUD, false)],
[self::MEMBER_SINCE, DI::l10n()->t('Display Membership Date'), DI::l10n()->t('Display membership date in profile'), false, DI::config()->get('feature_lock', self::MEMBER_SINCE, false)],
],
//Advanced Calendar Settings
'advanced_calendar' => [
DI::l10n()->t('Advanced Calendar Settings'),
['public_calendar', DI::l10n()->t('Allow anonymous access to your calendar'), DI::l10n()->t('Allows anonymous visitors to consult your calendar and your public events. Contact birthday events are private to you.'), false, DI::config()->get('feature_lock', 'public_calendar', false)],
[self::PUBLIC_CALENDAR, DI::l10n()->t('Allow anonymous access to your calendar'), DI::l10n()->t('Allows anonymous visitors to consult your calendar and your public events. Contact birthday events are private to you.'), false, DI::config()->get('feature_lock', self::PUBLIC_CALENDAR, false)],
]
];

View File

@ -172,8 +172,7 @@ class GroupManager
*/
public static function profileAdvanced($uid)
{
$profile = intval(Feature::isEnabled($uid, 'forumlist_profile'));
if (!$profile) {
if (!Feature::isEnabled($uid, Feature::GROUPS)) {
return '';
}

View File

@ -48,7 +48,6 @@ use Friendica\Model\User;
use Friendica\Network\HTTPException;
use Friendica\Object\EMail\ItemCCEMail;
use Friendica\Protocol\Activity;
use Friendica\Protocol\ActivityPub;
use Friendica\Util\ACLFormatter;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Emailer;
@ -440,9 +439,11 @@ class Item
];
if (!empty($item['language'])) {
$menu[$this->l10n->t('Languages')] = 'javascript:alert(\'' . ItemModel::getLanguageMessage($item) . '\');';
$menu[$this->l10n->t('Languages')] = 'javascript:displayLanguage(' . $item['uri-id'] . ');';
}
$menu[$this->l10n->t('Search Text')] = 'javascript:displaySearchText(' . $item['uri-id'] . ');';
if ((($cid == 0) || ($rel == Contact::FOLLOWER)) &&
in_array($item['network'], Protocol::FEDERATED)
) {
@ -800,14 +801,14 @@ class Item
*/
public function addShareLink(string $body, int $quote_uri_id): string
{
$post = Post::selectFirstPost(['uri', 'plink'], ['uri-id' => $quote_uri_id]);
$post = Post::selectFirstPost(['uri'], ['uri-id' => $quote_uri_id]);
if (empty($post)) {
return $body;
}
$body = BBCode::removeSharedData($body);
$body .= "\n" . ($post['plink'] ?: $post['uri']);
$body .= "\nRE: " . $post['uri'];
return $body;
}
@ -910,40 +911,6 @@ class Item
return $post;
}
public function moveAttachmentsFromBodyToAttach(array $post): array
{
if (!preg_match_all('/(\[attachment\]([0-9]+)\[\/attachment\])/', $post['body'], $match)) {
return $post;
}
foreach ($match[2] as $attachment_id) {
$attachment = Attach::selectFirst(['id', 'uid', 'filename', 'filesize', 'filetype'], ['id' => $attachment_id, 'uid' => $post['uid']]);
if (empty($attachment)) {
continue;
}
if ($post['attach']) {
$post['attach'] .= ',';
}
$post['attach'] .= Post\Media::getAttachElement(
$this->baseURL . '/attach/' . $attachment['id'],
$attachment['filesize'],
$attachment['filetype'],
$attachment['filename'] ?? ''
);
$fields = [
'allow_cid' => $post['allow_cid'], 'allow_gid' => $post['allow_gid'],
'deny_cid' => $post['deny_cid'], 'deny_gid' => $post['deny_gid']
];
$condition = ['id' => $attachment_id];
Attach::update($fields, $condition);
}
$post['body'] = str_replace($match[1], '', $post['body']);
return $post;
}
private function setObjectType(array $post): array
{
if (empty($post['post-type'])) {
@ -1023,8 +990,13 @@ class Item
return $post;
}
public function finalizePost(array $post): array
public function finalizePost(array $post, bool $preview): array
{
if ($preview) {
$post['body'] = Attach::addAttachmentToBody($post['body'], $post['uid']);
} else {
Attach::setPermissionFromBody($post);
}
if (preg_match("/\[attachment\](.*?)\[\/attachment\]/ism", $post['body'], $matches)) {
$post['body'] = preg_replace("/\[attachment].*?\[\/attachment\]/ism", PageInfo::getFooterFromUrl($matches[1]), $post['body']);
}
@ -1045,7 +1017,7 @@ class Item
public function postProcessPost(array $post, array $recipients = [])
{
if (!\Friendica\Content\Feature::isEnabled($post['uid'], 'explicit_mentions') && ($post['gravity'] == ItemModel::GRAVITY_COMMENT)) {
if (!Feature::isEnabled($post['uid'], Feature::EXPLICIT_MENTIONS) && ($post['gravity'] == ItemModel::GRAVITY_COMMENT)) {
Tag::createImplicitMentions($post['uri-id'], $post['thr-parent-id']);
}
@ -1078,7 +1050,7 @@ class Item
$to_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $to['author-id']]);
$parent = Post::selectFirstPost(['author-id'], ['uri-id' => $parentUriId]);
$parent_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $parent['author-id']]);
$followers = '';
foreach (array_column(Tag::getByURIId($parentUriId, [Tag::TO, Tag::CC, Tag::BCC]), 'url') as $url) {
if ($url == $parent_author['ap-followers']) {

View File

@ -251,7 +251,7 @@ class Nav
$nav['home'] = [$homelink, $this->l10n->t('Home'), '', $this->l10n->t('Home Page')];
}
if (intval($this->config->get('config', 'register_policy')) === \Friendica\Module\Register::OPEN && !$this->session->isAuthenticated()) {
if (\Friendica\Module\Register::getPolicy() === \Friendica\Module\Register::OPEN && !$this->session->isAuthenticated()) {
$nav['register'] = ['register', $this->l10n->t('Register'), '', $this->l10n->t('Create an account')];
}
@ -284,8 +284,8 @@ class Nav
$gdirpath = Profile::zrl($this->config->get('system', 'directory'), true);
}
if (($this->session->getLocalUserId() || $this->config->get('system', 'community_page_style') != Community::DISABLED_VISITOR) &&
!($this->config->get('system', 'community_page_style') == Community::DISABLED)) {
if (Feature::isEnabled($this->session->getLocalUserId(), Feature::COMMUNITY) && (($this->session->getLocalUserId() || $this->config->get('system', 'community_page_style') != Community::DISABLED_VISITOR) &&
!($this->config->get('system', 'community_page_style') == Community::DISABLED))) {
$nav['community'] = ['community', $this->l10n->t('Community'), '', $this->l10n->t('Conversations on this and other servers')];
}

View File

@ -32,6 +32,7 @@ use Friendica\Database\Database;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\ParseUrl;
@ -86,7 +87,7 @@ class OEmbed
if (!in_array($ext, $noexts)) {
// try oembed autodiscovery
$html_text = DI::httpClient()->fetch($embedurl, HttpClientAccept::HTML, 15);
$html_text = DI::httpClient()->fetch($embedurl, HttpClientAccept::HTML, 15, '', HttpClientRequest::SITEINFO);
if (!empty($html_text)) {
$dom = new DOMDocument();
if (@$dom->loadHTML($html_text)) {
@ -100,7 +101,7 @@ class OEmbed
// but their OEmbed endpoint is only accessible by HTTPS ¯\_(ツ)_/¯
$href = str_replace(['http://www.youtube.com/', 'http://player.vimeo.com/'],
['https://www.youtube.com/', 'https://player.vimeo.com/'], $href);
$result = DI::httpClient()->fetchFull($href . '&maxwidth=' . $a->getThemeInfoValue('videowidth'));
$result = DI::httpClient()->fetchFull($href . '&maxwidth=' . $a->getThemeInfoValue('videowidth'), HttpClientAccept::DEFAULT, 0, '', HttpClientRequest::SITEINFO);
if ($result->isSuccess()) {
$json_string = $result->getBodyString();
break;

File diff suppressed because it is too large Load Diff

View File

@ -29,9 +29,9 @@ use Friendica\Core\Renderer;
use Friendica\Core\Search;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Util\Network;
use Friendica\Util\Strings;
use Friendica\Util\XML;
use GuzzleHttp\Psr7\Uri;
use League\HTMLToMarkdown\HtmlConverter;
use Psr\Http\Message\UriInterface;
@ -253,13 +253,14 @@ class HTML
self::tagToBBCode($doc, 'span', ['class' => 'type-link'], '[class=type-link]', '[/class]');
self::tagToBBCode($doc, 'span', ['class' => 'type-video'], '[class=type-video]', '[/class]');
self::tagToBBCode($doc, 'strong', [], '[b]', '[/b]');
self::tagToBBCode($doc, 'em', [], '[i]', '[/i]');
self::tagToBBCode($doc, 'b', [], '[b]', '[/b]');
self::tagToBBCode($doc, 'i', [], '[i]', '[/i]');
self::tagToBBCode($doc, 'u', [], '[u]', '[/u]');
self::tagToBBCode($doc, 's', [], '[s]', '[/s]');
self::tagToBBCode($doc, 'del', [], '[s]', '[/s]');
$elements = [
'b', 'del', 'em', 'i', 'ins', 'kbd', 'mark',
's', 'samp', 'strong', 'sub', 'sup', 'u', 'var'
];
foreach ($elements as $element) {
self::tagToBBCode($doc, $element, [], '[' . $element . ']', '[/' . $element . ']');
}
self::tagToBBCode($doc, 'strike', [], '[s]', '[/s]');
self::tagToBBCode($doc, 'big', [], "[size=large]", "[/size]");
@ -406,7 +407,7 @@ class HTML
}
$parts = array_merge($base, parse_url($url));
$url2 = Network::unparseURL($parts);
$url2 = (string)Uri::fromParts((array)$parts);
return str_replace($url, $url2, $link);
}
@ -1060,4 +1061,15 @@ class HTML
return null;
}
/**
* Check if a document contains HTML or entities
*
* @param string $text
* @return boolean
*/
public static function isHTML(string $text): bool
{
return ($text != html_entity_decode($text)) || ($text != strip_tags($text));
}
}

View File

@ -67,7 +67,7 @@ class Widget
if (DI::config()->get('system', 'invitation_only')) {
$x = intval(DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'invites_remaining'));
if ($x || DI::app()->isSiteAdmin()) {
if ($x || DI::userSession()->isSiteAdmin()) {
DI::page()['aside'] .= '<div class="side-link widget" id="side-invite-remain">'
. DI::l10n()->tt('%d invitation available', '%d invitations available', $x)
. '</div>';
@ -336,7 +336,7 @@ class Widget
*/
public static function categories(int $uid, string $baseurl, string $selected = ''): string
{
if (!Feature::isEnabled($uid, 'categories')) {
if (!Feature::isEnabled($uid, Feature::CATEGORIES)) {
return '';
}
@ -428,7 +428,7 @@ class Widget
return '';
}
if (Feature::isEnabled($uid, 'tagadelic')) {
if (Feature::isEnabled($uid, Feature::TAGCLOUD)) {
$owner_id = Contact::getPublicIdByUserId($uid);
if (!$owner_id) {
@ -598,4 +598,4 @@ class Widget
$channelname
);
}
}
}

View File

@ -21,6 +21,7 @@
namespace Friendica\Content\Widget;
use Friendica\Content\Conversation\Entity\Community;
use Friendica\Core\Renderer;
use Friendica\DI;
use Friendica\Model\Tag;
@ -39,9 +40,9 @@ class TrendingTags
* @return string Formatted HTML code
* @throws \Exception
*/
public static function getHTML(string $content = 'global', int $period = 24): string
public static function getHTML(string $content = Community::GLOBAL, int $period = 24): string
{
if ($content == 'local') {
if ($content == Community::LOCAL) {
$tags = Tag::getLocalTrendingHashtags($period, 20);
} else {
$tags = Tag::getGlobalTrendingHashtags($period, 20);

View File

@ -43,9 +43,10 @@ class VCard
* @template widget/vcard.tpl
* @param array $contact
* @param bool $hide_mention
* @param bool $hide_follow
* @return string
*/
public static function getHTML(array $contact, bool $hide_mention = false): string
public static function getHTML(array $contact, bool $hide_mention = false, bool $hide_follow = false): string
{
if (!isset($contact['network']) || !isset($contact['id'])) {
Logger::warning('Incomplete contact', ['contact' => $contact ?? []]);
@ -87,7 +88,7 @@ class VCard
}
}
if (empty($contact['self']) && Protocol::supportsFollow($contact['network'])) {
if (!$hide_follow && empty($contact['self']) && Protocol::supportsFollow($contact['network'])) {
if (in_array($rel, [Contact::SHARING, Contact::FRIEND])) {
$unfollow_link = 'contact/unfollow?url=' . urlencode($contact_url) . '&auto=1';
} elseif (!$pending) {

View File

@ -140,7 +140,8 @@ class Addon
$func();
}
Hook::delete(['file' => 'addon/' . $addon . '/' . $addon . '.php']);
// Handles both relative and absolute file paths
Hook::delete(['`file` LIKE ?', "%addon/$addon/$addon.php"]);
unset(self::$addons[array_search($addon, self::$addons)]);
}

View File

@ -495,6 +495,13 @@ class Installer
);
$returnVal = $returnVal ? $status : false;
$status = $this->checkFunction('idn_to_ascii',
DI::l10n()->t('IDN Functions PHP module'),
DI::l10n()->t('Error: IDN Functions PHP module required but not installed.'),
true
);
$returnVal = $returnVal ? $status : false;
return $returnVal;
}

View File

@ -75,8 +75,11 @@ class Renderer
{
DI::profiler()->startRecording('rendering');
// pass $baseurl to all templates if it isn't set
$vars = array_merge(['$baseurl' => DI::baseUrl(), '$APP' => DI::app()], $vars);
// Default template variables
$vars = array_merge([
'$baseurl' => DI::baseUrl(),
'$VERSION' => \Friendica\App::VERSION,
], $vars);
$t = self::getTemplateEngine();
@ -84,7 +87,7 @@ class Renderer
$output = $t->replaceMacros($template, $vars);
} catch (Exception $e) {
DI::logger()->critical($e->getMessage(), ['template' => $template, 'vars' => $vars]);
$message = DI::app()->isSiteAdmin() ?
$message = DI::userSession()->isSiteAdmin() ?
$e->getMessage() :
DI::l10n()->t('Friendica can\'t display this page at the moment, please contact the administrator.');
throw new ServiceUnavailableException($message);
@ -113,7 +116,7 @@ class Renderer
$template = $t->getTemplateFile($file, $subDir);
} catch (Exception $e) {
DI::logger()->critical($e->getMessage(), ['file' => $file, 'subDir' => $subDir]);
$message = DI::app()->isSiteAdmin() ?
$message = DI::userSession()->isSiteAdmin() ?
$e->getMessage() :
DI::l10n()->t('Friendica can\'t display this page at the moment, please contact the administrator.');
throw new ServiceUnavailableException($message);
@ -142,7 +145,7 @@ class Renderer
} else {
$admin_message = DI::l10n()->t('template engine cannot be registered without a name.');
DI::logger()->critical($admin_message, ['class' => $class]);
$message = DI::app()->isSiteAdmin() ?
$message = DI::userSession()->isSiteAdmin() ?
$admin_message :
DI::l10n()->t('Friendica can\'t display this page at the moment, please contact the administrator.');
throw new ServiceUnavailableException($message);
@ -176,7 +179,7 @@ class Renderer
$admin_message = DI::l10n()->t('template engine is not registered!');
DI::logger()->critical($admin_message, ['template_engine' => $template_engine]);
$message = DI::app()->isSiteAdmin() ?
$message = DI::userSession()->isSiteAdmin() ?
$admin_message :
DI::l10n()->t('Friendica can\'t display this page at the moment, please contact the administrator.');
throw new ServiceUnavailableException($message);

View File

@ -24,6 +24,8 @@ namespace Friendica\Core;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Network\HTTPException;
use Friendica\Object\Search\ContactResult;
use Friendica\Object\Search\ResultList;
@ -125,7 +127,7 @@ class Search
$searchUrl .= '&page=' . $page;
}
$resultJson = DI::httpClient()->fetch($searchUrl, HttpClientAccept::JSON);
$resultJson = DI::httpClient()->fetch($searchUrl, HttpClientAccept::JSON, 0, '', HttpClientRequest::CONTACTDISCOVER);
$results = json_decode($resultJson, true);
@ -232,7 +234,7 @@ class Search
$return = Contact::searchByName($search, $mode, true);
} else {
$p = $page > 1 ? 'p=' . $page : '';
$curlResult = DI::httpClient()->get(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::CONTACTDISCOVER]);
if ($curlResult->isSuccess()) {
$searchResult = json_decode($curlResult->getBodyString(), true);
if (!empty($searchResult['profiles'])) {

View File

@ -21,6 +21,7 @@
namespace Friendica\Core;
use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Worker\Entity\Process;
use Friendica\Database\DBA;
use Friendica\DI;
@ -54,7 +55,8 @@ class Worker
const FAST_COMMANDS = ['APDelivery', 'Delivery'];
const LOCK_PROCESS = 'worker_process';
const LOCK_WORKER = 'worker';
const LOCK_WORKER = 'worker';
const LAST_CHECK = 'worker::check';
private static $up_start;
private static $db_duration = 0;
@ -832,6 +834,17 @@ class Worker
} else {
self::spawnWorker();
}
} elseif (($active > $queues) && ($active < $maxqueues) && ($load < $maxsysload)) {
$max_idletime = DI::config()->get('system', 'worker_max_idletime');
$last_check = DI::cache()->get(self::LAST_CHECK);
$last_date = $last_check ? date('c', $last_check) : '';
if (($max_idletime > 0) && (time() > $last_check + $max_idletime) && !DBA::exists('workerqueue', ["`done` AND `executed` > ?", DateTimeFormat::utc('now - ' . $max_idletime . ' second')])) {
DI::cache()->set(self::LAST_CHECK, time(), Duration::HOUR);
Logger::info('The last worker execution had been too long ago.', ['last' => $last_check, 'last-check' => $last_date, 'seconds' => $max_idletime, 'load' => $load, 'max_load' => $maxsysload, 'active_worker' => $active, 'max_worker' => $maxqueues]);
return false;
} elseif ($max_idletime > 0) {
Logger::debug('Maximum idletime not reached.', ['last' => $last_check, 'last-check' => $last_date, 'seconds' => $max_idletime, 'load' => $load, 'max_load' => $maxsysload, 'active_worker' => $active, 'max_worker' => $maxqueues]);
}
}
}

View File

@ -96,7 +96,6 @@ class Activities extends BaseFactory
break;
default:
$this->logger->warning('Unsupported verb in parent item:', ['parent_item' => $parent_item]);
break;
}
}

View File

@ -22,6 +22,7 @@
namespace Friendica\Factory\Api\Mastodon;
use Friendica\BaseFactory;
use Friendica\Content\Conversation\Entity\Timeline;
use Friendica\Database\Database;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Psr\Log\LoggerInterface;
@ -45,4 +46,14 @@ class ListEntity extends BaseFactory
$circle = $this->dba->selectFirst('group', ['name'], ['id' => $id, 'deleted' => false]);
return new \Friendica\Object\Api\Mastodon\ListEntity($id, $circle['name'] ?? '', 'list');
}
public function createFromChannel(Timeline $channel): \Friendica\Object\Api\Mastodon\ListEntity
{
return new \Friendica\Object\Api\Mastodon\ListEntity('channel:' . $channel->code, $channel->label, 'followed');
}
public function createFromGroup(array $group): \Friendica\Object\Api\Mastodon\ListEntity
{
return new \Friendica\Object\Api\Mastodon\ListEntity('group:' . $group['id'], $group['name'], 'followed');
}
}

View File

@ -105,14 +105,12 @@ class APContact
/**
* Fetches a profile from a given url
*
* @param string $url profile url
* @param boolean $update true = always update, false = never update, null = update when not found or outdated
* @param string $url profile url
* @param ?boolean $update true = always update, false = never update, null = update when not found or outdated
* @return array profile array
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
* @todo Rewrite parameter $update to avoid true|false|null (boolean is binary, null adds a third case)
*/
public static function getByURL(string $url, $update = null): array
public static function getByURL(string $url, bool $update = null): array
{
if (empty($url) || Network::isUrlBlocked($url)) {
Logger::info('Domain is blocked', ['url' => $url]);
@ -184,7 +182,7 @@ class APContact
DI::cache()->set($cachekey, System::callstack(20), Duration::FIVE_MINUTES);
}
if (Network::isLocalLink($url) && ($local_uid = User::getIdForURL($url))) {
if (DI::baseUrl()->isLocalUrl($url) && ($local_uid = User::getIdForURL($url))) {
try {
$data = Transmitter::getProfile($local_uid);
$local_owner = User::getOwnerDataById($local_uid);

View File

@ -30,6 +30,7 @@ use Friendica\Object\Image;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Mimetype;
use Friendica\Security\Security;
use Friendica\Util\Network;
/**
* Class to handle attach database table
@ -109,16 +110,17 @@ class Attach
/**
* Retrieve a single record given the ID
*
* @param int $id Row id of the record
* @param int $id Row id of the record
* @param int $uid User-Id
*
* @return bool|array
*
* @throws \Exception
* @see \Friendica\Database\DBA::select
*/
public static function getById(int $id)
public static function getById(int $id, int $uid)
{
return self::selectFirst([], ['id' => $id]);
return self::selectFirst([], ['id' => $id, 'uid' => $uid]);
}
/**
@ -197,7 +199,7 @@ class Attach
* @return boolean|integer Row id on success, False on errors
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function store(string $data, int $uid, string $filename, string $filetype = '' , int $filesize = null, string $allow_cid = '', string $allow_gid = '', string $deny_cid = '', string $deny_gid = '')
public static function store(string $data, int $uid, string $filename, string $filetype = '', int $filesize = null, string $allow_cid = '', string $allow_gid = '', string $deny_cid = '', string $deny_gid = '')
{
if ($filetype === '') {
$filetype = Mimetype::getContentType($filename);
@ -279,9 +281,9 @@ class Attach
{
if (!is_null($img)) {
// get items to update
$items = self::selectToArray(['backend-class','backend-ref'], $conditions);
$items = self::selectToArray(['backend-class', 'backend-ref'], $conditions);
foreach($items as $item) {
foreach ($items as $item) {
try {
$backend_class = DI::storageManager()->getWritableStorageByName($item['backend-class'] ?? '');
$fields['backend-ref'] = $backend_class->put($img->asString(), $item['backend-ref'] ?? '');
@ -313,9 +315,9 @@ class Attach
public static function delete(array $conditions, array $options = []): bool
{
// get items to delete data info
$items = self::selectToArray(['backend-class','backend-ref'], $conditions);
$items = self::selectToArray(['backend-class', 'backend-ref'], $conditions);
foreach($items as $item) {
foreach ($items as $item) {
try {
$backend_class = DI::storageManager()->getWritableStorageByName($item['backend-class'] ?? '');
$backend_class->delete($item['backend-ref'] ?? '');
@ -328,4 +330,41 @@ class Attach
return DBA::delete('attach', $conditions, $options);
}
public static function setPermissionFromBody(array $post)
{
preg_match_all("/\[attachment\](.*?)\[\/attachment\]/ism", $post['body'], $matches, PREG_SET_ORDER);
foreach ($matches as $attachment) {
if (DI::baseUrl()->isLocalUrl($attachment[1]) && preg_match('|.*?/attach/(\d+)|', $attachment[1], $match)) {
$fields = [
'allow_cid' => $post['allow_cid'], 'allow_gid' => $post['allow_gid'],
'deny_cid' => $post['deny_cid'], 'deny_gid' => $post['deny_gid']
];
self::update($fields, ['id' => $match[1], 'uid' => $post['uid']]);
}
}
}
public static function addAttachmentToBody(string $body, int $uid): string
{
preg_match_all("/\[attachment\](.*?)\[\/attachment\]/ism", $body, $matches, PREG_SET_ORDER);
foreach ($matches as $attachment) {
if (DI::baseUrl()->isLocalUrl($attachment[1]) && preg_match('|.*?/attach/(\d+)|', $attachment[1], $match)) {
$attach = self::getById($match[1], $uid);
if (empty($attach)) {
return $body;
}
$media = [
'type' => Post\Media::DOCUMENT,
'url' => $attachment[1],
'size' => $attach['filesize'],
'mimetype' => $attach['filetype'],
'description' => $attach['filename']
];
$media = Post\Media::addType($media);
$body = str_replace($attachment[0], Post\Media::addAttachmentToBody($media, ''), $body);
}
}
return $body;
}
}

View File

@ -113,15 +113,6 @@ class Contact
* @}
*/
/** @deprecated Use Entity\LocalRelationship::MIRROR_DEACTIVATED instead */
const MIRROR_DEACTIVATED = LocalRelationship::MIRROR_DEACTIVATED;
/** @deprecated Now does the same as MIRROR_OWN_POST */
const MIRROR_FORWARDED = 1;
/** @deprecated Use Entity\LocalRelationship::MIRROR_OWN_POST instead */
const MIRROR_OWN_POST = LocalRelationship::MIRROR_OWN_POST;
/** @deprecated Use Entity\LocalRelationship::MIRROR_NATIVE_RESHARE instead */
const MIRROR_NATIVE_RESHARE = LocalRelationship::MIRROR_NATIVE_RESHARE;
/**
* @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition
@ -892,10 +883,10 @@ class Contact
$fields['avatar'] = User::getAvatarUrl($user);
$fields['header'] = User::getBannerUrl($user);
$fields['forum'] = $user['page-flags'] == User::PAGE_FLAGS_COMMUNITY;
$fields['forum'] = in_array($user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN]);
$fields['prv'] = $user['page-flags'] == User::PAGE_FLAGS_PRVGROUP;
$fields['unsearchable'] = !$profile['net-publish'];
$fields['manually-approve'] = in_array($user['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP]);
$fields['manually-approve'] = in_array($user['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP, User::PAGE_FLAGS_COMM_MAN]);
$fields['baseurl'] = DI::baseUrl();
$fields['gsid'] = GServer::getID($fields['baseurl'], true);
@ -1793,7 +1784,7 @@ class Contact
return;
}
if (Network::isLocalLink($contact['url'])) {
if (DI::baseUrl()->isLocalUrl($contact['url'])) {
return;
}
@ -1910,7 +1901,7 @@ class Contact
return $contact;
}
$local = !empty($contact['url']) && Network::isLocalLink($contact['url']);
$local = !empty($contact['url']) && DI::baseUrl()->isLocalUrl($contact['url']);
if (!$local && !empty($contact['id']) && !empty($contact['avatar'])) {
self::updateAvatar($contact['id'], $contact['avatar'], true);
@ -2300,7 +2291,7 @@ class Contact
if (($uid == 0) && !$force && empty($contact['thumb']) && empty($contact['micro']) && !$create_cache) {
if (($contact['avatar'] != $avatar) || empty($contact['blurhash'])) {
$update_fields = ['avatar' => $avatar];
if (!Network::isLocalLink($avatar)) {
if (!DI::baseUrl()->isLocalUrl($avatar)) {
try {
$fetchResult = HTTPSignature::fetchRaw($avatar, 0, [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE]]);
@ -2348,7 +2339,7 @@ class Contact
$cache_avatar = DI::config()->get('system', 'cache_contact_avatar');
// Local contact avatars don't need to be cached
if ($cache_avatar && Network::isLocalLink($contact['url'])) {
if ($cache_avatar && DI::baseUrl()->isLocalUrl($contact['url'])) {
$cache_avatar = !DBA::exists('contact', ['nurl' => $contact['nurl'], 'self' => true]);
}

View File

@ -364,6 +364,53 @@ class User
return $frequency;
}
/**
* Set the channel only value for contact id and user id
*
* @param int $cid Either public contact id or user's contact id
* @param int $uid User ID
* @param int $isChannelOnly Is channel only
* @return void
* @throws \Exception
*/
public static function setChannelOnly(int $cid, int $uid, bool $isChannelOnly)
{
$cdata = Contact::getPublicAndUserContactID($cid, $uid);
if (empty($cdata)) {
return;
}
DBA::update('user-contact', ['channel-only' => $isChannelOnly], ['cid' => $cdata['public'], 'uid' => $uid], true);
}
/**
* Returns if the contact is channel only for contact id and user id
*
* @param int $cid Either public contact id or user's contact id
* @param int $uid User ID
* @return bool Contact is channel only
* @throws HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function getChannelOnly(int $cid, int $uid): bool
{
$cdata = Contact::getPublicAndUserContactID($cid, $uid);
if (empty($cdata)) {
return false;
}
$isChannelOnly = false;
if (!empty($cdata['public'])) {
$public_contact = DBA::selectFirst('user-contact', ['channel-only'], ['cid' => $cdata['public'], 'uid' => $uid]);
if (DBA::isResult($public_contact)) {
$isChannelOnly = $public_contact['channel-only'] ?? false;
}
}
return $isChannelOnly;
}
/**
* Set/Release that the user is blocked by the contact
*

View File

@ -516,7 +516,7 @@ class Event
throw new HTTPException\UnauthorizedException(DI::l10n()->t('Access to this profile has been restricted.'));
}
if (!DI::userSession()->isAuthenticated() && !Feature::isEnabled($owner['uid'], 'public_calendar')) {
if (!DI::userSession()->isAuthenticated() && !Feature::isEnabled($owner['uid'], Feature::PUBLIC_CALENDAR)) {
throw new HTTPException\UnauthorizedException(DI::l10n()->t('Permission denied.'));
}

View File

@ -34,6 +34,7 @@ use Friendica\Module\Register;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Network\Probe;
use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\Relay;
@ -611,7 +612,7 @@ class GServer
$in_webroot = empty(parse_url($url, PHP_URL_PATH));
// When a nodeinfo is present, we don't need to dig further
$curlResult = DI::httpClient()->get($url . '/.well-known/x-nodeinfo2', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/.well-known/x-nodeinfo2', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if ($curlResult->isTimeout()) {
self::setFailureByUrl($url);
return false;
@ -622,7 +623,7 @@ class GServer
} else {
$serverdata = self::parseNodeinfo210($curlResult);
if (empty($serverdata)) {
$curlResult = DI::httpClient()->get($url . '/.well-known/nodeinfo', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/.well-known/nodeinfo', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
$serverdata = self::fetchNodeinfo($url, $curlResult);
}
}
@ -640,9 +641,9 @@ class GServer
if ($in_webroot) {
// Fetch the landing page, possibly it reveals some data
$accept = 'application/activity+json,application/ld+json,application/json,*/*;q=0.9';
$curlResult = DI::httpClient()->get($url, $accept);
$curlResult = DI::httpClient()->get($url, $accept, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess() && $curlResult->getReturnCode() == '406') {
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML);
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
$html_fetched = true;
} else {
$html_fetched = false;
@ -655,10 +656,10 @@ class GServer
$serverdata = $data['server'];
$systemactor = $data['actor'];
if (!$html_fetched && !in_array($serverdata['detection-method'], [self::DETECT_SYSTEM_ACTOR, self::DETECT_AP_COLLECTION])) {
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML);
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
}
} elseif (!$html_fetched && (strlen($curlResult->getBodyString()) < 1000)) {
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML);
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
}
if ($serverdata['detection-method'] != self::DETECT_SYSTEM_ACTOR) {
@ -867,7 +868,7 @@ class GServer
{
Logger::info('Discover relay data', ['server' => $server_url]);
$curlResult = DI::httpClient()->get($server_url . '/.well-known/x-social-relay', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($server_url . '/.well-known/x-social-relay', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess()) {
return;
}
@ -962,7 +963,7 @@ class GServer
*/
private static function fetchStatistics(string $url, array $serverdata): array
{
$curlResult = DI::httpClient()->get($url . '/statistics.json', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/statistics.json', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess()) {
return $serverdata;
}
@ -1109,7 +1110,7 @@ class GServer
*/
private static function parseNodeinfo1(string $nodeinfo_url): array
{
$curlResult = DI::httpClient()->get($nodeinfo_url, HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($nodeinfo_url, HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess()) {
return [];
}
@ -1209,7 +1210,7 @@ class GServer
*/
private static function parseNodeinfo2(string $nodeinfo_url): array
{
$curlResult = DI::httpClient()->get($nodeinfo_url, HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($nodeinfo_url, HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess()) {
return [];
}
@ -1429,7 +1430,7 @@ class GServer
*/
private static function fetchSiteinfo(string $url, array $serverdata): array
{
$curlResult = DI::httpClient()->get($url . '/siteinfo.json', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/siteinfo.json', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess()) {
return $serverdata;
}
@ -1586,8 +1587,11 @@ class GServer
private static function getNomadName(string $url): string
{
$name = 'nomad';
$curlResult = DI::httpClient()->get($url . '/manifest', 'application/manifest+json');
$curlResult = DI::httpClient()->get($url . '/manifest', 'application/manifest+json', [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess() || ($curlResult->getBodyString() == '')) {
if ($curlResult->getReturnCode() == 418) {
$name = 'streams';
}
return $name;
}
@ -1607,7 +1611,7 @@ class GServer
*/
private static function getNomadVersion(string $url): string
{
$curlResult = DI::httpClient()->get($url . '/api/z/1.0/version', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/api/z/1.0/version', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess() || ($curlResult->getBodyString() == '')) {
return '';
}
@ -1629,7 +1633,7 @@ class GServer
private static function validHostMeta(string $url): bool
{
$xrd_timeout = DI::config()->get('system', 'xrd_timeout');
$curlResult = DI::httpClient()->get($url . Probe::HOST_META, HttpClientAccept::XRD_XML, [HttpClientOptions::TIMEOUT => $xrd_timeout]);
$curlResult = DI::httpClient()->get($url . Probe::HOST_META, HttpClientAccept::XRD_XML, [HttpClientOptions::TIMEOUT => $xrd_timeout, HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess()) {
return false;
}
@ -1728,7 +1732,7 @@ class GServer
{
$serverdata['poco'] = '';
$curlResult = DI::httpClient()->get($url . '/poco', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/poco', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess()) {
return $serverdata;
}
@ -1758,7 +1762,7 @@ class GServer
*/
public static function checkMastodonDirectory(string $url, array $serverdata): array
{
$curlResult = DI::httpClient()->get($url . '/api/v1/directory?limit=1', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/api/v1/directory?limit=1', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess()) {
return $serverdata;
}
@ -1785,7 +1789,7 @@ class GServer
*/
private static function detectPeertube(string $url, array $serverdata): array
{
$curlResult = DI::httpClient()->get($url . '/api/v1/config', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/api/v1/config', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess() || ($curlResult->getBodyString() == '')) {
return $serverdata;
}
@ -1833,7 +1837,7 @@ class GServer
*/
private static function detectNextcloud(string $url, array $serverdata, bool $validHostMeta): array
{
$curlResult = DI::httpClient()->get($url . '/status.php', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/status.php', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess() || ($curlResult->getBodyString() == '')) {
return $serverdata;
}
@ -1869,7 +1873,7 @@ class GServer
*/
private static function fetchWeeklyUsage(string $url, array $serverdata): array
{
$curlResult = DI::httpClient()->get($url . '/api/v1/instance/activity', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/api/v1/instance/activity', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess() || ($curlResult->getBodyString() == '')) {
return $serverdata;
}
@ -1909,7 +1913,7 @@ class GServer
*/
private static function detectMastodonAlikes(string $url, array $serverdata): array
{
$curlResult = DI::httpClient()->get($url . '/api/v1/instance', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/api/v1/instance', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess() || ($curlResult->getBodyString() == '')) {
return $serverdata;
}
@ -1981,7 +1985,7 @@ class GServer
*/
private static function detectHubzilla(string $url, array $serverdata): array
{
$curlResult = DI::httpClient()->get($url . '/api/statusnet/config.json', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/api/statusnet/config.json', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess() || ($curlResult->getBodyString() == '')) {
return $serverdata;
}
@ -2078,7 +2082,7 @@ class GServer
private static function detectGNUSocial(string $url, array $serverdata): array
{
// Test for GNU Social
$curlResult = DI::httpClient()->get($url . '/api/gnusocial/version.json', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/api/gnusocial/version.json', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if ($curlResult->isSuccess() && ($curlResult->getBodyString() != '{"error":"not implemented"}') &&
($curlResult->getBodyString() != '') && (strlen($curlResult->getBodyString()) < 30)) {
$serverdata['platform'] = 'gnusocial';
@ -2096,7 +2100,7 @@ class GServer
}
// Test for Statusnet
$curlResult = DI::httpClient()->get($url . '/api/statusnet/version.json', HttpClientAccept::JSON);
$curlResult = DI::httpClient()->get($url . '/api/statusnet/version.json', HttpClientAccept::JSON, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if ($curlResult->isSuccess() && ($curlResult->getBodyString() != '{"error":"not implemented"}') &&
($curlResult->getBodyString() != '') && (strlen($curlResult->getBodyString()) < 30)) {
@ -2134,9 +2138,9 @@ class GServer
{
// There is a bug in some versions of Friendica that will return an ActivityStream actor when the content type "application/json" is requested.
// Because of this me must not use ACCEPT_JSON here.
$curlResult = DI::httpClient()->get($url . '/friendica/json');
$curlResult = DI::httpClient()->get($url . '/friendica/json', HttpClientAccept::DEFAULT, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
if (!$curlResult->isSuccess()) {
$curlResult = DI::httpClient()->get($url . '/friendika/json');
$curlResult = DI::httpClient()->get($url . '/friendika/json', HttpClientAccept::DEFAULT, [HttpClientOptions::REQUEST => HttpClientRequest::SERVERINFO]);
$friendika = true;
$platform = 'Friendika';
} else {
@ -2447,7 +2451,7 @@ class GServer
$protocols = ['activitypub', 'diaspora', 'dfrn', 'ostatus'];
foreach ($protocols as $protocol) {
$query = '{nodes(protocol:"' . $protocol . '"){host}}';
$curlResult = DI::httpClient()->fetch('https://the-federation.info/graphql?query=' . urlencode($query), HttpClientAccept::JSON);
$curlResult = DI::httpClient()->fetch('https://the-federation.info/graphql?query=' . urlencode($query), HttpClientAccept::JSON, 0, '', HttpClientRequest::SERVERDISCOVER);
if (!empty($curlResult)) {
$data = json_decode($curlResult, true);
if (!empty($data['data']['nodes'])) {
@ -2464,7 +2468,7 @@ class GServer
if (!empty($accesstoken)) {
$api = 'https://instances.social/api/1.0/instances/list?count=0';
$curlResult = DI::httpClient()->get($api, HttpClientAccept::JSON, [HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . $accesstoken]]]);
$curlResult = DI::httpClient()->get($api, HttpClientAccept::JSON, [HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . $accesstoken], HttpClientOptions::REQUEST => HttpClientRequest::SERVERDISCOVER]]);
if ($curlResult->isSuccess()) {
$servers = json_decode($curlResult->getBodyString(), true);

View File

@ -39,6 +39,7 @@ use Friendica\DI;
use Friendica\Model\Post\Category;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Network\HTTPException\ServiceUnavailableException;
use Friendica\Protocol\Activity;
@ -108,7 +109,7 @@ class Item
'owner-id', 'owner-link', 'owner-alias', 'owner-name', 'owner-avatar', 'owner-network', 'owner-contact-type', 'owner-updated', 'owner-gsid',
'causer-id', 'causer-link', 'causer-alias', 'causer-name', 'causer-avatar', 'causer-contact-type', 'causer-network', 'causer-gsid',
'contact-id', 'contact-uid', 'contact-link', 'contact-name', 'contact-avatar',
'writable', 'self', 'cid', 'alias',
'writable', 'restrictions', 'self', 'cid', 'alias', 'post-reason',
'event-created', 'event-edited', 'event-start', 'event-finish',
'event-summary', 'event-desc', 'event-location', 'event-type',
'event-nofinish', 'event-ignore', 'event-id',
@ -121,7 +122,7 @@ class Item
const DELIVER_FIELDLIST = [
'uid', 'id', 'parent', 'uri-id', 'uri', 'thr-parent', 'parent-uri', 'guid',
'parent-guid', 'conversation', 'received', 'created', 'edited', 'verb', 'object-type', 'object', 'target',
'private', 'title', 'body', 'raw-body', 'language', 'location', 'coord', 'app', 'sensitive',
'private', 'title', 'content-warning', 'body', 'raw-body', 'language', 'location', 'coord', 'app', 'sensitive',
'inform', 'deleted', 'extid', 'post-type', 'post-reason', 'gravity',
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
'author-id', 'author-addr', 'author-link', 'author-name', 'author-avatar', 'owner-id', 'owner-link', 'contact-uid',
@ -168,6 +169,11 @@ class Item
const GRAVITY_COMMENT = 6;
const GRAVITY_UNKNOWN = 9;
// Restrictions
const CANT_REPLY = 1;
const CANT_LIKE = 2;
const CANT_ANNOUNCE = 4;
/**
* Update existing item entries
*
@ -759,7 +765,7 @@ class Item
{
$fields = [
'uid', 'uri', 'parent-uri', 'id', 'deleted',
'uri-id', 'parent-uri-id',
'uri-id', 'parent-uri-id', 'restrictions', 'verb',
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
'wall', 'private', 'origin', 'author-id'
];
@ -783,6 +789,11 @@ class Item
return [];
}
if (self::hasRestrictions($item, $parent['author-id'], $parent['restrictions'])) {
Logger::notice('Restrictions apply - ignoring item', ['restrictions' => $parent['restrictions'], 'verb' => $parent['verb'], 'uri-id' => $item['uri-id'], 'thr-parent-id' => $item['thr-parent-id'], 'uid' => $item['uid']]);
return [];
}
if ($parent['uri-id'] == $parent['parent-uri-id']) {
return $parent;
}
@ -838,7 +849,7 @@ class Item
private static function prepareOriginPost(array $item): array
{
$item = DI::contentItem()->initializePost($item);
$item = DI::contentItem()->finalizePost($item);
$item = DI::contentItem()->finalizePost($item, false);
return $item;
}
@ -1034,7 +1045,7 @@ class Item
$item['deny_gid'] = $store_permissions ? $toplevel_parent['deny_gid'] : '';
}
$parent_origin = $toplevel_parent['origin'];
$parent_origin = $toplevel_parent['origin'];
// Don't federate received participation messages
if ($item['verb'] != Activity::FOLLOW) {
@ -1078,8 +1089,6 @@ class Item
) {
$item['object-type'] = Activity\ObjectType::IMAGE;
}
$item = DI::contentItem()->moveAttachmentsFromBodyToAttach($item);
}
$item['parent-uri-id'] = ItemURI::getIdByURI($item['parent-uri']);
@ -1325,6 +1334,8 @@ class Item
return 0;
}
Post\Origin::insert($posted_item);
// update the commented timestamp on the parent
if (DI::config()->get('system', 'like_no_comment')) {
// Update when it is a comment
@ -1439,6 +1450,27 @@ class Item
return $post_user_id;
}
private static function hasRestrictions(array $item, int $author_id, int $restrictions = null): bool
{
if (empty($restrictions) || ($author_id == $item['author-id'])) {
return false;
}
if (($restrictions & self::CANT_REPLY) && ($item['verb'] == Activity::POST)) {
return true;
}
if (($restrictions & self::CANT_ANNOUNCE) && ($item['verb'] == Activity::ANNOUNCE)) {
return true;
}
if (($restrictions & self::CANT_LIKE) && in_array($item['verb'], [Activity::LIKE, Activity::DISLIKE, Activity::ATTEND, Activity::ATTENDMAYBE, Activity::ATTENDNO])) {
return true;
}
return false;
}
private static function reshareChannelPost(int $uri_id, int $reshare_id = 0)
{
if (!DI::config()->get('system', 'allow_relay_channels')) {
@ -3315,7 +3347,6 @@ class Item
$item['tags'] = $tags['tags'];
$item['hashtags'] = $tags['hashtags'];
$item['mentions'] = $tags['mentions'];
$sensitive = $item['sensitive'] && !DI::pConfig()->get($uid, 'system', 'display_sensitive', false);
if (!$is_preview) {
$item['body'] = preg_replace("#\s*\[attachment .*?].*?\[/attachment]\s*#ism", "\n", $item['body']);
@ -3383,11 +3414,11 @@ class Item
$shared_links = array_merge($shared_links, $sharedSplitAttachments['visual']->column('url'));
$shared_links = array_merge($shared_links, $sharedSplitAttachments['link']->column('url'));
$shared_links = array_merge($shared_links, $sharedSplitAttachments['additional']->column('url'));
$item['body'] = self::replaceVisualAttachments($sharedSplitAttachments['visual'], $item['body'], $sensitive);
$item['body'] = self::replaceVisualAttachments($sharedSplitAttachments['visual'], $item['body']);
}
$itemSplitAttachments = DI::postMediaRepository()->splitAttachments($item['uri-id'], $shared_links, $item['has-media'] ?? false);
$item['body'] = self::replaceVisualAttachments($itemSplitAttachments['visual'], $item['body'] ?? '', $sensitive);
$item['body'] = self::replaceVisualAttachments($itemSplitAttachments['visual'], $item['body'] ?? '');
self::putInCache($item);
$item['body'] = $body;
@ -3408,8 +3439,8 @@ class Item
$filter_reasons[] = DI::l10n()->t('Content from %s is collapsed', $item['author-name']);
}
if (!empty($item['content-warning']) && (!$uid || !DI::pConfig()->get($uid, 'system', 'disable_cw', false))) {
$filter_reasons[] = DI::l10n()->t('Content warning: %s', $item['content-warning']);
if ($item['sensitive'] && (!$uid || !DI::pConfig()->get($uid, 'system', 'disable_cw', false))) {
$filter_reasons[] = DI::l10n()->t('Sensitive content');
}
$item['attachments'] = $itemSplitAttachments;
@ -3442,9 +3473,9 @@ class Item
}
if (!empty($sharedSplitAttachments)) {
$s = self::addGallery($s, $sharedSplitAttachments['visual'], $sensitive);
$s = self::addVisualAttachments($sharedSplitAttachments['visual'], $shared_item, $s, true, $sensitive);
$s = self::addLinkAttachment($shared_uri_id ?: $item['uri-id'], $sharedSplitAttachments, $body, $s, true, $quote_shared_links, $sensitive);
$s = self::addGallery($s, $sharedSplitAttachments['visual']);
$s = self::addVisualAttachments($sharedSplitAttachments['visual'], $shared_item, $s, true);
$s = self::addLinkAttachment($shared_uri_id ?: $item['uri-id'], $sharedSplitAttachments, $body, $s, true, $quote_shared_links);
$s = self::addNonVisualAttachments($sharedSplitAttachments['additional'], $item, $s, true);
$body = BBCode::removeSharedData($body);
}
@ -3455,9 +3486,9 @@ class Item
$s = substr($s, 0, $pos);
}
$s = self::addGallery($s, $itemSplitAttachments['visual'], $sensitive);
$s = self::addVisualAttachments($itemSplitAttachments['visual'], $item, $s, false, $sensitive);
$s = self::addLinkAttachment($item['uri-id'], $itemSplitAttachments, $body, $s, false, $shared_links, $sensitive);
$s = self::addGallery($s, $itemSplitAttachments['visual']);
$s = self::addVisualAttachments($itemSplitAttachments['visual'], $item, $s, false);
$s = self::addLinkAttachment($item['uri-id'], $itemSplitAttachments, $body, $s, false, $shared_links);
$s = self::addNonVisualAttachments($itemSplitAttachments['additional'], $item, $s, false);
$s = self::addQuestions($item, $s);
@ -3491,10 +3522,9 @@ class Item
*
* @param string $s
* @param PostMedias $PostMedias
* @param bool $sensitive
* @return string
*/
private static function addGallery(string $s, PostMedias $PostMedias, bool $sensitive): string
private static function addGallery(string $s, PostMedias $PostMedias): string
{
foreach ($PostMedias as $PostMedia) {
if (!$PostMedia->preview || ($PostMedia->type !== Post\Media::IMAGE)) {
@ -3504,10 +3534,9 @@ class Item
if ($PostMedia->hasDimensions()) {
$pattern = '#<a href="' . preg_quote($PostMedia->url) . '">(.*?)"></a>#';
$s = preg_replace_callback($pattern, function () use ($PostMedia, $sensitive) {
$s = preg_replace_callback($pattern, function () use ($PostMedia) {
return Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image/single_with_height_allocation.tpl'), [
'$image' => $PostMedia,
'$sensitive' => $sensitive,
'$allocated_height' => $PostMedia->getAllocatedHeight(),
'$allocated_max_width' => ($PostMedia->previewWidth ?? $PostMedia->width) . 'px',
]);
@ -3576,10 +3605,9 @@ class Item
*
* @param PostMedias $PostMedias
* @param string $body
* @param bool $sensitive
* @return string modified body
*/
private static function replaceVisualAttachments(PostMedias $PostMedias, string $body, bool $sensitive): string
private static function replaceVisualAttachments(PostMedias $PostMedias, string $body): string
{
DI::profiler()->startRecording('rendering');
@ -3588,7 +3616,7 @@ class Item
if (DI::baseUrl()->isLocalUri($PostMedia->preview)) {
continue;
}
$proxy = DI::baseUrl() . $PostMedia->getPreviewPath(Proxy::SIZE_LARGE, $sensitive);
$proxy = DI::baseUrl() . $PostMedia->getPreviewPath(Proxy::SIZE_LARGE);
$search = ['[img=' . $PostMedia->preview . ']', ']' . $PostMedia->preview . '[/img]'];
$replace = ['[img=' . $proxy . ']', ']' . $proxy . '[/img]'];
@ -3597,7 +3625,7 @@ class Item
if (DI::baseUrl()->isLocalUri($PostMedia->url)) {
continue;
}
$proxy = DI::baseUrl() . $PostMedia->getPreviewPath(Proxy::SIZE_LARGE, $sensitive);
$proxy = DI::baseUrl() . $PostMedia->getPreviewPath(Proxy::SIZE_LARGE);
$search = ['[img=' . $PostMedia->url . ']', ']' . $PostMedia->url . '[/img]'];
$replace = ['[img=' . $proxy . ']', ']' . $proxy . '[/img]'];
@ -3615,11 +3643,10 @@ class Item
* @param array $item
* @param string $content
* @param bool $shared
* @param bool $sensitive
* @return string modified content
* @throws ServiceUnavailableException
*/
private static function addVisualAttachments(PostMedias $PostMedias, array $item, string $content, bool $shared, bool $sensitive): string
private static function addVisualAttachments(PostMedias $PostMedias, array $item, string $content, bool $shared): string
{
DI::profiler()->startRecording('rendering');
$leading = '';
@ -3634,7 +3661,7 @@ class Item
if ($PostMedia->mimetype->type == 'image' || $PostMedia->preview) {
$preview_size = Proxy::SIZE_MEDIUM;
$preview_url = DI::baseUrl() . $PostMedia->getPreviewPath($preview_size, $sensitive);
$preview_url = DI::baseUrl() . $PostMedia->getPreviewPath($preview_size);
} else {
$preview_size = 0;
$preview_url = '';
@ -3655,13 +3682,14 @@ class Item
/// @todo Move the template to /content as well
$media = Renderer::replaceMacros(Renderer::getMarkupTemplate('video_top.tpl'), [
'$video' => [
'id' => $PostMedia->id,
'src' => (string)$PostMedia->url,
'name' => $PostMedia->name ?: $PostMedia->url,
'preview' => $preview_url,
'mime' => (string)$PostMedia->mimetype,
'height' => $height,
'width' => $width,
'id' => $PostMedia->id,
'src' => (string)$PostMedia->url,
'name' => $PostMedia->name ?: $PostMedia->url,
'preview' => $preview_url,
'mime' => (string)$PostMedia->mimetype,
'height' => $height,
'width' => $width,
'description' => $PostMedia->description,
],
]);
if (($item['post-type'] ?? null) == Item::PT_VIDEO) {
@ -3728,12 +3756,11 @@ class Item
* @param string $content
* @param bool $shared
* @param array $ignore_links A list of URLs to ignore
* @param bool $sensitive
* @return string modified content
* @throws InternalServerErrorException
* @throws ServiceUnavailableException
*/
private static function addLinkAttachment(int $uriid, array $attachments, string $body, string $content, bool $shared, array $ignore_links, bool $sensitive): string
private static function addLinkAttachment(int $uriid, array $attachments, string $body, string $content, bool $shared, array $ignore_links): string
{
DI::profiler()->startRecording('rendering');
// Don't show a preview when there is a visual attachment (audio or video)
@ -3776,9 +3803,9 @@ class Item
if ($preview && $attachment->preview) {
if ($attachment->previewWidth >= 500) {
$data['image'] = DI::baseUrl() . $attachment->getPreviewPath(Proxy::SIZE_MEDIUM, $sensitive);
$data['image'] = DI::baseUrl() . $attachment->getPreviewPath(Proxy::SIZE_MEDIUM);
} else {
$data['preview'] = DI::baseUrl() . $attachment->getPreviewPath(Proxy::SIZE_MEDIUM, $sensitive);
$data['preview'] = DI::baseUrl() . $attachment->getPreviewPath(Proxy::SIZE_MEDIUM);
}
}
@ -3803,11 +3830,6 @@ class Item
$data = BBCode::getAttachmentData($match[1]);
}
if ($sensitive) {
$data['image'] = '';
$data['preview'] = '';
}
DI::profiler()->stopRecording();
if (isset($data['url']) && !in_array(strtolower($data['url']), $ignore_links)) {
@ -3830,6 +3852,11 @@ class Item
$preview_mode = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'preview_mode', BBCode::PREVIEW_LARGE);
if ($preview_mode != BBCode::PREVIEW_NONE) {
$rendered = BBCode::convertAttachment('', BBCode::INTERNAL, false, $data, $uriid, $preview_mode);
} elseif (!self::containsLink($content, $data['url'], Post\Media::HTML)) {
$rendered = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/link.tpl'), [
'$url' => $data['url'],
'$title' => $data['title'],
]);
} else {
$rendered = '';
}
@ -3950,10 +3977,17 @@ class Item
{
if (!empty($item['plink']) && Network::isValidHttpUrl($item['plink'])) {
$plink = $item['plink'];
} elseif (!empty($item['uri']) && Network::isValidHttpUrl($item['uri']) && !Network::isLocalLink($item['uri'])) {
} elseif (!empty($item['uri']) && Network::isValidHttpUrl($item['uri']) && !DI::baseUrl()->isLocalUrl($item['uri'])) {
$plink = $item['uri'];
}
if (($item['post-reason'] == self::PR_ANNOUNCEMENT) && ($item['owner-contact-type'] == Contact::TYPE_COMMUNITY) && ($item['owner-network'] == Protocol::DFRN)) {
$contact = Contact::getById($item['owner-id'], ['baseurl']);
if (!empty($contact['baseurl'])) {
$plink = $contact['baseurl'] . '/display/' . $item['guid'];
}
}
if (DI::userSession()->getLocalUserId()) {
$ret = [
'href' => "display/" . $item['guid'],
@ -4096,9 +4130,14 @@ class Item
return is_numeric($hookData['item_id']) ? $hookData['item_id'] : 0;
}
$curlResult = DI::httpClient()->head($uri, [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::JSON_AS]);
if (HTTPSignature::isValidContentType($curlResult->getContentType(), $uri)) {
$fetched_uri = ActivityPub\Processor::fetchMissingActivity($uri, [], '', $completion, $uid);
try {
$curlResult = DI::httpClient()->head($uri, [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::JSON_AS, HttpClientOptions::REQUEST => HttpClientRequest::ACTIVITYPUB]);
if (HTTPSignature::isValidContentType($curlResult->getContentType(), $uri)) {
$fetched_uri = ActivityPub\Processor::fetchMissingActivity($uri, [], '', $completion, $uid);
}
} catch (\Throwable $th) {
Logger::info('Invalid link', ['uid' => $uid, 'uri' => $uri, 'code' => $th->getCode(), 'message' => $th->getMessage()]);
return 0;
}
if (!empty($fetched_uri)) {

View File

@ -32,6 +32,8 @@ use Friendica\Core\Storage\Exception\ReferenceStorageException;
use Friendica\Core\Storage\Exception\StorageException;
use Friendica\Core\Storage\Type\SystemResource;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Object\Image;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Images;
@ -234,7 +236,8 @@ class Photo
FROM `photo` WHERE `uid` = ? AND NOT `photo-type` IN (?, ?) $sqlExtra
GROUP BY `resource-id` $sqlExtra2",
$values
));
)
);
}
/**
@ -549,7 +552,7 @@ class Photo
// get photo to update
$photos = self::selectToArray(['backend-class', 'backend-ref'], $conditions);
foreach($photos as $photo) {
foreach ($photos as $photo) {
try {
$backend_class = DI::storageManager()->getWritableStorageByName($photo['backend-class'] ?? '');
$fields['backend-ref'] = $backend_class->put($image->asString(), $photo['backend-ref']);
@ -580,7 +583,9 @@ class Photo
$micro = '';
$photo = DBA::selectFirst(
'photo', ['resource-id'], ['uid' => $uid, 'contact-id' => $cid, 'scale' => 4, 'photo-type' => self::CONTACT_AVATAR]
'photo',
['resource-id'],
['uid' => $uid, 'contact-id' => $cid, 'scale' => 4, 'photo-type' => self::CONTACT_AVATAR]
);
if (!empty($photo['resource-id'])) {
$resource_id = $photo['resource-id'];
@ -597,7 +602,7 @@ class Photo
$filename = basename($image_url);
if (!empty($image_url)) {
$ret = DI::httpClient()->get($image_url, HttpClientAccept::IMAGE);
$ret = DI::httpClient()->get($image_url, HttpClientAccept::IMAGE, [HttpClientOptions::REQUEST => HttpClientRequest::MEDIAPROXY]);
Logger::debug('Got picture', ['Content-Type' => $ret->getHeader('Content-Type'), 'url' => $image_url]);
$img_str = $ret->getBodyString();
$type = $ret->getContentType();
@ -681,7 +686,9 @@ class Photo
}
$photo = DBA::selectFirst(
'photo', ['blurhash'], ['uid' => $uid, 'contact-id' => $cid, 'scale' => 4, 'photo-type' => self::CONTACT_AVATAR]
'photo',
['blurhash'],
['uid' => $uid, 'contact-id' => $cid, 'scale' => 4, 'photo-type' => self::CONTACT_AVATAR]
);
return [$image_url, $thumb, $micro, $photo['blurhash']];
@ -751,7 +758,8 @@ class Photo
if (!DI::config()->get('system', 'no_count', false)) {
/// @todo This query needs to be renewed. It is really slow
// At this time we just store the data in the cache
$albums = DBA::toArray(DBA::p("SELECT COUNT(DISTINCT `resource-id`) AS `total`, `album`, MIN(`created`) AS `created`
$albums = DBA::toArray(DBA::p(
"SELECT COUNT(DISTINCT `resource-id`) AS `total`, `album`, MIN(`created`) AS `created`
FROM `photo`
WHERE `uid` = ? AND `photo-type` IN (?, ?, ?) $sql_extra
GROUP BY `album` ORDER BY `created` DESC",
@ -762,7 +770,8 @@ class Photo
));
} else {
// This query doesn't do the count and is much faster
$albums = DBA::toArray(DBA::p("SELECT '' AS `total`, `album`, MIN(`created`) AS `created`
$albums = DBA::toArray(DBA::p(
"SELECT '' AS `total`, `album`, MIN(`created`) AS `created`
FROM `photo` USE INDEX (`uid_album_scale_created`)
WHERE `uid` = ? AND `photo-type` IN (?, ?, ?) $sql_extra
GROUP BY `album` ORDER BY `created` DESC",
@ -902,9 +911,11 @@ class Photo
*/
public static function setPermissionForResource(string $image_rid, int $uid, string $str_contact_allow, string $str_circle_allow, string $str_contact_deny, string $str_circle_deny)
{
$fields = ['allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow,
'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny,
'accessible' => DI::pConfig()->get($uid, 'system', 'accessible-photos', false)];
$fields = [
'allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow,
'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny,
'accessible' => DI::pConfig()->get($uid, 'system', 'accessible-photos', false)
];
$condition = ['resource-id' => $image_rid, 'uid' => $uid];
Logger::info('Set permissions', ['condition' => $condition, 'permissions' => $fields]);
@ -1046,7 +1057,7 @@ class Photo
{
$filename = basename($image_url);
if (!empty($image_url)) {
$ret = DI::httpClient()->get($image_url, HttpClientAccept::IMAGE);
$ret = DI::httpClient()->get($image_url, HttpClientAccept::IMAGE, [HttpClientOptions::REQUEST => HttpClientRequest::MEDIAPROXY]);
Logger::debug('Got picture', ['Content-Type' => $ret->getHeader('Content-Type'), 'url' => $image_url]);
$img_str = $ret->getBodyString();
$type = $ret->getContentType();

View File

@ -379,6 +379,21 @@ class Post
return self::selectView('post-user-view', $selected, $condition, $params);
}
/**
* Select rows from the post-origin-view view
*
* @param array $selected Array of selected fields, empty for all
* @param array $condition Array of fields for condition
* @param array $params Array of several parameters
*
* @return boolean|object
* @throws \Exception
*/
public static function selectOrigin(array $selected = [], array $condition = [], array $params = [])
{
return self::selectView('post-origin-view', $selected, $condition, $params);
}
/**
* Select rows from the post-view view
*
@ -424,6 +439,21 @@ class Post
return self::selectView('post-thread-view', $selected, $condition, $params);
}
/**
* Select rows from the post-thread-origin-view view
*
* @param array $selected Array of selected fields, empty for all
* @param array $condition Array of fields for condition
* @param array $params Array of several parameters
*
* @return boolean|object
* @throws \Exception
*/
public static function selectOriginThread(array $selected = [], array $condition = [], array $params = [])
{
return self::selectView('post-thread-origin-view', $selected, $condition, $params);
}
/**
* Select rows from the given view for a given user
*
@ -513,6 +543,11 @@ class Post
return self::selectViewForUser('post-timeline-view', $uid, $selected, $condition, $params);
}
public static function selectLocalTimelineForUser(int $uid, array $selected = [], array $condition = [], array $params = [])
{
return self::selectViewForUser('post-timeline-origin-view', $uid, $selected, $condition, $params);
}
/**
* Select rows from the post-thread-user-view view for a given user
*

View File

@ -418,4 +418,12 @@ class Engagement
}
return $fullTextSearch;
}
public static function unescapeKeywords(string $fullTextSearch): string
{
foreach (self::KEYWORDS as $keyword) {
$fullTextSearch = preg_replace('~(' . $keyword . ')_(.[\w\*@\.-]+)~', '$1:$2', $fullTextSearch);
}
return $fullTextSearch;
}
}

View File

@ -28,6 +28,7 @@ use Friendica\Core\Protocol;
use Friendica\Database\Database;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Attach;
use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Model\ItemURI;
@ -35,6 +36,7 @@ use Friendica\Model\Photo;
use Friendica\Model\Post;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Protocol\ActivityPub;
use Friendica\Util\Images;
use Friendica\Util\Network;
@ -180,14 +182,20 @@ class Media
*/
public static function fetchAdditionalData(array $media): array
{
if (Network::isLocalLink($media['url'])) {
if (DI::baseUrl()->isLocalUrl($media['url'])) {
$media = self::fetchLocalData($media);
if (preg_match('|.*?/search\?(.+)|', $media['url'], $matches)) {
return $media;
}
if (empty($media['mimetype']) || empty($media['size'])) {
Logger::debug('Unknown local link', ['url' => $media['url']]);
}
}
// Fetch the mimetype or size if missing.
if (Network::isValidHttpUrl($media['url']) && (empty($media['mimetype']) || empty($media['size']))) {
$timeout = DI::config()->get('system', 'xrd_timeout');
$curlResult = DI::httpClient()->head($media['url'], [HttpClientOptions::TIMEOUT => $timeout]);
$curlResult = DI::httpClient()->head($media['url'], [HttpClientOptions::TIMEOUT => $timeout, HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]);
// Workaround for systems that can't handle a HEAD request
if (!$curlResult->isSuccess() && ($curlResult->getReturnCode() == 405)) {
@ -389,7 +397,17 @@ class Media
*/
private static function fetchLocalData(array $media): array
{
if (!preg_match('|.*?/photo/(.*[a-fA-F0-9])\-(.*[0-9])\..*[\w]|', $media['url'] ?? '', $matches)) {
if (preg_match('|.*?/attach/(\d+)|', $media['url'], $matches)) {
$attachment = Attach::selectFirst(['filename', 'filetype', 'filesize'], ['id' => $matches[1]]);
if (!empty($attachment)) {
$media['name'] = $attachment['filename'];
$media['mimetype'] = $attachment['filetype'];
$media['size'] = $attachment['filesize'];
}
return $media;
}
if (!preg_match('|.*?/photo/(.*[a-fA-F0-9])\-(.*[0-9])\..*[\w]|', $media['url'], $matches)) {
return $media;
}
$photo = Photo::selectFirst([], ['resource-id' => $matches[1], 'scale' => $matches[2]]);
@ -902,19 +920,7 @@ class Media
$body = BBCode::removeAttachment($body);
foreach (self::getByURIId($uriid, $types) as $media) {
if (Item::containsLink($body, $media['preview'] ?? $media['url'], $media['type'])) {
continue;
}
if ($media['type'] == self::IMAGE) {
$body .= "\n" . Images::getBBCodeByUrl($media['url'], $media['preview'], $media['description'] ?? '');
} elseif ($media['type'] == self::AUDIO) {
$body .= "\n[audio]" . $media['url'] . "[/audio]\n";
} elseif ($media['type'] == self::VIDEO) {
$body .= "\n[video]" . $media['url'] . "[/video]\n";
} else {
$body .= "\n[url]" . $media['url'] . "[/url]\n";
}
$body = self::addAttachmentToBody($media, $body);
}
if (preg_match("/.*(\[attachment.*?\].*?\[\/attachment\]).*/ism", $original_body, $match)) {
@ -924,6 +930,24 @@ class Media
return $body;
}
public static function addAttachmentToBody(array $media, string $body): string
{
if (Item::containsLink($body, $media['preview'] ?? $media['url'], $media['type'])) {
return $body;
}
if ($media['type'] == self::IMAGE) {
$body .= "\n" . Images::getBBCodeByUrl($media['url'], $media['preview'], $media['description'] ?? '');
} elseif ($media['type'] == self::AUDIO) {
$body .= "\n[audio]" . $media['url'] . "[/audio]\n";
} elseif ($media['type'] == self::VIDEO) {
$body .= "\n[video]" . $media['url'] . "[/video]\n";
} else {
$body .= "\n[url]" . $media['url'] . "[/url]\n";
}
return $body;
}
/**
* Add an [attachment] element to the body for a given uri-id with a HTML media element
*

93
src/Model/Post/Origin.php Normal file
View File

@ -0,0 +1,93 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Model\Post;
use Friendica\Database\DBA;
use \BadMethodCallException;
use Friendica\Database\Database;
use Friendica\DI;
class Origin
{
/**
* Insert a new post origin entry
*
* @param array $fields
* @return boolean was the insert successful?
* @throws \Exception
*/
public static function insert(array $data = []): bool
{
if (!$data['origin'] || ($data['uid'] == 0)) {
return false;
}
$fields = DI::dbaDefinition()->truncateFieldsForTable('post-origin', $data);
return DBA::insert('post-origin', $fields, Database::INSERT_IGNORE);
}
/**
* Update a post origin entry
*
* @param integer $uri_id
* @param integer $uid
* @param array $data
* @param bool $insert_if_missing
* @return bool
* @throws \Exception
*/
public static function update(int $uri_id, int $uid, array $data = [], bool $insert_if_missing = false)
{
if (empty($uri_id)) {
throw new BadMethodCallException('Empty URI_id');
}
$fields = DI::dbaDefinition()->truncateFieldsForTable('post-origin', $data);
// Remove the key fields
unset($fields['uri-id']);
unset($fields['uid']);
if (empty($fields)) {
return true;
}
return DBA::update('post-origin', $fields, ['uri-id' => $uri_id, 'uid' => $uid], $insert_if_missing ? true : []);
}
/**
* Delete a row from the post-origin table
*
* @param array $conditions Field condition(s)
* @param array $options
* - cascade: If true we delete records in other tables that depend on the one we're deleting through
* relations (default: true)
*
* @return boolean was the delete successful?
* @throws \Exception
*/
public static function delete(array $conditions, array $options = [])
{
return DBA::delete('post-origin', $conditions, $options);
}
}

View File

@ -37,6 +37,7 @@ use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Network\HTTPException;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Protocol\Activity;
@ -296,7 +297,7 @@ class Profile
if (DI::userSession()->getLocalUserId() && ($profile['uid'] ?? 0) != DI::userSession()->getLocalUserId()) {
$profile_contact = Contact::getByURL($profile['nurl'], null, [], DI::userSession()->getLocalUserId());
}
if (!empty($profile['cid']) && self::getMyURL()) {
if (!empty($profile['cid']) && DI::userSession()->getMyUrl()) {
$profile_contact = Contact::selectFirst([], ['id' => $profile['cid']]);
}
@ -321,19 +322,19 @@ class Profile
// Who is the logged-in user to this profile?
$visitor_contact = [];
if (!empty($profile['uid']) && self::getMyURL()) {
$visitor_contact = Contact::selectFirst(['rel'], ['uid' => $profile['uid'], 'nurl' => Strings::normaliseLink(self::getMyURL())]);
if (!empty($profile['uid']) && DI::userSession()->getMyUrl()) {
$visitor_contact = Contact::selectFirst(['rel'], ['uid' => $profile['uid'], 'nurl' => Strings::normaliseLink(DI::userSession()->getMyUrl())]);
}
$local_user_is_self = self::getMyURL() && ($profile['url'] == self::getMyURL());
$visitor_is_authenticated = (bool)self::getMyURL();
$local_user_is_self = DI::userSession()->getMyUrl() && ($profile['url'] == DI::userSession()->getMyUrl());
$visitor_is_authenticated = (bool)DI::userSession()->getMyUrl();
$visitor_is_following =
in_array($visitor_contact['rel'] ?? 0, [Contact::FOLLOWER, Contact::FRIEND])
|| in_array($profile_contact['rel'] ?? 0, [Contact::SHARING, Contact::FRIEND]);
$visitor_is_followed =
in_array($visitor_contact['rel'] ?? 0, [Contact::SHARING, Contact::FRIEND])
|| in_array($profile_contact['rel'] ?? 0, [Contact::FOLLOWER, Contact::FRIEND]);
$visitor_base_path = self::getMyURL() ? preg_replace('=/profile/(.*)=ism', '', self::getMyURL()) : '';
$visitor_base_path = DI::userSession()->getMyUrl() ? preg_replace('=/profile/(.*)=ism', '', DI::userSession()->getMyUrl()) : '';
if (!$local_user_is_self) {
if (!$visitor_is_authenticated) {
@ -695,17 +696,6 @@ class Profile
]);
}
/**
* Retrieves the my_url session variable
*
* @return string
* @deprecated since version 2022.12, please use UserSession->getMyUrl instead
*/
public static function getMyURL(): string
{
return DI::userSession()->getMyUrl();
}
/**
* Process the 'zrl' parameter and initiate the remote authentication.
*
@ -729,7 +719,7 @@ class Profile
*/
public static function zrlInit(App $a)
{
$my_url = self::getMyURL();
$my_url = DI::userSession()->getMyUrl();
$my_url = Network::isUrlValid($my_url);
if (empty($my_url) || DI::userSession()->getLocalUserId()) {
@ -781,7 +771,7 @@ class Profile
$magic_path = $basepath . '/magic' . '?owa=1&dest=' . $dest . '&' . $addr_request;
// We have to check if the remote server does understand /magic without invoking something
$serverret = DI::httpClient()->head($basepath . '/magic', [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::HTML]);
$serverret = DI::httpClient()->head($basepath . '/magic', [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::HTML, HttpClientOptions::REQUEST => HttpClientRequest::MAGICAUTH]);
if ($serverret->isSuccess()) {
Logger::info('Doing magic auth for visitor ' . $my_url . ' to ' . $magic_path);
System::externalRedirect($magic_path);
@ -915,7 +905,7 @@ class Profile
}
$achar = strpos($url, '?') ? '&' : '?';
$mine = self::getMyURL();
$mine = DI::userSession()->getMyUrl();
if ($mine && !Strings::compareLink($mine, $url)) {
return $url . $achar . 'zrl=' . urlencode($mine);

View File

@ -25,7 +25,7 @@ use Friendica\Core\Logger;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use GuzzleHttp\Psr7\Uri;
class PushSubscriber
{
@ -179,7 +179,7 @@ class PushSubscriber
$parts = parse_url($subscriber['callback_url']);
unset($parts['path']);
$server_url = Network::unparseURL($parts);
$server_url = (string)Uri::fromParts((array)$parts);
$gsid = GServer::getID($server_url, true);
if (!empty($gsid)) {
GServer::setProtocol($gsid, Post\DeliveryData::OSTATUS);

View File

@ -25,6 +25,7 @@ use DivineOmega\DOFileCachePSR6\CacheItemPool;
use DivineOmega\PasswordExposed;
use ErrorException;
use Exception;
use Friendica\App;
use Friendica\Content\Pager;
use Friendica\Core\Hook;
use Friendica\Core\L10n;
@ -37,6 +38,8 @@ use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Module;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Network\HTTPException;
use Friendica\Object\Image;
use Friendica\Protocol\Delivery;
@ -72,6 +75,7 @@ class User
const PAGE_FLAGS_FREELOVE = 3;
const PAGE_FLAGS_BLOG = 4;
const PAGE_FLAGS_PRVGROUP = 5;
const PAGE_FLAGS_COMM_MAN = 6;
/**
* @}
*/
@ -161,6 +165,7 @@ class User
}
}
$system['name'] = App::PLATFORM . " '" . App::CODENAME . "' " . App::VERSION . '-' . DB_UPDATE_VERSION;
$system['sprvkey'] = $system['uprvkey'] = $system['prvkey'];
$system['spubkey'] = $system['upubkey'] = $system['pubkey'];
$system['nickname'] = $system['nick'];
@ -1397,7 +1402,7 @@ class User
$photo_failure = false;
$filename = basename($photo);
$curlResult = DI::httpClient()->get($photo, HttpClientAccept::IMAGE);
$curlResult = DI::httpClient()->get($photo, HttpClientAccept::IMAGE, [HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]);
if ($curlResult->isSuccess()) {
Logger::debug('Got picture', ['Content-Type' => $curlResult->getHeader('Content-Type'), 'url' => $photo]);
$img_str = $curlResult->getBodyString();

View File

@ -77,6 +77,11 @@ class Inbox extends BaseApi
throw new \Friendica\Network\HTTPException\BadRequestException();
}
if (!HTTPSignature::isValidContentType($this->server['CONTENT_TYPE'] ?? '')) {
Logger::notice('Unexpected content type', ['content-type' => $this->server['CONTENT_TYPE'] ?? '', 'agent' => $this->server['HTTP_USER_AGENT'] ?? '']);
throw new \Friendica\Network\HTTPException\UnsupportedMediaTypeException();
}
if (DI::config()->get('debug', 'ap_inbox_log')) {
if (HTTPSignature::getSigner($postdata, $_SERVER)) {
$filename = 'signed-activitypub';

View File

@ -51,7 +51,7 @@ class Whoami extends BaseApi
$data['name'] = $owner['name'];
$data['preferredUsername'] = $owner['nick'];
$data['alsoKnownAs'] = [];
$data['manuallyApprovesFollowers'] = in_array($owner['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP]);
$data['manuallyApprovesFollowers'] = in_array($owner['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP, User::PAGE_FLAGS_COMM_MAN]);
$data['discoverable'] = (bool)$owner['net-publish'];
$data['tag'] = [];

View File

@ -34,25 +34,24 @@ class Features extends BaseAdmin
self::checkFormSecurityTokenRedirectOnError('/admin/features', 'admin_manage_features');
$features = Feature::get(false);
foreach ($features as $fname => $fdata) {
foreach (Feature::get(false) as $fdata) {
foreach (array_slice($fdata, 1) as $f) {
$feature = $f[0];
$feature_state = 'feature_' . $feature;
$featurelock = 'featurelock_' . $feature;
switch ($_POST['featureselect_' . $feature]) {
case 0:
DI::config()->set('feature', $feature, false);
DI::config()->delete('feature_lock', $feature);
break;
if (!empty($_POST[$feature_state])) {
$val = intval($_POST[$feature_state]);
} else {
$val = 0;
}
DI::config()->set('feature', $feature, $val);
case 1:
DI::config()->set('feature', $feature, true);
DI::config()->delete('feature_lock', $feature);
break;
if (!empty($_POST[$featurelock])) {
DI::config()->set('feature_lock', $feature, 1);
} else {
DI::config()->delete('feature_lock', $feature);
case 2:
DI::config()->delete('feature', $feature);
DI::config()->set('feature_lock', $feature, true);
break;
}
}
}
@ -64,17 +63,15 @@ class Features extends BaseAdmin
{
parent::content();
$features = [];
$features = [];
$selection = [DI::l10n()->t('No'), DI::l10n()->t('Yes'), DI::l10n()->t('Locked')];
foreach (Feature::get(false) as $fname => $fdata) {
$features[$fname] = [];
$features[$fname][0] = $fdata[0];
foreach (array_slice($fdata, 1) as $f) {
$set = DI::config()->get('feature', $f[0], $f[3]);
$features[$fname][1][] = [
['feature_' . $f[0], $f[1], $set, $f[2]],
['featurelock_' . $f[0], DI::l10n()->t('Lock feature %s', $f[1]), $f[4], '']
];
$selected = $f[4] ? 2 : (int)$set;
$features[$fname][1][] = ['featureselect_' . $f[0], $f[1], $selected, $f[2], $selection];
}
}

View File

@ -221,7 +221,6 @@ class Federation extends BaseAdmin
'$page' => DI::l10n()->t('Federation Statistics'),
'$intro' => $intro,
'$counts' => $counts,
'$version' => App::VERSION,
'$legendtext' => DI::l10n()->tt('Currently this node is aware of %2$s node (%3$s active users last month, %4$s active users last six months, %5$s registered users in total) from the following platforms:', 'Currently this node is aware of %2$s nodes (%3$s active users last month, %4$s active users last six months, %5$s registered users in total) from the following platforms:', $total, number_format($total), number_format($month), number_format($halfyear), number_format($users)),
]);
}

View File

@ -66,11 +66,14 @@ class Settings extends BaseAdmin
parent::content();
$log_choices = [
LogLevel::ERROR => 'Error',
LogLevel::WARNING => 'Warning',
LogLevel::NOTICE => 'Notice',
LogLevel::INFO => 'Info',
LogLevel::DEBUG => 'Debug',
LogLevel::EMERGENCY => 'Emergency',
LogLevel::ALERT => 'Alert',
LogLevel::CRITICAL => 'Critical',
LogLevel::ERROR => 'Error',
LogLevel::WARNING => 'Warning',
LogLevel::NOTICE => 'Notice',
LogLevel::INFO => 'Info',
LogLevel::DEBUG => 'Debug',
];
if (ini_get('log_errors')) {

View File

@ -47,6 +47,8 @@ class View extends BaseAdmin
$filters_valid_values = [
'level' => [
'',
LogLevel::EMERGENCY,
LogLevel::ALERT,
LogLevel::CRITICAL,
LogLevel::ERROR,
LogLevel::WARNING,
@ -54,7 +56,7 @@ class View extends BaseAdmin
LogLevel::INFO,
LogLevel::DEBUG,
],
'context' => ['', 'index', 'worker'],
'context' => ['', 'index', 'worker', 'daemon'],
];
$filters = [
'level' => $_GET['level'] ?? '',
@ -71,10 +73,10 @@ class View extends BaseAdmin
} else {
try {
$data = DI::parsedLogIterator()
->open($f)
->withLimit(self::LIMIT)
->withFilters($filters)
->withSearch($search);
->open($f)
->withLimit(self::LIMIT)
->withFilters($filters)
->withSearch($search);
} catch (\Exception $e) {
$error = DI::l10n()->t('Couldn\'t open <strong>%1$s</strong> log file.<br/>Check to see if file %1$s is readable.', $f);
}

View File

@ -56,13 +56,14 @@ class Queue extends BaseAdmin
}
// @TODO Move to Model\WorkerQueue::getEntries()
$entries = DBA::select('workerqueue', ['id', 'parameter', 'created', 'priority', 'command'], $condition, ['limit' => 999, 'order' => ['created']]);
$entries = DBA::select('workerqueue', ['id', 'parameter', 'created', 'next_try', 'priority', 'command'], $condition, ['limit' => 999, 'order' => ['created']]);
$r = [];
while ($entry = DBA::fetch($entries)) {
// fix GH-5469. ref: src/Core/Worker.php:217
$entry['parameter'] = Arrays::recursiveImplode(json_decode($entry['parameter'], true), ': ');
$entry['created'] = DateTimeFormat::local($entry['created']);
$entry['next_try'] = DateTimeFormat::local($entry['next_try']);
$r[] = $entry;
}
DBA::close($entries);
@ -76,8 +77,10 @@ class Queue extends BaseAdmin
'$command_header' => DI::l10n()->t('Command'),
'$param_header' => DI::l10n()->t('Job Parameters'),
'$created_header' => DI::l10n()->t('Created'),
'$next_try_header' => DI::l10n()->t('Next Try'),
'$prio_header' => DI::l10n()->t('Priority'),
'$info' => $info,
'$status' => $status,
'$entries' => $r,
]);
}

View File

@ -97,7 +97,6 @@ class Site extends BaseAdmin
$adjust_poll_frequency = !empty($_POST['adjust_poll_frequency']);
$min_poll_interval = (!empty($_POST['min_poll_interval']) ? intval(trim($_POST['min_poll_interval'])) : 0);
$explicit_content = !empty($_POST['explicit_content']);
$proxify_content = !empty($_POST['proxify_content']);
$local_search = !empty($_POST['local_search']);
$blocked_tags = (!empty($_POST['blocked_tags']) ? trim($_POST['blocked_tags']) : '');
$cache_contact_avatar = !empty($_POST['cache_contact_avatar']);
@ -271,7 +270,6 @@ class Site extends BaseAdmin
$transactionConfig->set('system', 'adjust_poll_frequency' , $adjust_poll_frequency);
$transactionConfig->set('system', 'min_poll_interval' , $min_poll_interval);
$transactionConfig->set('system', 'explicit_content' , $explicit_content);
$transactionConfig->set('system', 'proxify_content' , $proxify_content);
$transactionConfig->set('system', 'local_search' , $local_search);
$transactionConfig->set('system', 'blocked_tags' , Strings::cleanTags($blocked_tags));
$transactionConfig->set('system', 'cache_contact_avatar' , $cache_contact_avatar);
@ -518,7 +516,6 @@ class Site extends BaseAdmin
'$private_addons' => ['private_addons', DI::l10n()->t('Disallow public access to addons listed in the apps menu.'), DI::config()->get('config', 'private_addons'), DI::l10n()->t('Checking this box will restrict addons listed in the apps menu to members only.')],
'$disable_embedded' => ['disable_embedded', DI::l10n()->t('Don\'t embed private images in posts'), DI::config()->get('system', 'disable_embedded'), DI::l10n()->t('Don\'t replace locally-hosted private photos in posts with an embedded copy of the image. This means that contacts who receive posts containing private photos will have to authenticate and load each image, which may take a while.')],
'$explicit_content' => ['explicit_content', DI::l10n()->t('Explicit Content'), DI::config()->get('system', 'explicit_content'), DI::l10n()->t('Set this to announce that your node is used mostly for explicit content that might not be suited for minors. This information will be published in the node information and might be used, e.g. by the global directory, to filter your node from listings of nodes to join. Additionally a note about this will be shown at the user registration page.')],
'$proxify_content' => ['proxify_content', DI::l10n()->t('Proxify external content'), DI::config()->get('system', 'proxify_content'), DI::l10n()->t('Route external content via the proxy functionality. This is used for example for some OEmbed accesses and in some other rare cases.')],
'$local_search' => ['local_search', DI::l10n()->t('Only local search'), DI::config()->get('system', 'local_search'), DI::l10n()->t('Blocks search for users who are not logged in to prevent crawlers from blocking your system.')],
'$blocked_tags' => ['blocked_tags', DI::l10n()->t('Blocked tags for trending tags'), DI::config()->get('system', 'blocked_tags'), DI::l10n()->t("Comma separated list of hashtags that shouldn't be displayed in the trending tags.")],
'$cache_contact_avatar' => ['cache_contact_avatar', DI::l10n()->t('Cache contact avatars'), DI::config()->get('system', 'cache_contact_avatar'), DI::l10n()->t('Locally store the avatar pictures of the contacts. This uses a lot of storage space but it increases the performance.')],

View File

@ -196,7 +196,7 @@ class Summary extends BaseAdmin
'$title' => DI::l10n()->t('Administration'),
'$page' => DI::l10n()->t('Summary'),
'$queues' => $queues,
'$version' => [DI::l10n()->t('Version'), App::VERSION],
'$version_label' => DI::l10n()->t('Version'),
'$platform' => App::PLATFORM,
'$codename' => App::CODENAME,
'$build' => DI::config()->get('system', 'build'),

View File

@ -47,7 +47,7 @@ class Config extends BaseApi
'broughtby' => '',
'broughtbyurl' => '',
'timezone' => DI::config()->get('system', 'default_timezone'),
'closed' => (DI::config()->get('config', 'register_policy') == Register::CLOSED),
'closed' => Register::getPolicy() === Register::CLOSED,
'inviteonly' => (bool)DI::config()->get('system', 'invitation_only'),
'private' => (bool)DI::config()->get('system', 'block_public'),
'textlimit' => (string) DI::config()->get('config', 'api_import_size', DI::config()->get('config', 'max_import_size')),

View File

@ -71,6 +71,7 @@ class UpdateCredentials extends BaseApi
}
if ($user['account-type'] == Contact::TYPE_COMMUNITY) {
// @todo Support for PAGE_FLAGS_COMM_MAN
$user['page-flags'] = $request['locked'] ? User::PAGE_FLAGS_PRVGROUP : User::PAGE_FLAGS_COMMUNITY;
} elseif ($user['account-type'] == Contact::TYPE_PERSON) {
if ($request['locked']) {

View File

@ -26,7 +26,7 @@ use Friendica\Database\DBA;
use Friendica\Model\GServer;
use Friendica\Module\BaseApi;
use Friendica\Network\HTTPException;
use Friendica\Util\Network;
use GuzzleHttp\Psr7\Uri;
/**
* Undocumented API endpoint that is implemented by both Mastodon and Pleroma
@ -47,7 +47,7 @@ class Peers extends BaseApi
while ($instance = DBA::fetch($instances)) {
$urldata = parse_url($instance['url']);
unset($urldata['scheme']);
$return[] = ltrim(Network::unparseURL($urldata), '/');
$return[] = ltrim((string)Uri::fromParts((array)$urldata), '/');
}
DBA::close($instances);

View File

@ -166,9 +166,9 @@ class InstanceV2 extends BaseApi
private function buildRegistrationsInfo(): InstanceEntity\Registrations
{
$register_policy = intval($this->config->get('config', 'register_policy'));
$enabled = ($register_policy != Register::CLOSED);
$approval_required = ($register_policy == Register::APPROVE);
$register_policy = Register::getPolicy();
$enabled = $register_policy !== Register::CLOSED;
$approval_required = $register_policy === Register::APPROVE;
return new InstanceEntity\Registrations($enabled, $approval_required);
}

View File

@ -21,16 +21,36 @@
namespace Friendica\Module\Api\Mastodon;
use Friendica\Core\System;
use Friendica\App;
use Friendica\Core\L10n;
use Friendica\DI;
use Friendica\Content\Conversation\Factory\Channel as ChannelFactory;
use Friendica\Content\Conversation\Repository;
use Friendica\Content\GroupManager;
use Friendica\Module\BaseApi;
use Friendica\Model\Circle;
use Friendica\Module\Api\ApiResponse;
use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface;
/**
* @see https://docs.joinmastodon.org/methods/timelines/lists/
*/
class Lists extends BaseApi
{
/** @var ChannelFactory */
protected $channel;
/** @var Repository\UserDefinedChannel */
protected $userDefinedChannel;
public function __construct(Repository\UserDefinedChannel $userDefinedChannel, ChannelFactory $channel, \Friendica\Factory\Api\Mastodon\Error $errorFactory, App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, ApiResponse $response, array $server, array $parameters = [])
{
parent::__construct($errorFactory, $app, $l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
$this->channel = $channel;
$this->userDefinedChannel = $userDefinedChannel;
}
protected function delete(array $request = [])
{
$this->checkAllowedScope(self::SCOPE_WRITE);
@ -102,6 +122,18 @@ class Lists extends BaseApi
foreach (Circle::getByUserId($uid) as $circle) {
$lists[] = DI::mstdnList()->createFromCircleId($circle['id']);
}
foreach ($this->channel->getTimelines($uid) as $channel) {
$lists[] = DI::mstdnList()->createFromChannel($channel);
}
foreach ($this->userDefinedChannel->selectByUid($uid) as $channel) {
$lists[] = DI::mstdnList()->createFromChannel($channel);
}
foreach (GroupManager::getList($uid, true, true, true) as $group) {
$lists[] = DI::mstdnList()->createFromGroup($group);
}
} else {
$id = $this->parameters['id'];

View File

@ -83,6 +83,10 @@ class Statuses extends BaseApi
$item['network'] = $post['network'];
$item['gravity'] = $post['gravity'];
$item['verb'] = $post['verb'];
$item['allow_cid'] = $post['allow_cid'];
$item['allow_gid'] = $post['allow_gid'];
$item['deny_cid'] = $post['deny_cid'];
$item['deny_gid'] = $post['deny_gid'];
$item['app'] = $this->getApp();
$item['sensitive'] = $request['sensitive'];

View File

@ -21,7 +21,6 @@
namespace Friendica\Module\Api\Mastodon\Statuses;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Item;
@ -54,7 +53,7 @@ class Bookmark extends BaseApi
if ($item['uid'] == 0) {
$stored = Item::storeForUserByUriId($item['uri-id'], $uid, ['post-reason' => Item::PR_ACTIVITY]);
if (!empty($stored)) {
$item = Post::selectFirst(['id', 'gravity'], ['id' => $stored]);
$item = Post::selectFirst(['id', 'uri-id', 'gravity'], ['id' => $stored]);
if (!DBA::isResult($item)) {
$this->logAndJsonError(404, $this->errorFactory->RecordNotFound());
}

View File

@ -52,7 +52,7 @@ class Reblog extends BaseApi
if ($item['network'] == Protocol::DIASPORA) {
Diaspora::performReshare($this->parameters['id'], $uid);
} elseif (!in_array($item['network'], [Protocol::DFRN, Protocol::ACTIVITYPUB, Protocol::TWITTER])) {
} elseif (!in_array($item['network'], [Protocol::DFRN, Protocol::ACTIVITYPUB, Protocol::BLUESKY, Protocol::TUMBLR, Protocol::TWITTER])) {
$this->logAndJsonError(
422,
$this->errorFactory->UnprocessableEntity($this->t("Posts from %s can't be shared", ContactSelector::networkToName($item['network'])))

View File

@ -58,7 +58,7 @@ class Unreblog extends BaseApi
if (!Item::markForDeletionById($item['id'])) {
$this->logAndJsonError(404, $this->errorFactory->RecordNotFound());
}
} elseif (!in_array($item['network'], [Protocol::DFRN, Protocol::ACTIVITYPUB, Protocol::TWITTER])) {
} elseif (!in_array($item['network'], [Protocol::DFRN, Protocol::ACTIVITYPUB, Protocol::BLUESKY, Protocol::TUMBLR, Protocol::TWITTER])) {
$this->logAndJsonError(
422,
$this->errorFactory->UnprocessableEntity($this->t("Posts from %s can't be unshared", ContactSelector::networkToName($item['network'])))

View File

@ -21,21 +21,39 @@
namespace Friendica\Module\Api\Mastodon\Timelines;
use Friendica\App;
use Friendica\Core\L10n;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Conversation;
use Friendica\Model\Item;
use Friendica\Model\Post;
use Friendica\Model\Verb;
use Friendica\Module\Api\ApiResponse;
use Friendica\Module\BaseApi;
use Friendica\Module\Conversation\Timeline;
use Friendica\Network\HTTPException;
use Friendica\Object\Api\Mastodon\TimelineOrderByTypes;
use Friendica\Protocol\Activity;
use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface;
/**
* @see https://docs.joinmastodon.org/methods/timelines/
*/
class ListTimeline extends BaseApi
{
/** @var Timeline */
protected $timeline;
public function __construct(Timeline $timeline, \Friendica\Factory\Api\Mastodon\Error $errorFactory, App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, ApiResponse $response, array $server, array $parameters = [])
{
parent::__construct($errorFactory, $app, $l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
$this->timeline = $timeline;
}
/**
* @throws HTTPException\InternalServerErrorException
*/
@ -61,6 +79,70 @@ class ListTimeline extends BaseApi
'friendica_order' => TimelineOrderByTypes::ID, // Sort order options (defaults to ID)
], $request);
$display_quotes = self::appSupportsQuotes();
if (substr($this->parameters['id'], 0, 6) == 'group:') {
$items = $this->getStatusesForGroup($uid, $request);
} elseif (substr($this->parameters['id'], 0, 8) == 'channel:') {
$items = $this->getStatusesForChannel($uid, $request);
} else{
$items = $this->getStatusesForCircle($uid, $request);
}
$statuses = [];
foreach ($items as $item) {
try {
$status = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes);
$this->updateBoundaries($status, $item, $request['friendica_order']);
$statuses[] = $status;
} catch (\Throwable $th) {
Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]);
}
}
if (!empty($request['min_id'])) {
$statuses = array_reverse($statuses);
}
self::setLinkHeader($request['friendica_order'] != TimelineOrderByTypes::ID);
$this->jsonExit($statuses);
}
private function getStatusesForGroup(int $uid, array $request): array
{
$cdata = Contact::getPublicAndUserContactID((int)substr($this->parameters['id'], 6), $uid);
$cid = $cdata['public'];
$condition = ["(`uid` = ? OR (`uid` = ? AND NOT `global`))", 0, $uid];
$condition1 = DBA::mergeConditions($condition, ["`owner-id` = ? AND `gravity` = ?", $cid, Item::GRAVITY_PARENT]);
$condition2 = DBA::mergeConditions($condition, [
"`author-id` = ? AND `gravity` = ? AND `vid` = ? AND `protocol` != ? AND `thr-parent-id` = `parent-uri-id`",
$cid, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Conversation::PARCEL_DIASPORA
]);
$condition1 = $this->addPagingConditions($request, $condition1);
$condition2 = $this->addPagingConditions($request, $condition2);
$sql1 = "SELECT `uri-id` FROM `post-thread-user-view` WHERE " . array_shift($condition1);
$sql2 = "SELECT `thr-parent-id` AS `uri-id` FROM `post-user-view` WHERE " . array_shift($condition2);
$condition = array_merge($condition1, $condition2);
$sql = $sql1 . " UNION " . $sql2 . " GROUP BY `uri-id` " . DBA::buildParameter($this->buildOrderAndLimitParams($request));
return Post::toArray(DBA::p($sql, $condition));
}
private function getStatusesForChannel(int $uid, array $request): array
{
$request['friendica_order'] = TimelineOrderByTypes::ID;
return $this->timeline->getChannelItemsForAPI(substr($this->parameters['id'], 8), $uid, $request['limit'], $request['min_id'], $request['max_id']);
}
private function getStatusesForCircle(int $uid, array $request): array
{
$condition = [
"`uid` = ? AND `gravity` IN (?, ?) AND `contact-id` IN (SELECT `contact-id` FROM `group_member` WHERE `gid` = ?)",
$uid, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, $this->parameters['id']
@ -89,26 +171,6 @@ class ListTimeline extends BaseApi
}
$items = Post::selectTimelineForUser($uid, ['uri-id'], $condition, $params);
$display_quotes = self::appSupportsQuotes();
$statuses = [];
while ($item = Post::fetch($items)) {
try {
$status = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes);
$this->updateBoundaries($status, $item, $request['friendica_order']);
$statuses[] = $status;
} catch (\Throwable $th) {
Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]);
}
}
DBA::close($items);
if (!empty($request['min_id'])) {
$statuses = array_reverse($statuses);
}
self::setLinkHeader($request['friendica_order'] != TimelineOrderByTypes::ID);
$this->jsonExit($statuses);
return Post::toArray($items);
}
}

View File

@ -85,7 +85,11 @@ class PublicTimeline extends BaseApi
$condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_PARENT]);
}
$items = Post::selectTimelineForUser($uid, ['uri-id'], $condition, $params);
if ($request['local']) {
$items = Post::selectLocalTimelineForUser($uid, ['uri-id'], $condition, $params);
} else {
$items = Post::selectTimelineForUser($uid, ['uri-id'], $condition, $params);
}
$display_quotes = self::appSupportsQuotes();

View File

@ -54,7 +54,7 @@ class Retweet extends BaseApi
$item = Post::selectFirst($fields, ['uri-id' => $id, 'uid' => [0, $uid], 'private' => [Item::PUBLIC, Item::UNLISTED]], ['order' => ['uid' => true]]);
if (DBA::isResult($item) && !empty($item['body'])) {
if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::TWITTER])) {
if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::BLUESKY, Protocol::TUMBLR, Protocol::TWITTER])) {
if (!Item::performActivity($id, 'announce', $uid)) {
throw new InternalServerErrorException();
}

View File

@ -59,7 +59,7 @@ abstract class BaseAdmin extends BaseModule
}
}
if (!DI::app()->isSiteAdmin()) {
if (!DI::userSession()->isSiteAdmin()) {
throw new HTTPException\ForbiddenException(DI::l10n()->t('You don\'t have access to administration pages.'));
}

View File

@ -91,7 +91,7 @@ class BaseProfile extends BaseModule
];
} else {
$owner = User::getByNickname($nickname, ['uid']);
if(DI::userSession()->isAuthenticated() || $owner && Feature::isEnabled($owner['uid'], 'public_calendar')) {
if(DI::userSession()->isAuthenticated() || $owner && Feature::isEnabled($owner['uid'], Feature::PUBLIC_CALENDAR)) {
$tabs[] = [
'label' => DI::l10n()->t('Calendar'),
'url' => DI::baseUrl() . '/calendar/show/' . $nickname,

View File

@ -42,7 +42,7 @@ class Bookmarklet extends BaseModule
if (!DI::userSession()->getLocalUserId()) {
$output = '<h2>' . DI::l10n()->t('Login') . '</h2>';
$output .= Login::form(DI::args()->getQueryString(), intval($config->get('config', 'register_policy')) === Register::CLOSED ? false : true);
$output .= Login::form(DI::args()->getQueryString(), Register::getPolicy() !== Register::CLOSED);
return $output;
}

View File

@ -22,14 +22,11 @@
namespace Friendica\Module\Calendar\Event;
use Friendica\App;
use Friendica\Content\Feature;
use Friendica\Core\L10n;
use Friendica\Core\Session\Capability\IHandleUserSessions;
use Friendica\Core\System;
use Friendica\Model\Event;
use Friendica\Model\Item;
use Friendica\Model\Post;
use Friendica\Model\User;
use Friendica\Module\Response;
use Friendica\Network\HTTPException;
use Friendica\Util\DateTimeFormat;
@ -46,17 +43,16 @@ class Get extends \Friendica\BaseModule
/** @var IHandleUserSessions */
protected $session;
public function __construct(App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, array $server, array $parameters = [])
public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, array $server, array $parameters = [])
{
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
$this->session = $session;
$this->app = $app;
}
protected function rawContent(array $request = [])
{
$nickname = $this->parameters['nickname'] ?? $this->app->getLoggedInUserNickname();
$nickname = $this->parameters['nickname'] ?? $this->session->getLocalUserNickname();
if (!$nickname) {
throw new HTTPException\UnauthorizedException();
}

View File

@ -55,7 +55,7 @@ class Show extends BaseModule
protected function rawContent(array $request = [])
{
$nickname = $this->parameters['nickname'] ?? $this->app->getLoggedInUserNickname();
$nickname = $this->parameters['nickname'] ?? $this->session->getLocalUserNickname();
if (!$nickname) {
throw new HTTPException\UnauthorizedException();
}

View File

@ -78,7 +78,7 @@ class Export extends BaseModule
$this->baseUrl->redirect('profile/' . $nickname . '/restricted');
}
if (!$this->session->isAuthenticated() && !Feature::isEnabled($owner['uid'], 'public_calendar')) {
if (!$this->session->isAuthenticated() && !Feature::isEnabled($owner['uid'], Feature::PUBLIC_CALENDAR)) {
$this->sysMessages->addNotice($this->t('Permission denied.'));
$this->baseUrl->redirect('profile/' . $nickname);
}

View File

@ -78,7 +78,7 @@ class Show extends BaseModule
$this->baseUrl->redirect('profile/' . $nickname . '/restricted');
}
if (!$this->session->isAuthenticated() && !Feature::isEnabled($owner['uid'], 'public_calendar')) {
if (!$this->session->isAuthenticated() && !Feature::isEnabled($owner['uid'], Feature::PUBLIC_CALENDAR)) {
$this->sysMessages->addNotice($this->t('Permission denied.'));
return Login::form();
}
@ -91,7 +91,7 @@ class Show extends BaseModule
$this->page->registerFooterScript('view/asset/moment/min/moment-with-locales.min.js');
$this->page->registerFooterScript('view/asset/fullcalendar/dist/fullcalendar.min.js');
$is_owner = $nickname == $this->app->getLoggedInUserNickname();
$is_owner = $nickname == $this->session->getLocalUserNickname();
$htpl = Renderer::getMarkupTemplate('calendar/calendar_head.tpl');
$this->page['htmlhead'] .= Renderer::replaceMacros($htpl, [

View File

@ -141,13 +141,15 @@ class Circle extends BaseModule
protected function content(array $request = []): string
{
$change = false;
$change = false;
$relation = $request['rel'] ?? '';
if (!DI::userSession()->getLocalUserId()) {
throw new \Friendica\Network\HTTPException\ForbiddenException();
}
DI::page()['aside'] = Model\Circle::sidebarWidget('contact', 'circle', 'extended', ((DI::args()->getArgc() > 1) ? DI::args()->getArgv()[1] : 'everyone'));
DI::page()['aside'] .= Widget::contactRels($this->server['REQUEST_URI'], $relation);
// With no circle number provided we jump to the unassigned contacts as a starting point
// @TODO: Replace with parameter from router
@ -298,6 +300,9 @@ class Circle extends BaseModule
// Format the data of the circle members
foreach ($members as $member) {
if (!self::matchRelation($relation, $member['rel'])) {
continue;
}
if ($member['url']) {
$entry = Contact::getContactTemplateVars($member);
$entry['label'] = 'members';
@ -332,6 +337,9 @@ class Circle extends BaseModule
if (DBA::isResult($contacts)) {
// Format the data of the contacts who aren't in the contact circle
foreach ($contacts as $member) {
if (!self::matchRelation($relation, $member['rel'])) {
continue;
}
if (!in_array($member['id'], $preselected)) {
$entry = Contact::getContactTemplateVars($member);
$entry['label'] = 'contacts';
@ -366,4 +374,19 @@ class Circle extends BaseModule
return Renderer::replaceMacros($tpl, $context);
}
private static function matchRelation(string $relation, int $rel)
{
switch ($relation) {
case 'followers':
return($rel == Model\Contact::FOLLOWER);
case 'following':
return($rel == Model\Contact::SHARING);
case 'mutuals':
return($rel == Model\Contact::FRIEND);
case 'nothing':
return($rel == Model\Contact::NOTHING);
}
return true;
}
}

View File

@ -186,7 +186,7 @@ class Follow extends BaseModule
$this->page['aside'] = '';
if (!in_array($protocol, [Protocol::PHANTOM, Protocol::MAIL])) {
$this->page['aside'] = VCard::getHTML($contact);
$this->page['aside'] = VCard::getHTML($contact, false, true);
$output .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'),
['$title' => $this->t('Posts and Replies')]

View File

@ -37,6 +37,7 @@ use Friendica\Module\Contact as ModuleContact;
use Friendica\Module\Response;
use Friendica\Navigation\SystemMessages;
use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface;
@ -122,10 +123,10 @@ class MatchInterests extends BaseModule
continue;
}
$result = $this->httpClient->post($server . '/search/user/tags', $searchParameters);
$result = $this->httpClient->post($server . '/search/user/tags', $searchParameters, [], 0, HttpClientRequest::CONTACTDISCOVER);
if (!$result->isSuccess()) {
// try legacy endpoint
$result = $this->httpClient->post($server . '/msearch', $searchParameters);
$result = $this->httpClient->post($server . '/msearch', $searchParameters, [], 0, HttpClientRequest::CONTACTDISCOVER);
if (!$result->isSuccess()) {
$this->logger->notice('Search-Endpoint not available for server.', ['server' => $server]);
continue;

View File

@ -24,6 +24,7 @@ namespace Friendica\Module\Contact;
use Friendica\App;
use Friendica\BaseModule;
use Friendica\Contact\LocalRelationship;
use Friendica\Contact\LocalRelationship\Entity\LocalRelationship as LocalRelationshipEntity;
use Friendica\Content\ContactSelector;
use Friendica\Content\Nav;
use Friendica\Content\Text\BBCode;
@ -95,32 +96,32 @@ class Profile extends BaseModule
return;
}
Hook::callAll('contact_edit_post', $_POST);
Hook::callAll('contact_edit_post', $request);
$fields = [];
if (isset($_POST['hidden'])) {
$fields['hidden'] = !empty($_POST['hidden']);
if (isset($request['hidden'])) {
$fields['hidden'] = !empty($request['hidden']);
}
if (isset($_POST['notify_new_posts'])) {
$fields['notify_new_posts'] = !empty($_POST['notify_new_posts']);
if (isset($request['notify_new_posts'])) {
$fields['notify_new_posts'] = !empty($request['notify_new_posts']);
}
if (isset($_POST['fetch_further_information'])) {
$fields['fetch_further_information'] = intval($_POST['fetch_further_information']);
if (isset($request['fetch_further_information'])) {
$fields['fetch_further_information'] = intval($request['fetch_further_information']);
}
if (isset($_POST['remote_self'])) {
$fields['remote_self'] = intval($_POST['remote_self']);
if (isset($request['remote_self'])) {
$fields['remote_self'] = intval($request['remote_self']);
}
if (isset($_POST['ffi_keyword_denylist'])) {
$fields['ffi_keyword_denylist'] = $_POST['ffi_keyword_denylist'];
if (isset($request['ffi_keyword_denylist'])) {
$fields['ffi_keyword_denylist'] = $request['ffi_keyword_denylist'];
}
if (isset($_POST['poll'])) {
$priority = intval($_POST['poll']);
if (isset($request['poll'])) {
$priority = intval($request['poll']);
if ($priority > 5 || $priority < 0) {
$priority = 0;
}
@ -128,12 +129,16 @@ class Profile extends BaseModule
$fields['priority'] = $priority;
}
if (isset($_POST['info'])) {
$fields['info'] = $_POST['info'];
if (isset($request['info'])) {
$fields['info'] = $request['info'];
}
if (isset($_POST['channel_frequency'])) {
Contact\User::setChannelFrequency($cdata['user'], $this->session->getLocalUserId(), $_POST['channel_frequency']);
if (isset($request['channel_frequency'])) {
Contact\User::setChannelFrequency($cdata['user'], $this->session->getLocalUserId(), $request['channel_frequency']);
}
if (isset($request['channel_only'])) {
Contact\User::setChannelOnly($cdata['user'], $this->session->getLocalUserId(), $request['channel_only']);
}
if (!Contact::update($fields, ['id' => $cdata['user'], 'uid' => $this->session->getLocalUserId()])) {
@ -319,28 +324,29 @@ class Profile extends BaseModule
if ($contact['network'] == Protocol::FEED) {
$remote_self_options = [
Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
Contact::MIRROR_OWN_POST => $this->t('Mirror as my own posting')
LocalRelationshipEntity::MIRROR_DEACTIVATED => $this->t('No mirroring'),
LocalRelationshipEntity::MIRROR_OWN_POST => $this->t('Mirror as my own posting')
];
} elseif ($contact['network'] == Protocol::ACTIVITYPUB) {
$remote_self_options = [
Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
Contact::MIRROR_NATIVE_RESHARE => $this->t('Native reshare')
LocalRelationshipEntity::MIRROR_DEACTIVATED => $this->t('No mirroring'),
LocalRelationshipEntity::MIRROR_NATIVE_RESHARE => $this->t('Native reshare')
];
} elseif ($contact['network'] == Protocol::DFRN) {
$remote_self_options = [
Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
Contact::MIRROR_OWN_POST => $this->t('Mirror as my own posting'),
Contact::MIRROR_NATIVE_RESHARE => $this->t('Native reshare')
LocalRelationshipEntity::MIRROR_DEACTIVATED => $this->t('No mirroring'),
LocalRelationshipEntity::MIRROR_OWN_POST => $this->t('Mirror as my own posting'),
LocalRelationshipEntity::MIRROR_NATIVE_RESHARE => $this->t('Native reshare')
];
} else {
$remote_self_options = [
Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
Contact::MIRROR_OWN_POST => $this->t('Mirror as my own posting')
LocalRelationshipEntity::MIRROR_DEACTIVATED => $this->t('No mirroring'),
LocalRelationshipEntity::MIRROR_OWN_POST => $this->t('Mirror as my own posting')
];
}
$channel_frequency = Contact\User::getChannelFrequency($contact['id'], $this->session->getLocalUserId());
$channel_frequency = Contact\User::getChannelFrequency($contact['id'], $this->session->getLocalUserId());
$channel_only = Contact\User::getChannelOnly($contact['id'], $this->session->getLocalUserId());
$poll_interval = null;
if ((($contact['network'] == Protocol::FEED) && !$this->config->get('system', 'adjust_poll_frequency')) || ($contact['network'] == Protocol::MAIL)) {
@ -432,6 +438,7 @@ class Profile extends BaseModule
'$frequency_always' => ['channel_frequency', $this->t('Display all posts of this contact'), Contact\User::FREQUENCY_ALWAYS, $this->t('All posts from this contact will appear on the "for you" channel'), $channel_frequency == Contact\User::FREQUENCY_ALWAYS],
'$frequency_reduced' => ['channel_frequency', $this->t('Display only few posts'), Contact\User::FREQUENCY_REDUCED, $this->t('When a contact creates a lot of posts in a short period, this setting reduces the number of displayed posts in every channel.'), $channel_frequency == Contact\User::FREQUENCY_REDUCED],
'$frequency_never' => ['channel_frequency', $this->t('Never display posts'), Contact\User::FREQUENCY_NEVER, $this->t('Posts from this contact will never be displayed in any channel'), $channel_frequency == Contact\User::FREQUENCY_NEVER],
'$channel_only' => ['channel_only', $this->t('Channel Only'), $channel_only, $this->t('If enabled, posts from this contact will only appear in channels, but not in the network stream.')],
]);
$arr = ['contact' => $contact, 'output' => $o];

View File

@ -30,6 +30,7 @@ use Friendica\Module\Response;
use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Network\HTTPException;
use Friendica\Util\Profiler;
use Friendica\Util\Strings;
@ -107,7 +108,7 @@ class Redir extends \Friendica\BaseModule
}
// Test for magic auth on the target system
$response = $this->httpClient->head($basepath . '/magic', [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::HTML]);
$response = $this->httpClient->head($basepath . '/magic', [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::HTML, HttpClientOptions::REQUEST => HttpClientRequest::MAGICAUTH]);
if ($response->isSuccess()) {
$separator = strpos($target_url, '?') ? '&' : '?';
$target_url .= $separator . 'zrl=' . urlencode($visitor) . '&addr=' . urlencode($contact_url);

View File

@ -133,7 +133,7 @@ class Unfollow extends \Friendica\BaseModule
'$keywords_label' => ''
]);
$this->page['aside'] = Widget\VCard::getHTML(Contact::getByURL($contact['url'], false));
$this->page['aside'] = Widget\VCard::getHTML(Contact::getByURL($contact['url'], false), false, true);
$o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), ['$title' => $this->t('Posts and Replies')]);

View File

@ -119,7 +119,7 @@ class Channel extends Timeline
$this->page['aside'] .= $this->getNoSharerWidget('channel');
}
if (Feature::isEnabled($this->session->getLocalUserId(), 'trending_tags')) {
if (Feature::isEnabled($this->session->getLocalUserId(), Feature::TRENDING_TAGS)) {
$this->page['aside'] .= TrendingTags::getHTML($this->selectedTab);
}
@ -128,7 +128,7 @@ class Channel extends Timeline
}
if ($this->channel->isTimeline($this->selectedTab) || $this->userDefinedChannel->isTimeline($this->selectedTab, $this->session->getLocalUserId())) {
$items = $this->getChannelItems($request);
$items = $this->getChannelItems($request, $this->session->getLocalUserId());
$order = 'created';
} else {
$items = $this->getCommunityItems();

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